guonan
2025-05-23 fef12378282c0a8cf44411b079ac20ad4f397817
src/views/left/CitySim.vue
@@ -11,12 +11,17 @@
      重点区域仿真(10m精度)
    </div>
    <div class="forms">
      <el-form :model="forms" label-width="auto" style="max-width: 600px">
      <el-form
        :rules="rules"
        :model="forms"
        label-width="auto"
        style="max-width: 600px"
      >
        <el-form-item label="方案名称:">
          <el-input
            v-model="forms.name"
            style="max-width: 600px"
            placeholder="Please input"
            placeholder="请输入方案名称"
          >
          </el-input>
        </el-form-item>
@@ -36,20 +41,35 @@
            <template #append>mm/h</template>
          </el-upload>
        </el-form-item>
        <el-form-item label="雨强单位" v-if="forms.fileList.length !== 0">
          <el-select
            v-model="forms.intensityUnit"
            placeholder="Select"
            style="max-width: 600px"
          >
            <el-option
              v-for="item in intensityOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item
          label="行政区域:"
          v-if="simStore.selectTab == '行政区划仿真'"
        >
          <el-select
            @change="changeGeom"
            v-model="forms.geom"
            placeholder="Select"
            placeholder="请选择模拟区域"
            style="max-width: 600px"
          >
            <el-option
              v-for="item in options"
              :key="item.value"
              :label="item.label"
              :value="item.value"
              :value="item"
            />
          </el-select>
        </el-form-item>
@@ -58,15 +78,16 @@
          v-if="simStore.selectTab == '重点区域仿真'"
        >
          <el-select
            @change="changeGeom"
            v-model="forms.geom"
            placeholder="Select"
            placeholder="请选择模拟区域"
            style="max-width: 600px"
          >
            <el-option
              v-for="item in options"
              :key="item.value"
              :label="item.label"
              :value="item.value"
              :value="item"
            />
          </el-select>
        </el-form-item>
@@ -75,7 +96,7 @@
          <el-input
            v-model="forms.rainfall"
            style="max-width: 600px"
            placeholder="Please input"
            placeholder="请输入降雨量"
          >
            <template #append>mm</template>
          </el-input>
@@ -104,7 +125,7 @@
            disabled
            v-model="forms.duration"
            style="max-width: 600px"
            placeholder="Please input"
            placeholder="请输入降雨时长"
          >
            <template #append>h</template>
          </el-input>
@@ -114,7 +135,7 @@
          <el-input
            v-model="forms.intensity"
            style="max-width: 600px"
            placeholder="Please input"
            placeholder="请输入降雨强度"
          >
            <template #append>mm/h</template>
          </el-input>
@@ -124,7 +145,7 @@
      </el-form>
      <div style="display: flex; justify-content: flex-end">
        <el-button type="primary" @click="addSimCheme">保存方案</el-button>
        <el-button type="success" @click="startPlay">开始模拟</el-button>
        <el-button type="success" @click="startPlay">保存并开始模拟</el-button>
      </div>
    </div>
  </div>
