f812a5d9c4a973930a79581e0c1de91a7054b547..f5302b82a348926546410e52dc4c51621e457105
2025-08-14 riku
修改了场景信息导入的模板文件
f5302b 对比 | 目录
2025-08-13 riku
新增场景信息文件导入功能
bf42ef 对比 | 目录
2025-08-01 riku
新增问题复发清清单界面
5efcf8 对比 | 目录
已修改11个文件
已添加3个文件
490 ■■■■ 文件已修改
public/现场监管场景信息导入模板.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/dataproductApi.js 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/sceneApi.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components.d.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/button/FYDownloadTableButton.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/map/SceneMap.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionTime.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/table/FYTable.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/menu.js 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/excel.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/data-product/ProdLawEnforceList.vue 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/data-product/base-data-product/PordProblemRecurrence.vue 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/scene/CompSceneImport.vue 103 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/ÏÖ³¡¼à¹Ü³¡¾°ÐÅÏ¢µ¼ÈëÄ£°å.xlsx
Binary files differ
src/api/fysp/dataproductApi.js
@@ -18,7 +18,7 @@
        }
        // æ–‡æ¡£å·²å­˜åœ¨ï¼Œè¿”回文件数据流
        else {
          return res
          return res;
          // const name = Base64.decode(res.headers.get('filename'));
          // const url = window.URL.createObjectURL(res.data);
          // const link = document.createElement('a');
@@ -30,5 +30,23 @@
          // window.URL.revokeObjectURL(url);
        }
      });
  },
  /**
   * èŽ·å–é—®é¢˜æ•´æ”¹æ¸…å•
   */
  fetchProbChangeList(option) {
    return $fysp.post(`dataProduct/problemChange?`, option).then((res) => {
      return res.data;
    });
  },
  /**
   * èŽ·å–é—®é¢˜å¤å‘æ¸…å•
   */
  fetchProbRecurrence(option) {
    return $fysp.post(`dataProduct/problemRecurrence?`, option).then((res) => {
      return res.data;
    });
  }
};
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);
  }
};
src/components.d.ts
@@ -20,6 +20,7 @@
    ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
    ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
    ElButton: typeof import('element-plus/es')['ElButton']
    ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
    ElCalendar: typeof import('element-plus/es')['ElCalendar']
    ElCard: typeof import('element-plus/es')['ElCard']
    ElCascader: typeof import('element-plus/es')['ElCascader']
@@ -36,6 +37,9 @@
    ElDialog: typeof import('element-plus/es')['ElDialog']
    ElDivider: typeof import('element-plus/es')['ElDivider']
    ElDrawer: typeof import('element-plus/es')['ElDrawer']
    ElDropdown: typeof import('element-plus/es')['ElDropdown']
    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
    ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
    ElEmpty: typeof import('element-plus/es')['ElEmpty']
    ElForm: typeof import('element-plus/es')['ElForm']
    ElFormItem: typeof import('element-plus/es')['ElFormItem']
@@ -51,8 +55,11 @@
    ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
    ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
    ElOption: typeof import('element-plus/es')['ElOption']
    ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
    ElPagination: typeof import('element-plus/es')['ElPagination']
    ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
    ElPopover: typeof import('element-plus/es')['ElPopover']
    ElRadio: typeof import('element-plus/es')['ElRadio']
    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
    ElRow: typeof import('element-plus/es')['ElRow']
@@ -71,6 +78,7 @@
    ElTag: typeof import('element-plus/es')['ElTag']
    ElText: typeof import('element-plus/es')['ElText']
    ElTooltip: typeof import('element-plus/es')['ElTooltip']
    ElTransfer: typeof import('element-plus/es')['ElTransfer']
    ElTree: typeof import('element-plus/es')['ElTree']
    ElUpload: typeof import('element-plus/es')['ElUpload']
    Footer: typeof import('./components/core/Footer.vue')['default']
