vue3 antdesign上传解析excel

html 复制代码
<a-upload 
    :show-upload-list="false" 
    ref="upload" 
    :beforeUpload="handleFileUpload"
    @change="handleChange" 
    :customRequest="selectCertFile" 
    accept=".xlsx, .xls, .csv"
>
   <a-button :loading="loading">
       <upload-outlined></upload-outlined>
            {{ loading ? '上传中...' : '点击上传Excel文件' }}
       </a-button>
</a-upload>
<!-- 动态表格 不含表头-->
<a-table 
    v-if="tableData.length > 0" 
    :columns="filteredColumns" 
    :data-source="filteredTableData"
    :pagination="false" 
    bordered size="middle" 
    :scroll="{ x: 'max-content' }" 
    :row-class-name="rowTableClassName"
    style="margin: 0 10px 10px 10px;" :loading="isloading"  
    :showHeader="false">
                <!-- 自定义单元格以支持合并-->
        <template #bodyCell="{ column, record, index }">
             <span v-if="record[column.dataIndex]?.value !== 'dropdown'">                    
                  {{record[column.dataIndex]?.value}}
             </span>
                <!-- 解析excel后底部添加下拉选择框-->
             <div v-else style="display: flex;flex-wrap: wrap;">
                  <a-select v-model:value="dropdownValues[column.key][0]" 
                     :autoWidth="false"
                      style="width: 100%; margin-bottom: 8px;"
                       placeholder="请选择" :options="provinceOptions"
                       @change="(value) => handleProvinceChange(column.key, value)" />
                  <a-select v-model:value="dropdownValues[column.key][1]" 
                      :autoWidth="false"
                       style="width: 100%; margin-bottom: 8px;" placeholder="请选择"
                       :options="getCityOptions(column.key)"
                       @change="(value) => handleCityChange(column.key, value)"
                       :disabled="!dropdownValues[column.key][0]" />
                   <a-select v-model:value="dropdownValues[column.key][2]" 
                        :autoWidth="false" style="width: 100%;"
                        placeholder="请选择" :options="getCountyOptions(column.key)" 
                        show-search :filter-option="filterOption"
                       :disabled="!dropdownValues[column.key][1]" />
             </div>
         </template>
  </a-table>
