Files
ndm-web-client/src/pages/alarm-page.vue

375 lines
12 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, putNdmDeviceAlarmLog } from '@/apis/requests';
import { renderAlarmDateCell, renderAlarmTypeCell, renderDeviceTypeCell, renderFaultLevelCell } from '@/components/helper';
import { AlarmType } from '@/enums/alarm-type';
import { DeviceType, DeviceTypeCode, DeviceTypeName, type DeviceTypeVal } from '@/enums/device-type';
import { FaultLevel } from '@/enums/fault-level';
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, NTag, NPopconfirm } 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: '',
alarmType_in: [] as string[],
faultLevel_in: [] as string[],
// 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.alarmType_in = [];
searchFields.faultLevel_in = [];
searchFields.alarmDate = [dayjs().startOf('date').subtract(1, 'week').valueOf(), dayjs().endOf('date').valueOf()];
};
const getExtraFields = () => {
const stationCode_in = searchFields.stationCode_in.length > 0 ? [...searchFields.stationCode_in] : undefined;
const deviceName_like = !!searchFields.deviceName_like ? searchFields.deviceName_like : undefined;
const deviceType_in =
searchFields.deviceType_in.length > 0
? searchFields.deviceType_in.flatMap((deviceType) => {
return DeviceTypeCode[deviceType as DeviceTypeVal];
})
: undefined;
const alarmType_in = searchFields.alarmType_in.length > 0 ? [...searchFields.alarmType_in] : undefined;
const faultLevel_in = searchFields.faultLevel_in.length > 0 ? [...searchFields.faultLevel_in] : undefined;
const alarmDate_ge = searchFields.alarmDate[0];
const alarmDate_le = searchFields.alarmDate[1];
return {
stationCode_in,
deviceName_like,
deviceType_in,
alarmType_in,
faultLevel_in,
alarmDate_ge,
alarmDate_le,
};
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const onDateChange = (value: [number, number] | null) => {
if (!value) {
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 searchFieldsChanged = ref(false);
watch(searchFields, () => {
searchFieldsChanged.value = true;
});
const tableColumns: DataTableColumns<NdmDeviceAlarmLogResultVO> = [
{ title: '告警流水号', key: 'alarmNo' },
{
title: '告警时间',
key: 'alarmDate',
render: renderAlarmDateCell,
},
{
title: '车站',
key: 'stationName',
render: (rowData) => {
const station = stationList.value.find((item) => item.code === rowData.stationCode);
return station?.name || '-';
},
},
{
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) => {
return rowData.alarmCategory === '2' ? '是' : '否';
},
},
{ title: '恢复时间', key: 'updatedTime' },
{
title: '告警确认',
key: 'alarmConfirm',
align: 'center',
render: (rowData) => {
if (rowData.alarmConfirm === '1') {
return h(NTag, { type: 'default' }, { default: () => '已确认' });
}
const { id } = rowData;
return h(
NPopconfirm,
{
onPositiveClick: () => {
rowData.alarmConfirm = '1';
confirmAlarm({ id });
},
},
{
trigger: () => h(NButton, { type: 'info', size: 'small' }, { default: () => '确认' }),
default: () => '确认告警?',
},
);
},
},
// { 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],
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: getExtraFields(),
current: tablePagination.page ?? 1,
size: tablePagination.pageSize ?? 10,
sort: 'id',
order: 'descending',
});
return res;
},
onSuccess: (res) => {
const { records, size, total } = res;
tablePagination.pageSize = parseInt(size);
tablePagination.itemCount = parseInt(total);
tableData.value = records;
},
onError: (error) => {
console.error(error);
window.$message.error(error.message);
},
});
const onClickReset = () => {
resetSearchFields();
tablePagination.page = 1;
tablePagination.pageSize = 10;
tablePagination.itemCount = 0;
getAlarmList();
};
const onClickQuery = () => {
if (searchFieldsChanged.value) {
tablePagination.page = 1;
tablePagination.pageSize = 10;
searchFieldsChanged.value = false;
}
getAlarmList();
};
const { mutate: confirmAlarm } = useMutation({
mutationFn: async (params: { id?: string }) => {
await putNdmDeviceAlarmLog('', {
id: params.id,
alarmConfirm: '1',
});
},
onError: (error, variables) => {
console.error(error);
window.$message.error(error.message);
const { id } = variables;
if (id) {
const alarmLog = tableData.value.find((item) => item.id === id);
if (alarmLog) {
alarmLog['alarmConfirm'] = '2';
}
}
},
});
const { mutate: downloadTableData, isPending: isDownloading } = useMutation({
mutationFn: async () => {
const data = await ndmDeviceAlarmLogDefaultExportByTemplate('', {
model: {},
extra: getExtraFields(),
current: tablePagination.page ?? 1,
size: tablePagination.pageSize ?? 10,
order: 'descending',
sort: 'id',
});
return data;
},
onSuccess: (data) => {
const time = dayjs().format('YYYY-MM-DD_HH-mm-ss');
downloadByData(data, `设备告警记录_${time}.xlsx`);
},
onError: (error) => {
console.error(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">
<NSelect
v-model:value="searchFields.alarmType_in"
:options="[
...Object.keys(AlarmType).map<SelectOption>((alarmTypeKey) => {
return {
label: AlarmType[alarmTypeKey],
value: alarmTypeKey,
};
}),
]"
placeholder="请选择告警类型"
multiple
clearable
/>
</NFormItemGi>
<NFormItemGi :span="1" label="告警级别" label-placement="left">
<NSelect
v-model:value="searchFields.faultLevel_in"
:options="[
...Object.keys(FaultLevel).map<SelectOption>((faultLevel) => {
return {
label: FaultLevel[faultLevel],
value: faultLevel,
};
}),
]"
placeholder="请选择告警级别"
multiple
clearable
/>
</NFormItemGi>
<NFormItemGi :span="1" label="告警时间" label-placement="left">
<NDatePicker v-model:value="searchFields.alarmDate" type="datetimerange" @update:value="undefined" />
</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>