46-mode-layer-visibility.md 10 KB

46: 一级模式图层计划与选择态矩阵

Type: implementation Status: resolved Blocked by: 42 Labels: ready-for-agent

What to build

按规格 18 建立唯一的模式图层计划纯函数(建议 domain/modeLayerPlan.js),统一输出当前模式的 markers、blocked、legend、locateTargets、degradedNotes。引导态和四个一级模式使用已裁决矩阵;地图引擎和组件只消费计划,不做第二次过滤。一级模式切换保持 Viewer、选择身份和快照;总览与分项切换暂停/恢复选择,分项之间清空。

Acceptance

  • 引导态不显示业务标记、图例、计数、详情,但允许预取。
  • 以注入的可绘制对象验证完整矩阵:总体概览可见监测点、未关联设备、选中点的有效关联设备、设施点和管线;基础设施仅设施点和管线;运行监测为监测点、未关联设备、有效关联设备和管线;预警处置为异常点、预警/报警未关联设备、关联设备中 ALARM/WARNING 和管线。实际设施/管线源未就绪时只验计划与诚实降级,不宣称这些图层已落地。
  • 异常点只依据设备状态事实回联,不借用监测点聚合状态;跨端点不同步如实提示。
  • 设施或管线数据未就绪显示 degraded note,不伪装为空;设施点不污染监测点计数/图例;事件只回联定位不建独立标记。
  • 管线四模式常驻;地面/管线视图只改变相机和地表/瓦片透明度,不控制管线层显隐。
  • 图例与实际模式标记同源,摘要计数不含关联设备,设备图例独立。
  • 总览↔分项保留选择并按可见性暂停/恢复;分项↔分项清空;模式切换不换快照、不重建 Viewer、不动镜头。
  • 纯逻辑覆盖五态矩阵、异常判定、图例一致性、引导门控、降级说明和回联目标;外壳走查覆盖模式切换、图例和引导态。

Out of scope

  • 设施点拾取/详情/视觉词表、管线数据源、设备图层底层生命周期、设备状态特效和重叠展开。

进度:100%(2026-09-30 收口;2026-09-30 补回归修复)

已完成并关闭。 领域断言 29/29、变异 8/8 全部打红、真机走查 10/10(连跑两次稳定)、 回归走查 5/5(人工走查暴露的缺陷修复后固化),全量零依赖回归绿(t04/t28 两处既有红与本票无关)。

人工走查后补修一处真缺陷(2026-09-30):票 45 人工走查用默认样例(现网边界)时, 点监测点点位摘要里的设备卡片 → 摘要面板直接消失。根因在 modeLayerPlan.selectionVisible() 的设备级判定要求 row.placement === 'drawn',而默认样例设备统一 UNVERIFIED_COORDINATE/no-location ⇒ 设备级选择被判「不可见」⇒ 外壳 <aside v-if="… selectionVisibleInMode"> 卸载。设备级详情 (档案/指标/预警等分区)与该设备能否上图无关,一台无地图位置的设备仍要看它的详情分区。 修法:设备级选择只要在 associatedDevices 里找到该设备行,就按模式可见性判(预警处置只显示 报警/预警),不再要求 placement === 'drawn'。已补 F6-no-location-device-detail-visible(纯逻辑) 与 verify/t46-no-location-detail-walkthrough.mjs(5/5,真实鼠标点设备卡片路径)钉住。

交付物

  • domain/modeLayerPlan.js(新):「一张图什么时候显示什么」的唯一领域接缝(规格 18)。 planModeLayers() 输入 { mode, objects, abnormalDevices, facilities, pipelines, associatedDevices, selection, entered }, 输出 { markers, blocked, legend, byStatus, locateTargets, degradedNotes, facilities, pipelines, associatedDevices, layerStates, selection };另有 planModeSelectionTransition()(总览↔分项保留 / 分项↔分项清空)与 isAssociatedDeviceVisible()(关联设备模式判据的唯一住址)。
  • services/mapService.js:syncPointMarkersForSummary(objects) 改为 syncPointMarkersForPlan(plan) —— 服务层只消费计划,不再自己调 planPointMarkers 重判(规格:引擎和组件不做第二次过滤)。
  • components/MapCanvas.vue:接收外壳算好的 plan prop;图例/降级说明直接渲染计划输出; 引导态门控图例(v-if="entered")与计数浮标;新增 data-map-degraded-note 走查断言面。
  • domain/deviceLayer.js:isVisibleInMode() 改为别名转发到 modeLayerPlan.isAssociatedDeviceVisible (判据唯一住址;t42 既有断言入口不变)。
  • stores/deviceLayerStore.js:syncForSelection({ entered }) —— 引导态关联设备图层整体撤下(数据仍可预取)。
  • CockpitShell.vue:外壳是唯一计算计划的地方(握有 mapStore/设备行/异常设备/选择态全部输入); modeLayerPlan computed 一次计算,MapCanvas/详情浮层只消费;模式 watcher 执行 planModeSelectionTransition 的结论(分项↔分项 closeDetail(),唯一的关闭入口); modeAbnormalDevices 来自 /monitoring/situation(运行概况分区的 abnormalEquipment[]), 未取到时传 null(计划输出降级说明,不猜异常点);introVisible watcher 联动设备层重算。
  • verify/t46-mode-layer-plan.mjs(新,29/29,含回归 F6-no-location-device-detail-visible)+ 变异 8/8 全打红; verify/t46-mode-layer-walkthrough.mjs(新,10/10); verify/t46-no-location-detail-walkthrough.mjs(新,5/5,默认样例点设备卡片面板不消失)。

