Bläddra i källkod

feat(cockpit): 采集记录区分「未判定」并说明原因,不拿白色读数冒充正常

采集契约只给逐格 alarm 布尔值、无阈值/等级时,格子标记为未判定并带可读原因(未配置阈值/缺少采集值/缺少上报状态/上报状态无效);面板底部只在确有未判定项时说明,避免绿色行底被读成合格证明。走查四模式与管线视图无新增网络错误。
ChenJZ 1 dag sedan
förälder
incheckning
ad95c17482

+ 4 - 3
src/modules/cockpit/components/EquipmentDetailSections.vue

@@ -127,8 +127,9 @@
                                         <td
                                             v-if="!recordTableOf(panel).columns[cellIndex].isTime || row.timeRowspan > 0"
                                             data-collection-cell
-                                            :data-reading-state="cell.alert?.state"
-                                            :style="cell.alert ? { '--reading-color': cell.alert.color } : undefined"
+                                            :data-reading-state="cell.assessment?.state"
+                                            :style="cell.assessment ? { '--reading-color': cell.assessment.color } : undefined"
+                                            :title="cell.assessment?.label"
                                             :rowspan="recordTableOf(panel).columns[cellIndex].isTime ? row.timeRowspan : undefined"
                                             :class="{ 'collection-table__time': recordTableOf(panel).columns[cellIndex].isTime, 'collection-table__audio-col': recordTableOf(panel).columns[cellIndex].isAudio }"
                                         >
@@ -167,7 +168,7 @@
                         </table>
                     </div>
                     <p v-if="!recordTableOf(panel).total" class="equipment-section__hint" data-section-empty-list>暂无采集记录</p>
-                    <p v-else class="equipment-section__hint" data-collection-assessment-note>{{ recordTableOf(panel).assessmentNote }}</p>
+                    <p v-else-if="recordTableOf(panel).assessmentNote" class="equipment-section__hint" data-collection-assessment-note>{{ recordTableOf(panel).assessmentNote }}</p>
                     <PagerNav
                         v-if="recordTableOf(panel).total"
                         :page="recordTableOf(panel).page"

+ 39 - 6
src/modules/cockpit/domain/equipmentDetailColors.js

@@ -15,13 +15,46 @@ export function detailEventLevel(event) {
     return { level, tone, color: COLORS[tone], label: level ? family + ' · ' + level + '级' : family + ' · 未分级' }
 }
 
-/**
- * 当前采集契约只发布逐格 alarm 布尔值,无预警/等级/规则配置。
- * 只消费这条明确结论,不拿历史事件阈值或设备总体状态重判每个采集值。
- */
+const UNKNOWN_REASONS = {
+    NO_THRESHOLD: '未配置阈值',
+    THRESHOLD_NOT_CONFIGURED: '未配置阈值',
+    MISSING_VALUE: '缺少采集值',
+    MISSING_STATUS: '缺少上报状态',
+    INVALID_STATUS: '上报状态无效',
+    METRIC_UNMAPPED: '指标未关联规则',
+    COMPOSITE_RULE_REQUIRED: '需频段及连续确认等联合规则判定',
+    NOT_APPLICABLE: '该字段不参与判定',
+    INVALID_VALUE: '采集值无法判定',
+    UNSUPPORTED_METRIC: '该指标暂不支持判定',
+    RULE_UNAVAILABLE: '规则暂不可用',
+    INVALID_THRESHOLD: '阈值配置无效',
+}
+
+/** 后端逐格判定是唯一权威来源;旧版接口仅兼容明确的 alarm=true。 */
+export function collectionCellAssessment(cell) {
+    const assessment = cell?.assessment
+    const status = String(assessment?.status || '').toUpperCase()
+    if (['NORMAL', 'WARNING', 'ALARM', 'UNKNOWN'].includes(status)) {
+        if (status === 'UNKNOWN') return {
+            state: 'unknown', color: 'var(--text-strong, #fff)', alert: null,
+            label: '未判定:' + (UNKNOWN_REASONS[assessment.reason] || '当前规则无法给出结论'),
+        }
+        const state = status.toLowerCase()
+        if (state === 'normal') return { state, color: COLORS.lv4, alert: null, label: '正常' }
+        const level = detailEventLevel({ ...assessment, family: status })
+        const color = state === 'alarm' ? COLORS.lv1 : COLORS.lv2
+        const alert = { state, color, dotColor: level.color, label: level.label }
+        return { state, color, alert, label: alert.label }
+    }
+    if (cell?.alarm === true) {
+        const alert = { state: 'alarm', color: COLORS.lv1, dotColor: COLORS.lv1, label: '报警 · 未分级(采集记录未提供等级)' }
+        return { state: 'alarm', color: COLORS.lv1, alert, label: alert.label }
+    }
+    return { state: 'unknown', color: 'var(--text-strong, #fff)', alert: null, label: '未判定:采集接口未提供判定结果' }
+}
+
 export function collectionCellAlert(cell) {
-    if (cell?.alarm !== true) return null
-    return { state: 'alarm', color: COLORS.lv1, dotColor: COLORS.lv1, label: '报警 · 未分级(采集记录未提供等级)' }
+    return collectionCellAssessment(cell).alert
 }
 
 /** 最新记录按整组最高严重性取色;绿色是无已发布异常标记时的默认强调,不是阈值合格证明。 */

