4b509ebb4ab4b83790a9cd8e9b806b2e84186d06..f37845dd0a787dd42bf6c72e923433f30fcd8cc3
2025-07-03 guonan
实时模拟
f37845 对比 | 目录
2025-07-03 guonan
1
58926a 对比 | 目录
2025-07-03 guonan
Merge branch 'master' of http://103.135.160.14:9034/r/NslWeb
e38b91 对比 | 目录
2025-07-03 guonan
历史回放
ca7405 对比 | 目录
2025-07-03 wangjuncheng
2
9ca762 对比 | 目录
2025-07-03 guonan
提交
fe4106 对比 | 目录
已修改8个文件
已删除1个文件
2048 ■■■■ 文件已修改
public/CimSDK/Workers/Model/ModelLibrary.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/json/rainfall.json 1507 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/trApi.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/menu/TimeLine.vue 238 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monifangzhen/schemeCard.vue 86 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/simulation.js 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/left/CitySim.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/left/KGSimOption/RealTimeSimulation.vue 157 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/CimSDK/Workers/Model/ModelLibrary.html
@@ -148,7 +148,7 @@
        var Viewer = parent.earthCtrl.viewer;
        var sgworld = parent.earthCtrl;
        var Cesium = parent.Cesium;
        var ModelLibraryURL = 'http://192.168.56.107:8088/ModelLibrary/'
        var ModelLibraryURL = 'http://192.168.37.60:8088/ModelLibrary/'
        var imgRootURL = ModelLibraryURL + 'image/';
        //获取数据
