Files
bigscreenWeb/src/views/SagTraceResult_WX/components/manage/securityDetail.vue

1022 lines
27 KiB
Vue
Raw Normal View History

2025-09-25 13:32:47 +08:00
<template>
<div>
<!-- 只有在 iframe 加载完成后才显示按钮 -->
<el-button
style="width: 100%"
type="primary"
color="#223772"
@click="showDetailClick"
v-if="iframeLoaded"
>
<span v-if="props.currentFlag == 0">暂降溯源</span>
<span v-if="props.currentFlag == 1">谐波溯源</span>
<span v-if="props.currentFlag == 2">谐波放大</span>
实时信息
<!-- 添加三角标图标 -->
<el-icon :class="['arrow-icon', { 'is-expanded': showDetail }]" size="26">
<CaretTop />
</el-icon>
</el-button>
<!-- 暂降溯源 -->
<div class="tableBox" v-if="showDetail && props.currentFlag == 0">
<el-form
:inline="true"
style="display: flex; justify-content: space-between"
>
<el-form-item label="关键字筛选">
<el-input
clearable
style="width: 150px"
v-model="searchValue"
size="small"
placeholder="电站、测点、用户信息"
></el-input>
<el-popover placement="bottom" :width="550" trigger="click">
<template #reference>
<el-button
size="small"
:icon="DArrowRight"
type="primary"
style="margin-left: 10px"
>更多</el-button
>
</template>
<el-form label-width="auto">
<!-- <el-form-item label="关键字筛选">
<el-input
clearable
style="width: 150px"
v-model="searchValue"
size="small"
placeholder="电站、测点、用户信息"
></el-input>
</el-form-item> -->
<el-form-item label="运维单位" class="mb10">
<el-tree-select
v-model="deptsIndex"
:teleported="false"
:data="deptsList"
:render-after-expand="false"
clearable
style="width: 150px"
:props="{ value: 'id', label: 'name', children: 'children' }"
/>
</el-form-item>
<el-form-item label="触发类型" class="mb10">
<el-select
clearable
:teleported="false"
v-model="eventForm.eventType"
placeholder="请选择触发类型"
style="width: 150px"
>
<el-option
v-for="item in eventTypeList"
:label="item.name"
:value="item.id"
:key="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="残余电压百分比" class="mb10">
<el-input-number
v-model="eventForm.eventValueMin"
:min="0"
style="width: 150px"
:max="100"
2025-10-27 08:55:54 +08:00
:precision="2"
:step="0.1"
2025-09-25 13:32:47 +08:00
@change="
(e) => (e == null ? (eventForm.eventValueMin = 1) : null)
"
><template #suffix>
<span>%</span>
</template></el-input-number
>
<span> < 残余电压 < </span>
<el-input-number
v-model="eventForm.eventValueMax"
:min="0"
style="width: 150px"
:max="100"
2025-10-27 08:55:54 +08:00
:precision="2"
:step="0.1"
2025-09-25 13:32:47 +08:00
@change="
(e) => (e == null ? (eventForm.eventValueMax = 1) : null)
"
><template #suffix>
<span>%</span>
</template></el-input-number
>
</el-form-item>
<el-form-item label="暂降持续事时间" class="mb10">
<el-input-number
v-model="eventForm.eventDurationMin"
:min="0"
style="width: 150px"
:max="1000000"
2025-10-27 08:55:54 +08:00
:precision="2"
:step="0.1"
2025-09-25 13:32:47 +08:00
@change="
(e) => (e == null ? (eventForm.eventDurationMin = 1) : null)
"
><template #suffix>
2025-10-27 08:55:54 +08:00
<span>s</span>
2025-09-25 13:32:47 +08:00
</template></el-input-number
>
<span> < 持续时间 < </span>
<el-input-number
v-model="eventForm.eventDurationMax"
:min="0"
style="width: 150px"
:max="1000000"
2025-10-27 08:55:54 +08:00
:precision="2"
:step="0.1"
2025-09-25 13:32:47 +08:00
@change="
(e) => (e == null ? (eventForm.eventDurationMax = 1) : null)
"
><template #suffix>
2025-10-27 08:55:54 +08:00
<span>s</span>
2025-09-25 13:32:47 +08:00
</template></el-input-number
>
</el-form-item>
</el-form>
</el-popover>
</el-form-item>
<el-form-item style="margin-right: 0px">
<el-button size="small" :icon="Search" type="primary" @click="init()"
>查询</el-button
>
<el-button size="small" :icon="RefreshLeft" @click="clearInit()"
>重置</el-button
>
<el-button
type="primary"
:icon="Download"
@click="exportTable_0"
size="small"
>导出
</el-button>
</el-form-item>
</el-form>
<el-table
:data="realData"
border
style="width: 100%; margin-top: -10px"
:header-cell-style="{ textAlign: 'center' }"
stripe
height="160px"
element-loading-background="#343849c7"
:scrollbar-always-on="true"
size="small"
>
<!-- <el-table-column type="index" align="center" label="序号" width="70" /> -->
<el-table-column label="序号" align="center" type="index" width="70">
<template #default="scope">
<span>{{
(params.pageNum - 1) * params.pageSize + scope.$index + 1
}}</span>
</template>
</el-table-column>
<el-table-column prop="startTime" align="center" label="发生时间" />
<el-table-column
prop="stationName"
align="center"
label="变电站"
width="160"
/>
<el-table-column
prop="lineName"
align="center"
label="监测点"
width="160"
/>
<el-table-column
prop="objName"
label="用户"
align="center"
show-overflow-tooltip
>
<template v-slot="scope">
<span>{{ scope.row.objName ? scope.row.objName : "/" }}</span>
</template>
</el-table-column>
<el-table-column
prop="eventType"
align="center"
label="触发类型"
width="100"
/>
<el-table-column
prop="featureAmplitude"
align="center"
label="残余电压(%)"
width="120"
>
<template v-slot="scope">
<span>{{ (scope.row.featureAmplitude * 100).toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column
prop="duration"
align="center"
2025-10-27 08:55:54 +08:00
label="持续时间(s)"
2025-09-25 13:32:47 +08:00
width="100"
/>
<el-table-column fixed="right" label="操作" width="100" align="center">
<template #default="scope">
<el-button
link
type="primary"
size="small"
@click.stop="trendCharts(scope.row)"
>波形</el-button
>
</template>
</el-table-column>
</el-table>
<div class="table-pagination">
<el-pagination
small
:currentPage="params.pageNum"
:page-size="params.pageSize"
:page-sizes="[10, 20, 50, 100]"
background
:layout="'sizes,total, ->, prev, pager, next, jumper'"
:total="params.total"
@size-change="onTableSizeChange"
@current-change="onTableCurrentChange"
></el-pagination>
</div>
</div>
<!-- 谐波放大 -->
<div class="tableBox" v-if="showDetail && props.currentFlag == 2">
<div style="display: flex; justify-content: flex-end">
<el-button
type="primary"
:icon="Download"
@click="exportTable_2"
size="small"
>导出
</el-button>
</div>
<el-table
:data="amplifyTableData"
border
style="width: 100%; margin-top: 13px"
:header-cell-style="{ textAlign: 'center' }"
stripe
height="160px"
element-loading-background="#343849c7"
:scrollbar-always-on="true"
size="small"
>
<!-- <el-table-column type="index" align="center" label="序号" width="70" /> -->
<el-table-column label="序号" align="center" type="index" width="70">
<template #default="scope">
<span>{{
(params1.pageNum - 1) * params1.pageSize + scope.$index + 1
}}</span>
</template>
</el-table-column>
<!-- <el-table-column
prop="startTime"
align="center"
label="开始时间"
width="180"
/>
<el-table-column
prop="endTime"
align="center"
label="结束时间"
width="180"
/> -->
<el-table-column
prop="timeRange"
align="center"
label="谐波放大时间范围"
/>
<el-table-column
prop="monitorName"
align="center"
label="监测点"
width="200"
/>
<el-table-column
prop="stationName"
align="center"
label="所属电站"
width="200"
/>
<el-table-column
prop="objName"
label="用户"
align="center"
show-overflow-tooltip
/>
<el-table-column
prop="harmonicCount"
align="center"
label="谐波次数"
width="90"
/>
<el-table-column prop="phase" align="center" label="相别" width="80" />
<el-table-column
prop="duration"
align="center"
label="持续时间(min)"
width="110"
>
<template v-slot="scope">
<span>{{ Math.floor(scope.row.duration / 60) }}</span>
</template>
</el-table-column>
<el-table-column prop="address" align="center" label="操作" width="90">
<template #default="scope">
<el-button
size="small"
type="primary"
link
@click="arendChart(scope.row)"
>趋势图</el-button
>
</template>
</el-table-column>
</el-table>
<div class="table-pagination">
<el-pagination
small
:currentPage="params1.pageNum"
:page-size="params1.pageSize"
:page-sizes="[10, 20, 50, 100]"
background
:layout="'sizes,total, ->, prev, pager, next, jumper'"
:total="params1.total"
@size-change="onTableSizeChange1"
@current-change="onTableCurrentChange1"
></el-pagination>
</div>
</div>
<!-- 谐波溯源 -->
<div class="tableBox" v-if="showDetail && props.currentFlag == 1">
<div style="display: flex; justify-content: flex-end">
<el-button
type="primary"
:icon="Download"
@click="exportTable_1"
size="small"
>导出
</el-button>
</div>
<el-table
:data="traceabilityTableData"
border
style="width: 100%; margin-top: 12px"
:header-cell-style="{ textAlign: 'center' }"
stripe
height="160px"
element-loading-background="#343849c7"
:scrollbar-always-on="true"
size="small"
>
<!-- <el-table-column type="index" align="center" label="序号" width="70" /> -->
<el-table-column label="序号" align="center" type="index" width="70">
<template #default="scope">
<span>{{
(params2.pageNum - 1) * params2.pageSize + scope.$index + 1
}}</span>
</template>
</el-table-column>
<el-table-column
prop="lineName"
align="center"
label="背景监测点"
width="180"
/>
<el-table-column
prop="subName"
label="所属变电站"
align="center"
width="200"
show-overflow-tooltip
/>
<el-table-column
prop="dataType"
align="center"
label="数据类型"
width="160"
/>
<el-table-column
prop="dataTimes"
align="center"
label="谐波次数"
width="160"
/>
<el-table-column
prop="userDataName"
align="center"
label="责任对象"
show-overflow-tooltip
/>
<el-table-column
prop="updateTime"
align="center"
label="计算时间"
width="200"
/>
</el-table>
<div class="table-pagination">
<el-pagination
small
:currentPage="params2.pageNum"
:page-size="params2.pageSize"
:page-sizes="[10, 20, 50, 100]"
background
:layout="'sizes,total, ->, prev, pager, next, jumper'"
:total="params2.total"
@size-change="onTableSizeChange2"
@current-change="onTableCurrentChange2"
></el-pagination>
</div>
</div>
<!-- 波形图 -->
<el-dialog
:close-on-click-modal="false"
draggable
v-model="trendVisible"
v-if="trendVisible"
title="波形"
width="70%"
>
<waveForm ref="waveFormRef" />
</el-dialog>
<!-- 谐波放大表格趋势图 -->
<TrendChart ref="trendChartRef"></TrendChart>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted, onUnmounted, watch, computed } from "vue";
import type { TabsPaneContext } from "element-plus";
import {
getEventList,
getInfoList,
loginDeptTree,
getDicDataByTypeCode,
responsibilityList,
} from "@/api/manage_wx";
import { useStore } from "vuex";
import waveForm from "@/components/BX/waveForm.vue";
import TrendChart from "../eventStatistics/trendChart.vue";
import {
DArrowRight,
Search,
RefreshLeft,
CaretTop,
Download,
ArrowDown,
} from "@element-plus/icons-vue";
import * as XLSX from "xlsx";
// 定义 props 接收父组件传递的 flag 值
const props = defineProps({
currentFlag: {
type: Number,
default: 0,
},
});
// 定义 emit
const emit = defineEmits(["show-detail-change"]);
// 监听 currentFlag 的变化
watch(
() => props.currentFlag,
(newVal, oldVal) => {
if (newVal == 2) {
getInfoListAmplify();
}
if (newVal == 1) {
initialResponsibilityList();
}
},
{ immediate: true }
);
const store = useStore();
const showDetail = ref(false);
let tableData = reactive([]);
const iframeLoaded = ref(false); // 新增iframe 加载状态
const realData = ref([]); //报警信息-》实时信息
const trendVisible = ref(false);
const waveFormRef = ref();
const amplifyTableData = ref([]); //谐波放大表格
const loading = ref(false);
const trendChartRef = ref();
2025-09-25 13:32:47 +08:00
// 页码
const params = reactive({
pageNum: 1,
pageSize: 20,
total: 0,
});
// 页码
const params1 = reactive({
pageNum: 1,
pageSize: 20,
total: 0,
});
const params2 = reactive({
pageNum: 1,
pageSize: 20,
total: 0,
});
// 查询
const searchValue = ref("");
const deptsIndex = ref("");
const deptsList = ref([]); //部门列表
const eventTypeList = ref([]); //触发类型
const eventForm: any = reactive({
eventValueMin: null,
eventValueMax: null,
eventDurationMin: null,
eventDurationMax: null,
eventType: null,
});
const clearInit = async () => {
searchValue.value = "";
eventForm.eventValueMin = null;
eventForm.eventValueMax = null;
eventForm.eventDurationMin = null;
eventForm.eventDurationMax = null;
eventForm.eventType = null;
deptsIndex.value = null;
await eventList();
};
// 部门
const initDept = () => {
loginDeptTree({
deptIndex: store.state.deptId,
}).then((res: any) => {
deptsList.value = res.data;
});
};
// 触发类型
const dicDataByTypeCode = () => {
getDicDataByTypeCode({
dictTypeCode: "Event_Statis",
}).then((res: any) => {
eventTypeList.value = res.data;
});
};
const onTableSizeChange = (size: number) => {
params.pageSize = size;
eventList();
};
const onTableCurrentChange = (page: number) => {
params.pageNum = page;
eventList();
};
const onTableSizeChange1 = (size: number) => {
params1.pageSize = size;
getInfoListAmplify();
};
const onTableCurrentChange1 = (page: number) => {
params1.pageNum = page;
getInfoListAmplify();
};
const onTableSizeChange2 = (size: number) => {
params2.pageSize = size;
initialResponsibilityList();
};
const onTableCurrentChange2 = (page: number) => {
params2.pageNum = page;
initialResponsibilityList();
};
// 谐波放大表格趋势图
const arendChart = (row: any) => {
trendChartRef.value.open(row);
};
// 谐波放大表格
const getInfoListAmplify = () => {
loading.value = true;
getInfoList({
deptId: store.state.deptId,
searchBeginTime: store.state.timeValue[0],
searchEndTime: store.state.timeValue[1],
pageNum: params1.pageNum,
pageSize: params1.pageSize,
}).then((res: any) => {
if (res.code == "A0000") {
// 对返回的数据进行处理,拼接字段
// amplifyTableData.value = res.data.records;
params1.total = res.data.total;
amplifyTableData.value = res.data.records.map((item: any) => {
return {
...item,
// 拼接新的字段用于展示
timeRange: `${item.startTime || ""}~${item.endTime || ""}`,
};
});
} else {
amplifyTableData.value = [];
}
loading.value = false;
});
};
// 谐波溯源表格
const traceabilityTableData = ref([]);
// 谐波溯源表格
const initialResponsibilityList = () => {
loading.value = true;
responsibilityList({
deptId: store.state.deptId,
searchBeginTime: store.state.timeValue[0],
searchEndTime: store.state.timeValue[1],
pageNum: params2.pageNum,
pageSize: params2.pageSize,
})
.then((res: any) => {
if (res.code == "A0000") {
traceabilityTableData.value = res.data.records;
params2.total = res.data.total;
// 直接将 records 赋值给 tableData
} else {
traceabilityTableData.value = [];
}
loading.value = false;
})
.catch((error) => {
traceabilityTableData.value = [];
});
};
// 父页面的脚本
const handleMessage = (event: MessageEvent) => {
try {
// 监听 iframe 加载完成的消息
if (event.data && event.data.type === "IFRAME_LOADED") {
iframeLoaded.value = event.data.data.loaded;
//console.log("iframe 加载完成,显示报警信息详情按钮");
return;
}
// 检查 event.data.data 是否存在且是数组
if (event.data && event.data.data) {
if (Array.isArray(event.data.data)) {
// 如果是数组,使用扩展运算符
tableData.splice(0, tableData.length, ...event.data.data);
} else {
// 如果不是数组,将其转换为数组或直接赋值
tableData.splice(0, tableData.length, event.data.data);
}
}
} catch (error) {
console.error("处理消息时出错:", error);
console.error("接收到的数据:", event.data);
}
};
// 添加接收父组件控制展开的方法
const setShowDetail = (value: boolean) => {
showDetail.value = value;
emit("show-detail-change", value);
};
const showDetailClick = () => {
showDetail.value = !showDetail.value;
emit("show-detail-change", showDetail.value);
};
onMounted(() => {
initDept();
dicDataByTypeCode();
window.addEventListener("message", handleMessage);
});
onUnmounted(() => {
window.removeEventListener("message", handleMessage);
});
//波形
const waveClick = () => {};
const formComFlag = (row: any) => {
if (row.comFlag == 0) {
return "中断";
} else if (row.comFlag == 1) {
return "正常";
}
};
// 暂降实时数据
const eventList = () => {
getEventList({
deptId: store.state.deptId,
searchBeginTime: store.state.timeValue[0],
searchEndTime: store.state.timeValue[1],
pageNum: params.pageNum,
pageSize: params.pageSize,
searchValue: searchValue.value,
...eventForm,
eventValueMin:
eventForm.eventValueMin == null ? null : eventForm.eventValueMin / 100,
eventValueMax:
eventForm.eventValueMax == null ? null : eventForm.eventValueMax / 100,
})
.then((res: any) => {
if (res.code == "A0000") {
realData.value = res.data.records;
params.total = res.data.total;
}
})
.catch((error) => {
console.error("获取实时信息失败:", error);
});
};
watch(
() => store.state.realData,
(newData) => {
if (newData) {
realData.value = newData;
params.total = realData.value.length;
}
},
{ immediate: true }
);
//点击趋势图 波形
const trendCharts = (row: any) => {
row.eventdetail_index = row.eventId;
trendVisible.value = true;
setTimeout(() => {
waveFormRef.value?.open({
...row,
bdname: row.stationName,
pointname: row.lineName,
timeid: row.startTime,
eventvalue: row.featureAmplitude,
persisttime: row.duration,
});
}, 500);
};
// 暂降溯源导出
const exportTable_0 = async () => {
let columnExpor: any = [
[
"发生时间",
"变电站",
"监测点",
"用户",
"触发类型",
"残余电压(%)",
2025-10-27 08:55:54 +08:00
"持续时间(s)",
2025-09-25 13:32:47 +08:00
],
];
let list = [];
await getEventList({
deptId: store.state.deptId,
searchBeginTime: store.state.timeValue[0],
searchEndTime: store.state.timeValue[1],
searchValue: searchValue.value,
...eventForm,
eventValueMin:
eventForm.eventValueMin == null ? null : eventForm.eventValueMin / 100,
eventValueMax:
eventForm.eventValueMax == null ? null : eventForm.eventValueMax / 100,
pageNum: 1,
pageSize: params.total,
}).then((res) => {
let data = res.data.records.map((item) => {
return [
item.startTime,
item.stationName,
item.lineName,
item.objName,
item.eventType,
(item.featureAmplitude * 100).toFixed(2),
item.duration,
];
});
list = [...columnExpor, ...data];
// 创建工作表
const worksheet = XLSX.utils.aoa_to_sheet(list);
worksheet["!cols"] = list.map((col) => ({ wch: 20 }));
// 创建工作簿
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "暂降溯源实时信息");
// 写出文件
XLSX.writeFile(workbook, "暂降溯源实时信息" + ".xlsx");
});
};
// 谐波溯源导出
const exportTable_1 = async () => {
let columnExpor: any = [
[
"背景监测点",
"所属变电站",
"数据类型",
"谐波次数",
"责任对象",
"计算时间",
],
];
let list = [];
await responsibilityList({
deptId: store.state.deptId,
searchBeginTime: store.state.timeValue[0],
searchEndTime: store.state.timeValue[1],
pageNum: 1,
pageSize: params2.total,
}).then((res) => {
let data = res.data.records.map((item) => {
return [
item.lineName,
item.subName,
item.dataType,
item.dataTimes,
item.userDataName,
item.updateTime,
];
});
list = [...columnExpor, ...data];
// 创建工作表
const worksheet = XLSX.utils.aoa_to_sheet(list);
worksheet["!cols"] = list.map((col) => ({ wch: 20 }));
// 创建工作簿
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "谐波溯源实时信息");
// 写出文件
XLSX.writeFile(workbook, "谐波溯源实时信息" + ".xlsx");
});
};
// 谐波放大导出
const exportTable_2 = async () => {
let columnExpor: any = [
[
"谐波放大时间范围",
"监测点",
"所属电站",
"用户",
"谐波次数",
"相别",
"持续时间(min)",
],
];
let list = [];
await getInfoList({
deptId: store.state.deptId,
searchBeginTime: store.state.timeValue[0],
searchEndTime: store.state.timeValue[1],
pageNum: 1,
pageSize: params1.total,
}).then((res) => {
let data = res.data.records.map((item) => {
return [
item.startTime + "~" + item.endTime,
item.monitorName,
item.stationName,
item.objName,
item.harmonicCount,
item.phase,
Math.floor(item.duration / 60),
];
});
list = [...columnExpor, ...data];
// 创建工作表
const worksheet = XLSX.utils.aoa_to_sheet(list);
worksheet["!cols"] = list.map((col) => ({ wch: 20 }));
// 创建工作簿
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "谐波放大实时信息");
// 写出文件
XLSX.writeFile(workbook, "谐波放大实时信息" + ".xlsx");
});
};
const init = () => {
eventList();
};
const refreshResponsibilityList = () => {
initialResponsibilityList();
};
2025-09-25 13:32:47 +08:00
defineExpose({
init,
setShowDetail, // 暴露方法给父组件调用
refreshResponsibilityList
2025-09-25 13:32:47 +08:00
});
</script>
<style lang="less" scoped>
.tableBox {
background-color: #343849c7;
}
.el-scrollbar__bar.is-vertical {
background: #ccc !important;
/* 轨道背景色 */
border-radius: 10px;
/* 轨道圆角 */
}
/* 滚动条滑块 */
.el-scrollbar__thumb {
background: #1d83ce !important;
/* 滑块颜色 */
border-radius: 10px;
/* 滑块圆角 */
}
.demo-tabs > .el-tabs__content {
padding: 32px;
color: #6b778c;
font-size: 32px;
font-weight: 600;
}
::v-deep(.el-tabs__item) {
color: #fff;
}
::v-deep(.el-tabs__item.is-active) {
color: var(--el-color-primary);
}
::v-deep(.el-tabs__nav-wrap::after) {
color: var(--el-color-primary);
}
::v-deep(.el-tabs__active-bar) {
color: var(--el-color-primary);
}
.table-pagination {
box-sizing: border-box;
width: 100%;
max-width: 100%;
background-color: var(--ba-bg-color-overlay);
padding-top: 10px;
}
.arrow-icon {
margin-left: 8px;
transition: transform 0.3s ease;
font-size: 26px;
color: #fff;
}
.arrow-icon.is-expanded {
transform: rotate(180deg);
}
</style>