关键取舍(都来自已 resolved 的规格 18 / 决策票 13,不在本票发明)

  1. 计划只算一次、在外壳:MapCanvas 不再 import 计划函数(避免"顺手自己再过滤一遍"的诱惑), 它只消费 plan prop。服务层入参从 objects 改为 { markers, byStatus },模式裁决无第二条路径。
  2. 数据可用性先于模式可见性(票 42 的既有纪律):关联设备的 classifyDevice 先判 pending/inconsistent/no-location/unavailable/terrain-pending,计划层只对已 drawn 的行做模式过滤。
  3. 异常点只认设备状态事实:WARNING/ALARM 之外的 WORK_ORDER/OFFLINE/UNKNOWN 不构成异常点 (A6 钉住);异常设备数据未就绪时预警处置不画任何点并给 abnormal-devices-unready 降级说明 (A7:不猜、不借用点位聚合状态)。
  4. degraded ≠ hidden ≠ empty:设施/管线契约未交付传 null → degraded + 降级说明; 契约就绪(哪怕空数组)→ ready;模式不显示 → hidden。管线未就绪在四模式都是 degraded 而非 hidden(A8b:常驻 ≠ 数据就绪才常驻,不把缺数据伪装成"该模式没有对象")。
  5. 设备级选择优先看设备:设备级选择保留 pointCode 供"返回点位"用,判据若先查 pointCode 会把"设备被本模式隐藏"误判成"点位可见 ⇒ 显示"(本票自查抓到,见下)。
  6. 详情暂停 = 隐藏但保留选择身份:selection.visible=false 时浮层隐藏(v-if 摘除), selection 状态一个字节不动;切回目标模式可见时自动恢复——恢复就是"计划重新判定为可见", 不需要额外状态机。

本票自查抓到并修掉的 2 处真问题

  1. 设备级选择的可见性判据顺序错误:初版 selectionVisible() 先查 pointCode 再查 deviceCode —— 设备级选择带着 pointCode(返回点位要用),于是"选中的 NORMAL 设备在预警处置应暂停" 被判成"其点位可见 ⇒ 显示"(F4 逮到)。修法:设备级优先看设备,点位级才看点位标记。
  2. 走查 W6 曾是恒真空转:初版把"降级可见性"写在受控有效坐标的第一趟(两台设备都上图, 走不到降级分支),blockedRows.length === 0 ? true : … 恒真却显示 PASS。修法:另跑一趟 默认样例(现网边界 renderable=false),要求降级行确实存在。

真机走查(已执行,10/10,连跑两次稳定)

驱动:verify/cdp-driver.mjs(票 44 交付的零依赖 CDP 驱动;本机 puppeteer-core 仍缺失), document.hidden === false(Cesium 真实渲染)。

  • W0 引导态门控:进入前图例 0 行、标记计划 0(数据可预取)——外壳 introVisible → 计划 entered=false 的门控链路真实生效;
  • W1–W2:进入系统后总览上图 2(device-layer 受控样例的两个监测点);
  • W3 基础设施:标记计划 0、上图 0(监测点/未关联设备/关联设备全隐藏;图例 8 行是票 09 的 "0 不隐藏行"既有契约,矩阵的落点在标记计划);
  • W4 运行监测:上图 2,与总览一致;
  • W5 预警处置:标记计划 0 + pipelines-unready 降级说明(该样例 operationOverview 未取数 ⇒ 异常设备数据未就绪 ⇒ 不猜异常点,如实降级);
  • W6 图例与标记同源:标记 2、图例 8 行(含 0 计数行),形状 2 种;
  • W7–W8 快照与 Viewer:四模式切换后快照标记逐字节一致、引擎全程 ready(不换快照、不重建 Viewer);
  • W10:全程无 pageerror / console.error。

与下游票的边界(不在本票实现)

  • 票 45:重叠设备的聚合符号、展开环与逐台拾取。
  • 票 47:状态特效;并请明确 deviceStatus.js 的 deviceLegend() 去留(票 44 移交)。
  • 设施点/管线数据契约:计划已就位(facilities/pipelines 入参 + degraded 说明), 数据到位后只需把真实集合传进 planModeLayers,无需再动矩阵。

回归

零依赖闸门 35 道全绿(含本票新增 t46;t10 的 F5/F7 守卫按票 46 结构更新 —— 意图不变:planPointMarkers 唯一调用点在计划层、MapCanvas 只喂计划、浮层仍是 main 兄弟节点)。 两处红与本票无关:① t28-fill-container.mjs 需 puppeteer(模块已不在本机); ② t04-config.mjs 的「不该硬编码后端地址」指向 services/cockpitApi.js(并发会话正在改)。

按 AGENTS.md:本会话未跑 build;yarn dev 未由我启动(8000 端口已有服务在跑)。 浏览器仅用于本次走查(维护者授权的提权通道,同票 44)。