Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/dev' into dev

hmm 8 mesi fa
parent
commit
dca2ab7e44

+ 82 - 4
package-lock.json

@@ -45,6 +45,7 @@
         "image-conversion": "^2.1.1",
         "js-cookie": "3.0.5",
         "jsencrypt": "3.3.2",
+        "luckyexcel": "^1.0.1",
         "nprogress": "0.2.0",
         "ol": "^8.0.0",
         "pinia": "^2.1.7",
@@ -4853,6 +4854,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+    },
     "node_modules/cors": {
       "version": "2.8.5",
       "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz",
@@ -7318,8 +7324,7 @@
     "node_modules/inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "node_modules/inherits-browser": {
       "version": "0.1.0",
@@ -7874,6 +7879,62 @@
         "graceful-fs": "^4.1.6"
       }
     },
+    "node_modules/jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+      "dependencies": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
+      }
+    },
+    "node_modules/jszip/node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+    },
+    "node_modules/jszip/node_modules/lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "dependencies": {
+        "immediate": "~3.0.5"
+      }
+    },
+    "node_modules/jszip/node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
+    "node_modules/jszip/node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/jszip/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "node_modules/jszip/node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
     "node_modules/keyv": {
       "version": "4.5.4",
       "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
@@ -8066,6 +8127,14 @@
         "yallist": "^3.0.2"
       }
     },
+    "node_modules/luckyexcel": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/luckyexcel/-/luckyexcel-1.0.1.tgz",
+      "integrity": "sha512-hvbJmCXNp/vST/huA6sieDn32Ib8bd80L9aIu5ZGxniJvZle7VlpHZrl6weLGaEnX99+t7cPAoYGqrqbfZp/AQ==",
+      "dependencies": {
+        "jszip": "^3.5.0"
+      }
+    },
     "node_modules/magic-string": {
       "version": "0.30.11",
       "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.11.tgz",
@@ -9257,6 +9326,11 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
     "node_modules/proj4": {
       "version": "2.12.1",
       "resolved": "https://registry.npmmirror.com/proj4/-/proj4-2.12.1.tgz",
@@ -10325,6 +10399,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+    },
     "node_modules/shebang-command": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -12035,8 +12114,7 @@
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
-      "dev": true
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
     },
     "node_modules/utrie": {
       "version": "1.0.2",

+ 1 - 0
package.json

@@ -54,6 +54,7 @@
     "image-conversion": "^2.1.1",
     "js-cookie": "3.0.5",
     "jsencrypt": "3.3.2",
+    "luckyexcel": "^1.0.1",
     "nprogress": "0.2.0",
     "ol": "^8.0.0",
     "pinia": "^2.1.7",

+ 37 - 0
src/api/dataFilling/fillingManage.ts

@@ -0,0 +1,37 @@
+import request from '@/utils/request';
+import axios from 'axios';
+
+// 创建填报
+export function fillingAdd(data) {
+  return request({
+    url: '/api/dataFilling/report',
+    method: 'post',
+    data: data
+  });
+}
+
+// 填报管理
+export function fillingSelect(data) {
+  return request({
+    url: '/api/dataFilling/select',
+    method: 'post',
+    data: data
+  });
+}
+
+// 表格详情
+export const fillingList = (reportId) => {
+  return request({
+    url: `/api/dataFilling/report_structure/${reportId}`, // 相对于基础 URL 的路径
+    method: 'get'
+  });
+};
+
+// 填报修改
+export function fillingChange(data) {
+  return request({
+    url: '/api/dataFilling/report/',
+    method: 'post',
+    data: data
+  });
+}

+ 13 - 0
src/api/globalMap/index.ts

@@ -96,3 +96,16 @@ export const getVideoInfo = (params) => {
     data: params
   });
 };
+
+// 定点分析列表查询
+export const getEmergencyRescuePointInfoList = (params) => {
+  return request({
+    url: '/api/gateway/v2/get_emergency_rescue_point_info_list',
+    method: 'post',
+    data: {
+      query: {
+        ...params
+      }
+    }
+  });
+};

+ 100 - 4
src/hooks/AMap/useAMap.ts

@@ -1,4 +1,6 @@
 import AMapLoader from '@amap/amap-jsapi-loader';
