/** * panelId → 视图计划构造函数的**唯一注册表**(票 18)。 * * 为什么要有这一层:票 17 把「四块面板画法相同」兑现成了一个共享渲染层 * (当时叫 `InfrastructurePanel.vue`,票 18 改名 `PanelBlocks.vue`),但它当时自己 * 按 `panelId` 去问 `domain/infrastructurePanels.js` 要视图 —— 那个模块的名字与职责 * 都只属于**基础设施模式**。票 18 接进运行监测模式时,若组件继续直接引用它, * 组件就从"通用块渲染"退化成"基础设施专属",票 19/20 接进来时还会再长出第三个入口。 * * 所以分发收在**这一处**:每个模式各自拥有自己的 `domain/*Panels.js`(领域 + 视图计划), * 本文件只把它们注册到一起;组件吃**算好的 `view`**,不再认识任何模式。 * * 与票 17 的分工一字不变(本票不发明新形状): * · 组件只按 `block.kind` 分派画法,不认识后端字段、不认识 panelId; * · 「哪个面板有哪些块」归各模式的 domain 模块(纯函数,可脱浏览器断言)。 * * 本文件是纯函数层:不 import service、不碰 DOM。 */ import { withFilterCaliber } from './filterCaliber.js' import { INFRASTRUCTURE_PANEL_BUILDERS } from './infrastructurePanels.js' import { MONITORING_PANEL_BUILDERS } from './monitoringPanels.js' import { WARNING_RESPONSE_PANEL_BUILDERS } from './warningResponsePanels.js' import { OVERVIEW_PANEL_BUILDERS } from './overviewPanels.js' /** * 全部已实现面板的视图构造器。 * * panelId 扁平且全局唯一(ADR 0003),所以合并不会撞键;万一将来撞了, * 应当是**立刻可见的错误**而不是静默覆盖 —— 下面 `assertRegistry()` 会在模块加载时拦住。 */ const BUILDERS = { ...OVERVIEW_PANEL_BUILDERS, ...INFRASTRUCTURE_PANEL_BUILDERS, ...MONITORING_PANEL_BUILDERS, ...WARNING_RESPONSE_PANEL_BUILDERS, } /** 合并前逐键检查:两个模式不得注册同一个 panelId(撞了说明目录或命名出错)。 */ function assertRegistry() { const groups = [OVERVIEW_PANEL_BUILDERS, INFRASTRUCTURE_PANEL_BUILDERS, MONITORING_PANEL_BUILDERS, WARNING_RESPONSE_PANEL_BUILDERS] const seen = new Set() const duplicates = [] for (const group of groups) { for (const panelId of Object.keys(group)) { if (seen.has(panelId)) duplicates.push(panelId) seen.add(panelId) } } if (duplicates.length) { throw new Error(`panelId 在模式注册表里重复:${duplicates.join(', ')}`) } } assertRegistry() /** 这个 panelId 有没有接真实视图(没有的仍是占位面板,占位文案是"哪几块还没接数据"的信号)。 */ export function hasPanelView(panelId) { return Object.prototype.hasOwnProperty.call(BUILDERS, panelId) } /** 已实现的面板 id 清单(外壳据此决定走真面板还是占位)。 */ export function implementedPanelIds() { return Object.keys(BUILDERS) } /** * 算一个面板的视图计划。 * * `data` 为 null(失败/空/还没取数)时**仍然返回结构完整的视图**(块为空)—— * 「失败」不该让整块面板消失,那会连面板标题都没了(票 17 的立场,本票沿用)。 * * @param {string} panelId 目录键 * @param {object|null} data 分区里的领域对象 * @param {object} section 分区(`domain/sectionState.js` 的 `section()`) * @param {object} [extra] 额外渲染输入(票 18 的台账徽标走这里;别的面板不需要) * @returns {object|null} 视图计划;panelId 没接数据时回 null(调用方画占位) */ export function panelView(panelId, data, section, extra) { const build = BUILDERS[panelId] if (!build) return null // 票 67:全局筛选(当前范围/行业)**生效到哪里、没生效到哪里**由 domain/filterCaliber.js // 一张表说了算,在这里统一并进视图的 `gap`(口径缺口)。放在注册表这一处而不是逐面板 // 各写一句:面板新增/改名时不会漏,闸门也能按 `implementedPanelIds()` 做正向普查。 return withFilterCaliber(build(data, section, extra), panelId) }