guonan
2025-07-01 62c24bfb0f54998fceeb3b37eb14a74e6bfd83e9
修改小时雨强
echart暂停播放的问题
威胁人房户
定时器问题
已修改4个文件
449 ■■■■■ 文件已修改
src/components/monifangzhen/echartInfo.vue 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monifangzhen/schemeCard.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/tools/DebuffDetail.vue 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/left/CitySim.vue 167 ●●●●● 补丁 | 查看 | 原始文档 | 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数字日期