From f373e0c0797e1800bf066fdfbb748bb9242230f6 Mon Sep 17 00:00:00 2001 From: wangjuncheng <1> Date: 星期二, 08 七月 2025 17:46:02 +0800 Subject: [PATCH] 泥位计 --- src/components/menu/TimeLine copy.vue | 1130 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 957 insertions(+), 173 deletions(-) diff --git a/src/components/menu/TimeLine copy.vue b/src/components/menu/TimeLine copy.vue index 43dcd07..c685395 100644 --- a/src/components/menu/TimeLine copy.vue +++ b/src/components/menu/TimeLine copy.vue @@ -17,7 +17,7 @@ class="fas fa-step-forward" /> </div> - <div class="speed-control"> + <div class="speed-control" v-show="speedShow"> <div @click="toggleSpeedMenu">{{ playbackRate }}X</div> <div class="speed-menu" v-show="showSpeedMenu"> <div @@ -34,12 +34,23 @@ <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) }} + <!-- {{ 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"> @@ -51,18 +62,61 @@ 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%)' }" > - {{ time }} + <div class="date-part">{{ time.split(" ")[0] }}</div> + <div class="time-part">{{ time.split(" ")[1] }}</div> </div> </div> </div> </div> - <el-button @click="handleBack" style="margin-top: 26px;margin-left: 30px;margin-right: 10px;">缁撴潫妯℃嫙</el-button> + <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> @@ -70,20 +124,44 @@ import { ref, computed, - onUnmounted, 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 } from "@/utils/water"; -import { getRainfall } from "@/api/index"; -import { EventBus } from "@/eventBus"; // 寮曞叆浜嬩欢鎬荤嚎 -import { ElMessage } from 'element-plus' +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, ElMessageBox } from "element-plus"; +// 鐘舵�佺鐞嗗櫒 +import { useSimStore } from "@/store/simulation"; +import { storeToRefs } from "pinia"; +const simStore = useSimStore(); +const { selectedScheme, frameNum, layerDate } = storeToRefs(simStore); - +const emit = defineEmits([ + "timeUpdate", + "isPlaying", + "playbackFinished", + "isColorRender", +]); +// 瀹氫箟props const props = defineProps({ waterSimulateParams: { type: Object, @@ -93,229 +171,900 @@ }, }); // 鍝嶅簲寮忕姸鎬� +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(86400); // 涓�澶╃殑绉掓暟 -const duration = ref(470); // 涓�澶╃殑绉掓暟 +const duration = ref(60); // 涓�澶╃殑绉掓暟 const playbackRate = ref(1); const playbackRates = ref([1, 2, 4, 8]); const showSpeedMenu = ref(false); +const speedShow = ref(false); -const timeMarkers = ref([ - "00:00", - "03:19", - "06:39", - "09:59", - "13:19", - "16:39", - "19:59", - "23:19", -]); +const waterTimestamps = ref([]); // 瀛樺偍鏃堕棿杞存暟鎹� +const timeMarkers = ref([]); const timelineTrack = ref(null); - -const startDate = computed(() => { - return dayjs(props.waterSimulateParams.date[0]); -}); -const endDate = computed(() => { - return dayjs(props.waterSimulateParams.date[1]); -}); +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(() => { - return (currentTime.value / duration.value) * 100; -}); +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 visibleDates = computed(() => { - // 鐢熸垚鏃堕棿杞翠笂鏄剧ず鐨勬棩鏈� - const dates = []; - const currentDateValue = dayjs(startDate.value); - const endDateValue = dayjs(endDate.value); - - // let tempDate = currentDateValue - // while (tempDate.isSame(endDateValue) || tempDate.isBefore(endDateValue)) { - // dates.push(tempDate.toDate()) - // tempDate = tempDate.add(1, 'day') - // } - - return [currentDateValue, endDateValue]; -}); - -const currentTimeFormatted = computed(() => { - return formatTime(currentTime.value); -}); - +// 鎴戦渶瑕佸姞涓�涓垽鏂� +const finishPlay = ref(false); +// 鎾斁鎺у埗 const togglePlay = () => { - // 濡傛灉褰撳墠鏄仠姝㈢姸鎬佷笖宸茬粡鎾斁瀹屾瘯锛岀偣鍑绘椂閲嶇疆鏃堕棿 - if (!isPlaying.value && currentTime.value >= duration.value) { + // 杩欓噷搴旇鍐嶈瀹氬嚑涓檺鍒讹紝濡傛灉缂哄皯浠�涔堟暟鎹紝鏃犳硶杩涜浠跨湡 + if (!isPlaying.value && currentTime.value >= duration.value) currentTime.value = 0; - emit("timeUpdate", progressPercentage.value); - // earthCtrl.environment.disableEffect("rain"); - } isPlaying.value = !isPlaying.value; emit("isPlaying", isPlaying.value); if (isPlaying.value) { startPlayback(); - // 濡傛灉鏄粠澶村紑濮嬫挱鏀� - if (currentTime.value === 0) { - emit("playbackFinished", false); + + 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 = () => { - // earthCtrl.environment.showEffect("rain"); clearInterval(playInterval); - playInterval = setInterval(() => { - currentTime.value += 600 * playbackRate.value; - // emit("playbackFinished", true); - if (currentTime.value >= duration.value) { - emit("playbackFinished", false); + if (selectedScheme.value.type === 2) { + // 绫诲瀷涓� 2锛氭瘡 5 绉掕烦鍔ㄤ竴娆� + playInterval = setInterval(() => { + const fiveSeconds = 5; + const totalDuration = duration.value; // 鎬绘椂闀匡紙绉掞級 - currentTime.value = duration.value; // 鍋滃湪鏈�鍚庝竴甯� - stopPlayback(); // 鍋滄鎾斁 - isPlaying.value = false; // 鏇存柊鎾斁鐘舵�� - emit("isPlaying", isPlaying.value); // 閫氱煡鎾斁鐘舵�佸彉鍖� - emit("playbackFinished", true); // 閫氱煡鎾斁瀹屾垚 - emit("timeUpdate", progressPercentage.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(); + }, 5000); // 姣� 5 绉掓墽琛屼竴娆� + } else { + // 杩欓噷闈㈣繕鏄綘鐨勬挱鏀句唬鐮侊紝涓婇潰鐨刬f涓槸浜旂閽熻烦鍔ㄤ竴娆$殑瀹炴椂妯℃嫙 + 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); + rainTotalInfo.value = rainfallList; + calculateTimeStep(rainTotalInfo.value); + // 浣跨敤绀轰緥 + timeStepInfo = calculateTimeStep(rainTotalInfo.value); + // 鎻愬彇 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; + } + } + // 榛樿鏃犻洦鐘舵�� - emit("timeUpdate", progressPercentage.value); - }, 1000); + 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; +} +// ============================================================================ +// 浼樺寲鏂瑰紡锛屽彲浠ユ眰鍑烘暣涓椂闂磋酱涓婏紝绗竴娆¢亣鍒拌繖鍏釜闃堝�煎緱鏃堕棿鐐癸紝鐒跺悗鍒嗘椂闂存鏄剧ず锛宎鏃堕棿鍐呮樉绀虹姸鎬�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 { + // 姝e父鏃堕棿鍓嶈繘鏃讹紝淇濇寔娓愯繘鍙樺寲 + 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( + // `姝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 = () => { - // earthCtrl.environment.disableEffect("rain"); clearInterval(playInterval); }; const skipForward = () => { - // 鍚戝墠璺宠浆10鍒嗛挓 - currentTime.value = Math.min(currentTime.value + 600, duration.value); - emit("timeUpdate", progressPercentage.value); + 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 = () => { - // 鍚戝悗璺宠浆10鍒嗛挓 - currentTime.value = Math.max(currentTime.value - 600, 0); - emit("timeUpdate", progressPercentage.value); + 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 toggleSpeedMenu = () => { - showSpeedMenu.value = !showSpeedMenu.value; -}; - +// 璁剧疆鎾斁閫熺巼 const setPlaybackRate = (rate) => { + isColorRenderEnabled.value = false; playbackRate.value = rate; showSpeedMenu.value = false; - - if (isPlaying.value) { - stopPlayback(); - startPlayback(); - } -}; - -const formatTime = (seconds) => { - const hours = Math.floor(seconds / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - const secs = Math.floor(seconds % 60); - - return `${hours.toString().padStart(2, "0")}:${minutes - .toString() - .padStart(2, "0")}:${secs.toString().padStart(2, "0")}`; -}; - -const formatDate = (date) => { - return dayjs(date).format("YYYY-MM-DD"); -}; - -const seekToPosition = (event) => { - const rect = timelineTrack.value.getBoundingClientRect(); - const clickPosition = event.clientX - rect.left; - const percentage = clickPosition / rect.width; - - currentTime.value = percentage * duration.value; + // 鍋滄褰撳墠鎾斁 + 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 rainFallData = ref([]); -function getRainfallData() { - getRainfall().then((res) => { - // rainFallData.value = res.data.map(item => { - // return dayjs(item.time).format("HH:mm") - // }) +// 杈呭姪鍑芥暟锛氭壘鍒版渶鎺ヨ繎鐨勬椂闂存埑绱㈠紩 +function findClosestTimestampIndex(currentTimeValue) { + if (waterTimestamps.value.length === 0) return 0; - // const rainfallData = res.data.map(item => { - // return item.rainfall - // }) - rainFallData.value = res.data; - }); -} + // 璁$畻褰撳墠鏃堕棿瀵瑰簲鐨勬绉掓椂闂存埑 + const baseTime = waterTimestamps.value[0]; + const currentTimestamp = baseTime + currentTimeValue * 1000; -let mockTimer = null; -let currentRainfall = ref(0.0001); -function randomMockWater() { - let delay = (3 / playbackRate.value) * 1000; - if (delay < 1000) { - delay = 1000; - } - if (mockTimer) { - clearImmediate(mockTimer); - mockTimer = null; - } - mockTimer = setTimeout(() => { - const rainfall = rainFallData.value.find( - (item) => - dayjs(item.time).format("HH:mm:ss") == currentTimeFormatted.value - ); - if (rainfall && rainfall.total) { - // console.log(rainfall.total); + // 鎵惧埌鏈�鎺ヨ繎鐨� timestamp 绱㈠紩 + let closestIndex = 0; + let minDiff = Infinity; - createWaterPrimitive(rainfall.total / 50000); + waterTimestamps.value.forEach((timestamp, index) => { + const diff = Math.abs(timestamp - currentTimestamp); + if (diff < minDiff) { + minDiff = diff; + closestIndex = index; } - }, delay); + }); + + return closestIndex; } +watch( + () => selectedScheme.value, + (newVal) => { + if (newVal) { + console.log("閫変腑鏂规宸叉敼鍙�:", newVal); + } + } +); watch( () => currentTime.value, () => { - randomMockWater(); + 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); + } } ); -// 瀹氫箟缁勪欢浜嬩欢 -const emit = defineEmits(["timeUpdate", "isPlaying", "playbackFinished"]); +// 鏃堕棿鏍囪鐢熸垚 +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") + ); +} -// 鍒濆鍖栨椂瑙﹀彂涓�娆℃椂闂存洿鏂帮紝纭繚鐖剁粍浠惰兘鑾峰彇鍒濆鏃堕棿 -onMounted(() => { - getRainfallData(); - emit("timeUpdate", progressPercentage.value); +watch( + () => waterTimestamps.value, + (newTimestamps) => { + if (newTimestamps.length > 0) + timeMarkers.value = generateTimeMarkers(newTimestamps); + }, + { immediate: true } +); + +const jsonFetch = ref(null); + +// 鎻愬彇涓虹嫭绔嬪嚱鏁� +async function initializeSimulationData(force = false) { + try { + const schemeInfo = selectedScheme.value; + serviceInfo = schemeInfo.serviceName; + + if (schemeInfo.type == 2) { + speedShow.value = false; + jsonFetch.value = layerDate.value; + } 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 + ); + + // 鏇存柊鏃堕棿杞寸浉鍏虫暟鎹� + 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", + }); + } +} + +// 鎸傝浇鏃惰皟鐢� +onMounted(async () => { + // 鍥犱负杩欎釜鍑芥暟瀹炴椂妯℃嫙鐩戝惉涔熼渶瑕佷娇鐢紝鎵�浠ュ皝瑁呬簡涓�涓嚱鏁� + await initializeSimulationData(); }); + +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; + } +}); + +// 鏍规嵁杩斿洖鏁版嵁鐨勪釜鏁板幓娓叉煋鏃堕棿杞� +function updateTimelineRange() { + if (waterTimestamps.value.length > 0) { + const [first, last] = [ + waterTimestamps.value[0], + waterTimestamps.value.at(-1), + ]; + duration.value = (last - first) / 1000; // 姣杞 + } +} + onBeforeUnmount(() => { stopPlayback(); - let delay = (3 / playbackRate.value) * 1000; - - setTimeout(() => { - destoryWaterPrimitive(); - }, delay); + destoryWaterPrimitive(); }); -const { startSimulate, endSimulate } = inject("simulateActions"); -// 杩斿洖鎸夐挳鐐瑰嚮浜嬩欢 -function handleBack() { - ElMessage({ - message: '妯℃嫙杩涚▼姝e湪鍏抽棴涓�...', - type: 'success', - }) + +const { endSimulate } = inject("simulateActions"); + +async function handleBack() { + // 瀹炴椂妯℃嫙寮圭獥纭鏄繑鍥炴柟妗堝垪琛ㄨ繕鏄仠姝㈡ā鎷� + if (selectedScheme.value.type === 2) { + try { + await ElMessageBox.confirm("缁撴潫妯℃嫙", { + confirmButtonText: "缁х画妯℃嫙", + cancelButtonText: "缁撴潫妯℃嫙", + type: "warning", + }); + // 鐢ㄦ埛鐐瑰嚮浜嗙‘璁わ紝杩欓噷涓嶆墽琛屼换浣曟搷浣滐紝浠呭叧闂璇濇 + } catch (error) { + 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: "妯℃嫙杩涚▼姝e湪鍏抽棴涓�...", type: "success" }); } </script> - <style scoped> .timeline-container { display: flex; @@ -326,8 +1075,8 @@ left: 50%; transform: translateX(-50%); z-index: 99; - width: 878px; - height: 108px; + width: 44%; + height: 10%; /* background-color: #1a2634; */ background: url("@/assets/img/menubar/bar.png"); background-size: 100% 100%; @@ -396,7 +1145,7 @@ } .speed-menu div { - padding: 5px 10px; + /* padding: 5px 5px; */ text-align: center; } @@ -407,9 +1156,9 @@ } .timeline { - margin-top: 20px; + margin-top: 10px; position: relative; - flex: 1; + flex: 0.9; } .dates { @@ -452,17 +1201,52 @@ } .time-markers { - display: flex; - justify-content: space-between; position: absolute; width: 100%; - top: 15px; - color: #fff; + 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