11-equipment-detail-aggregate.md 8.2 KB

11: 设备详情聚合与分区状态

What to build: 通过设备详情聚合契约加载设备档案、最新指标、预警、报警、工单状态和最新图片。各分区可独立成功、失败、为空或过期。

Blocked by: 10

Status: resolved Labels: ready-for-agent

  • 设备详情按设备编码加载。
  • 档案、指标、预警、报警、工单和图片分区独立渲染。
  • 任一分区失败时不遮盖其他成功分区。
  • 空数据、字段缺失和过期状态均有明确 UI。
  • 最新图片只展示后端指定的一张。
  • 有分区状态映射单测。

进度:100%

已完成并关闭(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 的接缝,本票接手后该断言不再成立(已改为断言按编码取数)。

真机走查(29/29,2026-09-18)

 node .scratch/cockpit-shell-panel-layout/verify/t11-equipment-detail-walkthrough.mjs

五组,都是从 DOM 读分区内容(证明界面真画出来了,而不是 store 里有值):

  • A 成功态(12 项):选卡片进设备级且表头编码 = 选中设备;六块分区按序画出、各带自己的状态标签; 档案 14 行含设备名;指标 2 条含真 0 读数(0m³/h 没被当成缺失);预警/报警各 1 条、等级 2 级/1 级; 工单 WO-2026090200001=处理中 · 故障维修;图片分区恰好 1 张 <img>;打开详情不改动地图标记数 (票 10 的常驻语义不回退);整卡可见文本零内部代号零字段路径;无页面错误。
  • B 分区级部分失败(6 项):档案/工单/图片三块 failed 且措辞明确,指标 2 条 / 预警 1 条 / 报警 1 条 照常画出真实内容,浮层没白屏也没自动关。B5 专门断言「恰好 3 坏 3 好」, 否则 B3「好块仍在」会变成恒真。
  • C 空数据(3 项):六块全 empty、各自说「当前没有数据」,且没有伪造的 0 / 空行 / 空图。
  • D 过期(4 项):指标块 stale(retained=true)仍画出 2 条读数,说的是「数据已过期」而非「无数据」, 且不牵连档案块(照常 success 14 行)。
  • E 整包失败(4 项):说一句实话(六块都没数据)而不是逐块谎报「其它分区不受影响」; 给出「重新加载」入口;浮层仍在、表头仍带设备编码。

场景注入用 __COCKPIT_MOCK_ONLY__ 把异常样例只施加在聚合端点,地图与点位摘要保持 success —— 否则全局 empty 会把地图清空、没有可点的标记,走查会退化成「点不到东西」的假失败。

Answer

设备详情六个分区按设备编码加载:取数收进 selectDevice/openDevice 两个唯一进级入口 (「进了设备级却没取数」结构上不可能发生),竞态由 token 纪律收口(已答过的同一台不再重取、 失败仍可重试、迟到响应丢弃);分区视图是纯函数层,前端只画后端给的那一张图、只报缺失条数、 把接口代号翻成人话;「整包失败」与「分区级失败」严格分开 —— 前者说一句实话并给重试入口, 后者才逐块渲染,所以「任一分区失败不遮盖其它分区」在真机上是可验证的形态而不是口号。

Comments

2026-09-18 认领与阶段判定

已认领(Status: claimed)。本票无 needs-triage 标签、Blocked by: 10 且票 10 已 resolved, 故不经过诊断阶段,直接进入实施。

2026-09-29 跨票同步(由票 20 触发,非缺陷)

票 20 按后端权威枚举 WorkOrderStatusEnum(0–7)对齐了工单状态字典(维护者 2026-09-29 裁定), 本票交付面的行为未变,但两处断言/样例里的码值随之更新(属于"同一份业务词表的消费者跟着改"):

  • verify/t11-equipment-detail.mjs 的 B7-work-order-state-from-dictionary: 入参码 4 → 3(都要落在「处理中」上;旧码在新字典里是「待验收」);
  • 断言措辞「按七态字典补」→「按状态字典(后端 0–7)补」。

本票逻辑闸门复跑 61/61 通过,无功能回退。