فهرست منبع

填报管理新增

yangyuxuan 7 ماه پیش
والد
کامیت
970f67a9fe
4فایلهای تغییر یافته به همراه296 افزوده شده و 59 حذف شده
  1. 128 0
      package-lock.json
  2. 1 0
      package.json
  3. 9 0
      src/types/components.d.ts
  4. 158 59
      src/views/dataFilling/fillingAdd.vue

+ 128 - 0
package-lock.json

@@ -11,6 +11,7 @@
       "dependencies": {
         "@amap/amap-jsapi-loader": "^1.0.1",
         "@element-plus/icons-vue": "2.3.1",
+        "@handsontable/vue3": "^15.0.1",
         "@highlightjs/vue-plugin": "2.1.0",
         "@turf/turf": "^7.2.0",
         "@univerjs/core": "^0.4.1",
@@ -1231,6 +1232,23 @@
         "node": ">=6"
       }
     },
+    "node_modules/@handsontable/pikaday": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/@handsontable/pikaday/-/pikaday-1.0.0.tgz",
+      "integrity": "sha512-1VN6N38t5/DcjJ7y7XUYrDx1LuzvvzlrFdBdMG90Qo1xc8+LXHqbWbsTEm5Ec5gXTEbDEO53vUT35R+2COmOyg==",
+      "license": "(0BSD OR MIT)",
+      "peer": true
+    },
+    "node_modules/@handsontable/vue3": {
+      "version": "15.0.1",
+      "resolved": "https://registry.npmmirror.com/@handsontable/vue3/-/vue3-15.0.1.tgz",
+      "integrity": "sha512-a138HtC4tjGW8VenDQZYXjv3AoCpS4CeLAPFOBIQR6MB4Fb0xEn/uXBSy4SA/sfSGxhpyNhx3R1BPpxVJ0SXoA==",
+      "license": "SEE LICENSE IN LICENSE.txt",
+      "peerDependencies": {
+        "handsontable": ">=15.0.0",
+        "vue": "^3.2.22"
+      }
+    },
     "node_modules/@highlightjs/vue-plugin": {
       "version": "2.1.0",
       "resolved": "https://registry.npmmirror.com/@highlightjs/vue-plugin/-/vue-plugin-2.1.0.tgz",
@@ -4695,6 +4713,14 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/trusted-types": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+      "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+      "license": "MIT",
+      "optional": true,
+      "peer": true
+    },
     "node_modules/@types/uuid": {
       "version": "10.0.0",
       "resolved": "https://registry.npmmirror.com/@types/uuid/-/uuid-10.0.0.tgz",
@@ -7222,6 +7248,17 @@
         "node": "*"
       }
     },
+    "node_modules/chevrotain": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmmirror.com/chevrotain/-/chevrotain-6.5.0.tgz",
+      "integrity": "sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "regexp-to-ast": "0.4.0"
+      }
+    },
     "node_modules/chokidar": {
       "version": "3.6.0",
       "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
@@ -7493,6 +7530,18 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/core-js": {
+      "version": "3.40.0",
+      "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.40.0.tgz",
+      "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "peer": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
     "node_modules/core-util-is": {
       "version": "1.0.3",
       "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -8064,6 +8113,16 @@
       "resolved": "https://registry.npmmirror.com/domify/-/domify-1.4.2.tgz",
       "integrity": "sha512-m4yreHcUWHBncGVV7U+yQzc12vIlq0jMrtHZ5mW6dQMiL/7skSYNVX9wqKwOtyO9SGCgevrAFEgOCAHmamHTUA=="
     },
+    "node_modules/dompurify": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.2.4.tgz",
+      "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==",
+      "license": "(MPL-2.0 OR Apache-2.0)",
+      "peer": true,
+      "optionalDependencies": {
+        "@types/trusted-types": "^2.0.7"
+      }
+    },
     "node_modules/domutils": {
       "version": "1.7.0",
       "resolved": "https://registry.npmmirror.com/domutils/-/domutils-1.7.0.tgz",
@@ -9685,6 +9744,23 @@
         "node": ">=0.8.0"
       }
     },
