From bf42ef43fccdf3d3486eec84ad4073b0c7650aba Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期三, 13 八月 2025 17:35:37 +0800
Subject: [PATCH] 新增场景信息文件导入功能

---
 public/现场监管场景信息导入模板.xlsx                                                |    0 
 src/views/fysp/scene/CompSceneImport.vue                                |  103 +++++++++++++++++---
 src/components/table/FYTable.vue                                        |    2 
 src/views/fysp/data-product/ProdLawEnforceList.vue                      |   43 ++------
 src/api/index.js                                                        |    2 
 src/components/button/FYDownloadTableButton.vue                         |   33 ++++++
 src/api/fysp/sceneApi.js                                                |    5 +
 src/components.d.ts                                                     |    1 
 src/views/fysp/data-product/base-data-product/PordProblemRecurrence.vue |   32 ++++-
 src/components/map/SceneMap.vue                                         |   56 +++++++---
 src/utils/excel.js                                                      |    2 
 11 files changed, 203 insertions(+), 76 deletions(-)

diff --git "a/public/\347\216\260\345\234\272\347\233\221\347\256\241\345\234\272\346\231\257\344\277\241\346\201\257\345\257\274\345\205\245\346\250\241\346\235\277.xlsx" "b/public/\347\216\260\345\234\272\347\233\221\347\256\241\345\234\272\346\231\257\344\277\241\346\201\257\345\257\274\345\205\245\346\250\241\346\235\277.xlsx"
new file mode 100644
index 0000000..af2ebb6
--- /dev/null
+++ "b/public/\347\216\260\345\234\272\347\233\221\347\256\241\345\234\272\346\231\257\344\277\241\346\201\257\345\257\274\345\205\245\346\250\241\346\235\277.xlsx"
Binary files differ
diff --git a/src/api/fysp/sceneApi.js b/src/api/fysp/sceneApi.js
index 2d51a0c..82d5f40 100644
--- a/src/api/fysp/sceneApi.js
+++ b/src/api/fysp/sceneApi.js
@@ -93,5 +93,10 @@
    */
   findScene(scene) {
     return $fysp.post('scense/search', scene).then((res) => res.data);
+  },
+
+  importScene(file) {
+    return $fysp.post('scense/import', file).then((res) => res.data);
   }
+
 };
diff --git a/src/api/index.js b/src/api/index.js
index 766ad71..a839aee 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -1,7 +1,7 @@
 import axios from 'axios';
 import { ElMessage } from 'element-plus';
 
-const debug = true;
+const debug = false;
 
 // let ip1 = 'http://47.100.191.150:9005/';
 // let ip1_file = 'http://47.100.191.150:9005/';
diff --git a/src/components.d.ts b/src/components.d.ts
index 3b9ac0b..19bc486 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -86,6 +86,7 @@
     FYBgTaskCard: typeof import('./components/bg-task/FYBgTaskCard.vue')['default']
     FYBgTaskDialog: typeof import('./components/bg-task/FYBgTaskDialog.vue')['default']
     FYBgTaskItem: typeof import('./components/bg-task/FYBgTaskItem.vue')['default']
+    FYDownloadTableButton: typeof import('./components/button/FYDownloadTableButton.vue')['default']
     FYForm: typeof import('./components/form/FYForm.vue')['default']
     FYImageSelectDialog: typeof import('./components/FYImageSelectDialog.vue')['default']
     FYInfoSearch: typeof import('./components/search-option/FYInfoSearch.vue')['default']
