Skip to content

AppNavInspector 编辑 off-spec 的 nav path/kind(运行期不生效)——应改为 type + 分类型目标选择器 #2245

Description

@os-zhuang

背景

来源:UX eval #5(picker-first / 消除手敲标识符)。审计原本把 AppNavInspector 的 nav path 列为「低复杂度:把自由文本 InspectorTextField 换成按 kind 联动的 combobox」。但深入排查后发现 path/kind 本身是 off-spec 的、运行期不生效 —— 仅美化这个字段会产出一个「运行期忽略、且被兄弟编辑器主动删除」的键,违反本仓 contract-first 原则。据此开 issue 记录正确修法。

注:同一批审计里的另一项 lookup dependsOn 早已在 #1937(2026-06-24)修好(选中字段渲染为可删 chip + InspectorComboField(allowCustom={false}) over host fields),无需再做。本 issue 只针对 nav path

现象 / 根因:inspector 编辑的字段不在 spec 里,运行期也不读

AppNavInspector 编辑 kind + path,但 App 导航的 spec 契约 是一个按 type判别联合,各类型有各自的 typed 目标字段,根本没有 path、没有 kind:

  • spec:packages/spec/src/ui/app.zod.ts(framework)
    • ObjectNavItemtype:'object' + objectName(+ viewName/recordId)
    • DashboardNavItemtype:'dashboard' + dashboardName
    • PageNavItemtype:'page' + pageName(+ params)
    • ReportNavItemtype:'report' + reportName
    • UrlNavItemtype:'url' + url(+ target)
    • ComponentNavItemtype:'component' + componentRef
    • GroupNavItemtype:'group' + children
    • BaseNavItem 要求 snake_case id
  • 运行期只认 typed 字段:packages/layout/src/NavigationRenderer.tsx:382resolveHref 全程 switch(item.type)objectName/pageName/dashboardName/reportName/url/componentRef;packages/layout/src/AppSchemaRenderer.tsx:218-221 同理。从不读 path/kind
  • 没有任何 normalizerpathpageNamekindtype(objectui 与 framework 两仓均已 grep 确认)。

因此:在 AppNavInspector 里给 object/page/dashboard/report 类型写 path,导航不会生效(resolveHref 拿不到 typed 字段 → # 或空)。这是 declared ≠ enforced 的典型(Prime Directive #10)。

相关位置:

  • off-spec 编辑:packages/app-shell/src/views/metadata-admin/inspectors/AppNavInspector.tsx:179-181(path = InspectorTextField;kind = InspectorSelectField,KINDS 在 line 44)
  • canvas 新建即写 off-spec:packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.tsx:180({ label, path: '' },无 type/id)
  • 注册可达:inspectors/index.ts:32 registerMetadataInspector('app', AppNavInspector)(泛用 metadata-admin 里编辑 app 时的 inspector)

已有的「正确」先例(但只覆盖 object)

StudioDesignSurface.tsxStudioNavItemInspector(line 691)已经按 spec 写:

  • 绑定对象 → patch({ id, type:'object', objectName, object:undefined, path:undefined, label })(line 757-764)
  • 解绑 → patch({ type:undefined, objectName:undefined, object:undefined })(line 749)
  • 注释(line 752-756)明确:「nav 是 type 判别联合、BaseNavItem 需要 snake_case id;object/path 被清掉以免残留无效键」

即:现有代码已把 path 当作会导致 navigation.0: Invalid input 的「无效残留键」主动删除。若再把 path 做成 picker 去写它,等于跟这个既有正确实现对着干。但 StudioNavItemInspector 只是一个对象 <select>,不支持 page/dashboard/report/url/component

为什么「仅把 path 变成 picker」是错的

建议修复(契约正确 · 泛化 StudioNavItemInspector)

AppNavInspector 的目标编辑从 kind+path 改为 type + 分类型 typed-target 选择器:

  1. kind 选择器 → type 选择器,选项取 spec 类型:object / page / dashboard / report / url / component / group(action 可后续)。
  2. path 文本 → kind-aware typed-target 选择器(可编辑 combobox,降级为自由文本,mirror FlowReferenceField/ReferenceCombobox + InspectorComboField):
    • object → 写 objectName,选项 client.list('object')
    • page → 写 pageName,选项 client.list('page')
    • dashboard → 写 dashboardName,选项 client.list('dashboard')
    • report → 写 reportName,选项 client.list('report')
    • url → 写 url(自由文本,外链)
    • component → 写 componentRef(已知组件列表或自由文本)
    • group → 无目标(只有 children)
  3. 切换 type 时:set type、清除其它 typed 字段 + legacy path/kind、保证 snake_case id(参照 StudioNavItemInspector)。
  4. 读回向后兼容:显示时兼容旧的 kind/path 与 typed 字段;任意编辑都归一化为 spec 形状(edit 即迁移)。
  5. 抽出纯解析逻辑 type → { targetKey, metaType }(哪个 typed 字段 + 拉哪个 metadata list),加单测。
  6. 收敛:让 StudioNavItemInspector 复用这套泛化后的 picker(或反过来),避免两套 nav 编辑器长期分叉。

验收标准

  • AppNavInspector 里编辑 object/page/dashboard/report/url 项,产出 spec 合法的判别联合项(过 NavigationItemSchema/AppSchema 校验),不再出现 path/kind,且带合法 id
  • 运行期 resolveHref 能据此产出正确 href(点击真正跳转)。
  • type→target 解析器有单测。
  • 补齐 i18n key(en + zh-CN)。
  • AppNavCanvas.addItem 新建项不再只写 {label, path:''}(至少带 type/id,或走同一归一化)。

备注

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdesigner

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions