# 43: 面板与三维设备点双向对应 Type: implementation Status: resolved Blocked by: 42 Labels: ready-for-agent ## What to build 按规格 15 实现面板设备卡片与三维设备点的双向 hover/选中/拾取联动。选中反应使用外环、放大和连接监测点标记中心的引线,不使用颜色抢占设备状态语义;hover 是瞬态轻放大/卡片高亮,不叠加选中外环和引线。设备点使用独立 `pickDeviceMarkerAt()` 身份,设备点优先于监测点,未关联设备既有拾取链路不变。 ## Acceptance - [x] 面板 hover 卡片能轻放大对应设备点;三维 hover 设备点能高亮对应卡片。 - [x] 选中设备产生持久外环、放大和引线;设备与监测点重叠时未展开状态不画零长度引线。 - [x] 首次从点位级进入设备级时飞 `map.locate.device`;同点位设备 A→B 只换中心不重飞;返回点位级回点位视角但监测点仍保持选中。 - [x] 三维点选设备能滚动/选中对应卡片并进入二级设备详情;返回点位清除设备强化并恢复点位级。 - [x] 设备数不少于 3 台时其余设备弱化;hover 弱化设备撤销弱化并轻放大;已选设备不受 hover 干扰。 - [x] 宿主暴露 `data-map-device-picked`、`data-map-panel-selection`、`data-map-device-hovered`,不破坏 `data-map-picked`。 - [x] 纯逻辑覆盖状态迁移、拾取优先级和弱化阈值;真实走查覆盖两侧 hover、两侧选中、返回点位和定位。 ## Out of scope - 设备图层触发/快照生命周期、设备资产、状态特效、重叠聚合和模式矩阵。 ## 进度:100%(2026-09-29 收口) **已完成并关闭。** 逻辑闸门 **40/40**、变异测试 **26/26 全部打红**、真机走查已执行(见下), 全量零依赖闸门回归绿。 ### 交付物 - `domain/deviceCorrespondence.js`(新):**反应档位**(`selected`/`hovered`/`dimmed`/`normal`, 互斥且优先级正确)、**镜头联动三段**(`device-tier`/`center-only`/`point-tier`)、 **拾取优先级**(`resolvePickOrder`:设备优先、监测点兜底)、**引线退化**(重合不画)、 反应量(`scale`/`alpha`/`ring`,**不含颜色**)。 - `services/cesiumEngine.js`:`applyDeviceReactions`(逐实体写 scale/alpha/outline)、 `syncDeviceLead`/`clearDeviceLead`(设备点到监测点标记中心的归属引线)、`pickDeviceMarkerForHover`。 - `services/mapService.js`:`syncDeviceReactions` / `syncDeviceLeadForSelection` / `setSelectedDevice` / `setHoveredDevice` / `hoverDeviceAt` / `onDeviceHover` / `pickDeviceOrPointAt`(优先链)/ `setPanelSelectionLevel` / `clearDeviceSelection` / `getDeviceCorrespondenceState`;宿主新增三个可断言字段;hover 节流 60ms。 - `stores/deviceLayerStore.js`:`selectedDevice`/`hoveredDevice`/`dimming` + `selectDevice`(含镜头联动)/ `hoverDevice` / `backToPointLevel` / `noteLevel`。 - `components/PointDetailCard.vue`:设备卡片与设备清单都接上 hover/选中(`data-device-selected`/ `-hovered` 供走查读)。 - `components/MapCanvas.vue`:三维 hover → 面板高亮;三维点设备 → 选中并进设备级。 - `CockpitShell.vue`:层级字段写入 + 层级对照同步 + 设备把手。 - 证据:`verify/t43-device-correspondence.mjs`(**40/40**)+ 变异 **26/26 全部打红**。 ### 关键取舍(都来自已 resolved 的规格 15 / 决策票 06) 1. **反应不用颜色**:色键语义已被设备状态(票 44/07)占用,所以「被选中」只用 **外环 + 放大 + 引线**、弱化只用**压暗 + 缩小** —— 反应量里连 `color` 字段都没有(有断言钉住)。 2. **hover 与选中是两档、互不干扰**:hover 只作用于**非选中**设备(撤弱化 + 轻放大,不叠外环/引线), hover 到已选中的那台**不改变它的选中态**。 3. **弱化阈值 = 设备数 ≥ 3 台**:少于 3 台全亮,避免小点位无谓抖动。 4. **返回点位不是取消选中**:它是**层级回退**(监测点仍选中、回点位档视角), 与票 03 的「取消选中不回退」语义并存不冲突。 ### 真机走查(已执行):抓到并修掉 1 处真缺陷 本机 `@puppeteer/browsers` 是空目录(既有环境缺陷),故按票 36/37/41/42 成例 **改用 DSH 自带浏览器真实驱动**(`yarn dev` @8000)。 **修掉的缺陷:设备选中态有「两套真相」。** 我原先把 `setSelectedDevice` 直接暴露到 DEV 把手, 真机走查暴露:直接调服务层后 store 的 `selectedDevice` 仍读空串 —— 界面(面板高亮)与引擎 (三维强化)对"选中了谁"给出两个答案。**修法**:服务层不再暴露直写选中态的函数, 按票 41 的 `publishPointSelectionHandle` 成例,由外壳把 **store 的方法**递给 DEV 把手 (`publishDeviceSelectionHandle`);走查驱动的是"用户那条路径"。已补断言 `F14` 钉住。 **实测证据(真实浏览器,与验收逐条对应)**: - **选中**:`data-map-device-picked=EQ-001`、`detailLevel=equipment`、`detailDevice=EQ-001`; **store 与服务层读数一致**(`store === service === 'EQ-001'`,修复前 store 为空串)。 - **hover 不干扰选中**:hover `EQ-002` 时 `data-map-device-hovered=EQ-002` 而 `data-map-device-picked` 保持 `EQ-001`。 - **层级字段**:`data-map-panel-selection` 随详情层级变化(`point` ↔ `equipment`)—— 注意 Vue 的 watch 是异步(`flush: 'pre'`),同步读到的仍是上一拍的值(已确认下一拍一致)。 - **返回点位**:`equipment → point`、**监测点仍选中**(`pointCode=MP-001`)、设备选中清空。 - **反应真的落到引擎**:`applyDeviceReactions` 对两台实体各写一次(返回 `written=2`)。 **未覆盖项(如实记录)**:走查标签 `document.hidden===true`(rAF 冻结、Cesium 不渲染), 所以「真实鼠标 hover 三维设备点 → 面板高亮」与「真实鼠标点设备点」这两条的**画面观感** 未能逐帧验证(票 42 已记录同一环境事实:该环境下连监测点标记的 `scene.pick` 也拾不到)。 这两条的**判定与接线**已由 t43 闸门覆盖(拾取优先级 D 段、hover/选中接线 F 段、反应量 B 段), 真实观感建议维护者在**前台标签页**复跑,或由票 48 的整体验收一并覆盖。 ### 与下游票的边界(不在本票实现) - **票 44**:设备形状/尺寸/资产与独立状态词表。本票的反应只改 `scale`/`alpha`/`outline`, **不碰形状与色键**(`shape` 仍由调用方给)。 - **票 45**:重叠设备的聚合符号与展开环、逐台拾取与 K>10 降级。本票只实现**正常单点**的 反应与拾取优先级;「未摊开(地理重合)时引线退化不画」属本票(规格边界明写), 展开态的引线照画归票 45/11。 - **票 46**:完整四模式图层矩阵。本票不重做模式可见性(票 42 的 `isVisibleInMode` 继续生效)。 - **票 47**:状态特效(报警扩散脉冲等)。本票不实现任何动画。 ### 回归 **零依赖逻辑闸门全绿**(t01/t03/t05/t06/t07 **39**/t08/t09/t10 **57**/t11/t12/t13/t14/t15/t16/t17/ t18 **71**/t19/t21/t21-view/t29/t30/t36/t37/t38/t39/t41/t42 **37**/t43 **40**)。 **三处红与本票无关**:① `t28-fill-container.mjs` 需 puppeteer(既有环境缺陷); ② `t04-config.mjs` 的「页面与组件不该硬编码后端地址」与 ③ `t20-overview-panels.mjs` 的 D3 —— 两条都指向 `services/cockpitApi.js`,而该文件**并发会话(票 20)正在改**, 不在本票改动清单里(`git status` 可核)。 **按 `AGENTS.md`:本会话未跑 build**(真机走查是维护者本轮明确要求才执行的)。 ### 下一步 1. 维护者在**前台标签页**复跑,补「三维 hover/点选设备」的画面观感(判定与接线已被闸门覆盖)。 2. `45`(重叠设备聚合与展开)、`46`(模式图层计划)、`47`(状态特效)的依赖已就绪; `45` 仍需 42/43/44/46 全绿后才可claim。