What to build: 通过设备详情聚合契约加载设备档案、最新指标、预警、报警、工单状态和最新图片。各分区可独立成功、失败、为空或过期。
Blocked by: 10
Status: resolved Labels: ready-for-agent
已完成并关闭(2026-09-18)。 逻辑闸门 61/61、真机走查 29/29;回归 12 个既有逻辑闸门全绿。
domain/equipmentDetailView.js(新):六个分区的界面视图(纯函数)。
面板渲染计划 EQUIPMENT_SECTION_PANELS(档案 / 指标 / 预警 / 报警 / 工单 / 图片)、
占位符与空/失败/不可用措辞、档案行 / 指标行 / 事件行 / 工单行 / 图片视图,
外加三个判据:sectionHasContent(过期算有值)、equipmentDetailMatches(这份载荷是不是当前这台)、
aggregateUnavailable(整包失败,见下)。components/EquipmentDetailSections.vue(新):六块分区各带自己的状态标签与内容,
一块坏了缩成一行提示、其余照常画。components/PointDetailCard.vue:第二级由「待接入」占位换成真分区容器。stores/detailStore.js:loadEquipmentAggregate(按编码取数)+ deviceLoading,
并把进设备级的两个入口(selectDevice / openDevice)改成自己负责取数。mocks/contractMocks.js:新增 equipment-partial 场景(档案/工单/图片各自 FAILED,
指标/预警/报警 SUCCESS)。原先那段分区级失败样例挂在 failed 分支下永远走不到
(failed 在注册层就抛错),所以「任一分区失败不遮盖其它分区」在 mock 上一直无法演示。1. 票 10 的「进第二级不取数」是延期,不是契约。 票 10 票面原文写明「设备详情聚合的加载是票 11 的契约」,
所以本票把取数收进 selectDevice / openDevice 这两个唯一的进级入口:
「进了设备级却没取数」在结构上不可能发生,调用方不必记得多调一次。
竞态收口沿用票 10 的点位级同款(token 丢弃迟到响应),并追加两条:
已答过的同一台不再重复取(返回卡片列表再点回来不重拉),
失败/不可用不算已答(留重试路径,否则永远没有恢复路径)。
2. 「整包失败」与「分区级失败」必须分开,否则界面对值班人员撒谎。
聚合端点整体读不到时 data 为 null、六块一起没数据,若照常渲染六块,
每块都会说「该分区加载失败(其它分区不受影响)」—— 那句话在只有一块坏时是真的,
在六块一起坏时是假的。所以整包失败走一句实话 + 「重新加载」入口,
分区级失败(aggregateState: PARTIAL、各分区自带 state、data 在)才逐块渲染。
这是本票验收第 3 条的反面,专门写进了 aggregateUnavailable 的注释与单测。
(附带发现:失败分区 data 为 null、带不出设备编码,所以该判据刻意不比对编码,
靠 store 的 token 纪律保证「失败态必定属于当前那台」;D11 把这个前提钉住了。)
3. 不把接口词汇漏到大屏上。 后端 stateReason(SAMPLE_OUTDATED 这类)
与字段缺失清单(archive.deviceName 这类路径)都是接口契约词汇。
票 05 的真机走查已经逮到过一次同类泄漏,所以:认得出的原因翻人话、
认不出的回空串;缺失只报条数,字段路径进 title(排查拿得到,值班人员看不到)。
两条都有单测 + 组件静态守卫(F4b/F4c),并且都做了变异测试确认守卫有牙
(F4b 第一版只查引号字面量,会放过模板里的裸文本,已修)。
逻辑闸门 61/61(verify/t11-equipment-detail.mjs,六段:分区映射、视图层、store 取数与竞态、
分区独立、图片单值、静态守卫)。真机走查 29/29(verify/t11-equipment-detail-walkthrough.mjs,
五组见下)。回归 12 个既有逻辑闸门全绿
(t01 / t04 / t05 188 / t06 / t07 39 / t08 / t09 38 / t10 57 / t21 / t21-view / t29 / t30)。
t05 的静态守卫面已扩到本票新增的两个组件与一个 domain —— 不补进去,新代码就是检查不到的荒地。t10 的 C4 按本票改写了:它原本断言 「进第二级不取数」,那正是票 10 延期给票 11 的接缝,本票接手后该断言不再成立(已改为断言按编码取数)。
node .scratch/cockpit-shell-panel-layout/verify/t11-equipment-detail-walkthrough.mjs
五组,都是从 DOM 读分区内容(证明界面真画出来了,而不是 store 里有值):
0m³/h 没被当成缺失);预警/报警各 1 条、等级 2 级/1 级;
工单 WO-2026090200001=处理中 · 故障维修;图片分区恰好 1 张 <img>;打开详情不改动地图标记数
(票 10 的常驻语义不回退);整卡可见文本零内部代号零字段路径;无页面错误。empty、各自说「当前没有数据」,且没有伪造的 0 / 空行 / 空图。stale(retained=true)仍画出 2 条读数,说的是「数据已过期」而非「无数据」,
且不牵连档案块(照常 success 14 行)。场景注入用 __COCKPIT_MOCK_ONLY__ 把异常样例只施加在聚合端点,地图与点位摘要保持 success ——
否则全局 empty 会把地图清空、没有可点的标记,走查会退化成「点不到东西」的假失败。
设备详情六个分区按设备编码加载:取数收进 selectDevice/openDevice 两个唯一进级入口
(「进了设备级却没取数」结构上不可能发生),竞态由 token 纪律收口(已答过的同一台不再重取、
失败仍可重试、迟到响应丢弃);分区视图是纯函数层,前端只画后端给的那一张图、只报缺失条数、
把接口代号翻成人话;「整包失败」与「分区级失败」严格分开 —— 前者说一句实话并给重试入口,
后者才逐块渲染,所以「任一分区失败不遮盖其它分区」在真机上是可验证的形态而不是口号。
已认领(Status: claimed)。本票无 needs-triage 标签、Blocked by: 10 且票 10 已 resolved,
故不经过诊断阶段,直接进入实施。
票 20 按后端权威枚举 WorkOrderStatusEnum(0–7)对齐了工单状态字典(维护者 2026-09-29 裁定),
本票交付面的行为未变,但两处断言/样例里的码值随之更新(属于"同一份业务词表的消费者跟着改"):
verify/t11-equipment-detail.mjs 的 B7-work-order-state-from-dictionary:
入参码 4 → 3(都要落在「处理中」上;旧码在新字典里是「待验收」);本票逻辑闸门复跑 61/61 通过,无功能回退。