src/components/monifangzhen/echartInfo.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/monifangzhen/schemeCard.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/tools/DebuffDetail.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/left/CitySim.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/components/monifangzhen/echartInfo.vue
@@ -450,16 +450,33 @@ } }; // 控制方法:精确控制动画时间,最后一帧在第 90 秒 let fixedFrameNum = null; let startTime = null; // 将startTime移到外层 let elapsedBeforePause = 0; // 记录暂停前已经过去的时间 const startUpdating = () => { if (updateInterval || dataIndex.value >= rainfallData.value.length) return; if (updateInterval || dataIndex.value >= rainfallData.value.length) { console.log("Animation already running or completed"); return; } const totalDuration = simStore.frameNum * 1000; // 90秒 // 如果是首次启动或重新开始 if (fixedFrameNum === null) { fixedFrameNum = simStore.frameNum; elapsedBeforePause = 0; startTime = Date.now(); } else { // 如果是暂停后继续,调整startTime以反映已经过去的时间 startTime = Date.now() - elapsedBeforePause; } const totalDuration = fixedFrameNum * 1000; const totalPoints = rainfallData.value.length; const startTime = Date.now(); const animate = (index = 0) => { const animate = (index) => { if (index >= totalPoints) { console.log("Animation completed"); stopUpdating(); return; } @@ -469,7 +486,7 @@ const delay = Math.max(0, startTime + expectedTime - now); updateInterval = setTimeout(() => { dataIndex.value = index + 1; // 因为是从 0 开始 push 的 dataIndex.value = index; updateData(); animate(index + 1); }, delay); @@ -478,13 +495,26 @@ animate(dataIndex.value); }; // 暂停函数需要记录已经过去的时间 const stopUpdating = () => { clearTimeout(updateInterval); updateInterval = null; if (updateInterval) { clearTimeout(updateInterval); updateInterval = null; // 记录暂停时已经过去的时间 elapsedBeforePause = Date.now() - startTime; } }; // const stopUpdating = () => { // clearTimeout(updateInterval); // updateInterval = null; // }; const resetLoading = () => { stopUpdating(); fixedFrameNum = null; startTime = null; elapsedBeforePause = 0; dataIndex.value = 0; data1.value = [0]; data2.value = [0]; src/components/monifangzhen/schemeCard.vue
@@ -220,9 +220,11 @@ (item) => item.result == "创建仿真" || item.result == "完成" || item.result == "-1" item.result == "-1" || item.result == null ); simAPIStore.shouldPoll = !shouldStop; // 修改 Pinia 状态 console.log(shouldStop, "aaaaaaaaaaaaaaaa"); // 3. 如果需要停止 if (shouldStop) { if (intervalId) { @@ -241,7 +243,6 @@ watch( () => simAPIStore.shouldPoll, (isStarted) => { console.log(isStarted, "定时器"); if (isStarted) { getScheme(); // 首次立即获取一次 intervalId = setInterval(getScheme, 60 * 1000); // 每隔一分钟执行 src/components/tools/DebuffDetail.vue
@@ -12,108 +12,149 @@ </div> </template> <script> // 状态管理器 <script setup> import { ref, onMounted } from "vue"; import { useSimStore } from "@/store/simulation"; import { storeToRefs } from "pinia"; const simStore = useSimStore(); 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 filteredData = simStore.DangerPoint.filter((item) => item.position?.includes("孙胡沟") ); if (selectedScheme.value.type !== 2) { const rainfallList = data.rainfalls; // 提取 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; // 更新 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"); }, // 响应式数据 const detailList = ref([ { name: "最大雨强:", value: (Math.random() * 100).toFixed(2) + " mm/h", }, { name: "平均雨强:", value: (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: "145 人", }, { name: "威胁财产:", value: "4872 万元", }, ]); const updateThreatData = () => { // 筛选 position 包含 "孙胡沟" 的数据 const filteredData = simStore.DangerPoint.filter((item) => item.position?.includes("孙胡沟") ); if (filteredData.length === 0) { console.warn("未找到 position 包含 '孙胡沟' 的数据"); return; } // 初始化累加值 let totalHousehold = 0; // 威胁户数 let totalPerson = 0; // 威胁人数 let totalRoom = 0; // 威胁房数 // 遍历并累加 filteredData.forEach((item) => { totalHousehold += Number(item.threatHouseNum) || 0; totalPerson += Number(item.threatPersonNum) || 0; totalRoom += Number(item.threatRoomNum) || 0; }); // 更新 detailList detailList.value[5].value = `${totalHousehold} 户`; detailList.value[6].value = `${totalPerson} 人`; detailList.value[4].value = `${totalRoom} 间`; console.log("威胁户数:", totalHousehold); console.log("威胁人数:", totalPerson); console.log("威胁房数:", totalRoom); }; // 方法定义 const 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; } } if (selectedScheme.value.type !== 2) { const rainfallList = data.rainfalls; // 提取 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; // 更新 detailList 中的“最大雨强”和“平均雨强” detailList.value[0].value = maxRain.toFixed(2) + " mm/h"; // 最大雨强 detailList.value[1].value = avgRain.toFixed(2) + " mm/h"; // 平均雨强 console.log( "当前方案下最小雨量、最大雨量、平均雨量:", minRain.toFixed(2), maxRain.toFixed(2), avgRain.toFixed(2) ); } }; const closeMsg = () => { // 使用 defineEmits 定义 emit emit("close"); }; const showMsg = () => { emit("open"); }; // 定义 emit const emit = defineEmits(["close", "open"]); // 生命周期钩子 onMounted(() => { console.log(filteredData); updateThreatData(); getRainfallData(); }); </script> <style lang="less" scoped> .detail { background: url("@/assets/img/tools/messagebg.png"); @@ -133,7 +174,6 @@ left: 20px; font-weight: 700; font-size: 18px; font-weight: 700; color: #fff; line-height: 40px; width: 270px; @@ -148,11 +188,8 @@ height: 20px; text-align: center; line-height: 20px; text-align: center; font-weight: 700; font-size: 18px; font-weight: 700; color: #fff; cursor: pointer; } src/views/left/CitySim.vue
@@ -447,15 +447,16 @@ /** * 数据处理主函数 * @param {Array} data - 解析后的原始数据数组,每个元素是一个对象 */ const processData = (data) => { // 检查空数据 // 检查是否为空数据 if (data.length === 0) { ElMessage.warning("文件内容为空!"); return; } // 匹配字段名 // 匹配列名(例如“时间”、“小时雨强”) const columns = matchColumns(data[0]); // 校验必要字段是否存在 @@ -475,22 +476,18 @@ return; } // 时间列校验是否升序 // 校验时间列是否升序排列 if (!isTimeColumnSorted(data, columns.time)) { ElMessage.error("时间列必须按升序排列!"); forms.fileList = []; return; } // 提取单位 forms.intensityUnit = extractUnitFromHeader(columns.intensity); // 提取单位(如 mm/h),若没有则设为空字符串 forms.intensityUnit = extractUnitFromHeader(columns.intensity) || ""; if (!forms.intensityUnit) { forms.intensityUnit = ""; } // 转换 key 名并转换数值类型 forms.rainFallList = data.map((row) => ({ // 将原始数据转换为统一结构的对象数组 const rawRainFallList = data.map((row) => ({ time: row[columns.time], intensity: parseFloat(row[columns.intensity]), total: columns.totalRainfall @@ -498,21 +495,45 @@ : undefined, })); console.log(forms.rainFallList, "解析后的降雨数据"); console.log(rawRainFallList, "原始降雨数据"); // 计算统计信息 const firstTime = parseDateTime(data[0][columns.time]); const lastTime = parseDateTime(data[data.length - 1][columns.time]); // 判断是否为整小时数据(即相邻时间间隔是否为整小时) const isHourlyData = checkIfHourlyData(rawRainFallList); let hourlyRainfallList = []; if (!isHourlyData) { // 如果不是整小时数据,按小时进行聚合处理 hourlyRainfallList = aggregateToHourlyRainfall(rawRainFallList); console.log(hourlyRainfallList, "修正后的小时雨强"); } else { // 如果是整小时数据,直接使用原始雨强值 hourlyRainfallList = rawRainFallList.map((item) => ({ time: item.time, intensity: item.intensity, })); } // 更新 forms.rainFallList,可用于图表显示等用途 forms.rainFallList = hourlyRainfallList; // 计算起始时间和结束时间(毫秒数) const firstTime = parseDateTime(hourlyRainfallList[0]?.time); const lastTime = parseDateTime( hourlyRainfallList[hourlyRainfallList.length - 1]?.time ); // 计算持续时间(单位:小时) const durationSeconds = Math.floor((lastTime - firstTime) / 1000); forms.duration = (durationSeconds / 3600).toFixed(2); // 小时 forms.duration = (durationSeconds / 3600).toFixed(2); // 单位:小时 // 找出最大小时雨强 const maxIntensity = Math.max( ...data .map((row) => parseFloat(row[columns.intensity])) .filter((v) => !isNaN(v)) ...hourlyRainfallList.map((item) => item.intensity).filter((v) => !isNaN(v)) ).toFixed(2); forms.intensity = maxIntensity; // 若有总降雨量列,取出最后一个值作为总降雨量 if (columns.totalRainfall) { const lastTotal = parseFloat(data[data.length - 1][columns.totalRainfall]); forms.rainfall = isNaN(lastTotal) ? 0 : lastTotal.toFixed(2); @@ -521,56 +542,80 @@ } }; // // 处理数据 // const processData = (data) => { // // 1. 检查数据是否为空 // if (data.length === 0) { // ElMessage.warning("文件内容为空!"); // return; // } /** * 检查数据是否为整小时记录 * @param {Array} rainList - 原始降雨数据列表,每个元素包含 time 和 intensity * @returns {boolean} - 是否为整小时数据 */ function checkIfHourlyData(rainList) { if (rainList.length < 2) return true; // 只有一个点,默认视为整小时数据 // // 2. 获取表头(第一列是时间列) // const tableColumns = Object.keys(data[0]); // const timeColumn = tableColumns[0]; // 假设第一列是时间 for (let i = 1; i < rainList.length; i++) { // 解析两个相邻时间点 const time1 = parseDateTime(rainList[i - 1].time); const time2 = parseDateTime(rainList[i].time); // // 3. 校验时间列是否按升序排列 // if (!isTimeColumnSorted(data, timeColumn)) { // ElMessage.error("时间列必须按升序排列!"); // forms.fileList = []; // return; // 终止处理 // } // 计算时间差(分钟) const diffMinutes = Math.abs(time2 - time1) / (1000 * 60); // const intensityColumn = tableColumns[1]; // 雨强列(如 "小时雨强(mm/h)") // // console.log(intensityColumn, "intensityColumnintensityColumnintensityColumn"); // // 3. 提取第二列的单位(如 "(mm/h)" → "mm/h") // const intensityUnit = extractUnitFromHeader(intensityColumn); // forms.intensityUnit = intensityUnit; // 存储单位(可选) // console.log(forms.intensityUnit,'aaaaaaaaaaaaaaaaaaaaa') // 如果时间差不是整小时(不能被60整除),则不是整小时数据 if (diffMinutes % 60 !== 0) { return false; } } // // 4. 如果校验通过,继续处理数据 // forms.rainFallList = transformKeys(data); // console.log(forms.rainFallList, "data"); return true; } // // 5. 计算降雨时长、雨强、累计雨量(原逻辑) // const firstTime = parseDateTime(data[0][timeColumn]); // const lastTime = parseDateTime(data[data.length - 1][timeColumn]); // const timeDuration = Math.floor((lastTime - firstTime) / 1000); // // 降雨时长 // forms.duration = (timeDuration / 3600).toFixed(2); // // 降雨强度 // const maxValue = Math.max( // ...data.map((row) => { // const value = parseFloat(row[tableColumns[1]]); // return isNaN(value) ? -Infinity : value; // }) // ).toFixed(2); // forms.intensity = maxValue; /** * 将任意时间粒度的雨强数据,按小时聚合为“小时雨强” * @param {Array} rainList - 原始数据列表,每个元素包含 time 和 intensity * @returns {Array} - 按小时分组的聚合结果 */ function aggregateToHourlyRainfall(rainList) { const grouped = {}; // 用于临时存储每个小时的数据 // const lastValue = data[data.length - 1][tableColumns[2]]; // forms.rainfall = lastValue; for (const item of rainList) { // 解析时间字符串为时间戳 const timestamp = parseDateTime(item.time); // }; // 如果解析失败,跳过当前项 if (isNaN(timestamp)) { console.warn("无效的时间格式,已跳过", item.time); continue; } // 将时间戳转为 Date 对象以便操作日期 const dt = new Date(timestamp); // 构造年月日+小时键(如:"2024-08-25 14") const year = String(dt.getFullYear()).padStart(4, "0"); const month = String(dt.getMonth() + 1).padStart(2, "0"); // 注意月份从0开始 const date = String(dt.getDate()).padStart(2, "0"); const hour = String(dt.getHours()).padStart(2, "0"); const hourKey = `${year}-${month}-${date} ${hour}`; // 初始化该小时的聚合对象 if (!grouped[hourKey]) { grouped[hourKey] = { time: `${hourKey}:00:00`, // 标准化为整点时间 intensity: 0, }; } // 累加该小时内所有雨强值 grouped[hourKey].intensity += item.intensity; } // 将聚合结果转为数组并保留两位小数 const result = Object.values(grouped).map((item) => ({ time: item.time, intensity: Number(item.intensity.toFixed(2)), })); return result; } /** * 解析日期时间字符串或Excel数字日期,返回时间戳(毫秒数) * @param {string|number} dateString - 日期字符串或Excel数字日期