What to build: 只读展示工单七态和状态时间线。验收驳回表现为“待验收回退处理中”的动作轨迹,不展示延期状态。
Blocked by: 11
Status: resolved Labels: ready-for-agent
已完成并关闭(2026-09-18)。 逻辑闸门 49/49、真机走查 21/21;回归 15 个既有逻辑闸门全绿, 相邻的两个真机走查(t11 设备详情 29/29、t10 点位详情 28/28)也复跑确认共用详情卡没回退。
domain/workOrderTimeline.js(新):七态动作轨迹的纯函数层。
stepAction()(推进/回退/取消/同态/认不出的码)、trackOf()(按 operatedAt 排序、
记 reachedCode 走到过的最远处、数回退次数)、tracksByOrder()(每张工单一条)、
pathView()(七态路径视图)、pathOnlyTrack()(只有路径、没有轨迹的兜底)。components/WorkOrderTimeline.vue(新):单张工单的七态路径 + 动作流水两段。domain/equipmentDetailView.js:新增 timelineSource()(按工单编码配轨迹 + 轨迹分区状态);
DEFERRED_SECTIONS 只剩 alarmWarningRelations(timelines 已由本票接手)。components/EquipmentDetailSections.vue:工单面板内每张工单挂它自己那条时间线。mocks/contractMocks.js:默认轨迹补成完整七态 + 一次回退;新增 work-order-timeline
(三张工单、三种形状:一路推进 / 待验收回退处理中 / 已取消)与 work-order-timeline-failed
(工单列表成功、只有轨迹分区失败)两个场景;/work-orders/*/timeline 改为按编码回显
(认不出的编码抛错,与真接口 404 语义一致)。verify/t12-work-order-timeline.mjs(新,49 项)、verify/t12-work-order-timeline-walkthrough.mjs(新,21 项)。1. 「回退是动作」是一条判定,所以它落在纯函数里。 票面原句「验收驳回显示为回退动作,
不是独立终态」在实现上就是「上一条 5 待验收、这一条 4 处理中 ⇒ rollback」。
TIMELINE_ACTION 只有六个取值(start/forward/rollback/cancel/hold/unknown),
没有「已驳回」「延期」这类伪状态;认不出的码(如后端真给了「已驳回」)落 UNKNOWN
且状态名留空 —— 不猜、也不接受第八态。七态路径永远七个节点,驳回不多出一个。
2. 当前态与"走到过的最远处"必须分开,否则回退看不见。 待验收被驳回后当前态是 4,
但"走到过待验收"是事实。只画当前态的话,回退在界面上会消失(看起来像从没到过 5)。
所以 trackOf() 同时给 stateCode(工单自己的当前态)与 reachedCode(走到过最远),
路径视图按两者分别标 current 与 reached。
3. 轨迹分区失败 ≠ 没有动作记录,而七态路径不该跟着一起丢。 两条独立判断,都是"别对值班人员撒谎":
loadTimelines() 会在时间线读不到时给 SOURCE_SECTION_FAILED,工单列表可能照常成功,
所以「轨迹没取到」(timelineSource().placeholder)与「这张工单没有动作记录」
必须说成两句不同的话,模板上是两条不同分支(F9/F10 两条静态守卫钉住);pathOnlyTrack()),只是没有动作流水 ——
因为"轨迹没取到"而连"这张工单现在在哪一态"也一起不画,那是丢掉本来已经拿到的信息。4.(本轮最值得记的一条)静态守卫与逻辑闸门都盖不住「组件里调了没 import 的函数」。
真机走查第一轮逮到一个真 bug:EquipmentDetailSections.vue 在脚本体里写了
return full || pathOnlyTrack(order),却忘了 import —— 结果是 pathOnlyTrack is not defined
把整块工单面板打没(C 组 4 条判红)。三处闸门当时全绿:静态守卫只查"有没有出现被禁的东西"、
逻辑闸门测的是 domain 层(压根不加载 .vue)、而模板级引用检查看不见脚本体。
补上 F11(模板表达式引用完整性)+ F12(脚本体调用点完整性) 两条守卫,
并且用变异测试确认有牙:去掉那行 import 后 F12 恰好报出 pathOnlyTrack。
(F11 第一版只扫模板,变异测试证明它对本案完全无效 —— 那次假绿已修;
F12 第一版又被注释里的 O(n²) 与 JSDoc 里的 timelineSource() 判成假红,已改为先剥注释再扫。)
教训:能加载/能编译 ≠ 能渲染;模板与脚本体要分别守。
票 11 票面写明「timelines 归票 12」,本票接手时没有新增第七块面板:
七态轨道是工单状态面板里每张工单自己的一条线(验收第 2 条「每张工单有独立时间线」),
所以它挂在 kind: 'orders' 那一块上。票 11 的 F5 断言与措辞已同步更新
(DEFERRED_SECTIONS 现只剩 alarmWarningRelations),t05 的静态守卫面已把本票新增的
一个 domain 与一个组件补进去。
逻辑闸门 49/49(六段:A 七态字典 / B 动作判定 / C 轨迹组装 / D 七态路径视图 / E 分区独立(走真契约 mock)/ F 静态守卫,含 F11/F12 两条引用完整性守卫)。回归 15 个既有 逻辑闸门全绿,其中 t05 从 178 增至 194(守卫面扩容)、t11 仍 61/61。
node .scratch/cockpit-shell-panel-layout/verify/t12-work-order-timeline.mjs
真机走查 21/21(yarn dev 已在 8000/8001 运行,Chrome 152 headless):
node .scratch/cockpit-shell-panel-layout/verify/t12-work-order-timeline-walkthrough.mjs
四组,内容从 DOM 读(证明界面真画出来了,而不是 store 里有值):
data-wo-code 与工单编码一一对应;三张步数各不相同(证明不是同一份轨迹抄三遍);
轨迹分区 success、无占位说明。reached(两者可区分);对照那张一路推进的零回退
(故"恰一次回退"不是恒真);已取消标 offPath 且是当前态(不接在已结案后面冒充第八步);
详情里按钮只有「返回点位 / 关闭」(零写操作);无页面错误。(四组共 4+9+5+3 = 21 项。)
另复跑相邻两个真机走查确认共用详情卡未回退:t11 设备详情 29/29、t10 点位详情 28/28。
| 验收项 | 证据 |
|---|---|
| 工单状态完整展示七态 | 逻辑 A1/A3 + D1;走查 B1(DOM 上七个节点) |
| 每张工单有独立时间线 | 逻辑 C8/E1/E2;走查 A2/A3(三张各自一条、步数不同) |
| 验收驳回是回退动作、不是独立终态 | 逻辑 B2/C2/C3/C4/D1/D2;走查 B2/B3/B4/B5 |
| 不提供任何工单写操作 | 逻辑 F6;走查 B8(详情里只有返回/关闭) |
| 有工单状态映射单测 | 逻辑闸门 A–D 四段(49 项中的 22 项) |
**工单七态与状态时间线只读展示完毕:七态路径(七个节点,驳回不多出第八态)与动作轨迹分成两段,
domain/workOrderTimeline.js 是纯函数判定层 —— 「验收驳回」由状态码 5→4 判成 rollback
动作而不是终态;当前态与"走到过的最远处"分开记,否则回退在界面上会消失;
「轨迹没取到」与「这张工单没有动作记录」是两句不同的话(模板上是两条不同分支),
但七态路径不跟着轨迹一起丢(当前状态码来自工单列表分区,走 pathOnlyTrack() 照铺)。
不新增第七块面板:轨道挂在工单面板内每张工单下面。逻辑闸门 49/49、真机走查 21/21。**
本票最值得留给后人的一条:静态守卫与逻辑闸门都盖不住「组件里调了没 import 的函数」——
真机走查第一轮逮到 pathOnlyTrack is not defined(三处闸门当时全绿,因为守卫只查"有没有
出现被禁的东西"、逻辑闸门测的是 domain 层不加载 .vue、而模板级引用检查看不见脚本体)。
已补 F11/F12 两条引用完整性守卫,并用变异测试确认有牙。能加载 ≠ 能渲染;模板与脚本体要分别守。
已认领(Status: claimed)。本票无 needs-triage 标签、Blocked by: 11 且票 11 已 resolved,
故不经过诊断阶段,直接进入实施。
用户同意继续后跑了真机走查。第一轮 17/21:C 组 4 条判红,根因是
EquipmentDetailSections.vue 调了 pathOnlyTrack(order) 却忘了 import
(pathOnlyTrack is not defined,整块工单面板打没)。补上 import 并新增
F11(模板表达式引用完整性)+ F12(脚本体调用点完整性)两条守卫、变异测试确认有牙后,
第二轮 21/21 通过,逻辑闸门同时升到 49/49。相邻两个走查复跑无回退
(t11 29/29、t10 28/28)。本票据此 close(100%)。
走查需要 Chrome(puppeteer 以 piped stdio 启动子进程),当前沙箱模式会以 spawn EPERM
拒绝;已按一次沙箱升级重试的路径取得授权后跑通。后续他人复跑若遇同一报错,
那是沙箱边界而不是脚本问题。
本票被重开复核,不是要重做,而是因为票 20 按后端权威枚举改动了本票的交付面。 维护者 2026-09-29 裁定:以后端
WorkOrderStatusEnum(0–7)为唯一真理,删除其他不必要的字典。 该裁定落在票 20 里执行,外溢到本票,故按 Q15=(a) 重开记录并提醒维护者。
CockpitWorkOrderStateDictionary 的类注释原文:
对外公开的工单状态字典,与运行时
WorkOrderStatusEnum(0–7)对齐。 0 待派单、1 待接单、2 已接单、3 处理中、4 待验收、5 已结案、6 延期中、7 已取消; 终态为 5、7,延期中是处理旁路。
com.zksy.ops.enums.WorkOrderStatusEnum 逐项与之一致。而本票当初按旧的 1–7 七态实现,
于是每一态都错位一格:本票把「已结案」标在码 6 上,而后端的 6 是「延期中」、5 才是「已结案」。
| 本票原验收原句 | 现状 | 改为 |
|---|---|---|
| 「工单状态完整展示待派单…已取消」(七态) | 0–7 是 8 个码 | 0–7 共 8 个状态,「七态」这个说法作废 |
| 「不展示延期状态」 | 6 延期中是后端正式状态(ACTIVE_CODES 含它、FINISHED_CODES 只含 5/7) |
延期中要展示,且是处理旁路(路径外节点,与已取消同类) |
| 「验收驳回显示为回退动作,不是独立终态」 | 仍然成立 | 一字未改(回退仍是动作;码从 4→3) |
ADR 0003 里那句「工单只读展示七态…不展示延期」已同步更正(见该 ADR 的 2026-09-29 修订节)。
domain/workOrder.js:WORK_ORDER_STATES 改为 0–7(新增 bypass 标记),
WORK_ORDER_CLOSED_CODE 6 → 5;文件头写明旧口径的坑。domain/workOrderTimeline.js:推进主线 1→6 改为 0→5;
新增 WORK_ORDER_BYPASS_CODE = 6 与 TIMELINE_ACTION.BYPASS;
stepAction() 里进出 6 判为旁路(既不冒充主线前进、也不算回退);
pathView() 输出 bypass 标记。ROLLBACK 的语义与判定一字未改。components/WorkOrderTimeline.vue:文案「七态路径」→「状态路径」,
新增 data-wo-path-bypass 与「(处理旁路)」提示。mocks/contractMocks.js:时间线样例全部按 0–7 重排(每条记录的码都小了一格);
新增 WO-TL-DELAYED(延期中旁路)场景,使旁路分支可演示而不只写在注释里。verify/t12-work-order-timeline.mjs:49 → 52 项。A1/A2/A3 改按 0–7;
B6-no-delay-action-exists(守"没有延期档"这条已被后端推翻的假设)改为
B6-delay-is-bypass-action;新增 A2b(6 是旁路非终态)与 D3b(延期中是路径外旁路)。
本票核心的 B2/C2/C3/C4(回退是动作、走到过最远处)逐字保留、仍然全绿。复核通过后本票可再次 close(当前 Status: resolved 保持不变,本段只是如实记录跨票改动)。