panelViews.js 4.2 KB

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