public/json/rainfall.json
ÎļþÒÑɾ³ý
src/api/trApi.js
@@ -81,6 +81,28 @@
    throw error;
  }
}
// å®žæ—¶æ¨¡æ‹Ÿçš„结果
export async function getSimresult(ids) {
  try {
    const res = await instance.get(`/simu/results?id=${ids}`);
    return res.data;
  } catch (error) {
    console.error("Error deleting simulation data:", error);
    throw error;
  }
}
// ç»“束实时模拟
export async function stopSim(ids) {
  try {
    const res = await instance.get(`/simu/stop?id=${ids}`);
    return res.data;
  } catch (error) {
    console.error("Error deleting simulation data:", error);
    throw error;
  }
}
// **************************************************************************************************************
// è§£æžjson获取泥石流参数
export function parseWaterSimulationData(jsonData) {
src/components/menu/TimeLine.vue
@@ -2,20 +2,30 @@
  <div class="timeline-container">
    <div class="controls">
      <div class="control-btn" @click="skipBackward">
        <img src="@/assets/img/timeline/left.png" class="fas fa-step-backward" />
        <img
          src="@/assets/img/timeline/left.png"
          class="fas fa-step-backward"
        />
      </div>
      <div class="control-btn play-btn" @click="togglePlay">
        <img v-show="isPlaying" src="@/assets/img/timeline/stop.png" />
        <img v-show="!isPlaying" src="@/assets/img/timeline/start.png" />
      </div>
      <div class="control-btn" @click="skipForward">
        <img src="@/assets/img/timeline/right.png" class="fas fa-step-forward" />
        <img
          src="@/assets/img/timeline/right.png"
          class="fas fa-step-forward"
        />
      </div>
      <div class="speed-control" v-show="speedShow">
        <div @click="toggleSpeedMenu">{{ playbackRate }}X</div>
        <div class="speed-menu" v-show="showSpeedMenu">
          <div v-for="rate in playbackRates" :key="rate" @click.capture="setPlaybackRate(rate)"
            :class="{ active: playbackRate === rate }">
          <div
            v-for="rate in playbackRates"
            :key="rate"
            @click.capture="setPlaybackRate(rate)"
            :class="{ active: playbackRate === rate }"
          >
            {{ rate }}X
          </div>
        </div>
@@ -25,19 +35,33 @@
    <div class="timeline">
      <div class="dates">
        <div class="current-date">当前播放时间:{{ currentPlayingTime }}</div>
        <div v-for="(date, index) in visibleDates" :key="index" class="date-label">
        <div
          v-for="(date, index) in visibleDates"
          :key="index"
          class="date-label"
        >
          <!-- {{ formatDate(date) }} -->
        </div>
        <div>
          ä¸“题渲染:
          <el-switch v-model="isColorRenderEnabled" @change="handleColorRenderChange" style="margin-top: -3px"
            :disabled="!isPlaying || !isWaterPrimitiveCreated" />
          <el-switch
            v-model="isColorRenderEnabled"
            @change="handleColorRenderChange"
            style="margin-top: -3px"
            :disabled="!isPlaying || !isWaterPrimitiveCreated"
          />
          <!-- active-text="开" inactive-text="关" -->
        </div>
      </div>
      <div class="timeline-track" ref="timelineTrack" @click="seekToPosition">
        <div class="timeline-progress" :style="{ width: progressPercentage + '%' }"></div>
        <div class="timeline-cursor" :style="{ left: progressPercentage + '%' }"></div>
        <div
          class="timeline-progress"
          :style="{ width: progressPercentage + '%' }"
        ></div>
        <div
          class="timeline-cursor"
          :style="{ left: progressPercentage + '%' }"
        ></div>
        <div class="scale-markers">
          <div class="scale-marker" style="left: 0%"></div>
          <div class="scale-marker" style="left: 25%"></div>
@@ -46,8 +70,12 @@
          <div class="scale-marker" style="left: 100%"></div>
        </div>
        <div class="time-markers">
          <div v-for="(time, index) in timeMarkers" :key="index" class="time-marker"
            :style="{ left: `${index * 25}%`, transform: 'translateX(-50%)' }">
          <div
            v-for="(time, index) in timeMarkers"
            :key="index"
            class="time-marker"
            :style="{ left: `${index * 25}%`, transform: 'translateX(-50%)' }"
          >
            <div class="date-part">{{ time.split(" ")[0] }}</div>
            <div class="time-part">{{ time.split(" ")[1] }}</div>
          </div>
@@ -56,27 +84,38 @@
    </div>
    <div>
      <div style="display: flex">
        <ratelevel ref="ratelevelRef" :playing-time="sendCurrentPlayingTime"
          @finish-calculation="handleFinishCalculation" style="
        <ratelevel
          ref="ratelevelRef"
          :playing-time="sendCurrentPlayingTime"
          @finish-calculation="handleFinishCalculation"
          style="
            margin-top: 12px;
            margin-left: 28px;
            margin-right: 10px;
            justify-content: flex-end;
          " />
        <crossanalysis ref="crossRef" style="
          "
        />
        <crossanalysis
          ref="crossRef"
          style="
            margin-top: 12px;
            margin-left: 16px;
            margin-right: 20px;
            justify-content: flex-end;
          " />
          "
        />
      </div>
      <el-button @click="handleBack" style="
      <el-button
        @click="handleBack"
        style="
          margin-top: 3px;
          margin-left: 28px;
          margin-right: 10px;
          width: 75%;
          height: 30%;
        ">结束模拟</el-button>
        "
        >结束模拟</el-button
      >
    </div>
  </div>
</template>
@@ -107,9 +146,9 @@
  updateWaterColor,
} from "@/utils/water";
import mapUtils from "@/utils/tools.js";
import { fetchWaterSimulationData } from "@/api/trApi.js";
import { fetchWaterSimulationData, stopSim } from "@/api/trApi.js";
import { EventBus } from "@/eventBus";
import { ElMessage } from "element-plus";
import { ElMessage, ElMessageBox } from "element-plus";
// çŠ¶æ€ç®¡ç†å™¨
import { useSimStore } from "@/store/simulation";
import { storeToRefs } from "pinia";
@@ -278,7 +317,8 @@
const startPlayback = () => {
  clearInterval(playInterval);
  if (selectedScheme.value.type === 2) {
  // æ–°å»ºæ–¹æ¡ˆä¸­çš„实时模拟不能倍速
  if (selectedScheme.value.type === 2 && simStore.rePlayList.length == 0) {
    // ç±»åž‹ä¸º 2:每 5 ç§’跳动一次
    playInterval = setInterval(() => {
      const fiveSeconds = 5;
@@ -541,9 +581,9 @@
// å…¨å±€çŠ¶æ€è®°å½•
const colorState = {
  currentColor: "#F5F0E6", // å½“前颜色
  currentAlpha: -0.3,      // å½“前透明度
  colorStages: null,       // é¢„计算的颜色阶段时间点
  maxColorTime: null       // è®°å½•达到最深颜色时的时间点
  currentAlpha: -0.3, // å½“前透明度
  colorStages: null, // é¢„计算的颜色阶段时间点
  maxColorTime: null, // è®°å½•达到最深颜色时的时间点
};
// é¢„计算颜色阶段时间点
@@ -556,17 +596,17 @@
    { hex: "#D4B483", luminance: 184.0 }, // stage 2
    { hex: "#B78B6A", luminance: 148.4 }, // stage 3
    { hex: "#8B5A3A", luminance: 101.0 }, // stage 4
    { hex: "#744C33", luminance: 84.5 },  // stage 5
    { hex: "#5D3D2C", luminance: 68.1 }   // stage 6
    { hex: "#744C33", luminance: 84.5 }, // stage 5
    { hex: "#5D3D2C", luminance: 68.1 }, // stage 6
  ];
  const alphaStops = [
    -0.2,   // stage 0
    -0.3,   // stage 1
    -0.4,   // stage 2
    -0.5,   // stage 3
    -0.6,   // stage 4
    -0.7,   // stage 5
    -0.8    // stage 6
    -0.2, // stage 0
    -0.3, // stage 1
    -0.4, // stage 2
    -0.5, // stage 3
    -0.6, // stage 4
    -0.7, // stage 5
    -0.8, // stage 6
  ];
  // ç´¯è®¡é™é›¨é‡é˜ˆå€¼ï¼ˆmm)
  const R_THRESHOLDS = [0, 200, 240, 280, 310, 350]; // å…±6个阶段对应6个阈值
@@ -579,7 +619,7 @@
    const total = rainTotalInfo.value[i].total; // ä½¿ç”¨ total æ›¿ä»£ intensity
    timeTotals.push({
      time,
      total
      total,
    });
  }
@@ -594,7 +634,7 @@
          startTime: time,
          color: COLOR_STOPS[stage].hex,
          alpha: alphaStops[stage],
          threshold
          threshold,
        };
        break;
      }
@@ -606,7 +646,7 @@
    startTime: 0,
    color: COLOR_STOPS[0].hex,
    alpha: alphaStops[0],
    threshold: 0
    threshold: 0,
  };
  colorState.colorStages = stages;
@@ -623,7 +663,10 @@
  // æŸ¥æ‰¾å½“前时间点所属的阶段
  let currentStage = 0;
  for (let i = colorState.colorStages.length - 1; i >= 0; i--) {
    if (colorState.colorStages[i] && currentTime.value >= colorState.colorStages[i].startTime) {
    if (
      colorState.colorStages[i] &&
      currentTime.value >= colorState.colorStages[i].startTime
    ) {
      currentStage = i;
      break;
    }
@@ -631,15 +674,21 @@
  // è®°å½•达到最深颜色的时间点
  if (currentStage >= colorState.colorStages.length - 1) {
    if (colorState.maxColorTime === null || currentTime.value > colorState.maxColorTime) {
    if (
      colorState.maxColorTime === null ||
      currentTime.value > colorState.maxColorTime
    ) {
      colorState.maxColorTime = currentTime.value;
    }
  }
  // åˆ¤æ–­æ˜¯å¦éœ€è¦å¼ºåˆ¶æ›´æ–°é¢œè‰²
  const isTimeGoingBackward = currentTime.value < colorState.lastTime;
  const isBeforeMaxColorTime = colorState.maxColorTime !== null && currentTime.value <= colorState.maxColorTime;
  const shouldForceUpdate = isForceUpdate && (isTimeGoingBackward || isBeforeMaxColorTime);
  const isBeforeMaxColorTime =
    colorState.maxColorTime !== null &&
    currentTime.value <= colorState.maxColorTime;
  const shouldForceUpdate =
    isForceUpdate && (isTimeGoingBackward || isBeforeMaxColorTime);
  // æ›´æ–°é¢œè‰²é€»è¾‘
  if (shouldForceUpdate || isTimeGoingBackward) {
@@ -652,7 +701,9 @@
    const newAlpha = colorState.colorStages[currentStage].alpha;
    // åªåº”用更暗的颜色和更低的透明度
    if (calculateLuminance(newColor) < calculateLuminance(colorState.currentColor)) {
    if (
      calculateLuminance(newColor) < calculateLuminance(colorState.currentColor)
    ) {
      colorState.currentColor = newColor;
    }
    if (newAlpha < colorState.currentAlpha) {
@@ -717,7 +768,8 @@
  // åˆ¤æ–­æ˜¯å¦éœ€è¦å¼ºåˆ¶æ›´æ–°é¢œè‰²
  const isGoingBackward = newTime < currentTime.value;
  const isBeforeMaxColor = colorState.maxColorTime !== null && newTime <= colorState.maxColorTime;
  const isBeforeMaxColor =
    colorState.maxColorTime !== null && newTime <= colorState.maxColorTime;
  const shouldForceUpdate = isGoingBackward || isBeforeMaxColor;
  currentTime.value = newTime;
@@ -910,16 +962,25 @@
);
const jsonFetch = ref(null);
const currentReplayIndex = ref(0); // å½“前播放的rePlayList索引
// æå–为独立函数
async function initializeSimulationData(force = false) {
async function initializeSimulationData(replayItem = null) {
  try {
    const schemeInfo = selectedScheme.value;
    serviceInfo = schemeInfo.serviceName;
    if (schemeInfo.type == 2) {
      speedShow.value = false;
      jsonFetch.value = layerDate.value;
      if (
        replayItem ||
        (simStore.rePlayList && simStore.rePlayList.length != 0)
      ) {
        jsonFetch.value =
          replayItem || simStore.rePlayList[currentReplayIndex.value];
        speedShow.value = true;
      } else {
        jsonFetch.value = layerDate.value;
        speedShow.value = false;
      }
    } else {
      getRainfallData();
      speedShow.value = true;
@@ -962,11 +1023,36 @@
  }
}
// æŒ‚载时调用
onMounted(async () => {
  // å› ä¸ºè¿™ä¸ªå‡½æ•°å®žæ—¶æ¨¡æ‹Ÿç›‘听也需要使用,所以封装了一个函数
  await initializeSimulationData();
});
// æ’­æ”¾å®ŒæˆåŽçš„回调
function handlePlayFinished() {
  if (selectedScheme.value.type !== 2) return;
  finishPlay.value = false;
  currentReplayIndex.value++;
  if (currentReplayIndex.value < simStore.rePlayList.length) {
    console.log(currentReplayIndex.value);
    // è‡ªåŠ¨æ’­æ”¾ä¸‹ä¸€ä¸ª
    initializeSimulationData(simStore.rePlayList[currentReplayIndex.value]);
    togglePlay();
    shouldAutoPlay.value = false;
  } else {
    // æ‰€æœ‰é¡¹ç›®æ’­æ”¾å®Œæˆ
    currentReplayIndex.value = 0; // é‡ç½®ç´¢å¼•
    isPlaying.value = false; // åœæ­¢æ’­æ”¾
  }
}
// ç›‘听播放完成事件
watch(
  () => finishPlay.value,
  (newVal) => {
    if (newVal && selectedScheme.value.type === 2) {
      handlePlayFinished();
    }
  }
);
const shouldAutoPlay = ref(false);
// ç›‘听 layerDate å˜åŒ–后标记准备播放
watch(
@@ -986,6 +1072,13 @@
    shouldAutoPlay.value = false;
  }
});
// æŒ‚载时调用
onMounted(async () => {
  // å› ä¸ºè¿™ä¸ªå‡½æ•°å®žæ—¶æ¨¡æ‹Ÿç›‘听也需要使用,所以封装了一个函数
  await initializeSimulationData();
});
// æ ¹æ®è¿”回数据的个数去渲染时间轴
function updateTimelineRange() {
  if (waterTimestamps.value.length > 0) {
@@ -1002,28 +1095,63 @@
});
const { endSimulate } = inject("simulateActions");
function handleBack() {
  endSimulate();
  // åœæ­¢å®žæ—¶æ¨¡æ‹Ÿå®šæ—¶å™¨
  EventBus.emit("close-time");
async function handleBack() {
  // å®žæ—¶æ¨¡æ‹Ÿå¼¹çª—确认是返回方案列表还是停止模拟
  if (selectedScheme.value.type === 2) {
    try {
      await ElMessageBox.confirm("方案未停止时结束模拟后,后台将停止计算", {
        confirmButtonText: "返回列表",
        cancelButtonText: "结束模拟",
        type: "warning",
      });
      // ç”¨æˆ·ç‚¹å‡»äº†ç¡®è®¤ï¼Œè¿™é‡Œä¸æ‰§è¡Œä»»ä½•操作,仅关闭对话框
    } catch (error) {
      stopSim(selectedScheme.value.id).then((res) => {
        if (res.code == 404) {
          ElMessage.warning("该服务已停止");
        } else {
          ElMessage.success("服务正在停止中");
        }
      });
      // return;
    }
  }
  // ä¸ç®¡type是不是2,最终都执行结束模拟的操作
  endSimulation();
}
async function endSimulation() {
  EventBus.emit("close-time");
  endSimulate();
  isWaterPrimitiveCreated.value = false;
  // ç»“束计算和停止拾取
  if (ratelevelRef.value) {
    ratelevelRef.value.endCalculation();
    ratelevelRef.value.stopPicking();
  }
  // æ¸…除点
  if (crossRef.value) {
    crossRef.value.clearPoints();
    console.log("执行删除点功能");
  }
  emit("isColorRender", false);
  // å»¶è¿Ÿåˆ é™¤é›¨é‡å›¾å±‚
  setTimeout(() => {
    mapUtils.delRain();
  }, 3000);
  destoryWaterPrimitive();
  // å‘送事件隐藏相关信息
  EventBus.emit("hide-schemeInfo");
  EventBus.emit("clear-water-depth");
  EventBus.emit("clear-water-velocity");
  ElMessage({ message: "模拟进程正在关闭中...", type: "success" });
}
</script>
src/components/monifangzhen/schemeCard.vue
@@ -29,6 +29,9 @@
            @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>
@@ -50,7 +53,15 @@
<script setup>
import { EventBus } from "@/eventBus"; // å¼•入事件总线
import { onMounted, ref, watch, defineEmits, onUnmounted } from "vue";
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";
@@ -61,11 +72,11 @@
import { ElMessage, ElMessageBox } from "element-plus";
const emit = defineEmits(["start", "end", "reset", "closeBtn"]);
import {
  getRegionData,
  getSimData,
  deleteSimData,
  getSimStart,
  getSimDataById,
  getSimresult,
} from "@/api/trApi.js";
const simStore = useSimStore();
@@ -101,8 +112,9 @@
// å®žæ—¶æ¨¡æ‹Ÿäº”分钟请求一次的定时器
const realTimeSimInterval = ref(null);
const { startSimulate, endSimulate } = inject("simulateActions");
async function startPlay(item) {
  console.log(item, "item");
  if (item.status === 2) {
    ElMessage.warning("当前方案正在分析中,无法进入模拟!");
    return;
@@ -131,7 +143,7 @@
    currentScheme.value = item;
    schemeInfoShow.value = true;
    emit("closeBtn", false);
    emit("start");
    startSimulate();
    return;
  }
@@ -151,47 +163,35 @@
    return;
  }
  // å¤„理 type == 2 çš„æƒ…况(实时模拟)
  if (item.type === 2) {
    // æ¸…除已有定时器,防止重复启动
    if (realTimeSimInterval.value) {
      clearInterval(realTimeSimInterval.value);
    }
    // å³åˆ»æ‰§è¡Œä¸€æ¬¡
    await executeRealTimeSimulation(item);
    // æ¯éš” 5 åˆ†é’Ÿæ‰§è¡Œä¸€æ¬¡
    realTimeSimInterval.value = setInterval(() => {
      executeRealTimeSimulation(item);
    }, 5 * 60 * 1000); // 5分钟
    return;
  }
  // é»˜è®¤æƒ…况:有服务名称
  simStore.setSelectedScheme(item);
}
// å°è£…实时模拟的异步操作
async function executeRealTimeSimulation(item) {
  try {
    const ress = await getSimStart(item.id);
    const res = await getSimDataById(item.id);
    item.serviceName = res.data[0]?.serviceName || null;
    simStore.setSelectedScheme(item);
    getScheme();
    if (ress.code === 200) {
      simStore.layerDate = ress.data;
      initeWaterPrimitiveView();
      emit("start");
    }
  } catch (e) {
    console.error("实时模拟获取模拟数据失败:", e);
  }
// å®žæ—¶æ¨¡æ‹ŸåŽ†å²å›žæ”¾
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, "lisi");
      }
      // ä½¿ç”¨ nextTick ç¡®ä¿ DOM æ›´æ–°åŽå†æ‰§è¡ŒåŽç»­æ“ä½œ
      nextTick(() => {
        initeWaterPrimitiveView();
        startSimulate();
      });
    })
    .catch((error) => {
      console.log("请求失败:", error);
      // é”™è¯¯å¤„理
    });
}
function handleBack(value) {
@@ -221,10 +221,10 @@
        item.result == "创建仿真" ||
        item.result == "完成" ||
        item.result == "-1" ||
        item.result == null
        item.result == "停止" ||
        item.result == "运行中"
    );
    simAPIStore.shouldPoll = !shouldStop; // ä¿®æ”¹ Pinia çŠ¶æ€
    console.log(shouldStop, "aaaaaaaaaaaaaaaa");
    // 3. å¦‚果需要停止
    if (shouldStop) {
      if (intervalId) {
src/store/simulation.js
@@ -2,6 +2,8 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useSimStore = defineStore('simulation', () => {
    // åŽ†å²å›žæ”¾åˆ—è¡¨
    const rePlayList = ref([])
    // åŒ—京市所有村的code
    const townCodeAll = ref([])
    // å®žæ—¶æ¨¡æ‹Ÿæœ€æ–°çš„layer
@@ -47,10 +49,22 @@
    // é™é›¨å•位
    const intensityUnit = ref()
    const setSelectedScheme = (scheme) => {
        selectedScheme.value = scheme
        rainFalls.value = JSON.parse(scheme.data).rainfalls
        intensityUnit.value = JSON.parse(scheme.data).intensityUnit
    }
        selectedScheme.value = scheme;
        try {
            const parsedData = JSON.parse(scheme.data);
            // åªæœ‰å½“ rainfalls å’Œ intensityUnit å­˜åœ¨ä¸”非空时才赋值
            if (parsedData.rainfalls && parsedData.intensityUnit) {
                rainFalls.value = parsedData.rainfalls;
                intensityUnit.value = parsedData.intensityUnit;
            } else {
                console.warn("缺少必要的 rainfalls æˆ– intensityUnit å­—段");
            }
        } catch (error) {
            console.error("解析 scheme.data å‡ºé”™", error);
        }
    };
    const clearSelectedScheme = () => {
        selectedScheme.value = null
    }
@@ -200,6 +214,7 @@
        devices,
        frameNum,
        layerDate,
        rePlayList,
        // æ–¹æ¡ˆç›¸å…³æ–¹æ³•
        setSchemCard,
src/views/left/CitySim.vue
@@ -186,7 +186,7 @@
const intensityOptions = ref([
  { value: "mm/h", label: "mm/h" },
  { value: "mm/5min", label: "mm/5min" },
  { value: "mm/1min", label: "mm/1min" },
  { value: "mm/min", label: "mm/min" },
]);
// å®šä¹‰ä¸€ä¸ªæ–¹æ³•,用于根据 type èŽ·å–åŒºåŸŸæ•°æ®
@@ -495,7 +495,9 @@
      : undefined,
  }));
  console.log(rawRainFallList, "原始降雨数据");
  // æ›´æ–° forms.rainFallList,可用于图表显示等用途
  forms.rainFallList = rawRainFallList;
  // åˆ¤æ–­æ˜¯å¦ä¸ºæ•´å°æ—¶æ•°æ®ï¼ˆå³ç›¸é‚»æ—¶é—´é—´éš”æ˜¯å¦ä¸ºæ•´å°æ—¶ï¼‰
  const isHourlyData = checkIfHourlyData(rawRainFallList);
@@ -513,9 +515,6 @@
      intensity: item.intensity,
    }));
  }
  // æ›´æ–° forms.rainFallList,可用于图表显示等用途
  forms.rainFallList = rawRainFallList;
  // è®¡ç®—起始时间和结束时间(毫秒数)
  const firstTime = parseDateTime(hourlyRainfallList[0]?.time);