+    "node_modules/handsontable": {
+      "version": "15.0.1",
+      "resolved": "https://registry.npmmirror.com/handsontable/-/handsontable-15.0.1.tgz",
+      "integrity": "sha512-pG5klHXpcWXiaDgtImkEZQ28CrM3j9dHSXx7Y4MJ7A/v07rbWdGMl/kDJsg1jvH1joWgevE5ZqH9rjaFt0+WIg==",
+      "license": "SEE LICENSE IN LICENSE.txt",
+      "peer": true,
+      "dependencies": {
+        "@handsontable/pikaday": "^1.0.0",
+        "core-js": "^3.37.0",
+        "dompurify": "^3.1.7",
+        "moment": "2.30.1",
+        "numbro": "2.5.0"
+      },
+      "optionalDependencies": {
+        "hyperformula": "^2.6.2"
+      }
+    },
     "node_modules/has-ansi": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/has-ansi/-/has-ansi-2.0.0.tgz",
@@ -9917,6 +9993,18 @@
         "node": ">=10.17.0"
       }
     },
+    "node_modules/hyperformula": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmmirror.com/hyperformula/-/hyperformula-2.7.1.tgz",
+      "integrity": "sha512-mpVF5zOyNpksZzgTaCQyRAzdC/X43+taz5x1n7zNbs/PUUv0AuLmsy2yfihCr+vihUzN/pk+gXBbOfNpXKOpgA==",
+      "license": "GPL-3.0-only",
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "chevrotain": "^6.5.0",
+        "tiny-emitter": "^2.1.0"
+      }
+    },
     "node_modules/ids": {
       "version": "1.0.5",
       "resolved": "https://registry.npmmirror.com/ids/-/ids-1.0.5.tgz",
@@ -11069,6 +11157,16 @@
         "saxen": "^8.1.2"
       }
     },
+    "node_modules/moment": {
+      "version": "2.30.1",
+      "resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
+      "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/mrmime": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.0.tgz",
@@ -11276,6 +11374,19 @@
         "url": "https://github.com/fb55/nth-check?sponsor=1"
       }
     },
+    "node_modules/numbro": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmmirror.com/numbro/-/numbro-2.5.0.tgz",
+      "integrity": "sha512-xDcctDimhzko/e+y+Q2/8i3qNC9Svw1QgOkSkQoO0kIPI473tR9QRbo2KP88Ty9p8WbPy+3OpTaAIzehtuHq+A==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "bignumber.js": "^8 || ^9"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/numeral": {
       "version": "2.0.6",
       "resolved": "https://registry.npmmirror.com/numeral/-/numeral-2.0.6.tgz",
@@ -12734,6 +12845,14 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/regexp-to-ast": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/regexp-to-ast/-/regexp-to-ast-0.4.0.tgz",
+      "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==",
+      "license": "MIT",
+      "optional": true,
+      "peer": true
+    },
     "node_modules/regexp-util": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/regexp-util/-/regexp-util-2.0.0.tgz",
@@ -14052,6 +14171,14 @@
       "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
       "dev": true
     },
