/** * 一级模式图层计划(票 46;规格 18 / 决策票 13)。 * * 这是「一张图什么时候显示什么」的唯一领域接缝:不碰 Vue/Cesium/接口,不在组件或引擎 * 二次过滤。设施点、管线和异常设备数据尚未全部具备时,计划如实输出 degradedNotes, * 不把缺数据伪装成空数据,也不伪造设施/管线标记。 */ import { MAP_OBJECT_KIND } from './mapSummary.js' import { planPointMarkers, pointLegend } from './pointStatus.js' export const MODE_LAYER = { OVERVIEW: 'overview', INFRASTRUCTURE: 'infrastructure', MONITORING: 'monitoring', WARNING_RESPONSE: 'warningResponse', } export const MODE_SELECTION = { PRESERVE: 'preserve', CLEAR: 'clear', } const ATTENTION_STATUSES = new Set(['ALARM', 'WARNING']) const MODE_KEYS = new Set(Object.values(MODE_LAYER)) function hasArray(value) { return Array.isArray(value) } function note(key, text, mode) { return { key, text, mode } } /** 关联设备的模式过滤唯一判据;数据可用性仍由 deviceLayer.js 先判。 */ export function isAssociatedDeviceVisible(mode, status) { if (mode === MODE_LAYER.INFRASTRUCTURE) return false if (mode === MODE_LAYER.WARNING_RESPONSE) return ATTENTION_STATUSES.has(status) return true } /** 总览↔分项保留选择;分项↔分项清空选择。 */ export function planModeSelectionTransition({ fromMode, toMode, selection } = {}) { const current = selection || { level: 'none', pointCode: '', deviceCode: '' } if (!fromMode || fromMode === toMode) return { action: MODE_SELECTION.PRESERVE, selection: current } if (fromMode === MODE_LAYER.OVERVIEW || toMode === MODE_LAYER.OVERVIEW) { return { action: MODE_SELECTION.PRESERVE, selection: current } } return { action: MODE_SELECTION.CLEAR, selection: { level: 'none', pointCode: '', deviceCode: '' }, } } function abnormalCodeSet(abnormalDevices) { return new Set( (Array.isArray(abnormalDevices) ? abnormalDevices : []) .filter((item) => item && ATTENTION_STATUSES.has(item.status)) .map((item) => item.code) .filter(Boolean), ) } function abnormalStatusMap(abnormalDevices) { return new Map( (Array.isArray(abnormalDevices) ? abnormalDevices : []) .filter((item) => item && item.code) .map((item) => [item.code, item.status || '']), ) } function isAbnormalPoint(object, abnormalCodes) { return object && object.kind === MAP_OBJECT_KIND.MONITORING_POINT && Array.isArray(object.deviceCodes) && object.deviceCodes.some((code) => abnormalCodes.has(code)) } function visibleMapObjects(mode, objects, abnormalDevices) { const abnormalCodes = abnormalCodeSet(abnormalDevices) const source = Array.isArray(objects) ? objects : [] if (mode === MODE_LAYER.INFRASTRUCTURE) return [] if (mode === MODE_LAYER.WARNING_RESPONSE) { return source.filter((object) => { if (!object) return false if (object.kind === MAP_OBJECT_KIND.MONITORING_POINT) return isAbnormalPoint(object, abnormalCodes) if (object.kind === MAP_OBJECT_KIND.UNASSOCIATED_EQUIPMENT) return abnormalCodes.has(object.code) return false }) } return source.filter((object) => object && ( object.kind === MAP_OBJECT_KIND.MONITORING_POINT || object.kind === MAP_OBJECT_KIND.UNASSOCIATED_EQUIPMENT )) } function planNonPointLayers({ mode, entered, facilities, pipelines, degradedNotes }) { const facilityVisible = mode === MODE_LAYER.OVERVIEW || mode === MODE_LAYER.INFRASTRUCTURE const pipelineVisible = entered && MODE_KEYS.has(mode) const facilityReady = hasArray(facilities) const pipelineReady = hasArray(pipelines) if (facilityVisible && !facilityReady) degradedNotes.push(note('facilities-unready', '设施数据未就绪', mode)) if (pipelineVisible && !pipelineReady) degradedNotes.push(note('pipelines-unready', '管线数据未就绪', mode)) // 票 33:**原样引用,不 slice()**。每 30s freshness tick 会让计划整体重算—— // 以前这里每次 `slice()` 出新数组,`MapCanvas` 的 `deep:false` watcher 靠引用判变化, // 于是「数据没变」也被判成变了,批批触发管线全量重同步(票 26 诊断的放大器之一)。 // 计划的消费者(mapService/MapCanvas)只读不写这两个数组,别名是安全的。 return { facilities: facilityVisible && facilityReady ? facilities : [], pipelines: pipelineVisible && pipelineReady ? pipelines : [], layerStates: { facilities: facilityVisible ? (facilityReady ? 'ready' : 'degraded') : 'hidden', pipelines: pipelineVisible ? (pipelineReady ? 'ready' : 'degraded') : 'hidden', }, } } function selectionVisible({ mode, selection, objects, markers, associatedDevices, abnormalCodes }) { const current = selection || {} if (!current.level || current.level === 'none') return false // 设备级选择**优先看设备**(设备级选择仍保留 pointCode 供"返回点位"用, // 先查 pointCode 会把"设备被本模式隐藏"误判成"点位可见 ⇒ 显示")。 if (current.deviceCode) { // 该设备是**当前点位的关联设备**(在 `associatedDevices` 里找得到): // 详情面板与该设备能否上图无关 —— 一台 `no-location`(后端判不可上图)的设备 // 仍然要看它的档案/指标/预警等分区,所以这里**不要求 placement === 'drawn'**, // 只按模式可见性(预警处置只显示报警/预警)判定。 // (本票真机人工走查抓到:默认现网边界设备统一 `UNVERIFIED_COORDINATE/no-location`, // 点设备卡片后 `selectionVisible=false` ⇒ 外壳 `