直接上代码:
需要需要添加的依赖有:
npm install --save xlsx
npm install -S file-saver
npm install -D script-loader
npm install --save xlsx-style(修改表格样式要下载)
Export2Excel.js 这是网上找的插件,做了些许修改
1 /* eslint-disable */ 2 require('script-loader!file-saver'); 3 require('./Blob.js'); //blob.js也是网上找的,下面会贴上代码 4 require('script-loader!xlsx/dist/xlsx.core.min');
//注意 直接import xlsx-style会报错,因为npm install xlsx-style 下载下来的依赖 源码有错,需要修改,下面会讲到 5 import XLSX from "xlsx-style" 6 7 function generateArray(table) { 8 var out = []; 9 var rows = table.querySelectorAll('tr'); 10 var ranges = []; 11 for (var R = 0; R < rows.length; ++R) { 12 var outRow = []; 13 var row = rows[R]; 14 var columns = row.querySelectorAll('td'); 15 for (var C = 0; C < columns.length; ++C) { 16 var cell = columns[C]; 17 var colspan = cell.getAttribute('colspan'); 18 var rowspan = cell.getAttribute('rowspan'); 19 var cellValue = cell.innerText; 20 if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue; 21 22 //Skip ranges 23 ranges.forEach(function(range) { 24 if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) { 25 for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null); 26 } 27 }); 28 29 //Handle Row Span 30 if (rowspan || colspan) { 31 rowspan = rowspan || 1; 32 colspan = colspan || 1; 33 ranges.push({ 34 s: { 35 r: R, 36 c: outRow.length 37 }, 38 e: { 39 r: R + rowspan - 1, 40 c: outRow.length + colspan - 1 41 } 42 }); 43 }; 44 45 //Handle Value 46 outRow.push(cellValue !== "" ? cellValue : null); 47 48 //Handle Colspan 49 if (colspan) 50 for (var k = 0; k < colspan - 1; ++k) outRow.push(null); 51 } 52 out.push(outRow); 53 } 54 return [out, ranges]; 55 }; 56 57 function datenum(v, date1904) { 58 if (date1904) v += 1462; 59 var epoch = Date.parse(v); 60 return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); 61 } 62 63 function sheet_from_array_of_arrays(data, opts) { 64 var ws = {}; 65 var range = { 66 s: { 67 c: 10000000, 68 r: 10000000 69 }, 70 e: { 71 c: 0, 72 r: 0 73 } 74 }; 75 for (var R = 0; R != data.length; ++R) { 76 for (var C = 0; C != data[R].length; ++C) { 77 if (range.s.r > R) range.s.r = R; 78 if (range.s.c > C) range.s.c = C; 79 if (range.e.r < R) range.e.r = R; 80 if (range.e.c < C) range.e.c = C; 81 var cell = { 82 v: data[R][C] 83 }; 84 if (cell.v == null) continue; 85 var cell_ref = XLSX.utils.encode_cell({ 86 c: C, 87 r: R 88 }); 89 90 if (typeof cell.v === 'number') cell.t = 'n'; 91 else if (typeof cell.v === 'boolean') cell.t = 'b'; 92 else if (cell.v instanceof Date) { 93 cell.t = 'n'; 94 cell.z = XLSX.SSF._table[14]; 95 cell.v = datenum(cell.v); 96 } else cell.t = 's'; 97 98 ws[cell_ref] = cell; 99 } 100 } 101 if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range); 102 return ws; 103 } 104 105 function Workbook() { 106 if (!(this instanceof Workbook)) return new Workbook(); 107 this.SheetNames = []; 108 this.Sheets = {}; 109 } 110 111 function s2ab(s) { 112 var buf = new ArrayBuffer(s.length); 113 var view = new Uint8Array(buf); 114 for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; 115 return buf; 116 } 117 118 export function export_table_to_excel(id) { 119 var theTable = document.getElementById(id); 120 console.log('a') 121 var oo = generateArray(theTable); 122 var ranges = oo[1]; 123 124 /* original data */ 125 var data = oo[0]; 126 var ws_name = "SheetJS"; 127 console.log(data); 128 129 var wb = new Workbook(), 130 ws = sheet_from_array_of_arrays(data); 131 132 /* add ranges to worksheet */ 133 // ws['!cols'] = ['apple', 'banan'];
//合并单元格 134 ws['!merges'] = ranges; 135 136 /* add worksheet to workbook */ 137 wb.SheetNames.push(ws_name); 138 wb.Sheets[ws_name] = ws; 139 140 var wbout = XLSX.write(wb, { 141 bookType: 'xlsx', 142 bookSST: false, 143 type: 'binary' 144 }); 145 146 saveAs(new Blob([s2ab(wbout)], { 147 type: "application/octet-stream" 148 }), "test.xlsx") 149 } 150 151 function formatJson(jsonData) { 152 console.log(jsonData) 153 } 154 export function export_json_to_excel(multiHeader, th, merges, data, defaultTitle) { 155 data = [...data]; 156 data.unshift(th); 157 158 159 if (multiHeader) { 160 data.unshift(multiHeader); 161 } 162 var ws_name = "SheetJS"; 163 164 var wb = new Workbook(), 165 ws = sheet_from_array_of_arrays(data); 166 167 /* add worksheet to workbook */ 168 wb.SheetNames.push(ws_name);
//合并单元格 169 ws["!merges"] = merges; 170 wb.Sheets[ws_name] = ws; 171 172 var dataInfo = wb.Sheets[wb.SheetNames[0]]; 173 var cellArr = merges.map(c => c.s); 174 var secArr = merges.map(c => c.e); 175 var cellArr1 = []; 176 cellArr.forEach(cellObj => { 177 var cell_ref = XLSX.utils.encode_cell({ 178 c: cellObj.c, 179 r: cellObj.r 180 }); 181 cellArr1.push(cell_ref); 182 }); 183
//设置单元格样式 184 const borderAll = { 185 border: { 186 //单元格外侧框线 187 top: { 188 style: "thin" 189 }, 190 bottom: { 191 style: "thin" 192 }, 193 left: { 194 style: "thin" 195 }, 196 right: { 197 style: "thin" 198 } 199 } 200 }; 201 //给所有单元格加上边框 202 for (var i in dataInfo) { 203 if (i == '!ref' || i == '!merges' || i == '!cols' || $.inArray(i, cellArr1) >= 0) { 204 205 } else { 206 dataInfo[i + ''].s = borderAll; 207 } 208 } 209 //设置单元格背景色、字体以及字体大小等 210 var bgColArr = ["FDE9D9", 'FFFF00', 'DAEEF3', 'CCC0DA', 'C5D9F1']; 211 //设置主标题样式 212 var headerStyle = { 213 font: { 214 name: '宋体', 215 color: { 216 rgb: "303133" 217 }, 218 bold: true, 219 italic: false, 220 underline: false 221 }, 222 alignment: { 223 horizontal: "center", 224 vertical: "center" 225 }, 226 fill: { 227 228 } 229 }; 230 let mm = 0; 231 cellArr1.forEach(cellObj => { 232 var hStyle = Object.assign({}, headerStyle); 233 hStyle.fill = { 234 fgColor: { 235 rgb: bgColArr[mm] 236 } 237 }; 238 dataInfo[cellObj].s = hStyle; 239 mm++; 240 }); 241 242 var secCellStyle = Object.assign({}, borderAll, headerStyle); 243 secArr.forEach((s, index) => { 244 var hStyle = Object.assign({}, secCellStyle); 245 hStyle.fill = { 246 fgColor: { 247 rgb: bgColArr[index] 248 } 249 }; 250 var startIndex = 0; 251 if (index == 0) { 252 startIndex = 0; 253 } else { 254 startIndex = secArr[index - 1].c + 1; 255 } 256 for (var se = startIndex; se <= s.c; se++) { 257 var cell_ref = XLSX.utils.encode_cell({ 258 c: se, 259 r: 1 260 }); 261 dataInfo[cell_ref].s = hStyle; 262 } 263 }); 264 265 var wbout = XLSX.write(wb, { 266 bookType: 'xlsx', 267 bookSST: false, 268 type: 'binary' 269 }); 270 var title = defaultTitle || '列表' 271 saveAs(new Blob([s2ab(wbout)], { 272 type: "application/octet-stream" 273 }), title + ".xlsx") 274 }
import XLSX from "xlsx-style" 可能会报cpexcel.js的错误,需要修改源码:
将node_modules/xlsx-style/dist/cpexcel.js 中的var cpt = require('./cpt' + 'able'); 修改为var cpt = cptable;
如果还报fs相关的错误,需要单独配置webpack.base.conf.js,以下是5-7行代码
1 module.exports = { 2 entry: { 3 app: ["babel-polyfill", './src/main.js'] 4 }, 5 node: { 6 fs: "empty" 7 } 8 }
以下是Blod.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(/^\[object\s(.*)\]$/)[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));
以下是我的代码层级结构:
下面是具体页面的具体调用,直接上代码:
1 let val = ['MachineNo', 'MachineIndex', 'ProdMonth', 'MachineType', 'StatusStr']; 2 let proArr = ['StartTime', 'EndTime', 'ValidTime']; 3 let outArr = ['CaseNo', 'ToCountry', 'ToPlace', 'OutTime', 'InvertoryNum', 'OrderTime']; 4 let tsArr = ['InstallPlace', 'InstallTime', 'CompCount', 'FinCompTime']; 5 val = val.concat(opColKey).concat(acColKey).concat(proArr).concat(partNameArr).concat(outArr).concat(tsArr); 6 let startCell = 5 + opColName.length + acColName.length;
//以下是合并单元格 7 let merges = [{ 8 //合并基本情报 9 s: { //s为开始 10 c: 0, //开始列 11 r: 0 //开始取值范围 12 }, 13 e: { //e结束 14 c: (startCell - 1), //结束列 15 r: 0 //结束范围 16 } 17 }, 18 { 19 //合并生产情报 20 s: { //s为开始 21 c: startCell, //开始列 22 r: 0 //开始取值范围 23 }, 24 e: { //e结束 25 c: (startCell + 3 - 1), //结束列 26 r: 0 //结束范围 27 } 28 }, 29 { 30 //合并部件情报 31 s: { //s为开始 32 c: (startCell + 3), //开始列 33 r: 0 //开始取值范围 34 }, 35 e: { //e结束 36 c: (startCell + 3 + this.partNameHeader.length - 1), //结束列 37 r: 0 //结束范围 38 } 39 }, 40 { 41 //合并出库情报 42 s: { //s为开始 43 c: (startCell + 3 + this.partNameHeader.length), //开始列 44 r: 0 //开始取值范围 45 }, 46 e: { //e结束 47 c: (startCell + 3 + this.partNameHeader.length + 6 - 1), //结束列 48 r: 0 //结束范围 49 } 50 }, 51 { 52 //合并技服情报 53 s: { //s为开始 54 c: (startCell + 3 + this.partNameHeader.length + 6), //开始列 55 r: 0 //开始取值范围 56 }, 57 e: { //e结束 58 c: (startCell + 3 + this.partNameHeader.length + 6 + 4 - 1), //结束列 59 r: 0 //结束范围 60 } 61 }, 62 ]; 63 this.exportExcel(multiHeader, tHeader, merges, val, data);
1 exportExcel(multiHeader, tHeader, merges, val, tableData) { 2 require.ensure([], () => { 3 const { 4 export_json_to_excel 5 } = require('../excel/Export2Excel.js'); 6 let filterVal = val; 7 //把后台传过来的数据存到list 8 const list = tableData; 9 const data = this.formatJson(filterVal, list); 10 export_json_to_excel(multiHeader, tHeader, merges, data, '统合情报管理报表'); 11 }) 12 }, 13 formatJson(filterVal, jsonData) { 14 return jsonData.map(v => filterVal.map(j => v[j])); 15 }