wangjuncheng
2025-05-15 e185715da3eb168373efa60f684774a0694990f2
Merge branch 'master' of http://103.135.160.14:9034/r/NslWeb
已添加1个文件
已修改13个文件
1407 ■■■■■ 文件已修改
src/api/hpApi.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/infobox.css 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monifangzhen/echartInfo.vue 321 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monifangzhen/schemeCard.vue 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/tools/LayerTree.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/simAPI.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/simulation.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/left/CitySim.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/left/KGSimOption/HistorySimulation.vue 313 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/left/KGSimOption/PredictiveSimulation.vue 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/left/KGSimOption/RealTimeSimulation copy.vue 341 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/left/KGSimOption/RealTimeSimulation.vue 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mnfz.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/hpApi.js
@@ -4,7 +4,7 @@
export async function fetchToken() {
  try {
    const response = await axios.post("/hp/auth/getToken", {
      username: "yxxx",
      username: "yunweiyonghu",
      password: "c2d6bba7f0a67701a97550684e39fa5f",
      systemName: "RS_SYSTEM",
    });
src/assets/css/infobox.css
@@ -150,7 +150,8 @@
  height: 100%;
  z-index: 40;
  border-radius: 4px;
  padding: 10px 10px;
  padding: 10px 0px;
  box-sizing: border-box;
}
.echartCont p {
@@ -165,6 +166,13 @@
  width: 100%;
  height: 30%;
}
.echartBox {
  flex: 1;
  margin-top: 10px;
  height: calc(100% - 41px);
}
/* .echarts1{
  width: 100%;
  height: 100%;
@@ -173,11 +181,6 @@
  width: 100%;
  height: 100%;
} */
.echartBox {
  flex: 1;
  margin-top: 10px;
  height: calc(100% - 41px);
}
.listinfo-btns {
  width: 100%;
src/components/monifangzhen/echartInfo.vue
@@ -105,7 +105,9 @@
  onUnmounted,
} from "vue";
import dayjs from "dayjs";
import { getRainfall } from "@/api";
import { useSimStore } from "@/store/simulation";
const simStore = useSimStore();
const { rainFalls } = simStore;
let dataIntervalId = null; // è¡¨æ ¼å®šæ—¶å™¨ ID
const jsonData = ref([]); // JSON æ•°æ®
@@ -224,11 +226,11 @@
  // è§¦å‘事件,将当前行的 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 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);
@@ -317,23 +319,69 @@
  return timeOnly;
};
const setEcharts1 = () => {
  // 1. åˆå§‹åŒ–图表
  const chartDom = document.getElementById("echarts1");
  myChart1 = echarts.init(chartDom);
// æš‚时先不用,主要功能为一分钟插值六十个数据
// function processData(originalData) {
//   const processedData = [];
//   let currentTotal = 0; // åŠ¨æ€ç´¯åŠ çš„ total
  // 2. æ•°æ®å®šä¹‰ï¼ˆå®Œå…¨ä¿æŒæ‚¨çš„原始变量)
//   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([]);
  let xAxisData = ref(["00:00"]);
  let updateInterval = null;
  let dataIndex = ref(0);
  // 4. åŠ¨æ€è®¡ç®—Y轴(新增的核心逻辑)
  // åŠ¨æ€è®¡ç®—Y轴范围
  const getDynamicYAxis = (dataArray) => {
    const currentMax = Math.max(...dataArray, 1); // ç¡®ä¿æœ€å°æ˜¾ç¤ºèŒƒå›´
    const currentMax = Math.max(...dataArray, 1);
    const step = Math.ceil(currentMax / 3);
    return {
      max: step * 3,
@@ -341,20 +389,21 @@
    };
  };
  // 5. åŠ è½½æ•°æ®ï¼ˆå®Œå…¨ä¿æŒæ‚¨çš„é€»è¾‘ï¼‰
  // åŠ è½½JSON数据
  const loadJsonData = async () => {
    try {
      const response = await fetch("/json/rainfall.json");
      const result = await response.json();
      if (result?.data?.length) {
        rainfallData.value = result.data;
        xAxisData.value = ["00:00"] || syncTimeWithTimeline(); // åˆå§‹æ—¶é—´ç‚¹
      // è¿™ä¸ª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].value];
          data2.value = [rainfallData.value[0].total];
          updateChart(); // åˆå§‹åŒ–图表
          // data1.value = [rainfallData.value[0].intensity];
          // data2.value = [rainfallData.value[0].total];
          // é»˜è®¤åˆå§‹ä»Ž0开始的
          data1.value = [0];
          data2.value = [0];
          updateChart();
        }
      }
    } catch (error) {
@@ -362,60 +411,70 @@
    }
  };
  // 6. å›¾è¡¨é…ç½®ï¼ˆä»…修改Y轴部分)
  // æ›´æ–°å›¾è¡¨é…ç½®
  const updateChart = () => {
    const option = {
      // >>> ä¿æŒæ‚¨åŽŸæœ‰çš„æ‰€æœ‰é…ç½® <<<
      animation: false,
      tooltip: { trigger: "axis" },
      grid: { left: "1%", right: "1%", bottom: "1%", containLabel: true },
      // // è°ƒæ•´grid布局解决Y轴标签显示问题
      grid: {
        // left: "1%", // å·¦ä¾§ç•™æ›´å¤šç©ºé—´
        // right: "1%", // å³ä¾§ç•™æ›´å¤šç©ºé—´
        bottom: "1%",
        containLabel: false,
      },
      legend: {
        data: ["降雨数据", "累计雨量"],
        textStyle: { color: "#fff" },
        right: "10px",
        // æ·»åŠ legend点击事件处理
        selected: {
          é™é›¨æ•°æ®: true,
          ç´¯è®¡é›¨é‡: true,
        },
      },
      // >>> æ‚¨åŽŸæ¥çš„xAxis配置(完全不变) <<<
      xAxis: [
        {
          type: "category",
          data: xAxisData.value, // æ‚¨çš„æ—¶é—´æ•°æ®
          data: xAxisData.value,
          axisLabel: { color: "#fff", rotate: 0 },
        },
      ],
      // >>> ä¿®æ”¹çš„Y轴配置(动态范围) <<<
      yAxis: [
        {
          // å·¦ä¾§Y轴(降雨)
          type: "value",
          name: "单位:mm",
          min: 0,
          ...getDynamicYAxis(data1.value), // åŠ¨æ€èŒƒå›´
          ...getDynamicYAxis(data1.value),
          axisLabel: { color: "#fff" },
          splitLine: { show: false },
          // ç¡®ä¿åç§°æ˜¾ç¤ºå®Œæ•´
          nameTextStyle: {
            color: "#fff",
          },
        },
        {
          // å³ä¾§Y轴(累计)
          type: "value",
          name: "单位:mm",
          min: 0,
          ...getDynamicYAxis(data2.value), // åŠ¨æ€èŒƒå›´
          ...getDynamicYAxis(data2.value),
          axisLabel: { color: "#fff" },
          splitLine: { show: true },
          nameTextStyle: {
            color: "#fff",
          },
        },
      ],
      // >>> ä¿æŒæ‚¨åŽŸæœ‰çš„series配置 <<<
      series: [
        {
          name: "降雨数据",
          type: "bar",
          type: "bar", // æ˜Žç¡®æŒ‡å®šç±»åž‹
          data: data1.value,
          itemStyle: { color: "#3268fe" },
        },
        {
          name: "累计雨量",
          type: "line",
          type: "line", // æ˜Žç¡®æŒ‡å®šç±»åž‹
          yAxisIndex: 1,
          data: data2.value,
          lineStyle: { color: "#ffb637" },
@@ -425,32 +484,30 @@
    myChart1.setOption(option, true);
  };
  // 7. æ•°æ®æ›´æ–°ï¼ˆä¿æŒæ‚¨çš„æ—¶é—´é€»è¾‘)
  // æ•°æ®æ›´æ–°
  const updateData = () => {
    if (dataIndex.value < rainfallData.value.length) {
      const item = rainfallData.value[dataIndex.value];
      data1.value.push(item.value);
      data1.value.push(item.intensity);
      data2.value.push(item.total);
      xAxisData.value.push(syncTimeWithTimeline()); // æ‚¨çš„æ—¶é—´ç”Ÿæˆæ–¹æ³•
      xAxisData.value.push(syncTimeWithTimeline());
      dataIndex.value++;
      updateChart(); // åŠ¨æ€è°ƒæ•´Yè½´
      updateChart();
    } else {
      stopUpdating();
    }
  };
  // 8. æŽ§åˆ¶æ–¹æ³•(完全不变)
  const startUpdating = (interval = 1000) => {
  // æŽ§åˆ¶æ–¹æ³•
  const startUpdating = (interval = 60000) => {
    if (!updateInterval) {
      updateInterval = setInterval(updateData, interval);
    }
  };
  const stopUpdating = () => {
    if (updateInterval) {
      clearInterval(updateInterval);
      updateInterval = null;
    }
    clearInterval(updateInterval);
    updateInterval = null;
  };
  const resetLoading = () => {
@@ -460,13 +517,13 @@
    data2.value = [];
    xAxisData.value = ["00:00"];
    if (rainfallData.value.length) {
      data1.value = [rainfallData.value[0].value];
      data1.value = [rainfallData.value[0].intensity];
      data2.value = [rainfallData.value[0].total];
    }
    updateChart();
  };
  // 9. åˆå§‹åŒ–
  // åˆå§‹åŒ–
  loadJsonData();
  return {
@@ -479,146 +536,138 @@
const setEcharts2 = () => {
  const chartDom = document.getElementById("echarts2");
  myChart2 = echarts.init(chartDom);
  const myChart2 = echarts.init(chartDom);
  let jsonData = ref([]);
  let data1 = ref([]);
  let data2 = ref([]);
  let xAxisData = ref([]);
  let updateInterval = null;
  // å›¾è¡¨æ•°æ®ï¼ˆä¸Žecharts1保持相同结构)
  let flowData = ref([]); // åŽŸå§‹æ•°æ®
  let data1 = ref([]); // å®žæ—¶æµé‡
  let data2 = ref([]); // ç´¯è®¡æµé‡
  let xAxisData = ref(["00:00"]);
  let updateInterval = ref(null);
  let dataIndex = ref(0);
  // åŠ è½½ JSON æ•°æ®ï¼ˆå®Œå…¨ä¿æŒåŽŸæœ‰é€»è¾‘ï¼‰
  // åŠ¨æ€è®¡ç®—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 && result.data && Array.isArray(result.data)) {
        jsonData.value = result.data;
        xAxisData.value = ["00:00"] || syncTimeWithTimeline(); // ä¿æŒæ‚¨çš„æ—¶é—´åˆå§‹åŒ–
        if (jsonData.value.length > 0) {
          data1.value.push(jsonData.value[0].value);
          data2.value.push(jsonData.value[0].total);
      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();
        }
        updateChart(); // åˆå§‹åŒ–时自动计算Y轴范围
      }
    } catch (error) {
      console.error("数据加载失败:", error);
    }
  };
  // åŠ¨æ€è®¡ç®—Y轴参数(新增核心功能)
  const calculateDynamicYAxis = (dataArray) => {
    const currentMax = Math.max(...dataArray, 1); // ç¡®ä¿æœ€å°æ˜¾ç¤ºèŒƒå›´
    const step = Math.ceil(currentMax / 3);
    return {
      max: step * 3,
      interval: step
    };
  };
  // æ›´æ–°å›¾è¡¨é…ç½®ï¼ˆä»…修改Y轴部分)
  // å›¾è¡¨é…ç½®ï¼ˆä¸Žecharts1保持相同结构和样式)
  const updateChart = () => {
    const option = {
      // >>> ä¿æŒæ‚¨åŽŸæœ‰çš„æ‰€æœ‰é…ç½® <<<
      animation: false,
      tooltip: {
        trigger: "axis",
        axisPointer: { type: "cross" }
        axisPointer: { type: "cross" },
      },
      grid: {
        left: "1%",
        right: "1%",
        // æ³¨é‡Šæ˜¯å› ä¸º y轴上的单位被覆盖掉了
        // left: "1%",
        // right: "1%",
        bottom: "1%",
        containLabel: true
        containLabel: false,
      },
      legend: {
        data: ["实时流量", "累计流量"],
        textStyle: { color: "#fff" },
        right: "10px"
        right: "10px",
        selected: {
          å®žæ—¶æµé‡: true,
          ç´¯è®¡æµé‡: true,
        },
      },
      // >>> æ‚¨åŽŸæ¥çš„xAxis配置(完全不变) <<<
      xAxis: [{
        type: "category",
        data: xAxisData.value,
        axisLabel: { color: "#fff", rotate: 0 }
      }],
      // >>> ä¿®æ”¹çš„Y轴配置(动态范围) <<<
      xAxis: [
        {
          type: "category",
          data: xAxisData.value,
          axisLabel: {
            color: "#fff",
            rotate: 0,
          },
        },
      ],
      yAxis: [
        { // å·¦ä¾§Y轴(实时流量)
        {
          type: "value",
          name: "单位:m³/min",
          min: 0,
          ...calculateDynamicYAxis(data1.value),
          axisLabel: {
            formatter: "{value}",
            color: "#fff",
            align: "right"
          },
          axisLabel: { color: "#fff" },
          splitLine: { show: false },
          nameTextStyle: {
            padding: [0, 0, 0, 20],
            color: "#fff"
            color: "#fff",
          },
          splitLine: { show: false }
        },
        { // å³ä¾§Y轴(累计流量)
        {
          type: "value",
          name: "单位:m³",
          min: 0,
          ...calculateDynamicYAxis(data2.value),
          axisLabel: {
            formatter: "{value}",
            color: "#fff",
            align: "left"
          },
          axisLabel: { color: "#fff" },
          splitLine: { show: true },
          nameTextStyle: {
            padding: [0, 10, 0, 0],
            color: "#fff"
            color: "#fff",
          },
          splitLine: { show: true }
        }
        },
      ],
      // >>> ä¿æŒæ‚¨åŽŸæœ‰çš„series配置 <<<
      series: [
        {
          name: "实时流量",
          type: "bar",
          tooltip: { valueFormatter: value => value + " m³/min" },
          data: data1.value,
          itemStyle: { color: "blue" }
          itemStyle: {
            color: "#3268fe",
          },
        },
        {
          name: "累计流量",
          type: "line",
          yAxisIndex: 1,
          tooltip: { valueFormatter: value => value + " m³" },
          data: data2.value,
          lineStyle: { color: "#ffb637" }
        }
      ]
          lineStyle: {
            color: "#ffb637",
          },
        },
      ],
    };
    myChart2.setOption(option, true); // å¼ºåˆ¶åˆ·æ–°
    myChart2.setOption(option, true);
  };
  // æ•°æ®æ›´æ–°ï¼ˆä¿æŒæ‚¨çš„æ—¶é—´é€»è¾‘)
  // æ•°æ®æ›´æ–°ï¼ˆä¸Žecharts1相同逻辑)
  const updateData = () => {
    if (dataIndex.value < jsonData.value.length) {
      const newItem = jsonData.value[dataIndex.value];
      data1.value.push(newItem.value);
      data2.value.push(newItem.total);
      xAxisData.value.push(syncTimeWithTimeline()); // æ‚¨çš„æ—¶é—´ç”Ÿæˆæ–¹æ³•
      dataIndex.value += 2; // ä¿æŒæ‚¨çš„索引步长
      updateChart(); // è‡ªåŠ¨è°ƒæ•´Yè½´
    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);
@@ -638,9 +687,9 @@
    data1.value = [];
    data2.value = [];
    xAxisData.value = ["00:00"];
    if (jsonData.value.length) {
      data1.value.push(jsonData.value[0].value);
      data2.value.push(jsonData.value[0].total);
    if (flowData.value.length > 0) {
      data1.value = [flowData.value[0].value];
      data2.value = [flowData.value[0].total];
    }
    updateChart();
  };
@@ -652,7 +701,7 @@
    myChart2,
    startUpdating,
    stopUpdating,
    resetLoading
    resetLoading,
  };
};
src/components/monifangzhen/schemeCard.vue
@@ -1,8 +1,13 @@
<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)">
    <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>
