Skip to content

动作参数弹窗:内联 lookup 参数无法声明引用目标(配置被静默剥离 + 文案谎报「即将上线」) #3405

Description

@baozhoutao

问题

动作参数弹窗(ActionParamDialog)里的 type: 'lookup' 参数,如果是内联声明(不带 field:),永远渲染成纯文本框,提示「粘贴 xxx 的记录 ID(UUID)」,并附一句「可视化选择器即将上线」。

真机场景(PLAT-DEF-005,天顺 EHR 质检派工):质检主管点【指派】/【转派】,需要选一个质检员,结果拿到的是要求粘 UUID 的文本框——得先跑到别处把人的 UUID 复制出来再粘回去,对真人基本不可用。同一套引用字段在新建/编辑记录弹窗里是正常的搜索式选择器。

关键点:不是用户没配。用户配了,被平台静默吃掉了,还回了一句假话。

租户侧原样(os-tianshun-ehr/src/objects/quality_dispatch.ts):

params: [
  { name: 'inspector', label: '质检员', type: 'lookup', reference: 'sys_user', required: true },
]

reference: 'sys_user' 意图明确,但它被丢了两次,且全程零反馈。

三个平台侧缺陷

1. 内联 lookup 参数无法声明引用目标 —— 能力缺口

packages/spec/src/ui/action.zod.ts 的 ActionParamSchema 全量键是:

name / field / objectOverride / label / type / required / options / placeholder / helpText / defaultValue / multiple / accept / maxSize / defaultFromRow / visible / requiresFeature

没有引用目标这一项。 讽刺的是 schema 里已经有一段注释写着「Widget config for inline params」,把 multiple / accept / maxSize 专门补给了内联参数——唯独漏了 lookup 需要的那一个。于是 file/image 类内联参数能配全,lookup 内联参数配不全。

消费端 objectui/packages/app-shell/src/utils/paramToField.ts:59:

if (LOOKUP_WIDGET_TYPES.has(type) && !param.referenceTo) type = 'text';

而 referenceTo 目前唯一的来源是 resolveActionParams.ts 的字段引用分支(从对象字段的 reference_to ?? reference 抄过来);内联分支(resolveActionParams.ts:161-176)的返回对象里根本没有这一项,RawActionParam 接口也没声明它。

所以内联 lookup 参数 100% 必然降级成文本框,与作者怎么写无关。

2. 未知键静默剥离,配错零反馈 —— 校验缺陷

ActionParamSchema 是 zod .strip,reference 这种未知键在服务端解析时直接丢弃,不报错、不告警。作者写了一个语义完全正确、且与 FieldSchema.reference 同名的键,得到的唯一反馈是一个文本框。

这是这次排查跑偏的根源:现象看起来像「平台功能没做」,实际是「配置被吃了」。

3.「可视化选择器即将上线」是假话 —— 文案缺陷(objectui)

objectui/packages/i18n/src/locales/zh.ts:2219:

lookupHelpText: '请输入引用记录的 ID。可视化选择器即将上线。'
lookupPlaceholder: '粘贴 {{label}} 的记录 ID(UUID)'

选择器早就上线了(字段引用式 lookup 参数走的就是 <LookupField>,搜索式选择器)。这句话把「你这个参数的写法拿不到选择器」谎报成「平台还没做这功能」,把用户和排查的人一起带沟里。共 10 个语种:en / zh / ja / ko / de / fr / es / pt / ru / ar。

方案

framework(本仓)

  1. ActionParamSchema 增加 reference,键名与 FieldSchema.reference(packages/spec/src/data/field.zod.ts:292)保持一致——这样租户已经写出来的那行代码原样就是合法的,不用让用户改写成第二种拼法。放在现有「Widget config for inline params」注释块里,和 multiple / accept / maxSize 并列:

    /** Reference target for inline `lookup` / `master_detail` params (field-backed params inherit it from the referenced field). Mirrors FieldSchema.reference. */
    reference: SnakeCaseIdentifierSchema.optional(),
  2. type: 'lookup' | 'master_detail' 且拿不到引用目标时报校验错误(内联参数没写 reference,或字段引用参数解析不到目标)。加在 ActionParamSchema 现有的 .refine() 链上,和 'ActionParam requires either "name" or "field"' 同级。让配错在构建期炸出来,而不是运行期悄悄降级。

  3. (评估)未知键从 .strip 收紧为报错或告警。这条影响面比前两条大得多,可能踩到其他既有元数据,建议单独评估、必要时拆出去做;前两条不依赖它。

objectui(objectstack-ai/objectui,同一 PR 组内跟进)

  1. RawActionParam 补 reference?: string,resolveActionParam() 内联分支透传为 referenceTo(字段引用分支已有的映射逻辑不动)。
  2. paramToField.ts:59 的 text 降级只保留「确实拿不到引用目标」的兜底,配上开发期 console.warn。
  3. 改掉 lookupHelpText / lookupPlaceholder 的假话文案,10 个语种一起改:不再说「即将上线」,改成指明这个参数缺引用目标。

验收

  • 内联 { name:'inspector', type:'lookup', reference:'sys_user' } 在动作参数弹窗里渲染出搜索式选择器(能按姓名/邮箱搜人),与新建/编辑记录弹窗一致。
  • 内联 lookup 参数漏写 reference → 构建期校验报错,而不是静默变文本框。
  • 10 个语种再无「即将上线 / coming soon」这类描述。
  • PLAT-DEF-005 场景真机复现:质检主管点【指派】能直接搜人选人,全程不接触 UUID。