diff --git a/src/components/button/FYDownloadTableButton.vue b/src/components/button/FYDownloadTableButton.vue
new file mode 100644
index 0000000..a5c90f2
--- /dev/null
+++ b/src/components/button/FYDownloadTableButton.vue
@@ -0,0 +1,33 @@
+<template>
+  <el-button
+    icon="Download"
+    type="primary"
+    plain
+    @click="download"
+    :loading="dlLoading"
+    >{{ label }}</el-button
+  >
+</template>
+<script setup>
+import { ref } from 'vue';
+import { conversionFromTable } from '@/utils/excel';
+
+const props = defineProps({
+  // 涓嬭浇鏂囦欢鍚嶇О
+  fileName: String,
+  // 琛ㄦ牸鍏冪礌id
+  tableId: String,
+  label: {
+    type: String,
+    default: '涓嬭浇'
+  }
+});
+
+const dlLoading = ref(false);
+
+function download() {
+  dlLoading.value = true;
+  conversionFromTable(props.tableId, props.fileName);
+  dlLoading.value = false;
+}
+</script>
diff --git a/src/components/map/SceneMap.vue b/src/components/map/SceneMap.vue
index e301ec3..3abe4c6 100644
--- a/src/components/map/SceneMap.vue
+++ b/src/components/map/SceneMap.vue
@@ -9,12 +9,20 @@
     ></FYOptionScene>
     <slot name="left-top"></slot>
   </el-row>
+  <!-- <el-row class="right-wrap">
+    <el-col :span="4">
+      <el-button>close</el-button>
+    </el-col>
+    <el-col :span="20">
+      
+    </el-col>
+  </el-row> -->
   <el-scrollbar class="right-wrap">
     <div v-for="s in selectedSceneList" :key="s.guid">
       <el-checkbox
         v-model="s._checked"
         :label="s.name"
-        @change="handleChange"
+        @change="handleChange(s)"
       />
       <!-- <el-text>{{ s.name }}</el-text> -->
     </div>
@@ -53,7 +61,7 @@
     if (nV != oV) {
       clearSceneMarks();
       createSceneMarks();
-      filterMarkViews();
+      filterMarkViews(true);
     }
   },
   { immediate: true }
@@ -62,14 +70,20 @@
 watch(scenetype, (nV, oV) => {
   if (nV != oV) {
     clearSceneMarks();
-    filterMarkViews();
+    filterMarkViews(true);
   }
 });
 