@@ -78,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']
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>
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>
src/components/search-option/FYOptionTime.vue
@@ -19,6 +19,7 @@
const MONTH = 'month';
const DATE = 'date';
const RANGE = 'datetimerange';
const RANGE2 = 'daterange';
export default {
  props: {
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"
@@ -40,6 +40,7 @@
    :cell-class-name="cellClassName"
    @paste="handlePaste"
    @sort-change="handleSortChange"
    :show-overflow-tooltip="true"
    border
  >
    <slot name="table-column" :size="fontSize"></slot>
@@ -219,7 +220,7 @@
  mounted() {
    this.tableHeight = this.calcTableHeight();
    this.onSearch();
  }
  },
};
</script>
src/constants/menu.js
@@ -47,20 +47,25 @@
    icon: 'DataAnalysis',
    name: '基础数据产品',
    children: [
      // {
      //   path: '/fysp/data-product/base/ProdScenseInfo',
      //   icon: 'Document',
      //   name: '场景清单',
      // },
      // {
      //   path: '/fysp/data-product/base/ProdMonitorTaskInfo',
      //   icon: 'Document',
      //   name: '监管清单',
      // },
      // {
      //   path: '/fysp/data-product/base/ProdTreatmentDeviceInfo',
      //   icon: 'Document',
      //   name: '防治设备清单',
      // },
      {
        path: '/fysp/data-product/ProdScenseInfo',
        path: '/fysp/data-product/base/PordProblemRecurrence',
        icon: 'Document',
        name: '场景清单',
      },
      {
        path: '/fysp/data-product/ProdMonitorTaskInfo',
        icon: 'Document',
        name: '监管清单',
      },
      {
        path: '/fysp/data-product/ProdTreatmentDeviceInfo',
        icon: 'Document',
        name: '防治设备清单',
        name: '问题复发清单',
      },
    ]
  },
src/router/index.js
@@ -170,21 +170,27 @@
  {
    // åŸºç¡€äº§å“-场景清单
    name: 'ProdScenseInfo',
    path: '/fysp/data-product/ProdScenseInfo',
    path: '/fysp/data-product/base/ProdScenseInfo',
    component: () => import('@/views/fysp/data-product/base-data-product/ProdScenseInfo.vue')
  },
  {
    // åŸºç¡€äº§å“-监管清单
    name: 'ProdMonitorTaskInfo',
    path: '/fysp/data-product/ProdMonitorTaskInfo',
    path: '/fysp/data-product/base/ProdMonitorTaskInfo',
    component: () => import('@/views/fysp/data-product/base-data-product/ProdMonitorTaskInfo.vue')
  },
  {
    // åŸºç¡€äº§å“-防治设备清单
    name: 'ProdTreatmentDeviceInfo',
    path: '/fysp/data-product/ProdTreatmentDeviceInfo',
    path: '/fysp/data-product/base/ProdTreatmentDeviceInfo',
    component: () => import('@/views/fysp/data-product/base-data-product/ProdTreatmentDeviceInfo.vue')
  },
  {
    // åŸºç¡€äº§å“-问题复发清单
    name: 'PordProblemRecurrence',
    path: '/fysp/data-product/base/PordProblemRecurrence',
    component: () => import('@/views/fysp/data-product/base-data-product/PordProblemRecurrence.vue')
  },
  /**********************************飞羽环境***********************************************/
  {
src/utils/excel.js
@@ -20,7 +20,7 @@
  //     }
  //   }
  // }
  console.log(tables);
  // console.log(tables);
  //------放入以上拿到switch状态,以及下拉框代码,如果没克隆元素则会影响到原表格-------------//
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å¹´MM月'
      )}联合执法清单`;
    }
  },
  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å¹´MM月'
      )}联合执法清单.xlsx`;
      // FileSaver.saveAs(blob, name);
      conversionFromTable('fyTable', name)
    }
  },
  mounted() {
    this.addRefreshEvent(this.$refs.tableRef.doLayout);
src/views/fysp/data-product/base-data-product/PordProblemRecurrence.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,172 @@
<template>
  <FYTable
    id="fyTable"
    @search="onSearch"
    :data="showData"
    :pagination="false"
    ref="tableRef"
  >
    <template #options>
      <!-- åŒºåŽ¿ -->
      <FYOptionLocation
        :allOption="false"
        :level="3"
        :checkStrictly="false"
        v-model:value="formSearch.locations"
      ></FYOptionLocation>
      <!-- åœºæ™¯ç±»åž‹ -->
      <FYOptionScene
        :allOption="false"
        :type="2"
        v-model:value="formSearch.scenetype"
      ></FYOptionScene>
      <!-- æ—¶é—´ -->
      <FYOptionTime
        :initValue="false"
        type="daterange"
        v-model:value="formSearch.timeArr"
        style="width: 300px"
      ></FYOptionTime>
    </template>
    <template #options-expand>
      <el-radio-group v-model="radio">
        <el-radio :value="1">按问题名称统计</el-radio>
        <el-radio :value="2">按问题类型统计</el-radio>
      </el-radio-group>
    </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 }">
          {{ row.index + 1 }}
        </template>
      </el-table-column> -->
      <el-table-column fixed="left" label="唯一编号" width="90" prop="index">
      </el-table-column>
      <el-table-column
        prop="sceneName"
        :show-overflow-tooltip="true"
        label="名称"
      >
      </el-table-column>
      <el-table-column prop="sceneType" label="类型" width="60" />
      <!-- <el-table-column prop="provinceName" label="省份" width="90">
      </el-table-column>
      <el-table-column prop="cityName" label="城市" width="90">
      </el-table-column> -->
      <el-table-column prop="districtName" label="区县" width="90">
      </el-table-column>
      <el-table-column prop="townName" label="街镇" width="110">
      </el-table-column>
      <el-table-column prop="problemType" label="问题类型" width="170">
      </el-table-column>
      <el-table-column v-if="radio == 1" prop="problemName" label="问题名称">
      </el-table-column>
      <el-table-column prop="proNum" label="问题数" width="70">
      </el-table-column>
      <el-table-column prop="changeNum" label="整改数" width="70">
      </el-table-column>
    </template>
  </FYTable>
