refactor: 重构项目结构
- 优化 `车站-设备-告警` 轮询机制 - 改进设备卡片的布局 - 支持修改设备 - 告警轮询中获取完整告警数据 - 车站告警详情支持导出完整的 `今日告警列表` - 支持将状态持久化到 `IndexedDB` - 新增轮询控制 (调试模式) - 新增离线开发模式 (调试模式) - 新增 `IndexedDB` 数据控制 (调试模式)
This commit is contained in:
144
src/components/station/alarm-detail-modal/alarm-detail-modal.vue
Normal file
144
src/components/station/alarm-detail-modal/alarm-detail-modal.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<script setup lang="ts">
|
||||
import type { NdmDeviceAlarmLogResultVO, Station } from '@/apis';
|
||||
import { ALARM_TYPES, DEVICE_TYPE_LITERALS, DEVICE_TYPE_NAMES, FAULT_LEVELS, tryGetDeviceType } from '@/enums';
|
||||
import { renderAlarmDateCell, renderAlarmTypeCell, renderDeviceTypeCell, renderFaultLevelCell } from '@/helpers';
|
||||
import { useAlarmStore } from '@/stores';
|
||||
import { downloadByData } from '@/utils';
|
||||
import dayjs from 'dayjs';
|
||||
import { NButton, NDataTable, NFlex, NGrid, NGridItem, NModal, NStatistic, NTag, type DataTableBaseColumn, type DataTableRowData, type PaginationProps } from 'naive-ui';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { computed, h, reactive, ref, toRefs } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
station?: Station;
|
||||
}>();
|
||||
|
||||
const show = defineModel<boolean>('show', { default: false });
|
||||
|
||||
const { station } = toRefs(props);
|
||||
|
||||
const alarmStore = useAlarmStore();
|
||||
const { lineAlarms } = storeToRefs(alarmStore);
|
||||
|
||||
const classifiedAlarmCounts = computed<{ label: string; count: number }[]>(() => {
|
||||
const stationCode = station.value?.code;
|
||||
if (!stationCode) return [];
|
||||
const stationAlarms = lineAlarms.value[stationCode];
|
||||
if (!stationAlarms) return [];
|
||||
return Object.values(DEVICE_TYPE_LITERALS).map<{ label: string; count: number }>((deviceType) => {
|
||||
return {
|
||||
label: DEVICE_TYPE_NAMES[deviceType],
|
||||
count: stationAlarms[deviceType].length,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const tableColumns = ref<DataTableBaseColumn<NdmDeviceAlarmLogResultVO>[]>([
|
||||
{ title: '告警流水号', key: 'alarmNo' },
|
||||
{ title: '告警时间', key: 'alarmDate', render: renderAlarmDateCell },
|
||||
{ title: '设备类型', key: 'deviceType', render: renderDeviceTypeCell },
|
||||
{ title: '设备名称', key: 'deviceName' },
|
||||
{ title: '告警类型', key: 'alarmType', align: 'center', render: renderAlarmTypeCell },
|
||||
{ title: '故障级别', key: 'faultLevel', align: 'center', render: renderFaultLevelCell },
|
||||
// { title: '故障编码', key: 'faultCode', align: 'center' },
|
||||
// { title: '故障位置', key: 'faultLocation' },
|
||||
{ title: '故障描述', key: 'faultDescription' },
|
||||
{ title: '修复建议', key: 'alarmRepairSuggestion' },
|
||||
{ title: '是否恢复', key: 'alarmCategory', align: 'center', render: (rowData) => (rowData.alarmCategory === '2' ? '是' : '否') },
|
||||
{ title: '恢复时间', key: 'updatedTime' },
|
||||
{
|
||||
title: '告警确认',
|
||||
key: 'alarmConfirm',
|
||||
align: 'center',
|
||||
render: (rowData) => (rowData.alarmConfirm === '1' ? h(NTag, { type: 'default' }, { default: () => '已确认' }) : h(NTag, { type: 'warning' }, { default: () => '未确认' })),
|
||||
},
|
||||
// { title: '设备ID', key: 'deviceId' },
|
||||
]);
|
||||
|
||||
const DEFAULT_PAGE_SIZE = 10;
|
||||
const pagination = reactive<PaginationProps>({
|
||||
size: 'small',
|
||||
showSizePicker: true,
|
||||
page: 1,
|
||||
pageSize: DEFAULT_PAGE_SIZE,
|
||||
pageSizes: [5, 10, 20, 50, 80, 100],
|
||||
prefix: ({ itemCount }) => {
|
||||
return h('div', {}, { default: () => `共${itemCount}条` });
|
||||
},
|
||||
onUpdatePage: (page: number) => {
|
||||
pagination.page = page;
|
||||
},
|
||||
onUpdatePageSize: (pageSize: number) => {
|
||||
pagination.pageSize = pageSize;
|
||||
pagination.page = 1;
|
||||
},
|
||||
});
|
||||
|
||||
const tableData = computed<DataTableRowData[]>(() => {
|
||||
const stationCode = station.value?.code;
|
||||
if (!stationCode) return [];
|
||||
const stationAlarms = lineAlarms.value[stationCode];
|
||||
if (!stationAlarms) return [];
|
||||
return stationAlarms['unclassified'];
|
||||
});
|
||||
|
||||
const onAfterLeave = () => {
|
||||
pagination.page = 1;
|
||||
pagination.pageSize = 10;
|
||||
};
|
||||
|
||||
const exportAlarms = () => {
|
||||
const keys = tableColumns.value.map((column) => column.key);
|
||||
const csvHeader = `${tableColumns.value.map((column) => column.title).join(',')}\n`;
|
||||
let csvRows = '';
|
||||
for (const row of tableData.value) {
|
||||
const alarm = row as NdmDeviceAlarmLogResultVO;
|
||||
const csvRow = `${keys
|
||||
.map((key) => {
|
||||
const fieldKey = key as keyof NdmDeviceAlarmLogResultVO;
|
||||
if (fieldKey === 'alarmDate') return dayjs(Number(alarm[fieldKey])).format('YYYY-MM-DD HH:mm:ss');
|
||||
if (fieldKey === 'deviceType') {
|
||||
const deviceType = tryGetDeviceType(alarm[fieldKey]);
|
||||
if (!deviceType) return '-';
|
||||
return DEVICE_TYPE_NAMES[deviceType];
|
||||
}
|
||||
if (fieldKey === 'alarmType') return ALARM_TYPES[alarm[fieldKey] ?? ''];
|
||||
if (fieldKey === 'faultLevel') return FAULT_LEVELS[alarm[fieldKey] ?? ''];
|
||||
if (fieldKey === 'alarmCategory') return alarm[fieldKey] === '2' ? '是' : '否';
|
||||
if (fieldKey === 'alarmConfirm') return alarm[fieldKey] === '1' ? '已确认' : '未确认';
|
||||
return alarm[fieldKey];
|
||||
})
|
||||
.join(',')}\n`;
|
||||
csvRows = csvRows.concat(csvRow);
|
||||
}
|
||||
const csvContent = csvHeader.concat(csvRows);
|
||||
const time = dayjs().format('YYYY-MM-DD_HH-mm-ss');
|
||||
downloadByData(csvContent, `${station.value?.name}_设备告警记录_${time}.csv`, 'text/csv;charset=utf-8', '\ufeff');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NModal v-model:show="show" preset="card" style="width: 100vw; height: 100vh" :close-on-esc="false" :mask-closable="false" @after-leave="onAfterLeave">
|
||||
<template #header>
|
||||
<span>{{ `${station?.name} - 设备告警详情` }}</span>
|
||||
</template>
|
||||
<template #default>
|
||||
<NFlex vertical :size="12" style="height: 100%">
|
||||
<NGrid cols="9" style="flex: 0 0 auto">
|
||||
<NGridItem v-for="item in classifiedAlarmCounts" :key="item.label" span="1">
|
||||
<NStatistic :label="item.label + '告警'" :value="item.count" />
|
||||
</NGridItem>
|
||||
</NGrid>
|
||||
|
||||
<NFlex align="center" style="flex: 0 0 auto">
|
||||
<div style="font-size: medium">今日设备告警列表</div>
|
||||
<NButton type="primary" style="margin-left: auto" @click="exportAlarms">导出</NButton>
|
||||
</NFlex>
|
||||
|
||||
<NDataTable flex-height style="height: 100%; min-height: 0; flex: 1 1 auto" :single-line="false" :columns="tableColumns" :data="tableData" :pagination="pagination" />
|
||||
</NFlex>
|
||||
</template>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
Reference in New Issue
Block a user