-function handleChange(value) {
-  console.log(value);
-
-  filterMarkViews();
+function handleChange(scene) {
+  const mv = markViewList.find((v) => {
+    return scene.guid == v.getExtData().guid;
+  });
+  if (scene._checked) {
+    map.add(mv);
+  } else {
+    map.remove(mv);
+  }
+  // filterMarkViews();
 }
 
 function createSceneMarks() {
@@ -111,8 +125,11 @@
 /**
  * 绛涢�夋墍閫夌被鍨嬬殑鍦烘櫙
  */
-function filterMarkViews() {
+function filterMarkViews(setFitView) {
   onMapMounted(() => {
+    if (markViewList.length > 0) {
+      map.remove(markViewList);
+    }
     if (scenetype.value == undefined) {
       markViewList = allMarkViews;
     } else {
@@ -125,21 +142,21 @@
     }
     markViewList = markViewList.filter((v) => {
       const _index = selectedSceneList.value.findIndex((s) => {
-        console.log(s.guid, v.getExtData().guid);
-
-        s.guid == v.getExtData().guid;
+        return s.guid == v.getExtData().guid && s._checked;
       });
       return _index != -1;
     });
     map.add(markViewList);
-    setTimeout(() => {
-      map.setFitView(markViewList);
-      // const list = markViewList.map((v) => {
-      //   const _extData = v.getExtData();
-      //   return [_extData.longitude, _extData.latitude];
-      // });
-      // mapUtil.setBound(list);
-    }, 1000);
+    if (setFitView) {
+      setTimeout(() => {
+        map.setFitView(markViewList);
+        // const list = markViewList.map((v) => {
+        //   const _extData = v.getExtData();
+        //   return [_extData.longitude, _extData.latitude];
+        // });
+        // mapUtil.setBound(list);
+      }, 1000);
+    }
   });
 }
 
@@ -166,5 +183,6 @@
   border-radius: 4px;
   padding: 2px 8px;
   max-width: 300px;
+  box-shadow: var(--el-box-shadow);
 }
 </style>
diff --git a/src/components/table/FYTable.vue b/src/components/table/FYTable.vue
index 4a5d520..b759ee0 100644
--- a/src/components/table/FYTable.vue
+++ b/src/components/table/FYTable.vue
@@ -28,7 +28,7 @@
     <slot name="options-expand2"></slot>
   </div>
   <el-table
-    id="fyTable"
+    v-bind="$attrs"
     ref="tableRef"
     :data="tableData"
     v-loading="loading"
diff --git a/src/utils/excel.js b/src/utils/excel.js
index a42c45f..07a0cc2 100644
--- a/src/utils/excel.js
+++ b/src/utils/excel.js
@@ -20,7 +20,7 @@
   //     }
   //   }
   // }
-  console.log(tables);
+  // console.log(tables);
 
   //------鏀惧叆浠ヤ笂鎷垮埌switch鐘舵�侊紝浠ュ強涓嬫媺妗嗕唬鐮侊紝濡傛灉娌″厠闅嗗厓绱犲垯浼氬奖鍝嶅埌鍘熻〃鏍�-------------//
 
diff --git a/src/views/fysp/data-product/ProdLawEnforceList.vue b/src/views/fysp/data-product/ProdLawEnforceList.vue
index 2dccfb8..940d70d 100644
--- a/src/views/fysp/data-product/ProdLawEnforceList.vue
+++ b/src/views/fysp/data-product/ProdLawEnforceList.vue
@@ -1,5 +1,6 @@
 <template>
   <FYTable
+    id="fyTable"
     @search="onSearch"
     :data="tableData"
     :pagination="false"
@@ -47,15 +48,12 @@
       </el-descriptions>
     </template>
     <template #buttons>
-      <el-button
-        icon="Download"
-        type="primary"
-        plain
-        @click="download"
-        :loading="dlLoading"
+      <FYDownloadTableButton
+        label="涓嬭浇娓呭崟"
+        table-id="fyTable"
+        :file-name="fileName"
         :disabled="downloadDisabled"
-        >涓嬭浇娓呭崟</el-button
-      >
+      ></FYDownloadTableButton>
     </template>
     <template #table-column="{ size }">
       <el-table-column fixed="left" label="搴忓彿" width="53">
@@ -160,13 +158,8 @@
 import evaluateApi from '@/api/fysp/evaluateApi';
 import problemApi from '@/api/fysp/problemApi';
 import sceneApi from '@/api/fysp/sceneApi';
-import { conversionFromTable } from "@/utils/excel";
 import { envCreditCode } from '@/constants/index';
 import { useTablePaste } from '@/composables/tablePaste';
-import { useCloned } from '@vueuse/core';
-import { useMessageBoxTip } from '@/composables/messageBox';
-import * as XLSX from 'xlsx';
-import FileSaver from 'file-saver';
 
 export default {
   setup() {
@@ -239,6 +232,12 @@
         });
         return b;
       }
+    },
+    fileName() {
+      const { locations, scenetype, time } = this.formSearch;
+      return `${locations.dName}${dayjs(time).format(
+        'YYYY骞碝M鏈�'
+      )}鑱斿悎鎵ф硶娓呭崟`;
     }
   },
   methods: {
@@ -327,24 +326,6 @@
       const property = column['property'];
       return row[property] === value;
     },