@@ -137,14 +158,21 @@
import { ElMessage, ElMessageBox } from "element-plus";
import { initeWaterPrimitiveView } from "@/utils/water";
import { SimAPIStore } from "@/store/simAPI";
import { getRegionData } from "@/api/trApi";
import { getRegionData, getSimStart, getSimDataById } from "@/api/trApi";
import { storeToRefs } from "pinia";
import dayjs from "dayjs";
import { EventBus } from "@/eventBus"; // 引入事件总线
const simStore = SimAPIStore();
const { selectTab } = storeToRefs(simStore);
const options = reactive([]);
const intensityOptions = ref([
  { value: "mm/h", label: "mm/h" },
  { value: "mm/5min", label: "mm/5min" },
]);
// 定义一个方法,用于根据 type 获取区域数据
const fetchRegionData = (type) => {
@@ -187,41 +215,53 @@
const forms = reactive({
  name: "",
  geom: "",
  rainfall: "",
  duration: "",
  intensity: "",
  rainfall: null,
  duration: null,
  intensity: null,
  fileList: [],
  type: 3,
  rainFallList: [],
  hours: "",
  hours: null,
  intensityUnit: "",
});
// 计算累计时长
const calculateHoursDifference = (val) => {
  if (!val || val.length !== 2) return 0;
  const [startDate, endDate] = val;
  const diffInHours = dayjs(endDate).diff(dayjs(startDate), "hour", true); // true 表示返回浮点数
  return diffInHours;
const flyHeight = ref(100000);
// 将选中区域传递给gisView文件,做标红flyTo显示
const changeGeom = (val) => {
  if (selectTab.value == "行政区划仿真") {
    flyHeight.value = 100000;
  } else {
    flyHeight.value = 5000;
  }
  EventBus.emit("select-geom", { geom: val.value, flyHeight: flyHeight.value });
};
const { calculateHoursDifference } = inject("calculateHours");
const change = (val) => {
  forms.duration = calculateHoursDifference(val);
};
const addSimCheme = async () => {
  await simStore.addSimCheme(forms);
  try {
    await simStore.addSimCheme(forms);
    resetForm(); // 只有在保存成功后才重置表单
    EventBus.emit("close-selectArea");
  } catch (error) {}
};
// 重置表单
const resetForm = () => {
  forms.name = "";
  forms.geom = "";
  forms.eares = "孙胡沟";
  forms.rainfall = "";
  forms.duration = "";
  forms.intensity = "";
  forms.rainfall = null;
  forms.duration = null;
  forms.intensity = null;
  forms.fileList = [];
  forms.rainFallList = [];
  forms.hours = "";
  forms.hours = null;
  forms.intensityUnit = "";
};
// 计算属性:获取上传文件的名称列表
@@ -273,37 +313,93 @@
const transformKeys = (data) => {
  return data.map((item) => ({
    time: item["时间"], // "时间" → "time"
    intensity: item["小时雨强mm/h"], // "小时雨强mm/h" → "intensity"
    total: item["累计雨量"], // "累计雨量" → "total"
    intensity: parseFloat(item["小时雨强mm/h"]), // 转为浮点数
    total: parseFloat(item["累计雨量"]), // 转为浮点数
  }));
};
/**
 * 检查时间列是否按升序排列
 * @param {Array} data - 表格数据
 * @param {string} timeColumn - 时间列的字段名
 * @returns {boolean} - 是否按升序排列
 */
const isTimeColumnSorted = (data, timeColumn) => {
  for (let i = 1; i < data.length; i++) {
    const prevTime = parseDateTime(data[i - 1][timeColumn]);
    const currentTime = parseDateTime(data[i][timeColumn]);
    // 如果前一个时间 > 当前时间,说明不是升序
    if (prevTime > currentTime) {
      console.error(`时间乱序:第 ${i} 行`, {
        prevTime: new Date(prevTime),
        currentTime: new Date(currentTime),
      });
      return false;
    }
  }
  return true; // 所有时间都按升序排列
};
/**
 * 从表头提取单位(如 "小时雨强mm/h" → "mm/h")
 * @param {string} header - 表头字符串
 * @returns {string} - 提取的单位(如 "mm/h"),默认返回空字符串
 */
const extractUnitFromHeader = (header) => {
  if (!header) return "";
  // 直接匹配 "mm/h"、"m/s" 等常见单位
  const unitRegex = /(mm\/h|m\/s|mm|℃|%|hPa|km\/h)/; // 根据需要扩展
  const match = header.match(unitRegex);
  return match ? match[0] : "";
};
// 处理数据
const processData = (data) => {
  // 1. 检查数据是否为空
  if (data.length === 0) {
    ElMessage.warning("文件内容为空!");
    return;
  }
  // 2. 获取表头(第一列是时间列)
  const tableColumns = Object.keys(data[0]);
  const timeColumn = tableColumns[0]; // 假设第一列是时间
  // 3. 校验时间列是否按升序排列
  if (!isTimeColumnSorted(data, timeColumn)) {
    ElMessage.error("时间列必须按升序排列!");
    forms.fileList = [];
    return; // 终止处理
  }
  const intensityColumn = tableColumns[1]; // 雨强列(如 "小时雨强(mm/h)")
  console.log(intensityColumn, "intensityColumnintensityColumnintensityColumn");
  // 3. 提取第二列的单位(如 "(mm/h)" → "mm/h")
  const intensityUnit = extractUnitFromHeader(intensityColumn);
  forms.intensityUnit = intensityUnit; // 存储单位(可选)
  // 4. 如果校验通过,继续处理数据
  forms.rainFallList = transformKeys(data);
  console.log(forms.rainFallList, "data");
  if (data.length === 0) return;
  const tableColumns = Object.keys(data[0]);
  // 第一列:时间
  const firstTime = parseDateTime(data[0][tableColumns[0]]);
  const lastTime = parseDateTime(data[data.length - 1][tableColumns[0]]);
  // 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); // 更新降雨时长
  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; // 更新降雨强度
  forms.intensity = maxValue;
  // 第三列:累计降雨量
  const lastValue = data[data.length - 1][tableColumns[2]];
  forms.rainfall = lastValue; // 更新降雨量
  forms.rainfall = lastValue;
};
/**
@@ -372,9 +468,35 @@
};
// 开始模拟
function startPlay() {
  initeWaterPrimitiveView();
  startSimulate();
async function startPlay() {
  try {
    // 保存方案
    const res = await simStore.addSimCheme(forms);
    const schemeId = res.data?.data?.id;
    if (!schemeId) {
      ElMessage.error("方案保存失败,未获取到有效 ID");
      return;
    }
    // 调用求解器
    const simStartRes = await getSimStart(schemeId);
    console.log(simStartRes, "getSimStart 返回结果");
    // 关闭选择区域窗口、初始化视图并开始模拟
    EventBus.emit("close-selectArea");
    // 暂时不在此处开始模拟,模拟都在方案列表中进行模拟
    // initeWaterPrimitiveView();
    // startSimulate();
    ElMessage.warning({
      message: "请返回方案列表开始模拟!",
      duration: 10000, // 提示框显示时长,单位为毫秒,默认是3000毫秒
    });
  } catch (error) {
    console.error("启动模拟过程中发生错误:", error);
    // ElMessage.error("启动模拟失败,请稍后再试");
  }
}
</script>