eventStatusVocab.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /**
  2. * 预警/报警**状态词表**(票 71 返工,维护者 2026-10-29 第 1 条反馈)。
  3. *
  4. * ── 为什么必须分族,不能共用 ──
  5. * 维护者原话:「预警和报警状态是不一样的,不能完全通用(样式可以通用)」。
  6. * 后端口径逐条核实过,两族是**两套互不相通的生命周期**:
  7. *
  8. * 预警(`WarningWorkflow` / `EarlyWarningStatusEnum`,六态,线性推进):
  9. * DRAFT 草稿 → PENDING 待接收 → RECEIVED 已接收 → PROCESSING 处置中
  10. * → HANDLED 已处置 → CLOSED 已解除(**唯一终态**)
  11. * 后端注释原句:「已处置仍需核查,只有『已解除』才是终态」。
  12. *
  13. * 报警(`alarm_data.alarm_status` 经后端归一,二态):
  14. * UNHANDLED 未处理 → HANDLED 已处理(处置手段是工单)
  15. * 见 `BusinessCockpitEventAdapter` / `CockpitOverviewWarningSettingsService`。
  16. *
  17. * 共用一张表的实际后果(返工前就踩了):
  18. * · `PENDING` 在预警里是「待接收」,旧表却译成「待处理」—— 同一屏两种含义;
  19. * · 报警的 `UNHANDLED`/`HANDLED` 在旧表里**根本没有词条**,直接漏英文码上大屏;
  20. * · 预警的 RECEIVED/PROCESSING/CLOSED 同理漏码。
  21. *
  22. * ── 共用的是**形状**,不是词条 ──
  23. * 两个族的词表都走同一个 `statusVocabulary()` 构造函数(同一套字段:label/tone/terminal/
  24. * unhandled),所以「怎么画一个状态标签」只有一份实现;而「某个码是什么意思」各族自述。
  25. * 认不出的机器状态显示「状态未知」;协议码仍保留在领域数据中,不编造正常状态。
  26. */
  27. /** 状态标签的语义色键(沿用 `statusTones.js` 的四档,不新造色)。 */
  28. const TONE = { PENDING: 'lv2', ACTIVE: 'lv3', DONE: 'lv4', NEUTRAL: 'neutral' }
  29. /**
  30. * 列表状态徽标高亮色(维护者 2026-11-13 指定):
  31. * 待接收/已接收=黄、处置中=蓝、已处置=紫、已解除=绿。
  32. *
  33. * 与语义色键 `tone` 分开:`tone` 服务标签点/图例的中性语义,已被其它面板消费;
  34. * 这里是「设备详情预警列表」的状态高亮口径,改它不动别处。紫色无既有语义色键,
  35. * 由 tokens.css 的 --status-handled 单源给出。
  36. */
  37. export const STATUS_DISPLAY_TONE = {
  38. WARNING: {
  39. PENDING: 'lv2',
  40. RECEIVED: 'lv2',
  41. PROCESSING: 'lv3',
  42. HANDLED: 'handled',
  43. CLOSED: 'lv4',
  44. },
  45. ALARM: {
  46. UNHANDLED: 'lv2',
  47. HANDLED: 'lv4',
  48. },
  49. }
  50. /** 某族某状态的高亮色键;认不出回中性(不猜成某个真实态的颜色)。 */
  51. export function statusDisplayTone(family, status) {
  52. const key = String(family || '').trim().toUpperCase()
  53. const code = String(status || '').trim().toUpperCase()
  54. const table = STATUS_DISPLAY_TONE[key]
  55. return (table && table[code]) || 'neutral'
  56. }
  57. /**
  58. * 预警六态(`EarlyWarningStatusEnum`,码与中文名逐条照抄后端枚举)。
  59. *
  60. * `terminal` 只标 **CLOSED**:HANDLED(已处置)仍需核查,不是终态 ——
  61. * 这是后端注释明写的口径,也是「处置数」与「已解除数」必须分开算的依据。
  62. */
  63. export const WARNING_STATUS = {
  64. DRAFT: { label: '草稿', tone: TONE.NEUTRAL, terminal: false, unhandled: true },
  65. PENDING: { label: '待接收', tone: TONE.PENDING, terminal: false, unhandled: true },
  66. RECEIVED: { label: '已接收', tone: TONE.ACTIVE, terminal: false, unhandled: false },
  67. PROCESSING: { label: '处置中', tone: TONE.ACTIVE, terminal: false, unhandled: false },
  68. HANDLED: { label: '已处置', tone: TONE.ACTIVE, terminal: false, unhandled: false },
  69. CLOSED: { label: '已解除', tone: TONE.DONE, terminal: true, unhandled: false },
  70. }
  71. /** 报警二态(后端归一后的取值)。 */
  72. export const ALARM_STATUS = {
  73. UNHANDLED: { label: '未处理', tone: TONE.PENDING, terminal: false, unhandled: true },
  74. HANDLED: { label: '已处理', tone: TONE.DONE, terminal: true, unhandled: false },
  75. }
  76. /** 族 → 词表。认不出的族回空表(调用方据此只说原码,不冒充)。 */
  77. const VOCABULARY_BY_FAMILY = {
  78. WARNING: WARNING_STATUS,
  79. ALARM: ALARM_STATUS,
  80. }
  81. /**
  82. * 某一族的**状态词表**(唯一入口;组件与卡片都不直接认码)。
  83. *
  84. * @param {string} family `WARNING`/`ALARM`(大小写不敏感)
  85. * @returns {object} 码 → `{label, tone, terminal, unhandled}`;族认不出回空对象
  86. */
  87. export function statusVocabulary(family) {
  88. const key = String(family || '').trim().toUpperCase()
  89. // 返回浅拷贝:调用方只读,但也不该拿到能改到这张表内部的原对象。
  90. return { ...(VOCABULARY_BY_FAMILY[key] || {}) }
  91. }
  92. /**
  93. * 该族里「已了结」的状态码集合(算**处置数**用)。
  94. *
  95. * 预警只有 CLOSED;报警只有 HANDLED。用集合而不是单个字符串,
  96. * 是因为将来任一族加终态时改这一处即可,不用去改算法。
  97. * @param {string} family
  98. * @returns {string[]}
  99. */
  100. export function terminalStatuses(family) {
  101. return Object.entries(statusVocabulary(family))
  102. .filter(([, meta]) => meta.terminal === true)
  103. .map(([code]) => code)
  104. }
  105. /**
  106. * 状态码 → 展示文本。
  107. *
  108. * 三条判定顺序(与 `localizeCodeText` 同一条思路):
  109. * 1. 本族词表命中 → 中文;
  110. * 2. 已发布中文保留;未登记机器码显示「状态未知」;空字段留空,不造状态。
  111. *
  112. * @param {string} family `WARNING`/`ALARM`
  113. * @param {string} status 后端状态码
  114. */
  115. export function statusText(family, status) {
  116. const code = typeof status === 'string' ? status.trim() : ''
  117. if (!code) return ''
  118. const vocabulary = statusVocabulary(family)
  119. const meta = Object.hasOwn(vocabulary, code.toUpperCase()) ? vocabulary[code.toUpperCase()] : null
  120. return meta ? meta.label : /\p{Script=Han}/u.test(code) && !/\p{Script=Latin}/u.test(code) ? code : '状态未知'
  121. }
  122. /**
  123. * 状态码 → 语义色键(标签必须带文字,色只是辅助 —— 票 06 的纪律)。
  124. * 认不出的码走中性灰,**不猜**成「处理中」。
  125. */
  126. export function statusTone(family, status) {
  127. const code = typeof status === 'string' ? status.trim() : ''
  128. if (!code) return 'neutral'
  129. const meta = statusVocabulary(family)[code]
  130. return meta ? meta.tone : 'neutral'
  131. }
  132. /** 该状态是不是本族的「已了结」终态(算处置数用)。 */
  133. export function isTerminalStatus(family, status) {
  134. const code = typeof status === 'string' ? status.trim() : ''
  135. return code !== '' && terminalStatuses(family).includes(code)
  136. }
  137. /** 该状态是不是本族的「还没人接手」(未处理/待接收一类)。 */
  138. export function isUnhandledStatus(family, status) {
  139. const code = typeof status === 'string' ? status.trim() : ''
  140. if (!code) return false
  141. const meta = statusVocabulary(family)[code]
  142. return !!(meta && meta.unhandled === true)
  143. }