需求说明
前端页面通过表格呈现查询数据, 并由一个按钮点击事件将表格的数据以 xlsx 的文件格式导出下载.
开发环境
Chrome + vue 2.0 + Element UI
方案 1:XLSX + FILE-SAVER
由于文件下载需求的普遍性, 以及 vue 和 Element 的流行性, 网上类似的方案很多. 稍作整理后的形式如下:
- // 导出按钮绑定的函数
- exportXLSX() {
- // 从表格生成 workbook
- let wb = XLSX.utils.table_to_book(document.querySelector('#tableID'));
- let wbout = XLSX.write(wb, {
- bookType: "xlsx",
- bookSST: true,
- type: "array"
- });
- try {
- // 下载
- let b = new Blob([wbout], { type: "application/octet-stream" });
- FileSaver.saveAs(b, "filename.xlsx");
- } catch (e) {
- if (typeof console !== "undefined") {
- console.log(e, wbout);
- }
- }
- return wbout;
- }
该函数流程大致为:
通过 ID 找到表格
利用 XLSX 的 table_to_book 方法将表格数据直接生成到工作簿
将工作簿包装为 blob 对象
通过 FileSaver 实现文件保存功能
问题 1: 可能导出重复数据
当使用 Element UI 的 el-table 中的 fixed 属性时, 实际生成了 2 张表格, 因此以上方案导出的数据会出现重复的现象. 就像这样:
重复的数据
既然原因很清楚了, 那么这个问题的解决办法也容易找到: 在生成工作簿的时候, 首先去除带有 fix 属性的表格, 在工作簿对象生成后再添加上.
这样, 生成工作簿的代码更新为:
- let fix = document.querySelector('.el-table__fixed');
- if (fix) {
- let wb = XLSX.utils.table_to_book(document.querySelector('#tableID').removeChild(fix));
- document.querySelector('#tableID').appendChild(fix);
- } else {
- let wb = XLSX.utils.table_to_book(document.querySelector('#tableID'));
- }
问题 2: 分页数据只导出页面显示的部分
如果表格存在分页, 上述方法导出的是当前显示的部分. 网上也有几种解决办法:
前端隐藏一个全量的 table, 专门用于导出 (个人不推荐).
导出的时候临时修改 pageSize, 使全量数据都在表格上. 完成后还原.
这两个我都没尝试, 也就不贴代码了, 通过搜索都可以找到.
方案 2: 只使用 XLSX 解决问题
之前两个问题的解决办法要么触发了前端变更的操作, 要么保存了多余的内容. 虽然能解决问题, 但总有些变扭.
重新看了看 SheetJS 项目的文档, 发现有 json_to_sheet 方法, 官方的示例如下:
- var ws = XLSX.utils.json_to_sheet([
- { S:1, h:2, e:3, e_1:4, t:5, J:6, S_1:7 },
- { S:2, h:3, e:4, e_1:5, t:6, J:7, S_1:8 }
- ], {header:["S","h","e","e_1","t","J","S_1"]});
如果不显式定义列名, 则以第一个对象的 Object.keys 作为列名.
The default column order is determined by the first appearance of the field using Object.keys
这不正和 el-table 的表单数据格式一模一样么. 本地已经有 tableData, 且是全量数据, 不存在分页问题, 直接用 tableData 加上 json_to_sheet 方法生成 sheet 便可. 但这个方法返回的是 worksheet, 而非 workbook, 因此需要添加到一个 workbook. 最后使用 workbook 的 writeFile 方法即可完成下载. 最终代码如下:
- exportXLSX() {
- let ws = XLSX.utils.json_to_sheet(this.tableData);
- let wb = XLSX.utils.book_new();
- XLSX.utils.book_append_sheet(wb, ws, 'sheetname');
- return XLSX.writeFile(wb, 'filename.xlsx');
- }
代码可以如此简洁, 且用不着 file-saver 的原因是, 其实 writeFile 方法包装了很多行为, 包括使浏览器生成文件的链接并强制点击, 触发文件保存的动作等.
- XLSX.writeFile wraps a few techniques for triggering a file save:
- URL browser API creates an object URL for the file, which the library uses by creating a link and forcing a click. It is supported in modern browsers.
- msSaveBlob is an IE10+ API for triggering a file save.
- IE_FileSave uses VBScript and ActiveX to write a file in IE6+ for Windows XP and Windows 7. The shim must be included in the containing html page.
不知道这样的兼容性如何, 至少 Chrome 80 版本完全可行.
参考
SheetJS JS-xlsx 的 Git 库
Element-ui 组件库 Table 表格导出 Excel 表格
记 element + xlsx 导出表格数据重复的坑
来源: http://www.jianshu.com/p/a1b53ec95cea