¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <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" |
| | | /> |
| | | </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" |
| | | /> |
| | | </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 }" |
| | | > |
| | | {{ rate }}X |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="timeline"> |
| | | <div class="dates"> |
| | | <div class="current-date">å½åææ¾æ¶é´ï¼{{ currentPlayingTime }}</div> |
| | | <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" |
| | | /> |
| | | <!-- 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="scale-markers"> |
| | | <div class="scale-marker" style="left: 0%"></div> |
| | | <div class="scale-marker" style="left: 25%"></div> |
| | | <div class="scale-marker" style="left: 50%"></div> |
| | | <div class="scale-marker" style="left: 75%"></div> |
| | | <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 class="date-part">{{ time.split(" ")[0] }}</div> |
| | | <div class="time-part">{{ time.split(" ")[1] }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div> |
| | | <div style="display: flex"> |
| | | <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=" |
| | | margin-top: 12px; |
| | | margin-left: 16px; |
| | | margin-right: 20px; |
| | | justify-content: flex-end; |
| | | " |
| | | /> |
| | | </div> |
| | | <el-button |
| | | @click="handleBack" |
| | | style=" |
| | | margin-top: 3px; |
| | | margin-left: 28px; |
| | | margin-right: 10px; |
| | | width: 75%; |
| | | height: 30%; |
| | | " |
| | | >ç»ææ¨¡æ</el-button |
| | | > |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { |
| | | ref, |
| | | computed, |
| | | onMounted, |
| | | watch, |
| | | defineProps, |
| | | onBeforeUnmount, |
| | | inject, |
| | | reactive, |
| | | watchEffect, |
| | | } from "vue"; |
| | | import ratelevel from "@/components/menu/flowRate_waterLevel.vue"; |
| | | import crossanalysis from "@/components/menu/CrossSectionalAnalysis.vue"; |
| | | |
| | | import dayjs from "dayjs"; |
| | | import { |
| | | createWaterPrimitive, |
| | | destoryWaterPrimitive, |
| | | pauseWaterSimulation, |
| | | resumeWaterSimulation, |
| | | setTimeForWaterSimulation, |
| | | toggleWaterColorRender, |
| | | updateWaterColor, |
| | | } from "@/utils/water"; |
| | | import mapUtils from "@/utils/tools.js"; |
| | | import { fetchWaterSimulationData, stopSim } from "@/api/trApi.js"; |
| | | import { EventBus } from "@/eventBus"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | // ç¶æç®¡çå¨ |
| | | import { useSimStore } from "@/store/simulation"; |
| | | import { storeToRefs } from "pinia"; |
| | | const simStore = useSimStore(); |
| | | const { selectedScheme, frameNum, layerDate, schemWaterInfo } = |
| | | storeToRefs(simStore); |
| | | import { clearAllPoints } from "@/utils/map"; |
| | | |
| | | const emit = defineEmits([ |
| | | "timeUpdate", |
| | | "isPlaying", |
| | | "playbackFinished", |
| | | "isColorRender", |
| | | ]); |
| | | // å®ä¹props |
| | | const props = defineProps({ |
| | | waterSimulateParams: { |
| | | type: Object, |
| | | default: () => ({ |
| | | date: ["2025-02-14T16:00:00.000Z", "2025-02-15T16:00:00.000Z"], |
| | | }), |
| | | }, |
| | | }); |
| | | // ååºå¼ç¶æ |
| | | let serviceInfo = ref(null); // å½åæ¹æ¡çæå¡å°å |
| | | const ratelevelRef = ref(null); // è·ååç»ä»¶å®ä¾çå¼ç¨ |
| | | const crossRef = ref(null); // è·ååç»ä»¶å®ä¾çå¼ç¨ |
| | | const currentPlayingTime = ref(""); // å½åææ¾æ¶é´ |
| | | const sendCurrentPlayingTime = ref(""); // å½åææ¾æ¶é´ |
| | | const isPlaying = ref(false); |
| | | const playbackFinished = ref(true); |
| | | const currentTime = ref(0); |
| | | const duration = ref(60); // ä¸å¤©çç§æ° |
| | | const playbackRate = ref(1); |
| | | const playbackRates = ref([1, 2, 4, 8]); |
| | | const showSpeedMenu = ref(false); |
| | | const speedShow = ref(false); |
| | | |
| | | const waterTimestamps = ref([]); // å卿¶é´è½´æ°æ® |
| | | const timeMarkers = ref([]); |
| | | const timelineTrack = ref(null); |
| | | const isColorRenderEnabled = ref(false); // åè®¾è¿æ¯ä½ çé¢è²æ¸²æå¼å
³ç¶æ |
| | | const isWaterPrimitiveCreated = ref(false); |
| | | let playInterval = null; |
| | | let timeStepInfo = null; |
| | | let rainTotalInfo = []; |
| | | const isRainEnabled = ref(false); |
| | | const rainParams = reactive({ |
| | | rainSize: 0.5, |
| | | rainSpeed: 50, |
| | | rainColor: "#99B3CC", |
| | | rainDensity: 30, // é¨çå¯åº¦ |
| | | }); |
| | | let minFlowRate = ref(); |
| | | let maxFlowRate = ref(); |
| | | // 计ç®å±æ§ |
| | | const progressPercentage = computed( |
| | | () => (currentTime.value / duration.value) * 100 |
| | | ); |
| | | const visibleDates = computed(() => |
| | | Array.from( |
| | | new Set(waterTimestamps.value.map((ts) => dayjs(ts).format("YYYY-MM-DD"))) |
| | | ).map((date) => dayjs(date).toDate()) |
| | | ); |
| | | |
| | | // æéè¦å ä¸ä¸ªå¤æ |
| | | const finishPlay = ref(false); |
| | | // ææ¾æ§å¶ |
| | | const togglePlay = () => { |
| | | // è¿éåºè¯¥å设å®å 个éå¶ï¼å¦æç¼ºå°ä»ä¹æ°æ®ï¼æ æ³è¿è¡ä»¿ç |
| | | if (!isPlaying.value && currentTime.value >= duration.value) |
| | | currentTime.value = 0; |
| | | |
| | | isPlaying.value = !isPlaying.value; |
| | | emit("isPlaying", isPlaying.value); |
| | | |
| | | if (isPlaying.value) { |
| | | startPlayback(); |
| | | |
| | | if (!isWaterPrimitiveCreated.value) { |
| | | // console.log(serviceInfo, 'è¿éæ¯å½åæ¹æ¡çæå¡ä¿¡æ¯ï¼'); |
| | | // è¿ééè¿water.jsä¸å»åé请æ±è·åæ°´é¢æ¨¡æ |
| | | createWaterPrimitive({ |
| | | baseUrl: `/simu/${serviceInfo}`, |
| | | // baseUrl: `/simu/c2h1dc`, |
| | | interval: intervalMap[playbackRate.value], |
| | | colorRender: isColorRenderEnabled.value, |
| | | minFlowRate, |
| | | maxFlowRate, |
| | | }); |
| | | isWaterPrimitiveCreated.value = true; |
| | | } else { |
| | | resumeWaterSimulation(); |
| | | toggleWaterColorRender(isColorRenderEnabled.value); // æ´æ°é¢è²æ¸²æ |
| | | } |
| | | |
| | | if (currentTime.value === 0) emit("playbackFinished", false); |
| | | |
| | | if (isRainEnabled.value) { |
| | | mapUtils.toggleRain(rainParams, true); |
| | | } |
| | | } else { |
| | | stopPlayback(); |
| | | pauseWaterSimulation(); |
| | | |
| | | isRainEnabled.value = true; |
| | | setTimeout(() => { |
| | | mapUtils.delRain(); |
| | | }, 3000); |
| | | } |
| | | }; |
| | | |
| | | // é¢è²æ¸²æåæ¢äºä»¶ |
| | | const handleColorRenderChange = (enabled) => { |
| | | if (!isPlaying.value) { |
| | | ElMessage({ |
| | | message: "请å
å¯å¨æ°´ä½æ¨¡æååè¿è¡ä¸é¢ææåæ¢ã", |
| | | type: "warning", |
| | | }); |
| | | return; // 黿¢åç»é»è¾æ§è¡ |
| | | } |
| | | if (isWaterPrimitiveCreated.value) { |
| | | console.log("å½åæ¯å¦å¼å¯ä¸é¢æ¸²æï¼", enabled); |
| | | emit("isColorRender", enabled); |
| | | toggleWaterColorRender(enabled); |
| | | } |
| | | }; |
| | | const intervalMap = { |
| | | 1: 1000, // 1åé |
| | | 2: 500, // 2åé |
| | | 4: 250, // 4åé |
| | | 8: 125, // 8åé |
| | | }; |
| | | // ææ¾é»è¾ |
| | | // const startPlayback = () => { |
| | | // clearInterval(playInterval); |
| | | |
| | | // playInterval = setInterval(() => { |
| | | // // æ¾å°å½åæ¶é´å¯¹åºçç´¢å¼ |
| | | // const currentIndex = findClosestTimestampIndex(currentTime.value); |
| | | // const nextIndex = currentIndex + 1; |
| | | |
| | | // // å¦æå·²ç»æ¯æåä¸ä¸ªæ¶é´ç¹ï¼åæ¢ææ¾ |
| | | // if (nextIndex >= waterTimestamps.value.length) { |
| | | // currentTime.value = duration.value; |
| | | // stopPlayback(); |
| | | // isPlaying.value = false; |
| | | // emit("isPlaying", false); |
| | | // emit("playbackFinished", true); |
| | | // return; |
| | | // } |
| | | |
| | | // // æ´æ°æ¶é´ä¸ºä¸ä¸ä¸ªæ¶é´ç¹çæ¶é´å·®ï¼ç§ï¼ |
| | | // const nextTimestamp = waterTimestamps.value[nextIndex]; |
| | | // const baseTimestamp = waterTimestamps.value[0]; |
| | | // currentTime.value = (nextTimestamp - baseTimestamp) / 1000; |
| | | |
| | | // // è§¦åæ´æ° |
| | | // if (selectedScheme.value.type !== 2) { |
| | | // updateWaterColorByTime(); |
| | | // updateWeatherByProgress(); |
| | | // } |
| | | |
| | | // const progress = currentTime.value / duration.value; |
| | | // emit("timeUpdate", progress * 100); |
| | | // }, 1000 / playbackRate.value); // æ ¹æ®ææ¾éçè°æ´é´é |
| | | // }; |
| | | const startPlayback = () => { |
| | | clearInterval(playInterval); |
| | | |
| | | // æ°å»ºæ¹æ¡ä¸ç宿¶æ¨¡æä¸è½åé |
| | | if (selectedScheme.value.type === 2 && simStore.rePlayList.length == 0) { |
| | | console.log("æ°å»ºæ¹æ¡å®æ¶æ¨¡æäºç§ä¸è·³"); |
| | | // 宿¶æ¨¡æï¼æ¯ 5 ç§è·³å¨ä¸æ¬¡ |
| | | playInterval = setInterval(() => { |
| | | const fiveSeconds = 5; |
| | | const totalDuration = duration.value; // æ»æ¶é¿ï¼ç§ï¼ |
| | | |
| | | currentTime.value += fiveSeconds; |
| | | |
| | | if (currentTime.value >= totalDuration) { |
| | | currentTime.value = totalDuration; |
| | | stopPlayback(); |
| | | isPlaying.value = false; |
| | | finishPlay.value = true; |
| | | emit("isPlaying", false); |
| | | emit("playbackFinished", true); |
| | | return; |
| | | } |
| | | |
| | | // 触åè¿åº¦æ´æ° |
| | | // const progress = currentTime.value / totalDuration; |
| | | // 宿¶æ¨¡æåºè¯¥ä¸ç¨æ¾ç¤ºå¼¹çªå§ |
| | | // emit("timeUpdate", progress * 100); |
| | | |
| | | // 妿éè¦è§¦åæäºæ´æ°å½æ°ï¼ä¹å¯ä»¥ä¿ç |
| | | updateWaterColorByTime(); |
| | | // updateWeatherByProgress(); |
| | | // ä¿®æ¹ä¸ºåºå®é¶æ®µï¼ç¼æ
¢ä¸é¨çç¶æ |
| | | const rainParams = { |
| | | rainSize: 0.5, |
| | | rainSpeed: 20, |
| | | rainDensity: 15, |
| | | rainColor: "#ADD8E6", |
| | | }; |
| | | console.log("宿¶æ¨¡æå¼å§ä¸é¨"); |
| | | // è°ç¨å·¥å
·æ¹æ³æ´æ°é¨æ |
| | | mapUtils.toggleRain(rainParams, true); |
| | | }, 5000); // æ¯ 5 ç§æ§è¡ä¸æ¬¡ |
| | | } else { |
| | | // è¿éé¢è¿æ¯ä½ çææ¾ä»£ç ï¼ä¸é¢çif䏿¯äºç§éè·³å¨ä¸æ¬¡ç宿¶æ¨¡æ |
| | | playInterval = setInterval(() => { |
| | | const currentIndex = findClosestTimestampIndex(currentTime.value); |
| | | const nextIndex = currentIndex + 1; |
| | | |
| | | if (nextIndex >= waterTimestamps.value.length) { |
| | | currentTime.value = duration.value; |
| | | stopPlayback(); |
| | | isPlaying.value = false; |
| | | finishPlay.value = true; |
| | | emit("isPlaying", false); |
| | | emit("playbackFinished", true); |
| | | return; |
| | | } |
| | | |
| | | const nextTimestamp = waterTimestamps.value[nextIndex]; |
| | | const baseTimestamp = waterTimestamps.value[0]; |
| | | currentTime.value = (nextTimestamp - baseTimestamp) / 1000; |
| | | |
| | | if (selectedScheme.value.type !== 2) { |
| | | updateWaterColorByTime(); |
| | | updateWeatherByProgress(); |
| | | } |
| | | |
| | | const progress = currentTime.value / duration.value; |
| | | if (selectedScheme.value.type !== 2) { |
| | | emit("timeUpdate", progress * 100); |
| | | } |
| | | }, 1000 / playbackRate.value); |
| | | } |
| | | }; |
| | | // éé¨ååé¨å |
| | | // é鍿°æ®ç¸å
³åé |
| | | let rainFallValues = ref([]); // åå¨åå§éé¨éæ°æ® |
| | | let minRainValue = ref(Infinity); |
| | | let averageRainIntensity = ref(); |
| | | let maxRainValue = ref(-Infinity); |
| | | // è·åé鍿°æ® |
| | | function getRainfallData() { |
| | | if (!selectedScheme.value || !selectedScheme.value.data) { |
| | | console.warn("selectedScheme æ data ä¸åå¨"); |
| | | return; |
| | | } |
| | | |
| | | let data = selectedScheme.value.data; |
| | | |
| | | // 妿æ¯å符串ï¼åå°è¯è§£æä¸ºå¯¹è±¡ |
| | | if (typeof data === "string") { |
| | | try { |
| | | data = JSON.parse(data); |
| | | console.log("è§£æåçé鍿°æ®ï¼", data); |
| | | } catch (e) { |
| | | console.error("data 䏿¯ææç JSON å符串"); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | console.log("éé¨å¼ºåº¦çå使¯ï¼", data.intensityUnit); |
| | | |
| | | // 夿 rainfalls æ¯å¦ä¸ºå¯¹è±¡ï¼å¦ææ¯åè½¬ææ°ç» |
| | | let rainfalls = data.rainfalls; |
| | | if (typeof rainfalls === "object" && !Array.isArray(rainfalls)) { |
| | | rainfalls = Object.values(rainfalls); |
| | | console.warn("â ï¸ rainfalls æ¯å¯¹è±¡ï¼å·²è½¬æ¢ä¸ºæ°ç»"); |
| | | } |
| | | |
| | | // æå°æ¶èåé鍿°æ® |
| | | const hourlyRainfallMap = {}; |
| | | |
| | | rainfalls.forEach((record) => { |
| | | const originalTime = new Date(record.time); |
| | | if (isNaN(originalTime.getTime())) { |
| | | console.warn("æ æçæ¶é´æ ¼å¼ï¼", record.time); |
| | | return; |
| | | } |
| | | |
| | | // æé âå°æ¶â级å«çæ¶é´é®ï¼æ¯å¦ï¼2024-08-25 20:00:00 |
| | | const hourKey = new Date( |
| | | originalTime.getFullYear(), |
| | | originalTime.getMonth(), |
| | | originalTime.getDate(), |
| | | originalTime.getHours() |
| | | ); |
| | | |
| | | const hourStr = hourKey.toISOString().slice(0, 16).replace("T", " "); |
| | | |
| | | if (!hourlyRainfallMap[hourStr]) { |
| | | hourlyRainfallMap[hourStr] = { |
| | | intensity: 0, |
| | | time: hourStr, |
| | | total: record.total, // é»è®¤ç¨ç¬¬ä¸ä¸ªè®°å½ç total |
| | | }; |
| | | } |
| | | |
| | | hourlyRainfallMap[hourStr].intensity += record.intensity; |
| | | // åæå¤§ç totalï¼å 为æ¯ç´¯ç§¯å¼ï¼ |
| | | if (record.total > hourlyRainfallMap[hourStr].total) { |
| | | hourlyRainfallMap[hourStr].total = record.total; |
| | | } |
| | | }); |
| | | |
| | | // è½¬æ¢ map ææ°ç»å¹¶æåº |
| | | const hourlyRainfallList = Object.values(hourlyRainfallMap).sort((a, b) => |
| | | a.time.localeCompare(b.time) |
| | | ); |
| | | |
| | | console.log("â
æå°æ¶èååçé鍿°æ®ï¼", hourlyRainfallList); |
| | | |
| | | // 设置å
¨å±åé |
| | | rainTotalInfo.value = hourlyRainfallList; |
| | | |
| | | // è®¡ç®æ¶é´æ¥é¿ |
| | | timeStepInfo = calculateTimeStep(rainTotalInfo.value); |
| | | |
| | | // æå intensity å¼ |
| | | rainFallValues.value = hourlyRainfallList.map((r) => r.intensity); |
| | | // 计ç®å¹³åé¨å¼º |
| | | if (rainFallValues.value.length > 0) { |
| | | const sumIntensity = rainFallValues.value.reduce( |
| | | (sum, val) => sum + val, |
| | | 0 |
| | | ); |
| | | averageRainIntensity.value = sumIntensity / rainFallValues.value.length; |
| | | } else { |
| | | averageRainIntensity.value = 0; // æè
null è¡¨ç¤ºæ æ°æ® |
| | | } |
| | | |
| | | console.log("å¹³åé¨å¼ºä¸ºï¼", averageRainIntensity.value); |
| | | |
| | | minRainValue.value = Math.min(...rainFallValues.value); |
| | | maxRainValue.value = Math.max(...rainFallValues.value); |
| | | |
| | | console.log( |
| | | "å½åæ¹æ¡ä¸æå°é¨éåæå¤§é¨éï¼", |
| | | minRainValue.value, |
| | | maxRainValue.value |
| | | ); |
| | | } |
| | | // å®ä¹éé¨ç级åå
¶å¯¹åºçè§è§åæ° |
| | | const rainLevels = [ |
| | | { |
| | | name: "å°é¨", |
| | | min: 0.1, |
| | | max: 9.9, |
| | | size: 0.5, // 鍿»´å¤§å°ï¼æ´å° |
| | | speed: 20, // ä¸è½éåº¦ï¼æ´æ
¢ |
| | | density: 15, // 鍿»´å¯åº¦ï¼æ´ç¨ç |
| | | color: "#ADD8E6", // æµ
èè²ï¼è±¡å¾è½»æçå°é¨ |
| | | }, |
| | | { |
| | | name: "ä¸é¨", |
| | | min: 10, |
| | | max: 24.9, |
| | | size: 0.6, |
| | | speed: 24, |
| | | density: 18, |
| | | color: "#ADD8E6", |
| | | }, |
| | | { |
| | | name: "大é¨", |
| | | min: 25, |
| | | max: 49.9, |
| | | size: 0.7, |
| | | speed: 28, |
| | | density: 21, |
| | | color: "#ADD8E6", |
| | | }, |
| | | { |
| | | name: "æ´é¨", |
| | | min: 50, |
| | | max: 99.9, |
| | | size: 0.8, |
| | | speed: 32, |
| | | density: 24, |
| | | color: "#ADD8E6", |
| | | }, |
| | | { |
| | | name: "大æ´é¨", |
| | | min: 100, |
| | | size: 0.9, |
| | | speed: 36, |
| | | density: 27, |
| | | color: "#ADD8E6", |
| | | }, |
| | | ]; |
| | | // æ ¹æ®éé¨éè¿å对åºçé¨å½¢é
ç½® |
| | | function getRainLevel(rainValue) { |
| | | for (let level of rainLevels) { |
| | | if ( |
| | | level.min <= rainValue && |
| | | (level.max === undefined || rainValue <= level.max) |
| | | ) { |
| | | return level; |
| | | } |
| | | } |
| | | // é»è®¤æ é¨ç¶æ |
| | | |
| | | return { name: "æ é¨", size: 0.3, speed: 10, density: 10, color: "#F0F8FF" }; |
| | | } |
| | | // æ ¹æ®ææ¾è¿åº¦æ´æ°å¤©æ°ææï¼å·²ä¼åï¼ |
| | | let lastUsedIndex = -1; // ç¼åä¸ä¸æ¬¡ä½¿ç¨çç´¢å¼ï¼é²æ¢é夿´æ° |
| | | let lastRainValue = null; |
| | | |
| | | function calculateTimeStep(dataArray) { |
| | | if (!dataArray || dataArray.length < 2) { |
| | | console.warn("æ°æ®ä¸è¶³ï¼æ æ³è®¡ç®æ¶é´æ¥é¿"); |
| | | return null; |
| | | } |
| | | |
| | | // è§£ææ¶é´å符串为 Date 对象 |
| | | function parseTime(timeStr) { |
| | | return new Date(timeStr.replace(" ", "T")); // å
¼å®¹ ISO æ ¼å¼ |
| | | } |
| | | |
| | | const firstTime = parseTime(dataArray[0].time); |
| | | const secondTime = parseTime(dataArray[1].time); |
| | | |
| | | // è®¡ç®æ¶é´å·®ï¼æ¯«ç§ï¼ |
| | | const diffMs = Math.abs(secondTime - firstTime); |
| | | |
| | | // 转æ¢ä¸ºå°æ¶æ°ï¼ä¿çå°æ°ï¼ |
| | | let timeStepHours = diffMs / (1000 * 60 * 60); // æ¯«ç§ -> å°æ¶ |
| | | |
| | | // å¯éï¼éåææç¸é»é¡¹æ£æ¥æ¯å¦ä¸è´ |
| | | for (let i = 1; i < dataArray.length - 1; i++) { |
| | | const current = parseTime(dataArray[i].time); |
| | | const next = parseTime(dataArray[i + 1].time); |
| | | const step = Math.abs(next - current) / (1000 * 60 * 60); // æ¯«ç§ -> å°æ¶ |
| | | if (Math.abs(step - timeStepHours) > 0.01) { |
| | | console.warn( |
| | | `å¨ç´¢å¼ ${i} å¤åç°äºä¸åçæ¶é´æ¥é¿: ${step.toFixed(2)} å°æ¶` |
| | | ); |
| | | } |
| | | } |
| | | |
| | | return timeStepHours; |
| | | } |
| | | // ============================================================================ |
| | | // ä¼åæ¹å¼ï¼å¯ä»¥æ±åºæ´ä¸ªæ¶é´è½´ä¸ï¼ç¬¬ä¸æ¬¡éå°è¿å
个éå¼å¾æ¶é´ç¹ï¼ç¶ååæ¶é´æ®µæ¾ç¤ºï¼aæ¶é´å
æ¾ç¤ºç¶æ1ï¼ç¶åç¶æäº¤çå¤è®¾ç½®é¢è²æ¸åï¼å
¶ä½åçï¼è¿æ ·è·³è½¬å¾æ¶åè½å¤ç´æ¥è·³è½¬å°å½åå¾é¢è²ä¿¡æ¯é¶æ®µï¼ç´æ¥åºç¨ï¼å³å¯ |
| | | // ============================================================================ |
| | | // å
¨å±ç¶æè®°å½ |
| | | const colorState = { |
| | | currentColor: "#F5F0E6", // å½åé¢è² |
| | | currentAlpha: -0.3, // å½åéæåº¦ |
| | | colorStages: null, // é¢è®¡ç®çé¢è²é¶æ®µæ¶é´ç¹ |
| | | maxColorTime: null, // è®°å½è¾¾å°ææ·±é¢è²æ¶çæ¶é´ç¹ |
| | | }; |
| | | |
| | | // é¢è®¡ç®é¢è²é¶æ®µæ¶é´ç¹ |
| | | function precomputeColorStages() { |
| | | if (!rainTotalInfo.value || rainTotalInfo.value.length === 0) return; |
| | | // é¢è²é
ç½®ï¼äº®åº¦éåï¼ |
| | | const COLOR_STOPS = [ |
| | | { hex: "#F5F0E6", luminance: 240.4 }, // stage 0 |
| | | { hex: "#E6D5B8", luminance: 214.8 }, // stage 1 |
| | | { 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 |
| | | ]; |
| | | 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 |
| | | ]; |
| | | // 累计éé¨ééå¼ï¼mmï¼ |
| | | const R_THRESHOLDS = [0, 200, 240, 280, 310, 350]; // å
±6ä¸ªé¶æ®µå¯¹åº6个éå¼ |
| | | // æ¶é´åéé¨éä¿¡æ¯ |
| | | const timeTotals = []; |
| | | const initialTimestamp = new Date(rainTotalInfo.value[0].time).getTime(); |
| | | for (let i = 0; i < rainTotalInfo.value.length; i++) { |
| | | const timestamp = new Date(rainTotalInfo.value[i].time).getTime(); |
| | | const time = (timestamp - initialTimestamp) / 1000; |
| | | const total = rainTotalInfo.value[i].total; // ä½¿ç¨ total æ¿ä»£ intensity |
| | | timeTotals.push({ |
| | | time, |
| | | total, |
| | | }); |
| | | } |
| | | |
| | | // æ¾åºæ¯ä¸ªé¶æ®µé¦æ¬¡è¾¾å°çæ¶é´ç¹ |
| | | const stages = []; |
| | | for (let stage = 1; stage < R_THRESHOLDS.length + 1; stage++) { |
| | | const threshold = R_THRESHOLDS[stage - 1]; |
| | | for (let i = 0; i < timeTotals.length; i++) { |
| | | const { time, total } = timeTotals[i]; |
| | | if (total >= threshold) { |
| | | stages[stage] = { |
| | | startTime: time, |
| | | color: COLOR_STOPS[stage].hex, |
| | | alpha: alphaStops[stage], |
| | | threshold, |
| | | }; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å¡«å
é¶æ®µ0 |
| | | stages[0] = { |
| | | startTime: 0, |
| | | color: COLOR_STOPS[0].hex, |
| | | alpha: alphaStops[0], |
| | | threshold: 0, |
| | | }; |
| | | |
| | | colorState.colorStages = stages; |
| | | } |
| | | |
| | | function updateWaterColorByTime(isForceUpdate = false) { |
| | | if (!rainTotalInfo.value || rainTotalInfo.value.length === 0) return; |
| | | |
| | | // 馿¬¡è°ç¨æ¶é¢è®¡ç®é¢è²é¶æ®µ |
| | | if (colorState.colorStages === null) { |
| | | precomputeColorStages(); |
| | | } |
| | | |
| | | // æ¥æ¾å½åæ¶é´ç¹æå±çé¶æ®µ |
| | | let currentStage = 0; |
| | | for (let i = colorState.colorStages.length - 1; i >= 0; i--) { |
| | | if ( |
| | | colorState.colorStages[i] && |
| | | currentTime.value >= colorState.colorStages[i].startTime |
| | | ) { |
| | | currentStage = i; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // è®°å½è¾¾å°ææ·±é¢è²çæ¶é´ç¹ |
| | | if (currentStage >= colorState.colorStages.length - 1) { |
| | | 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); |
| | | |
| | | // æ´æ°é¢è²é»è¾ |
| | | if (shouldForceUpdate || isTimeGoingBackward) { |
| | | // å¼ºå¶æ´æ°ææ¶é´åéæ¶ï¼ç´æ¥åºç¨å½åé¶æ®µçé¢è² |
| | | colorState.currentColor = colorState.colorStages[currentStage].color; |
| | | colorState.currentAlpha = colorState.colorStages[currentStage].alpha; |
| | | } else { |
| | | // æ£å¸¸æ¶é´åè¿æ¶ï¼ä¿ææ¸è¿åå |
| | | const newColor = colorState.colorStages[currentStage].color; |
| | | const newAlpha = colorState.colorStages[currentStage].alpha; |
| | | |
| | | // åªåºç¨æ´æçé¢è²åæ´ä½çéæåº¦ |
| | | if ( |
| | | calculateLuminance(newColor) < calculateLuminance(colorState.currentColor) |
| | | ) { |
| | | colorState.currentColor = newColor; |
| | | } |
| | | if (newAlpha < colorState.currentAlpha) { |
| | | colorState.currentAlpha = newAlpha; |
| | | } |
| | | } |
| | | |
| | | // æ´æ°æ¶é´è®°å½ |
| | | colorState.lastTime = currentTime.value; |
| | | // ====== æ°å¢ï¼å¨ updateWaterColor åæå°å½åä¿¡æ¯ ====== |
| | | // // è·åå½å累计éé¨é |
| | | let currentTotal = null; |
| | | const baseTimestamp = new Date(rainTotalInfo.value[0].time).getTime(); |
| | | const currentTimeMs = baseTimestamp + currentTime.value * 1000; |
| | | |
| | | // æ¾å°ææ¥è¿çé鍿°æ®ç¹ |
| | | for (let i = rainTotalInfo.value.length - 1; i >= 0; i--) { |
| | | const dataTimeMs = new Date(rainTotalInfo.value[i].time).getTime(); |
| | | if (dataTimeMs <= currentTimeMs) { |
| | | currentTotal = rainTotalInfo.value[i].total; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // æå°ä¿¡æ¯ |
| | | // console.log("========================================"); |
| | | // console.log(`ãæ¶é´æ³ã: ${new Date(currentTimeMs).toLocaleString()}`); |
| | | console.log( |
| | | `ã累计éé¨é Rã: ${ |
| | | currentTotal !== null ? currentTotal.toFixed(2) : "æªç¥" |
| | | } mm` |
| | | ); |
| | | // console.log(`ãå½åé¶æ®µã: 第 ${currentStage} é¶æ®µ`); |
| | | console.log( |
| | | `ãé¢è² HEXã: ${colorState.colorStages[currentStage]?.color || "æªå®ä¹"}` |
| | | ); |
| | | // console.log(`ãéæåº¦ Alphaã: ${colorState.colorStages[currentStage]?.alpha || 'æªå®ä¹'}`); |
| | | // console.log("========================================"); |
| | | // åºç¨é¢è² |
| | | updateWaterColor(colorState.currentColor, colorState.currentAlpha); |
| | | } |
| | | |
| | | // è¾
å©å½æ°ä¿æä¸å |
| | | function calculateLuminance(hex) { |
| | | const [r, g, b] = hexToRgb(hex); |
| | | return 0.299 * r + 0.587 * g + 0.114 * b; |
| | | } |
| | | |
| | | function hexToRgb(hex) { |
| | | const bigint = parseInt(hex.slice(1), 16); |
| | | return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255]; |
| | | } |
| | | |
| | | // æ¶é´è½´è·³è½¬å½æ° |
| | | const seekToPosition = (event) => { |
| | | if (!isWaterPrimitiveCreated.value) { |
| | | ElMessage.warning("请å
å¯å¨æ°´ä½æ¨¡æååè¿è¡æ¶é´è½´è·³è½¬ã"); |
| | | return; |
| | | } |
| | | |
| | | const rect = timelineTrack.value.getBoundingClientRect(); |
| | | const percentage = (event.clientX - rect.left) / rect.width; |
| | | const targetTime = Math.round(percentage * duration.value); |
| | | |
| | | const closestIndex = findClosestTimestampIndex(targetTime); |
| | | const baseTimestamp = waterTimestamps.value[0]; |
| | | const newTime = (waterTimestamps.value[closestIndex] - baseTimestamp) / 1000; |
| | | |
| | | // 夿æ¯å¦éè¦å¼ºå¶æ´æ°é¢è² |
| | | const isGoingBackward = newTime < currentTime.value; |
| | | const isBeforeMaxColor = |
| | | colorState.maxColorTime !== null && newTime <= colorState.maxColorTime; |
| | | const shouldForceUpdate = isGoingBackward || isBeforeMaxColor; |
| | | |
| | | currentTime.value = newTime; |
| | | setTimeForWaterSimulation(closestIndex); |
| | | |
| | | // æ ¹æ®æ¡ä»¶æ´æ°é¢è² |
| | | updateWaterColorByTime(shouldForceUpdate); |
| | | |
| | | if (!isPlaying.value) pauseWaterSimulation(); |
| | | }; |
| | | // ============================================================================ |
| | | |
| | | function updateWeatherByProgress() { |
| | | if (rainFallValues.value.length === 0) return; |
| | | // console.log(`æ¶é´è½´æ»æ¶é¿: ${duration.value}, å½åæ¶é´: ${currentTime.value}`); // æå°æ¶é´è½´ä¿¡æ¯ |
| | | const progress = currentTime.value / duration.value; |
| | | const floatIndex = progress * (rainFallValues.value.length - 1); |
| | | const index = Math.floor(floatIndex); // å½åç´¢å¼ |
| | | const nextIndex = Math.min(index + 1, rainFallValues.value.length - 1); // ä¸ä¸ç´¢å¼ |
| | | const currentRain = rainFallValues.value[index]; |
| | | const nextRain = rainFallValues.value[nextIndex]; |
| | | // å¯ç¨æå¼ï¼alpha å¹³æ»è¿æ¸¡ï¼ |
| | | const alpha = floatIndex - index; |
| | | // const rainValue = currentRain + (nextRain - currentRain) * alpha; |
| | | const rainValue = currentRain + (nextRain - currentRain); |
| | | // æå°å½åå¤ççé¨éæ°æ® |
| | | // console.log( |
| | | // `æ£å¨å¤ççé¨éæ°æ®ç¹: å½å=${currentRain}, ä¸ä¸ä¸ª=${nextRain}, æå¼å=${rainValue.toFixed( |
| | | // 2 |
| | | // )}, ç´¢å¼=${index}` |
| | | // ); |
| | | // 妿å½åç´¢å¼æªåå䏿å¼å·®å¼ä¸å¤§ï¼è·³è¿é夿´æ° |
| | | if (index === lastUsedIndex && Math.abs(rainValue - lastRainValue) < 0.1) { |
| | | // console.log('ç±äºæ°æ®æ æ¾èååï¼è·³è¿æ¬æ¬¡æ´æ°'); |
| | | return; |
| | | } |
| | | |
| | | lastUsedIndex = index; |
| | | lastRainValue = rainValue; |
| | | |
| | | // è·å对åºçé¨å½¢é
ç½® |
| | | const rainLevel = getRainLevel(rainValue); |
| | | |
| | | // if (rainLevel.name === 'æ é¨') { |
| | | // // æ é¨ç¶æï¼æ¸
é¤é¨æ |
| | | // mapUtils.delRain(); |
| | | // console.log('æ§è¡äºæ é¨ç¶æï¼æ¸
é¤äºé¨æ'); |
| | | // return; |
| | | // } |
| | | |
| | | // éæ é¨ç¶æï¼æå»ºé¨æ»´åæ°å¹¶æ´æ°é¨æ |
| | | const rainParams = { |
| | | rainSize: rainLevel.size, |
| | | rainSpeed: rainLevel.speed, |
| | | rainDensity: rainLevel.density, |
| | | rainColor: rainLevel.color, |
| | | }; |
| | | console.log("å½åé¨éæ°æ®ï¼", rainValue, "å½åé¨å½¢ï¼", rainLevel); |
| | | // è°ç¨å·¥å
·æ¹æ³æ´æ°é¨æ |
| | | mapUtils.toggleRain(rainParams, true); |
| | | } |
| | | const stopPlayback = () => { |
| | | clearInterval(playInterval); |
| | | }; |
| | | |
| | | const skipForward = () => { |
| | | if (waterTimestamps.value.length === 0) return; |
| | | const currentIndex = findClosestTimestampIndex(currentTime.value); |
| | | const nextIndex = currentIndex + 1; |
| | | if (nextIndex >= waterTimestamps.value.length) { |
| | | return; |
| | | } |
| | | const baseTimestamp = waterTimestamps.value[0]; |
| | | currentTime.value = (waterTimestamps.value[nextIndex] - baseTimestamp) / 1000; |
| | | setTimeForWaterSimulation(nextIndex); |
| | | if (!isPlaying.value) pauseWaterSimulation(); |
| | | }; |
| | | |
| | | const skipBackward = () => { |
| | | if (waterTimestamps.value.length === 0) return; |
| | | const currentIndex = findClosestTimestampIndex(currentTime.value); |
| | | const prevIndex = currentIndex - 1; |
| | | if (prevIndex < 0) { |
| | | return; |
| | | } |
| | | const baseTimestamp = waterTimestamps.value[0]; |
| | | currentTime.value = (waterTimestamps.value[prevIndex] - baseTimestamp) / 1000; |
| | | setTimeForWaterSimulation(prevIndex); |
| | | if (!isPlaying.value) pauseWaterSimulation(); |
| | | }; |
| | | const toggleSpeedMenu = () => (showSpeedMenu.value = !showSpeedMenu.value); |
| | | |
| | | // è®¾ç½®ææ¾éç |
| | | const setPlaybackRate = (rate) => { |
| | | isColorRenderEnabled.value = false; |
| | | playbackRate.value = rate; |
| | | showSpeedMenu.value = false; |
| | | // 忢å½åææ¾ |
| | | stopPlayback(); |
| | | setTimeout(() => { |
| | | mapUtils.delRain(); |
| | | }, 3000); |
| | | // éç½®æ¶é´è½´å°åå§ç¶æ |
| | | currentTime.value = 0; // æ¶é´å½é¶ |
| | | emit("timeUpdate", progressPercentage.value); |
| | | isPlaying.value = false; |
| | | emit("isPlaying", false); |
| | | |
| | | // éæ¯ç°æçæ°´ä½æ¨¡æå± |
| | | if (isWaterPrimitiveCreated.value) { |
| | | destoryWaterPrimitive(); |
| | | isWaterPrimitiveCreated.value = false; // éç½®æ å¿åé |
| | | } |
| | | isPlaying.value = false; |
| | | emit("isPlaying", false); |
| | | |
| | | pauseWaterSimulation(); // è°ç¨æåæ¥å£ |
| | | EventBus.emit("clear-echart"); |
| | | EventBus.emit("reset-table"); |
| | | }; |
| | | |
| | | // è¾
å©å½æ°ï¼æ¾å°ææ¥è¿çæ¶é´æ³ç´¢å¼ |
| | | function findClosestTimestampIndex(currentTimeValue) { |
| | | if (waterTimestamps.value.length === 0) return 0; |
| | | |
| | | // 计ç®å½åæ¶é´å¯¹åºçæ¯«ç§æ¶é´æ³ |
| | | const baseTime = waterTimestamps.value[0]; |
| | | const currentTimestamp = baseTime + currentTimeValue * 1000; |
| | | |
| | | // æ¾å°ææ¥è¿ç timestamp ç´¢å¼ |
| | | let closestIndex = 0; |
| | | let minDiff = Infinity; |
| | | |
| | | waterTimestamps.value.forEach((timestamp, index) => { |
| | | const diff = Math.abs(timestamp - currentTimestamp); |
| | | if (diff < minDiff) { |
| | | minDiff = diff; |
| | | closestIndex = index; |
| | | } |
| | | }); |
| | | |
| | | return closestIndex; |
| | | } |
| | | watch( |
| | | () => selectedScheme.value, |
| | | (newVal) => { |
| | | if (newVal) { |
| | | console.log("é䏿¹æ¡å·²æ¹å:", newVal); |
| | | } |
| | | } |
| | | ); |
| | | watch( |
| | | () => currentTime.value, |
| | | () => { |
| | | if (waterTimestamps.value.length > 0) { |
| | | sendCurrentPlayingTime.value = dayjs(waterTimestamps.value[0]) |
| | | .add(currentTime.value, "second") |
| | | .valueOf(); // ä½¿ç¨ valueOf() è·ååå§æ¶é´æ³ |
| | | |
| | | // æ´æ° currentPlayingTime æ ¼å¼ååçæ¶é´å符串 |
| | | currentPlayingTime.value = dayjs(sendCurrentPlayingTime.value).format( |
| | | "YYYY-MM-DD HH:mm:ss" |
| | | ); |
| | | EventBus.emit("time-update", currentPlayingTime.value); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | // æ¶é´æ è®°çæ |
| | | function generateTimeMarkers(timestamps) { |
| | | if (!timestamps || timestamps.length === 0) return []; |
| | | const sorted = [...timestamps].sort((a, b) => dayjs(a).diff(dayjs(b))); |
| | | const interval = Math.floor( |
| | | dayjs(sorted.at(-1)).diff(dayjs(sorted[0]), "second") / 4 |
| | | ); |
| | | return Array.from({ length: 5 }, (_, i) => |
| | | dayjs(sorted[0]) |
| | | .add(i * interval, "second") |
| | | .format("MM-DD HH:mm:ss") |
| | | ); |
| | | } |
| | | |
| | | watch( |
| | | () => waterTimestamps.value, |
| | | (newTimestamps) => { |
| | | if (newTimestamps.length > 0) |
| | | timeMarkers.value = generateTimeMarkers(newTimestamps); |
| | | }, |
| | | { immediate: true } |
| | | ); |
| | | |
| | | const jsonFetch = ref(null); |
| | | const currentReplayIndex = ref(0); // å½åææ¾çrePlayListç´¢å¼ |
| | | // æå为ç¬ç«å½æ° |
| | | async function initializeSimulationData(replayItem = null) { |
| | | try { |
| | | const schemeInfo = selectedScheme.value; |
| | | serviceInfo = schemeInfo.serviceName; |
| | | |
| | | if (schemeInfo.type == 2) { |
| | | 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; |
| | | jsonFetch.value = null; |
| | | } |
| | | |
| | | // console.log('è·åå°ç serviceName:', serviceInfo); |
| | | // æ ¹æ® layer.json è·åæ¶é´è½´ä¿¡æ¯ |
| | | const { |
| | | waterTimestamps: timestamps, |
| | | watersMaxHeight, |
| | | watersMinHeight, |
| | | } = await fetchWaterSimulationData(serviceInfo, jsonFetch.value); |
| | | |
| | | console.log( |
| | | "å½åæ¹æ¡ä¸çæå¤§æ°´ä½æ·±åº¦åæå°æ°´ä½æ·±åº¦", |
| | | watersMaxHeight, |
| | | watersMinHeight |
| | | ); |
| | | const waterInfoArr = [ |
| | | watersMaxHeight, |
| | | maxRainValue.value, |
| | | averageRainIntensity.value, |
| | | ]; |
| | | schemWaterInfo.value = waterInfoArr; |
| | | // æ´æ°æ¶é´è½´ç¸å
³æ°æ® |
| | | if (timestamps) { |
| | | frameNum.value = timestamps.length; |
| | | waterTimestamps.value = timestamps; |
| | | updateTimelineRange(); |
| | | timeMarkers.value = generateTimeMarkers(timestamps); |
| | | sendCurrentPlayingTime.value = timestamps[0]; |
| | | currentPlayingTime.value = dayjs(timestamps[0]).format( |
| | | "YYYY-MM-DD HH:mm:ss" |
| | | ); |
| | | } |
| | | minFlowRate = watersMinHeight; |
| | | maxFlowRate = watersMaxHeight; |
| | | } catch (error) { |
| | | console.error("Error loading water simulation data:", error); |
| | | ElMessage({ |
| | | message: "é鍿°æ®åºéï¼è¯·éæ°æ°å»ºæ¨¡ææ¹æ¡ï¼", |
| | | type: "warning", |
| | | }); |
| | | } |
| | | } |
| | | // ææ¾å®æåçåè° |
| | | function handlePlayFinished() { |
| | | if (selectedScheme.value.type !== 2) return; |
| | | finishPlay.value = false; |
| | | currentReplayIndex.value++; |
| | | |
| | | if (currentReplayIndex.value < simStore.rePlayList.length) { |
| | | // èªå¨ææ¾ä¸ä¸ä¸ª |
| | | initializeSimulationData(simStore.rePlayList[currentReplayIndex.value]); |
| | | togglePlay(); |
| | | shouldAutoPlay.value = false; |
| | | } else { |
| | | // ææé¡¹ç®ææ¾å®æ |
| | | currentReplayIndex.value = 0; // éç½®ç´¢å¼ |
| | | isPlaying.value = false; // åæ¢ææ¾ |
| | | emit("timeUpdate", 100); // 卿æé¡¹ç®ææ¾å®æ¯å触å |
| | | } |
| | | } |
| | | |
| | | // ç嬿æ¾å®æäºä»¶ |
| | | watch( |
| | | () => finishPlay.value, |
| | | (newVal) => { |
| | | if ( |
| | | newVal && |
| | | selectedScheme.value.type === 2 && |
| | | simStore.rePlayList.length > 0 |
| | | ) { |
| | | handlePlayFinished(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | const shouldAutoPlay = ref(false); |
| | | // çå¬ layerDate åååæ è®°åå¤ææ¾ |
| | | watch( |
| | | () => layerDate.value, |
| | | async (newVal) => { |
| | | if (selectedScheme.value.type === 2 && newVal) { |
| | | shouldAutoPlay.value = true; |
| | | } |
| | | }, |
| | | { deep: true } |
| | | ); |
| | | // çå¾
finishPlay æåååææ¾ |
| | | watchEffect(() => { |
| | | if (shouldAutoPlay.value && finishPlay.value && !isPlaying.value) { |
| | | initializeSimulationData(); |
| | | togglePlay(); |
| | | shouldAutoPlay.value = false; |
| | | } |
| | | }); |
| | | |
| | | // æè½½æ¶è°ç¨ |
| | | onMounted(async () => { |
| | | // å 为è¿ä¸ªå½æ°å®æ¶æ¨¡æçå¬ä¹éè¦ä½¿ç¨ï¼æä»¥å°è£
äºä¸ä¸ªå½æ° |
| | | await initializeSimulationData(); |
| | | }); |
| | | |
| | | // æ ¹æ®è¿åæ°æ®ç个æ°å»æ¸²ææ¶é´è½´ |
| | | function updateTimelineRange() { |
| | | if (waterTimestamps.value.length > 0) { |
| | | const [first, last] = [ |
| | | waterTimestamps.value[0], |
| | | waterTimestamps.value.at(-1), |
| | | ]; |
| | | duration.value = (last - first) / 1000; // 毫ç§è½¬ç§ |
| | | } |
| | | } |
| | | onBeforeUnmount(() => { |
| | | stopPlayback(); |
| | | destoryWaterPrimitive(); |
| | | }); |
| | | |
| | | const { endSimulate } = inject("simulateActions"); |
| | | |
| | | async function handleBack() { |
| | | // 宿¶æ¨¡æå¼¹çªç¡®è®¤æ¯è¿åæ¹æ¡åè¡¨è¿æ¯åæ¢æ¨¡æ |
| | | if (selectedScheme.value.type === 2) { |
| | | try { |
| | | await ElMessageBox.confirm("æ¹æ¡æªåæ¢æ¶ç»ææ¨¡æåï¼åå°å°åæ¢è®¡ç®", { |
| | | confirmButtonText: "ç»ææ¨¡æ", |
| | | cancelButtonText: "è¿åå表", |
| | | type: "warning", |
| | | }); |
| | | |
| | | const res = await stopSim(selectedScheme.value.id); |
| | | if (res.code == 404) { |
| | | ElMessage.warning("该æå¡å·²åæ¢"); |
| | | } else { |
| | | ElMessage.success("æå¡æ£å¨åæ¢ä¸"); |
| | | } |
| | | } catch (error) { |
| | | // ç¨æ·ç¹å»äºãè¿åå表ãæè
åºç°é误 |
| | | // return; |
| | | } |
| | | } |
| | | |
| | | // ä¸ç®¡ type æ¯ä¸æ¯ 2ï¼æç»é½æ§è¡ç»ææ¨¡ææä½ |
| | | endSimulation(); |
| | | } |
| | | |
| | | async function endSimulation() { |
| | | clearAllPoints(); |
| | | simStore.openDia = true; |
| | | simStore.crossSection = []; |
| | | // ç»ææ¨¡æä¹åæ¸
é¤layerå表 |
| | | simStore.rePlayList = []; |
| | | 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> |
| | | <style scoped> |
| | | .timeline-container { |
| | | display: flex; |
| | | /* align-items: center; */ |
| | | justify-content: space-between; |
| | | position: absolute; |
| | | bottom: 10%; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | z-index: 99; |
| | | width: 44%; |
| | | height: 10%; |
| | | /* background-color: #1a2634; */ |
| | | background: url("@/assets/img/menubar/bar.png"); |
| | | background-size: 100% 100%; |
| | | color: white; |
| | | /* border-radius: 8px; */ |
| | | font-family: Arial, sans-serif; |
| | | padding: 0 25px; |
| | | } |
| | | |
| | | .controls { |
| | | display: flex; |
| | | /* align-items: center; */ |
| | | margin: 25px 25px 10px 0; |
| | | } |
| | | |
| | | .control-btn { |
| | | background: none; |
| | | border: none; |
| | | color: white; |
| | | font-size: 16px; |
| | | cursor: pointer; |
| | | margin-right: 10px; |
| | | width: 30px; |
| | | height: 30px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border-radius: 50%; |
| | | |
| | | img { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | |
| | | .control-btn:hover { |
| | | background-color: rgba(255, 255, 255, 0.1); |
| | | } |
| | | |
| | | .play-btn { |
| | | background-color: #4a90e2; |
| | | width: 36px; |
| | | height: 36px; |
| | | } |
| | | |
| | | .speed-control { |
| | | position: relative; |
| | | cursor: pointer; |
| | | /* padding: 5px 10px; */ |
| | | border-radius: 4px; |
| | | background-color: rgba(255, 255, 255, 0.1); |
| | | width: 36px; |
| | | height: 36px; |
| | | line-height: 36px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .speed-menu { |
| | | position: absolute; |
| | | top: 100%; |
| | | left: 0; |
| | | background-color: #2a3a4a; |
| | | border-radius: 4px; |
| | | z-index: 10; |
| | | width: 60px; |
| | | } |
| | | |
| | | .speed-menu div { |
| | | /* padding: 5px 5px; */ |
| | | text-align: center; |
| | | } |
| | | |
| | | .speed-menu div:hover, |
| | | .speed-menu div.active { |
| | | /* background-color: #4a90e2; */ |
| | | background-color: rgba(127, 255, 212, 0.5); |
| | | } |
| | | |
| | | .timeline { |
| | | margin-top: 10px; |
| | | position: relative; |
| | | flex: 0.9; |
| | | } |
| | | |
| | | .dates { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .date-label { |
| | | font-size: 14px; |
| | | color: #fff; |
| | | } |
| | | |
| | | .timeline-track { |
| | | height: 8px; |
| | | background-color: rgba(255, 255, 255, 0.1); |
| | | border-radius: 4px; |
| | | position: relative; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .timeline-progress { |
| | | height: 100%; |
| | | background-color: #4a90e2; |
| | | border-radius: 4px; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | } |
| | | |
| | | .timeline-cursor { |
| | | width: 12px; |
| | | height: 12px; |
| | | background-color: white; |
| | | border-radius: 50%; |
| | | position: absolute; |
| | | top: 50%; |
| | | transform: translate(-50%, -50%); |
| | | z-index: 2; |
| | | } |
| | | |
| | | .time-markers { |
| | | position: absolute; |
| | | width: 100%; |
| | | top: 20px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .time-marker { |
| | | position: absolute; |
| | | font-size: 12px; |
| | | color: #fff; |
| | | text-align: center; |
| | | width: 25%; |
| | | white-space: nowrap; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | } |
| | | |
| | | /* .date-part { |
| | | margin-bottom: 2px; |
| | | } */ |
| | | .time-part { |
| | | font-size: 11px; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .current-date { |
| | | margin-bottom: 5px; |
| | | font-size: 15px; |
| | | color: #fff; |
| | | transform: translateX(-8%); |
| | | } |
| | | |
| | | .scale-markers { |
| | | position: absolute; |
| | | width: 100%; |
| | | height: 10px; |
| | | top: 10px; |
| | | } |
| | | |
| | | .scale-marker { |
| | | position: absolute; |
| | | width: 3px; |
| | | height: 6px; |
| | | background-color: rgba(255, 255, 255, 1); |
| | | transform: translateX(-50%); |
| | | } |
| | | </style> |