Quellcode durchsuchen

[Fix 0000] 修复矿端首页数据显示异常的问题

houzekong vor 1 Monat
Ursprung
Commit
fac0bc32a1

+ 9 - 0
src/components/Configurable/preset/BoardTable.vue

@@ -252,6 +252,12 @@
     '4': StatusColorEnum.red, // 红
   };
 
+  // 设备状态映射(口径与密闭监测页 sealedMonitor 的「设备状态」列一致),未命中的值原样展示
+  const statusMap = {
+    '-1': '断线',
+    '0': '正常',
+  };
+
   // 修改 getter 或创建一个专门的渲染函数
   const renderCellComponent = (row: any, col: TableColumn) => {
     return {
@@ -263,6 +269,9 @@
           const color = riskColorMap[level] || StatusColorEnum.blue;
           return h('span', { style: { color } }, text);
         }
+        if (col.prop === 'status') {
+          return h('span', {}, statusMap[String(value)] ?? value);
+        }
         return h('span', {}, value);
       },
     };

+ 11 - 10
src/views/dashboard/SealedGoaf/configurable.data.sealedGoaf.ts

@@ -842,7 +842,6 @@ export const testConfigSealedMine: Config[] = [
       position: 'top:140px;left:25px;',
     },
   },
-
   // 2. 密闭预警统计
   {
     deviceType: 'warning_statistics',
@@ -1062,8 +1061,8 @@ export const testConfigSealedMine: Config[] = [
                 { label: '一氧化碳(ppm)', value: '${coVal}' },
                 { label: '二氧化碳(%)', value: '${co2Val}' },
                 { label: '氧气(%)', value: '${o2Val}' },
-                { label: '乙烯(ppm)', value: '${c2h2Val}' },
-                { label: '乙炔(ppm)', value: '${c2h4Val}' },
+                { label: '乙烯(ppm)', value: '${c2h4Val}' },
+                { label: '乙炔(ppm)', value: '${c2h2Val}' },
                 { label: '压差(Pa)', value: '${sourcePressure}' },
                 { label: '温度(℃)', value: '${temperature}' },
               ],
@@ -1077,8 +1076,8 @@ export const testConfigSealedMine: Config[] = [
                 { label: '一氧化碳(ppm)', value: '${coValOut}' },
                 { label: '二氧化碳(%)', value: '${co2ValOut}' },
                 { label: '氧气(%)', value: '${o2ValOut}' },
-                { label: '乙烯(ppm)', value: '${c2h2ValOut}' },
-                { label: '乙炔(ppm)', value: '${c2h4ValOut}' },
+                { label: '乙烯(ppm)', value: '${c2h4ValOut}' },
+                { label: '乙炔(ppm)', value: '${c2h2ValOut}' },
                 { label: '压差(Pa)', value: '${sourcePressure}' },
                 { label: '温度(℃)', value: '${temperatureOut}' },
               ],
@@ -1103,7 +1102,7 @@ export const testConfigSealedMine: Config[] = [
             columns: [
               { name: '安装位置', prop: 'devicePos' },
               { name: '预警等级', prop: 'alarmLevel' },
-              { name: '状态', prop: 'gjMineStatus' },
+              { name: '状态', prop: 'status' },
             ],
           },
         },
@@ -1148,6 +1147,8 @@ export const testConfigSealedMine: Config[] = [
           },
         ],
       },
