<template>
|
<div class="right">
|
<div class="right-top">
|
<span>模拟结果</span>
|
</div>
|
<div class="echartsBox" id="layerBox">
|
<div class="echartCont">
|
<p style="cursor: pointer" @click="rainClick">降雨数据</p>
|
<div class="echartBox">
|
<div id="echarts1" style="width: 100%; height: 100%"></div>
|
</div>
|
</div>
|
<div class="echartCont">
|
<p>断面模拟</p>
|
<div class="echartBox">
|
<div id="echarts2" style="width: 100%; height: 100%"></div>
|
</div>
|
</div>
|
<div class="echartCont">
|
<p style="cursor: pointer" @click="debuffClick">威胁对象</p>
|
<div class="echartBox">
|
<div class="table-container" ref="tableContainer">
|
<el-table
|
:data="tableData"
|
style="width: 100%; font-size: 10px"
|
height="100%"
|
@row-click="handleRowClick"
|
>
|
<el-table-column label="影响区名称" width="30" align="center">
|
<template #default="scope">
|
影响区{{ scope.row.zoneId }}
|
</template>
|
</el-table-column>
|
<el-table-column
|
prop="time"
|
label="影响时间"
|
width="50"
|
align="center"
|
></el-table-column>
|
<el-table-column
|
prop="population"
|
label="人员(人)"
|
width="23"
|
align="center"
|
></el-table-column>
|
<el-table-column
|
prop="room"
|
label="房屋(间)"
|
width="23"
|
align="center"
|
></el-table-column>
|
<el-table-column
|
prop="households"
|
label="户数(户)"
|
width="23"
|
align="center"
|
></el-table-column>
|
<el-table-column
|
prop="property"
|
label="财产(万元)"
|
width="23"
|
align="center"
|
></el-table-column>
|
<el-table-column
|
prop="maxDepth"
|
label="最大水深(米)"
|
width="28"
|
align="center"
|
></el-table-column>
|
<el-table-column
|
prop="maxVelocity"
|
label="最大流速(m/s)"
|
width="35"
|
align="center"
|
></el-table-column>
|
<el-table-column
|
prop="raininess"
|
label="雨强(mm/h)"
|
width="28"
|
align="center"
|
></el-table-column>
|
<el-table-column
|
prop="warningLevel"
|
label="预警等级"
|
width="28"
|
align="center"
|
></el-table-column>
|
</el-table>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script setup>
|
import * as echarts from "echarts";
|
import { EventBus } from "@/eventBus"; // 引入事件总线
|
import {
|
ref,
|
onMounted,
|
onBeforeUnmount,
|
nextTick,
|
watch,
|
onUnmounted,
|
} from "vue";
|
import dayjs from "dayjs";
|
import { useSimStore } from "@/store/simulation";
|
const simStore = useSimStore();
|
const { rainFalls } = simStore;
|
|
let dataIntervalId = null; // 表格定时器 ID
|
const jsonData = ref([]); // JSON 数据
|
const tableData = ref([]); // 表格数据
|
const currentIndex = ref(0); // 当前加载索引
|
const isPaused = ref(false); // 是否暂停标志
|
const chart1Data = ref(null); //降雨数据
|
const chart2Data = ref(null); //断面数据
|
let intervalId1 = null; //降雨数据定时器
|
let intervalId2 = null; //断面数据定时器
|
|
// 根据时间轴匹配的x轴的时间显示
|
const nowTime = ref(null);
|
|
const props = defineProps({
|
isDynamicMode: {
|
type: Boolean,
|
},
|
isFinish: {
|
type: Boolean,
|
},
|
});
|
|
watch(
|
() => props.isFinish,
|
(newVal) => {
|
if (!newVal) {
|
resetTable();
|
chart1Data.value.resetLoading();
|
chart1Data.value.startUpdating(1000); // 每隔 1 秒更新一次
|
chart2Data.value.resetLoading();
|
chart2Data.value.startUpdating(1000); // 每隔 1 秒更新一次
|
}
|
}
|
);
|
// 清除函数=====================================================================
|
// 监听事件总线的自定义事件
|
EventBus.on("reset-table", () => {
|
resetTable(); // 调用重置表格的函数
|
});
|
|
// 清除echarts图表
|
EventBus.on("clear-echart", () => {
|
chart1Data.value.resetLoading();
|
chart2Data.value.resetLoading();
|
});
|
|
// 清除威胁对象中的数据
|
const resetTable = () => {
|
currentIndex.value = 0;
|
tableData.value = [];
|
if (dataIntervalId) {
|
clearInterval(dataIntervalId);
|
}
|
startAddingData();
|
};
|
|
// 暂停时停止所有的更新
|
const handleHideSchemeInfo = () => {
|
// 停止所有动态更新
|
if (intervalId1) {
|
clearInterval(intervalId1);
|
chart1Data.value.stopUpdating(); // 每隔 1 秒更新一次
|
intervalId1 = null;
|
}
|
if (intervalId2) {
|
clearInterval(intervalId2);
|
chart2Data.value.stopUpdating(); // 每隔 1 秒更新一次
|
intervalId2 = null;
|
}
|
if (dataIntervalId) {
|
clearInterval(dataIntervalId);
|
dataIntervalId = null;
|
}
|
};
|
|
// 监听时间轴结束模拟
|
EventBus.on("hide-schemeInfo", handleHideSchemeInfo);
|
|
// 监听父组件传递的数据变化
|
watch(
|
() => props.isDynamicMode,
|
(isEnabled) => {
|
isPaused.value = !isEnabled;
|
|
if (isEnabled) {
|
// 如果启用了动态模式
|
if (jsonData.value.length === 0) {
|
getDangerInfo(); // 加载初始数据
|
} else {
|
startAddingData(); // 开始添加数据
|
}
|
|
// 启动第一个图表的定时更新
|
if (!intervalId1 && chart1Data.value) {
|
intervalId1 = setInterval(() => {
|
chart1Data.value.startUpdating(1000);
|
}, 10);
|
}
|
|
// 启动第二个图表的定时更新
|
if (!intervalId2 && chart2Data.value) {
|
intervalId2 = setInterval(() => {
|
chart2Data.value.startUpdating(1000); // 每隔 1 秒更新一次
|
}, 10);
|
}
|
} else {
|
handleHideSchemeInfo();
|
}
|
},
|
{ immediate: true } // 立即执行监听器
|
);
|
// 点击数据实现面片闪动的触发函数
|
function handleRowClick(row) {
|
console.log("Row clicked:", row);
|
// 触发事件,将当前行的 ID 发送到地图组件
|
EventBus.emit("row-clicked", row.id);
|
}
|
// const listData = cityData.listData;
|
// const data = ref([
|
// 8.16, 15.38, 13.94, 9.46, 86.42, 71.32, 28.52, 25.9, 13.74, 14.54, 15.53,
|
// 9.17, 0, 0.09, 0.86, 8.15, 44.8, 21.86, 6.2, 4.98, 2.82, 2.36, 3.1, 1.06,
|
// ]);
|
const rainChangeShow = ref(false);
|
const tableContainer = ref(null);
|
|
let myChart1 = null;
|
let myChart2 = null;
|
|
// 威胁对象数据
|
const getDangerInfo = async () => {
|
try {
|
const response = await fetch(
|
"/json/listMaxInfluenceArea_wgs84_output.json"
|
);
|
const result = await response.json();
|
console.log("Loaded JSON data:", result);
|
if (result && result.data && Array.isArray(result.data.items)) {
|
jsonData.value = result.data.items;
|
console.log("jsonData is an array with length:", jsonData.value.length);
|
startAddingData();
|
} else {
|
console.error(
|
"Invalid JSON format: 'data.items' is missing or not an array!"
|
);
|
}
|
} catch (error) {
|
console.error("Error fetching data:", error);
|
}
|
};
|
|
const startAddingData = () => {
|
// 如果定时器已存在,先清除
|
if (dataIntervalId) {
|
clearInterval(dataIntervalId);
|
}
|
|
dataIntervalId = setInterval(() => {
|
if (isPaused.value) {
|
console.log("Loading is paused. Waiting for resume...");
|
return; // 如果暂停,则不执行后续逻辑
|
}
|
|
if (currentIndex.value < jsonData.value.length) {
|
const newItem = jsonData.value[currentIndex.value];
|
tableData.value.unshift(newItem);
|
scrollToTop();
|
currentIndex.value++;
|
} else {
|
console.log("All items added.");
|
clearInterval(dataIntervalId);
|
}
|
}, 2000);
|
};
|
|
const scrollToTop = () => {
|
nextTick(() => {
|
if (tableContainer.value) {
|
tableContainer.value.scrollTop = 0;
|
}
|
});
|
};
|
|
const rainClick = () => {
|
rainChangeShow.value = !rainChangeShow.value;
|
let desc = { func_name: "RainChange", visibility: rainChangeShow.value };
|
ps.emitMessage(desc);
|
};
|
|
const debuffClick = () => {
|
// Assuming you have access to parent components in a different way in Vue 3
|
// You might need to use provide/inject or props/emits instead
|
console.log("Debuff click");
|
};
|
|
const chartssize = (container, charts) => {
|
if (!charts || !charts.style) return;
|
const style = window.getComputedStyle(container);
|
charts.style.width = style.width;
|
charts.style.height = style.height;
|
};
|
|
// 时间轴时间截取处理
|
const syncTimeWithTimeline = () => {
|
// 2025-05-24 00:25
|
// // 将时间字符串转换为分钟数 (格式: "YYYY-MM-DD mm:ss")
|
const timeParts = nowTime.value.split(" ");
|
const timeOnly = timeParts[1]; // 获取 "mm:ss" 部分
|
return timeOnly;
|
};
|
|
// 暂时先不用,主要功能为一分钟插值六十个数据
|
// function processData(originalData) {
|
// const processedData = [];
|
// let currentTotal = 0; // 动态累加的 total
|
|
// for (let i = 0; i < originalData.length; i++) {
|
// const current = originalData[i];
|
// const targetIntensity = current.intensity;
|
// let remainingIntensity = targetIntensity; // 剩余需要分配的 intensity
|
|
// // 生成60个点(动态随机填充,允许出现低值和高值)
|
// for (let j = 0; j < 60; j++) {
|
// // 1. 动态生成 intensity(随机,但最后一点补足剩余值)
|
// let intensity;
|
// if (j === 59) {
|
// intensity = remainingIntensity; // 最后一点强制用完剩余值
|
// } else {
|
// // 随机生成一个比例(0.1~0.5之间的低概率 + 偶尔高值)
|
// const isLowValue = Math.random() < 0.7; // 70%概率生成低值
|
// const maxAllowed = remainingIntensity / (60 - j); // 确保不超剩余值
|
// intensity = isLowValue
|
// ? Math.random() * maxAllowed * 0.3 // 低值范围
|
// : Math.random() * maxAllowed * 1.5; // 偶尔高值
|
// }
|
// remainingIntensity -= intensity;
|
|
// // 2. 实时累加 total
|
// currentTotal += intensity;
|
|
// processedData.push({
|
// time: current.time,
|
// intensity: intensity,
|
// total: currentTotal,
|
// });
|
// }
|
|
// // 验证当前段的总 intensity 是否匹配原始数据
|
// console.log(
|
// `Segment ${i}: Generated intensity sum = ${(
|
// targetIntensity - remainingIntensity
|
// ).toFixed(2)}, Original = ${targetIntensity}`
|
// );
|
// }
|
|
// return processedData;
|
// }
|
|
// 设置降雨图表
|
const setEcharts1 = () => {
|
const chartDom = document.getElementById("echarts1");
|
const myChart1 = echarts.init(chartDom);
|
|
// 图表数据
|
let rainfallData = ref([]);
|
let data1 = ref([]);
|
let data2 = ref([]);
|
let xAxisData = ref(["00:00"]);
|
let updateInterval = null;
|
let dataIndex = ref(0);
|
|
// 动态计算Y轴范围
|
const getDynamicYAxis = (dataArray) => {
|
const currentMax = Math.max(...dataArray, 1);
|
const step = Math.ceil(currentMax / 3);
|
return {
|
max: step * 3,
|
interval: step,
|
};
|
};
|
|
// 加载JSON数据
|
const loadJsonData = async () => {
|
try {
|
// 这个result是用的上述的插值(暂时先不用)
|
// const result = processData(simStore.rainFalls);
|
const result = simStore.rainFalls;
|
if (result?.length) {
|
rainfallData.value = result;
|
if (rainfallData.value.length > 0) {
|
// data1.value = [rainfallData.value[0].intensity];
|
// data2.value = [rainfallData.value[0].total];
|
// 默认初始从0开始的
|
data1.value = [0];
|
data2.value = [0];
|
updateChart();
|
}
|
}
|
} catch (error) {
|
console.error("数据加载失败:", error);
|
}
|
};
|
|
// 更新图表配置
|
const updateChart = () => {
|
const option = {
|
animation: false,
|
tooltip: { trigger: "axis" },
|
// // 调整grid布局解决Y轴标签显示问题
|
grid: {
|
// left: "1%", // 左侧留更多空间
|
// right: "1%", // 右侧留更多空间
|
bottom: "1%",
|
containLabel: false,
|
},
|
legend: {
|
data: ["降雨数据", "累计雨量"],
|
textStyle: { color: "#fff" },
|
right: "10px",
|
// 添加legend点击事件处理
|
selected: {
|
降雨数据: true,
|
累计雨量: true,
|
},
|
},
|
xAxis: [
|
{
|
type: "category",
|
data: xAxisData.value,
|
axisLabel: { color: "#fff", rotate: 0 },
|
},
|
],
|
yAxis: [
|
{
|
type: "value",
|
name: "单位:mm",
|
min: 0,
|
...getDynamicYAxis(data1.value),
|
axisLabel: { color: "#fff" },
|
splitLine: { show: false },
|
// 确保名称显示完整
|
nameTextStyle: {
|
color: "#fff",
|
},
|
},
|
{
|
type: "value",
|
name: "单位:mm",
|
min: 0,
|
...getDynamicYAxis(data2.value),
|
axisLabel: { color: "#fff" },
|
splitLine: { show: true },
|
nameTextStyle: {
|
color: "#fff",
|
},
|
},
|
],
|
series: [
|
{
|
name: "降雨数据",
|
type: "bar", // 明确指定类型
|
data: data1.value,
|
itemStyle: { color: "#3268fe" },
|
},
|
{
|
name: "累计雨量",
|
type: "line", // 明确指定类型
|
yAxisIndex: 1,
|
data: data2.value,
|
lineStyle: { color: "#ffb637" },
|
},
|
],
|
};
|
myChart1.setOption(option, true);
|
};
|
|
// 数据更新
|
const updateData = () => {
|
if (dataIndex.value < rainfallData.value.length) {
|
const item = rainfallData.value[dataIndex.value];
|
data1.value.push(item.intensity);
|
data2.value.push(item.total);
|
xAxisData.value.push(syncTimeWithTimeline());
|
dataIndex.value++;
|
updateChart();
|
} else {
|
stopUpdating();
|
}
|
};
|
|
// 控制方法
|
const startUpdating = (interval = 60000) => {
|
if (!updateInterval) {
|
updateInterval = setInterval(updateData, interval);
|
}
|
};
|
|
const stopUpdating = () => {
|
clearInterval(updateInterval);
|
updateInterval = null;
|
};
|
|
const resetLoading = () => {
|
stopUpdating();
|
dataIndex.value = 0;
|
data1.value = [];
|
data2.value = [];
|
xAxisData.value = ["00:00"];
|
if (rainfallData.value.length) {
|
data1.value = [rainfallData.value[0].intensity];
|
data2.value = [rainfallData.value[0].total];
|
}
|
updateChart();
|
};
|
|
// 初始化
|
loadJsonData();
|
|
return {
|
myChart1,
|
startUpdating,
|
stopUpdating,
|
resetLoading,
|
};
|
};
|
|
const setEcharts2 = () => {
|
const chartDom = document.getElementById("echarts2");
|
const myChart2 = echarts.init(chartDom);
|
|
// 图表数据(与echarts1保持相同结构)
|
let flowData = ref([]); // 原始数据
|
let data1 = ref([]); // 实时流量
|
let data2 = ref([]); // 累计流量
|
let xAxisData = ref(["00:00"]);
|
let updateInterval = ref(null);
|
let dataIndex = ref(0);
|
|
// 动态计算Y轴范围
|
const calculateDynamicYAxis = (dataArray) => {
|
const currentMax = Math.max(...dataArray, 1);
|
const step = Math.ceil(currentMax / 3);
|
return {
|
max: step * 3,
|
interval: step,
|
};
|
};
|
const loadJsonData = async () => {
|
try {
|
const response = await fetch("/json/于家西沟断面下数据.json");
|
const result = await response.json();
|
if (result?.data?.length) {
|
flowData.value = result.data;
|
if (flowData.value.length > 0) {
|
data1.value = [flowData.value[0].value];
|
data2.value = [flowData.value[0].total];
|
updateChart();
|
}
|
}
|
} catch (error) {
|
console.error("数据加载失败:", error);
|
}
|
};
|
|
// 图表配置(与echarts1保持相同结构和样式)
|
const updateChart = () => {
|
const option = {
|
animation: false,
|
tooltip: {
|
trigger: "axis",
|
axisPointer: { type: "cross" },
|
},
|
grid: {
|
// 注释是因为 y轴上的单位被覆盖掉了
|
// left: "1%",
|
// right: "1%",
|
bottom: "1%",
|
containLabel: false,
|
},
|
legend: {
|
data: ["实时流量", "累计流量"],
|
textStyle: { color: "#fff" },
|
right: "10px",
|
selected: {
|
实时流量: true,
|
累计流量: true,
|
},
|
},
|
xAxis: [
|
{
|
type: "category",
|
data: xAxisData.value,
|
axisLabel: {
|
color: "#fff",
|
rotate: 0,
|
},
|
},
|
],
|
yAxis: [
|
{
|
type: "value",
|
name: "单位:m³/min",
|
min: 0,
|
...calculateDynamicYAxis(data1.value),
|
axisLabel: { color: "#fff" },
|
splitLine: { show: false },
|
nameTextStyle: {
|
color: "#fff",
|
},
|
},
|
{
|
type: "value",
|
name: "单位:m³",
|
min: 0,
|
...calculateDynamicYAxis(data2.value),
|
axisLabel: { color: "#fff" },
|
splitLine: { show: true },
|
nameTextStyle: {
|
color: "#fff",
|
},
|
},
|
],
|
series: [
|
{
|
name: "实时流量",
|
type: "bar",
|
data: data1.value,
|
itemStyle: {
|
color: "#3268fe",
|
},
|
},
|
{
|
name: "累计流量",
|
type: "line",
|
yAxisIndex: 1,
|
data: data2.value,
|
lineStyle: {
|
color: "#ffb637",
|
},
|
},
|
],
|
};
|
myChart2.setOption(option, true);
|
};
|
|
// 数据更新(与echarts1相同逻辑)
|
const updateData = () => {
|
if (dataIndex.value < flowData.value.length) {
|
const item = flowData.value[dataIndex.value];
|
data1.value.push(item.value);
|
data2.value.push(item.total);
|
xAxisData.value.push(syncTimeWithTimeline());
|
dataIndex.value++;
|
updateChart();
|
} else {
|
stopUpdating();
|
}
|
};
|
|
// 控制方法(与echarts1完全一致)
|
const startUpdating = (interval = 1000) => {
|
if (!updateInterval) {
|
updateInterval = setInterval(updateData, interval);
|
}
|
};
|
|
const stopUpdating = () => {
|
if (updateInterval) {
|
clearInterval(updateInterval);
|
updateInterval = null;
|
}
|
};
|
|
const resetLoading = () => {
|
stopUpdating();
|
dataIndex.value = 0;
|
data1.value = [];
|
data2.value = [];
|
xAxisData.value = ["00:00"];
|
if (flowData.value.length > 0) {
|
data1.value = [flowData.value[0].value];
|
data2.value = [flowData.value[0].total];
|
}
|
updateChart();
|
};
|
|
// 初始化
|
loadJsonData();
|
|
return {
|
myChart2,
|
startUpdating,
|
stopUpdating,
|
resetLoading,
|
};
|
};
|
|
const handleResize = () => {
|
const chartBox1 = document.getElementById("echarts1");
|
const chartBox2 = document.getElementById("echarts2");
|
if (chartBox1 && myChart1) chartssize(chartBox1, myChart1);
|
if (chartBox2 && myChart2) chartssize(chartBox2, myChart2);
|
};
|
|
onMounted(() => {
|
// 时间轴时间的变化
|
EventBus.on("time-update", (time) => {
|
nowTime.value = time;
|
});
|
chart1Data.value = setEcharts1();
|
chart2Data.value = setEcharts2();
|
myChart1 = chart1Data.value.myChart1;
|
myChart2 = chart2Data.value.myChart2;
|
window.addEventListener("resize", handleResize);
|
});
|
|
onBeforeUnmount(() => {
|
nowTime.value = null;
|
if (intervalId1) clearInterval(intervalId1);
|
if (intervalId2) clearInterval(intervalId2);
|
if (dataIntervalId) clearInterval(dataIntervalId);
|
if (myChart1) myChart1.dispose();
|
if (myChart2) myChart2.dispose();
|
window.removeEventListener("resize", handleResize);
|
});
|
onUnmounted(() => {
|
EventBus.off("reset-table"); // 移除事件监听
|
EventBus.off("clear-echart");
|
EventBus.off("time-update"); // 清理事件监听
|
});
|
</script>
|
|
<style lang="less" scoped>
|
@import url("../../assets/css/right.css");
|
@import url("../../assets/css/infobox.css");
|
|
:deep(.el-table td),
|
:deep(.el-table th) {
|
padding: 0 !important;
|
}
|
|
:deep(.el-table .cell) {
|
line-height: normal !important;
|
padding: 0 1px !important;
|
}
|
|
.table-container {
|
margin-left: 20px;
|
max-height: 100%;
|
overflow-y: auto;
|
}
|
|
.warp {
|
height: calc(27% - 1px);
|
width: 95%;
|
position: absolute;
|
margin: 0 auto;
|
overflow: hidden;
|
color: white;
|
|
ul {
|
list-style: none;
|
padding: 0;
|
margin: 0 auto;
|
|
li,
|
a {
|
display: block;
|
height: 30px;
|
line-height: 30px;
|
display: flex;
|
justify-content: space-between;
|
font-size: 15px;
|
}
|
|
.date {
|
width: 85px;
|
}
|
}
|
}
|
</style>
|