Repository navigation
Description
现象
控制台列表页点「新建」→ 填完表单点「创建」后,浏览器一定会离开列表:要么在列表上盖一层记录抽屉(?recordId=…),要么整页跳到记录页(/<object>/record/<id>)。作者无法用元数据改变这个去向 —— 想让操作员留在列表连续录入(队列式登记场景:一次登记多条加减分 / 多条调整申请)就没有办法。
在 form view 上声明 submitBehavior: { kind: 'continue' }(spec ui/view.zod.ts 的 FormViewSchema.submitBehavior,文档说「reset the form so another response can be entered」)通过了 os validate,但对这条路径完全无效,创建后照样弹记录抽屉。
读了一下 @objectstack/console@17.2.0 的产物,去向是这样定的:
- 应用外壳把列表的「新建」渲染成
formType: 'modal'的object-form,并给它传了onSuccess回调; plugin-form里的提交处理是if (e.onSuccess) await e.onSuccess(n); else if (e.submitBehavior) {…}——onSuccess短路了submitBehavior,所以表单视图上声明的submitBehavior在这条路径上永远读不到;- 而那个
onSuccess的去向由recordSurface(objectDef)算出来:可见字段数 >= 12 → 记录页,否则 → 记录抽屉。既不看submitBehavior,也不看列表视图的navigation。
也就是说:「新建之后去哪儿」这件事目前唯一的输入是对象的字段个数,没有任何元数据开关。
最小复现(17.2.0)
- 建一个字段数 < 12 的对象(下例
demo_note,5 个字段),给它一个列表视图和一个 form view; - form view 上显式声明
submitBehavior: { kind: 'continue' };
// *.view.ts
export const NoteViews = defineView({
list: { label: '便签', type: 'grid', data: { provider: 'object', object: 'demo_note' }, columns: [{ field: 'title' }] },
formViews: {
form: {
type: 'simple',
data: { provider: 'object', object: 'demo_note' },
submitBehavior: { kind: 'continue' }, // ← 声明了,validate 通过
sections: [{ name: 'main', label: '便签', columns: 2, fields: [{ field: 'title', required: true }, { field: 'body' }] }],
},
},
});os validate→ 通过,无告警;os dev→ 控制台打开/_console/apps/<app>/demo_note→ 点「新建」→ 填必填项 → 点「创建」。
期望:表单重置,停在列表上,可以接着录下一条(kind: 'continue' 的语义)。
实际:URL 变成 /_console/apps/<app>/demo_note?recordId=<新记录 id>,列表上盖出记录抽屉,「新建」按钮被挡住;submitBehavior 没有任何效果。
字段数 ≥ 12 的对象上同样声明 submitBehavior,实际结果是整页跳到 /demo_note/record/<id> —— 同样与声明无关。
我们这边的实测(KPI 应用,17.2.0):kpi_bonus(10 个字段)→ 抽屉;kpi_adjustment(15 个字段)→ 记录页;两者都在 form view 上试过 submitBehavior: { kind: 'continue' },行为一个字没变。
期望能力
三选一都能解决,按我们的偏好排序:
- 让列表「新建」这条路径读
submitBehavior:外壳的onSuccess只在表单没声明submitBehavior时兜底,声明了就让表单自己的行为优先(与/_console/forms/:name路径一致)。kind: 'continue'就正好表达「留在列表继续录入」。 - 或者在列表视图上给一个显式键表达「新建成功后的去向」(留在列表 / 开抽屉 / 跳记录页),让
recordSurface的字段数启发式只作为默认值。 - 至少让
submitBehavior在这条路径上声明即报错,而不是解析通过却静默无效 —— 现在的表现符合「parse-clean no-op」,作者要读产物才知道自己写的键没人读。
平台版本
@objectstack/spec / @objectstack/console / @objectstack/cli 均为 17.2.0;运行方式 objectstack dev,SQLite(better-sqlite3)驱动,单租户。
Activity
os-zhuang commentedon Sep 8, 2026
分诊:
repo:objectui/Bug/priority:p2/pm:queue域 —— 读代码判定:三处相关代码全在 objectui,本仓无一处:
本仓这边只有
packages/console那个 4 文件的发布壳(其package.json自陈「Source of truth: @object-ui/console … objectui」)。⇒repo:objectui。⭐ 复核推翻了「这个能力不存在」的读法 ——
submitBehavior是实现了的,被短路的只是这一条路径上报读产物得出「
submitBehavior在这条路径上永远读不到」,这是对的;但很容易被后来者读成「console 还没做submitBehavior」。不是:⇒ ⭐
kind: 'continue'在ObjectForm上有专门的钉子测试,是通的。 缺陷是优先级:列表「新建」这条路径由外壳注入onSuccess,而DrawerForm.tsx:399/:445的写法是if (schema.onSuccess) { await … }—— 宿主回调无条件先行,作者声明的submitBehavior连读都没读。这把本卡从「请实现一个能力」(Feature)变成「一条路径吞掉了已实现的声明」(Bug),也大幅缩小了修复面:不是造功能,是让一个已经存在且已测的分支在这条路径上有机会执行。
类型
Bug按类型判据:
submitBehavior是 specui/view.zod.tsFormViewSchema上已声明的键,文档写明continue是「reset the form so another response can be entered」,os validate放行。而这条路径上它静默无效。⇒ 已声明契约未兑现(class b),同时是典型的 parse-clean no-op 陷阱(class c:作者写了一个没人读的键,且必须去读产物才知道)。⛔ 不是 Feature。等级
p2kpi_bonus10 字段 → 抽屉,kpi_adjustment15 字段 → 记录页),声明submitBehavior后「行为一个字没变」。RECORD_SURFACE_PAGE_THRESHOLD,上报测得阈值 12)在决定一个业务流程的形状,而作者加/删一个字段就会翻转它 —— 且没有任何声明能覆盖。交给认领席(三条建议里,本席对确定性的判断)
上报按偏好排了三条,本席按是否需要裁决重排:
submitBehavior,宿主onSuccess退为兜底)—— 确定,推荐先做这条。 它只对「显式声明了一个今天什么都不做的键」的作者改变行为;没声明的一律走原路。⇒ 只拒绝/改变已经坏掉的东西,零回归面。而且它让这条路径与/_console/forms/:name路径一致,消除的是两条路径两套语义。kind: 'continue'已经表达了「留在列表继续录入」。⛔ 不要在本卡里顺手加 spec 键。submitBehavior在这条路径上就生效了,也就无需报错。⇒ 只有在维护者决定这条路径永远不读submitBehavior时,建议 3 才成立;那时它属domain:spec的 lint,另立卡。⇒ 本卡范围 = 建议 1。若认领席读完认为建议 1 不可行(例如
onSuccess承载了 SDUI/内联字段采集器的写入语义,见DrawerForm.tsx:394那条注释「the inline-fields collector, whoseonSuccessIS the write」),停下回报 —— 那条注释确实标出了一个不能一刀切退为兜底的onSuccess用法,是本卡最需要小心的地方。origin/main当刻,两者可能已分叉,认领时重读。RECORD_SURFACE_PAGE_THRESHOLD是一个具名导出常量(plugin-view/src/index.tsx:21),不是散落在代码里的魔数。⇒ 认领时按符号读它的值,⛔ 不要继承卡面的 12。分诊席声明:本席只分类/定级/路由,⛔ 不认领、⛔ 不派工、⛔ 不写码、⛔ 不合并、⛔ 不裁决决策箱卡。
Generated by Claude Code