@@ -15,15 +20,26 @@
      </div>
      <div class="cardMenu">
        <div style="float: right; margin-top: 3px">
          <el-button size="small" @click="setSchemClick(item)">方案详情</el-button>
          <el-button size="small" @click="setSchemClick(item)"
            >方案详情</el-button
          >
          <el-button size="small" @click="startPlay(item)">进入模拟</el-button>
          <!--  :disabled="item.taskStatus !== 2" -->
        </div>
      </div>
    </el-card>
    <schemeInfo v-if="schemeInfoShow" :selectedScheme="currentScheme" @back="handleBack" />
    <schemeInfo
      v-if="schemeInfoShow"
      :selectedScheme="currentScheme"
      @back="handleBack"
    />
  </div>
  <Message @close="close" class="mess" v-show="messageShow" :mesData="mesData" />
  <Message
    @close="close"
    class="mess"
    v-show="messageShow"
    :mesData="mesData"
  />
</template>
<script setup>
@@ -73,7 +89,7 @@
  //   alert("当前方案尚未完成,无法进入模拟!");
  //   return;
  // }
  simStore.setSelectedScheme(item)
  simStore.setSelectedScheme(item);
  currentScheme.value = item;
  schemeInfoShow.value = true;
@@ -96,7 +112,6 @@
// æ³¨å†Œäº‹ä»¶ç›‘听器
EventBus.on("hide-schemeInfo", handleHideSchemeInfo);
/////////////////////// è°ƒç”¨æŽ¥å£ï¼ˆä½¿ç”¨æ—¶æ‰“开) ///////////////////////
import { getRegionData, getSimData, deleteSimData } from "@/api/trApi.js";
@@ -150,17 +165,15 @@
    });
    return;
  }
  const selectedScheme = schemeList.value.find((item) => item.id === selectedId.value);
  const selectedScheme = schemeList.value.find(
    (item) => item.id === selectedId.value
  );
  const schemeName = selectedScheme ? selectedScheme.name : "未知方案";
  ElMessageBox.confirm(
    `确定要删除方案 "${schemeName}" å—?`,
    "删除方案",
    {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }
  )
  ElMessageBox.confirm(`确定要删除方案 "${schemeName}" å—?`, "删除方案", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      deleteSimData(selectedId.value).then((res) => {
        getScheme();
@@ -170,8 +183,7 @@
        message: `方案 "${schemeName}" åˆ é™¤æˆåŠŸ`,
      });
    })
    .catch(() => {
    });
    .catch(() => {});
};
/////////////////////// è°ƒç”¨æŽ¥å£ç»“束 ///////////////////////
onUnmounted(() => {
src/components/tools/LayerTree.vue
@@ -384,6 +384,7 @@
</script>
<style lang="less" scoped>
@import url("../../assets/css/infobox.css");
.layer-tree {
  background: url("@/assets/img/tools/plotting_new.png");
  width: 200px;
src/store/simAPI.js
@@ -94,6 +94,7 @@
    // ä¿å­˜æ–¹æ¡ˆå¼¹çª—
    const addSimCheme = async (forms) => {
        // è¡¨å•验证
        if (!validateForm(forms)) {
            throw new Error('表单验证未通过'); // æŠ›å‡ºé”™è¯¯ï¼Œé˜»æ­¢ç»§ç»­æ‰§è¡Œ
        }
@@ -117,6 +118,7 @@
    }
    return {
        selectTab,
        isLoading,
src/store/simulation.js
@@ -27,8 +27,11 @@
    const schemCard = ref([])
    const backToHome = ref(false)
    const selectedScheme = ref(null)
    const rainFalls = ref()
    const setSelectedScheme = (scheme) => {
        selectedScheme.value = scheme
        rainFalls.value = JSON.parse(scheme.data).rainfalls
        console.log(rainFalls.value, 'shceme')
    }
    const clearSelectedScheme = () => {
        selectedScheme.value = null
@@ -141,6 +144,7 @@
        showDangerAssess,
        schemCard,
        backToHome,
        rainFalls,
        // æ–¹æ¡ˆç›¸å…³æ–¹æ³•
        setSchemCard,
src/views/left/CitySim.vue
@@ -11,7 +11,12 @@
      é‡ç‚¹åŒºåŸŸä»¿çœŸï¼ˆ10m精度)
    </div>
    <div class="forms">
      <el-form :model="forms" label-width="auto" style="max-width: 600px">
      <el-form
        :rules="rules"
        :model="forms"
        label-width="auto"
        style="max-width: 600px"
      >
        <el-form-item label="方案名称:">
          <el-input
            v-model="forms.name"
@@ -466,7 +471,10 @@
};
// å¼€å§‹æ¨¡æ‹Ÿ
function startPlay() {
async function startPlay() {
  // å¼€å§‹æ¨¡æ‹Ÿå‰éœ€è¦å…ˆä¿å­˜æ–¹æ¡ˆ
  await simStore.addSimCheme(forms);
  EventBus.emit("close-selectArea");
  initeWaterPrimitiveView();
  startSimulate();
}
src/views/left/KGSimOption/HistorySimulation.vue
@@ -1,17 +1,22 @@
<template>
  <div class="history-simulation">
    <div class="left-top">
      <span>历史模拟</span>
      <span class="clickable-text" @click="toggleDetails">{{
        isCollapsed ? "展开" : "收起"
      }}</span>
    </div>
    <div class="details" :class="{ hidden: isCollapsed }">
      <div class="input-group">
        <div class="input-item">
          <label>历史雨情:</label>
  <div class="left-top">
    <span>历史模拟</span>
    <span class="clickable-text" @click="toggleDetails">{{
      isCollapsed ? "展开" : "收起"
    }}</span>
  </div>
  <el-form
    ref="historySimForm"
    class="history-simulation"
    :model="formData"
    label-width="auto"
    style="max-width: 600px"
  >
    <el-collapse-transition style="margin-top: 10px">
      <div v-show="!isCollapsed">
        <el-form-item label="历史雨情:" prop="history">
          <el-select
            v-model="rainfallHistory"
            v-model="formData.history"
            placeholder="请选择"
            popper-class="mySelectStyle"
          >
@@ -20,118 +25,77 @@
              :key="item.id"
              :label="item.name"
              :value="item.name"
            ></el-option>
            />
          </el-select>
        </div>
      </div>
      <div class="input-group">
        <div class="input-item">
          <label>方案名称:</label>
        </el-form-item>
        <el-form-item label="方案名称:" prop="name">
          <el-input v-model="formData.name" type="text" placeholder="请输入" />
        </el-form-item>
        <el-form-item label="降雨总量:" prop="rainfall">
          <el-input
            v-model="schemeName"
            type="text"
            placeholder="请输入"
          ></el-input>
        </div>
        <div class="input-item">
          <label>降雨总量:</label>
          <el-input
            v-model="totalRainfall"
            v-model="formData.rainfall"
            type="number"
            placeholder="请输入"
          ></el-input>
          <span>mm</span>
        </div>
        <div class="input-item">
          <label>降雨强度:</label>
          >
            <template #append>mm</template>
          </el-input>
        </el-form-item>
        <el-form-item label="降雨强度:" prop="intensity">
          <el-input
            v-model="rainfallIntensity"
            v-model="formData.intensity"
            type="number"
            placeholder="请输入"
          ></el-input>
          <span>mm/h</span>
        </div>
        <div class="input-item">
          <label>选择时间:</label>
          >
            <template #append>mm/h</template>
          </el-input>
        </el-form-item>
        <el-form-item label="选择时间:" prop="hours">
          <el-date-picker
            v-model="hours"
            v-model="formData.hours"
            type="datetimerange"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            format="YYYY-MM-DD HH:mm:ss"
            date-format="YYYY/MM/DD ddd"
            time-format="A hh:mm:ss"
            @change="change"
            @change="handleTimeChange"
          />
          <span>h</span>
        </div>
        <div class="input-item">
          <label>降雨时长:</label>
          <el-input
            disabled
            v-model="rainfallDuration"
            type="number"
            placeholder="请输入"
          ></el-input>
          <span>h</span>
        </div>
      </div>
      <!-- <div>
        <label>仿真参数:</label>
        <div style="width: 100%; height: 60px; background-color: #fff"></div>
      </div> -->
    </div>
    <div class="buttons">
      <el-button type="primary" @click="openSaveDialog">保存方案</el-button>
      <el-button type="success" @click="startPlay">开始模拟</el-button>
    </div>
        </el-form-item>
    <!-- ä¿å­˜æ–¹æ¡ˆå¯¹è¯æ¡† -->
    <!-- <el-dialog
      v-model="saveDialogVisible"
      title="保存方案"
      width="50%"
      :before-close="handleClose"
      custom-class="custom-dialog"
    >
      <div class="dialog-content">
        <p><strong>方案名称:</strong>{{ schemeName }}</p>
        <p><strong>所选重点沟:</strong>{{ props.selectedArea }}</p>
        <p><strong>模拟类型:</strong>历史模拟</p>
        <p><strong>历史雨情:</strong>{{ selectedRainfallName }}</p>
        <p><strong>降雨总量:</strong>{{ totalRainfall }} mm</p>
        <p><strong>降雨强度:</strong>{{ rainfallIntensity }} mm/h</p>
        <p><strong>降雨时长:</strong>{{ rainfallDuration }} h</p>
        <el-form-item label="降雨时长:" prop="duration">
          <el-input
            v-model="formData.duration"
            type="number"
            placeholder="自动计算"
            disabled
          >
            <template #append>h</template>
          </el-input>
        </el-form-item>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="saveDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="confirmSave">确定保存</el-button>
        </span>
      </template>
    </el-dialog> -->
    </el-collapse-transition>
  </el-form>
  <div class="buttons">
    <el-button type="primary" @click="openSaveDialog">保存方案</el-button>
    <el-button type="success" @click="startPlay">开始模拟</el-button>
  </div>
</template>
<script setup>
import { ref, computed, inject } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { ref, computed, reactive, inject } from "vue";
import { ElMessage } from "element-plus";
import { initeWaterPrimitiveView } from "@/utils/water";
import { useSimStore } from "@/store/simulation.js"; // å¼•å…¥ Store
import { useSimStore } from "@/store/simulation.js";
import { getRainfallData } from "@/api/hpApi.js";
import { SimAPIStore } from "@/store/simAPI";
import { EventBus } from "@/eventBus"; // å¼•入事件总线
import { EventBus } from "@/eventBus";
// èŽ·å– Store å®žä¾‹
const simStore = SimAPIStore();
// æµ‹è¯•获取雨量数据
// const getRainData = () => {
//   getRainfallData()
//     .then((a) => {
//       console.log(a, '宏图获取雨量数据');
//     })
// };
// æ³¨å…¥æ¨¡æ‹Ÿæ“ä½œæ–¹æ³•
const { startSimulate, endSimulate } = inject("simulateActions");
@@ -144,15 +108,19 @@
  },
});
// æ•°æ®ç»‘定
const rainfallHistory = ref("XXå¹´50mm降雨"); // é»˜è®¤é€‰ä¸­ç¬¬äºŒé¡¹
const totalRainfall = ref(50); // é™é›¨æ€»é‡
const schemeName = ref("方案名称"); // æ–¹æ¡ˆåç§°
const rainfallIntensity = ref(70); // é™é›¨å¼ºåº¦
const hours = ref(null);
const rainfallDuration = ref(null); // é™é›¨æ—¶é•¿
const isCollapsed = ref(false); // æŽ§åˆ¶è¯¦æƒ…的展开/收起状态
const saveDialogVisible = ref(false); // æŽ§åˆ¶ä¿å­˜æ–¹æ¡ˆå¯¹è¯æ¡†çš„æ˜¾ç¤ºçŠ¶æ€
// è¡¨å•数据
const formData = reactive({
  history: "XXå¹´50mm降雨",
  name: "方案名称",
  rainfall: 50,
  intensity: 70,
  hours: null,
  duration: null,
  type: 3,
});
// æŽ§åˆ¶è¯¦æƒ…的展开/收起状态
const isCollapsed = ref(false);
// åŽ†å²é›¨æƒ…æ•°æ®
const HistoricalRainData = [
@@ -166,7 +134,7 @@
// èŽ·å–å½“å‰é€‰ä¸­çš„åŽ†å²é›¨æƒ…åç§°
const selectedRainfallName = computed(() => {
  const selected = HistoricalRainData.find(
    (item) => item.name === rainfallHistory.value
    (item) => item.name === formData.history
  );
  return selected ? selected.name : "";
});
@@ -178,92 +146,60 @@
const { calculateHoursDifference } = inject("calculateHours");
const change = (val) => {
  rainfallDuration.value = calculateHoursDifference(val);
const handleTimeChange = (val) => {
  formData.duration = calculateHoursDifference(val);
};
const resetForm = () => {
  formData.name = "方案名称";
  formData.rainfall = 50;
  formData.intensity = 70;
  formData.hours = null;
  formData.duration = null;
};
// æ‰“开保存方案对话框
const openSaveDialog = async () => {
  const forms = {
    name: schemeName.value,
    geom: props.selectedArea,
    type: 3,
    rainfall: totalRainfall.value,
    intensity: rainfallIntensity.value,
    duration: rainfallDuration.value,
    history: rainfallHistory.value,
  };
  await simStore.addSimCheme(forms);
  EventBus.emit("close-selectArea");
};
// å…³é—­ä¿å­˜æ–¹æ¡ˆå¯¹è¯æ¡†
const handleClose = () => {
  saveDialogVisible.value = false;
};
async function openSaveDialog() {
  try {
    formData.geom = props.selectedArea;
    await simStore.addSimCheme(formData);
    resetForm();
    EventBus.emit("close-selectArea");
    ElMessage.success("方案保存成功");
  } catch (error) {
    // ElMessage.error("方案保存失败");
    console.log(error);
  }
}
// å¼€å§‹æ¨¡æ‹Ÿ
function startPlay() {
  console.log("开始模拟按钮被点击");
  console.log("当前选中的区域:", props.selectedArea);
  initeWaterPrimitiveView();
  startSimulate();
async function startPlay() {
  try {
    formData.geom = props.selectedArea;
    await simStore.addSimCheme(formData);
    EventBus.emit("close-selectArea");
    initeWaterPrimitiveView();
    startSimulate();
    ElMessage.success("模拟开始");
  } catch (error) {
    ElMessage.error("模拟启动失败");
  }
}
</script>
<style scoped>
.history-simulation {
  margin-bottom: 20px;
}
.details {
  margin-top: 10px;
  transition: height 0.3s ease, opacity 0.3s ease;
  overflow: hidden;
}
.hidden {
  height: 0;
  opacity: 0;
  padding: 10px 10px 0px 0px;
  box-sizing: border-box;
}
.buttons {
  margin-top: 20px;
  display: flex;
  gap: 10px;
  justify-content: space-evenly;
}
.input-group {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.input-item {
  display: flex;
  align-items: center;
}
label {
  text-align: left;
  white-space: nowrap;
  margin-right: 10px;
}
.el-input {
  flex: 4;
}
span {
  flex: 1;
  text-align: left;
}
.el-select {
  flex: 4;
  text-align: left;
  margin-bottom: 10px;
  justify-content: flex-end;
  padding-right: 10px;
  box-sizing: border-box;
}
.clickable-text {
@@ -273,9 +209,24 @@
  color: #61f7d4;
}
/* è‡ªå®šä¹‰ Dialog çš„ z-index */
.custom-dialog {
  z-index: 5000 !important;
  /* ç¡®ä¿å¯¹è¯æ¡†è¦†ç›–其他元素 */
:deep(.el-form-item__content) {
  display: flex;
  align-items: center;
}
:deep(.el-input) {
  flex: 1;
}
:deep(.el-select) {
  width: 100%;
}
:deep(.el-input-group__append),
:deep(.el-input-group__prepend) {
  background-color: #084b42;
  color: #fff;
}
:deep(.el-form-item__label) {
  color: #61f7d4 !important;
}
</style>
src/views/left/KGSimOption/PredictiveSimulation.vue
@@ -43,7 +43,7 @@
      </el-form-item>
      <el-form-item label="方案名称:">
        <el-input
          v-model="forms.schemeName"
          v-model="forms.name"
          type="text"
          placeholder="请输入"
        ></el-input>
@@ -54,11 +54,7 @@
        >
      </el-form-item>
      <el-form-item label="降雨强度:">
        <el-input
          v-model="forms.rainfallIntensity"
          type="number"
          placeholder="请输入"
        >
        <el-input v-model="forms.intensity" type="number" placeholder="请输入">
          <template #append>mm/h</template></el-input
        >
      </el-form-item>
@@ -114,23 +110,12 @@
import { getRainfallData } from "@/api/hpApi";
import { EventBus } from "@/eventBus"; // å¼•入事件总线
onMounted(() => {
  getRain();
});
// èŽ·å– Store å®žä¾‹
const simStore = SimAPIStore();
// æ³¨å…¥æ¨¡æ‹Ÿæ“ä½œæ–¹æ³•
const { startSimulate, endSimulate } = inject("simulateActions");
function startPlay() {
  console.log("开始模拟按钮被点击");
  console.log("当前选中的区域:", props.selectedArea);
  initeWaterPrimitiveView();
  startSimulate();
}
// å®šä¹‰ Props
const props = defineProps({
@@ -162,13 +147,15 @@
const forms = reactive({
  rainfall: 50,
  schemeName: "",
  rainfallIntensity: 70,
  name: "",
  intensity: 70,
  duration: null,
  prediction: "降雨场次",
  mode: "正态分布",
  showRainFall: "",
  hours: null,
  type: 1,
  geom: null, // å…ˆè®¾ç½®ä¸º null æˆ–者其他默认值,
});
// é™é›¨åœºæ¬¡é€‰æ‹©
@@ -192,23 +179,42 @@
  forms.duration = calculateHoursDifference(val);
};
const resetForm = () => {
  forms.rainfall = 50;
  forms.name = "";
  forms.intensity = 70;
  forms.duration = null;
  forms.prediction = "降雨场次";
  forms.mode = "正态分布";
  forms.showRainFall = "";
  forms.hours = null;
  forms.type = 1;
  forms.geom = null; // å…ˆè®¾ç½®ä¸º null æˆ–者其他默认值,
};
// æ‰“开保存方案对话框
const openSaveDialog = async () => {
  const params = {
    name: forms.schemeName,
    geom: props.selectedArea,
    type: 1,
    rainfall: forms.rainfall,
    duration: forms.duration,
    intensity: forms.rainfallIntensity,
    mode: forms.mode,
    prediction: forms.prediction,
  };
  await simStore.addSimCheme(params);
  EventBus.emit("close-selectArea");
  // åœ¨ setup å†…部更新 geom çš„值
  try {
    forms.geom = props.selectedArea;
    await simStore.addSimCheme(forms);
    resetForm();
    EventBus.emit("close-selectArea");
  } catch (err) {}
};
// æ³¨å…¥æ¨¡æ‹Ÿæ“ä½œæ–¹æ³•
const { startSimulate, endSimulate } = inject("simulateActions");
// å¼€å§‹æ¨¡æ‹Ÿ
async function startPlay() {
  forms.geom = props.selectedArea;
  await simStore.addSimCheme(forms);
  EventBus.emit("close-selectArea");
  initeWaterPrimitiveView();
  startSimulate();
}
// æ‰“开方案
const openPlan = () => {
  // getRain();
src/views/left/KGSimOption/RealTimeSimulation copy.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,341 @@
<template>
  <div class="real-time-simulation">
    <div class="left-top">
      <span>实时模拟</span>
      <span class="clickable-text" @click="toggleDetails">{{
        isCollapsed ? "展开" : "收起"
      }}</span>
    </div>
    <div class="details" :class="{ hidden: isCollapsed }">
      <div class="input-group">
        <div class="input-item">
          <label>方案名称:</label>
          <el-input
            v-model="schemeName"
            type="text"
            placeholder="请输入"
          ></el-input>
        </div>
        <div class="input-item">
          <label>雨量数据:</label>
          <el-select
            v-model="selectedRainfall"
            placeholder="请选择"
            popper-class="mySelectStyle"
          >
            <el-option
              v-for="item in rainfallData"
              :key="item.id"
              :label="item.name"
              :value="item.id"
            ></el-option>
          </el-select>
        </div>
      </div>
      <div class="table-container">
        <div
          class="table-row"
          v-for="(item, index) in filteredTableData"
          :key="index"
        >
          <input type="checkbox" v-model="item.selected" />
          <span>{{ item.name }}</span>
        </div>
      </div>
      <!-- <div style="margin-top: 10px;">
        <label>仿真参数:</label>
        <div style="width: 100%; height: 60px; background-color: #fff;"></div>
      </div> -->
    </div>
    <div class="buttons">
      <el-button type="primary" @click="openSaveDialog">保存方案</el-button>
      <el-button type="success" @click="startPlay">开始模拟</el-button>
      <el-button type="success" @click="futurePredictions">未来预测</el-button>
    </div>
  </div>
</template>
<script setup>
import { ref, watch, defineProps, computed, inject } from "vue";
import { ElMessage } from "element-plus";
import { initeWaterPrimitiveView } from "@/utils/water";
import { SimAPIStore } from "@/store/simAPI";
import { EventBus } from "@/eventBus"; // å¼•入事件总线
// èŽ·å– Store å®žä¾‹
const simStore = SimAPIStore();
// èŽ·å– Store å®žä¾‹
const schemeName = ref("方案名称"); // æ–¹æ¡ˆåç§°
// å·¥å…·å‡½æ•°ï¼šæ·±æ‹·è´å¹¶è®¾ç½®é»˜è®¤é€‰ä¸­çŠ¶æ€
function deepCloneAndSetSelected(data) {
  const newData = {};
  for (const key in data) {
    newData[key] = data[key].map((item) => ({ ...item, selected: true }));
  }
  return newData;
}
// æŽ¥æ”¶çˆ¶ç»„件传递的 props
const props = defineProps({
  selectedArea: {
    type: Object,
    required: true,
  },
});
// å­ç»„件内部状态
const rainfallData = ref([]); // é›¨é‡æ•°æ®
const tableData = ref({}); // è¡¨æ ¼æ•°æ®ï¼ˆæŒ‰é›¨é‡æ•°æ®åˆ†ç»„)
const selectedRainfall = ref(""); // å½“前选中的雨量数据
const isCollapsed = ref(false); // æŽ§åˆ¶å±•å¼€/收起状态
const saveDialogVisible = ref(false); // æŽ§åˆ¶ä¿å­˜æ–¹æ¡ˆå¯¹è¯æ¡†æ˜¾ç¤ºçŠ¶æ€
// æ¨¡æ‹Ÿä¸åŒåŒºåŸŸçš„æ•°æ®
const areaDataMap = {
  å­™èƒ¡æ²Ÿ: {
    rainfallData: [
      { id: "1", name: "气象实时数据 - å­™èƒ¡æ²Ÿ" },
      { id: "2", name: "雨量计实时数据 - å­™èƒ¡æ²Ÿ" },
    ],
    tableData: {
      1: [
        { id: "001", name: "孙胡沟气象站001" },
        { id: "002", name: "孙胡沟气象站002" },
      ],
      2: [
        { id: "003", name: "孙胡沟雨量计003" },
        { id: "004", name: "孙胡沟雨量计004" },
      ],
    },
  },
  é±¼æ°´æ´žåŽæ²Ÿ: {
    rainfallData: [
      { id: "1", name: "气象实时数据 - é±¼æ°´æ´žåŽæ²Ÿ" },
      { id: "2", name: "雨量计实时数据 - é±¼æ°´æ´žåŽæ²Ÿ" },
    ],
    tableData: {
      1: [
        { id: "005", name: "鱼水洞后沟气象站005" },
        { id: "006", name: "鱼水洞后沟气象站006" },
      ],
      2: [
        { id: "007", name: "鱼水洞后沟雨量计007" },
        { id: "008", name: "鱼水洞后沟雨量计008" },
      ],
    },
  },
  äºŽå®¶è¥¿æ²Ÿ: {
    rainfallData: [
      { id: "1", name: "气象实时数据 - äºŽå®¶è¥¿æ²Ÿ" },
      { id: "2", name: "雨量计实时数据 - äºŽå®¶è¥¿æ²Ÿ" },
    ],
    tableData: {
      1: [
        { id: "009", name: "于家西沟气象站009" },
        { id: "010", name: "于家西沟气象站010" },
      ],
      2: [
        { id: "011", name: "于家西沟雨量计011" },
        { id: "012", name: "于家西沟雨量计012" },
      ],
    },
  },
};
// æ ¹æ® selectedArea æ›´æ–°æ•°æ®
watch(
  () => props.selectedArea,
  (newArea) => {
    if (areaDataMap[newArea]) {
      rainfallData.value = areaDataMap[newArea].rainfallData;
      // ä½¿ç”¨æ·±æ‹·è´è®¾ç½®é»˜è®¤é€‰ä¸­çŠ¶æ€
      tableData.value = deepCloneAndSetSelected(areaDataMap[newArea].tableData);
      selectedRainfall.value = ""; // æ¸…空选中的雨量数据
    } else {
      rainfallData.value = [];
      tableData.value = {};
      selectedRainfall.value = "";
    }
  },
  { immediate: true } // ç«‹å³æ‰§è¡Œä¸€æ¬¡ï¼Œç¡®ä¿åˆå§‹æ•°æ®æ­£ç¡®
);
// åŠ¨æ€è®¡ç®—è¡¨æ ¼æ•°æ®
const filteredTableData = computed(() => {
  return selectedRainfall.value && tableData.value[selectedRainfall.value]
    ? tableData.value[selectedRainfall.value]
    : [];
});
// èŽ·å–å½“å‰é€‰ä¸­çš„é›¨é‡æ•°æ®åç§°
const selectedRainfallName = computed(() => {
  const selected = rainfallData.value.find(
    (item) => item.id === selectedRainfall.value
  );
  return selected ? selected.name : "";
});
// èŽ·å–å½“å‰é€‰ä¸­çš„è®¾å¤‡ä¿¡æ¯
const selectedDevices = computed(() => {
  return filteredTableData.value.filter((item) => item.selected);
});
// å°è£…构造表单的方法
const buildSchemeForm = () => {
  return {
    name: schemeName.value,
    geom: props.selectedArea,
    type: 2,
    gauges: [
      {
        id: "xxxxxxxxxxxxxx",
        name: "雨量计1",
        x: 119.0,
        y: 28.0,
        r: 10000,
      },
      {
        id: "xxxxxxxxxxxxxx",
        name: "雨量计2",
        x: 119.0,
        y: 28.0,
        r: 10000,
      },
    ],
  };
};
// æ‰“开保存方案对话框
const openSaveDialog = async () => {
  await simStore.addSimCheme(buildSchemeForm());
  EventBus.emit("close-selectArea");
};
// å…³é—­ä¿å­˜æ–¹æ¡ˆå¯¹è¯æ¡†
const handleClose = () => {
  saveDialogVisible.value = false;
};
// æ³¨å…¥æ¨¡æ‹Ÿæ“ä½œæ–¹æ³•
const { startSimulate, endSimulate } = inject("simulateActions");
async function startPlay() {
  const selectedItems = filteredTableData.value.filter((item) => item.selected);
  if (selectedItems.length > 0) {
    console.log(
      "选中的项:",
      selectedItems.map((item) => item.name)
    );
  } else {
    console.log("未选中任何项");
  }
  console.log("当前选中的区域:", props.selectedArea);
  console.log("当前选中的雨量数据:", selectedRainfall.value);
  // å¼€å§‹æ¨¡æ‹Ÿå‰éœ€è¦å…ˆä¿å­˜æ–¹æ¡ˆ
  await simStore.addSimCheme(buildSchemeForm());
  EventBus.emit("close-selectArea");
  initeWaterPrimitiveView();
  startSimulate();
}
const toggleDetails = () => {
  isCollapsed.value = !isCollapsed.value;
};
const futurePredictions = () => {
  console.log("未来预测按钮被点击");
};
</script>
<style scoped>
.custom-dialog {
  z-index: 3000 !important;
}
.real-time-simulation {
  margin-bottom: 20px;
}
.clickable-text {
  margin-left: 160px;
  cursor: pointer;
  font-size: 14px;
  color: #61f7d4;
}
.details {
  margin-top: 10px;
  transition: height 0.3s ease, opacity 0.3s ease;
  overflow: hidden;
}
.hidden {
  height: 0;
  opacity: 0;
}
.input-group {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-right: 10px;
  box-sizing: border-box;
}
.input-item {
  display: flex;
  align-items: center;
}
label {
  text-align: left;
  white-space: nowrap;
  margin-right: 10px;
}
.el-select {
  flex: 4;
  text-align: left;
  margin-bottom: 10px;
}
.table-container {
  font-size: 12px;
  height: 120px;
  overflow-y: auto;
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 5px;
}
.table-row {
  display: flex;
  justify-content: space-between;
  padding: 5px 0;
  border-bottom: 1px solid #ddd;
}
.table-row:last-child {
  border-bottom: none;
}
.table-row span {
  flex: 1;
  text-align: left;
}
.table-row input[type="checkbox"] {
  margin-right: 10px;
}
.buttons {
  margin-top: 20px;
  display: flex;
  gap: 10px;
}
.el-button {
  flex: 1;
}
</style>
src/views/left/KGSimOption/RealTimeSimulation.vue
@@ -6,82 +6,65 @@
        isCollapsed ? "展开" : "收起"
      }}</span>
    </div>
    <div class="details" :class="{ hidden: isCollapsed }">
      <div class="input-group">
        <div class="input-item">
          <label>方案名称:</label>
          <el-input
            v-model="schemeName"
            type="text"
            placeholder="请输入"
          ></el-input>
    <el-form
      :model="formData"
      label-width="auto"
      style="max-width: 600px; padding-right: 10px; box-sizing: border-box"
    >
      <el-collapse-transition style="margin-top: 10px">
        <div v-show="!isCollapsed">
          <el-form-item label="方案名称:">
            <el-input
              v-model="formData.name"
              type="text"
              placeholder="请输入"
            ></el-input>
          </el-form-item>
          <el-form-item label="雨量数据:">
            <el-select
              v-model="formData.selectedRainfall"
              placeholder="请选择"
              popper-class="mySelectStyle"
            >
              <el-option
                v-for="item in rainfallData"
                :key="item.id"
                :label="item.name"
                :value="item.id"
              ></el-option>
            </el-select>
          </el-form-item>
          <el-form-item>
            <div class="table-container">
              <div
                class="table-row"
                v-for="(item, index) in filteredTableData"
                :key="index"
              >
                <input type="checkbox" v-model="item.selected" />
                <span>{{ item.name }}</span>
              </div>
            </div>
          </el-form-item>
        </div>
        <div class="input-item">
          <label>雨量数据:</label>
          <el-select
            v-model="selectedRainfall"
            placeholder="请选择"
            popper-class="mySelectStyle"
          >
            <el-option
              v-for="item in rainfallData"
              :key="item.id"
              :label="item.name"
              :value="item.id"
            ></el-option>
          </el-select>
        </div>
      </div>
      <div class="table-container">
        <div
          class="table-row"
          v-for="(item, index) in filteredTableData"
          :key="index"
        >
          <input type="checkbox" v-model="item.selected" />
          <span>{{ item.name }}</span>
        </div>
      </div>
      <!-- <div style="margin-top: 10px;">
      </el-collapse-transition>
    </el-form>
    <!-- <div style="margin-top: 10px;">
        <label>仿真参数:</label>
        <div style="width: 100%; height: 60px; background-color: #fff;"></div>
      </div> -->
    </div>
    <div class="buttons">
      <el-button type="primary" @click="openSaveDialog">保存方案</el-button>
      <el-button type="success" @click="startPlay">开始模拟</el-button>
      <el-button type="success" @click="futurePredictions">未来预测</el-button>
    </div>
    <!-- ä¿å­˜æ–¹æ¡ˆå¯¹è¯æ¡†
    <el-dialog
      v-model="saveDialogVisible"
      title="保存方案"
      width="50%"
      :before-close="handleClose"
       custom-class="custom-dialog"
    >
      <div class="dialog-content">
        <p><strong>所选重点沟:</strong>{{ props.selectedArea }}</p>
        <p><strong>模拟类型:</strong>实时模拟</p>
        <p><strong>雨量数据类型:</strong>{{ selectedRainfallName }}</p>
        <p><strong>设备信息:</strong></p>
        <ul>
          <li v-for="item in selectedDevices" :key="item.id">{{ item.name }}</li>
        </ul>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="saveDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="confirmSave">确定保存</el-button>
        </span>
      </template>
    </el-dialog> -->
  </div>
</template>
<script setup>
import { ref, watch, defineProps, computed, inject } from "vue";
import { ref, watch, defineProps, computed, inject, reactive } from "vue";
import { ElMessage } from "element-plus";
import { initeWaterPrimitiveView } from "@/utils/water";
import { SimAPIStore } from "@/store/simAPI";
@@ -90,27 +73,28 @@
// èŽ·å– Store å®žä¾‹
const simStore = SimAPIStore();
// èŽ·å– Store å®žä¾‹
const schemeName = ref("方案名称"); // æ–¹æ¡ˆåç§°
// æ³¨å…¥æ¨¡æ‹Ÿæ“ä½œæ–¹æ³•
const { startSimulate, endSimulate } = inject("simulateActions");
function startPlay() {
  const selectedItems = filteredTableData.value.filter((item) => item.selected);
  if (selectedItems.length > 0) {
    console.log(
      "选中的项:",
      selectedItems.map((item) => item.name)
    );
  } else {
    console.log("未选中任何项");
  }
  console.log("当前选中的区域:", props.selectedArea);
  console.log("当前选中的雨量数据:", selectedRainfall.value);
  initeWaterPrimitiveView();
  startSimulate();
}
// è¡¨å•数据
const formData = reactive({
  name: "方案名称",
  selectedRainfall: "",
  type: 2,
  gauges: [
    {
      id: "xxxxxxxxxxxxxx",
      name: "雨量计1",
      x: 119.0,
      y: 28.0,
      r: 10000,
    },
    {
      id: "xxxxxxxxxxxxxx",
      name: "雨量计2",
      x: 119.0,
      y: 28.0,
      r: 10000,
    },
  ],
});
// å·¥å…·å‡½æ•°ï¼šæ·±æ‹·è´å¹¶è®¾ç½®é»˜è®¤é€‰ä¸­çŠ¶æ€
function deepCloneAndSetSelected(data) {
@@ -128,13 +112,9 @@
    required: true,
  },
});
// å­ç»„件内部状态
const rainfallData = ref([]); // é›¨é‡æ•°æ®
const rainfallData = ref([]);
const tableData = ref({}); // è¡¨æ ¼æ•°æ®ï¼ˆæŒ‰é›¨é‡æ•°æ®åˆ†ç»„)
const selectedRainfall = ref(""); // å½“前选中的雨量数据
const isCollapsed = ref(false); // æŽ§åˆ¶å±•å¼€/收起状态
const saveDialogVisible = ref(false); // æŽ§åˆ¶ä¿å­˜æ–¹æ¡ˆå¯¹è¯æ¡†æ˜¾ç¤ºçŠ¶æ€
// æ¨¡æ‹Ÿä¸åŒåŒºåŸŸçš„æ•°æ®
const areaDataMap = {
@@ -192,31 +172,34 @@
watch(
  () => props.selectedArea,
  (newArea) => {
    if (areaDataMap[newArea]) {
      rainfallData.value = areaDataMap[newArea].rainfallData;
    console.log(newArea.label, "new");
    if (areaDataMap[newArea.label]) {
      rainfallData.value = areaDataMap[newArea.label].rainfallData;
      // ä½¿ç”¨æ·±æ‹·è´è®¾ç½®é»˜è®¤é€‰ä¸­çŠ¶æ€
      tableData.value = deepCloneAndSetSelected(areaDataMap[newArea].tableData);
      selectedRainfall.value = ""; // æ¸…空选中的雨量数据
      tableData.value = deepCloneAndSetSelected(
        areaDataMap[newArea.label].tableData
      );
      formData.selectedRainfall = ""; // æ¸…空选中的雨量数据
    } else {
      rainfallData.value = [];
      tableData.value = {};
      selectedRainfall.value = "";
      formData.selectedRainfall = "";
    }
  },
  { immediate: true } // ç«‹å³æ‰§è¡Œä¸€æ¬¡ï¼Œç¡®ä¿åˆå§‹æ•°æ®æ­£ç¡®
  }
  // { immediate: true } // ç«‹å³æ‰§è¡Œä¸€æ¬¡ï¼Œç¡®ä¿åˆå§‹æ•°æ®æ­£ç¡®
);
// åŠ¨æ€è®¡ç®—è¡¨æ ¼æ•°æ®
const filteredTableData = computed(() => {
  return selectedRainfall.value && tableData.value[selectedRainfall.value]
    ? tableData.value[selectedRainfall.value]
  return formData.selectedRainfall && tableData.value[formData.selectedRainfall]
    ? tableData.value[formData.selectedRainfall]
    : [];
});
// èŽ·å–å½“å‰é€‰ä¸­çš„é›¨é‡æ•°æ®åç§°
const selectedRainfallName = computed(() => {
  const selected = rainfallData.value.find(
    (item) => item.id === selectedRainfall.value
    (item) => item.id === formData.selectedRainfall
  );
  return selected ? selected.name : "";
});
@@ -226,61 +209,42 @@
  return filteredTableData.value.filter((item) => item.selected);
});
const resetForm = () => {
  formData.name = "";
  formData.selectedRainfall = "";
};
// æ‰“开保存方案对话框
const openSaveDialog = async () => {
  const forms = {
    name: schemeName.value,
    geom: props.selectedArea,
    type: 2,
    gauges: [
      {
        id: "xxxxxxxxxxxxxx",
        name: "雨量计1",
        x: 119.0,
        y: 28.0,
        r: 10000,
      },
      {
        id: "xxxxxxxxxxxxxx",
        name: "雨量计2",
        x: 119.0,
        y: 28.0,
        r: 10000,
      },
    ],
  };
  await simStore.addSimCheme(forms);
  try {
    formData.geom = props.selectedArea;
    await simStore.addSimCheme(formData);
    resetForm();
    EventBus.emit("close-selectArea");
  } catch (err) {}
};
// æ³¨å…¥æ¨¡æ‹Ÿæ“ä½œæ–¹æ³•
const { startSimulate, endSimulate } = inject("simulateActions");
async function startPlay() {
  const selectedItems = filteredTableData.value.filter((item) => item.selected);
  if (selectedItems.length > 0) {
    console.log(
      "选中的项:",
      selectedItems.map((item) => item.name)
    );
  } else {
    console.log("未选中任何项");
  }
  console.log("当前选中的区域:", props.selectedArea);
  // å¼€å§‹æ¨¡æ‹Ÿå‰éœ€è¦å…ˆä¿å­˜æ–¹æ¡ˆ
  formData.geom = props.selectedArea;
  await simStore.addSimCheme(formData);
  EventBus.emit("close-selectArea");
};
// å…³é—­ä¿å­˜æ–¹æ¡ˆå¯¹è¯æ¡†
const handleClose = () => {
  saveDialogVisible.value = false;
};
// // ç¡®è®¤ä¿å­˜
// const confirmSave = () => {
//   // æž„造新的方案对象
//   const newScheme = {
//     name: schemeName.value,//方案名称
//     id: Date.now().toString(), // å”¯ä¸€ ID
//     area: props.selectedArea, // åŒºåŸŸ
//     name: selectedRainfallName.value, // æ–¹æ¡ˆåç§°ï¼ˆé›¨é‡æ•°æ®ç±»åž‹ï¼‰
//     createTime: new Date().toISOString(), // åˆ›å»ºæ—¶é—´
//     taskStatus:1, // åˆå§‹çŠ¶æ€ä¸ºæœªå¼€å§‹
//     rainfallType: selectedRainfallName.value, // é›¨é‡æ•°æ®ç±»åž‹
//     devices: selectedDevices.value.map((item) => item.name), // è®¾å¤‡ä¿¡æ¯
//   };
//   // è°ƒç”¨ Store çš„æ–¹æ³•添加方案
//   simStore.addSchemCard(newScheme);
//   console.log("保存方案成功", newScheme);
//   ElMessage.success("方案已保存");
//   // å…³é—­å¯¹è¯æ¡†
//   saveDialogVisible.value = false;
// };
  initeWaterPrimitiveView();
  startSimulate();
}
const toggleDetails = () => {
  isCollapsed.value = !isCollapsed.value;
@@ -321,6 +285,8 @@
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-right: 10px;
  box-sizing: border-box;
}
.input-item {
@@ -343,6 +309,7 @@
.table-container {
  font-size: 12px;
  height: 120px;
  width: 90%;
  overflow-y: auto;
  border: 1px solid #ddd;
  border-radius: 4px;
@@ -372,7 +339,10 @@
.buttons {
  margin-top: 20px;
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding-right: 10px;
  box-sizing: border-box;
}
.el-button {
src/views/mnfz.vue
@@ -299,7 +299,7 @@
    url: "/json/line.json",
    color: "#00FF00",
    width: 12.0,
    pointColor: "#73FFA7",
    pointColor: "#FFFF73",
    speed: 2,
    far: 50000,
  });
vue.config.js
@@ -30,7 +30,7 @@
        secure: false
      },
      '/hp': {
        target: 'http://192.168.56.106:9511',
        target: 'http://192.168.56.106:9522',
        changeOrigin: true,
        pathRewrite: {
          '^/hp': ''