+    "node_modules/tiny-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
+      "license": "MIT",
+      "optional": true,
+      "peer": true
+    },
     "node_modules/tiny-inflate": {
       "version": "1.0.3",
       "resolved": "https://registry.npmmirror.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
@@ -15702,6 +15829,7 @@
       "version": "0.18.5",
       "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
       "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
+      "license": "Apache-2.0",
       "dependencies": {
         "adler-32": "~1.3.0",
         "cfb": "~1.2.1",

+ 1 - 0
package.json

@@ -20,6 +20,7 @@
   "dependencies": {
     "@amap/amap-jsapi-loader": "^1.0.1",
     "@element-plus/icons-vue": "2.3.1",
+    "@handsontable/vue3": "^15.0.1",
     "@highlightjs/vue-plugin": "2.1.0",
     "@turf/turf": "^7.2.0",
     "@univerjs/core": "^0.4.1",

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

@@ -28,6 +28,8 @@ declare module 'vue' {
     ElBadge: typeof import('element-plus/es')['ElBadge']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
+    ElCheckboxButton: typeof import('element-plus/es')['ElCheckboxButton']
+    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
     ElCol: typeof import('element-plus/es')['ElCol']
     ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
@@ -53,14 +55,21 @@ declare module 'vue' {
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSlider: typeof import('element-plus/es')['ElSlider']
+    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']

+ 158 - 59
src/views/dataFilling/fillingAdd.vue

@@ -1,22 +1,50 @@
 <template>
   <div class="app-container p-2">
+    <el-row :gutter="20" class="mb8" style="margin-top:5px">
+      <el-col :span="1.5">
+        <el-upload
+          class="upload-demo"
+          :http-request="handleFileUpload"
+          :before-upload="beforeUpload"
+          :file-list="fileList"
+          accept=".xlsx, .xls"
+        >
+          <el-button slot="trigger" size="big" type="primary">选择Excel文件</el-button>
+        </el-upload>
+      </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-col :span="1.5">
+        <el-button type="primary" @click="handleSaveTemporarily">暂存</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-row :gutter="20">
-      <el-col :lg="30" :xs="24" style="">
+      <el-col :lg="30" :xs="24" style="margin-top: -10px">
         <el-row :span="24" :gutter="10">
           <!-- 联系人姓名 -->
-          <el-col :span="12">
+          <el-col :span="8">
             <el-form-item label="联系人姓名:" prop="table_name" label-width="auto">
               <el-input v-model="creator_name" placeholder="请输入联系人姓名" style="width: 300px"></el-input>
             </el-form-item>
           </el-col>
           <!-- 联系电话 -->
-          <el-col :span="12">
+          <el-col :span="8">
             <el-form-item label="联系电话:" prop="table_phone" label-width="auto">
               <el-input v-model="creator_phone" placeholder="请输入联系电话" style="width: 300px"></el-input>
             </el-form-item>
           </el-col>
           <!-- 选择填报人 -->
-          <el-col :span="12">
+          <el-col :span="8">
             <el-form-item label="选择填报人:" prop="table_name" label-width="auto">
               <el-select v-model="selectedReporter" placeholder="请选择填报人" style="width: 300px">
                 <el-option v-for="reporter in reporters" :key="reporter.id" :label="reporter.name" :value="reporter.id" />
@@ -24,52 +52,28 @@
             </el-form-item>
           </el-col>
           <!-- 截止时间 -->
-          <el-col :span="12">
-            <el-form-item label="截止时间:" prop="release_time">
+          <el-col :span="8">
+            <el-form-item label="截&nbsp&nbsp&nbsp&nbsp:" prop="release_time">
               <el-date-picker v-model="selectedTime" type="date" placeholder="选择截止时间" @change="handleTimeChange" style="width: 150px" />
               <span class="label" style="margin-left: 20px">前报送该表</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>
-          <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="handleSave()"> 发布 </el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="danger" @click="handleReturn()"> 返回 </el-button>
+          <el-col :span="8">
+            <el-form-item label="表&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp名:" prop="table_name" label-width="auto">
+              <el-input v-model="table_name" placeholder="请输入表名" style="width: 300px"></el-input>
+            </el-form-item>
           </el-col>
+<!--          <el-col :span="1.5">-->
+<!--            <el-button type="primary" @click="handleReport()"> 智能识别 </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="field_names" 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 style="height: 350px">
+      <hot-table v-if="showTable" :data="hotData" ref="wrapper" :settings="hotSettings2" />
+    </div>
   </div>
 </template>
 
@@ -78,14 +82,22 @@ 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';
+import { HotTable } from '@handsontable/vue3'
+import Handsontable from 'handsontable'
+import 'handsontable/languages';
+import 'handsontable/dist/handsontable.full.css'
+import { registerAllModules } from 'handsontable/registry'
+registerAllModules()
 
+const fileList = ref([])
+const { proxy } = getCurrentInstance();
 const emits = defineEmits(['close']);
 const detailData = ref({
   title: '表单数据',
   start: '2024-10-15 17:02:22',
   end: '2024-10-15 18:00:00'
 });
-
+const hotData = ref([]);
 const field_names = ref([]);
 const editableHeaders = ref([]);
 const creator_name = ref('');
@@ -107,24 +119,10 @@ function saveEdit(rowIndex, header, value) {
   field_names.value[rowIndex][header] = value;
   localStorage.setItem('field_names', JSON.stringify(field_names.value));
 }
-
+const showTable = ref(false);
 const handleNewTemplate = () => {
-  // 创建一个新的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('空白模板已创建');
+  showTable.value = true;
+  created()
 };
 
 const handleImportExcel = () => {
@@ -195,6 +193,103 @@ const handleSave = async () => {
 const handleReturn = () => {
   emits('close');
 };
+
+const hotSettings = reactive({
+  autoColumnSize:true,
+  language: 'zh-CN', // 语言设置
+  // colWidths:129, // 默认单元格宽度
+  // rowHeights:28, // 默认单元格高度
+  wordWrap:true, // 单元格文字是否换行展示
+  width:'100%', // auto  or  100%
+  height:'100%', // auto  or  100%
+  className:'htMiddle, htCenter',
+  licenseKey: 'non-commercial-and-evaluation', // 隐藏版权文字
+});
+const created = () => {
+  let data = []
+  for(let i = 0; i < 10; i++) {
+    let arr = []
+    for(let x = 0; x < 10; x++) {
+      arr.push('')
+    }
+    data.push(arr);
+  }
+  showTable.value = false;
+  nextTick(() => {
+    hotData.value = data;
+    showTable.value = true;
+  })
+}
+
+//
+// const hotContainer = ref(null);
+// let hotInstance = null;
+// const handleFileUpload = (event) => {
+//   const file = event.target.files[0];
+//   const reader = new FileReader();
+//
+//   reader.onload = (e) => {
+//     const data = new Uint8Array(e.target.result);
+//     const workbook = XLSX.read(data, { type: 'array' });
+//     const firstSheetName = workbook.SheetNames[0];
+//     const worksheet = workbook.Sheets[firstSheetName];
+//     const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
+//
+//     // 如果Handsontable实例已经存在,销毁它以避免内存泄漏
+//     if (hotInstance) {
+//       hotInstance.destroy();
+//     }
+//
+//     // 初始化Handsontable实例
+//     hotInstance = new Handsontable(hotContainer.value, {
+//       data: jsonData,
+//       rowHeaders: true,
+//       colHeaders: true,
+//       // 其他Handsontable配置...
+//     });
+//   };
+//
+//   reader.readAsArrayBuffer(file);
+// };
+
+
+const hotSettings2 = reactive({
+  language: 'zh-CN',
+  colHeaders: true,
+  rowHeaders: true,
+  autoColumnSize:true,
+  width:'100%', // auto  or  100%
+  height:'100%', // auto  or  100%
+  licenseKey: 'non-commercial-and-evaluation', // 隐藏版权文字
+  colWidths:129, // 默认单元格宽度
+  rowHeights:28, // 默认单元格高度
+  wordWrap:true, // 单元格文字是否换行展示
+});
+const beforeUpload = (file) => {
+  const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.type === 'application/vnd.ms-excel';
+  if (!isExcel) {
+    proxy.$modal.msgError('只能上传xlsx/xls文件!');
+  }
+  return isExcel;
+};
+const handleFileUpload = ({ file }) => {
+  const reader = new FileReader();
+  reader.onload = (event) => {
+    showTable.value = false;
+    nextTick(() => {
+      const data = new Uint8Array(event.target.result);
+      const workbook = XLSX.read(data, { type: 'array' });
+      const firstSheetName = workbook.SheetNames[0];
+      const worksheet = workbook.Sheets[firstSheetName];
+      const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
+      hotData.value = jsonData.slice(1); // 假设第一行是标题行,我们跳过它
+      showTable.value = true;
+    })
+  };
+  reader.readAsArrayBuffer(file);
+  fileList.value = [];
+  return { success: true, file }; // 告诉 el-upload 上传成功(尽管实际上没有发送到服务器)
+};
 </script>
 
 <style scoped>
@@ -240,4 +335,8 @@ const handleReturn = () => {
   content: attr(data-placeholder); /* 可选:为空时显示占位符 */
   color: #999;
 }
+.upload-demo {
+  display: inline-block;
+  margin-bottom: 20px;
+}
 </style>