Repository navigation
动作参数弹窗:内联 lookup 参数无法声明引用目标(配置被静默剥离 + 文案谎报「即将上线」) #3405
Description
问题
动作参数弹窗(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(本仓)
-
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(),
-
type: 'lookup' | 'master_detail'且拿不到引用目标时报校验错误(内联参数没写reference,或字段引用参数解析不到目标)。加在ActionParamSchema现有的.refine()链上,和'ActionParam requires either "name" or "field"'同级。让配错在构建期炸出来,而不是运行期悄悄降级。 -
(评估)未知键从
.strip收紧为报错或告警。这条影响面比前两条大得多,可能踩到其他既有元数据,建议单独评估、必要时拆出去做;前两条不依赖它。
objectui(objectstack-ai/objectui,同一 PR 组内跟进)
RawActionParam补reference?: string,resolveActionParam()内联分支透传为referenceTo(字段引用分支已有的映射逻辑不动)。paramToField.ts:59的 text 降级只保留「确实拿不到引用目标」的兜底,配上开发期console.warn。- 改掉
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
baozhoutao commentedon Jul 22, 2026
两个 PR 已开,互相引用:
lookupaction param declare its reference target (#3405) #3406 ——ActionParamSchema增加reference(对齐FieldSchema.reference)+ 内联 picker 缺目标改为解析期报错 + app-showcase 补内联 picker 标本lookupaction params a real record picker (#3405) objectui#2786 —— 内联reference→referenceTo透传、降级 dev 期 warn、10 语种去掉「即将上线」假话方案第 3 条(未知键从
.strip收紧为报错/告警)本轮未做,仍待评估:影响面比前两条大,可能踩到其他既有元数据,且前两条不依赖它。验收结果
{ name:'p_account', type:'lookup', reference:'showcase_account' }在 showcase 的动作参数弹窗里渲染出搜索式选择器;GET /api/v1/data/showcase_account?top=50&search=华宁服务端过滤命中,选中后按记录 ID 回填,全程不接触 UUIDreference→objectstack validate报✗ ["reference"] ActionParam with type "lookup"/"master_detail" requires "reference"…;补上 →✓ Validation passed说明:验证在 framework showcase 上做的(内联 lookup 参数标本
p_account),不是在 EHR 租户上。EHR 侧要等@objectstack/spec带reference的版本发出来、且 console 更新后才能回归。fix(app-shell): give inline `lookup` action params a real record pick…
feat(spec): let an inline `lookup` action param declare its reference…
[design] ADR-0104: field runtime value-shape as a first-class contrac…
fix(spec): keep the schema's own JSDoc first in action.zod.ts (#3405)
fix(spec): regenerate skill references, keep the error map internal (#…
feat(spec): reject unknown keys on an action param instead of strippi…
chore: add an empty changeset for the showcase specimens (#3405, #3508)
i18n(showcase): translate the new inline picker param (#3405)
test(showcase): specimens for the inline picker and the record-backed…
os-zhuang commentedon Jul 30, 2026
方案三条全部落地,并已随
@objectstack/spec@17.0.0-rc.0发布。关闭。ActionParamSchema增加reference(对齐FieldSchema.reference)lookup/master_detail缺引用目标 → 解析期报错.strip收紧为报错(此前标注「单独评估」的那条)第 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):reference:p_account→showcase_account、p_assignee→sys_userGET /api/v1/data/sys_user?top=50&search=Dev,下拉给出Dev Admin / admin@objectos.aiActionParamDialog降级告警 —— 没有参数掉回 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
os-zhuang commentedon Jul 30, 2026
补:方案第 3 条的推广已开跟踪 issue —— #4001。
#3746 只收紧了
ActionParamSchema一个,全仓其余 1885 个z.object站点仍是默认.strip,所以本 issue 的失效机制在别处原样存在。#4001 带了目录级测量、可授权面(≈453 站点)与线上形状的分类建议、可复用的现有机制清单,以及 #3746 踩过的四个坑。Generated by Claude Code
fix(showcase): translate the object-less action, restoring the i18n r…
fix(showcase): translate the object-less action, restoring the i18n r…
feat(spec)!: reject unknown keys on flow and permission authoring sch…
test(spec): the first ui/ file group's test titles state each cited d…