vue3 解析excel字节流文件 以表格形式显示页面上 带公式
导出:解析excel 二进制文件
https://blog.csdn.net/weixin_43746297/article/details/146204040
导入:解析excel 二进制文件
解析:
注意:使用前需通过npm install xlsx
安装依赖库
动态表头生成
使用
sheet_to_json
的header:1
参数提取首行作为表头
Excel工作表转换为JSON格式:
const jsonData = XLSX.utils.sheet_to_json(firstSheet, {header: 1,raw: false, //保留公式defval: ""
})
参数配置如下:
-
header:
指定输出结果为二维数组格式,每行数据以数组形式呈现。若不设置该参数或设为其他值,则默认输出键值对格式的对象数组。 -
raw: false
保留单元格原始公式而非计算结果,适用于需要获取Excel公式的场景。若设为true
则输出公式计算结果。 -
defval: ""
为空单元格设置默认值为空字符串,避免解析时跳过空值导致字段缺失。未设置时默认会忽略空单元格。
jsonData
结构取决于header
参数:
- 当
header:1
时:结果为Array<Array<any>>
,每行数据为子数组 - 默认情况:结果为
Array<Object>
,首行作为键名
典型应用场景包括:
- 前端导入带公式的Excel表格
- 需要严格保留空单元格位置的表格处理
- 复杂表头数据的结构化转换
使用xlsx
库读取Excel数据
const workbook = XLSX.read(data, {type: 'array',cellFormula: true,cellHTML: false});
主要功能如下:
type: 'array'
参数表示输入数据是Uint8Array类型的二进制数组cellFormula: true
会保留Excel单元格中的公式信息,而不是只获取计算结果cellHTML: false
表示不需要将单元格内容转换为HTML格式
workbook对象包含属性:
- SheetNames属性(工作表名称列表)
- Sheets对象(包含各个工作表数据)
典型使用场景包括:
- 前端导入带公式的Excel文件
- 需要保留原始公式而非计算结果的场景
从接口中获取
1.封装接口方法 获取excel字节流:
封装示例
封装方法,调用方法和传参形式依照自己的,这里只是示例
注意:responseType: 'arraybuffer' 必填***
调用方法
exportHandover({'传参':'传参'}).then((res) => {// 获取到二进制流 res}).catch((err) => {//弹出错误信息});
- 这里是解析二进制流之后的数据格式 可自定义,可查看 下方完整代码
arr.push({sheetName,columns: headers.map((h, i) => ({prop: `col${i}`,label: h})),list: [{columns: headers.map((h, i) => ({prop: `col${i}`,label: h})),data: dataRows.map(row => {return headers.reduce((obj, header, idx) => {obj[`col${idx}`] = row[idx] || ""return obj}, {})})}]})
完整代码 :
此段代码适用于 excel 单文件/多文件
<template><div class="layout-padding w100"><div class="layout-padding-auto layout-padding-view"><el-tabs style="height: calc(100% - 50px)" v-model="activeFile" type="card"><el-tab-panev-for="file in fileData":key="file.sheetName":label="file.sheetName":name="file.sheetName"class="table-container"><el-table:data="file.list"border:header-cell-style="{ borderColor: '#C0C0C0',textAlign:'center' }":cell-style="{ borderColor: '#C0C0C0',textAlign:'center '}"><el-table-columnv-for="col in file.columns":key="col.prop":prop="col.prop":label="col.label"/></el-table></el-tab-pane></el-tabs></div></div>
</template><script setup lang="ts" name="team">
import {useI18n} from 'vue-i18n';
import {reactive, ref, onMounted} from 'vue';
import {viewDeta} from '/@/stores/viewDeta';
import {exportHandover} from "/@/api/shiftHandover";
import {downBlobFile} from '/@/utils/other';
import {ElLoading} from 'element-plus';
import * as XLSX from 'xlsx';
import {useMessage} from "/@/hooks/message";const stores = viewDeta();
// 引入组件
const {t} = useI18n();
// 定义变量内容
const editFormDialog = ref();
const fileData = ref()
const activeFile = ref()
const queryForm = reactive({yearMonth: new Date().toISOString().slice(0, 7),deptId: '',roleId: '',
})
const getDeptData = () => {if (stores.$state.rolesName != undefined) {const loading = ElLoading.service({lock: true,text: '数据加载中...',background: 'rgba(5,5,5,0.6)',})exportHandover({参数}).then((res) => {const data = new Uint8Array(res)loading.close()const workbook = XLSX.read(data, {type: 'array',cellFormula: true,cellHTML: false});let arr = []workbook.SheetNames.forEach((sheetName) => {const firstSheet = workbook.Sheets[sheetName]const jsonData = XLSX.utils.sheet_to_json(firstSheet, {header: 1,raw: false,defval: ""})const headers = jsonData[0] || []const dataRows = jsonData.slice(1)arr.push({sheetName,columns: headers.map((h, i) => ({prop: `col${i}`,label: h})),list: [{columns: headers.map((h, i) => ({prop: `col${i}`,label: h})),data: dataRows.map(row => {return headers.reduce((obj, header, idx) => {obj[`col${idx}`] = row[idx] || ""return obj}, {})})}]})})arr.map((item) => {item.list = item.list.flatMap(r => r.data)})fileData.value = arractiveFile.value = fileData.value[0].sheetName}).catch((err) => {useMessage().error(err.msg);loading.close()});}
};onMounted(() => {
});
</script>
<style scoped lang="scss">
.table-container {display: flex;flex-direction: column;height: 100%; /* 或父容器高度 */
}.table-container > .el-table {flex: 1;min-height: 0; /* 关键属性 */
}
</style>
结果:
2.ajax方法直接获取
接口路径:/api/excel
使用
axios
时需设置responseType: 'arraybuffer'
或'blob'
接收二进制流
const res = await axios.get('/api/excel', {responseType: 'arraybuffer'
});
完整代码:
此代码适用于单文件,如需多文件 参考上方封装接口方法
<template><div><button @click="loadExcelData">加载Excel数据</button><table v-if="tableData.length"><thead><tr><th v-for="col in columns" :key="col.key">{{ col.title }}</th></tr></thead><tbody><tr v-for="(row, rowIndex) in tableData" :key="rowIndex"><td v-for="col in columns" :key="col.key">{{ row[col.key] }}</td></tr></tbody></table></div>
</template><script setup>
import { ref } from 'vue';
import axios from 'axios';
import * as XLSX from 'xlsx';// 表格数据和列配置
const tableData = ref([]);
const columns = ref([]);// 从接口加载Excel数据
const loadExcelData = async () => {try {// 1. 请求二进制数据(关键:responseType: 'arraybuffer')const response = await axios.get('https://your-api.com/excel', {responseType: 'arraybuffer'});// 2. 解析Excelconst data = new Uint8Array(response.data);const workbook = XLSX.read(data, { type: 'array' });const firstSheet = workbook.Sheets[workbook.SheetNames[0]];// 3. 提取表头(假设第一行为表头)const headerRow = XLSX.utils.sheet_to_json(firstSheet, { header: 1 })[0];columns.value = headerRow.map(key => ({key,title: key}));// 4. 提取表格数据tableData.value = XLSX.utils.sheet_to_json(firstSheet);} catch (error) {console.error('加载失败:', error);}
};
</script>
上传文件获取
此方法适用于excel中单文件,如excel中多文件 请参考上方 封装接口方法
<template><div><el-uploadmultiple:auto-upload="false":on-change="handleFileChange"accept=".xlsx,.xls"><el-button type="primary">选择多个Excel文件</el-button></el-upload><el-table v-for="(table, index) in tables" :key="index":data="table.data"borderstyle="margin-top:20px"><el-table-columnv-for="col in table.columns":key="col.prop":prop="col.prop":label="col.label":formatter="col.formatter"/></el-table></div>
</template><script setup>
import { ref } from 'vue'
import * as XLSX from 'xlsx'const tables = ref([])const handleFileChange = async (files) => {tables.value = []for (const file of files) {const buffer = await file.raw.arrayBuffer()const workbook = XLSX.read(buffer, { type: 'array',cellFormula: true // 保留公式})workbook.SheetNames.forEach(sheetName => {const worksheet = workbook.Sheets[sheetName]const jsonData = XLSX.utils.sheet_to_json(worksheet, {header: 1,raw: false,defval: ""})if(jsonData.length > 0) {const columns = jsonData[0].map((header, idx) => ({prop: `col${idx}`,label: header || `列${idx+1}`,formatter: (row) => {const cellRef = XLSX.utils.encode_cell({r: row.__rowNum__, c: idx})return worksheet[cellRef]?.f ? XLSX.utils.sheet_to_json(worksheet, {header:1})[row.__rowNum__][idx] :row[`col${idx}`]}}))const data = jsonData.slice(1).map((row, rowIndex) => {const rowObj = { __rowNum__: rowIndex + 1 }row.forEach((cell, colIndex) => {rowObj[`col${colIndex}`] = cell})return rowObj})tables.value.push({name: `${file.name}-${sheetName}`,columns,data})}})}
}
</script>