+      // 说明:本模块 layout 只消费 table,以下 chart / gallery 不参与渲染(coalSeamFireData 为
+      // 「类别 → 数量」映射,gallery 的 coalSeamFireData[0] 取不到值),保留仅为兼容历史配置。
       chart: [
         {
           type: 'pie_drag',
@@ -1178,14 +1179,14 @@ export const testConfigSealedMine: Config[] = [
           // parser: 'json',
           readFrom: 'transCoalSeamFireData',
           columns: [
-            {
-              name: '所属煤层',
-              prop: 'coalSeamName',
-            },
             {
               name: '自燃倾向性',
               prop: 'coalSeamLevel',
             },
+            {
+              name: '数量',
+              prop: 'num',
+            },
           ],
         },
       ],

+ 3 - 31
src/views/dashboard/SealedGoaf/index.vue

@@ -52,7 +52,7 @@
   import { useAppStore } from '/@/store/modules/app';
   import { useMineDepartmentStore } from '/@/store/modules/mine';
   import SystemSelect from '/@/layouts/default/feature/SystemSelect.vue';
-  import { DEFAULT_DISPLAY_MAP, DEFAULT_OPERAND_SET, evaluateExpressionLite } from '../../system/algorithm/utils';
+  import { buildCoalSeamFireRows, enrichGoafMonitorRecords } from './sealedGoaf.transform';
   import { map } from 'lodash-es';
 
   const { title = '老空区永久密闭监测与分析系统' } = useGlobSetting();
@@ -213,34 +213,7 @@
       // 每个请求 .then() 中处理数据并 return,.catch() 返回兜底值使 Promise.all 永不 reject
       const [monitorData, coalSeamFireResult, goafStatusNum, goafAlarmNum, provinceAlarm] = await Promise.all([
         getGoafData(appStore.simpleMapParams)
-          .then((r) => {
-            if (r?.records) {
-              r.records.forEach((record) => {
-                const { coalSeamAlarmRule, sourcePressureAlarm, fireAlarm, fireAlarmReson, fireAlarmOut, fireAlarmOutReson } = record;
-                record.sourcePressureReason = evaluateExpressionLite(
-                  coalSeamAlarmRule[`ycWarn${sourcePressureAlarm.alarmLevel}`],
-                  ['sourcePressureChange'],
-                  DEFAULT_OPERAND_SET,
-                  DEFAULT_DISPLAY_MAP
-                ).join(';');
-
-                record.fireAlarmReason = evaluateExpressionLite(
-                  coalSeamAlarmRule[`fireWarn${fireAlarm.alarmLevel}`],
-                  fireAlarmReson,
-                  DEFAULT_OPERAND_SET,
-                  DEFAULT_DISPLAY_MAP
-                ).join(';');
-
-                record.fireAlarmOutReason = evaluateExpressionLite(
-                  coalSeamAlarmRule[`fireOutWarn${fireAlarmOut.alarmLevel}`],
-                  fireAlarmOutReson,
-                  DEFAULT_OPERAND_SET,
-                  DEFAULT_DISPLAY_MAP
-                ).join(';');
-              });
-            }
-            return r;
-          })
+          .then((r) => enrichGoafMonitorRecords(r))
           .catch((e) => {
             console.error('密闭监测详情获取失败:', e);
             return null;
@@ -248,8 +221,7 @@
 
         getCoalSeamFireNum(appStore.simpleMapParams)
           .then((r) => {
-            const trans = r ? Object.values(r).flat() : [];
-            return { data: r, trans };
+            return { data: r, trans: buildCoalSeamFireRows(r) };
           })
           .catch((e) => {
             console.error('煤层自燃倾向数据获取失败:', e);

+ 66 - 0
src/views/dashboard/SealedGoaf/sealedGoaf.transform.ts

@@ -0,0 +1,66 @@
+import { DEFAULT_DISPLAY_MAP, DEFAULT_OPERAND_SET, evaluateExpressionLite } from '../../system/algorithm/utils';
+
+/**
+ * 把「预警等级 + 煤层报警规则表达式」翻译成可读的预警原因文本。
+ *
+ * 接口存在告警对象整体为 null 的情况(如 sourcePressureAlarm),
+ * 因此任一环节缺失(规则缺失、告警为空、表达式非法)都返回空串而不抛错。
+ */
+function buildAlarmReason(coalSeamAlarmRule: any, alarm: any, rulePrefix: string, reasons: unknown): string {
+  try {
+    if (!coalSeamAlarmRule || !alarm) return '';
+    const expr = coalSeamAlarmRule[`${rulePrefix}${alarm.alarmLevel}`];
+    const operands = Array.isArray(reasons) ? (reasons as string[]) : [];
+    return evaluateExpressionLite(expr, operands, DEFAULT_OPERAND_SET, DEFAULT_DISPLAY_MAP).join(';');
+  } catch (e) {
+    console.error('预警原因计算失败:', e);
+    return '';
+  }
+}
+
+/**
+ * 为密闭监测数据(/province/device/getGoafData)的每条记录补充预警原因文本。
+ *
+ * 加工过程对单条记录做了隔离:某条记录字段异常只会丢失该条的原因文本,
+ * 不会让整个请求进入 catch 而把 monitorData 置空(否则「密闭详情展示」模块会整体空白)。
+ *
+ * @param result getGoafData 的原始返回
+ * @returns 传入的 result(原地补充字段);records 非数组时原样返回
+ */
+export function enrichGoafMonitorRecords(result: any): any {
+  const records = result?.records;
+  if (!Array.isArray(records)) return result;
+
+  records.forEach((record) => {
+    if (!record || typeof record !== 'object') return;
+    try {
+      const { coalSeamAlarmRule, sourcePressureAlarm, fireAlarm, fireAlarmReson, fireAlarmOut, fireAlarmOutReson } = record;
+      record.sourcePressureReason = buildAlarmReason(coalSeamAlarmRule, sourcePressureAlarm, 'ycWarn', ['sourcePressureChange']);
+      record.fireAlarmReason = buildAlarmReason(coalSeamAlarmRule, fireAlarm, 'fireWarn', fireAlarmReson);
+      record.fireAlarmOutReason = buildAlarmReason(coalSeamAlarmRule, fireAlarmOut, 'fireOutWarn', fireAlarmOutReson);
+    } catch (e) {
+      console.error('密闭监测记录预警原因计算失败:', e);
+    }
+  });
+
+  return result;
+}
+
+export interface CoalSeamFireRow {
+  /** 自燃倾向性类别,如「容易自燃」 */
+  coalSeamLevel: string;
+  /** 该类别的煤层数量 */
+  num: any;
+}
+
+/**
+ * 把 /province/homeData/getCoalSeamFireNum 的「自燃倾向性类别 → 数量」映射
+ * 转换为表格可渲染的行数组,并保持接口返回顺序。
+ *
+ * 例:{ 容易自燃: '1', 不易自燃: 0, 未检测: 0, 自燃: 0 }
+ *  → [{ coalSeamLevel: '容易自燃', num: '1' }, ...]
+ */
+export function buildCoalSeamFireRows(raw: Record<string, any> | null | undefined): CoalSeamFireRow[] {
+  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return [];
+  return Object.entries(raw).map(([coalSeamLevel, num]) => ({ coalSeamLevel, num }));
+}

+ 145 - 0
tests/sealedGoaf.transform.test.ts

@@ -0,0 +1,145 @@
+/**
+ * 矿端(isLeaf)取数转换回归测试
+ *
+ * 数据形状取自真实接口返回(deptId=1125&isLeaf=true):
+ *  - /province/device/getGoafData:records 中 sourcePressureAlarm / unsealAlarm / overLimitAlarm 均为 null
+ *  - /province/homeData/getCoalSeamFireNum:{ 容易自燃: '1', 不易自燃: 0, 未检测: 0, 自燃: 0 }
+ *
+ * 使用方式:
+ *   npx jest tests/sealedGoaf.transform.test.ts
+ */
+import {
+  buildCoalSeamFireRows,
+  enrichGoafMonitorRecords,
+} from '../src/views/dashboard/SealedGoaf/sealedGoaf.transform';
+
+/** 与 response.txt 中 coalSeamAlarmRule 一致的精简规则集 */
+const coalSeamAlarmRule = {
+  id: '1',
+  ycWarn1: '20<=sourcePressureChange && sourcePressureChange<=40',
+  ycWarn2: '40<sourcePressureChange && sourcePressureChange<=60',
+  fireWarn1: '(((coRzl<=20 || coZf<=0.5*coAvg) && coVal<=10)||o2Val<=5 ) && temperatureDifference<2',
+  fireOutWarn1: 'temperatureDifferenceOut<2 || (ch4ValOut>0 && ch4ValOut<0.5) || (coValOut>=0 && coValOut<5) || o2ValOut < 20',
+};
+
+/** 真实返回中的一条密闭监测记录(告警对象为 null 的情况) */
+function createRecord(overrides: Record<string, any> = {}) {
+  return {
+    devicePos: '25213撤架巷2号墙',
+    mineName: '红柳林煤矿',
+    coalSeamName: '5-2煤层',
+    coalSeamLevel: 'Ⅰ类容易自燃',
+    coalSeamAlarmRule,
+    // 现网返回中这三个告警对象均为 null
+    leakageAlarm: { alarmLevel: '1', alarmName: '闭外气体涌入-存在漏风供氧可能' },
+    sourcePressureAlarm: null,
+    fireAlarm: { alarmLevel: '1', alarmName: '低风险' },
+    fireAlarmReson: ['coVal', 'temperatureDifference', 'coZf', 'coRzl'],
+    fireAlarmOut: { alarmLevel: '1', alarmName: '低风险' },
+    fireAlarmOutReson: ['temperatureDifferenceOut', 'coValOut'],
+    unsealAlarm: null,
+    explosionAlarm: { alarmLevel: '1', alarmName: '增减风安全区' },
+    alarmLevel: 1,
+    status: 0,
+    linkStatus: 1,
+    ch4Val: '0.00',
+    c2h4Val: '0.00',
+    c2h2Val: '0.00',
+    sourcePressure: -675.38,
+    ...overrides,
+  };
+}
+
+describe('enrichGoafMonitorRecords', () => {
+  it('sourcePressureAlarm 为 null 时不抛错,且保证 sourcePressureReason 为空串', () => {
+    const payload = { records: Array.from({ length: 7 }, (_, i) => createRecord({ devicePos: `墙-${i}` })), total: 7 };
+
+    let result: any;
+    expect(() => {
+      result = enrichGoafMonitorRecords(payload);
+    }).not.toThrow();
+
+    // 原地返回同一个对象,保持原引用语义
+    expect(result).toBe(payload);
+    expect(result.records).toHaveLength(7);
+    result.records.forEach((record) => {
+      expect(record.sourcePressureReason).toBe('');
+    });
+  });
+
+  it('告警对象存在时仍能正常翻译出预警原因文本', () => {
+    const [record] = enrichGoafMonitorRecords({
+      records: [
+        createRecord({
+          sourcePressureAlarm: { alarmLevel: '1', alarmName: '内外压差风险报警' },
+        }),
+      ],
+    }).records;
+
+    expect(record.sourcePressureReason).not.toBe('');
+    expect(record.sourcePressureReason).toContain('压差变化');
+    expect(record.fireAlarmReason).toContain('温差');
+    expect(record.fireAlarmOutReason).toContain('密闭墙外温差');
+  });
+
+  it('规则缺失 / 告警缺失 / 规则键不存在时均返回空串而不抛错', () => {
+    const [noRule, noAlarm, missingRuleKey] = enrichGoafMonitorRecords({
+      records: [
+        createRecord({ coalSeamAlarmRule: null }),
+        createRecord({ fireAlarm: null, fireAlarmOut: null }),
+        createRecord({ fireAlarm: { alarmLevel: '4' }, fireAlarmOut: { alarmLevel: '4' } }),
+      ],
+    }).records;
+
+    expect(noRule.sourcePressureReason).toBe('');
+    expect(noRule.fireAlarmReason).toBe('');
+    expect(noRule.fireAlarmOutReason).toBe('');
+
+    expect(noAlarm.sourcePressureReason).toBe('');
+    expect(noAlarm.fireAlarmReason).toBe('');
+    expect(noAlarm.fireAlarmOutReason).toBe('');
+
+    // 规则集中只有 fireWarn1 / fireOutWarn1,等级 4 取不到对应规则,应退化为空串
+    expect(missingRuleKey.fireAlarmReason).toBe('');
+    expect(missingRuleKey.fireAlarmOutReason).toBe('');
+  });
+
+  it('reson 字段不是数组时按空数组处理,不抛错', () => {
+    const [record] = enrichGoafMonitorRecords({
+      records: [createRecord({ fireAlarmReson: null, fireAlarmOutReson: undefined })],
+    }).records;
+
+    expect(typeof record.fireAlarmReason).toBe('string');
+    expect(typeof record.fireAlarmOutReason).toBe('string');
+  });
+
+  it('records 缺失、非数组或含 null 项时安全返回(不再让整个请求进入 catch)', () => {
+    expect(enrichGoafMonitorRecords(null)).toBeNull();
+    expect(enrichGoafMonitorRecords({ total: 0 })).toEqual({ total: 0 });
+
+    const result = enrichGoafMonitorRecords({ records: [null, createRecord()] });
+    expect(result.records).toHaveLength(2);
+    expect(result.records[1].sourcePressureReason).toBe('');
+  });
+});
+
+describe('buildCoalSeamFireRows', () => {
+  it('把「类别 → 数量」映射转换为表格行并保持接口顺序', () => {
+    const rows = buildCoalSeamFireRows({ 容易自燃: '1', 不易自燃: 0, 未检测: 0, 自燃: 0 });
+
+    expect(rows).toEqual([
+      { coalSeamLevel: '容易自燃', num: '1' },
+      { coalSeamLevel: '不易自燃', num: 0 },
+      { coalSeamLevel: '未检测', num: 0 },
+      { coalSeamLevel: '自燃', num: 0 },
+    ]);
+  });
+
+  it('非对象返回值统一为空数组(表格显示空状态)', () => {
+    expect(buildCoalSeamFireRows(null)).toEqual([]);
+    expect(buildCoalSeamFireRows(undefined)).toEqual([]);
+    expect(buildCoalSeamFireRows({})).toEqual([]);
+    expect(buildCoalSeamFireRows([] as any)).toEqual([]);
+    expect(buildCoalSeamFireRows('容易自燃' as any)).toEqual([]);
+  });
+});