|
|
@@ -0,0 +1,377 @@
|
|
|
+<template>
|
|
|
+ <div class="fault-page">
|
|
|
+ <a-card :bordered="false">
|
|
|
+ <a-form :model="queryParams" layout="inline">
|
|
|
+ <a-form-item label="故障标题">
|
|
|
+ <a-input v-model:value="queryParams.faultTitle" allow-clear placeholder="请输入故障标题" />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="场站">
|
|
|
+ <a-select
|
|
|
+ v-model:value="queryParams.stationId"
|
|
|
+ allow-clear
|
|
|
+ show-search
|
|
|
+ class="query-select"
|
|
|
+ option-filter-prop="label"
|
|
|
+ :options="stationOptions"
|
|
|
+ />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="状态">
|
|
|
+ <a-select v-model:value="queryParams.status" allow-clear class="query-select" :options="statusOptions" />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item>
|
|
|
+ <a-space>
|
|
|
+ <a-button type="primary" :loading="loading" @click="handleQuery">查询</a-button>
|
|
|
+ <a-button @click="resetQuery">重置</a-button>
|
|
|
+ <a-button v-hasPermi="['alarm:faultProcess:add']" type="primary" @click="openForm()">新增</a-button>
|
|
|
+ </a-space>
|
|
|
+ </a-form-item>
|
|
|
+ </a-form>
|
|
|
+ </a-card>
|
|
|
+
|
|
|
+ <a-card :bordered="false" class="section">
|
|
|
+ <a-table
|
|
|
+ :columns="columns"
|
|
|
+ :data-source="rows"
|
|
|
+ :loading="loading"
|
|
|
+ :pagination="pagination"
|
|
|
+ row-key="id"
|
|
|
+ @change="handleTableChange"
|
|
|
+ >
|
|
|
+ <template #bodyCell="{ column, record }">
|
|
|
+ <template v-if="column.key === 'stationId'">
|
|
|
+ {{ stationText(record.stationId) }}
|
|
|
+ </template>
|
|
|
+ <template v-else-if="column.key === 'faultType'">
|
|
|
+ {{ dictText(faultTypeOptions, record.faultType) }}
|
|
|
+ </template>
|
|
|
+ <template v-else-if="column.key === 'faultLevel'">
|
|
|
+ {{ dictText(faultLevelOptions, record.faultLevel) }}
|
|
|
+ </template>
|
|
|
+ <template v-else-if="column.key === 'status'">
|
|
|
+ <a-tag :color="statusColor(record.status)">{{ statusText(record.status) }}</a-tag>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="column.key === 'operation'">
|
|
|
+ <a-space>
|
|
|
+ <a v-hasPermi="['alarm:faultProcess:edit']" @click="openForm(record)">编辑</a>
|
|
|
+ <a @click="openLogs(record)">日志</a>
|
|
|
+ <a-dropdown v-hasPermi="['alarm:faultProcess:edit']">
|
|
|
+ <a>处理</a>
|
|
|
+ <template #overlay>
|
|
|
+ <a-menu @click="({ key }) => openAction(record, key)">
|
|
|
+ <a-menu-item key="assign">认领/派单</a-menu-item>
|
|
|
+ <a-menu-item key="handle">处理记录</a-menu-item>
|
|
|
+ <a-menu-item key="resolve">解决</a-menu-item>
|
|
|
+ <a-menu-item key="close">关闭</a-menu-item>
|
|
|
+ <a-menu-item key="falseAlarm">误报</a-menu-item>
|
|
|
+ </a-menu>
|
|
|
+ </template>
|
|
|
+ </a-dropdown>
|
|
|
+ <a-popconfirm title="确认删除该故障处理记录?" @confirm="removeRecord(record)">
|
|
|
+ <a v-hasPermi="['alarm:faultProcess:remove']" class="danger-link">删除</a>
|
|
|
+ </a-popconfirm>
|
|
|
+ </a-space>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </a-table>
|
|
|
+ </a-card>
|
|
|
+
|
|
|
+ <a-modal v-model:open="formOpen" :title="form.id ? '编辑故障处理' : '新增故障处理'" width="720px" @ok="submitForm">
|
|
|
+ <a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
|
|
|
+ <a-form-item label="场站" name="stationId">
|
|
|
+ <a-select
|
|
|
+ v-model:value="form.stationId"
|
|
|
+ allow-clear
|
|
|
+ show-search
|
|
|
+ option-filter-prop="label"
|
|
|
+ :options="stationOptions"
|
|
|
+ />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="故障标题" name="faultTitle">
|
|
|
+ <a-input v-model:value="form.faultTitle" allow-clear />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="故障类型" name="faultType">
|
|
|
+ <a-select v-model:value="form.faultType" allow-clear :options="faultTypeOptions" />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="故障等级" name="faultLevel">
|
|
|
+ <a-select v-model:value="form.faultLevel" allow-clear :options="faultLevelOptions" />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="故障原因" name="faultReason">
|
|
|
+ <a-textarea v-model:value="form.faultReason" :rows="3" allow-clear />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="处理结果" name="processResult">
|
|
|
+ <a-textarea v-model:value="form.processResult" :rows="3" allow-clear />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="备注" name="remark">
|
|
|
+ <a-textarea v-model:value="form.remark" :rows="2" allow-clear />
|
|
|
+ </a-form-item>
|
|
|
+ </a-form>
|
|
|
+ </a-modal>
|
|
|
+
|
|
|
+ <a-modal v-model:open="actionOpen" :title="actionTitle" width="640px" @ok="submitAction">
|
|
|
+ <a-form :model="actionForm" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
|
|
|
+ <a-form-item v-if="['resolve', 'close', 'falseAlarm'].includes(actionType)" label="处理结果">
|
|
|
+ <a-textarea v-model:value="actionForm.processResult" :rows="3" allow-clear />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item v-if="actionType === 'handle'" label="处理内容">
|
|
|
+ <a-textarea v-model:value="actionForm.content" :rows="4" allow-clear />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="备注">
|
|
|
+ <a-textarea v-model:value="actionForm.remark" :rows="2" allow-clear />
|
|
|
+ </a-form-item>
|
|
|
+ </a-form>
|
|
|
+ </a-modal>
|
|
|
+
|
|
|
+ <a-modal v-model:open="logOpen" title="处理日志" width="860px" :footer="null">
|
|
|
+ <a-table :columns="logColumns" :data-source="logRows" :pagination="false" row-key="id" size="small">
|
|
|
+ <template #bodyCell="{ column, record }">
|
|
|
+ <template v-if="column.key === 'actionType'">
|
|
|
+ {{ actionText(record.actionType) }}
|
|
|
+ </template>
|
|
|
+ <template v-else-if="column.key === 'beforeStatus'">
|
|
|
+ {{ statusText(record.beforeStatus) }}
|
|
|
+ </template>
|
|
|
+ <template v-else-if="column.key === 'afterStatus'">
|
|
|
+ {{ statusText(record.afterStatus) }}
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </a-table>
|
|
|
+ </a-modal>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup name="ElectricityFaultProcess">
|
|
|
+import { onMounted, reactive, ref } from 'vue';
|
|
|
+import { message } from 'ant-design-vue';
|
|
|
+import { listStation } from '@/api/config/station';
|
|
|
+import { getDicts } from '@/api/system/dict/data';
|
|
|
+import {
|
|
|
+ addFaultProcess,
|
|
|
+ assignFaultProcess,
|
|
|
+ closeFaultProcess,
|
|
|
+ delFaultProcess,
|
|
|
+ falseAlarmFaultProcess,
|
|
|
+ getFaultProcessLogs,
|
|
|
+ handleFaultProcess,
|
|
|
+ listFaultProcess,
|
|
|
+ resolveFaultProcess,
|
|
|
+ updateFaultProcess
|
|
|
+} from '@/api/alarm/faultProcess';
|
|
|
+import { buildStationNameMap, toStationOptions } from '../components/stationAdapter';
|
|
|
+
|
|
|
+const loading = ref(false);
|
|
|
+const rows = ref([]);
|
|
|
+const stationOptions = ref([]);
|
|
|
+const stationNameMap = ref(new Map());
|
|
|
+const faultTypeOptions = ref([]);
|
|
|
+const faultLevelOptions = ref([]);
|
|
|
+const statusOptions = ref([]);
|
|
|
+const actionOptions = ref([]);
|
|
|
+const formOpen = ref(false);
|
|
|
+const actionOpen = ref(false);
|
|
|
+const logOpen = ref(false);
|
|
|
+const logRows = ref([]);
|
|
|
+const formRef = ref();
|
|
|
+const actionType = ref('');
|
|
|
+const actionTitle = ref('');
|
|
|
+const currentRecord = ref();
|
|
|
+
|
|
|
+const queryParams = reactive({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ faultTitle: undefined,
|
|
|
+ stationId: undefined,
|
|
|
+ status: undefined
|
|
|
+});
|
|
|
+
|
|
|
+const pagination = reactive({
|
|
|
+ current: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ total: 0,
|
|
|
+ showSizeChanger: true
|
|
|
+});
|
|
|
+
|
|
|
+const form = reactive({});
|
|
|
+const actionForm = reactive({});
|
|
|
+
|
|
|
+
|
|
|
+const rules = {
|
|
|
+ stationId: [{ required: true, message: '请选择场站', trigger: 'change' }],
|
|
|
+ faultTitle: [{ required: true, message: '请输入故障标题', trigger: 'blur' }],
|
|
|
+ faultType: [{ required: true, message: '请选择故障类型', trigger: 'change' }],
|
|
|
+ faultLevel: [{ required: true, message: '请选择故障等级', trigger: 'change' }]
|
|
|
+};
|
|
|
+
|
|
|
+const columns = [
|
|
|
+ { title: 'ID', dataIndex: 'id', width: 80 },
|
|
|
+ { title: '故障标题', dataIndex: 'faultTitle' },
|
|
|
+ { title: '场站', dataIndex: 'stationId', key: 'stationId', width: 160 },
|
|
|
+ { title: '故障类型', dataIndex: 'faultType', key: 'faultType', width: 120 },
|
|
|
+ { title: '等级', dataIndex: 'faultLevel', key: 'faultLevel', width: 100 },
|
|
|
+ { title: '状态', dataIndex: 'status', key: 'status', width: 100 },
|
|
|
+ { title: '处理人', dataIndex: 'handlerName', width: 120 },
|
|
|
+ { title: '创建时间', dataIndex: 'createTime', width: 180 },
|
|
|
+ { title: '操作', key: 'operation', width: 220, fixed: 'right' }
|
|
|
+];
|
|
|
+
|
|
|
+const logColumns = [
|
|
|
+ { title: '动作', dataIndex: 'actionType', key: 'actionType', width: 120 },
|
|
|
+ { title: '处理前', dataIndex: 'beforeStatus', key: 'beforeStatus', width: 100 },
|
|
|
+ { title: '处理后', dataIndex: 'afterStatus', key: 'afterStatus', width: 100 },
|
|
|
+ { title: '操作人', dataIndex: 'operatorName', width: 120 },
|
|
|
+ { title: '操作时间', dataIndex: 'actionTime', width: 160 },
|
|
|
+ { title: '内容', dataIndex: 'content' }
|
|
|
+];
|
|
|
+
|
|
|
+const normalizeRows = (res) => res?.rows || res?.data || [];
|
|
|
+const normalizeDict = (res, numberValue = false) => (res?.data || []).map((item) => ({
|
|
|
+ label: item.dictLabel,
|
|
|
+ value: numberValue ? Number(item.dictValue) : item.dictValue
|
|
|
+}));
|
|
|
+
|
|
|
+const stationText = (stationId) => stationNameMap.value.get(stationId) || stationId || '-';
|
|
|
+const dictText = (options, value) => options.find((item) => String(item.value) === String(value))?.label || value || '-';
|
|
|
+const actionText = (action) => dictText(actionOptions.value, action);
|
|
|
+const statusText = (status) => dictText(statusOptions.value, status);
|
|
|
+const statusColor = (status) => {
|
|
|
+ const colorMap = {
|
|
|
+ pending: 'default',
|
|
|
+ processing: 'processing',
|
|
|
+ resolved: 'success',
|
|
|
+ closed: 'blue',
|
|
|
+ false_alarm: 'warning'
|
|
|
+ };
|
|
|
+ return colorMap[status] || 'default';
|
|
|
+};
|
|
|
+
|
|
|
+const resetObject = (target, source = {}) => {
|
|
|
+ Object.keys(target).forEach((key) => delete target[key]);
|
|
|
+ Object.assign(target, source);
|
|
|
+};
|
|
|
+
|
|
|
+const loadOptions = async () => {
|
|
|
+ const [stationRes, typeRes, levelRes, statusRes, actionRes] = await Promise.all([
|
|
|
+ listStation({ pageNum: 1, pageSize: 500 }),
|
|
|
+ getDicts('fault_type'),
|
|
|
+ getDicts('fault_level'),
|
|
|
+ getDicts('fault_process_status'),
|
|
|
+ getDicts('fault_process_action')
|
|
|
+ ]);
|
|
|
+ const stationRows = normalizeRows(stationRes);
|
|
|
+ stationOptions.value = toStationOptions(stationRows);
|
|
|
+ stationNameMap.value = buildStationNameMap(stationRows);
|
|
|
+ faultTypeOptions.value = normalizeDict(typeRes);
|
|
|
+ faultLevelOptions.value = normalizeDict(levelRes, true);
|
|
|
+ statusOptions.value = normalizeDict(statusRes);
|
|
|
+ actionOptions.value = normalizeDict(actionRes);
|
|
|
+};
|
|
|
+
|
|
|
+const handleQuery = async () => {
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ const res = await listFaultProcess(queryParams);
|
|
|
+ rows.value = normalizeRows(res);
|
|
|
+ pagination.total = res?.total || rows.value.length;
|
|
|
+ pagination.current = queryParams.pageNum;
|
|
|
+ pagination.pageSize = queryParams.pageSize;
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const resetQuery = () => {
|
|
|
+ queryParams.pageNum = 1;
|
|
|
+ queryParams.faultTitle = undefined;
|
|
|
+ queryParams.stationId = undefined;
|
|
|
+ queryParams.status = undefined;
|
|
|
+ handleQuery();
|
|
|
+};
|
|
|
+
|
|
|
+const handleTableChange = (page) => {
|
|
|
+ queryParams.pageNum = page.current;
|
|
|
+ queryParams.pageSize = page.pageSize;
|
|
|
+ handleQuery();
|
|
|
+};
|
|
|
+
|
|
|
+const openForm = (record) => {
|
|
|
+ const source = record ? { ...record } : { sourceType: 'manual', status: 'pending', delFlag: '0' };
|
|
|
+ if (source.faultLevel !== undefined && source.faultLevel !== null) {
|
|
|
+ source.faultLevel = Number(source.faultLevel);
|
|
|
+ }
|
|
|
+ if (!record) {
|
|
|
+ delete source.alarmEventId;
|
|
|
+ }
|
|
|
+ delete source.handlerId;
|
|
|
+ delete source.handlerName;
|
|
|
+ resetObject(form, source);
|
|
|
+ formOpen.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+const submitForm = async () => {
|
|
|
+ await formRef.value?.validate();
|
|
|
+ if (form.id) {
|
|
|
+ await updateFaultProcess(form);
|
|
|
+ } else {
|
|
|
+ await addFaultProcess(form);
|
|
|
+ }
|
|
|
+ message.success('保存成功');
|
|
|
+ formOpen.value = false;
|
|
|
+ handleQuery();
|
|
|
+};
|
|
|
+
|
|
|
+const removeRecord = async (record) => {
|
|
|
+ await delFaultProcess(record.id);
|
|
|
+ message.success('删除成功');
|
|
|
+ handleQuery();
|
|
|
+};
|
|
|
+
|
|
|
+const openAction = (record, type) => {
|
|
|
+ currentRecord.value = record;
|
|
|
+ actionType.value = type;
|
|
|
+ const titleMap = {
|
|
|
+ assign: '认领/派单',
|
|
|
+ handle: '处理记录',
|
|
|
+ resolve: '解决故障',
|
|
|
+ close: '关闭故障',
|
|
|
+ falseAlarm: '标记误报'
|
|
|
+ };
|
|
|
+ actionTitle.value = titleMap[type];
|
|
|
+ resetObject(actionForm, {
|
|
|
+ processResult: record.processResult,
|
|
|
+ remark: undefined,
|
|
|
+ content: undefined
|
|
|
+ });
|
|
|
+ actionOpen.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+const submitAction = async () => {
|
|
|
+ const id = currentRecord.value.id;
|
|
|
+ const actionMap = {
|
|
|
+ assign: () => assignFaultProcess(id, actionForm),
|
|
|
+ handle: () => handleFaultProcess(id, actionForm),
|
|
|
+ resolve: () => resolveFaultProcess(id, actionForm),
|
|
|
+ close: () => closeFaultProcess(id, actionForm),
|
|
|
+ falseAlarm: () => falseAlarmFaultProcess(id, actionForm)
|
|
|
+ };
|
|
|
+ await actionMap[actionType.value]();
|
|
|
+ message.success('操作成功');
|
|
|
+ actionOpen.value = false;
|
|
|
+ handleQuery();
|
|
|
+};
|
|
|
+
|
|
|
+const openLogs = async (record) => {
|
|
|
+ const res = await getFaultProcessLogs(record.id);
|
|
|
+ logRows.value = res?.data || [];
|
|
|
+ logOpen.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ await loadOptions();
|
|
|
+ await handleQuery();
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less" scoped>
|
|
|
+.fault-page { padding: 16px; }
|
|
|
+.section { margin-top: 16px; }
|
|
|
+.query-select { width: 180px; }
|
|
|
+</style>
|