Files
ndm-web-client/src/pages/alarm-page.vue
2025-09-02 21:27:27 +08:00

263 lines
8.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import type { NdmDeviceAlarmLogResultVO } from '@/apis/models';
import { ndmDeviceAlarmLogDefaultExportByTemplate, postNdmDeviceAlarmLogPage } from '@/apis/requests';
import { DeviceType, DeviceTypeCode, DeviceTypeName, getDeviceTypeVal, type DeviceTypeVal } from '@/enums/device-type';
import { useCurrentAlarmsStore } from '@/stores/current-alarms';
import { useStationStore } from '@/stores/station';
import { downloadByData } from '@/utils/download';
import { useMutation } from '@tanstack/vue-query';
import dayjs from 'dayjs';
import type { DataTableColumns, DataTableRowData, PaginationProps, SelectOption } from 'naive-ui';
import { NForm, NInput, NButton, NSpace, NDataTable, NFormItemGi, NGrid, NSelect, NGridItem, NDatePicker } from 'naive-ui';
import { storeToRefs } from 'pinia';
import { ref, reactive, onBeforeMount, h, computed, watch } from 'vue';
const currentAlarmsStore = useCurrentAlarmsStore();
const { needReload } = storeToRefs(currentAlarmsStore);
watch(needReload, async (newVal) => {
if (newVal) {
needReload.value = false;
onClickReset();
}
});
const stationStore = useStationStore();
const { stationList } = storeToRefs(stationStore);
const stationSelectOptions = computed(() => {
return stationList.value.map<SelectOption>((station) => ({
label: station.name,
value: station.code,
}));
});
const searchFields = reactive({
stationCode_in: [] as string[],
deviceType_in: [] as string[],
deviceName_like: '',
// deviceId_likeRight: '',
alarmDate: [dayjs().startOf('date').valueOf(), dayjs().endOf('date').valueOf()] as [number, number],
});
const resetSearchFields = () => {
searchFields.stationCode_in = [];
searchFields.deviceType_in = [];
searchFields.deviceName_like = '';
searchFields.alarmDate = [dayjs().startOf('date').subtract(7, 'day').valueOf(), dayjs().endOf('date').valueOf()];
};
const onDateChange = (value: [number, number] | null) => {
if (!value || value.length !== 2) {
return;
}
const [start, end] = value;
const diffDays = dayjs(end).diff(dayjs(start), 'day');
if (diffDays > 7) {
// 如果超过7天自动调整结束时间
const adjustedEnd = dayjs(start).add(7, 'day').valueOf();
searchFields.alarmDate = [start, adjustedEnd];
window.$message.warning('时间范围不能超过7天已自动调整');
} else {
searchFields.alarmDate = value;
}
};
const tableColumns: DataTableColumns<NdmDeviceAlarmLogResultVO> = [
{ title: '告警流水号', key: 'alarmNo' },
{
title: '告警时间',
key: 'alarmDate',
render: (rowData /* , rowIndex */) => {
return dayjs(Number(rowData.alarmDate ?? 0)).format('YYYY-MM-DD HH:mm:ss');
},
},
{
title: '设备类型',
key: 'deviceType',
render: (rowData) => {
return DeviceTypeName[getDeviceTypeVal(rowData.deviceType)];
},
},
{ title: '设备名称', key: 'deviceName' },
{ title: '告警类型', key: 'alarmType', align: 'center' },
{ title: '故障级别', key: 'faultLevel', align: 'center' },
{ title: '故障编码', key: 'faultCode', align: 'center' },
// { title: '故障位置', key: 'faultLocation' },
{ title: '故障描述', key: 'faultDescription' },
{ title: '修复建议', key: 'alarmRepairSuggestion' },
{
title: '是否恢复',
key: 'alarmCategory',
align: 'center',
render: (rowData) => {
return rowData.alarmCategory === '2' ? '是' : '否';
},
},
{ title: '恢复时间', key: 'updatedTime' },
{
title: '告警确认',
key: 'alarmConfirm',
align: 'center',
render: (rowData) => {
return rowData.alarmConfirm === '1' ? '已确认' : '未确认';
},
},
// { title: '设备ID', key: 'deviceId' },
];
const tableData = ref<DataTableRowData[]>([]);
const tablePagination = reactive<PaginationProps>({
showSizePicker: true,
page: 1,
pageSize: 10,
pageSizes: [5, 10, 20, 50, 80, 100],
pageCount: 1,
itemCount: 0,
prefix: ({ itemCount }) => {
return h('div', {}, { default: () => `${itemCount}` });
},
onUpdatePage: (page: number) => {
tablePagination.page = page;
getAlarmList();
},
onUpdatePageSize: (pageSize: number) => {
tablePagination.pageSize = pageSize;
tablePagination.page = 1;
getAlarmList();
},
});
const { mutate: getAlarmList, isPending: isTableLoading } = useMutation({
mutationFn: async () => {
const res = await postNdmDeviceAlarmLogPage('', {
model: {},
extra: {
stationCode_in: [...searchFields.stationCode_in],
deviceName_like: searchFields.deviceName_like,
deviceType_in: searchFields.deviceType_in.flatMap((deviceType) => {
return DeviceTypeCode[deviceType as DeviceTypeVal];
}),
alarmDate_ge: searchFields.alarmDate[0],
alarmDate_le: searchFields.alarmDate[1],
},
current: tablePagination.page ?? 1,
size: tablePagination.pageSize ?? 10,
order: 'descending',
sort: 'id',
});
return res;
},
onSuccess: (res) => {
const { records, pages, size, total } = res;
tablePagination.pageSize = parseInt(size);
tablePagination.pageCount = parseInt(pages);
tablePagination.itemCount = parseInt(total);
tableData.value = records;
},
onError: (error) => {
window.$message.error(error.message);
},
});
const onClickReset = () => {
resetSearchFields();
tablePagination.page = 1;
tablePagination.pageSize = 10;
tablePagination.pageCount = 1;
tablePagination.itemCount = 0;
getAlarmList();
};
const onClickQuery = () => getAlarmList();
const { mutate: downloadTableData, isPending: isDownloading } = useMutation({
mutationFn: async () => {
const data = await ndmDeviceAlarmLogDefaultExportByTemplate('', {
model: {},
extra: {
stationCode_in: [...searchFields.stationCode_in],
deviceName_like: searchFields.deviceName_like,
deviceType_in: searchFields.deviceType_in,
alarmDate_ge: searchFields.alarmDate[0],
alarmDate_le: searchFields.alarmDate[1],
},
current: tablePagination.page ?? 1,
size: tablePagination.pageSize ?? 10,
order: 'descending',
sort: 'id',
});
return data;
},
onSuccess: (data) => {
downloadByData(data, `设备告警记录.xlsx`);
},
onError: (error) => {
window.$message.error(error.message);
},
});
const exportTableData = () => downloadTableData();
onBeforeMount(() => getAlarmList());
</script>
<template>
<!-- 容器上下布局表格自适应剩余高度 -->
<div style="height: 100%; display: flex; flex-direction: column">
<!-- 查询面板 -->
<div style="flex: 0 0 auto; padding: 8px">
<NForm>
<NGrid :cols="3" :x-gap="24">
<NFormItemGi :span="1" label="车站" label-placement="left">
<NSelect v-model:value="searchFields.stationCode_in" :options="stationSelectOptions" multiple clearable />
</NFormItemGi>
<NFormItemGi :span="1" label="设备类型" label-placement="left">
<NSelect
v-model:value="searchFields.deviceType_in"
:options="[
...Object.values(DeviceType).map<SelectOption>((deviceType) => {
return {
label: DeviceTypeName[deviceType],
value: deviceType,
};
}),
]"
placeholder="请选择设备类型"
multiple
clearable
/>
</NFormItemGi>
<NFormItemGi :span="1" label="设备名称" label-placement="left">
<NInput v-model:value="searchFields.deviceName_like" placeholder="请输入设备名称" clearable />
</NFormItemGi>
<NFormItemGi :span="1" label="告警发生时间" label-placement="left">
<NDatePicker v-model:value="searchFields.alarmDate" type="datetimerange" @update:value="onDateChange" />
</NFormItemGi>
</NGrid>
<!-- 按钮 -->
<NGrid :cols="1">
<NGridItem>
<NSpace>
<NButton @click="onClickReset">重置</NButton>
<NButton type="primary" :loading="isTableLoading" @click="onClickQuery">查询</NButton>
</NSpace>
</NGridItem>
</NGrid>
</NForm>
</div>
<!-- 工具栏横向右对齐按钮 -->
<div style="flex: 0 0 auto; display: flex; align-items: center; padding: 8px">
<div style="font-size: medium">设备告警列表</div>
<NSpace style="margin-left: auto">
<NButton type="primary" :loading="isDownloading" @click="exportTableData">导出</NButton>
</NSpace>
</div>
<!-- 表格区域填满剩余空间 -->
<div style="flex: 1 1 auto; min-height: 0; padding: 8px">
<NDataTable :loading="isTableLoading" :columns="tableColumns" :data="tableData" :pagination="tablePagination" :single-line="false" remote flex-height style="height: 100%" />
</div>
</div>
</template>
<style scoped lang="scss"></style>