</template>
<script setup>
import { ref, computed } from 'vue';
import dayjs from 'dayjs';
import dataproductApi from '@/api/fysp/dataproductApi.js';
const radio = ref(1);
const tableRef = ref(null);
const tableData = ref([]);
const formSearch = ref({
  locations: {},
  scenetype: {},
  timeArr: [dayjs().add(-1, 'M').date(1).toDate(), dayjs().toDate()]
});
const option = computed(() => {
  const { locations, scenetype, timeArr } = formSearch.value;
  return {
    provinceCode: locations.pCode,
    cityCode: locations.cCode,
    districtCode: locations.dCode,
    townCode: locations.tCode,
    startTime: dayjs(timeArr[0]).format('YYYY-MM-DD HH:mm:ss'),
    endTime: dayjs(timeArr[1])
      .hour(23)
      .minute(59)
      .second(59)
      .format('YYYY-MM-DD HH:mm:ss'),
    sceneTypeId: scenetype.value
  };
});
const showData = computed(() => {
  let res = [];
  switch (radio.value) {
    case 1:
      res = tableData.value;
      break;
    case 2:
      tableData.value.forEach((tb) => {
        const r = res.find((v) => {
          return v.sceneName == tb.sceneName && v.problemType == tb.problemType;
        });
        if (r == undefined) {
          res.push({ ...tb });
        } else {
          r.proNum += tb.proNum;
          r.changeNum += tb.changeNum;
        }
      });
      break;
    default:
      res = tableData.value;
      break;
  }
  if (tableRef.value) {
    tableRef.value.doLayout();
  }
  return res;
});
const fileName = computed(() => {
  const { locations, scenetype, timeArr } = formSearch.value;
  return `${locations.dName}${dayjs(timeArr[0]).format(
    'YYYYå¹´MM月DD日'
  )}至${dayjs(timeArr[1]).format('YYYYå¹´MM月DD日')}${
    scenetype.label
  }问题复发清单`;
});
const downloadDisabled = computed(() => {
  return tableData.value.length == 0;
});
function onSearch(page, callback) {
  fetchProbRecurrence().finally(() => callback());
}
function fetchProbRecurrence() {
  return dataproductApi.fetchProbRecurrence(option.value).then((res) => {
    tableData.value = res.data;
  });
}
// function handleChange(value) {
//   switch (value) {
//     case 1:
//       break;
//     case 2:
//       break;
//     default:
//       break;
//   }
// }
</script>
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('请上传Excel文件');
    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>