From 58b4e83e8079146007873d7ac40d76b21cbe3340 Mon Sep 17 00:00:00 2001
From: guonan <guonan201020@163.com>
Date: 星期二, 15 四月 2025 11:13:57 +0800
Subject: [PATCH] 解析表格文件以及方案保存合并

---
 package.json               |    4 
 src/views/left/CitySim.vue |  343 +++++++++++++++++++++++++++++++++++---------------------
 2 files changed, 217 insertions(+), 130 deletions(-)

diff --git a/package.json b/package.json
index 3aef287..df1f5bd 100644
--- a/package.json
+++ b/package.json
@@ -14,12 +14,14 @@
     "element-plus": "^2.9.5",
     "less": "^4.2.2",
     "less-loader": "^12.2.0",
+    "papaparse": "^5.5.2",
     "pinia": "^3.0.2",
     "sdp": "^3.2.0",
     "vue": "^3.2.13",
     "vue-router": "^4.0.3",
     "vue-seamless-scroll": "^1.1.23",
-    "vuex": "^4.0.0"
+    "vuex": "^4.0.0",
+    "xlsx": "^0.18.5"
   },
   "devDependencies": {
     "@vue/cli-plugin-babel": "~5.0.0",
diff --git a/src/views/left/CitySim.vue b/src/views/left/CitySim.vue
index 111e02f..2c489ca 100644
--- a/src/views/left/CitySim.vue
+++ b/src/views/left/CitySim.vue
@@ -8,24 +8,6 @@
     </div>
     <div class="forms">
       <el-form :model="forms" label-width="auto" style="max-width: 600px">
-        <el-form-item label="涓婁紶鍙傛暟">
-          <el-upload
-            v-model:file-list="forms.fileList"
-            class="upload-demo"
-            action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
-            multiple
-            :on-preview="handlePreview"
-            :on-remove="handleRemove"
-            :before-remove="beforeRemove"
-            :limit="3"
-            :on-exceed="handleExceed"
-            :before-upload="beforeUpload"
-            accept=".xlsx,.xls,.csv"
-          >
-            <el-button type="primary">鐐瑰嚮涓婁紶闄嶉洦鏁版嵁</el-button>
-            <template #append>mm/h</template>
-          </el-upload>
-        </el-form-item>
         <el-form-item label="琛屾斂鍖哄煙:" v-if="disForm == '琛屾斂鍖哄垝浠跨湡'">
           <el-select
             v-model="forms.eare"
@@ -40,7 +22,24 @@
             />
           </el-select>
         </el-form-item>
-        
+
+        <el-form-item label="涓婁紶鍙傛暟">
+          <el-upload
+            v-model:file-list="forms.fileList"
+            class="upload-demo"
+            :auto-upload="false"
+            :multiple="false"
+            :on-change="handleFileChange"
+            :limit="1"
+            :on-exceed="handleExceed"
+            :before-upload="beforeUpload"
+            accept=".xlsx,.xls,.csv"
+          >
+            <el-button type="primary">鐐瑰嚮涓婁紶闄嶉洦鏁版嵁</el-button>
+            <template #append>mm/h</template>
+          </el-upload>
+        </el-form-item>
+
         <el-form-item label="閲嶇偣鍖哄煙" v-if="disForm == '閲嶇偣鍖哄煙浠跨湡'">
           <el-select
             v-model="forms.eares"
@@ -65,6 +64,7 @@
             <template #append>mm</template>
           </el-input>
         </el-form-item>
+
         <el-form-item label="闄嶉洦鏃堕暱:">
           <el-input
             v-model="forms.duration"
@@ -72,8 +72,9 @@
             placeholder="Please input"
           >
             <template #append>h</template>
-          </el-input></el-form-item
-        >
+          </el-input>
+        </el-form-item>
+
         <el-form-item label="闄嶉洦寮哄害:">
           <el-input
             v-model="forms.intensity"
@@ -83,63 +84,65 @@
             <template #append>mm/h</template>
           </el-input>
         </el-form-item>
-        <el-form-item label="浠跨湡鍙傛暟:"> </el-form-item>
+
+        <el-form-item label="浠跨湡鍙傛暟:"></el-form-item>
       </el-form>
       <div style="display: flex; justify-content: flex-end">
         <el-button type="primary" @click="openSaveDialog">淇濆瓨鏂规</el-button>
         <el-button type="success" @click="startPlay">寮�濮嬫ā鎷�</el-button>
       </div>
+      <!-- 淇濆瓨鏂规瀵硅瘽妗� -->
+      <el-dialog
+        v-model="saveDialogVisible"
+        :title="dialogTitle"
+        width="50%"
+        :before-close="handleClose"
+        custom-class="custom-dialog"
+      >
+        <div class="dialog-content">
+          <p><strong>妯℃嫙绫诲瀷锛�</strong>{{ dialogTitle }}</p>
+          <p v-if="disForm === '琛屾斂鍖哄垝浠跨湡'">
+            <strong>琛屾斂鍖哄煙锛�</strong>{{ forms.eare }}
+          </p>
+          <p v-if="disForm === '閲嶇偣鍖哄煙浠跨湡'">
+            <strong>閲嶇偣鍖哄煙锛�</strong>{{ forms.eares }}
+          </p>
+          <p><strong>闄嶉洦閲忥細</strong>{{ forms.rainfall }} mm</p>
+          <p><strong>闄嶉洦鏃堕暱锛�</strong>{{ forms.duration }} h</p>
+          <p><strong>闄嶉洦寮哄害锛�</strong>{{ forms.intensity }} mm/h</p>
+          <p><strong>涓婁紶鏂囦欢锛�</strong>{{ uploadedFilesText }}</p>
+        </div>
+        <template #footer>
+          <span class="dialog-footer">
+            <el-button @click="saveDialogVisible = false">鍙栨秷</el-button>
+            <el-button type="primary" @click="confirmSave">纭畾淇濆瓨</el-button>
+          </span>
+        </template>
+      </el-dialog>
     </div>
-
-    <!-- 淇濆瓨鏂规瀵硅瘽妗� -->
-    <el-dialog
-      v-model="saveDialogVisible"
-      :title="dialogTitle"
-      width="50%"
-      :before-close="handleClose"
-      custom-class="custom-dialog"
-    >
-      <div class="dialog-content">
-        <p><strong>妯℃嫙绫诲瀷锛�</strong>{{ dialogTitle }}</p>
-        <p v-if="disForm === '琛屾斂鍖哄垝浠跨湡'"><strong>琛屾斂鍖哄煙锛�</strong>{{ forms.eare }}</p>
-        <p v-if="disForm === '閲嶇偣鍖哄煙浠跨湡'"><strong>閲嶇偣鍖哄煙锛�</strong>{{ forms.eares }}</p>
-        <p><strong>闄嶉洦閲忥細</strong>{{ forms.rainfall }} mm</p>
-        <p><strong>闄嶉洦鏃堕暱锛�</strong>{{ forms.duration }} h</p>
-        <p><strong>闄嶉洦寮哄害锛�</strong>{{ forms.intensity }} mm/h</p>
-        <p><strong>涓婁紶鏂囦欢锛�</strong>{{ uploadedFilesText }}</p>
-      </div>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="saveDialogVisible = false">鍙栨秷</el-button>
-          <el-button type="primary" @click="confirmSave">纭畾淇濆瓨</el-button>
-        </span>
-      </template>
-    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { reactive, ref, computed ,watch} from "vue";
-import { ElMessage, ElMessageBox } from "element-plus";
+import { reactive, ref, watch, inject, computed } from "vue";
+import * as XLSX from "xlsx";
+import Papa from "papaparse";
+import { ElMessage } from "element-plus";
 
-// 瀹氫箟 Props
-const props = defineProps({
-  clickValue: String,
-});
-
-// 鏁版嵁缁戝畾
-const disForm = ref(""); // 褰撳墠鏄剧ず鐨勮〃鍗曠被鍨�
+// 娉ㄥ叆鐖剁粍浠舵彁渚涚殑鏂规硶
+const { startSimulate, endSimulate } = inject("simulateActions");
 const saveDialogVisible = ref(false); // 鎺у埗淇濆瓨鏂规瀵硅瘽妗嗙殑鏄剧ず鐘舵��
+// 琛ㄥ崟鏁版嵁
 const forms = reactive({
-  eare: "鍖椾含甯�", // 琛屾斂鍖哄煙
-  eares: "瀛欒儭娌�", // 閲嶇偣鍖哄煙
-  rainfall: "50", // 闄嶉洦閲�
-  duration: "5", // 闄嶉洦鏃堕暱
-  intensity: "70", // 闄嶉洦寮哄害
-  fileList: [], // 涓婁紶鐨勬枃浠跺垪琛�
+  eare: "鍖椾含甯�",
+  eares: "瀛欒儭娌�",
+  rainfall: "",
+  duration: "",
+  intensity: "",
+  fileList: [],
 });
 
-// 琛屾斂鍖哄煙閫夐」
+// 鍩庡競鍜岄噸鐐瑰尯鍩熼�夐」
 const cityOptions = [
   { value: "鍖椾含甯�", label: "鍖椾含甯�" },
   { value: "涓滃煄鍖�", label: "涓滃煄鍖�" },
@@ -159,8 +162,6 @@
   { value: "瀵嗕簯鍖�", label: "瀵嗕簯鍖�" },
   { value: "寤跺簡鍖�", label: "寤跺簡鍖�" },
 ];
-
-// 閲嶇偣鍖哄煙閫夐」
 const earesOptions = [
   { value: "瀛欒儭娌�", label: "瀛欒儭娌�" },
   { value: "楸兼按娲炲悗娌�", label: "楸兼按娲炲悗娌�" },
@@ -176,17 +177,8 @@
 
 // 璁$畻灞炴�э細鑾峰彇涓婁紶鏂囦欢鐨勫悕绉板垪琛�
 const uploadedFilesText = computed(() => {
-  return forms.fileList.map(file => file.name).join(", ") || "鏃�";
+  return forms.fileList.map((file) => file.name).join(", ") || "鏃�";
 });
-
-// 鐩戝惉 Props 鍙樺寲锛屾洿鏂板綋鍓嶈〃鍗曠被鍨�
-watch(
-  () => props.clickValue,
-  (newValue) => {
-    disForm.value = newValue || "琛屾斂鍖哄垝浠跨湡";
-  },
-  { immediate: true, deep: true }
-);
 
 // 鎵撳紑淇濆瓨鏂规瀵硅瘽妗�
 const openSaveDialog = () => {
@@ -210,7 +202,7 @@
 
 // 纭淇濆瓨
 const confirmSave = () => {
-  console.log('淇濆瓨鏂规鎴愬姛', {
+  console.log("淇濆瓨鏂规鎴愬姛", {
     妯℃嫙绫诲瀷: dialogTitle.value,
     琛屾斂鍖哄煙: disForm.value === "琛屾斂鍖哄垝浠跨湡" ? forms.eare : null,
     閲嶇偣鍖哄煙: disForm.value === "閲嶇偣鍖哄煙浠跨湡" ? forms.eares : null,
@@ -219,63 +211,156 @@
     闄嶉洦寮哄害: `${forms.intensity} mm/h`,
     // 涓婁紶鏂囦欢: forms.fileList.map(file => file.name),
   });
-  ElMessage.success('鏂规宸蹭繚瀛�');
+  ElMessage.success("鏂规宸蹭繚瀛�");
   saveDialogVisible.value = false;
+  resetForm();
 };
+
+// 閲嶇疆琛ㄥ崟
+const resetForm = () => {
+  forms.eare = "鍖椾含甯�";
+  forms.eares = "瀛欒儭娌�";
+  forms.rainfall = "";
+  forms.duration = "";
+  forms.intensity = "";
+  forms.fileList = [];
+};
+
+// 鏂囦欢鍙樺寲鏃惰Е鍙戣В鏋�
+const handleFileChange = (file) => {
+  const reader = new FileReader();
+  reader.onload = (e) => {
+    const data = e.target.result;
+    if (file.name.endsWith(".csv")) {
+      parseCSV(data);
+    } else {
+      parseExcel(data);
+    }
+  };
+  reader.readAsArrayBuffer(file.raw);
+};
+
+// 瑙f瀽CSV鏂囦欢
+const parseCSV = (data) => {
+  Papa.parse(new TextDecoder("utf-8").decode(data), {
+    complete: (results) => {
+      if (results.data.length > 0) {
+        processData(results.data);
+      }
+    },
+    header: true,
+    skipEmptyLines: true,
+  });
+};
+
+// 瑙f瀽Excel鏂囦欢
+const parseExcel = (data) => {
+  const workbook = XLSX.read(data, { type: "array" });
+  const firstSheetName = workbook.SheetNames[0];
+  const worksheet = workbook.Sheets[firstSheetName];
+  const jsonData = XLSX.utils.sheet_to_json(worksheet);
+  processData(jsonData);
+};
+
+// 澶勭悊鏁版嵁
+const processData = (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]]);
+  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; // 鏇存柊闄嶉洦寮哄害
+
+  // 绗笁鍒楋細绱闄嶉洦閲�
+  const lastValue = data[data.length - 1][tableColumns[2]];
+  forms.rainfall = lastValue; // 鏇存柊闄嶉洦閲�
+};
+
+// 瑙f瀽鏃ユ湡鏃堕棿
+const parseDateTime = (dateString) => {
+  if (typeof dateString === "number") {
+    const parsedDate = XLSX.SSF.parse_date_code(dateString);
+    if (parsedDate) {
+      return new Date(
+        parsedDate.y,
+        parsedDate.m - 1,
+        parsedDate.d,
+        parsedDate.H || 0,
+        parsedDate.M || 0,
+        parsedDate.S || 0
+      ).getTime();
+    }
+  }
+
+  const parsedDate = new Date(dateString);
+  if (!isNaN(parsedDate.getTime())) {
+    return parsedDate.getTime();
+  }
+
+  const parts = dateString.split(/[/\s:]/);
+  if (parts.length >= 6) {
+    const year = parseInt(parts[0], 10);
+    const month = parseInt(parts[1], 10) - 1;
+    const day = parseInt(parts[2], 10);
+    const hour = parseInt(parts[3], 10) || 0;
+    const minute = parseInt(parts[4], 10) || 0;
+    const second = parseInt(parts[5], 10) || 0;
+
+    const date = new Date(year, month, day, hour, minute, second);
+    if (!isNaN(date.getTime())) {
+      return date.getTime();
+    }
+  }
+
+  console.warn(`鏃犳硶瑙f瀽鏃ユ湡: ${dateString}`);
+  return NaN;
+};
+
+const handleExceed = () => {
+  ElMessage.warning("姣忔鍙兘涓婁紶涓�涓枃浠�");
+};
+
+const beforeUpload = (file) => {
+  const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
+  const isCSV = file.name.endsWith(".csv");
+  if (!isExcel && !isCSV) {
+    ElMessage.error("鍙兘涓婁紶Excel鎴朇SV鏂囦欢");
+    return false;
+  }
+  return true;
+};
+
+const disForm = ref("");
+
+// 瀹氫箟 Props
+const props = defineProps({
+  clickValue: String,
+});
+
+// 鐩戝惉 Props 鍙樺寲
+watch(
+  () => props.clickValue,
+  (newValue) => {
+    disForm.value = newValue || "琛屾斂鍖哄垝浠跨湡";
+  },
+  { immediate: true, deep: true }
+);
 
 // 寮�濮嬫ā鎷�
 function startPlay() {
-  console.log("寮�濮嬫ā鎷熸寜閽鐐瑰嚮");
-}
-
-// 鏂囦欢涓婁紶鐩稿叧鏂规硶
-function handleRemove(file, uploadFiles) {
-  console.log(file, uploadFiles);
-}
-
-function handlePreview(uploadFile) {
-  console.log(uploadFile);
-}
-
-function handleExceed(files, uploadFiles) {
-  ElMessage.warning(
-    `The limit is 3, you selected ${files.length} files this time, add up to ${
-      files.length + uploadFiles.length
-    } totally`
-  );
-}
-
-const beforeUpload = (file) => {
-  const allowedTypes = [
-    "application/vnd.ms-excel",
-    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
-    "text/csv",
-    "application/csv",
-    "text/x-csv",
-    "application/x-csv",
-    "text/comma-separated-values",
-    "text/x-comma-separated-values",
-  ];
-
-  const isAllowed = allowedTypes.includes(file.type);
-  const extension = file.name.split(".").pop().toLowerCase();
-  const isExtensionValid = ["xls", "xlsx", "csv"].includes(extension);
-
-  if (!isAllowed || !isExtensionValid) {
-    ElMessage.error("鍙兘涓婁紶 Excel (.xls, .xlsx) 鎴� CSV 鏂囦欢");
-    return false; // 闃绘涓婁紶
-  }
-
-  return true; // 鍏佽涓婁紶
-};
-
-function beforeRemove(uploadFile, uploadFiles) {
-  return ElMessageBox.confirm(
-    `Cancel the transfer of ${uploadFile.name} ?`
-  ).then(
-    () => true,
-    () => false
-  );
+  startSimulate();
 }
 </script>
 
@@ -296,4 +381,4 @@
 /deep/ .el-form-item__label {
   color: #61f7d4 !important;
 }
-</style>
\ No newline at end of file
+</style>

--
Gitblit v1.9.3