Files
bigscreenWeb/src/views/SagTraceResult_WX/components/manage/securityDetail.vue
2025-10-27 15:08:43 +08:00

1022 lines
27 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

<template>
<div>
<!-- 只有在 iframe 加载完成后才显示按钮 -->
<el-button
style="width: 100%"
type="primary"
color="#223772"
@click="showDetailClick"
v-if="iframeLoaded && !store.state.showMap"
>
<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"
:precision="2"
:step="0.1"
@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"
:precision="2"
:step="0.1"
@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"
:precision="2"
:step="0.1"
@change="
(e) => (e == null ? (eventForm.eventDurationMin = 1) : null)
"
><template #suffix>
<span>s</span>
</template></el-input-number
>
<span> < 持续时间 < </span>
<el-input-number
v-model="eventForm.eventDurationMax"
:min="0"
style="width: 150px"
:max="1000000"
:precision="2"
:step="0.1"
@change="
(e) => (e == null ? (eventForm.eventDurationMax = 1) : null)
"
><template #suffix>
<span>s</span>
</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"
label="持续时间(s)"
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();
// 页码
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 = [
[
"发生时间",
"变电站",
"监测点",
"用户",
"触发类型",
"残余电压(%)",
"持续时间(s)",
],
];
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();
};
defineExpose({
init,
setShowDetail, // 暴露方法给父组件调用
refreshResponsibilityList
});
</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>