src/components/menu/TimeLine.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/monifangzhen/schemeCard.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/tools/DebuffDetail.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/components/menu/TimeLine.vue
@@ -2,20 +2,30 @@ <div class="timeline-container"> <div class="controls"> <div class="control-btn" @click="skipBackward"> <img src="@/assets/img/timeline/left.png" class="fas fa-step-backward" /> <img src="@/assets/img/timeline/left.png" class="fas fa-step-backward" /> </div> <div class="control-btn play-btn" @click="togglePlay"> <img v-show="isPlaying" src="@/assets/img/timeline/stop.png" /> <img v-show="!isPlaying" src="@/assets/img/timeline/start.png" /> </div> <div class="control-btn" @click="skipForward"> <img src="@/assets/img/timeline/right.png" class="fas fa-step-forward" /> <img src="@/assets/img/timeline/right.png" class="fas fa-step-forward" /> </div> <div class="speed-control"> <div class="speed-control" v-show="speedShow"> <div @click="toggleSpeedMenu">{{ playbackRate }}X</div> <div class="speed-menu" v-show="showSpeedMenu"> <div v-for="rate in playbackRates" :key="rate" @click.capture="setPlaybackRate(rate)" :class="{ active: playbackRate === rate }"> <div v-for="rate in playbackRates" :key="rate" @click.capture="setPlaybackRate(rate)" :class="{ active: playbackRate === rate }" > {{ rate }}X </div> </div> @@ -25,19 +35,33 @@ <div class="timeline"> <div class="dates"> <div class="current-date">当前播放时间:{{ currentPlayingTime }}</div> <div v-for="(date, index) in visibleDates" :key="index" class="date-label"> <div v-for="(date, index) in visibleDates" :key="index" class="date-label" > <!-- {{ formatDate(date) }} --> </div> <div> 专题渲染: <el-switch v-model="isColorRenderEnabled" @change="handleColorRenderChange" style="margin-top: -3px" :disabled="!isPlaying || !isWaterPrimitiveCreated" /> <el-switch v-model="isColorRenderEnabled" @change="handleColorRenderChange" style="margin-top: -3px" :disabled="!isPlaying || !isWaterPrimitiveCreated" /> <!-- active-text="开" inactive-text="关" --> </div> </div> <div class="timeline-track" ref="timelineTrack" @click="seekToPosition"> <div class="timeline-progress" :style="{ width: progressPercentage + '%' }"></div> <div class="timeline-cursor" :style="{ left: progressPercentage + '%' }"></div> <div class="timeline-progress" :style="{ width: progressPercentage + '%' }" ></div> <div class="timeline-cursor" :style="{ left: progressPercentage + '%' }" ></div> <div class="scale-markers"> <div class="scale-marker" style="left: 0%"></div> <div class="scale-marker" style="left: 25%"></div> @@ -46,8 +70,12 @@ <div class="scale-marker" style="left: 100%"></div> </div> <div class="time-markers"> <div v-for="(time, index) in timeMarkers" :key="index" class="time-marker" :style="{ left: `${index * 25}%`, transform: 'translateX(-50%)' }"> <div v-for="(time, index) in timeMarkers" :key="index" class="time-marker" :style="{ left: `${index * 25}%`, transform: 'translateX(-50%)' }" > <div class="date-part">{{ time.split(" ")[0] }}</div> <div class="time-part">{{ time.split(" ")[1] }}</div> </div> @@ -56,27 +84,38 @@ </div> <div> <div style="display: flex"> <ratelevel ref="ratelevelRef" :playing-time="sendCurrentPlayingTime" @finish-calculation="handleFinishCalculation" style=" <ratelevel ref="ratelevelRef" :playing-time="sendCurrentPlayingTime" @finish-calculation="handleFinishCalculation" style=" margin-top: 12px; margin-left: 28px; margin-right: 10px; justify-content: flex-end; " /> <crossanalysis ref="crossRef" style=" " /> <crossanalysis ref="crossRef" style=" margin-top: 12px; margin-left: 16px; margin-right: 20px; justify-content: flex-end; " /> " /> </div> <el-button @click="handleBack" style=" <el-button @click="handleBack" style=" margin-top: 3px; margin-left: 28px; margin-right: 10px; width: 75%; height: 30%; ">结束模拟</el-button> " >结束模拟</el-button > </div> </div> </template> @@ -143,13 +182,15 @@ 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 rainTotalInfo = ([]); let rainTotalInfo = []; const isRainEnabled = ref(false); const rainParams = reactive({ rainSize: 0.5, @@ -259,8 +300,11 @@ currentTime.value = (nextTimestamp - baseTimestamp) / 1000; // 触发更新 updateWaterColorByTime() updateWeatherByProgress(); if (selectedScheme.value.type !== 2) { updateWaterColorByTime(); updateWeatherByProgress(); } const progress = currentTime.value / duration.value; emit("timeUpdate", progress * 100); }, 1000 / playbackRate.value); // 根据播放速率调整间隔 @@ -303,7 +347,7 @@ const rainfallList = data.rainfalls; console.log("最终的 rainfallList:", rainfallList); rainTotalInfo.value = rainfallList rainTotalInfo.value = rainfallList; // 提取 intensity 值 rainFallValues.value = rainfallList.map((r) => r.intensity); @@ -373,7 +417,7 @@ } } // 默认无雨状态 return { name: "无雨", size: 0.3, speed: 10, density: 10, color: "#F0F8FF" }; } // 根据播放进度更新天气效果(已优化) @@ -393,20 +437,20 @@ const nextTotal = nextData.total; const total = currentTotal + (nextTotal - currentTotal) * alpha; // 根据 total 设置颜色 let color = '#D4F2E7'; // 默认蓝色 let color = "#D4F2E7"; // 默认蓝色 if (total >= 150) { color = '#663300'; // 黄 - 大雨 color = "#663300"; // 黄 - 大雨 } else if (total >= 125) { color = '#B26633'; // 黄绿 - 中雨 color = "#B26633"; // 黄绿 - 中雨 } else if (total >= 100) { color = '#CC9966'; // 绿 - 中雨 color = "#CC9966"; // 绿 - 中雨 } else if (total >= 75) { color = '#CCE5FF'; // 青绿 - 小雨 color = "#CCE5FF"; // 青绿 - 小雨 } else if (total >= 50) { color = '#99CCFF'; // 天蓝 - 小雨 color = "#99CCFF"; // 天蓝 - 小雨 } else if (total >= 25) { color = '#66B3FF'; // 浅蓝 - 微量 color = "#66B3FF"; // 浅蓝 - 微量 } // console.log(`当前 total: ${total.toFixed(2)}, 颜色: ${color}`); // updateWaterColor(color) @@ -426,7 +470,11 @@ // const rainValue = currentRain + (nextRain - currentRain) * alpha; const rainValue = currentRain + (nextRain - currentRain); // 打印当前处理的雨量数据 console.log(`正在处理的雨量数据点: 当前=${currentRain}, 下一个=${nextRain}, 插值后=${rainValue.toFixed(2)}, 索引=${index}`); console.log( `正在处理的雨量数据点: 当前=${currentRain}, 下一个=${nextRain}, 插值后=${rainValue.toFixed( 2 )}, 索引=${index}` ); // 如果当前索引未变化且插值差异不大,跳过重复更新 if (index === lastUsedIndex && Math.abs(rainValue - lastRainValue) < 0.1) { // console.log('由于数据无显著变化,跳过本次更新'); @@ -612,9 +660,15 @@ // 当前方案的所有信息 const schemeInfo = selectedScheme.value; serviceInfo = schemeInfo.serviceName; if (selectedScheme.value.type == 2) { speedShow.value = false; } else { getRainfallData(); speedShow.value = true; } // console.log('获取到的 serviceName:', serviceInfo); getRainfallData(); // 根据layer.json去获取时间轴信息 const { waterTimestamps: timestamps, src/components/monifangzhen/schemeCard.vue
@@ -16,10 +16,6 @@ <span style="color: aquamarine"> {{ item.result === "-1" ? "出错" : item.result || "创建仿真" }} </span> <!-- <span style="color: aquamarine">{{ item.result || "创建仿真" }}</span> --> <!-- <span style="color: aquamarine">{{ statusText[item.status] || "未知" }}</span> --> </p> </div> <div class="cardMenu"> @@ -37,10 +33,7 @@ :selectedScheme="currentScheme" @back="handleBack" /> <flowRateTab v-if="schemeInfoShow"> 123 </flowRateTab> <flowRateTab v-if="schemeInfoShow"> 123 </flowRateTab> </div> <Message @close="close" @@ -81,13 +74,6 @@ selectedId.value = id; } const statusText = { 0: "创建仿真", 1: "预处理", 2: "分析中", 10: "完成", 20: "出错", }; function formatTime(time) { return dayjs(time).format("YYYY-MM-DD HH:mm:ss"); @@ -108,62 +94,80 @@ messageShow.value = false; } function startPlay(item) { // 分析中 if (item.status == 2) { async function startPlay(item) { if (item.status === 2) { ElMessage.warning("当前方案正在分析中,无法进入模拟!"); return; } // 出错 if (item.status == 20) { if (item.status === 20) { ElMessage.error("当前方案分析出错,请重新新建方案!"); return; } // 调用求解器并拿到最新生成的serviceName // 新创建的方案没有状态以及serviceName则执行调用求解器 if (!item.status && !item.serviceName) { getSimStart(item.id).then((res) => { getSimDataById(item.id).then((res) => { item.serviceName = res.data[0].serviceName; simStore.setSelectedScheme(item); console.log(item, "无服务名称"); ElMessage.warning("当前方案正在分析中,请稍后再模拟"); getScheme(); }); }); } else { // 如果是已完成的方案(status == 10) if (item.status === 10) { const flyHeight = item.areaType === 1 ? 100000 : 50000; simStore.setSelectedScheme(item); console.log("有服务名称"); } const flyHeight = ref(100000); const shouldShowFill = false; // 求解器求解完成之后才可以显示时间轴 if (item.status == 10) { // 只有行政区划执行 if (item.areaType == 1) { flyHeight.value = 100000; if (item.areaType === 1) { EventBus.emit("select-geom", { geom: item.geom, flyHeight: flyHeight.value, shouldShowFill: shouldShowFill, flyHeight, shouldShowFill: false, }); } else { // 孙胡沟区域跳转视角 initeWaterPrimitiveView(); } currentScheme.value = item; schemeInfoShow.value = true; emit("closeBtn", false); emit("start"); return; } // 调用求解器(不在实时模拟的情况下) if (!item.status && !item.serviceName && item.type !== 2) { try { await getSimStart(item.id); const res = await getSimDataById(item.id); item.serviceName = res.data[0]?.serviceName || null; simStore.setSelectedScheme(item); ElMessage.warning("当前方案正在分析中,请稍后再模拟"); getScheme(); } catch (e) { console.error("获取模拟数据失败:", e); } return; } // 实时模拟 if (item.type === 2) { try { // 实时模拟调用求解器会直接在接口中返回结果 const ress = await getSimStart(item.id); const res = await getSimDataById(item.id); item.serviceName = res.data[0]?.serviceName || null; simStore.setSelectedScheme(item); getScheme(); if (ress.code === 200) { initeWaterPrimitiveView(); emit("start"); } } catch (e) { console.error("实时模拟获取模拟数据失败:", e); } return; } // 默认情况:有服务名称 simStore.setSelectedScheme(item); } function endPlay() { emit("end"); } function handleBack(value) { if (value === false) { src/components/tools/DebuffDetail.vue
@@ -1,15 +1,15 @@ <template> <div class="detail"> <div class="detail-top">统计分析结果</div> <!-- <div class="detail-btn" @click="showMsg">查看详情</div> --> <div class="detail-close" @click="closeMsg"></div> <div class="detail-context"> <div v-for="(item, key) in detailList" :key="key" class="detail-item"> <div class="detail-name">{{ item.name }}</div> <div class="detail-value">{{ item.value }}</div> </div> </div> </div> <div class="detail"> <div class="detail-top">统计分析结果</div> <!-- <div class="detail-btn" @click="showMsg">查看详情</div> --> <div class="detail-close" @click="closeMsg"></div> <div class="detail-context"> <div v-for="(item, key) in detailList" :key="key" class="detail-item"> <div class="detail-name">{{ item.name }}</div> <div class="detail-value">{{ item.value }}</div> </div> </div> </div> </template> <script> @@ -20,171 +20,176 @@ const { selectedScheme } = storeToRefs(simStore); export default { name: "detail", components: {}, props: { areaName: { type: String, default: "尹家西沟", }, }, data() { return { show: false, detailList: [ { name: "最大雨强:", value: Number(Math.random() * 100).toFixed(2) + " mm/h", }, { name: "平均雨强:", value: Number(Math.random() * 10).toFixed(2) + " mm/h", }, { name: "最大水深:", value: "1.86 m", }, { name: "最大流速:", value: "7 m/s", }, { name: "威胁房屋:", value: "406 间", }, { name: "威胁人口:", value: "145 户", }, { name: "威胁财产:", value: "4872 万元", }, ], } }, mounted() { this.getRainfallData(); // 组件挂载后执行获取雨量数据 }, methods: { 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); } catch (e) { console.error("data 不是有效的 JSON 字符串"); return; } } const rainfallList = data.rainfalls; name: "detail", components: {}, props: { areaName: { type: String, default: "尹家西沟", }, }, data() { return { show: false, detailList: [ { name: "最大雨强:", value: Number(Math.random() * 100).toFixed(2) + " mm/h", }, { name: "平均雨强:", value: Number(Math.random() * 10).toFixed(2) + " mm/h", }, { name: "最大水深:", value: "1.86 m", }, { name: "最大流速:", value: "7 m/s", }, { name: "威胁房屋:", value: "406 间", }, { name: "威胁人口:", value: "145 户", }, { name: "威胁财产:", value: "4872 万元", }, ], }; }, mounted() { this.getRainfallData(); // 组件挂载后执行获取雨量数据 }, methods: { 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); } catch (e) { console.error("data 不是有效的 JSON 字符串"); return; } } // 提取 intensity 值 const rainValues = rainfallList.map(r => r.intensity); const minRain = Math.min(...rainValues); const maxRain = Math.max(...rainValues); const avgRain = rainValues.reduce((sum, val) => sum + val, 0) / rainValues.length; if (selectedScheme.value.type !== 2) { const rainfallList = data.rainfalls; // 更新 detailList 中的“最大雨强”和“平均雨强” this.detailList[0].value = maxRain.toFixed(2) + " mm/h"; // 最大雨强 this.detailList[1].value = avgRain.toFixed(2) + " mm/h"; // 平均雨强 // 提取 intensity 值 const rainValues = rainfallList.map((r) => r.intensity); const minRain = Math.min(...rainValues); const maxRain = Math.max(...rainValues); const avgRain = rainValues.reduce((sum, val) => sum + val, 0) / rainValues.length; console.log('当前方案下最小雨量、最大雨量、平均雨量:', minRain.toFixed(2), maxRain.toFixed(2), avgRain.toFixed(2) ); }, closeMsg() { this.$emit("close") }, showMsg() { this.$emit("open") }, }, } // 更新 detailList 中的“最大雨强”和“平均雨强” this.detailList[0].value = maxRain.toFixed(2) + " mm/h"; // 最大雨强 this.detailList[1].value = avgRain.toFixed(2) + " mm/h"; // 平均雨强 console.log( "当前方案下最小雨量、最大雨量、平均雨量:", minRain.toFixed(2), maxRain.toFixed(2), avgRain.toFixed(2) ); } }, closeMsg() { this.$emit("close"); }, showMsg() { this.$emit("open"); }, }, }; </script> <style lang="less" scoped> .detail { background: url("@/assets/img/tools/messagebg.png"); background-size: 100% 100%; width: 391px; height: 420px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 99; background: url("@/assets/img/tools/messagebg.png"); background-size: 100% 100%; width: 391px; height: 420px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 99; } .detail-top { position: absolute; top: 5px; left: 20px; font-weight: 700; font-size: 18px; font-weight: 700; color: #fff; line-height: 40px; width: 270px; cursor: pointer; position: absolute; top: 5px; left: 20px; font-weight: 700; font-size: 18px; font-weight: 700; color: #fff; line-height: 40px; width: 270px; cursor: pointer; } .detail-close { position: absolute; right: 3px; top: 10px; width: 20px; height: 20px; text-align: center; line-height: 20px; text-align: center; position: absolute; right: 3px; top: 10px; width: 20px; height: 20px; text-align: center; line-height: 20px; text-align: center; font-weight: 700; font-size: 18px; font-weight: 700; color: #fff; cursor: pointer; font-weight: 700; font-size: 18px; font-weight: 700; color: #fff; cursor: pointer; } .detail-context { position: absolute; top: 40px; left: 20px; width: 350px; position: absolute; top: 40px; left: 20px; width: 350px; } .detail-item { height: 23px; margin-top: 15px; margin-left: 10px; height: 23px; margin-top: 15px; margin-left: 10px; } .detail-name { float: left; font-weight: 700; color: #94e0c4; float: left; font-weight: 700; color: #94e0c4; } .detail-value { float: left; color: #e1eee9; float: left; color: #e1eee9; } .detail-btn { background: url("@/assets/img/tools/messagebtn.png") no-repeat; position: absolute; bottom: 60px; right: 60px; width: 105px; height: 26px; text-align: center; color: #fff; cursor: pointer; background: url("@/assets/img/tools/messagebtn.png") no-repeat; position: absolute; bottom: 60px; right: 60px; width: 105px; height: 26px; text-align: center; color: #fff; cursor: pointer; } </style>