-    download() {
-      // const workbook = XLSX.utils.book_new();
-      // const worksheet = XLSX.utils.json_to_sheet(this.tableData);
-      // XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
-      // const excelData = XLSX.write(workbook, {
-      //   bookType: 'xlsx',
-      //   type: 'array'
-      // });
-      // const blob = new Blob([excelData], {
-      //   type: 'application/vnd.openxmlformats-officedocumnet.spreadsheetml.sheet'
-      // });
-      const { locations, scenetype, time } = this.formSearch;
-      const name = `${locations.dName}${dayjs(time).format(
-        'YYYY骞碝M鏈�'
-      )}鑱斿悎鎵ф硶娓呭崟.xlsx`;
-      // FileSaver.saveAs(blob, name);
-      conversionFromTable('fyTable', name)
-    }
   },
   mounted() {
     this.addRefreshEvent(this.$refs.tableRef.doLayout);
diff --git a/src/views/fysp/data-product/base-data-product/PordProblemRecurrence.vue b/src/views/fysp/data-product/base-data-product/PordProblemRecurrence.vue
index 72291e6..b5ee0fe 100644
--- a/src/views/fysp/data-product/base-data-product/PordProblemRecurrence.vue
+++ b/src/views/fysp/data-product/base-data-product/PordProblemRecurrence.vue
@@ -1,5 +1,6 @@
 <template>
   <FYTable
+    id="fyTable"
     @search="onSearch"
     :data="showData"
     :pagination="false"
@@ -34,7 +35,14 @@
         <el-radio :value="2">鎸夐棶棰樼被鍨嬬粺璁�</el-radio>
       </el-radio-group>
     </template>
-    <template #buttons> </template>
+    <template #buttons>
+      <FYDownloadTableButton
+        label="涓嬭浇娓呭崟"
+        table-id="fyTable"
+        :file-name="fileName"
+        :disabled="downloadDisabled"
+      ></FYDownloadTableButton>
+    </template>
     <template #table-column="{ size }">
       <!-- <el-table-column fixed="left" label="搴忓彿" width="53">
         <template #default="{ row }">
@@ -58,14 +66,9 @@
       </el-table-column>
       <el-table-column prop="townName" label="琛楅晣" width="110">
       </el-table-column>
-      <el-table-column prop="problemType" label="闂绫诲瀷" width="110">
+      <el-table-column prop="problemType" label="闂绫诲瀷" width="170">
       </el-table-column>
-      <el-table-column
-        v-if="radio == 1"
-        prop="problemName"
-        label="闂鍚嶇О"
-        width="200"
-      >
+      <el-table-column v-if="radio == 1" prop="problemName" label="闂鍚嶇О">
       </el-table-column>
       <el-table-column prop="proNum" label="闂鏁�" width="70">
       </el-table-column>
@@ -134,6 +137,19 @@
   return res;
 });
 
+const fileName = computed(() => {
+  const { locations, scenetype, timeArr } = formSearch.value;
+  return `${locations.dName}${dayjs(timeArr[0]).format(
+    'YYYY骞碝M鏈圖D鏃�'
+  )}鑷�${dayjs(timeArr[1]).format('YYYY骞碝M鏈圖D鏃�')}${
+    scenetype.label
+  }闂澶嶅彂娓呭崟`;
+});
+
+const downloadDisabled = computed(() => {
+  return tableData.value.length == 0;
+});
+
 function onSearch(page, callback) {
   fetchProbRecurrence().finally(() => callback());
 }
diff --git a/src/views/fysp/scene/CompSceneImport.vue b/src/views/fysp/scene/CompSceneImport.vue
index bd3bf3e..40f591b 100644
--- a/src/views/fysp/scene/CompSceneImport.vue
+++ b/src/views/fysp/scene/CompSceneImport.vue
@@ -1,5 +1,7 @@
 <template>
-  <el-button disabled icon="Upload" type="success" @click="dialogVisible = true">鎵归噺瀵煎叆</el-button>
+  <el-button icon="Upload" type="success" @click="dialogVisible = true"
+    >鎵归噺瀵煎叆</el-button
+  >
   <el-dialog
     v-model="dialogVisible"
     title="鍦烘櫙淇℃伅鎵归噺瀵煎叆"
