前言
这是一个常用的功能,就是导入和导出excel表格
但是时常会遇到一些复杂表头的表格导出和导入
比如我这个案例里面的三层表头的表格。
网上看了下发现了一个非常简单导出和导入方法
当然这个是纯前端的版本,会出现分页不好下载的情况。所以实际工作中,导出还是后端负责的。
效果图
这里是表格的样式,三层表头
这里是点击导出后的效果和表格格式
这里是点击导入后的效果和获取的数据格式
使用方法简介
1,要下载一个插件,输入这个指令:npm install -S file-saver xlsx
2,在页面中引入这个,直接在你需要写导入导出的表格页面写就行
3,给你的表格绑上这个id。用来导出的时候拿数据的
这里如果想要导入后获取数据传给后端,就在这个位置把他循环push到一个新数组内,然后在循环外面把这个数组传给后端
代码部分(纯前端导入导出)
<template>
<div>
<div class="titleBtn">
<!-- 导出Excel -->
<el-button
@click="exportClick"
type="primary"
size="small"
style="margin: 0 20px"
icon="el-icon-folder-opened"
>导出</el-button
>
<!-- 导入Excel -->
<el-upload
action="/上传文件的接口"
:on-change="onChange"
:auto-upload="false"
:show-file-list="false"
a***ept=".xls, .xlsx"
ref="upload"
:multiple="true"
>
<el-button type="warning" icon="el-icon-folder-add" size="small"
>导入</el-button
>
</el-upload>
</div>
<el-table :data="tableData" style="width: 100%" id="mainTable">
<el-table-column prop="date" label="日期" width="150"> </el-table-column>
<el-table-column label="配送信息">
<el-table-column prop="name" label="姓名" width="120">
</el-table-column>
<el-table-column label="地址">
<el-table-column prop="province" label="省份" width="120">
</el-table-column>
<el-table-column prop="city" label="市区" width="120">
</el-table-column>
<el-table-column prop="address" label="地址" width="300">
</el-table-column>
<el-table-column prop="zip" label="邮编" width="120">
</el-table-column>
</el-table-column>
</el-table-column>
</el-table>
</div>
</template>
<script>
import FileSaver from "file-saver";
import * as XLSX from "xlsx";
export default {
data() {
return {
tableData: [
{
date: "2016-05-02",
name: "王小虎",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333,
},
],
};
},
methods: {
//导出
exportClick() {
//第一个参数是到处后文件名,第二个是id绑定表格dom
this.exportExcel("test", "mainTable");
},
//转换数据
exportExcel(filename, tableId) {
var xlsxParam = { raw: true }; // 导出的内容只做解析,不进行格式转换
var table = document.querySelector("#" + tableId).cloneNode(true);
var wb = XLSX.utils.table_to_book(table, xlsxParam);
/* 获取二进制字符进行输出 */
var wbout = XLSX.write(wb, {
bookType: "xlsx",
bookSST: true,
type: "array",
});
try {
FileSaver.saveAs(
new Blob([wbout], { type: "application/octet-stream" }),
filename + ".xlsx"
);
} catch (e) {
if (typeof console !== "undefined") {
console.log(e, wbout);
}
}
return wbout;
},
//导入
onChange(file, fileList) {
this.readExcel(file); // 调用读取数据的方法
},
// 读取数据
readExcel(file) {
let that = this;
if (!file) {
//如果没有文件
return false;
} else if (!/.(xls|xlsx)$/.test(file.name.toLowerCase())) {
this.$message.error("上传格式不正确,请上传xls或者xlsx格式");
return false;
}
const fileReader = new FileReader();
fileReader.onload = (ev) => {
try {
const data = ev.target.result;
const workbook = XLSX.read(data, {
type: "binary",
});
if (workbook.SheetNames.length >= 1) {
this.$message({
message: "导入数据表格成功",
showClose: true,
type: "su***ess",
});
}
const wsname = workbook.SheetNames[0]; //取第一张表
const ws = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]); //生成json表格内容
console.log("生成json:", ws);
// that.tableData = [];
for (var i = 2; i < ws.length; i++) {
let sheetData = {
// 键名为绑定 el 表格的关键字,值则是 ws[i][对应表头名]
date: ws[i]["日期"],
name: ws[i]["配送信息"],
province: ws[i]["__EMPTY"],
city: ws[i]["__EMPTY_1"],
address: ws[i]["__EMPTY_2"],
zip: ws[i]["__EMPTY_3"],
};
console.log("上传的数据:", sheetData);
//添加到表格中
that.tableData.push(sheetData);
//正常导入需要拿到上传的数据就在这从新弄个数组push进去,然后传给后台,后台保存后查询表格返给前端。
}
this.$refs.upload.value = "";
} catch (e) {
console.log(e);
return false;
}
};
// 如果为原生 input 则应是 files[0]
fileReader.readAsBinaryString(file.raw);
},
},
};
</script>
<style scoped>
.titleBtn {
display: flex;
margin: 20px 0;
}
</style>
配合后端的两个方法
因为上面的纯前端写法有一个问题,就是有分页的时候我们没法拿到数据。
或者数据太大了我们下载实在是有点慢和卡。所以基本上工作中都是后端生成下载链接导出的。
这里再分享两个方法。
1,a标签下载
这种方法核心就是后端直接生成下载链接,前端只需要生成A标签然后下载就行了。较为常用的一个
daochu(){
// A标签导出方法:通过生成一个A标签然后触发后台传过来的下载链接完成导出
//核心注意需要给请求的格式改为:responseType: "blob",
this.axios
.post(
url, {}, {
token: true,
responseType: "blob",
}
)
.then((res) => {
if (res.status == 200) {
//拿到后台发过来的下载链接
let url = window.URL.createObjectURL(new Blob([res.data]));
//生成一个A标签
let link = document.createElement("a");
//样式设为none,没有大小,不占位置
link.style.display = "none";
//把链接地址给href
link.href = url;
//下载后的名字,用时间来标注避免重复
let filename = new Date().getTime() + ".xlsx";
//给A标签添加属性download,值为上面的名字
link.setAttribute("download", filename);
//在页面的尾部插入元素A标签
document.body.appendChild(link);
//点击A标签,这样就触发下载了。
link.click();
} else if (res.data.code != 200) {
this.$message("暂无数据");
}
this.exporloading = false;
})
.catch((err) => {
this.exporloading = false;
});
}
2,用上面的那种前端方法,我们直接让后端传给我们一个完整的表格数据,不分页的那种。
然后用这个数据赋值到表格的数组内,然后把表格dom拿去导出方法中也可以下载。
这里我是因为后端没有给全部的数据,然后数据不是很多,我就干脆自己前台直接循环请求把每一页的数据合并到数组内实现了拿到所有数据的目的,不过这样不推荐啊。
这里this.$excels是我封装了一下这个方法,然后main.js赋值原型了。方法还是和上面的一样哈。
封装的样子,和上面基本是一样的。
这是循环拿到的分页数据,看到了吧85条都拿到了,然后复制给tableData2数组,然后拿表格的dom,就可以导出全部数据的表格了
表格中多个sheet一次读取出来写法
有时候我们需要的不仅仅是第一个sheet的。可能一个表格中有很多个sheet然后每个sheet字段不一样。所以我们可以这样写,在上面的方法中稍加改动。
只需要把readExcel这个方法修改一下就行了。修改的地方主要就是加了个循环,读取每一个sheet的数据出来。
// 读取数据
readExcel(file) {
let that = this;
if (!file) {
//如果没有文件
return false;
} else if (!/.(xls|xlsx)$/.test(file.name.toLowerCase())) {
this.$message.error("上传格式不正确,请上传xls或者xlsx格式");
return false;
}
const fileReader = new FileReader();
fileReader.onload = (ev) => {
try {
const data = ev.target.result;
const workbook = XLSX.read(data, {
type: "binary",
});
if (workbook.SheetNames.length >= 1) {
//读取到表中sheet
this.$message({
message: "导入数据表格成功",
showClose: true,
type: "su***ess",
});
}
for (var i = 0; i < workbook.SheetNames.length; i++) {
const wsname = workbook.SheetNames[i]; //取第一张表
const ws = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]); //生成json表格内容
console.log("生成json:", ws);
// that.tableData = [];
// for (var i = 2; i < ws.length; i++) {
// let sheetData = {
// // 键名为绑定 el 表格的关键字,值则是 ws[i][对应表头名]
// date: ws[i]["日期"],
// name: ws[i]["配送信息"],
// province: ws[i]["__EMPTY"],
// city: ws[i]["__EMPTY_1"],
// address: ws[i]["__EMPTY_2"],
// zip: ws[i]["__EMPTY_3"],
// };
// console.log("上传的数据:", sheetData);
// //添加到表格中
// that.tableData.push(sheetData);
// //正常导入需要拿到上传的数据就在这从新弄个数组push进去,然后传给后台,后台保存后查询表格返给前端。
// }
}
this.$refs.upload.value = "";//清空上传列表,不能放在循环内清空,否则第一次循环sheet1时直接清空了上传列表,第二次循环sheet2就没数据了
} catch (e) {
console.log(e);
return false;
}
};
// 如果为原生 input 则应是 files[0]
fileReader.readAsBinaryString(file.raw);
},
多sheet读取效果图