+ 6 - 4
src/modules/cockpit/domain/equipmentDetailView.js

@@ -37,7 +37,7 @@ import { tracksByOrder } from './workOrderTimeline.js'
 import { eventLevelTag, shortEventTime, eventStatusText, eventTypeText, eventNameText } from './eventList.js'
 import { statusVocabulary } from './eventStatusVocab.js'
 import { pagedRows } from './pagination.js'
-import { detailEventLevel, collectionCellAlert, collectionGroupAccent } from './equipmentDetailColors.js'
+import { detailEventLevel, collectionCellAssessment, collectionGroupAccent } from './equipmentDetailColors.js'
 
 const RECORD_TEXT = {
     COD: '化学需氧量(COD)', PH: '酸碱度(pH)', pH: '酸碱度(pH)',
@@ -119,10 +119,11 @@ export function collectionRecordTable(data, page = 1) {
                 const reading = view.layout === 'long' && indicator ? splitIndicatorReading(text) : null
                 // 分离列布局中的纯名称不是数值,只有「名称:读数」组合才需要分栏。
                 const isReading = isReadingColumn(column, view.layout) && (!indicator || Boolean(reading?.label))
+                const assessment = isReading ? collectionCellAssessment(cell) : null
                 return {
-                    key: column.key, text, isReading, reading,
+                    key: column.key, text, isReading, reading, assessment,
                     alarm: cell?.alarm === true,
-                    alert: isReading ? collectionCellAlert(cell) : null,
+                    alert: assessment?.alert || null,
                     audioPath: column.isAudio && text !== DETAIL_PLACEHOLDER ? text : '',
                     audioLabel: column.isAudio ? column.label : '',
                 }
@@ -148,8 +149,9 @@ export function collectionRecordTable(data, page = 1) {
         groups.push({ key: rows[start].groupKey, rows: rows.slice(start, end), isLatest: rows[start].latestGroup, ...rows[start].accent })
         start = end
     }
+    const hasUnknown = allRows.some((row) => row.cells.some((cell) => cell.assessment?.state === 'unknown'))
     return { ...window, rows, columns, groups,
-        assessmentNote: '采集接口暂未提供阈值、预警判定与等级;当前仅按已返回的报警标记着色,绿色为最新记录默认强调。',
+        assessmentNote: hasUnknown ? '部分指标暂未判定(阈值未配置、数据缺失或接口未提供);白色读数不代表正常,绿色行底仅为最新采集默认强调。' : '',
     }
 }