1420 lines
38 KiB
Vue
1420 lines
38 KiB
Vue
<template>
|
||
<!--右中 暂降事件聚合成功后数量的统计 -->
|
||
<div class="plan">
|
||
<div class="titleBox_1">
|
||
<div class="titleBox_1">
|
||
<div style="display: flex">
|
||
<div class="titles" style="margin-left: 0px">
|
||
<!-- 暂降溯源 -->
|
||
<div
|
||
class="react-right"
|
||
:class="flag == 0 ? 'titleClick' : ''"
|
||
@click="flag = 0"
|
||
>
|
||
<span class="text">
|
||
暂降溯源<el-icon style="top: 2px; left: 2px"
|
||
><Lightning
|
||
/></el-icon>
|
||
</span>
|
||
</div>
|
||
|
||
<!-- 谐波溯源 -->
|
||
<div
|
||
class="react-right"
|
||
:class="flag == 1 ? 'titleClick' : ''"
|
||
@click="flag = 1"
|
||
>
|
||
<span class="text">
|
||
谐波溯源<el-icon style="top: 2px; left: 2px"
|
||
><DataLine
|
||
/></el-icon>
|
||
</span>
|
||
</div>
|
||
|
||
<!-- 谐波放大 -->
|
||
<div
|
||
class="react-right"
|
||
style="margin-left: 10px"
|
||
:class="flag == 2 ? 'titleClick' : ''"
|
||
@click="flag = 2"
|
||
>
|
||
<span class="text">
|
||
谐波放大<el-icon style="top: 2px; left: 2px"
|
||
><ZoomIn
|
||
/></el-icon>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 暂降聚合 -->
|
||
<div v-if="flag == 0">
|
||
<div class="titleBox" style="margin-top: 10px">暂降事件</div>
|
||
<div
|
||
style="
|
||
margin-left: 84%;
|
||
height: 34px;
|
||
line-height: 20px;
|
||
margin-top: -30px;
|
||
"
|
||
>
|
||
<el-button
|
||
size="small"
|
||
:icon="HelpFilled"
|
||
type="primary"
|
||
@click="polymerizationClick(1)"
|
||
>溯源
|
||
</el-button>
|
||
</div>
|
||
<div
|
||
style="display: flex"
|
||
v-loading="loading"
|
||
element-loading-background="#343849c7"
|
||
>
|
||
<div class="polymerization" style="width: 24%; height: 230px">
|
||
<div
|
||
style="background-color: #f8ff4470; cursor: pointer"
|
||
@click="polymerizationClick(2)"
|
||
>
|
||
<span class="text">{{ numRecords.eventCount }}</span>
|
||
<span style="font-size: 14px">暂降事件</span>
|
||
</div>
|
||
</div>
|
||
|
||
<dv-scroll-ranking-board
|
||
ref="rankingBoardRef"
|
||
:config="config"
|
||
style="width: 70%; height: 215px; margin: 10px 0 0 10px"
|
||
/>
|
||
</div>
|
||
<el-divider />
|
||
<div v-loading="loading" element-loading-background="#343849c7">
|
||
<div class="titleBox" style="margin-top: 10px">暂降溯源</div>
|
||
<div style="display: flex; margin-top: 10px">
|
||
<div class="polymerization" style="width: 24%; height: 240px">
|
||
<div style="background-color: #0a73ff70">
|
||
<span class="text">{{ numAggregation.eventCount }}</span>
|
||
<span style="font-size: 14px">暂降溯源</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
style="width: 73%; height: 260px; margin: 0px 0px 10px 10px"
|
||
class="eventList"
|
||
>
|
||
<div v-for="(item, i) in tableData">
|
||
<div class="eventListDiv">
|
||
<div>{{ i + 1 }}</div>
|
||
<div>{{ item.name }}</div>
|
||
<div>
|
||
<el-button
|
||
size="small"
|
||
type="primary"
|
||
link
|
||
@click="analysisClick(item)"
|
||
>溯源
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<el-divider />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 谐波溯源 -->
|
||
<div v-if="flag == 1">
|
||
<div class="titles title_1">
|
||
<el-button type="primary" size="small" @click="calculation" :icon="Aim"
|
||
>谐波责任度计算
|
||
</el-button>
|
||
<el-button type="primary" size="small" @click="collecting" :icon="Memo"
|
||
>用采数据管理
|
||
</el-button>
|
||
</div>
|
||
<div class="tableBox">
|
||
<el-table
|
||
:scrollbar-always-on="true"
|
||
:data="traceabilityTableData"
|
||
height="555px"
|
||
size="small"
|
||
:header-cell-style="{ textAlign: 'center' }"
|
||
border
|
||
v-loading="loading"
|
||
element-loading-background="#343849c7"
|
||
@row-click="handleTraceabilityRow"
|
||
:row-style="{ cursor: 'pointer' }"
|
||
highlight-current-row
|
||
:current-row-key="defaultCurrentRowKey"
|
||
row-key="id"
|
||
>
|
||
<el-table-column
|
||
type="index"
|
||
align="center"
|
||
label="序号"
|
||
width="50"
|
||
/>
|
||
<el-table-column
|
||
prop="updateTime"
|
||
align="center"
|
||
label="时间"
|
||
show-overflow-tooltip
|
||
width="140"
|
||
/>
|
||
<el-table-column prop="content" label="事件内容" />
|
||
<el-table-column fixed="right" label="操作" width="70">
|
||
<template #default="{ row }">
|
||
<el-button
|
||
link
|
||
type="primary"
|
||
size="small"
|
||
@click.stop="handleDetail(row)"
|
||
style="margin-left: 12px"
|
||
>详情
|
||
</el-button>
|
||
<el-button
|
||
link
|
||
type="danger"
|
||
size="small"
|
||
@click.stop="handleDelete(row)"
|
||
>删除
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</div>
|
||
<!-- 谐波放大 -->
|
||
<div v-if="flag == 2">
|
||
<div style="display: flex">
|
||
<div
|
||
class="eacharts2"
|
||
ref="eacharts2"
|
||
style="width: 100%; height: 609px"
|
||
v-loading="loading"
|
||
element-loading-background="#343849c7"
|
||
></div>
|
||
</div>
|
||
<!-- <div class="tableBox">
|
||
<el-table
|
||
:scrollbar-always-on="true"
|
||
:data="amplifyTableData"
|
||
height="319px"
|
||
size="small"
|
||
:header-cell-style="{ textAlign: 'center' }"
|
||
border
|
||
v-loading="loading"
|
||
element-loading-background="#343849c7"
|
||
>
|
||
<el-table-column
|
||
type="index"
|
||
align="center"
|
||
label="序号"
|
||
width="50"
|
||
/>
|
||
<el-table-column
|
||
prop="time"
|
||
align="center"
|
||
label="谐波放大时间范围"
|
||
width="160"
|
||
/>
|
||
<el-table-column prop="content" align="center" label="事件内容" />
|
||
<el-table-column
|
||
prop="address"
|
||
align="center"
|
||
label="操作"
|
||
width="70"
|
||
>
|
||
<template #default="scope">
|
||
|
||
<el-button
|
||
size="small"
|
||
type="primary"
|
||
link
|
||
@click="arendChart(scope.row)"
|
||
>趋势图</el-button
|
||
>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div> -->
|
||
</div>
|
||
|
||
<dropOffTable ref="dropOffTableRef" />
|
||
|
||
<Collecting ref="collectingRef" />
|
||
<Calculation
|
||
ref="calculationRef"
|
||
v-if="showCalculation"
|
||
@show-calculation="handleShowCalculation"
|
||
@close-dialog="handleChildClose"
|
||
/>
|
||
<TraceabilityDetail
|
||
ref="traceabilityDetailRef"
|
||
:detailsQuery="detailsQuery"
|
||
/>
|
||
<!-- 暂降溯源 暂降事件单行点击事件 -->
|
||
<DipDetail ref="dipDetail"></DipDetail>
|
||
<!-- 暂降溯源 暂降事件总点击事件 -->
|
||
<DipDetailTotal
|
||
ref="dipDetailTotalRef"
|
||
@aggregation-success="handleAggregationSuccess"
|
||
></DipDetailTotal>
|
||
<!-- 谐波放大表格详情 -->
|
||
<AmplifyDeatil ref="amplifyDeatilRef"></AmplifyDeatil>
|
||
<!-- 谐波放大表格趋势图 -->
|
||
<TrendChart ref="trendChartRef"></TrendChart>
|
||
<!-- 谐波溯源表格点击事件选择次数弹框 -->
|
||
<el-dialog
|
||
v-model="visibleTimes"
|
||
:close-on-click-modal="false"
|
||
title="谐波次数"
|
||
draggable
|
||
width="500px"
|
||
@close="handleCloseDialog"
|
||
style="height: 170px"
|
||
v-if="dataTimes && dataTimes.length > 0"
|
||
>
|
||
<div style="display: flex; margin-top: 10px">
|
||
<div style="margin-right: 10px">谐波次数</div>
|
||
<el-select
|
||
v-model="timeValue"
|
||
placeholder="请选择"
|
||
size="small"
|
||
style="width: 200px"
|
||
@change="handleTimeChange"
|
||
>
|
||
<el-option
|
||
v-for="item in dataTimes"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
<script setup lang="ts">
|
||
import { Lightning, DataLine, ZoomIn } from "@element-plus/icons-vue";
|
||
import {
|
||
ref,
|
||
reactive,
|
||
onMounted,
|
||
getCurrentInstance,
|
||
watch,
|
||
nextTick,
|
||
onBeforeUnmount,
|
||
} from "vue";
|
||
import { ElMessage, ElMessageBox } from "element-plus";
|
||
import {
|
||
eventSource,
|
||
eventAggregation,
|
||
hasEventList,
|
||
processEvents,
|
||
getEventList,
|
||
responsibilityList,
|
||
deleteByIds,
|
||
getInfoList,
|
||
tableInfo,
|
||
harmOneImage,
|
||
hasUpEventList,
|
||
} from "@/api/manage_wx/index";
|
||
|
||
import { HelpFilled, Aim, Memo } from "@element-plus/icons-vue";
|
||
const flag = ref(0); // 0 for 暂降, 1 for 谐波, 2 for 谐波放大
|
||
const time = ref(0); // 0 for 周, 1 for 月
|
||
const { proxy }: any = getCurrentInstance();
|
||
import dropOffTable from "./dropOffTable.vue";
|
||
import { useStore } from "vuex";
|
||
import Collecting from "./collecting.vue";
|
||
import Calculation from "./calculation.vue";
|
||
import TraceabilityDetail from "./traceabilityDetail.vue";
|
||
import DipDetail from "./dipDetail.vue";
|
||
import AmplifyDeatil from "./amplifyDeatil.vue";
|
||
import TrendChart from "./trendChart.vue";
|
||
import DipDetailTotal from "./dipDetailTotal.vue";
|
||
|
||
const store = useStore();
|
||
const eacharts1 = ref(null);
|
||
const eacharts2 = ref(null);
|
||
const dropOffTableRef = ref();
|
||
|
||
const loading = ref(false);
|
||
|
||
const showCollecting = ref(false);
|
||
|
||
const showCalculation = ref(false);
|
||
|
||
const numRecords = ref({
|
||
eventCount: 0,
|
||
// innerList: [],
|
||
}); //暂降记录个数
|
||
|
||
const numAggregation = ref({
|
||
eventCount: 0,
|
||
});
|
||
|
||
const eventList = ref([]); //闪烁点
|
||
|
||
const iframeEventLists = ref([]); //iframeDia页面需要的数据
|
||
|
||
const collectingRef = ref();
|
||
|
||
const calculationRef = ref();
|
||
|
||
const traceabilityDetailRef = ref();
|
||
|
||
const dipDetail = ref();
|
||
|
||
const amplifyDeatilRef = ref();
|
||
|
||
const trendChartRef = ref();
|
||
|
||
const dipDetailTotalRef = ref();
|
||
|
||
let pointX = ref();
|
||
|
||
let pointY = ref();
|
||
|
||
// 添加用于存储热力图数据的响应式变量
|
||
const pointData = ref([]);
|
||
|
||
const detailsQuery = reactive({
|
||
id: "",
|
||
time: "",
|
||
name: "",
|
||
});
|
||
|
||
const amplifyTableData = ref([]); //谐波放大表格
|
||
|
||
const tableData = reactive<any[]>([]);
|
||
|
||
const traceabilityTableData = ref([]);
|
||
|
||
// 添加用于存储默认高亮行key的响应式变量
|
||
const defaultCurrentRowKey = ref();
|
||
|
||
const config = reactive({
|
||
data: [],
|
||
waitTime: 5000,
|
||
unit: "次",
|
||
rowNum: 6,
|
||
carousel: "page",
|
||
color: "rgba(248, 255, 68, .7)",
|
||
});
|
||
|
||
const rankingBoardRef = ref();
|
||
|
||
// 监听 config.data 变化,动态设置 title 属性
|
||
watch(
|
||
() => config.data,
|
||
(newData) => {
|
||
setTimeout(() => {
|
||
if (rankingBoardRef.value && newData.length > 0) {
|
||
const nameElements = document.querySelectorAll(
|
||
".dv-scroll-ranking-board .info-name"
|
||
);
|
||
nameElements.forEach((el, index) => {
|
||
if (newData[index]) {
|
||
el.setAttribute("title", newData[index].name);
|
||
}
|
||
});
|
||
}
|
||
}, 100);
|
||
},
|
||
{ deep: true }
|
||
);
|
||
|
||
const renderChart2 = () => {
|
||
var myChar2 = proxy.$echarts.init(eacharts2.value);
|
||
|
||
var hours = pointX.value;
|
||
|
||
var days = pointY.value;
|
||
|
||
var data = [];
|
||
|
||
if (pointData.value && Array.isArray(pointData.value)) {
|
||
data = pointData.value.map((item) => [
|
||
item.date || 0, // x轴索引
|
||
item.monitorName || 0, // y轴索引
|
||
item.count || 0, // 值
|
||
]);
|
||
} else {
|
||
data = [];
|
||
}
|
||
|
||
// 判断是否需要纵向滚动条(y轴数据超过7条)
|
||
const needYScroll = days && days.length > 15;
|
||
|
||
// 计算滚动比例
|
||
const scrollRatio = needYScroll ? (7 / days.length) * 100 : 100;
|
||
|
||
myChar2.setOption({
|
||
tooltip: {
|
||
position: "top",
|
||
formatter: function (params: any) {
|
||
let tips = ``;
|
||
tips += params.name + "<br/>";
|
||
tips +=
|
||
params.marker + params.value[1] + ": " + params.value[2] + "次";
|
||
return tips;
|
||
},
|
||
},
|
||
animation: false,
|
||
grid: {
|
||
right: needYScroll ? "30" : "20", // 为滚动条留出空间
|
||
left: "90",
|
||
bottom: "60px",
|
||
top: "20",
|
||
},
|
||
xAxis: {
|
||
type: "category",
|
||
data: hours,
|
||
splitArea: {
|
||
show: true,
|
||
},
|
||
axisLabel: {
|
||
textStyle: {
|
||
color: "#ffffff",
|
||
fontSize: 10,
|
||
},
|
||
formatter: function (value) {
|
||
let time = "";
|
||
if (value.slice(-2) == "01") {
|
||
time = value;
|
||
} else {
|
||
time = value.slice(5, 10);
|
||
}
|
||
return time;
|
||
},
|
||
},
|
||
},
|
||
yAxis: {
|
||
type: "category",
|
||
data: days,
|
||
inverse: true, // 反转 y 轴,使第一条数据在顶部
|
||
axisLabel: {
|
||
textStyle: {
|
||
color: "#ffffff",
|
||
fontSize: 12,
|
||
},
|
||
// 只显示前6个字符,多余部分用省略号表示
|
||
formatter: function (value: string) {
|
||
if (value && value.length > 6) {
|
||
return value.substring(0, 6) + "...";
|
||
}
|
||
return value;
|
||
},
|
||
},
|
||
},
|
||
dataZoom: [
|
||
// 纵向滚动条(y轴)
|
||
...(needYScroll
|
||
? [
|
||
{
|
||
type: "slider",
|
||
show: true,
|
||
orient: "vertical",
|
||
start: 0,
|
||
end: scrollRatio,
|
||
right: "5",
|
||
top: "20",
|
||
bottom: "40",
|
||
width: 10,
|
||
},
|
||
{
|
||
type: "inside",
|
||
orient: "vertical",
|
||
start: 0,
|
||
end: scrollRatio,
|
||
},
|
||
]
|
||
: []),
|
||
// 横向滚动条(x轴)
|
||
{
|
||
type: "inside",
|
||
height: 10,
|
||
start: hours && hours.length > 12 ? 70 : 0,
|
||
bottom: "25px",
|
||
end: 100,
|
||
},
|
||
{
|
||
type: "slider",
|
||
start: hours && hours.length > 12 ? 70 : 0,
|
||
height: 10,
|
||
bottom: "25px",
|
||
end: 100,
|
||
},
|
||
],
|
||
visualMap: {
|
||
show: false,
|
||
min: 0,
|
||
max: 20,
|
||
calculable: true,
|
||
orient: "horizontal",
|
||
left: "center",
|
||
bottom: "15%",
|
||
type: "piecewise", // 必须设置为分段模式
|
||
pieces: [
|
||
{ min: 15, max: 20, color: "rgba(236,143,21,0.92)" }, // 第4段:绿色
|
||
{ min: 5, max: 15, color: "rgba(236,218,21,0.92)" }, // 第3段:蓝色
|
||
{ min: 1, max: 5, color: "#1d83ce60" }, // 第2段:黄色
|
||
{ min: 0, max: 0, color: "#008000" }, // 第1段:橙色
|
||
],
|
||
},
|
||
series: [
|
||
{
|
||
name: "",
|
||
type: "heatmap",
|
||
data: data,
|
||
label: {
|
||
show: true,
|
||
},
|
||
emphasis: {
|
||
itemStyle: {
|
||
shadowBlur: 10,
|
||
shadowColor: "rgba(0, 0, 0, 0.5)",
|
||
},
|
||
},
|
||
},
|
||
],
|
||
});
|
||
|
||
// 注册图表点击事件
|
||
myChar2.on("click", handleChartClick);
|
||
};
|
||
|
||
const handleChartClick = (params: any) => {
|
||
// console.log("Clicked params:", params);
|
||
if (params.componentType === "series") {
|
||
// // 通过数据索引直接获取原始数据
|
||
const dataIndex = params.dataIndex;
|
||
|
||
// 从 pointData 中获取对应的原始数据
|
||
const originalData =
|
||
pointData.value && pointData.value[dataIndex]
|
||
? pointData.value[dataIndex]
|
||
: null;
|
||
|
||
// 获取 lineId
|
||
const lineId = originalData ? originalData.lineId || null : null;
|
||
|
||
dropOffTableRef.value.open(
|
||
flag.value == 2 ? "暂降事件" : "谐波放大事件",
|
||
params.name,
|
||
lineId
|
||
);
|
||
}
|
||
};
|
||
|
||
// 定义 emit
|
||
const emit = defineEmits([
|
||
"flag-changed",
|
||
"expand-detail",
|
||
"refresh-security-detail",
|
||
]);
|
||
|
||
watch(flag, (newVal) => {
|
||
// 向父组件发送 flag 变化事件
|
||
emit("flag-changed", newVal);
|
||
setTimeout(() => {
|
||
if (flag.value == 0) {
|
||
initHasEventList();
|
||
// flag 变化后重新绑定事件
|
||
bindRankingClickEvent();
|
||
}
|
||
if (flag.value == 2) {
|
||
renderChart2();
|
||
initHasUpEventList();
|
||
}
|
||
if (flag.value == 1) {
|
||
callHarmOneImageApi(getFirstTableData.value, getTimes.value);
|
||
}
|
||
}, 100);
|
||
});
|
||
|
||
// 暂降记录个数
|
||
const initialData = () => {
|
||
loading.value = true;
|
||
eventSource({
|
||
deptId: store.state.deptId,
|
||
searchBeginTime: store.state.timeValue[0],
|
||
searchEndTime: store.state.timeValue[1],
|
||
}).then((res: any) => {
|
||
if (res.code == "A0000") {
|
||
numRecords.value.eventCount = res.data.eventCount;
|
||
// numRecords.value.innerList = res.data.innerList;
|
||
// 将 innerList 数据转换并赋值给 config.data
|
||
if (res.data.innerList && Array.isArray(res.data.innerList)) {
|
||
config.data = res.data.innerList.map((item: any) => ({
|
||
name: item.name || item.stationName || "未知站点", // 根据实际字段名调整
|
||
value: item.value || item.count || 0, // 根据实际字段名调整
|
||
id: item.id, // 根据实际字段名调整
|
||
}));
|
||
} else {
|
||
config.data = [];
|
||
}
|
||
loading.value = false;
|
||
}
|
||
});
|
||
};
|
||
|
||
// 暂降聚合个数
|
||
const initialAggregation = () => {
|
||
loading.value = true;
|
||
eventAggregation({
|
||
deptId: store.state.deptId,
|
||
searchBeginTime: store.state.timeValue[0],
|
||
searchEndTime: store.state.timeValue[1],
|
||
})
|
||
.then((res: any) => {
|
||
if (res.code == "A0000") {
|
||
numAggregation.value.eventCount = res.data.eventCount;
|
||
|
||
// 直接将 innerList 赋值给 tableData
|
||
if (res.data.innerList && Array.isArray(res.data.innerList)) {
|
||
tableData.splice(0, tableData.length, ...res.data.innerList);
|
||
} else {
|
||
tableData.splice(0, tableData.length);
|
||
}
|
||
loading.value = false;
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
console.error("获取聚合数据失败:", error);
|
||
tableData.splice(0, tableData.length);
|
||
});
|
||
};
|
||
|
||
// 提取发送消息到iframe的公共方法
|
||
const sendMessageToIframe = (type: string, payload: any) => {
|
||
// console.log(123,store.state.iframeLoad);
|
||
|
||
// window.parent.postMessage(
|
||
// {
|
||
// type,
|
||
// payload,
|
||
// },
|
||
// "*"
|
||
// );
|
||
// 如果 iframe 已加载完成,直接发送消息
|
||
if (store.state.iframeLoad) {
|
||
window.parent.postMessage(
|
||
{
|
||
type,
|
||
payload,
|
||
},
|
||
"*" // 建议替换为具体域名,如 'https://父页面域名'
|
||
);
|
||
return;
|
||
}
|
||
|
||
// 如果未加载完成,每 1 秒尝试发送一次
|
||
const interval = setInterval(() => {
|
||
console.log("等待 iframe 加载,1 秒后重试...");
|
||
if (store.state.iframeLoad) {
|
||
// 加载完成后发送消息并清除定时器
|
||
window.parent.postMessage(
|
||
{
|
||
type,
|
||
payload,
|
||
},
|
||
"*" // 建议替换为具体域名
|
||
);
|
||
clearInterval(interval);
|
||
}
|
||
}, 1000);
|
||
};
|
||
|
||
// 闪烁点
|
||
const initHasEventList = () => {
|
||
hasEventList({
|
||
deptId: store.state.deptId,
|
||
searchBeginTime: store.state.timeValue[0],
|
||
searchEndTime: store.state.timeValue[1],
|
||
})
|
||
.then((res: any) => {
|
||
if (res.code == "A0000") {
|
||
// 从返回的数据中提取所有lineId并存入eventList
|
||
if (res.data && Array.isArray(res.data)) {
|
||
// iframeEventLists.value = res.data;
|
||
// 如果返回的数据本身就是数组
|
||
eventList.value = res.data
|
||
.filter((item) => item.lineId) // 过滤掉没有lineId的项
|
||
.map((item) => item.lineId);
|
||
}
|
||
|
||
const dataObject = {
|
||
eventList: [...eventList.value],
|
||
color: "#375db4",
|
||
};
|
||
|
||
// 发送eventList数据到iframe,注意发送的是.value而不是响应式对象本身
|
||
sendMessageToIframe("SEND_KEYS_TO_IFRAME", dataObject);
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
console.error("获取闪烁点失败:", error);
|
||
eventList.value = []; // 出错时清空eventList
|
||
|
||
// 出错时也发送空数组到iframe
|
||
sendMessageToIframe("SEND_KEYS_TO_IFRAME", {});
|
||
});
|
||
};
|
||
|
||
// 谐波放大闪烁点
|
||
const initHasUpEventList = () => {
|
||
hasUpEventList({
|
||
deptId: store.state.deptId,
|
||
searchBeginTime: store.state.timeValue[0],
|
||
searchEndTime: store.state.timeValue[1],
|
||
})
|
||
.then((res: any) => {
|
||
if (res.code == "A0000") {
|
||
// 从返回的数据中提取所有lineId并存入eventList
|
||
if (res.data && Array.isArray(res.data)) {
|
||
// iframeEventLists.value = res.data;
|
||
// 如果返回的数据本身就是数组
|
||
eventList.value = res.data
|
||
.filter((item) => item.lineId) // 过滤掉没有lineId的项
|
||
.map((item) => item.lineId);
|
||
}
|
||
|
||
const dataObject = {
|
||
eventList: [...eventList.value],
|
||
color: "#f08a0a",
|
||
flagValue: "2",
|
||
};
|
||
|
||
// 发送eventList数据到iframe,注意发送的是.value而不是响应式对象本身
|
||
sendMessageToIframe("SEND_KEYS_TO_IFRAME", dataObject);
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
console.error("获取闪烁点失败:", error);
|
||
eventList.value = []; // 出错时清空eventList
|
||
|
||
// 出错时也发送空数组到iframe
|
||
sendMessageToIframe("SEND_KEYS_TO_IFRAME", {});
|
||
});
|
||
};
|
||
|
||
// 聚合
|
||
const handleAggregation = (row: any) => {
|
||
ElMessageBox.confirm(
|
||
`是否确认对当前用户部门${store.state.timeValue[0]}至${store.state.timeValue[1]}之间的暂降事件进行溯源?`,
|
||
"提示",
|
||
{
|
||
confirmButtonText: "确定",
|
||
cancelButtonText: "取消",
|
||
type: "warning",
|
||
}
|
||
)
|
||
.then(() => {
|
||
processEvents({
|
||
deptId: store.state.deptId,
|
||
searchBeginTime: store.state.timeValue[0],
|
||
searchEndTime: store.state.timeValue[1],
|
||
}).then((res: any) => {
|
||
if (res.code == "A0000") {
|
||
ElMessage({
|
||
type: "success",
|
||
message: res.message,
|
||
});
|
||
initialAggregation();
|
||
} else {
|
||
ElMessage({
|
||
type: "warning",
|
||
message: res.message,
|
||
});
|
||
}
|
||
});
|
||
})
|
||
.catch((error) => {});
|
||
};
|
||
|
||
// 范围分析 溯源
|
||
const analysisClick = (row: any) => {
|
||
hasEventList({
|
||
eventAssId: row.id,
|
||
})
|
||
.then((res: any) => {
|
||
if (res.code == "A0000") {
|
||
// 从返回的数据中提取所有lineId并存入eventList
|
||
if (res.data && Array.isArray(res.data)) {
|
||
// iframeEventLists.value = res.data;
|
||
// 如果返回的数据本身就是数组
|
||
eventList.value = res.data
|
||
.filter((item) => item.lineId) // 过滤掉没有lineId的项
|
||
.map((item) => item.lineId);
|
||
maxResponsibilityMonitorId.value = res.data
|
||
.filter((item) => item.isImport == 1)
|
||
.map((item) => item.lineId); // 筛选 isImport 为 1
|
||
}
|
||
|
||
const dataObject = {
|
||
eventList: [...eventList.value],
|
||
color: "#03de6d",
|
||
maxResponsibilityMonitorId: [...maxResponsibilityMonitorId.value],
|
||
maxColor: "#f9065b",
|
||
};
|
||
|
||
// 发送eventList数据到iframe,注意发送的是.value而不是响应式对象本身
|
||
sendMessageToIframe("SEND_KEYS_TO_IFRAME", dataObject);
|
||
|
||
// 发送消息通知父组件展开详情面板
|
||
emit("expand-detail", 0); // 0 表示暂降溯源
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
console.error("获取闪烁点失败:", error);
|
||
eventList.value = []; // 出错时清空eventList
|
||
|
||
// 出错时也发送空数组到iframe
|
||
sendMessageToIframe("SEND_KEYS_TO_IFRAME", {});
|
||
});
|
||
|
||
getEventList({
|
||
eventAssId: row.id,
|
||
}).then((res: any) => {
|
||
if (res.code == "A0000") {
|
||
// 通过 Vuex 更新 realData
|
||
store.dispatch("updateRealData", res.data.records);
|
||
}
|
||
});
|
||
};
|
||
|
||
const handleShowCalculation = (value) => {
|
||
showCalculation.value = value;
|
||
};
|
||
|
||
const handleShowCollecting = (value) => {
|
||
showCollecting.value = value;
|
||
};
|
||
// 谐波贡献度计算
|
||
const calculation = () => {
|
||
showCalculation.value = true;
|
||
nextTick(() => {
|
||
calculationRef.value.openDialog();
|
||
});
|
||
};
|
||
|
||
// 用采数据列表
|
||
const collecting = () => {
|
||
showCollecting.value = true;
|
||
nextTick(() => {
|
||
collectingRef.value.openDialog();
|
||
});
|
||
};
|
||
|
||
// 获取谐波溯源第一行数据和次数
|
||
const getFirstTableData = ref([]);
|
||
const getTimes = ref();
|
||
// 谐波溯源表格
|
||
const initialResponsibilityList = () => {
|
||
loading.value = true;
|
||
responsibilityList({
|
||
deptId: store.state.deptId,
|
||
searchBeginTime: store.state.timeValue[0],
|
||
searchEndTime: store.state.timeValue[1],
|
||
})
|
||
.then((res: any) => {
|
||
if (res.code == "A0000") {
|
||
// 直接将 records 赋值给 tableData
|
||
if (res.data.records && Array.isArray(res.data.records)) {
|
||
getFirstTableData.value = res.data.records[0];
|
||
if (res.data.records[0].dataTimes) {
|
||
if (res.data.records[0].dataTimes.includes(",")) {
|
||
getTimes.value = res.data.records[0].dataTimes.split(",")[0];
|
||
} else {
|
||
getTimes.value = res.data.records[0].dataTimes;
|
||
}
|
||
}
|
||
const processedData = res.data.records.map((item: any) => {
|
||
// 根据实际字段进行拼接,示例:
|
||
const content = `${item.lineName || ""}进行${
|
||
item.dataTimes || ""
|
||
}次谐波电压溯源,溯源对象:${item.userDataName || ""}`.trim();
|
||
return {
|
||
...item,
|
||
content: content,
|
||
};
|
||
});
|
||
traceabilityTableData.value = processedData;
|
||
// 设置默认高亮第一行(使用唯一标识字段,如id)
|
||
if (processedData.length > 0) {
|
||
defaultCurrentRowKey.value = processedData[0].id || 0;
|
||
}
|
||
} else {
|
||
traceabilityTableData.value = [];
|
||
defaultCurrentRowKey.value = null;
|
||
}
|
||
loading.value = false;
|
||
}
|
||
// 通知父组件刷新 manage/securityDetail.vue 中的数据
|
||
emit("refresh-security-detail");
|
||
})
|
||
.catch((error) => {
|
||
traceabilityTableData.value = [];
|
||
defaultCurrentRowKey.value = null;
|
||
loading.value = false;
|
||
});
|
||
};
|
||
|
||
// 谐波溯源详情
|
||
const handleDetail = (row: any) => {
|
||
detailsQuery.id = row.id;
|
||
detailsQuery.time = row.dataTimes;
|
||
detailsQuery.name = `${row.gdName}>${row.subName}>${row.devName}>${row.lineName}`;
|
||
nextTick(() => {
|
||
traceabilityDetailRef.value.openDialog();
|
||
});
|
||
};
|
||
|
||
// 删除
|
||
const handleDelete = (row: any) => {
|
||
ElMessageBox.confirm("确定删除该数据项?", "提示", {
|
||
confirmButtonText: "确定",
|
||
cancelButtonText: "取消",
|
||
type: "warning",
|
||
}).then(() => {
|
||
deleteByIds([row.id])
|
||
.then((res: any) => {
|
||
ElMessage.success("删除成功");
|
||
initialResponsibilityList();
|
||
})
|
||
.catch((err: any) => {
|
||
ElMessage.error(err.message);
|
||
});
|
||
});
|
||
};
|
||
// 点击事件处理器
|
||
let clickHandler = null;
|
||
|
||
const handleRankingClick = (name: any) => {
|
||
let eventList = config.data.filter((item) => item.name == name);
|
||
dipDetail.value.open(eventList[0].id);
|
||
};
|
||
|
||
// 谐波放大表格
|
||
// const getInfoListAmplify = () => {
|
||
// loading.value = true;
|
||
// getInfoList({
|
||
// deptId: store.state.deptId,
|
||
// searchBeginTime: store.state.timeValue[0],
|
||
// searchEndTime: store.state.timeValue[1],
|
||
// }).then((res: any) => {
|
||
// if (res.code == "A0000") {
|
||
// // 对返回的数据进行处理,拼接字段
|
||
// amplifyTableData.value = res.data.map((item: any) => {
|
||
// return {
|
||
// ...item,
|
||
// // 拼接新的字段用于展示
|
||
// time: `${item.startTime || ""}至${item.endTime || ""}`,
|
||
// content: `${item.monitorName || ""}(${
|
||
// item.objName.replace("无锡市", "").replace("无锡", "") || ""
|
||
// })谐波放大: ${item.harmonicCount || ""}次谐波${
|
||
// item.phase || ""
|
||
// }相,持续时间${Math.floor(item.duration / 60) || ""}分钟`,
|
||
// };
|
||
// });
|
||
// } else {
|
||
// amplifyTableData.value = [];
|
||
// }
|
||
// loading.value = false;
|
||
// });
|
||
// };
|
||
|
||
// 谐波放大测点
|
||
const tableInfoAmplify = () => {
|
||
loading.value = true;
|
||
tableInfo({
|
||
deptId: store.state.deptId,
|
||
searchBeginTime: store.state.timeValue[0],
|
||
searchEndTime: store.state.timeValue[1],
|
||
}).then((res: any) => {
|
||
if (res.code == "A0000") {
|
||
if (res.data.date) {
|
||
pointX.value = res.data.date;
|
||
}
|
||
if (res.data.monitorList) {
|
||
pointY.value = res.data.monitorList;
|
||
}
|
||
if (res.data.inner) {
|
||
pointData.value = res.data.inner;
|
||
}
|
||
nextTick(() => {
|
||
if (flag.value == 2) {
|
||
renderChart2();
|
||
}
|
||
});
|
||
} else {
|
||
pointX.value = [];
|
||
pointY.value = [];
|
||
pointData.value = [];
|
||
}
|
||
loading.value = false;
|
||
});
|
||
};
|
||
|
||
// 谐波放大表格详情
|
||
const amplifyDeatil = (row: any) => {
|
||
amplifyDeatilRef.value.open();
|
||
};
|
||
|
||
// 谐波放大表格趋势图
|
||
const arendChart = (row: any) => {
|
||
trendChartRef.value.open(row);
|
||
};
|
||
|
||
const visibleTimes = ref(false);
|
||
|
||
// 选择的次数
|
||
const timeValue = ref("");
|
||
|
||
const dataTimes = ref();
|
||
|
||
// 谐波溯源表格点击事件
|
||
// 添加用于存储最大 responsibilityData 对应的 monitorId 的响应式变量
|
||
const maxResponsibilityMonitorId = ref([]);
|
||
|
||
//谐波溯源背景测点数据
|
||
const backgroundPoint = ref([]);
|
||
|
||
const handleTraceabilityRow = (row: any, column: any) => {
|
||
if (column.label === "操作") return; // 如果是操作列,不执行行点击逻辑
|
||
if (row.dataTimes) {
|
||
if (row.dataTimes.includes(",")) {
|
||
// 包含逗号时,分割成数组并转换为下拉框所需格式
|
||
const timesArray = row.dataTimes
|
||
.split(",")
|
||
.map((item: string, index: number) => {
|
||
const trimmedItem = item.trim();
|
||
return {
|
||
value: trimmedItem,
|
||
label: trimmedItem,
|
||
};
|
||
});
|
||
dataTimes.value = timesArray;
|
||
|
||
// 设置默认选中第一个选项
|
||
// if (dataTimes.value && dataTimes.value.length > 0) {
|
||
// timeValue.value = dataTimes.value[0].value;
|
||
// }
|
||
|
||
// 保存当前行数据,用于后续调用接口
|
||
currentRow.value = row;
|
||
|
||
// 打开弹框
|
||
visibleTimes.value = true;
|
||
|
||
// 注意:这里不立即调用接口,等待用户选择后再调用
|
||
return;
|
||
} else {
|
||
// 不包含逗号时,直接调用接口
|
||
callHarmOneImageApi(row, row.dataTimes);
|
||
}
|
||
} else {
|
||
// 没有 dataTimes 时,直接调用接口
|
||
callHarmOneImageApi(row, "");
|
||
}
|
||
};
|
||
|
||
// 保存当前行数据的响应式变量
|
||
const currentRow = ref(null);
|
||
|
||
// 新增:处理下拉框值变化的函数
|
||
const handleTimeChange = (value: string) => {
|
||
// 当用户在下拉框中选择了一个值后,调用接口
|
||
if (currentRow.value) {
|
||
callHarmOneImageApi(currentRow.value, value);
|
||
visibleTimes.value = false;
|
||
}
|
||
};
|
||
|
||
// 谐溯源点击行的全部数据
|
||
const eventListAll = ref([]);
|
||
// 抽取接口调用逻辑到独立函数
|
||
const callHarmOneImageApi = (row: any, selectedTime: string) => {
|
||
//谐波溯源背景测点数据
|
||
if (row && row.lineId) {
|
||
backgroundPoint.value = [row.lineId];
|
||
} else {
|
||
backgroundPoint.value = [];
|
||
}
|
||
harmOneImage({
|
||
id: row.id,
|
||
time: selectedTime,
|
||
}).then((res: any) => {
|
||
if (res.code == "A0000") {
|
||
// 从返回的数据中提取所有monitorId并存入eventList
|
||
if (res.data && Array.isArray(res.data)) {
|
||
// 全部数据
|
||
eventListAll.value = res.data;
|
||
// 原有逻辑:提取所有 monitorId
|
||
eventList.value = res.data
|
||
.filter((item) => item.monitorId) // 过滤掉没有monitorId的项
|
||
.map((item) => item.monitorId);
|
||
|
||
// 新逻辑:使用 reduce 方法找到 responsibilityData 最大值对应的项
|
||
const validData = res.data.filter(
|
||
(item) =>
|
||
item.responsibilityData &&
|
||
!isNaN(item.responsibilityData) &&
|
||
item.monitorId
|
||
);
|
||
|
||
if (validData.length > 0) {
|
||
const maxResponsibilityItem = validData.reduce(
|
||
(maxItem, currentItem) => {
|
||
const currentValue = parseFloat(currentItem.responsibilityData);
|
||
const maxValue = parseFloat(maxItem.responsibilityData);
|
||
return currentValue > maxValue ? currentItem : maxItem;
|
||
}
|
||
);
|
||
|
||
// 存储最大 responsibilityData 对应的 monitorId
|
||
maxResponsibilityMonitorId.value = [maxResponsibilityItem.monitorId];
|
||
} else {
|
||
maxResponsibilityMonitorId.value = [];
|
||
}
|
||
}
|
||
|
||
const dataObject = {
|
||
eventListAll: [JSON.parse(JSON.stringify(eventListAll.value))],
|
||
eventList: [...eventList.value],
|
||
color: "#cf552d",
|
||
maxResponsibilityMonitorId: [...maxResponsibilityMonitorId.value],
|
||
maxColor: "#c708fe",
|
||
backgroundPointId: [...backgroundPoint.value],
|
||
backgroundPointColor: "#0936f4",
|
||
};
|
||
// 发送所有 monitorId 数据到 iframe
|
||
sendMessageToIframe("SEND_KEYS_TO_IFRAME", dataObject);
|
||
}
|
||
});
|
||
};
|
||
|
||
// 暂降溯源,暂降总事件
|
||
const polymerizationClick = (val) => {
|
||
dipDetailTotalRef.value.open(val);
|
||
};
|
||
|
||
// 修改 handleCloseDialog 函数
|
||
const handleCloseDialog = () => {
|
||
visibleTimes.value = false;
|
||
timeValue.value = "";
|
||
// 清空当前行数据
|
||
currentRow.value = null;
|
||
};
|
||
|
||
// 处理聚合成功的回调
|
||
const handleAggregationSuccess = () => {
|
||
// 调用 initialAggregation 函数
|
||
initialAggregation();
|
||
};
|
||
|
||
onMounted(() => {
|
||
// 获取组件DOM
|
||
bindRankingClickEvent();
|
||
});
|
||
|
||
// 重新绑定点击事件的函数
|
||
const bindRankingClickEvent = () => {
|
||
// 先移除之前的事件监听器
|
||
const oldRankingEl = rankingBoardRef.value?.$el;
|
||
if (oldRankingEl && clickHandler) {
|
||
oldRankingEl.removeEventListener("click", clickHandler);
|
||
}
|
||
|
||
// 获取新的组件DOM
|
||
const rankingEl = rankingBoardRef.value?.$el;
|
||
|
||
if (rankingEl) {
|
||
clickHandler = (e) => {
|
||
// 查找点击的行元素(类名为row-item)
|
||
const rowEl = e.target.closest(".row-item");
|
||
if (rowEl) {
|
||
// 查找行内的名称元素(类名为info-name)
|
||
const nameEl = rowEl.querySelector(".info-name");
|
||
if (nameEl) {
|
||
// 获取并传递名称文本
|
||
handleRankingClick(nameEl.textContent.trim());
|
||
}
|
||
}
|
||
};
|
||
|
||
// 添加事件监听
|
||
rankingEl.addEventListener("click", clickHandler);
|
||
}
|
||
};
|
||
|
||
onBeforeUnmount(() => {
|
||
const rankingEl = rankingBoardRef.value?.$el;
|
||
if (rankingEl && clickHandler) {
|
||
rankingEl.removeEventListener("click", clickHandler);
|
||
}
|
||
});
|
||
|
||
const handleChildClose = () => {
|
||
// 在这里执行父组件需要的逻辑
|
||
initialResponsibilityList();
|
||
};
|
||
|
||
const init = () => {
|
||
initialData();
|
||
initialAggregation();
|
||
initHasEventList();
|
||
initialResponsibilityList();
|
||
// getInfoListAmplify();
|
||
tableInfoAmplify();
|
||
|
||
if (flag.value == 2) {
|
||
initHasUpEventList();
|
||
}
|
||
};
|
||
|
||
defineExpose({
|
||
init,
|
||
initHasEventList,
|
||
//getEventList: () => [...iframeEventLists.value], // 提供获取 eventList 数据的方法 提供给iframeDia.vue
|
||
});
|
||
</script>
|
||
<style lang="scss" scoped>
|
||
@use "@/assets/scss/index.scss";
|
||
@import "@/assets/scss/element.scss";
|
||
|
||
.plan {
|
||
width: 100%;
|
||
|
||
.titles {
|
||
margin-right: 10px;
|
||
|
||
.react-right {
|
||
// width: 60px !important;
|
||
// line-height: 20px !important;
|
||
// font-size: 14px !important;
|
||
width: 110px !important;
|
||
line-height: 30px !important;
|
||
font-size: 16px !important;
|
||
}
|
||
|
||
.time {
|
||
width: 40px !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
.imputation {
|
||
position: absolute;
|
||
right: 10px;
|
||
top: 40px;
|
||
z-index: 999;
|
||
}
|
||
|
||
.polymerization {
|
||
padding: 20px 10px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
|
||
div {
|
||
height: 80px;
|
||
border-radius: 10px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
text-align: center;
|
||
|
||
.text {
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
margin-bottom: 10px;
|
||
}
|
||
}
|
||
|
||
// display: grid;
|
||
// gap: 5px;
|
||
// grid-template-rows: repeat(2, 1fr);
|
||
}
|
||
|
||
.eventList {
|
||
overflow-y: auto;
|
||
/* 可以添加一些内边距来让内容和滚动条之间有间距 */
|
||
padding-right: 8px;
|
||
}
|
||
|
||
:deep(.el-divider--horizontal) {
|
||
margin: 10px 0;
|
||
}
|
||
|
||
.eventListDiv {
|
||
color: #fff;
|
||
display: flex;
|
||
|
||
div:nth-child(1) {
|
||
width: 30px;
|
||
display: flex;
|
||
align-items: center;
|
||
color: var(--el-color-primary);
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
div:nth-child(2) {
|
||
flex: 1;
|
||
font-size: 13px;
|
||
}
|
||
|
||
div:nth-child(3) {
|
||
width: 30px;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
}
|
||
|
||
.title_1 {
|
||
display: flex;
|
||
float: right;
|
||
margin: 10px;
|
||
font-size: 16px;
|
||
}
|
||
|
||
/* 添加以下样式来处理文字省略 */
|
||
:deep(.dv-scroll-ranking-board) {
|
||
.row-item {
|
||
cursor: pointer;
|
||
.ranking-info {
|
||
.info-name {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
|
||
// 添加 title 属性显示完整内容
|
||
&[title] {
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.titleBox_1 {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
width: 100%;
|
||
line-height: 32px;
|
||
font-size: 18px;
|
||
padding-left: 20px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
:deep(.el-table__body tr.current-row > td.el-table__cell) {
|
||
background-color: #456a91de !important;
|
||
}
|
||
</style>
|