<template>
|
<div class="listCard">
|
<!-- <div>方案数量: {{ simStore.schemCard.length }}</div> -->
|
<el-card
|
v-if="!schemeInfoShow"
|
v-for="(item, key) in schemeList"
|
:key="key"
|
:class="{ selected: selectedId === item.id }"
|
@click="selectScheme(item.id)"
|
>
|
<div>
|
<p>方案名称 : {{ item.name }}</p>
|
<p>创建时间 : {{ formatTime(item.createTime) }}</p>
|
<p>
|
方案状态 :
|
<span style="color: aquamarine">
|
{{ item.result === "-1" ? "出错" : item.result || "创建仿真" }}
|
</span>
|
</p>
|
</div>
|
<div class="cardMenu">
|
<div style="float: right; margin-top: 3px">
|
<el-button size="small" @click="setSchemClick(item)"
|
>方案详情</el-button
|
>
|
<el-button
|
size="small"
|
v-show="item.type !== 2"
|
@click="startPlay(item)"
|
>进入模拟</el-button
|
>
|
<el-button size="small" v-show="item.type == 2" @click="rePlay(item)"
|
>历史回放</el-button
|
>
|
<!-- :disabled="item.status !== 2" -->
|
</div>
|
</div>
|
</el-card>
|
<schemeInfo
|
v-if="schemeInfoShow"
|
:selectedScheme="currentScheme"
|
@back="handleBack"
|
/>
|
<flowRateTab v-if="schemeInfoShow"> 123 </flowRateTab>
|
</div>
|
<Message
|
@close="close"
|
class="mess"
|
v-show="messageShow"
|
:mesData="mesData"
|
/>
|
</template>
|
|
<script setup>
|
import { EventBus } from "@/eventBus"; // 引入事件总线
|
import {
|
nextTick,
|
onMounted,
|
ref,
|
watch,
|
defineEmits,
|
onUnmounted,
|
inject,
|
} from "vue";
|
import dayjs from "dayjs";
|
import { initeWaterPrimitiveView } from "@/utils/water";
|
import Message from "@/components/tools/Message.vue";
|
import { useSimStore } from "@/store/simulation.js";
|
import { SimAPIStore } from "@/store/simAPI";
|
import schemeInfo from "@/components/monifangzhen/schemeInfo.vue";
|
import flowRateTab from "@/components/monifangzhen/flowRateTab.vue";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
const emit = defineEmits(["start", "end", "reset", "closeBtn"]);
|
import {
|
getSimData,
|
deleteSimData,
|
getSimStart,
|
getSimDataById,
|
getSimresult,
|
} from "@/api/trApi.js";
|
|
import { getAeraTownCode, getDeviceNWJ } from "@/api/hpApi";
|
import { createPoint, removeEntities, clearAllPoints } from "@/utils/map";
|
import { deviceDictList, getDictName } from "@/constant/dict.js";
|
|
const simStore = useSimStore();
|
const simAPIStore = SimAPIStore();
|
// 选中的方案 ID
|
const selectedId = ref(null);
|
// 当前选中的方案信息
|
const currentScheme = ref(null);
|
// 选中方案
|
function selectScheme(id) {
|
selectedId.value = id;
|
}
|
|
function formatTime(time) {
|
return dayjs(time).format("YYYY-MM-DD HH:mm:ss");
|
}
|
|
const messageShow = ref(false);
|
|
const schemeInfoShow = ref(false);
|
|
const mesData = ref(null);
|
|
function setSchemClick(item) {
|
mesData.value = item;
|
messageShow.value = true;
|
}
|
|
function close() {
|
messageShow.value = false;
|
}
|
|
// 实时模拟五分钟请求一次的定时器
|
const realTimeSimInterval = ref(null);
|
|
const { startSimulate, endSimulate } = inject("simulateActions");
|
|
const BJCode = ref([
|
{ label: "密云区", value: "110118000000" },
|
{ label: "房山区", value: "110111000000" },
|
{ label: "门头沟区", value: "110109000000" },
|
{ label: "延庆区", value: "110119000000" },
|
{ label: "怀柔区", value: "110116000000" },
|
{ label: "昌平区", value: "110114000000" },
|
{ label: "平谷区", value: "110117000000" },
|
{ label: "海淀区", value: "110108000000" },
|
{ label: "石景山区", value: "110107000000" },
|
{ label: "丰台区", value: "110106000000" },
|
]);
|
|
async function startPlay(item) {
|
simStore.openDia = false;
|
clearAllPoints();
|
const areaName = item.areaName;
|
|
let districtCode;
|
|
// 1. 判断是否包含 “区”
|
if (!areaName.includes("区")) {
|
console.log(
|
`方案中模拟【${areaName}】不包含“区”,使用默认编码:怀柔区(110116000000)`
|
);
|
districtCode = "110116000000"; // 手动指定为怀柔区编码
|
} else {
|
// 2. 在 BJCode 中查找匹配的区域 value
|
const matchedArea = BJCode.value.find((area) => area.label === areaName);
|
|
if (!matchedArea) {
|
console.warn(`未找到 ${areaName} 对应的区域编码`);
|
return;
|
}
|
|
districtCode = matchedArea.value;
|
}
|
|
// 1. 获取乡镇区域编码
|
const areaRes = await getAeraTownCode(districtCode);
|
const districtCodes = areaRes.data.map((item) => item.districtCode);
|
|
// 2. 泥位计类型ID
|
const ids = "1437295811";
|
|
// 3. 并行请求所有设备数据
|
const requests = districtCodes.map((code) =>
|
getDeviceNWJ(ids, code)
|
.then((res) => res.data?.pageData || []) // 安全提取 pageData
|
.catch((err) => {
|
console.error(`请求失败 (code: ${code})`, err);
|
return []; // 出错时也返回空数组,避免 Promise.all 中断
|
})
|
);
|
|
// 4. 等待所有请求完成
|
const allPageDataArrays = await Promise.all(requests);
|
|
// 5. 合并二维数组为一维数组
|
const mergedPageData = allPageDataArrays.flat();
|
|
// 6. 如果不是“区”,则过滤出 deviceName 包含 "孙胡沟" 的设备
|
const filteredPageData = areaName.includes("区")
|
? mergedPageData
|
: mergedPageData.filter((device) => device.deviceName.includes("孙胡沟"));
|
|
// 孙胡沟设备经纬度映射
|
const deviceMapping = {
|
怀柔区琉璃庙镇孙胡沟椴树底下东沟泥位计5007: {
|
lon: 116.598891,
|
lat: 40.554979,
|
},
|
怀柔区琉璃庙镇孙胡沟村上台子河东南沟泥位计5006: {
|
lon: 116.593215,
|
lat: 40.554399,
|
},
|
};
|
|
let displayedData = filteredPageData;
|
|
if (!areaName.includes("区")) {
|
// 添加 lon 和 lan 字段
|
displayedData = filteredPageData.map((device) => {
|
const mapping = deviceMapping[device.deviceName];
|
return {
|
...device,
|
...(mapping && { lon: mapping.lon, lat: mapping.lat }), // 如果 mapping 存在,才添加
|
};
|
});
|
|
// 添加额外的两个点位
|
const extraPoint1 = {
|
deviceName: "弯沟1",
|
longitude: 116.597836,
|
latitude: 40.564962,
|
// height: 530.14,
|
type: "泥位计",
|
lon: 116.597836,
|
lat: 40.564962,
|
dictDeviceType: "1437295811",
|
deviceId: "custom_extraPoint1", // 手动加一个唯一 ID
|
};
|
|
const extraPoint2 = {
|
deviceName: "弯沟2",
|
longitude: 116.591571,
|
latitude: 40.573093,
|
// height: 483.89,
|
type: "泥位计",
|
lon: 116.591571,
|
lat: 40.573093,
|
dictDeviceType: "1437295811",
|
deviceId: "custom_extraPoint2", // 手动加一个唯一 ID
|
};
|
|
displayedData = [...displayedData, extraPoint1, extraPoint2];
|
// displayedData = [...displayedData, extraPoint1];
|
}
|
|
console.log(
|
displayedData,
|
areaName.includes("区")
|
? "全部泥位计设备列表"
|
: "孙胡沟泥位计 + 额外点位列表"
|
);
|
|
// 7. 创建点
|
displayedData.forEach((item) => {
|
// 根据需求可增删
|
item.type = getDictName(deviceDictList, item.dictDeviceType);
|
item.name = item.deviceName;
|
item.id = item.deviceId;
|
item.className = "device";
|
item.showLabel = true;
|
|
createPoint(item);
|
});
|
|
if (item.status === 2) {
|
ElMessage.warning("当前方案正在分析中,无法进入模拟!");
|
return;
|
}
|
|
if (item.status === 20) {
|
ElMessage.error("当前方案分析出错,请重新新建方案!");
|
return;
|
}
|
|
// 如果是已完成的方案(status == 10)
|
if (item.status === 10) {
|
const flyHeight = item.areaType === 1 ? 100000 : 50000;
|
simStore.setSelectedScheme(item);
|
|
if (item.areaType === 1) {
|
EventBus.emit("select-geom", {
|
geom: item.geom,
|
flyHeight,
|
shouldShowFill: false,
|
});
|
} else {
|
initeWaterPrimitiveView();
|
}
|
|
currentScheme.value = item;
|
schemeInfoShow.value = true;
|
emit("closeBtn", false);
|
startSimulate();
|
return;
|
}
|
|
// 新建方案,没有 status 和 serviceName 且 type != 2
|
if (!item.status && !item.serviceName && item.type !== 2) {
|
try {
|
await getSimStart(item.id);
|
const res = await getSimDataById(item.id);
|
|
item.serviceName = res.data[0]?.serviceName || null;
|
simStore.setSelectedScheme(item);
|
ElMessage.warning("当前方案正在分析中,请稍后再模拟");
|
getScheme();
|
} catch (e) {
|
console.error("获取模拟数据失败:", e);
|
}
|
return;
|
}
|
|
// 默认情况:有服务名称
|
simStore.setSelectedScheme(item);
|
}
|
|
// 实时模拟历史回放
|
function rePlay(item) {
|
// 当前选中的方案
|
simStore.setSelectedScheme(item);
|
// 拿id去请求results接口,如果长度不为0,则可以进行历史回放
|
getSimresult(item.id)
|
.then((res) => {
|
if (res.code == 500) {
|
// 如果长度为0,提示用户并且不进行后续操作
|
ElMessage.warning("提示:没有可回放的数据!");
|
return; // 阻止后续操作
|
} else {
|
simStore.rePlayList = res.data;
|
console.log(simStore.rePlayList, "实时模拟历史回放列表");
|
}
|
// 使用 nextTick 确保 DOM 更新后再执行后续操作
|
nextTick(() => {
|
initeWaterPrimitiveView();
|
startSimulate();
|
});
|
})
|
.catch((error) => {
|
console.log("请求失败:", error);
|
// 错误处理
|
});
|
}
|
|
function handleBack(value) {
|
if (value === false) {
|
schemeInfoShow.value = false;
|
}
|
}
|
|
const handleHideSchemeInfo = () => {
|
schemeInfoShow.value = false;
|
emit("closeBtn", true);
|
};
|
|
// 注册事件监听器
|
EventBus.on("hide-schemeInfo", handleHideSchemeInfo);
|
|
const schemeList = ref([]);
|
let intervalId = null; // 用于存储 setInterval 的返回值
|
// 获取方案列表
|
async function getScheme() {
|
try {
|
const res = await getSimData();
|
schemeList.value = res.data;
|
|
const shouldStop = schemeList.value.every(
|
(item) =>
|
item.result == "创建仿真" ||
|
item.result == "完成" ||
|
item.result == "-1" ||
|
item.result == "已停止" ||
|
item.result == "运行中"
|
);
|
simAPIStore.shouldPoll = !shouldStop; // 修改 Pinia 状态
|
// 3. 如果需要停止
|
if (shouldStop) {
|
if (intervalId) {
|
clearInterval(intervalId);
|
intervalId = null;
|
console.log("停止轮询");
|
}
|
return;
|
}
|
} catch (error) {
|
console.error("Error fetching data:", error);
|
}
|
}
|
|
// 监听 shouldPoll 状态变化
|
watch(
|
() => simAPIStore.shouldPoll,
|
(isStarted) => {
|
if (isStarted) {
|
getScheme(); // 首次立即获取一次
|
intervalId = setInterval(getScheme, 60 * 1000); // 每隔一分钟执行
|
}
|
// else if (intervalId !== null) {
|
// clearInterval(intervalId);
|
// intervalId = null;
|
// }
|
},
|
{ immediate: true }
|
);
|
|
const props = defineProps({
|
deleteSim: Boolean, // 接收父组件传递的函数
|
showAddIns: Boolean,
|
});
|
|
// 新建方案完成之后方案列表需实时刷新
|
watch(
|
() => props.showAddIns,
|
(newVal) => {
|
if (newVal == false) {
|
getScheme();
|
}
|
}
|
);
|
// 删除仿真列表
|
watch(
|
() => props.deleteSim,
|
(newVal) => {
|
if (newVal) {
|
deleteSim();
|
emit("reset");
|
}
|
}
|
);
|
const deleteSim = () => {
|
// 确保有选中的方案
|
if (!selectedId.value) {
|
ElMessage({
|
type: "warning",
|
message: "请先选择一个方案进行删除!",
|
});
|
return;
|
}
|
const selectedScheme = schemeList.value.find(
|
(item) => item.id === selectedId.value
|
);
|
const schemeName = selectedScheme ? selectedScheme.name : "未知方案";
|
ElMessageBox.confirm(`确定要删除方案 "${schemeName}" 吗?`, "删除方案", {
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
deleteSimData(selectedId.value).then((res) => {
|
getScheme();
|
});
|
ElMessage({
|
type: "success",
|
message: `方案 "${schemeName}" 删除成功`,
|
});
|
})
|
.catch(() => {});
|
};
|
|
onMounted(() => {
|
getScheme(); // 页面加载时立即获取数据
|
});
|
|
onUnmounted(() => {
|
EventBus.off("hide-schemeInfo", handleHideSchemeInfo);
|
|
if (intervalId !== null) {
|
clearInterval(intervalId); // 清除定时器
|
intervalId = null; // 重置 intervalId
|
}
|
});
|
</script>
|
|
<style lang="less" scoped>
|
.listCard {
|
flex: none !important;
|
height: calc(100% - 46px);
|
width: 100%;
|
overflow: auto;
|
color: white !important;
|
font-size: 14px;
|
|
p {
|
color: white !important;
|
}
|
}
|
|
.cardMenu {
|
padding-bottom: 30px;
|
color: white !important;
|
|
:last-child {
|
float: right;
|
}
|
}
|
|
.listCard-btn {
|
float: right;
|
background: url("@/assets/img/left/cardbtn.png") no-repeat;
|
width: 105px;
|
height: 35px;
|
text-align: center;
|
line-height: 35px;
|
margin-left: 10px;
|
cursor: pointer;
|
}
|
|
.listCard-btn-ac {
|
background: url("@/assets/img/left/cardbtnac.png") no-repeat;
|
}
|
|
.listCard-btn:hover {
|
background: url("@/assets/img/left/cardbtnac.png") no-repeat;
|
}
|
|
.mess {
|
position: absolute;
|
top: 10%;
|
left: 100%;
|
// top: 160px;
|
// left: 460px;
|
}
|
|
/deep/.el-card__body {
|
padding: 10px 5px 5px 10px;
|
box-sizing: border-box;
|
}
|
|
/deep/.el-card {
|
margin: 5px 10px 0px 5px;
|
border: 1px solid #437a74;
|
background: rgba(0, 0, 0, 0) !important;
|
}
|
|
/deep/.el-card:hover {
|
scale: (1.02);
|
border: 1px solid #acf1dd;
|
}
|
|
.selected {
|
border: 2px solid #acf1dd !important;
|
/* 选中时的边框样式 */
|
}
|
</style>
|