Vue+Element 实现excel的导入导出
在最近项目中,用到了模板下载与批量上传的功能,下面是实现步骤(本地环境是@vue/cli 4.3.1+element,vue2.0可能会有报错,自行调整即可)
一、环境安装
1、先安装相关依赖
1 npm install -S xlsx file-saver
2 npm install -D script-loader
2、依赖装完之后,还需要准备两个js文件,分别是Blob.js和Export2Excel.js(这两个文件在文章末尾有源码),将两个文件放在项目指定目录中。我是放在里src目录下的utils中,放在其他目录也可以
二、excel文件导出
1、在相应vue文件中,定义触发导出excel文件的方法
1 <div class="insured-btn-div">
2 <el-button type="primary" size="small" @click="handleExport">模板下载</el-button>
3 </div>
2、再引入Export2Excel.js即可,源码如下
1 //模板下载
2 handleExport() {
3 require.ensure([], () => {
4 const { export_json_to_excel } = require('@/utils/Export2Excel');
5 console.log(this.insured);
6 let tHeader = [];
7 if (this.insured.length > 0) {
8 //从insured中提取标题
9 this.insured.forEach(item=>{
10 tHeader.push(item.name);
11 })
12 }
13 export_json_to_excel(tHeader, [], '被保险人信息模板');
14 return false;
15 })
16 },
注意:我的模板下载是一个只有标题的空excel,而且标题是直接从其他地方获取的,为了更好理解,再给出一个简单示例
1 // 下载
2 handleExport() {
3 require.ensure([], () => {
4 const {
5 export_json_to_excel
6 } = require('vendor/Export2Excel');
7 const tHeader = ['序号', '文章标题', '作者', '阅读数', '发布时间'];
8 const filterVal = ['id', 'title', 'author', 'views', 'display_time'];
9 const list = [
10 {id: 1, title: 2, author: 3, pageviews: 4, display_time: 5},
11 {id: 6, title: 7, author: 8, pageviews: 9, display_time: 10},
12 {id: 11, title: 12, author: 13, pageviews: 14, display_time: 15},
13 ];
14 const data = this.formatJson(filterVal, list);
15 export_json_to_excel(tHeader, data, '列表excel');
16 })
17 },
我的模板下载后是这样的:
简单示例得到的excel文件是这样的:由于filterVal中是views,而list中是pageviews,所以得到的阅读数那一栏都是空的
三、excel文件导出
模板下载之后,按照格式填写对应的数据,再上传解析
1、这是触发文件导出的方法,action="",不上传服务器,只是解析excel中的数据而已
1 <div class="insured-btn-div">
2 <el-upload
3 class="upload-demo"
4 action=""
5 :on-change="handleChange"
6 :show-file-list="false"
7 accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
8 :auto-upload="false">
9 <el-button size="small" type="primary">批量上传</el-button>
10 </el-upload>
11 </div>
2、解析excel文件的方法
1 //上传文件
2 handleChange(file, fileList){
3 var f = file.raw;// 获取文件内容
4 // 通过DOM取文件数据
5 var rABS = false; //是否将文件读取为二进制字符串
6 var reader = new FileReader();
7 var that = this;
8 //if (!FileReader.prototype.readAsBinaryString) {
9 FileReader.prototype.readAsBinaryString = function(f) {
10 var binary = "";
11 var rABS = false; //是否将文件读取为二进制字符串
12 var wb; //读取完成的数据
13 var outdata;
14 var reader = new FileReader();
15 reader.onload = function(e) {
16 var bytes = new Uint8Array(reader.result);
17 var length = bytes.byteLength;
18 for(var i = 0; i < length; i++) {
19 binary += String.fromCharCode(bytes[i]);
20 }
21 var XLSX = require('xlsx');
22 if(rABS) {
23 wb = XLSX.read(btoa(fixdata(binary)), { //手动转化
24 type: 'base64'
25 });
26 } else {
27 wb = XLSX.read(binary, {
28 type: 'binary'
29 });
30 }
31 outdata = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);//outdata就是你想要的东西
32 this.da = [...outdata] //这就是excel文件中的数据
33 // let arr = [];
34 // 下面是数据解析提取逻辑
35 if (that.insured.length > 0) {
36 //首先清空之前的被保人数据
37 that.insureTemplate.id = 0;//将模板id重置为0;
38 that.insureLength = 0; // 将投保人数据长度变为0
39 that.form.insuredTableData = [];//将投保人数据清空
40 for (let v of this.da) {
41 // 判断是否超出批量投保最大份数
42 if (that.insureTemplate.id >= that.product.batch_bill_max) {
43 break;
44 }
45 let obj = {};
46 that.insureTemplate.id += 1;
47 obj.id = that.insureTemplate.id;
48 that.insured.forEach(j=>{
49 if (v.hasOwnProperty(j.name)) {
50 obj[j.field] = v[j.name];
51 } else {
52 //模板里没有填的字段,直接赋空值
53 obj[j.field] = '';
54 }
55 });
56 // 提取被保人的生日及性别
57 if (obj.hasOwnProperty('insureder_cert_num')) {
58 let res = that.getBirthdayAndGenderByNum(obj.insureder_cert_num);
59 if (res[0] != 0) {
60 if (obj.hasOwnProperty('insureder_birthdate')) {
61 obj.insureder_birthdate = res[0];
62 }
63 if (obj.hasOwnProperty('insureder_sex')) {
64 if (res[1] == 0) {
65 //注意这个男女ID和后台对应
66 obj.insureder_sex = 49;
67 } else {
68 obj.insureder_sex = 48;
69 }
70 }
71 }
72 }
73 obj.price = '0.00';
74 that.form.insuredTableData.push(obj);
75 // obj.code = v['被保人姓名']
76 // obj.type = v['被保人手机号']
77 // arr.push(obj)
78 }
79 that.insureLength = that.insureTemplate.id;
80 }
81 // return arr
82 }
83 reader.readAsArrayBuffer(f);
84 }
85
86 if(rABS) {
87 reader.readAsArrayBuffer(f);
88 } else {
89 reader.readAsBinaryString(f);
90 }
91
92 }
这是模板文件中填写的数据
最后再贴上Blob.js和Export2Excel.js的源码
Blob.js
1 /* eslint-disable */
2 /* Blob.js
3 * A Blob implementation.
4 * 2014-05-27
5 *
6 * By Eli Grey, http://eligrey.com
7 * By Devin Samarin, https://github.com/eboyjr
8 * License: X11/MIT
9 * See LICENSE.md
10 */
11
12 /*global self, unescape */
13 /*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
14 plusplus: true */
15
16 /*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
17
18 (function (view) {
19 "use strict";
20
21 view.URL = view.URL || view.webkitURL;
22
23 if (view.Blob && view.URL) {
24 try {
25 new Blob;
26 return;
27 } catch (e) {}
28 }
29
30 // Internally we use a BlobBuilder implementation to base Blob off of
31 // in order to support older browsers that only have BlobBuilder
32 var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
33 var
34 get_class = function(object) {
35 return Object.prototype.toString.call(object).match(/^[objects(.*)]$/)[1];
36 }
37 , FakeBlobBuilder = function BlobBuilder() {
38 this.data = [];
39 }
40 , FakeBlob = function Blob(data, type, encoding) {
41 this.data = data;
42 this.size = data.length;
43 this.type = type;
44 this.encoding = encoding;
45 }
46 , FBB_proto = FakeBlobBuilder.prototype
47 , FB_proto = FakeBlob.prototype
48 , FileReaderSync = view.FileReaderSync
49 , FileException = function(type) {
50 this.code = this[this.name = type];
51 }
52 , file_ex_codes = (
53 "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
54 + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
55 ).split(" ")
56 , file_ex_code = file_ex_codes.length
57 , real_URL = view.URL || view.webkitURL || view
58 , real_create_object_URL = real_URL.createObjectURL
59 , real_revoke_object_URL = real_URL.revokeObjectURL
60 , URL = real_URL
61 , btoa = view.btoa
62 , atob = view.atob
63
64 , ArrayBuffer = view.ArrayBuffer
65 , Uint8Array = view.Uint8Array
66 ;
67 FakeBlob.fake = FB_proto.fake = true;
68 while (file_ex_code--) {
69 FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
70 }
71 if (!real_URL.createObjectURL) {
72 URL = view.URL = {};
73 }
74 URL.createObjectURL = function(blob) {
75 var
76 type = blob.type
77 , data_URI_header
78 ;
79 if (type === null) {
80 type = "application/octet-stream";
81 }
82 if (blob instanceof FakeBlob) {
83 data_URI_header = "data:" + type;
84 if (blob.encoding === "base64") {
85 return data_URI_header + ";base64," + blob.data;
86 } else if (blob.encoding === "URI") {
87 return data_URI_header + "," + decodeURIComponent(blob.data);
88 } if (btoa) {
89 return data_URI_header + ";base64," + btoa(blob.data);
90 } else {
91 return data_URI_header + "," + encodeURIComponent(blob.data);
92 }
93 } else if (real_create_object_URL) {
94 return real_create_object_URL.call(real_URL, blob);
95 }
96 };
97 URL.revokeObjectURL = function(object_URL) {
98 if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
99 real_revoke_object_URL.call(real_URL, object_URL);
100 }
101 };
102 FBB_proto.append = function(data/*, endings*/) {
103 var bb = this.data;
104 // decode data to a binary string
105 if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
106 var
107 str = ""
108 , buf = new Uint8Array(data)
109 , i = 0
110 , buf_len = buf.length
111 ;
112 for (; i < buf_len; i++) {
113 str += String.fromCharCode(buf[i]);
114 }
115 bb.push(str);
116 } else if (get_class(data) === "Blob" || get_class(data) === "File") {
117 if (FileReaderSync) {
118 var fr = new FileReaderSync;
119 bb.push(fr.readAsBinaryString(data));
120 } else {
121 // async FileReader won't work as BlobBuilder is sync
122 throw new FileException("NOT_READABLE_ERR");
123 }
124 } else if (data instanceof FakeBlob) {
125 if (data.encoding === "base64" && atob) {
126 bb.push(atob(data.data));
127 } else if (data.encoding === "URI") {
128 bb.push(decodeURIComponent(data.data));
129 } else if (data.encoding === "raw") {
130 bb.push(data.data);
131 }
132 } else {
133 if (typeof data !== "string") {
134 data += ""; // convert unsupported types to strings
135 }
136 // decode UTF-16 to binary string
137 bb.push(unescape(encodeURIComponent(data)));
138 }
139 };
140 FBB_proto.getBlob = function(type) {
141 if (!arguments.length) {
142 type = null;
143 }
144 return new FakeBlob(this.data.join(""), type, "raw");
145 };
146 FBB_proto.toString = function() {
147 return "[object BlobBuilder]";
148 };
149 FB_proto.slice = function(start, end, type) {
150 var args = arguments.length;
151 if (args < 3) {
152 type = null;
153 }
154 return new FakeBlob(
155 this.data.slice(start, args > 1 ? end : this.data.length)
156 , type
157 , this.encoding
158 );
159 };
160 FB_proto.toString = function() {
161 return "[object Blob]";
162 };
163 FB_proto.close = function() {
164 this.size = this.data.length = 0;
165 };
166 return FakeBlobBuilder;
167 }(view));
168
169 view.Blob = function Blob(blobParts, options) {
170 var type = options ? (options.type || "") : "";
171 var builder = new BlobBuilder();
172 if (blobParts) {
173 for (var i = 0, len = blobParts.length; i < len; i++) {
174 builder.append(blobParts[i]);
175 }
176 }
177 return builder.getBlob(type);
178 };
179 }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
Export2Excel.js
1 /* eslint-disable */
2 require('script-loader!file-saver');
3 require('@/utils/Blob');
4 require('script-loader!xlsx/dist/xlsx.core.min');
5 function generateArray(table) {
6 var out = [];
7 var rows = table.querySelectorAll('tr');
8 var ranges = [];
9 for (var R = 0; R < rows.length; ++R) {
10 var outRow = [];
11 var row = rows[R];
12 var columns = row.querySelectorAll('td');
13 for (var C = 0; C < columns.length; ++C) {
14 var cell = columns[C];
15 var colspan = cell.getAttribute('colspan');
16 var rowspan = cell.getAttribute('rowspan');
17 var cellValue = cell.innerText;
18 if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
19
20 //Skip ranges
21 ranges.forEach(function (range) {
22 if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
23 for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
24 }
25 });
26
27 //Handle Row Span
28 if (rowspan || colspan) {
29 rowspan = rowspan || 1;
30 colspan = colspan || 1;
31 ranges.push({s: {r: R, c: outRow.length}, e: {r: R + rowspan - 1, c: outRow.length + colspan - 1}});
32 }
33 ;
34
35 //Handle Value
36 outRow.push(cellValue !== "" ? cellValue : null);
37
38 //Handle Colspan
39 if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
40 }
41 out.push(outRow);
42 }
43 return [out, ranges];
44 };
45
46 function datenum(v, date1904) {
47 if (date1904) v += 1462;
48 var epoch = Date.parse(v);
49 return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
50 }
51
52 function sheet_from_array_of_arrays(data, opts) {
53 var ws = {};
54 var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
55 for (var R = 0; R != data.length; ++R) {
56 for (var C = 0; C != data[R].length; ++C) {
57 if (range.s.r > R) range.s.r = R;
58 if (range.s.c > C) range.s.c = C;
59 if (range.e.r < R) range.e.r = R;
60 if (range.e.c < C) range.e.c = C;
61 var cell = {v: data[R][C]};
62 if (cell.v == null) continue;
63 var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
64
65 if (typeof cell.v === 'number') cell.t = 'n';
66 else if (typeof cell.v === 'boolean') cell.t = 'b';
67 else if (cell.v instanceof Date) {
68 cell.t = 'n';
69 cell.z = XLSX.SSF._table[14];
70 cell.v = datenum(cell.v);
71 }
72 else cell.t = 's';
73
74 ws[cell_ref] = cell;
75 }
76 }
77 if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
78 return ws;
79 }
80
81 function Workbook() {
82 if (!(this instanceof Workbook)) return new Workbook();
83 this.SheetNames = [];
84 this.Sheets = {};
85 }
86
87 function s2ab(s) {
88 var buf = new ArrayBuffer(s.length);
89 var view = new Uint8Array(buf);
90 for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
91 return buf;
92 }
93
94 export function export_table_to_excel(id) {
95 var theTable = document.getElementById(id);
96 console.log('a')
97 var oo = generateArray(theTable);
98 var ranges = oo[1];
99
100 /* original data */
101 var data = oo[0];
102 var ws_name = "SheetJS";
103 console.log(data);
104
105 var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
106
107 /* add ranges to worksheet */
108 // ws['!cols'] = ['apple', 'banan'];
109 ws['!merges'] = ranges;
110
111 /* add worksheet to workbook */
112 wb.SheetNames.push(ws_name);
113 wb.Sheets[ws_name] = ws;
114
115 var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
116
117 saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
118 }
119
120 function formatJson(jsonData) {
121 console.log(jsonData)
122 }
123 export function export_json_to_excel(th, jsonData, defaultTitle) {
124
125 /* original data */
126
127 var data = jsonData;
128 data.unshift(th);
129 var ws_name = "SheetJS";
130
131 var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
132
133
134 /* add worksheet to workbook */
135 wb.SheetNames.push(ws_name);
136 wb.Sheets[ws_name] = ws;
137
138 var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
139 var title = defaultTitle || '列表'
140 saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx")
141 }
注意:在Export2Excel.js引入Blob.js时,目录位置要对应