src/views/left/KGSimOption/RealTimeSimulation.vue
@@ -81,7 +81,8 @@
import { useSimStore } from "@/store/simulation.js";
import { EventBus } from "@/eventBus"; // å¼•入事件总线
import { getDeviceInfoSHG, getYLJData } from "@/api/hpApi";
import { getSimStart, getSimDataById } from "@/api/trApi";
import { getSimStart, getSimDataById, getSimresult } from "@/api/trApi";
import { ControlSchemeType } from "@/assets/js/lib-pixelstreamingfrontend.esm";
// èŽ·å– Store å®žä¾‹
const simAPIStore = SimAPIStore();
@@ -219,14 +220,15 @@
// å®žæ—¶æ¨¡æ‹Ÿå®šæ—¶å™¨
let pollingInterval = null;
// ç”¨äºŽè®°å½•上次数据条数
let lastDataLength = 0;
let pollingTimer = null; // ç”¨äºŽä¿å­˜å®šæ—¶å™¨å¼•用
async function startPlay() {
  // å¼€å§‹æ¨¡æ‹Ÿå‰éœ€è¦å…ˆä¿å­˜æ–¹æ¡ˆ
  updateSelectedGauges();
  formData.geom = props.selectedArea;
  // ä¿å­˜æ–¹æ¡ˆ
  const resApi = await simAPIStore.addSimCheme(formData);
  const schemeId = resApi.data?.data?.id;
@@ -237,7 +239,6 @@
  EventBus.emit("close-selectArea");
  // æ˜¾ç¤ºåŠ è½½ä¸­æç¤º
  const loadingMessage = ElMessage({
    type: "info",
    message: "正在启动模拟...",
@@ -246,58 +247,130 @@
  });
  try {
    // è°ƒç”¨æ±‚解器并初始化模拟
    const resStart = await getSimStart(schemeId);
    await getSimStart(schemeId);
    // è¯·æ±‚完成后关闭加载提示
    loadingMessage.close();
    if (resStart.code === 200) {
      const res = await getSimDataById(schemeId);
      simStore.setSelectedScheme(res.data[0]);
      simStore.layerDate = resStart.data;
      initeWaterPrimitiveView();
    // å®šä¹‰ä¸€ä¸ªå‡½æ•°ç”¨äºŽè½®è¯¢èŽ·å–æ•°æ®
    const pollForResult = async () => {
      try {
        startSimulate(); // è¿™é‡Œå¯èƒ½ä¼šæŠ¥é”™
      } catch (error) {
        console.error("调用 startSimulate å‡ºé”™ï¼š", error);
      }
        const res = await getSimresult(schemeId);
        console.log(res.data, "实时模拟 - è½®è¯¢ç»“æžœ");
      // å¼€å§‹è½®è¯¢ä»»åŠ¡ï¼šæ¯ 5 åˆ†é’Ÿè°ƒç”¨ä¸€æ¬¡ getSimStart å¹¶æ›´æ–°æ–¹æ¡ˆæ•°æ®
      startPolling(schemeId);
    } else {
      ElMessage.error(resStart.message || "调用求解器失败");
    }
        if (res.code === 200 && res.data.length > 0) {
          // æˆåŠŸæ‹¿åˆ°æ•°æ®
          loadingMessage.close();
          handleNewData(res.data, schemeId);
          startPolling(schemeId);
          // âœ… æ¸…除定时器
          if (pollingTimer) {
            clearTimeout(pollingTimer);
            pollingTimer = null;
          }
        } else {
          // æ•°æ®æ— æ•ˆï¼Œç»§ç»­è½®è¯¢
          pollingTimer = setTimeout(pollForResult, 10 * 1000);
        }
      } catch (error) {
        console.error("请求模拟结果失败", error);
        pollingTimer = setTimeout(pollForResult, 10 * 1000); // è¯·æ±‚出错也继续轮询
      }
    };
    // é¦–次延迟 2 åˆ†é’Ÿå¼€å§‹è½®è¯¢
    pollingTimer = setTimeout(async () => {
      await pollForResult(); // å¼€å§‹ç¬¬ä¸€æ¬¡è½®è¯¢
    }, 3 * 60 * 1000); // 3分钟后第一次请求
  } catch (error) {
    loadingMessage.close();
    ElMessage.error("请求失败:" + (error.message || "未知错误"));
    console.error("调用 getSimStart å‡ºé”™ï¼š", error);
    if (pollingTimer) {
      clearTimeout(pollingTimer);
      pollingTimer = null;
    }
  }
}
// å¯åŠ¨è½®è¯¢å‡½æ•°
// å®šæ—¶äº”分钟请求
function startPolling(schemeId) {
  stopPolling(); // é¿å…é‡å¤å¯åЍ
  stopPolling(); // ç¡®ä¿ä¸ä¼šé‡å¤å¯åЍ
  pollingInterval = setInterval(async () => {
    try {
      const resStart = await getSimStart(schemeId);
      const res = await getSimresult(schemeId);
      if (resStart.code === 200) {
        const res = await getSimDataById(schemeId);
        simStore.setSelectedScheme(res.data[0]); // æ›´æ–°æ–¹æ¡ˆæ•°æ®
        simStore.layerDate = resStart.data; // æ›´æ–° layer æ•°æ®
      if (res.data && res.data.length > 0) {
        if (res.data.length === lastDataLength) {
          console.log("主轮询:无新数据,切换为 10 ç§’高频轮询");
        console.log("轮询获取最新数据成功");
      } else {
        console.warn("轮询请求失败:", resStart.message);
          clearInterval(pollingInterval);
          pollingInterval = null;
          startFastPolling(schemeId); // å¯åŠ¨é«˜é¢‘è½®è¯¢
        } else {
          handleNewData(res.data, schemeId);
        }
      }
    } catch (error) {
      console.error("轮询请求异常:", error);
      console.error("轮询获取模拟结果失败", error);
    }
  }, 5 * 60 * 1000); // æ¯ 5 åˆ†é’Ÿæ‰§è¡Œä¸€æ¬¡
  }, 5.6 * 60 * 1000); // æ¯ 5.5 åˆ†é’Ÿæ‰§è¡Œä¸€æ¬¡
}
let fastPollingInterval = null;
// å¦‚果五分钟没拿到最新的数据,则开启十秒钟调用一次,拿到最新的数据就停止
function startFastPolling(schemeId) {
  fastPollingInterval = setInterval(async () => {
    try {
      const res = await getSimresult(schemeId);
      if (res.data && res.data.length > 0) {
        if (res.data.length !== lastDataLength) {
          console.log("高频轮询:检测到新数据,恢复主轮询");
          clearInterval(fastPollingInterval);
          fastPollingInterval = null;
          handleNewData(res.data, schemeId);
          startPolling(schemeId); // é‡æ–°å¯åŠ¨ä¸»è½®è¯¢
        }
      }
    } catch (error) {
      console.error("高频轮询获取模拟结果失败", error);
    }
  }, 10 * 1000); // æ¯ 10 ç§’执行一次
}
// æ‹¿å–最新的layer.json存储到pinia中
async function handleNewData(dataArray, schemeId) {
  // æ‹¿æœåŠ¡åç§°
  const res = await getSimDataById(schemeId);
  simStore.setSelectedScheme(res.data[0]); // æ›´æ–°æ–¹æ¡ˆæ•°æ®
  const latestItem = dataArray[dataArray.length - 1];
  const currentLength = dataArray.length;
  if (currentLength <= lastDataLength) {
    console.log("本轮无新数据(长度未变化)");
    return;
  }
  // æ›´æ–°æ ‡è¯†
  lastDataLength = currentLength;
  // æ‰§è¡Œæ›´æ–°é€»è¾‘
  console.log("检测到新数据,更新中...");
  console.log(latestItem, "last");
  simStore.layerDate = latestItem;
  initeWaterPrimitiveView();
  try {
    startSimulate();
  } catch (error) {
    console.error("调用 startSimulate å‡ºé”™ï¼š", error);
  }
}
// åœæ­¢è½®è¯¢å‡½æ•°
@@ -305,8 +378,14 @@
  if (pollingInterval) {
    clearInterval(pollingInterval);
    pollingInterval = null;
    console.log("轮询已停止");
  }
  if (fastPollingInterval) {
    clearInterval(fastPollingInterval);
    fastPollingInterval = null;
  }
  console.log("轮询已停止");
}
EventBus.on("close-time", () => {
vue.config.js
@@ -21,7 +21,7 @@
    hot: true,
    proxy: {
      '/api': {
        target: 'http://192.168.56.107:8088',
        target: 'http://192.168.37.60:8088',
        // target: 'http://192.168.1.104:8078',
        changeOrigin: true,
        // pathRewrite: {
@@ -38,7 +38,7 @@
        secure: false
      },
      '/simu': {
        target: 'http://192.168.56.107:8088',
        target: 'http://192.168.37.60:8088',
        changeOrigin: true,
        secure: false
      }