+import { nanoid } from 'nanoid';
+import { deepClone } from '@/utils';
 
 export function useAMap(options) {
   let AMap, map, nowLayer, labelsLayer, scale, cluster;
@@ -69,9 +71,11 @@ export function useAMap(options) {
     addMarker([item], true);
     options.onMarkerClick(item);
   };
-
-  const addMarker = (points, isSearchItem?: boolean) => {
-    clearMarker('point');
+  // 添加多个点
+  const addMarker = (points, notClean?: boolean) => {
+    if (!notClean) {
+      clearMarker('point');
+    }
     addPoints = points;
     const count = points.length;
     const _renderClusterMarker = function (context) {
@@ -315,6 +319,97 @@ export function useAMap(options) {
       autoRotation: true
     });
   };
+
+  const drawData = (data) => {
+    data.forEach((item) => {
+      let graphic;
+      // debugger
+      if (['rectangle', 'polygon'].includes(item.type)) {
+        graphic = new AMap.Polygon({
+          path: item.path,
+          strokeColor: item.strokeColor,
+          strokeOpacity: item.strokeOpacity,
+          strokeWeight: item.strokeWeight,
+          fillColor: item.fillColor,
+          fillOpacity: item.fillOpacity
+        });
+        map.add(graphic);
+      } else if (item.type === 'circle') {
+        graphic = new AMap.Circle({
+          center: item.center,
+          radius: item.radius,
+          strokeColor: item.strokeColor,
+          strokeOpacity: item.strokeOpacity,
+          strokeWeight: item.strokeWeight,
+          fillColor: item.fillColor,
+          fillOpacity: item.fillOpacity
+        });
+        map.add(graphic);
+      } else if (item.type === 'straightLine') {
+        graphic = new AMap.Polyline({
+          path: item.path,
+          strokeColor: item.strokeColor,
+          strokeOpacity: item.strokeOpacity,
+          strokeWeight: item.strokeWeight,
+          strokeStyle: item.strokeStyle
+        });
+        map.add(graphic);
+      } else if (item.type === 'text') {
+        addText(item);
+      } else if (item.type === 'measureArea') {
+        graphic = new AMap.Polygon({
+          path: item.path,
+          strokeColor: item.strokeColor,
+          strokeOpacity: item.strokeOpacity,
+          strokeWeight: item.strokeWeight,
+          fillColor: item.fillColor,
+          fillOpacity: item.fillOpacity
+        });
+        map.add(graphic);
+        // 计算区域面积
+        const area = Math.round(AMap.GeometryUtil.ringArea(item.path));
+
+        const text = new AMap.Text({
+          position: item.path[item.path.length - 1],
+          text: '区域面积' + area + '平方米',
+          offset: new AMap.Pixel(-20, -20)
+        });
+        data.area = area;
+        map.add(text);
+      } else if (item.type === 'marker') {
+        addMarker([item], true);
+      }
+    });
+  };
+  const addText = (options) => {
+    // 文本覆盖物的样式
+    const textStyle = {
+      fontSize: options.fontSize,
+      color: options.fontColor,
+      borderColor: 'transparent',
+      backgroundColor: 'transparent',
+      borderWidth: 0,
+      cursor: 'pointer' // 鼠标悬停时显示指针
+    };
+
+    // 创建文本覆盖物
+    const text = new AMap.Text({
+      text: options.text, // 文本内容,可以根据需要自定义
+      position: options.lnglat, // 文本位置(经纬度)
+      style: textStyle, // 文本样式
+      zIndex: 100, // 文本层级
+      draggable: false // 是否可拖动(可选)
+    });
+    // 将文本覆盖物添加到地图
+    map.add(text);
+    const id = nanoid();
+    text._opts.extData = {
+      id: id
+    };
+    const data: any = deepClone(options);
+    data.id = id;
+    return { text, data };
+  };
   onMounted(() => {
     initMap(options);
   });
@@ -331,6 +426,7 @@ export function useAMap(options) {
     hideInfo,
     handleHover,
     creatMask,
-    trackPlayback
+    trackPlayback,
+    drawData
   };
 }

+ 0 - 33
src/types/components.d.ts

@@ -21,23 +21,14 @@ declare module 'vue' {
     DistributionMap: typeof import('./../components/Map/YztMap/DistributionMap.vue')['default']
     DrawMap: typeof import('./../components/Map/YztMap/DrawMap.vue')['default']
     Editor: typeof import('./../components/Editor/index.vue')['default']
-    ElAnchor: typeof import('element-plus/es')['ElAnchor']
-    ElAnchorLink: typeof import('element-plus/es')['ElAnchorLink']
     ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
     ElBadge: typeof import('element-plus/es')['ElBadge']
     ElButton: typeof import('element-plus/es')['ElButton']
-    ElCard: typeof import('element-plus/es')['ElCard']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
-    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
     ElCol: typeof import('element-plus/es')['ElCol']
-    ElCollapse: typeof import('element-plus/es')['ElCollapse']
-    ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
     ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
-    ElContainer: typeof import('element-plus/es')['ElContainer']
     ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
-    ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
-    ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
     ElDialog: typeof import('element-plus/es')['ElDialog']
     ElDivider: typeof import('element-plus/es')['ElDivider']
     ElDrawer: typeof import('element-plus/es')['ElDrawer']
@@ -47,45 +38,24 @@ declare module 'vue' {
     ElEmpty: typeof import('element-plus/es')['ElEmpty']
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
-    ElHeader: typeof import('element-plus/es')['ElHeader']
     ElIcon: typeof import('element-plus/es')['ElIcon']
-    ElImage: typeof import('element-plus/es')['ElImage']
     ElInput: typeof import('element-plus/es')['ElInput']
     ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
-    ElLink: typeof import('element-plus/es')['ElLink']
-    ElMain: typeof import('element-plus/es')['ElMain']
     ElMenu: typeof import('element-plus/es')['ElMenu']
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElPagination: typeof import('element-plus/es')['ElPagination']
     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']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
-    ElSegmented: typeof import('element-plus/es')['ElSegmented']
     ElSelect: typeof import('element-plus/es')['ElSelect']
-    ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
-    ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']
-    ElSlider: typeof import('element-plus/es')['ElSlider']
-    ElSpace: typeof import('element-plus/es')['ElSpace']
-    ElStep: typeof import('element-plus/es')['ElStep']
-    ElSteps: typeof import('element-plus/es')['ElSteps']
     ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
-    ElTabPane: typeof import('element-plus/es')['ElTabPane']
-    ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']
     ElText: typeof import('element-plus/es')['ElText']
-    ElTimeline: typeof import('element-plus/es')['ElTimeline']
-    ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
     ElTooltip: typeof import('element-plus/es')['ElTooltip']
-    ElTree: typeof import('element-plus/es')['ElTree']
-    ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
-    ElUpload: typeof import('element-plus/es')['ElUpload']
     ExcelEditor: typeof import('./../components/ExcelEditor/index.vue')['default']
     FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
     FooterSection: typeof import('./../components/FooterSection/index.vue')['default']
@@ -96,9 +66,6 @@ declare module 'vue' {
     HikvisionPlayer: typeof import('./../components/HKVideo/hikvision-player.vue')['default']
     HKVideo: typeof import('./../components/HKVideo/index.vue')['default']
     IconSelect: typeof import('./../components/IconSelect/index.vue')['default']
-    IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default']
-    IEpCaretTop: typeof import('~icons/ep/caret-top')['default']
-    IEpUploadFilled: typeof import('~icons/ep/upload-filled')['default']
     IFrame: typeof import('./../components/iFrame/index.vue')['default']
     ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default']
     ImageUpload: typeof import('./../components/ImageUpload/index.vue')['default']

+ 499 - 14
src/views/comprehensiveGuarantee/electronicDisasterMapManage/index.vue

@@ -4,9 +4,8 @@
     <!--左上事件列表-->
     <div class="event-box">
       <div class="tabs">
-        <div class="tab">事件位置{{!!selectData.event_id}}</div>
-        <el-button type="primary">定点选取</el-button>
-        <!--        <div class="btn">定点选取</div>-->
+        <div class="tab">事件位置</div>
+        <el-button type="primary" @click="toSelect">定点选取</el-button>
       </div>
       <div class="event-list">
         <div v-for="(item, index) in eventList" :key="index" class="event-item" @click="selectEvent(item)">
@@ -14,9 +13,59 @@
         </div>
       </div>
     </div>
-    <div v-show="!!selectData.event_id" class="search-box">
+    <!--应急资源-->
+    <div class="list">
+      <div class="list-header" @click="expand = !expand">
+        <div class="list-title">应急资源</div>
+        <i :class="expand ? 'icon-down' : 'icon-up'" />
+      </div>
+      <div v-show="expand" class="list-content">
+        <div v-for="(item, index) in emergency_resource" :key="index" class="list-item" @click="handleClick(item)">
+          <i :class="!!item.checked ? 'checked1' : 'checked2'" />
+          <div>{{ item.label }}</div>
+        </div>
+      </div>
+    </div>
+    <div v-show="!!selectData.longitude && !!selectData.latitude" class="search-box">
       <div class="search-title">{{ selectData.event_title }}</div>
-      <div v-for="(item, index) in routeData" :key="index" class="route-item" @click="drawRoute(item)">{{ item.policy }} | {{ item.distance / 1000 }}公里</div>
+      <div class="search-item">
+        <div class="text1">选择救灾资源</div>
+        <el-select
+          v-model="queryParams.dataType"
+          class="custom-select select-box"
+          placeholder="请选择"
+          popper-class="custom-select-popper"
+          :teleported="false"
+        >
+          <el-option v-for="item in disaster_relief_material" :key="item.value" :label="item.label" :value="item.value"></el-option>
+        </el-select>
+      </div>
+      <div class="search-item2">
+        <el-input v-model="queryParams.keyword" class="custom-input2" placeholder="请输入关键字搜索" style="width: 200px" />
+        <div class="common-btn-primary" @click="getList">搜索</div>
+      </div>
+      <div class="search-item3">
+        <el-checkbox v-model="checked1">自定义距离</el-checkbox>
+        <el-input v-model="distance" class="custom-input2" placeholder="自定义距离(公里)" style="width: 200px" />
+      </div>
+
+      <div class="list2">
+        <div>总数: {{ total }}个</div>
+        <div class="list-content">
+          <div v-for="(item, index) in dataList" :key="index" class="list-item">
+            <div class="text1">{{ item.name }}</div>
+            <div class="text-box">
+              <div class="text2">{{ item.address }}</div>
+              <div class="text3" @click="handleRoutes(item)">路线</div>
+            </div>
+
+          </div>
+        </div>
+      </div>
+      <div class="text2"></div>
+      <div v-for="(item, index) in routeData" :key="index" class="route-item" @click="drawRoute(item)">
+        {{ item.policy }} | {{ item.distance / 1000 }}公里
+      </div>
     </div>
   </div>
 </template>
@@ -25,12 +74,44 @@
 // 初始化地图
 import { useAMap } from '@/hooks/AMap/useAMap';
 import { getEventActiveList } from '@/api/duty/eventing';
+import { getEmergencyRescuePointInfoList, getPointInfo, getPointInfoList } from '@/api/globalMap';
+import { iconList, pointDetailTemplate } from '@/views/globalMap/data/mapData';
+import { getDictLabel } from '@/utils/dict';
+import {
+  getBuildingProjectDetails,
+  getChemicalcompanyDetails,
+  getChemicalWarehouseDetails,
+  getConstructionSitesDetails,
+  getEmergencyDisasterInfoOfficerDetails,
+  getEmergencyExpertDetails,
+  getEmergencyShelterTypeDetails,
+  getEmergencyTransportResourcesDetails,
+  getGasolinestationDetails,
+  getHospitalDetails,
+  getMajorHazardSourceDetails,
+  getMidmapDzzhDetails,
+  getMiningcompanyDetails,
+  getMiningOperationsDetails,
+  getRainbowDetails,
+  getSchoolDetails,
+  getShipRealtilmePositionDetails,
+  getStationInfoDetails,
+  getTouristAttractionDetails,
+  getUAVDetails,
+  getWarehouseDetails,
+  getWaterloggedRoadsDetails,
+  getYardSitesDetails
+} from '@/api/globalMap/spatialAnalysis';
 
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { emergency_resource, disaster_relief_material } = toRefs<any>(proxy?.useDict('emergency_resource', 'disaster_relief_material'));
+// 应急资源
+let expand = ref(true);
 let AMap, map, driving;
 let end = ref([110.925176, 21.678993]);
 let routeData = ref([]);
 let routeLine, startMarker, endMarker;
-const { getAMap, getMap, creatMask, switchMap } = useAMap({
+const { getAMap, getMap, creatMask, switchMap, addMarker, clearMarker, showInfo, hideInfo } = useAMap({
   el: 'aMap2',
   key: '30d3d8448efd68cb0b284549fd41adcf', // 申请好的Web端开发者Key,首次调用 load 时必填
   version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
@@ -38,7 +119,7 @@ const { getAMap, getMap, creatMask, switchMap } = useAMap({
   center: [110.925175, 22],
   dragEnable: true,
   scrollWheel: true,
-  plugins: ['AMap.Driving', 'AMap.DistrictSearch'],
+  plugins: ['AMap.Driving', 'AMap.DistrictSearch', 'AMap.MarkerCluster'],
   // layers: 'satellite',
   // 加载完成事件
   onLoadCompleted: () => {
@@ -49,17 +130,113 @@ const { getAMap, getMap, creatMask, switchMap } = useAMap({
     //构造路线导航类
     driving = new AMap.Driving({});
   },
-  onMarkerClick: (data) => {}
+  onMarkerClick: (data) => {
+    // 多点位
+    if (data.type === '1') {
+      let path = [];
+      props.pointType.forEach((item) => {
+        path.push(item.component);
+      });
+      getPointInfoList({
+        option: path.toString(),
+        longitude: data.longitude.toString(),
+        latitude: data.latitude.toString()
+      }).then((res) => {
+        const data2 = res.data.list;
+        let content = document.createElement('div');
+        // content.style.cssText = 'transform: scale(' + containerScale().scaleX + ');';
+        content.className = 'point-info';
+        let content2 = '';
+        content2 += '<div class="title-box"><div class="gradient-text">多点位信息</div></div>';
+        content2 += '<div class="icon1"></div>';
+        content2 += '<div class="icon2"></div>';
+        content2 += '<div class="icon3"></div>';
+        content2 += '<div class="icon4"></div>';
+        content.innerHTML = content2;
+        let tableBox = document.createElement('div');
+        tableBox.className = 'table-box';
+        let table = document.createElement('div');
+        table.className = 'table';
+        table.innerHTML = '<div class="point-item"><div class="td3">主题</div><div class="td3">名称</div></div>';
+        data2.forEach((item) => {
+          item.longitude = data.longitude;
+          item.latitude = data.latitude;
+          const div = document.createElement('div');
+          div.className = 'point-item point-item-hover';
+          div.innerHTML =
+            '<div class="td4">' + getDictLabel(point_type.value, item.dataType.toString()) + '</div><div class="td4">' + item.name + '</div>';
+          div.addEventListener('click', () => {
+            handlePointDetails(item);
+          });
+          table.appendChild(div);
+        });
+        tableBox.appendChild(table);
+        content.appendChild(tableBox);
+        let closeBtn = document.createElement('div');
+        closeBtn.className = 'close';
+        closeBtn.onclick = hideInfo;
+        content.appendChild(closeBtn);
+        showInfo(content, [data.longitude, data.latitude], true);
+      });
+    } else {
+      handlePointDetails(data);
+    }
+  }
 });
 let eventList = ref([]);
 let selectData = ref({
-  event_id: '',
-  event_title: ''
+  event_title: '',
+  longitude: '',
+  latitude: ''
+});
+let selectMarker;
+let checked1 = ref(false);
+let distance = ref('');
+let queryParams = reactive({
+  keyword: '',
+  dataType: ''
 });
+let total = ref(0);
+let dataList = ref([]);
+const toSelect = () => {
+  map.on('click', handleClickMap);
+};
+const handleClickMap = (e) => {
+  map.off('click', handleClickMap);
+  selectEvent(
+    {
+      longitude: e.lnglat.getLng(),
+      latitude: e.lnglat.getLat()
+    },
+    true
+  );
+};
 // 选中事件
-const selectEvent = (item) => {
+const selectEvent = (item, unFitView) => {
   selectData.value = item;
-  calculateRoutes([item.longitude, item.latitude], end.value, 0);
+  if (!!selectMarker) {
+    selectMarker.setMap(null);
+    selectMarker = null;
+  }
+  // 以 icon URL 的形式创建一个途经点
+  const icon = new AMap.Icon({
+    size: new AMap.Size(19, 31),
+    image: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png'
+  });
+  selectMarker = new AMap.Marker({
+    position: new AMap.LngLat(item.longitude, item.latitude),
+    icon: icon,
+    offset: new AMap.Pixel(-13, -30)
+  });
+  selectMarker.setMap(map);
+  if (!unFitView) {
+    map.setFitView([selectMarker]);
+  }
+};
+const handleRoutes = (item) => {
+  const start = [item.longitude, item.latitude];
+  const end = [selectData.value.longitude, selectData.value.latitude];
+  calculateRoutes(start, end, 0);
 };
 // 计算并展示三条路线
 const calculateRoutes = (start, end, index) => {
@@ -101,15 +278,25 @@ const drawRoute = (route) => {
     routeLine.setMap(null);
     routeLine = null;
   }
+  const icon1 = new AMap.Icon({
+    size: new AMap.Size(19, 31),
+    image: 'https://webapi.amap.com/theme/v1.3/markers/n/start.png'
+  });
+  const icon2 = new AMap.Icon({
+    size: new AMap.Size(19, 31),
+    image: 'https://webapi.amap.com/theme/v1.3/markers/n/end.png'
+  });
   startMarker = new AMap.Marker({
     position: path[0],
-    icon: 'https://webapi.amap.com/theme/v1.3/markers/n/start.png',
+    icon: icon1,
+    offset: new AMap.Pixel(-13, -30),
     map: map
   });
 
   endMarker = new AMap.Marker({
     position: path[path.length - 1],
-    icon: 'https://webapi.amap.com/theme/v1.3/markers/n/end.png',
+    icon: icon2,
+    offset: new AMap.Pixel(-13, -30),
     map: map
   });
   routeLine = new AMap.Polyline({
@@ -142,6 +329,236 @@ const parseRouteToPath = (route) => {
 
   return path;
 };
+const handlePointDetails = (data) => {
+  let methodList = {
+    '1': getEmergencyExpertDetails,
+    '2': getWarehouseDetails,
+    '3': getEmergencyShelterTypeDetails,
+    '4': getWaterloggedRoadsDetails,
+    '5': getSchoolDetails,
+    '6': getHospitalDetails,
+    '7': getGasolinestationDetails,
+    '8': getMiningcompanyDetails,
+    // '9': getChemicalcompanyDetails,
+    '10': getShipRealtilmePositionDetails,
+    '11': getChemicalcompanyDetails,
+    '12': getChemicalcompanyDetails,
+    '13': getChemicalcompanyDetails,
+    '14': getChemicalcompanyDetails,
+    '15': getUAVDetails,
+    '16': getRainbowDetails,
+    '17': getMidmapDzzhDetails,
+    '18': getMiningOperationsDetails,
+    '21': getBuildingProjectDetails,
+    '22': getChemicalWarehouseDetails,
+    '23': getMajorHazardSourceDetails,
+    '24': getStationInfoDetails,
+    '25': getYardSitesDetails,
+    '26': getTouristAttractionDetails,
+    '27': getConstructionSitesDetails,
+    '28': getEmergencyTransportResourcesDetails,
+    '29': getEmergencyDisasterInfoOfficerDetails
+  };
+  let titleList = {
+    '1': '专家信息',
+    '2': '物资与装备仓库信息',
+    '3': '避难所信息',
+    '4': '易涝点信息',
+    '5': '学校信息',
+    '6': '医院信息',
+    '7': '加油站信息',
+    '8': '非煤矿山企业信息',
+    '9': '危化企业信息',
+    '10': '船舶动态信息',
+    '11': '危险化学品经营企业信息',
+    '12': '危险化学品生产企业信息',
+    '13': '危险化学品使用企业(使用许可)信息',
+    '14': '化工企业(不发使用许可)信息',
+    '15': '无人机信息',
+    '16': '雨窝点',
+    '17': '地质灾害隐患点',
+    '18': '矿山施工',
+    '19': '工矿商贸',
+    '20': '气象灾害防御重点单位',
+    '21': '建筑工程',
+    '22': '储罐信息',
+    '23': '重大危险源',
+    '24': '客运站',
+    '25': '堆场',
+    '26': '旅游场所',
+    '27': '在建工地',
+    '28': '运输资源',
+    '29': '灾害信息员'
+  };
+  let method = methodList[data.dataType];
+  let title = !!titleList[data.dataType] ? titleList[data.dataType] : '信息';
+  if (!method) return;
+  method(data.id).then((res) => {
+    if (!!pointDetailTemplate[data.dataType]) {
+      let div = document.createElement('div');
+      // div.style.cssText = 'transform: scale(' + containerScale().scaleX + ');';
+      div.className = 'point-info';
+      let titleDom = document.createElement('div');
+      titleDom.className = 'title-box';
+      titleDom.innerHTML = '<div class="gradient-text">' + title + '</div></div>';
+      div.appendChild(titleDom);
+      if (data.dataType === 2) {
+        let btnBox = document.createElement('div');
+        let btn = document.createElement('div');
+        btnBox.className = 'flex';
+        btn.className = 'btn';
+        btn.innerHTML = '<div class="video-icon"></div><div>物资详情</div>';
+        btn.onclick = () => {
+          emits('handleShowWarehouse', data);
+        };
+        btnBox.appendChild(btn);
+        div.appendChild(btnBox);
+      } else if (data.dataType === 4) {
+        let btnBox = document.createElement('div');
+        let btn = document.createElement('div');
+        btnBox.className = 'flex';
+        btn.className = 'btn';
+        btn.innerHTML = '<div class="video-icon"></div><div>附近视频</div>';
+        btn.onclick = () => {
+          emits('handleShowVideo', data);
+        };
+        btnBox.appendChild(btn);
+        div.appendChild(btnBox);
+      }
+      let icon1 = document.createElement('div');
+      icon1.className = 'icon1';
+      let icon2 = document.createElement('div');
+      icon2.className = 'icon2';
+      let icon3 = document.createElement('div');
+      icon3.className = 'icon3';
+      let icon4 = document.createElement('div');
+      icon4.className = 'icon4';
+      div.appendChild(icon1);
+      div.appendChild(icon2);
+      div.appendChild(icon3);
+      div.appendChild(icon4);
+      let table = document.createElement('div');
+      table.className = 'table-box';
+      let content = '';
+      content += '<div class="table">';
+      const newData = filterTd(res.rows[0], data.dataType);
+      newData.forEach((item) => {
+        if (item.type === 'shortText') {
+          content += '<div class="tr">';
+          item.data.forEach((item2) => {
+            content += '<div class="point-item">';
+            content += '<div class="td1">' + item2.label + '</div><div class="td2">' + item2.value + '</div>';
+            content += '</div>';
+          });
+          content += '</div>';
+        } else {
+          content += '<div class="point-item2">';
+          content += '<div class="td1">' + item.data[0].label + '</div><div class="td2">' + item.data[0].value + '</div>';
+          content += '</div>';
+        }
+      });
+      content += '</div>';
+      table.innerHTML = content;
+      div.appendChild(table);
+      let closeBtn = document.createElement('div');
+      closeBtn.className = 'close';
+      closeBtn.onclick = hideInfo;
+      div.appendChild(closeBtn);
+      showInfo(div, [data.longitude, data.latitude], true);
+    }
+  });
+};
+const filterTd = (obj, dataType) => {
+  let data = [];
+  let tempData = {};
+  let i = 0;
+  for (let key in obj) {
+    let keyLabel = pointDetailTemplate[dataType][key];
+    if (!!keyLabel) {
+      if (i === 2) {
+        i = 0;
+      }
+      const value = !!obj[key] ? obj[key] : '';
+      if (value && value.length > 8) {
+        if (i === 0) {
+          data.push({ type: 'longText', data: [{ label: keyLabel, value: value }] });
+          i = 0;
+        } else {
+          tempData = { type: 'longText', data: [{ label: keyLabel, value: value }] };
+        }
+      } else {
+        if (i === 0) {
+          data.push({ type: 'shortText', data: [{ label: keyLabel, value: value }] });
+        } else {
+          data[data.length - 1].data.push({ label: keyLabel, value: value });
+        }
+        i++;
+        if (!!tempData && JSON.stringify(tempData) !== '{}') {
+          data.push(tempData);
+          tempData = {};
+          i = 0;
+        }
+      }
+    }
+  }
+  if (!!tempData && JSON.stringify(tempData) !== '{}') {
+    data.push(tempData);
+  }
+  if (data[data.length - 1].data && data[data.length - 1].data.length === 1 && data[data.length - 1].type === 'shortText') {
+    data[data.length - 1].data[1] = { label: '', value: '' };
+  }
+  return data;
+};
+const handleClick = (item) => {
+  item.checked = !item.checked;
+  let path = [];
+  emergency_resource.value.forEach((item) => {
+    if (!!item.checked) {
+      path.push(item.value);
+    }
+  });
+  if (path.length === 0) {
+    return clearMarker('point');
+  }
+  getPointInfo(path.toString()).then((res) => {
+    const data = res.data && res.data.list ? res.data?.list : [];
+    data.forEach((item2) => {
+      // 获取图标
+      if (iconList[item2.dataType]) {
+        item2.icon = iconList[item2.dataType].image;
+        item2.image = iconList[item2.dataType].image;
+        item2.imageHover = iconList[item2.dataType].imageHover;
+        item2.size = iconList[item2.dataType].size;
+      } else {
+        item2.icon = iconList['common'].image;
+        item2.image = iconList['common'].image;
+        item2.imageHover = iconList['common'].imageHover;
+        item2.size = iconList['common'].size;
+      }
+      if (item2.materia_name) {
+        item2.name = item2.materia_name;
+      }
+      item2.parentId = item.component;
+      item2.lnglat = [item2.longitude, item2.latitude];
+    });
+    addMarker(data);
+  });
+};
+// 搜索列表
+const getList = () => {
+  const params = {
+    dataType: queryParams.dataType,
+    keyword: queryParams.keyword,
+    distance: ''
+  };
+  if (checked1.value) {
+    params.distance = distance.value;
+  }
+  getEmergencyRescuePointInfoList(params).then((res) => {
+    dataList.value = res.rows;
+    total.value = res.total;
+  });
+};
 const initData = () => {
   getEventActiveList().then((res) => {
     eventList.value = res.data;
@@ -150,9 +567,15 @@ const initData = () => {
 onMounted(() => {
   initData();
 });
+onUnmounted(() => {
+  if (!!map) {
+    map.on('click', handleClickMap);
+  }
+});
 </script>
 
 <style lang="scss" scoped>
+@import '@/components/Map/map.scss';
 .map-container {
   width: 100%;
   height: 100%;
@@ -162,6 +585,10 @@ onMounted(() => {
     height: 100%;
     position: relative;
     overflow: hidden;
+    :deep(.amap-logo),
+    :deep(.amap-copyright) {
+      display: none !important;
+    }
   }
   .event-box {
     position: absolute;
@@ -223,5 +650,63 @@ onMounted(() => {
       color: #91ecf3;
     }
   }
+  .list {
+    position: absolute;
+    bottom: 10px;
+    left: 10px;
+    width: 240px;
+    background-color: #213567;
+    .list-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 5px 10px;
+      color: #fff;
+      cursor: pointer;
+      .icon-down {
+        background: url('@/assets/images/menu/down.png') no-repeat;
+      }
+      .icon-up {
+        background: url('@/assets/images/menu/up.png') no-repeat;
+      }
+      .icon-down,
+      .icon-up {
+        width: 18px;
+        height: 10px;
+        background-size: 100% 100%;
+      }
+    }
+    .list-content {
+      .list-item {
+        display: flex;
+        align-items: center;
+        font-size: 14px;
+        color: #fff;
+        cursor: pointer;
+        padding: 5px 10px;
+        &:hover {
+          background-color: #335293;
+        }
+        .checked1 {
+          background: url('@/assets/images/menu/checked-box2.png') no-repeat;
+        }
+        .checked2 {
+          background: url('@/assets/images/menu/checked-box.png') no-repeat;
+        }
+        .checked1,
+        .checked2 {
+          display: inline-block;
+          width: 20px;
+          height: 20px;
+          background-size: 100% 100%;
+          margin-right: 5px;
+        }
+      }
+    }
+  }
+}
+.amap-icon img {
+  width: 25px;
+  height: 34px;
 }
 </style>

+ 12 - 12
src/views/comprehensiveGuarantee/materialReserves/materialsDeclaration.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div v-show="!materialsDeclarationViewState.show && !materialsDeclarationEidtState.show && !materialsDeclarationAddState.show">
+    <div v-show="!materialsDeclarationViewState.show && !materialsDistributionEditState.show && !materialsDeclarationAddState.show">
       <h1>物资储备管理</h1>
       <el-row :gutter="10" class="mb8">
         <el-col :span="1.5">
@@ -35,10 +35,10 @@
         @pagination="fetchWorkrData"
       />
     </div>
+    <MaterialsDeclarationView v-if="materialsDeclarationViewState.show" :event-id="materialsDeclarationViewState.eventId" @close="handleCancel" />
+    <MaterialsDistributionEdit v-if="materialsDistributionEditState.show" :event-id="materialsDistributionEditState.eventId" @close="handleCancel" />
+    <MaterialsDeclarationAdd v-if="materialsDeclarationAddState.show" @close="handleCancel" />
   </div>
-  <MaterialsDeclarationView v-if="materialsDeclarationViewState.show" :event-id="materialsDeclarationViewState.eventId" @close="handleCancel" />
-  <MaterialsDeclarationEidt v-if="materialsDeclarationEidtState.show" :event-id="materialsDeclarationEidtState.eventId" @close="handleCancel" />
-  <MaterialsDeclarationAdd v-if="materialsDeclarationAddState.show" @close="handleCancel" />
 </template>
 
 <script setup lang="ts">
@@ -47,7 +47,7 @@ import { ElTable, ElTableColumn, ElForm, ElFormItem, ElInput, ElButton, ElText }
 import { ComponentInternalInstance, getCurrentInstance } from 'vue';
 import Pagination from '@/components/Pagination/index.vue'; // 假设这是分页组件的路径
 import MaterialsDeclarationView from './materialsDeclarationView.vue'; // 查看详情组件
-import MaterialsDeclarationEidt from './materialsDeclarationEidt.vue'; // 编辑组件
+import MaterialsDistributionEdit from './materialsDistributionEdit.vue'; // 编辑组件
 import MaterialsDeclarationAdd from './materialsDeclarationAdd.vue'; // 编辑组件
 const loading = ref(true);
 const showSearch = ref(true);
@@ -83,7 +83,7 @@ const mockData = [
     declaration_date: '2024-10-29 12:23:00',
     amount_declared: 1000,
     application_unit: '茂名市应急管理局',
-    declarant: '张三',
+    declarant: 'XX',
     approval_status: '未审批'
   },
   {
@@ -91,7 +91,7 @@ const mockData = [
     declaration_date: '2024-10-30 12:23:00',
     amount_declared: 1001,
     application_unit: '茂名市应急管理局',
-    declarant: '李四',
+    declarant: 'XX',
     approval_status: '审批通过'
   },
   {
@@ -99,7 +99,7 @@ const mockData = [
     declaration_date: '2024-10-31 12:23:00',
     amount_declared: 1002,
     application_unit: '茂名市应急管理局',
-    declarant: '王五',
+    declarant: 'XX',
     approval_status: '审批不通过'
   }
 ];
@@ -127,7 +127,7 @@ let materialsDeclarationViewState = reactive({
   eventId: ''
 });
 
-let materialsDeclarationEidtState = reactive({
+let materialsDistributionEditState = reactive({
   show: false,
   eventId: ''
 });
@@ -145,13 +145,13 @@ const handleView = (row: any) => {
 };
 
 const handleUpdate = (row: any) => {
-  materialsDeclarationEidtState.eventId = row.id;
-  materialsDeclarationEidtState.show = true;
+  materialsDistributionEditState.eventId = row.id;
+  materialsDistributionEditState.show = true;
 };
 
 const handleCancel = () => {
   materialsDeclarationViewState.show = false;
-  materialsDeclarationEidtState.show = false;
+  materialsDistributionEditState.show = false;
   materialsDeclarationAddState.show = false;
 };
 

+ 35 - 36
src/views/comprehensiveGuarantee/materialReserves/materialsDeclarationEidt.vue

@@ -3,7 +3,9 @@
     <el-row :gutter="20">
       <el-col :span="6">
         <h2>编辑</h2>
-        <p class="report-period">申报金额:20000.00元</p>
+        <el-form-item label="调度目的:">
+          <el-input v-model="queryParams.purpose" placeholder="请输入内容" clearable></el-input>
+        </el-form-item>
       </el-col>
       <el-col :lg="24" :xs="24">
         <el-table :data="tableData" border height="400">
@@ -42,23 +44,13 @@
               ></span>
             </template>
           </el-table-column>
-          <el-table-column label="物资单价(元)" prop="unitPrice">
+          <el-table-column label="物资用途" prop="materials">
             <template #default="{ row, $index }">
               <span
                 class="editable-span"
                 contenteditable="true"
-                @blur="saveEdit($index, 'unitPrice', $event.target.innerText)"
-                v-text="row.unitPrice"
-              ></span>
-            </template>
-          </el-table-column>
-          <el-table-column label="物资用途" prop="purpose">
-            <template #default="{ row, $index }">
-              <span
-                class="editable-span"
-                contenteditable="true"
-                @blur="saveEdit($index, 'purpose', $event.target.innerText)"
-                v-text="row.purpose"
+                @blur="saveEdit($index, 'materials', $event.target.innerText)"
+                v-text="row.materials"
               ></span>
             </template>
           </el-table-column>
@@ -84,42 +76,42 @@
 <script setup lang="ts">
 import { ref, onMounted } from 'vue';
 import { ElTable, ElButton, ElCol, ElRow, ElTableColumn } from 'element-plus';
-import { defineProps, defineEmits } from 'vue';
+import axios from 'axios'; // 引入axios
 
+// 定义父组件传递的属性
 const props = defineProps({
   eventId: String
 });
 
+// 定义子组件触发的事件
 const emits = defineEmits(['close']);
 
-const tableData = ref<any[]>([]);
+// 定义查询参数对象,包含调度目的
+const queryParams = ref({
+  purpose: '' // 调度目的
+});
 
-const loadFromLocalStorage = () => {
-  const storedData = localStorage.getItem('tableData');
-  if (storedData) {
-    tableData.value = JSON.parse(storedData);
-  } else {
-    tableData.value = [
-      { materialType: '三防物资', materialName: '雨衣', quantity: '100', unitPrice: '5.00', purpose: '2025年汛期储备' },
-      { materialType: '三防物资', materialName: '水鞋', quantity: '10', unitPrice: '5000.00', purpose: '2025年汛期储备' },
-      { materialType: '三防物资', materialName: '雨伞', quantity: '50', unitPrice: '100.00', purpose: '2025年汛期储备' }
-    ];
-  }
-};
+// 表格数据
+const tableData = ref([
+  { materialType: '三防物资', materialName: '雨衣', quantity: '100', materials: '高州泰利台风前置' },
+  { materialType: '三防物资', materialName: '水鞋', quantity: '10', materials: '高州泰利台风前置' },
+  { materialType: '三防物资', materialName: '雨伞', quantity: '50', materials: '高州泰利台风前置' }
+]);
 
+// 添加默认行
 const addDefaultRow = () => {
   const newRow = {
     materialType: '',
     materialName: '',
     quantity: '',
-    unitPrice: '',
-    purpose: ''
+    materials: ''
   };
   tableData.value.push(newRow);
 };
 
+// 保存编辑
 const saveEdit = (index: number, key: string, value: string) => {
-  if (key === 'quantity' || key === 'unitPrice') {
+  if (key === 'quantity') {
     const numValue = parseFloat(value);
     if (isNaN(numValue)) {
       return;
@@ -127,22 +119,29 @@ const saveEdit = (index: number, key: string, value: string) => {
     value = numValue.toString();
   }
   tableData.value[index][key] = value;
-  localStorage.setItem('tableData', JSON.stringify(tableData.value));
 };
 
+// 处理新增一行
 const handleAddRow = () => addDefaultRow();
 
+// 处理返回
 const handleReturn = () => {
   emits('close');
 };
 
-const handleSave = () => {
-  // 这里可以添加保存到服务器的逻辑
-  console.log('数据已保存:', tableData.value);
+// 处理保存
+const handleSave = async () => {
+  try {
+    const response = await axios.post('/api/submitData', { data: tableData.value, purpose: queryParams.value.purpose });
+    console.log('数据已成功提交:', response.data);
+  } catch (error) {
+    console.error('提交数据时发生错误:', error);
+  }
 };
 
+// 页面加载完成后执行
 onMounted(() => {
-  loadFromLocalStorage();
+  // 如果需要从后端加载数据,可以在这里调用相应的API
 });
 </script>
 

+ 9 - 22
src/views/comprehensiveGuarantee/materialReserves/materialsDeclarationView.vue

@@ -34,7 +34,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, onMounted } from 'vue';
+import { ref } from 'vue';
 import { ElTable, ElButton, ElCol, ElRow, ElTableColumn } from 'element-plus';
 import { defineProps, defineEmits } from 'vue';
 
@@ -44,31 +44,18 @@ const props = defineProps({
 
 const emits = defineEmits(['close']);
 
-const tableData = ref<any[]>([]);
-const approvalStatus = ref('待审批'); // 初始状态设为待审批
+// 静态数据
+const tableData = ref([
+  { materialType: '三防物资', materialName: '雨衣', quantity: '100', unitPrice: '5.00', purpose: '2025年汛期储备' },
+  { materialType: '三防物资', materialName: '水鞋', quantity: '10', unitPrice: '5000.00', purpose: '2025年汛期储备' },
+  { materialType: '三防物资', materialName: '雨伞', quantity: '50', unitPrice: '100.00', purpose: '2025年汛期储备' }
+]);
+
+const approvalStatus = ref('待审批');
 
-const loadFromLocalStorage = () => {
-  const storedData = localStorage.getItem('tableData');
-  const storedApprovalStatus = localStorage.getItem('approvalStatus');
-  if (storedData) {
-    tableData.value = JSON.parse(storedData);
-  } else {
-    tableData.value = [
-      { materialType: '三防物资', materialName: '雨衣', quantity: '100', unitPrice: '5.00', purpose: '2025年汛期储备' },
-      { materialType: '三防物资', materialName: '水鞋', quantity: '10', unitPrice: '5000.00', purpose: '2025年汛期储备' },
-      { materialType: '三防物资', materialName: '雨伞', quantity: '50', unitPrice: '100.00', purpose: '2025年汛期储备' }
-    ];
-  }
-  if (storedApprovalStatus) {
-    approvalStatus.value = storedApprovalStatus;
-  }
-};
 const handleReturn = () => {
   emits('close');
 };
-onMounted(() => {
-  loadFromLocalStorage();
-});
 </script>
 
 <style scoped>

+ 9 - 9
src/views/comprehensiveGuarantee/materialReserves/materialsDistribution.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div v-show="!materialsDeclarationViewState.show && !materialsDeclarationEidtState.show && !materialsDistributionAddState.show">
+    <div v-show="!materialsDistributionViewState.show && !materialsDeclarationEidtState.show && !materialsDistributionAddState.show">
       <h1>物资储备管理</h1>
       <el-row :gutter="10" class="mb8">
         <el-col :span="1.5">
@@ -35,10 +35,10 @@
         @pagination="fetchWorkrData"
       />
     </div>
+    <MaterialsDistributionView v-if="materialsDistributionViewState.show" :event-id="materialsDistributionViewState.eventId" @close="handleCancel" />
+    <MaterialsDeclarationEidt v-if="materialsDeclarationEidtState.show" :event-id="materialsDeclarationEidtState.eventId" @close="handleCancel" />
+    <MaterialsDistributionAdd v-if="materialsDistributionAddState.show" @close="handleCancel" />
   </div>
-  <MaterialsDeclarationView v-if="materialsDeclarationViewState.show" :event-id="materialsDeclarationViewState.eventId" @close="handleCancel" />
-  <MaterialsDeclarationEidt v-if="materialsDeclarationEidtState.show" :event-id="materialsDeclarationEidtState.eventId" @close="handleCancel" />
-  <MaterialsDistributionAdd v-if="materialsDistributionAddState.show" @close="handleCancel" />
 </template>
 
 <script setup lang="ts">
@@ -46,7 +46,7 @@ import { onMounted, reactive, ref, toRefs } from 'vue';
 import { ElTable, ElTableColumn, ElButton, ElText } from 'element-plus';
 import { ComponentInternalInstance, getCurrentInstance } from 'vue';
 import Pagination from '@/components/Pagination/index.vue'; // 假设这是分页组件的路径
-import MaterialsDeclarationView from './materialsDeclarationView.vue'; // 查看详情组件
+import MaterialsDistributionView from './materialsDistributionView.vue'; // 查看详情组件
 import MaterialsDeclarationEidt from './materialsDeclarationEidt.vue'; // 编辑组件
 import MaterialsDistributionAdd from './materialsDistributionAdd.vue'; // 编辑组件
 const loading = ref(true);
@@ -122,7 +122,7 @@ const resetQuery = () => {
   handleQuery();
 };
 
-let materialsDeclarationViewState = reactive({
+let materialsDistributionViewState = reactive({
   show: false,
   eventId: ''
 });
@@ -140,8 +140,8 @@ const handleAdd = () => {
   materialsDistributionAddState.show = true;
 };
 const handleView = (row: any) => {
-  materialsDeclarationViewState.eventId = row.id;
-  materialsDeclarationViewState.show = true;
+  materialsDistributionViewState.eventId = row.id;
+  materialsDistributionViewState.show = true;
 };
 
 const handleUpdate = (row: any) => {
@@ -150,7 +150,7 @@ const handleUpdate = (row: any) => {
 };
 
 const handleCancel = () => {
-  materialsDeclarationViewState.show = false;
+  materialsDistributionViewState.show = false;
   materialsDeclarationEidtState.show = false;
   materialsDistributionAddState.show = false;
 };

+ 174 - 0
src/views/comprehensiveGuarantee/materialReserves/materialsDistributionEdit.vue

@@ -0,0 +1,174 @@
+<template>
+  <div class="app-container p-2">
+    <el-row :gutter="20">
+      <el-col :span="6">
+        <h2>编辑</h2>
+        <p class="report-period">申报金额:20000.00元</p>
+      </el-col>
+      <el-col :lg="24" :xs="24">
+        <el-table :data="tableData" border height="400">
+          <el-table-column label="序号" prop="seqNo" width="80">
+            <template #default="{ $index }">
+              <span>{{ $index + 1 }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="物资类型" prop="materialType">
+            <template #default="{ row, $index }">
+              <span
+                class="editable-span"
+                contenteditable="true"
+                @blur="saveEdit($index, 'materialType', $event.target.innerText)"
+                v-text="row.materialType"
+              ></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="物资名称" prop="materialName">
+            <template #default="{ row, $index }">
+              <span
+                class="editable-span"
+                contenteditable="true"
+                @blur="saveEdit($index, 'materialName', $event.target.innerText)"
+                v-text="row.materialName"
+              ></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="物资数量(件)" prop="quantity">
+            <template #default="{ row, $index }">
+              <span
+                class="editable-span"
+                contenteditable="true"
+                @blur="saveEdit($index, 'quantity', $event.target.innerText)"
+                v-text="row.quantity"
+              ></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="物资单价(元)" prop="unitPrice">
+            <template #default="{ row, $index }">
+              <span
+                class="editable-span"
+                contenteditable="true"
+                @blur="saveEdit($index, 'unitPrice', $event.target.innerText)"
+                v-text="row.unitPrice"
+              ></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="物资用途" prop="purpose">
+            <template #default="{ row, $index }">
+              <span
+                class="editable-span"
+                contenteditable="true"
+                @blur="saveEdit($index, 'purpose', $event.target.innerText)"
+                v-text="row.purpose"
+              ></span>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-col>
+      <div class="common-dialog-footer" style="width: 100%; justify-content: center; display: flex">
+        <el-row :span="24" :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" @click="handleAddRow">新增一项</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" @click="handleSave">提交</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" @click="handleReturn">取消</el-button>
+          </el-col>
+        </el-row>
+      </div>
+    </el-row>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue';
+import { ElTable, ElButton, ElCol, ElRow, ElTableColumn } from 'element-plus';
+import axios from 'axios'; // 引入axios
+
+const props = defineProps({
+  eventId: String
+});
+
+const emits = defineEmits(['close']);
+
+const tableData = ref([
+  { materialType: '三防物资', materialName: '雨衣', quantity: '100', unitPrice: '5.00', purpose: '2025年汛期储备' },
+  { materialType: '三防物资', materialName: '水鞋', quantity: '10', unitPrice: '5000.00', purpose: '2025年汛期储备' },
+  { materialType: '三防物资', materialName: '雨伞', quantity: '50', unitPrice: '100.00', purpose: '2025年汛期储备' }
+]);
+
+const addDefaultRow = () => {
+  const newRow = {
+    materialType: '',
+    materialName: '',
+    quantity: '',
+    unitPrice: '',
+    purpose: ''
+  };
+  tableData.value.push(newRow);
+};
+
+const saveEdit = (index: number, key: string, value: string) => {
+  if (key === 'quantity' || key === 'unitPrice') {
+    const numValue = parseFloat(value);
+    if (isNaN(numValue)) {
+      return;
+    }
+    value = numValue.toString();
+  }
+  tableData.value[index][key] = value;
+};
+
+const handleAddRow = () => addDefaultRow();
+
+const handleReturn = () => {
+  emits('close');
+};
+
+const handleSave = async () => {
+  try {
+    const response = await axios.post('/api/submitData', { data: tableData.value });
+    console.log('数据已成功提交:', response.data);
+  } catch (error) {
+    console.error('提交数据时发生错误:', error);
+  }
+};
+
+onMounted(() => {
+  // 如果需要从后端加载数据,可以在这里调用相应的API
+});
+</script>
+
+<style scoped>
+.app-container {
+  font-family: Avenir, Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  text-align: center;
+  color: #2c3e50;
+}
+.report-period {
+  margin-top: 10px;
+  font-size: 14px;
+  color: #606266;
+}
+.editable-span {
+  cursor: pointer;
+  display: inline-block;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.editable-span[contenteditable='true'] {
+  white-space: normal;
+  outline: none; /* 移除编辑时的焦点边框 */
+}
+.editable-span[contenteditable='true']:empty::before {
+  content: attr(data-placeholder); /* 可选:为空时显示占位符 */
+  color: #999;
+}
+.common-dialog-footer {
+  margin-top: 20px;
+}
+</style>

+ 81 - 0
src/views/comprehensiveGuarantee/materialReserves/materialsDistributionView.vue

@@ -0,0 +1,81 @@
+<template>
+  <div class="app-container p-2">
+    <el-row :gutter="20">
+      <el-col :span="20">
+        <h2>详情</h2>
+        <div class="report-info">
+          <p class="report-amount">调度目的:应对高州泰利台风前置</p>
+          <p class="approval-status">审批情况:{{ approvalStatus }}</p>
+        </div>
+      </el-col>
+      <el-col :lg="24" :xs="24">
+        <el-table :data="tableData" border height="400">
+          <el-table-column label="序号" prop="seqNo" width="80">
+            <template #default="{ $index }">
+              <span>{{ $index + 1 }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="物资类型" prop="materialType"></el-table-column>
+          <el-table-column label="物资名称" prop="materialName"></el-table-column>
+          <el-table-column label="物资数量(件)" prop="quantity"></el-table-column>
+          <el-table-column label="物资用途" prop="purpose"></el-table-column>
+        </el-table>
+      </el-col>
+      <div class="common-dialog-footer" style="width: 100%; justify-content: center; display: flex">
+        <el-row :span="24" :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="danger" @click="handleReturn">返回</el-button>
+          </el-col>
+        </el-row>
+      </div>
+    </el-row>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue';
+import { ElTable, ElButton, ElCol, ElRow, ElTableColumn } from 'element-plus';
+import { defineProps, defineEmits } from 'vue';
+
+const props = defineProps({
+  eventId: String
+});
+
+const emits = defineEmits(['close']);
+
+// 静态数据
+const tableData = ref([
+  { materialType: '三防物资', materialName: '雨衣', quantity: '100', purpose: '高州泰利台风前置' },
+  { materialType: '三防物资', materialName: '水鞋', quantity: '10', purpose: '高州泰利台风前置' },
+  { materialType: '三防物资', materialName: '雨伞', quantity: '50', purpose: '高州泰利台风前置' }
+]);
+
+const approvalStatus = ref('待审批');
+
+const handleReturn = () => {
+  emits('close');
+};
+</script>
+
+<style scoped>
+.app-container {
+  font-family: Avenir, Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  text-align: center;
+  color: #2c3e50;
+}
+
+.report-info {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-top: 10px;
+}
+
+.report-amount,
+.approval-status {
+  font-size: 14px;
+  color: #606266;
+}
+</style>

+ 89 - 85
src/views/dataFilling/fillingAdd.vue

@@ -3,29 +3,37 @@
     <el-row :gutter="20">
       <el-col :lg="30" :xs="24" style="">
         <el-row :span="24" :gutter="10">
+          <!-- 联系人姓名 -->
           <el-col :span="6">
             <el-form-item label="联系人姓名:" prop="table_name" label-width="auto">
-              <el-input v-model="table_name" placeholder="请输入联系人姓名"></el-input>
+              <el-input v-model="creator_name" placeholder="请输入联系人姓名"></el-input>
             </el-form-item>
           </el-col>
+          <!-- 联系电话 -->
           <el-col :span="6">
-            <el-form-item label="联系电话:" prop="table_name" label-width="auto">
-              <el-input v-model="table_phone" placeholder="请输入联系电话"></el-input>
+            <el-form-item label="联系电话:" prop="table_phone" label-width="auto">
+              <el-input v-model="creator_phone" placeholder="请输入联系电话"></el-input>
             </el-form-item>
           </el-col>
+          <!-- 选择填报人 -->
           <el-col :span="6">
             <el-form-item label="选择填报人:" prop="table_name" label-width="auto">
               <el-select v-model="selectedReporter" placeholder="请选择填报人">
-                <el-option v-for="reporter in reporters" :key="reporter.id" :label="reporter.name" />
+                <el-option v-for="reporter in reporters" :key="reporter.id" :label="reporter.name" :value="reporter.id" />
               </el-select>
             </el-form-item>
           </el-col>
+          <!-- 截止时间 -->
           <el-col :span="6">
             <el-form-item label="截止时间:" prop="release_time">
               <el-date-picker v-model="selectedTime" type="date" placeholder="选择截止时间" @change="handleTimeChange" />
               <span class="label">前报送该表</span>
             </el-form-item>
           </el-col>
+          <!-- 操作按钮 -->
+          <el-col :span="6">
+            <el-input v-model="table_name" placeholder="请输入表名"></el-input>
+          </el-col>
           <el-col :span="1.5">
             <el-button type="primary" @click="handleReport()"> 智能识别 </el-button>
           </el-col>
@@ -51,24 +59,12 @@
           <el-button type="primary" @click="handleReload">重新加载</el-button>
         </el-col>
       </el-row>
+      <!-- 表格组件 -->
       <el-col :lg="30" :xs="24">
-        <el-table :data="tableData" border>
+        <el-table :data="field_names" border>
           <el-table-column v-for="header in editableHeaders" :key="header" :label="header" :prop="header">
-            <template #header="{ column }">
-              <span
-                class="editable-header"
-                contenteditable="true"
-                @blur="saveHeader(column.label, $event.target.innerText)"
-                v-text="column.label"
-              ></span>
-            </template>
             <template #default="{ row, $index }">
-              <span
-                class="editable-span"
-                contenteditable="true"
-                @blur="saveEdit($index, header, $event.target.innerText)"
-                v-text="row[header]"
-              ></span>
+              <el-input v-model="row[header]" @blur="saveEdit($index, header, row[header])" />
             </template>
           </el-table-column>
         </el-table>
@@ -78,9 +74,10 @@
 </template>
 
 <script setup lang="ts">
-import { ref, onMounted } from 'vue';
-import { ElTable, ElButton, ElCol, ElRow, ElTableColumn } from 'element-plus';
+import { onMounted, ref } from 'vue';
+import { ElButton, ElCol, ElDatePicker, ElFormItem, ElInput, ElOption, ElRow, ElSelect, ElTable, ElTableColumn } from 'element-plus';
 import * as XLSX from 'xlsx';
+import { fillingAdd } from '@/api/dataFilling/fillingManage';
 
 const emits = defineEmits(['close']);
 const detailData = ref({
@@ -89,61 +86,44 @@ const detailData = ref({
   end: '2024-10-15 18:00:00'
 });
 
-const editableHeaders = ref(['时间', '地点', '损坏程度', '救援人员', '物资']);
-const tableData = ref([]);
+const field_names = ref([]);
+const editableHeaders = ref([]);
+const creator_name = ref('');
+const creator_phone = ref('');
+const selectedReporter = ref(null);
+const reporters = ref([]); // 确保这个数组被正确初始化
+const selectedTime = ref(null);
+const table_name = ref('');
+const data_table_name = ref(''); // 假设这是另一个输入字段,需要在模板中添加对应的输入框
+const status = ref(0); // 假设这是一个选择器或输入框
+const issued_status = ref(0); // 假设这是一个选择器或输入框
+const period_type = ref(''); // 假设这是一个输入框或选择器
+const creator_id = ref(null); // 这通常是用户ID,可能需要从登录信息中获取
 
 // 初始化表格数据
-onMounted(() => {
-  loadFromLocalStorage();
-});
-
-// 加载数据
-function loadFromLocalStorage() {
-  const storedData = localStorage.getItem('tableData');
-  if (storedData) {
-    tableData.value = JSON.parse(storedData);
-  } else {
-    tableData.value = [
-      {
-        时间: '2024-01-01',
-        地点: '某地',
-        损坏程度: '轻度',
-        救援人员: '张三, 李四',
-        物资: '食品, 水'
-      },
-      {
-        时间: '2024-01-02',
-        地点: '某地',
-        损坏程度: '中度',
-        救援人员: '王五, 赵六',
-        物资: '帐篷, 医疗用品'
-      }
-    ];
-  }
-}
+onMounted(() => {});
 
 function saveEdit(rowIndex, header, value) {
-  tableData.value[rowIndex][header] = value;
-}
-
-function saveHeader(header, newValue) {
-  const index = editableHeaders.value.indexOf(header);
-  if (index !== -1) {
-    editableHeaders.value.splice(index, 1, newValue);
-  }
+  field_names.value[rowIndex][header] = value;
+  localStorage.setItem('field_names', JSON.stringify(field_names.value));
 }
 
 const handleNewTemplate = () => {
-  tableData.value = [];
-  for (let i = 0; i < 10; i++) {
-    tableData.value.push({
-      时间: '',
-      地点: '',
-      损坏程度: '',
-      救援人员: '',
-      物资: ''
+  // 创建一个新的10x10的数组,每个元素是一个对象,对象的键为列名,值为空字符串
+  const newHeaders = Array.from({ length: 10 }, (_, index) => `列${String.fromCharCode(65 + index)}`); // 生成列名
+  field_names.value = Array.from({ length: 10 }, (v, k) => {
+    const obj = {
+      序号: k + 1
+    };
+    newHeaders.forEach((header) => {
+      obj[header] = '';
     });
-  }
+    return obj;
+  });
+
+  // 更新editableHeaders
+  editableHeaders.value = ['序号', ...newHeaders];
+
   alert('空白模板已创建');
 };
 
@@ -154,40 +134,64 @@ const handleImportExcel = () => {
   input.onchange = async (e) => {
     const file = e.target.files[0];
     const reader = new FileReader();
-    reader.onload = async () => {
-      const data = await new Promise((resolve, reject) => {
-        const result = XLSX.read(reader.result);
-        resolve(result);
-      });
-      reader.readAsArrayBuffer(file);
+    reader.onload = async (e) => {
+      const data = e.target.result;
+      const workbook = XLSX.read(data, { type: 'array' });
+      const firstSheetName = workbook.SheetNames[0];
+      const worksheet = workbook.Sheets[firstSheetName];
+      field_names.value = XLSX.utils.sheet_to_json(worksheet);
+      alert('Excel文件已导入');
     };
-    const workbook = XLSX.utils.read(data);
-    const firstSheetName = workbook.SheetNames[0];
-    const worksheet = workbook.Sheets[firstSheetName];
-    const data = XLSX.utils.sheet_to_json(worksheet);
-    tableData.value = data;
-    alert('Excel文件已导入');
+    reader.readAsArrayBuffer(file);
   };
   input.click();
 };
 
 const handleReload = () => {
-  tableData.value = [];
+  field_names.value = [];
   alert('表格已清空');
 };
 
 const handleSaveTemporarily = () => {
-  localStorage.setItem('tableData', JSON.stringify(tableData.value));
+  localStorage.setItem('field_names', JSON.stringify(field_names.value));
   alert('数据已暂存');
 };
 
 const handleReport = () => console.log('上报');
 
-const handleSave = () => {
+const handleSave = async () => {
+  // 构造要发送的数据对象
+  const data = {
+    table_name: table_name.value,
+    data_table_name: data_table_name.value,
+    start_time: selectedTime.value ? selectedTime.value.startOfDay.format() : '',
+    end_time: selectedTime.value ? selectedTime.value.endOfDay.format() : '',
+    status: status.value,
+    issued_status: issued_status.value,
+    period_type: period_type.value,
+    creator_name: creator_name.value,
+    creator_id: creator_id.value,
+    creator_phone: creator_phone.value,
+    field_names: field_names.value,
+    user_ids: [] // 这个需要根据实际情况来填充
+  };
+
   // 保存本地状态
-  localStorage.setItem('tableData', JSON.stringify(tableData.value));
-  alert('数据已保存');
+  localStorage.setItem('field_names', JSON.stringify(field_names.value));
+  try {
+    // 调用fillingAdd接口,传递data作为请求体
+    const response = await fillingAdd(data);
+    if (response && response.success) {
+      alert('数据已成功保存');
+    } else {
+      alert('数据保存失败');
+    }
+  } catch (error) {
+    console.error('保存数据失败:', error);
+    alert('数据保存失败');
+  }
 };
+
 const handleReturn = () => {
   emits('close');
 };

+ 86 - 66
src/views/dataFilling/fillingManage.vue

@@ -4,21 +4,24 @@
       <div>
         <transition name="fade">
           <div v-show="showSearch">
-            <el-form ref="queryFormRef" :model="queryParams">
+            <el-form ref="queryFormRef" :model="form">
               <el-row :gutter="20">
+                <!-- 表格名称 -->
                 <el-col :span="6">
                   <el-form-item label="表格名称:" prop="table_name" label-width="auto">
-                    <el-input v-model="queryParams.table_name" placeholder="请输入表格名称"></el-input>
+                    <el-input v-model="form.table_name" placeholder="请输入表格名称"></el-input>
                   </el-form-item>
                 </el-col>
+                <!-- 表格状态 -->
                 <el-col :span="6">
                   <el-form-item label="表格状态:" prop="task_status">
-                    <el-radio-group v-model="queryParams.task_status">
+                    <el-radio-group v-model="form.task_status">
                       <el-radio value="0" size="large">在用</el-radio>
                       <el-radio value="1" size="large">禁止</el-radio>
                     </el-radio-group>
                   </el-form-item>
                 </el-col>
+                <!-- 发布时间 -->
                 <el-col :span="6">
                   <el-form-item label="发布时间:" prop="release_time">
                     <el-date-picker
@@ -31,19 +34,21 @@
                     />
                   </el-form-item>
                 </el-col>
+                <!-- 下发状态 -->
                 <el-col :span="6">
                   <el-form-item label="下发状态:" prop="release_status">
-                    <el-radio-group v-model="queryParams.release_status">
+                    <el-radio-group v-model="form.release_status">
                       <el-radio value="0" size="large">待发布</el-radio>
                       <el-radio value="1" size="large">已发布</el-radio>
                     </el-radio-group>
                   </el-form-item>
                 </el-col>
+                <!-- 操作按钮 -->
                 <el-col :span="6">
                   <el-button type="primary" @click="handleQuery">查询</el-button>
                   <el-button type="primary" @click="resetQuery">重置</el-button>
                   <el-button type="primary" @click="exportTableData()">导出</el-button>
-                  <el-button type="primary" @click="handleAdd()">新增</el-button>
+                  <el-button type="primary" @click="handleAdd">新增</el-button>
                 </el-col>
               </el-row>
             </el-form>
@@ -51,31 +56,49 @@
         </transition>
         <!-- 表格组件 -->
         <el-table ref="multipleTable" v-loading="loading" :data="tableData" @selection-change="handleSelectionChange">
-          <el-table-column label="序号" align="center" prop="table_id" />
+          <el-table-column label="序号" align="center" prop="id" />
           <el-table-column label="表格名称" align="center" prop="table_name" />
-          <el-table-column label="发布日期" align="center" prop="release_time" />
-          <el-table-column label="发布状态" align="center" prop="release_status" />
-          <el-table-column label="截止填报时间" align="center" prop="filling_time" />
-          <el-table-column label="任务状态" align="center" prop="status" />
+          <el-table-column label="发布日期" align="center" prop="created_at" />
+          <el-table-column label="发布状态" align="center" prop="issued_status">
+            <template #default="scope">
+              {{ scope.row.issued_status === 2 ? '已发布' : '待发布' }}
+            </template>
+          </el-table-column>
+          <el-table-column label="截止填报时间" align="center" prop="end_time" />
+          <el-table-column label="任务状态" align="center" prop="status">
+            <template #default="scope">
+              {{ scope.row.status === 1 ? '在用' : '禁止' }}
+            </template>
+          </el-table-column>
           <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
             <template #default="scope">
-              <el-text v-if="scope.row.release_status === '待发布'" class="common-btn-text-primary" @click="scope.row;">下发</el-text>
+              <!-- 只有待发布状态才显示下发按钮 -->
+              <el-text v-if="scope.row.issued_status === 1" class="common-btn-text-primary" @click="handleIssue(scope.row)">下发</el-text>
               <el-text class="common-btn-text-primary" @click="handleView(scope.row)">详情</el-text>
             </template>
           </el-table-column>
         </el-table>
-        <pagination v-show="total > 0" v-model:page="queryParams.page" v-model:limit="queryParams.pageSize" :total="total" @pagination="tableData" />
+        <pagination
+          v-show="total > 0"
+          v-model:page="queryParams.page"
+          v-model:limit="queryParams.pageSize"
+          :total="total"
+          @pagination="handlePagination"
+        />
       </div>
     </div>
     <FillingAdd v-if="fillingAddState.show" :event-id="fillingAddState.eventId" @close="handleCancel" />
     <TableDetails v-if="tableDetailsState.show" :event-id="tableDetailsState.eventId" @close="handleCancel" />
   </div>
 </template>
+
 <script setup lang="ts">
-import { onMounted, reactive, ref } from 'vue';
+import { onMounted, reactive, ref, toRefs } from 'vue';
 import FillingAdd from './fillingAdd.vue';
 import TableDetails from './tableDetails.vue';
 import { ElButton, ElCol } from 'element-plus';
+import { fillingSelect } from '@/api/dataFilling/fillingManage';
+import * as XLSX from 'xlsx';
 
 const loading = ref(true);
 const showSearch = ref(true);
@@ -84,6 +107,7 @@ const ids = ref<Array<number | string>>([]);
 const total = ref(0);
 const tableData = ref([]);
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
 const initFormData = reactive({
   table_id: '',
   table_name: '',
@@ -92,6 +116,7 @@ const initFormData = reactive({
   release_status: '',
   task_status: ''
 });
+
 const data = reactive({
   form: { ...initFormData },
   queryParams: {
@@ -103,34 +128,8 @@ const data = reactive({
   }
 });
 
-const { queryParams, form } = toRefs(data);
-// 模拟数据源
-const staticData = [
-  {
-    table_id: 1,
-    table_name: '测试表单',
-    release_time: '2024-10-15',
-    release_status: '已发布',
-    filling_time: '2024-10-15 18:00:00',
-    status: '在用'
-  },
-  {
-    table_id: 2,
-    table_name: '应急工单数据表',
-    release_time: '2024-06-24',
-    release_status: '待发布',
-    filling_time: '2025-06-24 18:00:00',
-    status: '在用'
-  },
-  {
-    table_id: 3,
-    table_name: '安全风险通告数据表',
-    release_time: '2024-06-24',
-    release_status: '已发布',
-    filling_time: '2025-06-24 18:00:00',
-    status: '在用'
-  }
-];
+const { form, queryParams } = toRefs(data);
+
 let fillingAddState = reactive({
   show: false,
   eventId: ''
@@ -142,59 +141,80 @@ let tableDetailsState = reactive({
 });
 
 const handleCancel = () => {
-  fillingAddmState.show = false;
+  fillingAddState.show = false;
   tableDetailsState.show = false;
 };
+
 const handleAdd = () => {
   fillingAddState.eventId = null; // 表示新增记录
   fillingAddState.show = true;
 };
+
 const handleView = (row) => {
   if (row) {
-    tableDetailsState.eventId = row.id;
+    tableDetailsState.eventId = row.report_id; // 使用 report_id 作为事件ID
     tableDetailsState.show = true;
   }
 };
 
-// 初始化数据
+const fetchFillList = () => {
+  loading.value = true;
+  fillingSelect(queryParams.value)
+    .then((res) => {
+      tableData.value = res.data;
+      total.value = res.total;
+    })
+    .finally(() => {
+      loading.value = false;
+    });
+};
+
 onMounted(() => {
-  tableData.value = staticData;
-  total.value = staticData.length;
-  loading.value = false;
+  fetchFillList();
 });
+
 const handleQuery = () => {
-  queryParams.value.page = 1;
-  fetchWorkrData();
+  queryParams.value.page = 1; // 重置页码
+  fetchFillList();
 };
+
 // 重置查询条件
 const resetQuery = () => {
   queryParams.value = { page: 1, pageSize: 10, table_name: '', release_status: '', task_status: '' };
   selectedTime.value = [];
   handleQuery();
 };
+
 const handleSelectionChange = (selection) => {
   ids.value = selection.map((item) => item.id);
 };
 
-const handleTimeChange = (value) => {
-  selectedTimeLabel.value = value ? `${value[0]} 至 ${value[1]}` : '请选择时间';
+const handleTimeChange = () => {
+  // selectedTimeLabel.value = value ? `${value[0]} 至 ${value[1]}` : '请选择时间';
 };
 
 const exportTableData = () => {
-  const dataForExport = tableData.value.map((item) => ({
-    table_id: item.table_id,
-    table_name: item.table_name,
-    release_time: item.release_time,
-    release_status: item.release_status,
-    filling_time: item.filling_time,
-    status: item.status
-  }));
-
-  // 使用 xlsx库创建一个工作簿和工作表,然后填入数据
-  const workbook = XLSXLSX.utils.book_new();
-  XLSX.utils.sheet_add_aoa(workbook, dataForExport, { name: 'sheet1' });
-
-  // 生成Excel文件并下载
-  XLSX.utils.save_a(workbook, 'tableData.xlsx');
+  const worksheet = XLSX.utils.json_to_sheet(tableData.value);
+  const workbook = { Sheets: { data: worksheet }, SheetNames: ['data'] };
+  const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
+
+  const blob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+  const link = document.createElement('a');
+  const url = window.URL.createObjectURL(blob);
+  link.href = url;
+  link.download = 'SheetJS.xlsx';
+  link.click();
+  window.URL.revokeObjectURL(url); // 清理
+};
+
+const handlePagination = ({ page, limit }) => {
+  queryParams.value.page = page;
+  queryParams.value.pageSize = limit;
+  fetchFillList();
+};
+
+const handleIssue = (row) => {
+  // 处理下发逻辑
+  console.log('下发操作:', row);
 };
 </script>

+ 232 - 5
src/views/dataFilling/tableDetails.vue

@@ -1,11 +1,238 @@
+<template>
+  <div class="app-container p-2">
+    <el-row :gutter="20">
+      <el-col :lg="30" :xs="24" style="">
+        <el-row :span="24" :gutter="10">
+          <!-- 联系人姓名 -->
+          <el-col :span="6">
+            <el-form-item label="联系人姓名:" prop="table_name" label-width="auto">
+              <div>{{ reportInfo.creator_name }}</div>
+            </el-form-item>
+          </el-col>
+          <!-- 联系电话 -->
+          <el-col :span="6">
+            <el-form-item label="联系电话:" prop="table_phone" label-width="auto">
+              <div>{{ reportInfo.creator_phone }}</div>
+            </el-form-item>
+          </el-col>
+          <!-- 选择填报人 -->
+          <el-col :span="6">
+            <el-form-item label="选择填报人:" prop="table_name" label-width="auto">
+              <el-select v-model="selectedReporter" placeholder="请选择填报人">
+                <el-option v-for="reporter in reporters" :key="reporter.id" :label="reporter.name" :value="reporter.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <!-- 截止时间 -->
+          <el-col :span="6">
+            <el-form-item label="截止时间:" prop="release_time">
+              <div>{{ reportInfo.end_time }}</div>
+              <span class="label">前报送该表</span>
+            </el-form-item>
+          </el-col>
+          <!-- 操作按钮 -->
+          <!-- 表名 -->
+          <el-col :span="6">
+            <el-form-item label="表名:" prop="table_name" label-width="auto">
+              <div>{{ reportInfo.table_name }}</div>
+            </el-form-item>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" @click="handleReport()">智能识别</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" @click="handleSaveTemporarily">暂存</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" @click="handleData">数据</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" @click="handleSave()">发布</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" @click="handleReturn()">返回</el-button>
+          </el-col>
+        </el-row>
+      </el-col>
+      <el-row :gutter="20" class="mb8">
+        <el-col :span="1.5">
+          <el-button type="primary" @click="handleImportExcel">导入Excel文件</el-button>
+        </el-col>
+        <el-col :span="1.5">
+          <el-button type="primary" @click="handleNewTemplate">空白模板</el-button>
+        </el-col>
+        <el-col :span="1.5">
+          <el-button type="primary" @click="handleReload">重新加载</el-button>
+        </el-col>
+      </el-row>
+      <!-- 表格组件 -->
+      <el-col :lg="30" :xs="24">
+        <el-table :data="tableStructure" border>
+          <el-table-column v-for="header in editableHeaders" :key="header" :label="header" :prop="header">
+            <template #default="{ row, $index }">
+              <el-input v-model="row[header]" @blur="saveEdit($index, header, row[header])" />
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
 <script setup lang="ts">
+import { onMounted, ref } from 'vue';
+import { ElButton, ElCol, ElFormItem, ElInput, ElOption, ElRow, ElSelect, ElTable, ElTableColumn } from 'element-plus';
+import * as XLSX from 'xlsx';
+import { fillingList } from '@/api/dataFilling/fillingManage';
 
-</script>
+const emits = defineEmits(['close']);
+const reportInfo = ref({
+  id: 0,
+  report_id: '',
+  table_name: '',
+  data_table_name: '',
+  start_time: '',
+  end_time: '',
+  status: 0,
+  issued_status: 0,
+  period_type: '',
+  creator_name: '',
+  creator_id: 0,
+  creator_phone: '', // 添加 creator_phone 属性
+  num_reporters: 0,
+  created_at: '',
+  updated_at: '',
+  num_reported: 0,
+  num_unreported: 0
+});
+const tableStructure = ref([]); // 修改为数组
+const editableHeaders = ref([]); // 添加 editableHeaders 定义
+const field_names = ref([]); // 添加 field_names 定义
+const selectedReporter = ref(null); // 确保这个变量被定义
+const reporters = ref([]); // 确保这个数组被定义
 
-<template>
+const props = defineProps({
+  eventId: String
+});
+const reportId = ref(props.eventId);
 
-</template>
+watch(reportId, async (newVal) => {
+  if (newVal) {
+    await fetchReportDetails(newVal);
+  }
+});
+
+// 初始化表格数据
+onMounted(async () => {
+  if (reportId.value) {
+    await fetchReportDetails(reportId.value);
+  }
+});
+
+const fetchReportDetails = async (reportId) => {
+  try {
+    const response = await fillingList(reportId);
+    console.log('接口返回的数据:', response); // 打印完整的响应数据以便调试
+    if (response.code === 200 && response.data) {
+      reportInfo.value = {
+        ...reportInfo.value,
+        ...response.data.report_info, // 确保 reportInfo 被更新
+        creator_phone: response.data.report_info.creator_phone // 确保 creator_phone 被更新
+      };
+      // 假设 response.data.table_structure 是数组形式,包含列信息
+      tableStructure.value = response.data.table_structure;
+      editableHeaders.value = ['序号', ...response.data.table_structure.map((item) => item.column_name)];
+    } else {
+      throw new Error(`接口返回错误,状态码:${response.code}`);
+    }
+  } catch (error) {
+    console.error('获取报告信息失败:', error);
+    alert('获取报告信息失败: ' + error.message);
+  }
+};
 
-<style scoped lang="scss">
+function saveEdit(rowIndex, header, value) {
+  field_names.value[rowIndex][header] = value;
+}
 
-</style>
+const handleNewTemplate = () => {
+  const newHeaders = Array.from({ length: 10 }, (_, index) => `列${String.fromCharCode(65 + index)}`);
+  field_names.value = Array.from({ length: 10 }, (v, k) => ({
+    序号: k + 1,
+    ...newHeaders.reduce((acc, header) => {
+      acc[header] = '';
+      return acc;
+    }, {})
+  }));
+  editableHeaders.value = ['序号', ...newHeaders];
+  alert('空白模板已创建');
+};
+
+const handleImportExcel = () => {
+  const input = document.createElement('input');
+  input.type = 'file';
+  input.accept = '.xlsx';
+  input.onchange = async (e) => {
+    const file = e.target.files[0];
+    const reader = new FileReader();
+    reader.onload = async (e) => {
+      const data = e.target.result;
+      const workbook = XLSX.read(data, { type: 'array' });
+      const firstSheetName = workbook.SheetNames[0];
+      const worksheet = workbook.Sheets[firstSheetName];
+      field_names.value = XLSX.utils.sheet_to_json(worksheet);
+      alert('Excel文件已导入');
+    };
+    reader.readAsArrayBuffer(file);
+  };
+  input.click();
+};
+
+const handleReload = () => {
+  field_names.value = [];
+  alert('表格已清空');
+};
+
+const handleSaveTemporarily = () => {
+  alert('数据已暂存');
+};
+
+const handleReport = () => console.log('上报');
+
+const handleSave = async () => {
+  const data = {
+    table_name: reportInfo.value.table_name,
+    data_table_name: reportInfo.value.data_table_name,
+    start_time: reportInfo.value.start_time,
+    end_time: reportInfo.value.end_time,
+    status: reportInfo.value.status,
+    issued_status: reportInfo.value.issued_status,
+    period_type: reportInfo.value.period_type,
+    creator_name: reportInfo.value.creator_name,
+    creator_id: reportInfo.value.creator_id,
+    creator_phone: reportInfo.value.creator_phone, // 使用 reportInfo 中的 creator_phone
+    field_names: field_names.value,
+    user_ids: []
+  };
+  try {
+    const response = await fillingAdd(data); // 确保 fillingAdd 函数被定义
+    if (response && response.success) {
+      alert('数据已成功保存');
+    } else {
+      alert('数据保存失败');
+    }
+  } catch (error) {
+    console.error('保存数据失败:', error);
+    alert('数据保存失败');
+  }
+};
+
+const handleReturn = () => {
+  emits('close');
+};
+
+const handleData = () => {
+  // 实现具体的数据处理逻辑
+  console.log('处理数据');
+};
+</script>