From d8e4d7a23117fa2945689e79fc7f66a1b1951825 Mon Sep 17 00:00:00 2001 From: guonan <guonan201020@163.com> Date: 星期二, 24 六月 2025 18:04:48 +0800 Subject: [PATCH] 提交 --- src/components/menu/TimeLine.vue | 989 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 989 insertions(+), 0 deletions(-) diff --git a/src/components/menu/TimeLine.vue b/src/components/menu/TimeLine.vue new file mode 100644 index 0000000..9f3df8d --- /dev/null +++ b/src/components/menu/TimeLine.vue @@ -0,0 +1,989 @@ +<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, +} 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 } from "@/api/trApi.js"; +import { EventBus } from "@/eventBus"; +import { ElMessage } from "element-plus"; +// 鐘舵�佺鐞嗗櫒 +import { useSimStore } from "@/store/simulation"; +import { storeToRefs } from "pinia"; +const simStore = useSimStore(); +const { selectedScheme, frameNum } = storeToRefs(simStore); + +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; +<<<<<<< HEAD +let rainTotalInfo = []; +======= +let timeStepInfo = null; +let rainTotalInfo = ([]); +>>>>>>> 68e0faf021b54ab38bba0b07c76c3d5f43dfc311 +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 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); // 鏍规嵁鎾斁閫熺巼璋冩暣闂撮殧 +}; +// 闄嶉洦鍙樺寲閮ㄥ垎 +// 闄嶉洦鏁版嵁鐩稿叧鍙橀噺 +let rainFallValues = ref([]); // 瀛樺偍鍘熷闄嶉洦閲忔暟鎹� +let minRainValue = ref(Infinity); +let maxRainValue = ref(-Infinity); +// 鑾峰彇闄嶉洦鏁版嵁 +function getRainfallData() { + if (!selectedScheme.value || !selectedScheme.value.data) { + console.warn("selectedScheme 鎴� data 涓嶅瓨鍦�"); + return; + } + // 娉ㄦ剰锛氭湁鏃� data 鍙兘鏄竴涓瓧绗︿覆锛堜緥濡� JSON 瀛楃涓诧級 + let data = selectedScheme.value.data; + // 濡傛灉鏄瓧绗︿覆锛屽垯灏濊瘯瑙f瀽鎴愬璞� + if (typeof data === "string") { + try { + data = JSON.parse(data); + console.log("瑙f瀽鍚庣殑闄嶉洦鏁版嵁锛�", data); + } catch (e) { + console.error("data 涓嶆槸鏈夋晥鐨� JSON 瀛楃涓�"); + return; + } + } + // 鎵撳嵃闄嶉洦寮哄害鐨勫崟浣� + console.log("闄嶉洦寮哄害鐨勫崟浣嶆槸锛�", data.intensityUnit); + // 鏍规嵁 intensityUnit 璋冩暣 rainfalls 涓殑 intensity 鍊� + if (data.intensityUnit === "mm/min") { + data.rainfalls.forEach((r) => (r.intensity *= 60)); + console.log("灏� mm/min 杞崲涓� mm/h 鍚庣殑 rainfalls:", data.rainfalls); + } else if (data.intensityUnit === "mm/5min") { + data.rainfalls.forEach((r) => (r.intensity *= 12)); + console.log("灏� mm/5min 杞崲涓� mm/h 鍚庣殑 rainfalls:", data.rainfalls); + } else if (data.intensityUnit !== "mm/h") { + console.warn("鏈煡鐨� intensity 鍗曚綅锛屾棤娉曡繘琛岃浆鎹�"); + } + + const rainfallList = data.rainfalls; + console.log("鏈�缁堢殑 rainfallList:", rainfallList); +<<<<<<< HEAD + rainTotalInfo.value = rainfallList; +======= + rainTotalInfo.value = rainfallList + calculateTimeStep(rainTotalInfo.value) + // 浣跨敤绀轰緥 + timeStepInfo = calculateTimeStep(rainTotalInfo.value); +>>>>>>> 68e0faf021b54ab38bba0b07c76c3d5f43dfc311 + + // 鎻愬彇 intensity 鍊� + rainFallValues.value = rainfallList.map((r) => r.intensity); + 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; + } + + // 瑙f瀽鏃堕棿瀛楃涓蹭负 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; +} + +function updateWaterColorByTime() { + if (!rainTotalInfo.value || rainTotalInfo.value.length === 0) return; + const progress = currentTime.value / duration.value; + const floatIndex = progress * (rainTotalInfo.value.length - 1); + const index = Math.floor(floatIndex); + const nextIndex = Math.min(index + 1, rainTotalInfo.value.length - 1); + const currentData = rainTotalInfo.value[index]; + const nextData = rainTotalInfo.value[nextIndex]; + // 鍚敤鎻掑�硷紙alpha 骞虫粦杩囨浮锛� + const alpha = floatIndex - index; + const currentTotal = currentData.total; + const nextTotal = nextData.total; + const total = currentTotal + (nextTotal - currentTotal) * alpha; + console.log(`璁$畻寰楀埌鐨勬椂闂存闀夸负: ${timeStepInfo} 灏忔椂`); + // 鏍规嵁 total 璁剧疆棰滆壊 + let color = "#D4F2E7"; // 榛樿钃濊壊 + + if (total >= 150) { +<<<<<<< HEAD + color = "#663300"; // 榛� - 澶ч洦 + } else if (total >= 125) { + color = "#B26633"; // 榛勭豢 - 涓洦 + } else if (total >= 100) { + color = "#CC9966"; // 缁� - 涓洦 + } else if (total >= 75) { + color = "#CCE5FF"; // 闈掔豢 - 灏忛洦 + } else if (total >= 50) { + color = "#99CCFF"; // 澶╄摑 - 灏忛洦 + } else if (total >= 25) { + color = "#66B3FF"; // 娴呰摑 - 寰噺 +======= + color = '#663300'; + } else if (total >= 125) { + color = '#B26633'; + } else if (total >= 100) { + color = '#CC9966'; + } else if (total >= 75) { + color = '#CCE5FF'; + } else if (total >= 50) { + color = '#99CCFF'; + } else if (total >= 25) { + color = '#66B3FF'; +>>>>>>> 68e0faf021b54ab38bba0b07c76c3d5f43dfc311 + } + // console.log(`褰撳墠 total: ${total.toFixed(2)}, 棰滆壊: ${color}`); + // updateWaterColor(color) +} + +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( + `姝e湪澶勭悊鐨勯洦閲忔暟鎹偣: 褰撳墠=${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"); +}; +// 鏃堕棿杞磋烦杞� +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); + + // 鐩存帴鎵惧埌鏈�杩戠殑 timestamp 绱㈠紩 + const closestIndex = findClosestTimestampIndex(targetTime); + const baseTimestamp = waterTimestamps.value[0]; + currentTime.value = + (waterTimestamps.value[closestIndex] - baseTimestamp) / 1000; + + // 鏇存柊姘翠綋妯℃嫙鏃堕棿 + setTimeForWaterSimulation(closestIndex); + if (!isPlaying.value) pauseWaterSimulation(); +}; +// 杈呭姪鍑芥暟锛氭壘鍒版渶鎺ヨ繎鐨勬椂闂存埑绱㈠紩 +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 } +); + +onMounted(async () => { + try { + // 褰撳墠鏂规鐨勬墍鏈変俊鎭� + const schemeInfo = selectedScheme.value; + serviceInfo = schemeInfo.serviceName; + if (selectedScheme.value.type == 2) { + speedShow.value = false; + } else { + getRainfallData(); + speedShow.value = true; + } + + // console.log('鑾峰彇鍒扮殑 serviceName:', serviceInfo); + + // 鏍规嵁layer.json鍘昏幏鍙栨椂闂磋酱淇℃伅 + const { + waterTimestamps: timestamps, + watersMaxHeight, + watersMinHeight, + } = await fetchWaterSimulationData(serviceInfo); + console.log( + "褰撳墠鏂规涓嬬殑鏈�澶ф按浣嶆繁搴﹀拰鏈�灏忔按浣嶆繁搴�", + watersMaxHeight, + watersMinHeight + ); + + // 鐜板湪鏄寜鐓ф�诲叡鏈夊灏戜釜鐐规潵娓叉煋鏃堕棿杞� + 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 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"); +function handleBack() { + 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: "妯℃嫙杩涚▼姝e湪鍏抽棴涓�...", 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> -- Gitblit v1.9.3