xiaoyi
5 天以前 27c8277d717b34b55f3ea967027b53b067f77df3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
<script lang="ts" setup>
  import type { VxeTableGridOptions } from '#/adapter/vxe-table';
  import type { MesDvTelemetryApi } from '#/api/mes/dv/telemetry';
 
  import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef } from 'vue';
 
  import { Page } from '@vben/common-ui';
  import { useVbenVxeGrid } from '#/adapter/vxe-table';
 
  import { Button, Card, Select, Statistic, Tag } from 'ant-design-vue';
 
  import {
    getTelemetryCheckList,
    getTelemetryCheckSummary,
    getTelemetryDeviceList,
  } from '#/api/mes/dv/telemetry';
 
  /** MES 供电参数比对校验(各设备最新参数 vs 标准值) */
  defineOptions({ name: 'MesDvTelemetryCheck' });
 
  const devices = ref<MesDvTelemetryApi.DeviceItem[]>([]);
  const selectedDeviceId = ref<string>();
  const summaryList = ref<MesDvTelemetryApi.CheckSummary[]>([]);
 
  const contentRef = useTemplateRef('contentRef');
  let resizeObserver: ResizeObserver | undefined;
 
  /** 计算表格高度:内容区可用高度 - 设备选择行/统计卡片/工具栏等固定开销 */
  function calcTableHeight() {
    const el = contentRef.value;
    if (!el) return;
    const height = Math.max(el.clientHeight - 230, 240);
    checkGridApi.setGridOptions({ height });
  }
 
  onMounted(async () => {
    calcTableHeight();
    resizeObserver = new ResizeObserver(calcTableHeight);
    if (contentRef.value) resizeObserver.observe(contentRef.value);
    devices.value = await getTelemetryDeviceList();
    await loadSummary();
  });
 
  onBeforeUnmount(() => resizeObserver?.disconnect());
 
  async function loadSummary() {
    summaryList.value = await getTelemetryCheckSummary({
      tbDeviceId: selectedDeviceId.value,
    });
  }
 
  /** 选设备或刷新:重载统计卡片 + 明细表 */
  async function refreshAll() {
    await loadSummary();
    checkGridApi.query();
  }
 
  /** 选中设备的统计;未选择则汇总全部设备 */
  const currentSummary = computed(() => {
    if (selectedDeviceId.value) {
      return (
        summaryList.value.find((s) => s.tbDeviceId === selectedDeviceId.value) ??
        {}
      );
    }
    return summaryList.value.reduce(
      (acc, s) => {
        acc.totalCount = (acc.totalCount ?? 0) + (s.totalCount ?? 0);
        acc.normalCount = (acc.normalCount ?? 0) + (s.normalCount ?? 0);
        acc.anomalyCount = (acc.anomalyCount ?? 0) + (s.anomalyCount ?? 0);
        return acc;
      },
      { totalCount: 0, normalCount: 0, anomalyCount: 0 } as MesDvTelemetryApi.CheckSummary,
    );
  });
 
  const anomalyRate = computed(() => {
    const total = currentSummary.value.totalCount ?? 0;
    if (total <= 0) return 0;
    return Number(
      (((currentSummary.value.anomalyCount ?? 0) * 100) / total).toFixed(1),
    );
  });
 
  const [CheckGrid, checkGridApi] = useVbenVxeGrid({
    gridOptions: {
      columns: [
        { field: 'deviceName', title: '设备名称', minWidth: 150 },
        { field: 'paramName', title: '信号名称', minWidth: 140 },
        { field: 'paramKeyName', title: '设备KEY名称', minWidth: 130 },
        { field: 'standardValue', title: '标准值', width: 110 },
        { field: 'timelyValue', title: '实时值', width: 110 },
        { field: 'avgValue', title: '均值', width: 110 },
        { field: 'maxValue', title: '最大值', width: 110 },
        { field: 'minValue', title: '最小值', width: 110 },
        {
          field: 'whetherAnomaly',
          title: '是否异常',
          width: 100,
          slots: { default: 'anomaly' },
        },
      ],
      height: 400,
      keepSource: true,
      // /check 返回裸数组,需关闭分页
      pagerConfig: { enabled: false },
      proxyConfig: {
        ajax: {
          query: async () =>
            await getTelemetryCheckList({
              tbDeviceId: selectedDeviceId.value,
            }),
        },
      },
      rowConfig: { keyField: 'id', isHover: true },
      toolbarConfig: { refresh: true },
    } as VxeTableGridOptions<MesDvTelemetryApi.Telemetry>,
  });
</script>
 
<template>
  <Page auto-content-height>
    <div ref="contentRef" class="flex h-full w-full flex-col p-4">
      <div class="mb-4 flex flex-wrap items-center gap-4">
        <Select
          v-model:value="selectedDeviceId"
          class="w-72"
          allow-clear
          placeholder="请选择设备(不选则展示全部设备)"
          :options="
            devices.map((d) => ({ label: d.deviceName, value: d.tbDeviceId }))
          "
          @change="refreshAll"
        />
        <Button @click="refreshAll">刷新</Button>
      </div>
      <div class="mb-4 grid grid-cols-2 gap-4 lg:grid-cols-4">
        <Card>
          <Statistic title="参数总数" :value="currentSummary.totalCount ?? 0" />
        </Card>
        <Card>
          <Statistic
            title="正常"
            :value="currentSummary.normalCount ?? 0"
            :value-style="{ color: '#52c41a' }"
          />
        </Card>
        <Card>
          <Statistic
            title="异常"
            :value="currentSummary.anomalyCount ?? 0"
            :value-style="{ color: '#ff4d4f' }"
          />
        </Card>
        <Card>
          <Statistic title="异常率" :value="anomalyRate" suffix="%" />
        </Card>
      </div>
      <CheckGrid table-title="供电参数比对明细" class="min-h-0 flex-1">
        <template #anomaly="{ row }">
          <Tag :color="row.whetherAnomaly ? 'red' : 'green'">
            {{ row.whetherAnomaly ? '异常' : '正常' }}
          </Tag>
        </template>
      </CheckGrid>
    </div>
  </Page>
</template>