
返回文章
ARTICLE
前端实现读取xlxs、xls数据并渲染到页面(VUE实现)
在前端实现 读取上传的xlsx、xls文件数据,并渲染到页面上 [github地址链接](https://github.com/canbaoSama/viteComponents/tree/master/src/pages/xlsx) 这里拿到的 excelJsonData 数据就是我们需要的 json 数据。但是这种数据格式只适合于渲染普通的表格
前端实现读取xlxs、xls数据并渲染到页面(VUE实现)
在前端实现 读取上传的xlsx、xls文件数据,并渲染到页面上
直接github抄一份😀😀😀
分步实现
安装 xlsx
npm i xlsx
引入组件
import * as XLSX from 'xlsx/xlsx.js';
通过 ant 的上传组件实现
<template>
<a-upload
:show-upload-list="false"
accept=".xlsx, .xls"
:multiline="false"
list-type="picture-card"
class="flex items-center flex-wrap justify-center"
:beforeUpload="beforeUpload"
>
<div class="upload-content">
<CloudUploadOutlined style="font-size: 40px; color: #1890ff" />
<div class="mt-1">导入文件</div>
</div>
</a-upload>
</template>
<script setup>
import { CloudUploadOutlined } from '@ant-design/icons-vue';
const excelJsonData = ref([]);
const readFile = async (file) =>
new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (ev) => {
resolve(ev.target.result);
};
reader.readAsBinaryString(file); //以二进制的方式读取
});
const beforeUpload = async (file) => {
spinning.value = true;
const data = await readFile(file);
const workbook = XLSX.read(data, { type: 'binary' });
const worksheet = workbook.Sheets[workbook.SheetNames[0]]; //获取第一个Sheet
excelJsonData.value = XLSX.utils.sheet_to_json(worksheet); //json数据格式
try {
parsingTable(worksheet);
} finally {
spinning.value = false;
}
};
</script>
这里拿到的 excelJsonData 数据就是我们需要的 json 数据。但是这种数据格式只适合于渲染普通的表格。
复杂表格数据处理
那这个时候就需要我们的核心方法 parsingTable,读取到表格的合并方式并生成渲染时需要的数据格式。
const dataSource = ref([]);
//对数据进行处理,实现表格合并展示的功能
const parsingTable = (table) => {
let header = []; //表格列
const keys = Object.keys(table);
let maxRowIndex = 0; //最大行数
//!ref工作表范围
if (table['!ref'] && table['!ref'].includes(':')) {
const refs = table['!ref'].split(':');
maxRowIndex = refs[1].replace(/[A-Z]/g, '');
}
for (const [i, h] of keys.entries()) {
//提取key中的英文字母
const col = h.replace(/[^A-Z]/g, '');
//单元格是以A-1的形式展示的,所以排除包含!的key
h.indexOf('!') === -1 && header.indexOf(col) === -1 && header.push(col);
//如果!ref不存在时, 设置某一列最后一个单元格的索引为最大行数
if ((!table['!ref'] || !table['!ref'].includes(':')) && header.some((c) => table[`${c}${i}`])) {
maxRowIndex = i > maxRowIndex ? i : maxRowIndex;
}
}
header = header.sort((a, b) => a.localeCompare(b)); //按字母顺序排序 [A, B, ..., E, F]
// console.log(header)
// console.log(maxRowIndex)
//excel的行表示为 1, 2, 3, ......, 所以index起始为1
for (let index = 1; index <= maxRowIndex; index++) {
let row = []; //行
//每行的单元格集合, 例: [A1, ..., F1]
row = header.map((item) => {
const key = `${item}${index}`;
const cell = table[key];
return {
key,
name: cell ? cell.v : '',
// style: cell ? cell.s : '', //单元格的样式/主题, 有些不适用
};
});
dataSource.value.push(row);
}
//合并单元格
if (table['!merges']) {
for (const item of table['!merges']) {
//s开始 e结束 c列 r行 (行、列的索引都是从0开始的)
for (let r = item.s.r; r <= item.e.r; r++) {
for (let c = item.s.c; c <= item.e.c; c++) {
// console.log('=======', r, c)
//查找单元格时需要r+1
//例:单元格A1的位置是{c: 0, r:0}
const rowIndex = r + 1;
if (!dataSource.value[r]) {
dataSource.value.splice(
r,
0,
header.map((col) => ({ key: `${col}${rowIndex}` })),
);
}
const cell = dataSource.value[r].find((a) => a.key === `${header[c]}${rowIndex}`);
cell.rowspan = 0;
cell.colspan = 0;
}
}
//合并时保留范围内左上角的单元格
const start = `${header[item.s.c]}${item.s.r + 1}`;
// let end = `${header[item.e.c]}${item.e.r + 1}`
// console.log(start)
const cell = dataSource.value[item.s.r].find((a) => a.key === start);
cell.rowspan = item.s.r !== item.e.r ? item.e.r - item.s.r + 1 : 1; //纵向合并
cell.colspan = item.s.c !== item.e.c ? item.e.c - item.s.c + 1 : 1; //横向合并
}
}
};
数据渲染生成表格
因为表格存在各个合并的情况,所以建议直接用原生表格实现,特别要注意的是,td 内的数据需要用 pre 标签包裹,否则样式不能保证。 当然,要是不想保持里面的换行、空格等,请自由发挥
<template>
<table id="tableView" style="border-collapse: collapse; border: 1px solid">
<tr v-for="(item, index) in dataSource" :key="index">
<template v-for="(sub, subIndex) in item" :key="subIndex">
<td v-if="sub.rowspan !== 0 && sub.colspan !== 0" :rowspan="sub.rowspan || 1" :colspan="sub.colspan || 1" style="width: auto">
<pre class="flex items-center m-0">{{ `${sub?.name?.toString().trim()}` }}</pre>
</td>
</template>
</tr>
</table>
</template>
<script setup>
defineProps({ dataSource: Array });
</script>
<style lang="less" scoped>
tr {
background: #fff;
&:first-child {
word-break: keep-all;
background-color: #fafafa;
font-weight: bold;
}
td {
padding: 16px;
border: 1px solid #f0f0f0;
word-break: keep-all;
&:first-child pre {
justify-content: center;
}
}
}
</style>