@@ -9,31 +11,102 @@
     <el-upload
       class="upload-demo"
       drag
-      action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
-      multiple
+      :limit="1"
+      v-model:file-list="fileList"
+      :auto-upload="false"
       accept=".xlsx"
+      :on-change="handleChange"
+      :on-exceed="handleExceed"
     >
       <el-icon class="el-icon--upload"><upload-filled /></el-icon>
       <div class="el-upload__text">鎷栧姩鏂囦欢鎴�<em>鐐瑰嚮涓婁紶</em></div>
       <template #tip>
-        <!-- <div class="el-upload__tip">jpg/png files with a size less than 500kb</div> -->
+        <el-text type="danger" size="small">{{ errMsg }}</el-text>
       </template>
     </el-upload>
     <template #footer>
-      <div class="dialog-footer">
-        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
-        <el-button type="primary" @click="dialogVisible = false">纭畾</el-button>
-      </div>
+      <el-row justify="space-between">
+        <el-button type="primary" plain @click="downloadTemplate"
+          >涓嬭浇妯℃澘</el-button
+        >
+        <div class="dialog-footer">
+          <el-button @click="handleClose">鍙栨秷</el-button>
+
+          <el-button type="primary" @click="uploadFile" :loading="loading"
+            >纭畾</el-button
+          >
+        </div>
+      </el-row>
     </template>
   </el-dialog>
 </template>
 
-<script>
-export default {
-  data() {
-    return {
-      dialogVisible: false
-    };
+<script setup>
+import { ref } from 'vue';
+import sceneApi from '@/api/fysp/sceneApi';
+import { ElMessage } from 'element-plus';
+
+const dialogVisible = ref(false);
+const fileList = ref([]);
+const errMsg = ref('');
+const loading = ref(false);
+
+function handleChange(file) {
+  console.log(file);
+  const ext = file.name.split('.').pop();
+  if (ext !== 'xlsx') {
+    ElMessage.error('璇蜂笂浼燛xcel鏂囦欢');
+    fileList.value.splice(0, 1);
+    return;
   }
-};
+  fileList.value = [file];
+}
+
+function handleExceed(files, fileList) {
+  ElMessage.error('鏈�澶氬彧鑳戒笂浼犱竴涓枃浠�');
+}
+
+function uploadFile() {
+  if (fileList.value.length === 0) {
+    ElMessage.error('璇蜂笂浼犳枃浠�');
+    return;
+  }
+  const formData = new FormData();
+  loading.value = true;
+  formData.append('file', fileList.value[0].raw);
+  errMsg.value = '';
+  sceneApi
+    .importScene(formData)
+    .then((res) => {
+      dialogVisible.value = false;
+      fileList.value = [];
+    })
+    .catch((err) => {
+      errMsg.value = err;
+    })
+    .finally(() => {
+      loading.value = false;
+    });
+}
+
+function downloadTemplate() {
+  // 鍒涘缓涓嬭浇閾炬帴
+  const link = document.createElement('a');
+  // 璁剧疆public鐩綍涓嬬殑鏂囦欢璺緞
+  link.href = '/鐜板満鐩戠鍦烘櫙淇℃伅瀵煎叆妯℃澘.xlsx';
+  // 璁剧疆涓嬭浇鏂囦欢鍚�
+  link.download = '鐜板満鐩戠鍦烘櫙淇℃伅瀵煎叆妯℃澘.xlsx';
+  // 灏嗛摼鎺ユ坊鍔犲埌椤甸潰
+  document.body.appendChild(link);
+  // 瑙﹀彂鐐瑰嚮涓嬭浇
+  link.click();
+  // 涓嬭浇瀹屾垚鍚庣Щ闄ら摼鎺�
+  document.body.removeChild(link);
+}
+
+function handleClose() {
+  dialogVisible.value = false;
+  fileList.value = [];
+  errMsg.value = '';
+}
 </script>

--
Gitblit v1.9.3