备注

  • 天顺 EHR 侧目前不做绕行(对象上虽有 inspector / supervisor 两个 lookup→sys_user 字段,改成 { field: 'inspector' } 字段引用式能立刻出选择器)。按决定走平台正解,租户配置保持原样不动。
  • 关联:objectui 侧 ADR-0059(动作参数复用表单字段 widget 渲染器)。

Activity

  1. baozhoutao commented on Jul 22, 2026

    @baozhoutao
    ContributorAuthor

    两个 PR 已开,互相引用:

    方案第 3 条(未知键从 .strip 收紧为报错/告警)本轮未做,仍待评估:影响面比前两条大,可能踩到其他既有元数据,且前两条不依赖它。

    验收结果

    • ✅ 内联 { name:'p_account', type:'lookup', reference:'showcase_account' } 在 showcase 的动作参数弹窗里渲染出搜索式选择器;GET /api/v1/data/showcase_account?top=50&search=华宁 服务端过滤命中,选中后按记录 ID 回填,全程不接触 UUID
    • ✅ 漏写 reference → objectstack validate 报 ✗ ["reference"] ActionParam with type "lookup"/"master_detail" requires "reference"…;补上 → ✓ Validation passed
    • ✅ 10 语种再无「即将上线 / coming soon」
    • ⏸️ PLAT-DEF-005 真机场景(天顺 EHR 指派/转派)待 spec 发版后回归 —— 按决定租户侧不做绕行,配置保持原样

    说明:验证在 framework showcase 上做的(内联 lookup 参数标本 p_account),不是在 EHR 租户上。EHR 侧要等 @objectstack/spec 带 reference 的版本发出来、且 console 更新后才能回归。

  2. self-assigned this
    on Jul 28, 2026
  3. os-zhuang commented on Jul 30, 2026

    @os-zhuang
    Contributor

    方案三条全部落地,并已随 @objectstack/spec@17.0.0-rc.0 发布。关闭。

    # 内容 PR
    1 ActionParamSchema 增加 reference(对齐 FieldSchema.reference) #3406
    2 内联 lookup/master_detail 缺引用目标 → 解析期报错 #3406
    3 未知键从 .strip 收紧为报错(此前标注「单独评估」的那条) #3746
    — objectui 侧透传 + 降级 warn + 10 语种去假话 objectstack-ai/objectui#2786
    — showcase 标本 + 真机验证 #3970

    第 3 条实测本仓零破坏:spec 258 文件 / 6716 用例通过,三个示例应用 validate 全过 —— 仓库里没有任何既有元数据带未声明的参数键。错误信息带修正处方,reference_to / referenceTo / targetObject → reference,visibleWhen → visible(后者尤其要紧:ADR-0089 让 visibleWhen 成为 view/page 的正统拼法,在动作参数上借用它过去会把能力开关整个剥掉、让参数无条件渲染)。

    真机验收

    showcase :5411 + objectui HEAD console dev :5412(/_console 是 vendored 旧构建,验 objectui 改动必须用它自己的 dev server):

    • 服务端两个内联 picker 参数都带上 reference:p_account→showcase_account、p_assignee→sys_user
    • 弹窗里 Assignee 渲染成可搜索 picker,输入「Dev」发出 GET /api/v1/data/sys_user?top=50&search=Dev,下拉给出 Dev Admin / admin@objectos.ai
    • 零 ActionParamDialog 降级告警 —— 没有参数掉回 UUID 文本框

    p_assignee(指向 sys_user 的内联 lookup)作为 PLAT-DEF-005 形状的常驻标本留在 showcase,#3970。

    遗留(不阻塞关闭)

    一、latest 标签仍指着 16.1.0,只有 rc 通道带这些修复。 天顺 EHR 回归 PLAT-DEF-005 需显式装 @objectstack/spec@rc(或钉 17.0.0-rc.0);走 latest 或 ^16 会拿到旧版、现象照原样复现,且看起来像「平台没修」。

    二、.strip 只收紧了 ActionParamSchema 一个。 全仓 z.object 里 strict=23 / passthrough=18 / 默认 strip=1897,集中在 api/protocol.zod.ts(104)、kernel/plugin-security.zod.ts(44)、ui/view.zod.ts(41)。也就是说「配置被静默吃掉」这个失效模式远不止动作参数一处,本 issue 只是它恰好被真实用户踩到并追到底的那一次。ADR-0078 正为此而写,但状态仍是 Proposed、核心机制未建。建议单独立项,并先分类(外部 API 响应等本就该保持宽松),不要一次性全改。


    Generated by Claude Code

  4. os-zhuang commented on Jul 30, 2026

    @os-zhuang
    Contributor

    补:方案第 3 条的推广已开跟踪 issue —— #4001。

    #3746 只收紧了 ActionParamSchema 一个,全仓其余 1885 个 z.object 站点仍是默认 .strip,所以本 issue 的失效机制在别处原样存在。#4001 带了目录级测量、可授权面(≈453 站点)与线上形状的分类建议、可复用的现有机制清单,以及 #3746 踩过的四个坑。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions