关于导出Excel,我想很多同学都有过接触过,比如:在一个页面点击一个导出按钮,然后就会导出一个Excel表,表里面会展示一些我们需要看的列表数据。这种实现方式呢一般前后端都可以做。但是根据我自己的经历我感觉后端做比较好,为什么呢?因为前端做导出Excel是要考虑很多因素的,比如:导出数据量有多大、导出的时间大概是需要多少时间、导出这么大数据花费这么长时间前端页面会不会挂掉,还有就是用户体验等等。
当然也不是说前端就不能做,这需要看我们项目中的需求是什么样的,如果考虑到的问题都不是什么问题,那么也可以纯前端去做。
因为我在项目中遇到好多导出Excel功能,但每次遇到都得去找,所以呢我就总结一下,做个备份。
1、导入
React
项目需要去安装xlsx
npm install xlsx --save // 本次安装的版本是"^0.14.1"
组件中需要导入xlsx
import XLSX from 'xlsx';
原生js
<script src="http://oss.sheetjs.com/js-xlsx/xlsx.full.min.js"></script>
2、导出Excel需要容器及按钮
<a href="" download="导出.xlsx" id="hf">
<span onClick={this.excelClick.bind(this)} ></span>
</a>
3、导出Excel逻辑代码
Object.assign(target, ...sources)
用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。
target
目标对象。
sources
源对象。
返回值:目标对象。
描述:如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖。后来的源的属性将类似地覆盖早先的属性。
事例:
var obj = { a: 3, b: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 3, b: 1 }
array1.reduce(callbackfn[, initialValue])
对数组中的所有元素调用指定的回调函数。该回调函数的返回值为累积结果,并且此返回值在下一次调用该回调函数时作为参数提供。
concat()
用于连接两个或多个数组。
import React from 'react';
import XLSX from 'xlsx';
let tmpDown; // 导出的二进制对象
class ExportExcel extends React.Component {
constructor() {
super();
this.state = {
dataW: [], // 信息表数据
dataT: [] // 成绩表数据
}
}
componentDidMount() {
const dataW = [{
name: '小明',
age: 20,
height: 170,
weight: 120,
hobby: '篮球'
}];
const dataT = [{
name: '小红',
cn: 100,
en: 105,
sx: 102,
}]
this.setState({ dataW, dataT });
}
// 将指定的自然数转换为26进制表示。映射关系:[0-25] -> [A-Z]。
getCharCol = (n) => {
let s = '';
let m = 0;
while (n > 0) {
m = n % 26 + 1
s = String.fromCharCode(m + 64) + s
n = (n - m) / 26
}
return s
}
// 导出Excel公共部分
excelCommon = (data) => {
let tmpObj = data[0]; // 拿到数据的第一个对象
data.unshift({}); // 在json内添加一个对象存放表格head
let keyMap = []; // 定义一个数组存放表格的head
// 循环json中拿到的对象目的是为了构建head
for (var k in tmpObj) {
keyMap.push(k); // 将对象中的key值存放到定义的数组中
data[0][k] = k; // 将表头的内容以key/value的形式存放到json的空对象中
};
let tmpdata = []; // 用来保存转换好的json
data.map((v, i) => keyMap.map((k, j) => Object.assign({}, {
v: v[k],
position: (j > 25 ? this.getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1)
}))).reduce((prev, next) => prev.concat(next)).forEach((v, i) => tmpdata[v.position] = {
v: v.v,
});
let outputPos = Object.keys(tmpdata); // 设置区域,比如表格从A1到D10
return {
outputPos,
tmpdata
}
}
// 字符串转字符流
s2ab = (s) => {
let buf = new ArrayBuffer(s.length);
let view = new Uint8Array(buf);
for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
// 导出Excel
excelClick = () => {
const type = "xlsx"; // 导出的格式这里写死为xlsx
let { dataW, dataT } = this.state;
// 表格中表头需要显示中文,将数据的key值换成中文
let dataWCN = [];
let dataTCN = [];
if (dataW.length > 0) {
dataW.map((i) => {
let dataWObj = {
"姓名": i.name,
"年龄": i.age || 0, // 导出数据中不能有字段值为null
"身高": i.height,
"体重": i.weight,
"爱好": i.hobby,
};
dataWCN.push(dataWObj);
})
} else {
let dataWObj = {
"姓名": '无',
"年龄": '无',
"身高": '无',
"体重": '无',
"爱好": '无'
};
dataWCN.push(dataWObj);
};
if (dataT.length > 0) {
dataT.map((i) => {
let dataTObj = {
"名称": i.name,
"语文": i.cn,
'外语': i.en,
"数学": i.sx
};
dataTCN.push(dataTObj);
})
} else {
let dataTObj = {
"名称": '无',
"语文": '无',
'外语': '无',
"数学": '无'
};
dataTCN.push(dataTObj);
}
// 调取公共部分构建数据
let upDataWCN = this.excelCommon(dataWCN);
let upDataTCN = this.excelCommon(dataTCN);
// 生成表格
let tmpWB = {
SheetNames: ['信息表', '成绩表'], // 保存的表标题
Sheets: {
'信息表': Object.assign({},
upDataWCN.tmpdata, // 表格内容
{
'!ref': upDataWCN.outputPos[0] + ':' + upDataWCN.outputPos[upDataWCN.outputPos.length - 1] // 设置填充区域
}),
'成绩表': Object.assign({},
upDataTCN.tmpdata, // 表格内容
{
'!ref': upDataTCN.outputPos[0] + ':' + upDataTCN.outputPos[upDataTCN.outputPos.length - 1] // 设置填充区域
})
}
};
// 创建二进制对象写入转换好的字节流
tmpDown = new Blob([this.s2ab(XLSX.write(tmpWB,
{bookType: (type === undefined ? 'xlsx':type),bookSST: false, type: 'binary'}// 这里是用来定义导出的格式类型
))], {
type: ""
});
var href = URL.createObjectURL(tmpDown); // 创建对象超链接
document.getElementById("hf").href = href; // 绑定a标签
// document.getElementById("hf").click(); // 模拟点击实现下载
setTimeout(function() { // 延时释放
URL.revokeObjectURL(tmpDown); // 用URL.revokeObjectURL()来释放这个object URL
}, 100);
}
render() {
return(
<a href="" download="导出.xlsx" id="hf">
<span className="expert_modeler_BO_export" onClick={this.excelClick.bind(this)} ></span>
</a>
)
}
}
export default ExportExcel;