lizhouming 8 månader sedan
förälder
incheckning
e85eec16f5

+ 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
+  });
+}

+ 0 - 27
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,44 +38,29 @@ 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']
@@ -96,9 +72,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']

+ 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>