javascript 复制代码
<script setup lang="ts">
import { onMounted, reactive, ref, computed, watch } from "vue";
import * as XLSX from "xlsx";
import { UploadOutlined } from "@ant-design/icons-vue";
import { message,notification } from "ant-design-vue";
import { Select } from 'ant-design-vue';
import type { TableProps, FormInstance,SelectProps } from "ant-design-vue";
const loading = ref(false);
const columns = ref([]);
// 表格数据
const tableData = ref([]);
const handleChange = async (info: any) => {
  
}
const selectCertFile = (info: any) => {
   
};
const handleFileUpload = (file:any) => {
    loading.value = true;
    const reader = new FileReader();
    reader.onload = (e: any) => {
        try {
            const data = new Uint8Array(e.target.result);
            const workbook = XLSX.read(data, { type: "array" });
            if (workbook.SheetNames.length > 1) {
                message.error('文件包含多个Sheet页');
                return false; // 阻止上传
            }
            const sheetName = workbook.SheetNames[0];
            const worksheet = workbook.Sheets[sheetName];
            // 获取合并单元格的信息
            const merges = worksheet["!merges"] || [];
            // 解析数据范围
            const range = XLSX.utils.decode_range(worksheet["!ref"]);
            const jsonData = [];
            const jsonAllData = [];

            const maxRows = Math.min(range.e.r + 1, 10);
            for (let row = range.s.r; row <= range.e.r; row++) {
                const rowData = {};
                for (let col = range.s.c; col <= range.e.c; col++) {
                    const cellAddress = XLSX.utils.encode_cell({ r: row, c: col });
                    const cellValue = worksheet[cellAddress]?.v || "";
                    rowData[`col${col}`] = { value: cellValue, rowSpan: 1, colSpan: 1 };             
                  // 初始化 rowSpan 和 colSpan
                }
                if (row - range.s.r < maxRows) {
                    jsonData.push({ ...rowData, key: row }); //只展示前10行数据
                }
                jsonAllData.push({ ...rowData, key: row }); // 每一行添加唯一 key
            }

            // 动态生成列配置,并移除所有单元格为空的列
            const columnsConfig = Array.from({ length: range.e.c - range.s.c + 1 }, (_, i) => `col${range.s.c + i}`);
            const filteredColumns = columnsConfig.filter(colKey => {
                return jsonData.some(row => row[colKey].value.trim() !== "");
            });
            columns.value = filteredColumns.map((colKey, index) => ({
                title: "",
                dataIndex: colKey,
                key: colKey,
                width: "180px",
                ellipsis: true,
                customCell: (record, rowIndex) => {
                    const cell = record[colKey];
                    return {
                        rowSpan: cell.rowSpan ?? 1,
                        colSpan: cell.colSpan ?? 1,
                    };
                },
            }));
            // 处理合并单元格
            merges.forEach((merge) => {
                const { s, e } = merge; // s 是开始单元格,e 是结束单元格
                // 遍历需要合并的区域
                // 检查合并单元格是否在有效范围内
                if (s.r >= range.s.r && s.c >= range.s.c && e.r <= range.e.r && e.c <= range.e.c) {
                    for (let row = s.r; row <= e.r; row++) {
                        for (let col = s.c; col <= e.c; col++) {
                            const colKey = `col${col}`;
                            if (filteredColumns.includes(colKey)) {
                                if (row === s.r && col === s.c) {
                                    jsonData[row][colKey].rowSpan = e.r - s.r + 1;
                                    jsonData[row][colKey].colSpan = e.c - s.c + 1;
                                } else {
                                    jsonData[row][colKey].rowSpan = 0;
                                    jsonData[row][colKey].colSpan = 0;
                               
                                }
                            }
                        }
                    }
                }
            });
            const dropdownRow = {};
            //底部添加行
            columns.value.forEach((col, index) => {
                dropdownRow[col.dataIndex] = { value: 'dropdown', rowSpan: 1, colSpan: 1 }; // 标记为dropdown类型
                dropdownValues[col.key] = ['', '', '']; // 初始化dropdownValues
            });
            jsonData.push({ ...dropdownRow, key: jsonData.length });
            tableData.value = jsonData;
//-------------------------- 上传接口--------------------------------
            //let formDatas = new FormData();
            //formDatas.append("file", file);
            //formDatas.append("pid", listForm.value.pid);
            //formDatas.append("type", '1');
            //isloading.value = true;
            //selectedRows.value = []
            //uploadSalary(formDatas).then((res: any) => {
                //if(res.status == 200){
                   // isloading.value = false;
                    //message.success('文件解析成功');
                    //fileListId.value = res.file_id;
                    //headerIndex.value = res.headernum;
                    //openTable.value = true;
                   // for(var i = 0; i < res.headernum+1; i++){
                    //    selectedRows.value.push(i)
                    //}
                //}else{
                 //   message.error(res.error);
                  //  isloading.value = false;
                 //   openTable.value = false;
               // }
           // })
            //.catch((error: any) => {
             //   isloading.value = false;
             //   if (error.response) {
             //       const status = error.response.status;
             //       if (status === 422) {
             //           message.error(error.response.data.error);
             //       } else {
             //           message.error(error.message);
             //       }
               // } else {
               // message.error(error.message);
            //}
       // });        
        } catch (error) {
            message.error("文件解析失败,请检查文件内容!");
        } finally {
            loading.value = false;
        }
    };
    reader.readAsArrayBuffer(file);
    return false;
};
// 过滤掉空白列和对应的列配置
const filteredColumns = computed(() => {
    const nonEmptyColumns = columns.value.filter((column, index) => {
        return tableData.value.some(row => row[`col${index}`]?.value !== "");
    });
    return nonEmptyColumns;
});

//处理表格数据
const filteredTableData = computed(() => {
    return tableData.value.map(row => {
        const newRow: any = {};
        filteredColumns.value.forEach((column, index) => {
            newRow[column.dataIndex] = row[column.dataIndex];
        });
        newRow.key = row.key; // 确保每一行都有唯一的 key
        return newRow;
    });
});
</script>
相关推荐
码猩1 分钟前
C# winform根据EXCEL匹配文件后将txt的图片分别下载到指定的文件夹里
开发语言·c#·excel
阳树阳树5 分钟前
Solidjs 响应式 & 编译原理初探
前端·javascript·面试
liangmou21219 分钟前
HTML5的笔记
前端·笔记·html·html5
MurphyChen17 分钟前
前端请求进化史 :从 Form 到 Server Actions 🚀
前端·javascript·面试
焚 城28 分钟前
AI结合VBA提升EXCEL办公效率尝试
ai·excel
兰德里的折磨55032 分钟前
基于若依和elementui实现文件上传(导入Excel表)
前端·elementui·excel
喝拿铁写前端35 分钟前
一个列表页面,初级中级高级前端之间的鸿沟就显出来了
前端·架构·代码规范
唐骁虎36 分钟前
Excel VBA 运行时错误1004’:方法‘Open’作用于对象‘Workbooks’时失败 的解决方法
excel
magic 2451 小时前
ES6变量声明:let、var、const全面解析
前端·javascript·ecmascript·es6
M_chen_M2 小时前
es6学习02-let命令和const命令
前端·学习·es6