1、安装依赖 npm install --save xlsx file-saver
页面
<template> <div> <div class="table-tool"> <Row> <Col :span="24"> <div class="right-tool"> <Tooltip content="导出EXCEL" placement="top" transfer> <div class="custom-btn" @click="confirmExport"> <div class="rippleAnimate"> <common-icon type="_icon_table_export" :size="18" /> </div> </div> </Tooltip> </div> </Col> </Row> </div> <Table ref="table" :columns="columns" :data="tableData2" :height="200"></Table> </div> </template> <script> import { exportToExcel } from "@/libs/tools"; import lodash from "lodash"; export default { name: "tqOutagePeriod", data() { return { columns: [ { title: "供电单位", key: "countyNoName", width: 100, align: "center", }, { title: "供电所", key: "stationNoName", width: 100, align: "center", }, { title: "线路编号", key: "xlbh", width: 100, align: "center", }, { title: "线路名称", key: "xlbhname", width: 100, align: "center", }, { title: "台区名称", key: "tgName", width: 100, align: "center", }, { title: "台区编号", key: "tgNo", width: 100, align: "center", }, { title: "是否煤改电台区", key: "mgd", width: 140, align: "center", }, { title: "2023年2月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[0])]); }, key: "a", width: 100, align: "center", children: [ { title: "上", key: "a1", width: 100, align: "center", }, { title: "下", key: "a2", width: 100, align: "center", }, ], }, { title: "2023年3月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[1])]); }, key: "b", width: 100, align: "center", children: [ { title: "上", key: "b1", width: 100, align: "center", }, { title: "下", key: "b2", width: 100, align: "center", }, ], }, { title: "2023年4月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[2])]); }, key: "c", width: 100, align: "center", children: [ { title: "上", key: "c1", width: 100, align: "center", }, { title: "下", key: "c2", width: 100, align: "center", }, ], }, { title: "2023年5月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[3])]); }, key: "d", width: 100, align: "center", children: [ { title: "上", key: "d1", width: 100, align: "center", }, { title: "下", key: "d2", width: 100, align: "center", }, ], }, { title: "2023年6月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[4])]); }, key: "e", width: 100, align: "center", children: [ { title: "上", key: "e1", width: 100, align: "center", }, { title: "下", key: "e2", width: 100, align: "center", }, ], }, { title: "2023年7月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[5])]); }, key: "f", width: 100, align: "center", children: [ { title: "上", key: "f1", width: 100, align: "center", }, { title: "下", key: "f2", width: 100, align: "center", }, ], }, { title: "2023年8月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[6])]); }, key: "g", width: 100, align: "center", children: [ { title: "上", key: "g1", width: 100, align: "center", }, { title: "下", key: "g2", width: 100, align: "center", }, ], }, { title: "2023年9月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[7])]); }, key: "h", width: 100, align: "center", children: [ { title: "上", key: "h1", width: 100, align: "center", }, { title: "下", key: "h2", width: 100, align: "center", }, ], }, { title: "2023年10月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[8])]); }, key: "i", width: 100, align: "center", children: [ { title: "上", key: "i1", width: 100, align: "center", }, { title: "下", key: "i2", width: 100, align: "center", }, ], }, { title: "2023年11月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[9])]); }, key: "j", width: 100, align: "center", children: [ { title: "上", key: "j1", width: 100, align: "center", }, { title: "下", key: "j2", width: 100, align: "center", }, ], }, { title: "2023年12月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[10])]); }, key: "k", width: 100, align: "center", children: [ { title: "上", key: "k1", width: 100, align: "center", }, { title: "下", key: "k2", width: 100, align: "center", }, ], }, { title: "2024年1月停电次数", renderHeader: (h, params) => { return h("div", [h("span", {}, ""), h("span", this.columnsTitle[11])]); }, key: "l", width: 100, align: "center", children: [ { title: "上", key: "l1", width: 100, align: "center", }, { title: "下", key: "l2", width: 100, align: "center", }, ], }, { title: "近一个月停电次数", key: "ygy", width: 140, align: "center", }, { title: "近两个月停电次数", key: "lgy", width: 140, align: "center", }, { title: "一个季度停电次数", key: "yhj", width: 140, align: "center", }, { title: "半年停电次数", key: "bn", width: 120, align: "center", }, { title: "全年停电次数", key: "qn", width: 120, align: "center", }, ], tableData2: [ { countyNoName: "承德市", stationNoName: "供电所", xlbh: "111", xlbhname: "22222", tgName: "一二三", tgNo: "123", mgd: "否", a1: "上a1", a2: "下a1", b1: "上b1", b2: "下b1", c1: "上c1", c2: "下c1", d1: "上d1", d2: "下d1", e1: "上e1", e2: "下e1", f1: "上f1", f2: "下f1", g1: "上g1", g2: "下g1", h1: "上h1", h2: "下h1", i1: "上i1", i2: "下i1", j1: "上j1", j2: "下j1", k1: "上k1", k2: "下k1", l1: "上l1", l2: "下l1", ygy: "1", lgy: "2", yhj: "3", bn: "4", qn: "5", }, ], columnsTitle: [ "2023年2月停电次数", "2023年3月停电次数", "2023年4月停电次数", "2023年5月停电次数", "2023年6月停电次数", "2023年7月停电次数", "2023年8月停电次数", "2023年9月停电次数", "2023年10月停电次数", "2023年11月停电次数", "2023年12月停电次数", "2024年1月停电次数", ], }; }, watch: {}, computed: {}, mounted() {}, methods: { confirmExport: lodash.debounce(function () { const columnsTitle2 = [ "2024年2月停电次数", "2024年3月停电次数", "2024年4月停电次数", "2024年5月停电次数", "2024年6月停电次数", "2024年7月停电次数", "2024年8月停电次数", "2024年9月停电次数", "2024年10月停电次数", "2024年11月停电次数", "2024年12月停电次数", "2025年1月停电次数", ]; const columns = this.columns.map((item, index) => { item.title = lodash.isEmpty(item.children) ? item.title : columnsTitle2[index - 7]; return item; }); exportToExcel(this.tableData2, columns, "长周期停电明细", "2"); }, 3000), }, components: {}, }; </script> <style scoped lang="less"> .right-tool { float: right; height: 30px; overflow: hidden; .custom-btn { display: inline-block; width: 30px; height: 30px; border-radius: 50%; overflow: hidden; text-align: center; cursor: pointer; padding: 6px 0; position: relative; i { color: #888; line-height: 1; } } .custom-btn:hover { i { color: #333; } } } </style>
tools.js 导出方法
import { export_json_to_excel } from "./ExportExcel.js"; // 行转列 function formatJson(filterVal, jsonData) { return jsonData.map(v => filterVal.map(j => { return v[j] }) ) } let CURRENT_CELL_INDEX = 0 // https://segmentfault.com/q/1010000023268006?utm_source=tag-newest //转换数字到EXCEL单元格编号 function numberToCellCode(number) { let s = '' while (number > 0) { let m = number % 26 if (m === 0) m = 26 s = String.fromCharCode(m + 64) + s number = (number - m) / 26 } return s } // 获取跨列 function getColspan(column) { var colspan = 0 var children = column.children || [] for (var i = 0; i < children.length; i++) { var item = children[i] if (item.children && item.children.length > 0) { colspan += getColspan(item) } else { colspan += 1 } } if (colspan == 0) { colspan = 1 } return colspan } // 获取跨列 function getRowspan(column, maxLevel) { let rowspan = 1 if (!column.children || column.children.length == 0) { rowspan = maxLevel - column.level + 1 } return rowspan } // 获取最大层级 function setCellCode(columns, parentNode) { const levels = [] columns.forEach((li, index) => { if (!CURRENT_CELL_INDEX) { CURRENT_CELL_INDEX = 1 } else { CURRENT_CELL_INDEX++ } if (parentNode && index == 0) { CURRENT_CELL_INDEX = parentNode.CellIndex } li.CellIndex = CURRENT_CELL_INDEX li.CellCode = numberToCellCode(CURRENT_CELL_INDEX) const children = li.children if (children && children.length > 0) { setCellCode(children, li) } }) return levels } // 获取最大层级 function getLevels(columns, parentNode) { const levels = [] columns.forEach((li, index) => { li.level = parentNode ? parentNode.level + 1 : 0 levels.push(li.level) const children = li.children if (children && children.length > 0) { const result = getLevels(children, li) levels.push(...result) } }) return levels } // 设置合并 function setMerges(columns = [], maxLevel, multiHeader = [], merges = []) { columns.forEach((li, index) => { const level = li.level + 1 const cellIndex = li.CellIndex - 1 const CellCode = li.CellCode const cellTitle = li.title const colSpan = getColspan(li) const rowSpan = getRowspan(li, maxLevel) li.colSpan = colSpan li.rowSpan = rowSpan if (!multiHeader[level - 1]) { multiHeader[level - 1] = [] } if (rowSpan > 1) { merges.push(`${CellCode}${level}:${CellCode}${level + rowSpan - 1}`) multiHeader[level - 1][cellIndex] = cellTitle for (let i = 1; i < rowSpan; i++) { if (!multiHeader[level - 1 + i]) { multiHeader[level - 1 + i] = [] } multiHeader[level - 1 + i][cellIndex] = '' } } else { multiHeader[level - 1][cellIndex] = cellTitle } if (colSpan > 1) { let endCellIndex = cellIndex let emptyCell = [] for (let i = 1; i < colSpan; i++) { endCellIndex++ emptyCell.push('') } const endCellCode = numberToCellCode(endCellIndex + 1) multiHeader[level - 1].splice(cellIndex + 1, 0, ...emptyCell) merges.push(`${CellCode}${level}:${endCellCode}${level}`) } const children = li.children if (children && children.length > 0) { setMerges(children, maxLevel, multiHeader, merges) } }) } // 前端导出动态二级表头Excel文件 export function exportToExcel(tableData, columns, filename, tableheader, customMerges) { let multiHeader = [] const header = [] const merges = [] CURRENT_CELL_INDEX = 0 setCellCode(columns, null, null) // console.log(columns, 'setCellCode') // 最大层级集合 const allLevels = getLevels(columns, null) // 获取最大层级 const maxLevel = Math.max(...allLevels) setMerges(columns, maxLevel, multiHeader, merges) // console.log(merges, 'setMerges') header.push(...multiHeader[maxLevel]) if (maxLevel > 0) { multiHeader.splice(maxLevel, 1) } else { multiHeader = [] } // 行转列 const filterVal = [] columns.forEach(row => { if (row.children && row.children.length > 0) { row.children.forEach(childrow => { filterVal.push(childrow.key) }) } else { filterVal.push(row.key) } }) const data = formatJson(filterVal, tableData) export_json_to_excel({ multiHeader, // 第二行表头 header, // 第一行表头 merges, data, filename, tableheader, }) }
ExportExcel.js文件
require('script-loader!file-saver'); require('script-loader!./Blob'); import XLSX from 'xlsx' function generateArray(table) { var out = []; var rows = table.querySelectorAll('tr'); var ranges = []; for (var R = 0; R < rows.length; ++R) { var outRow = []; var row = rows[R]; var columns = row.querySelectorAll('td'); for (var C = 0; C < columns.length; ++C) { var cell = columns[C]; var colspan = cell.getAttribute('colspan'); var rowspan = cell.getAttribute('rowspan'); var cellValue = cell.innerText; if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue; //Skip ranges ranges.forEach(function (range) { if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) { for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null); } }); //Handle Row Span if (rowspan || colspan) { rowspan = rowspan || 1; colspan = colspan || 1; ranges.push({ s: { r: R, c: outRow.length }, e: { r: R + rowspan - 1, c: outRow.length + colspan - 1 } }); } ; //Handle Value outRow.push(cellValue !== "" ? cellValue : null); //Handle Colspan if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null); } out.push(outRow); } return [out, ranges]; }; function datenum(v, date1904) { if (date1904) v += 1462; var epoch = Date.parse(v); return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); } function sheet_from_array_of_arrays(data, opts) { var ws = {}; var range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 } }; for (var R = 0; R != data.length; ++R) { for (var C = 0; C != data[R].length; ++C) { if (range.s.r > R) range.s.r = R; if (range.s.c > C) range.s.c = C; if (range.e.r < R) range.e.r = R; if (range.e.c < C) range.e.c = C; var cell = { v: data[R][C] }; if (cell.v == null) continue; var cell_ref = XLSX.utils.encode_cell({ c: C, r: R }); if (typeof cell.v === 'number') cell.t = 'n'; else if (typeof cell.v === 'boolean') cell.t = 'b'; else if (cell.v instanceof Date) { cell.t = 'n'; cell.z = XLSX.SSF._table[14]; cell.v = datenum(cell.v); } else cell.t = 's'; ws[cell_ref] = cell; } } if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range); return ws; } function Workbook() { if (!(this instanceof Workbook)) return new Workbook(); this.SheetNames = []; this.Sheets = {}; } function s2ab(s) { var buf = new ArrayBuffer(s.length); var view = new Uint8Array(buf); for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; return buf; } export function export_table_to_excel(id) { var theTable = document.getElementById(id); var oo = generateArray(theTable); var ranges = oo[1]; /* original data */ var data = oo[0]; var ws_name = "SheetJS"; var wb = new Workbook(), ws = sheet_from_array_of_arrays(data); /* add ranges to worksheet */ // ws['!cols'] = ['apple', 'banan']; ws['!merges'] = ranges; /* add worksheet to workbook */ wb.SheetNames.push(ws_name); wb.Sheets[ws_name] = ws; var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' }); saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), "test.xlsx") } export function export_json_to_excel({ multiHeader2 = [], multiHeader = [], header, data, filename, //文件名 merges = [], // 合并 autoWidth = true, bookType = "xlsx", myRowFont = ["1"], tableheader = tableheader } = {}) { /* original data */ filename = filename || "列表"; data = [...data]; if (header != []) { data.unshift(header); } for (let i = multiHeader2.length - 1; i > -1; i--) { data.unshift(multiHeader2[i]); } for (let i = multiHeader.length - 1; i > -1; i--) { data.unshift(multiHeader[i]); } // var ws_name = "SheetJS"; var wb = new Workbook(), ws = sheet_from_array_of_arrays(data); if (merges.length > 0) { if (!ws["!merges"]) ws["!merges"] = []; merges.forEach(item => { ws["!merges"].push(XLSX.utils.decode_range(item)); }); } if (autoWidth) { /*设置worksheet每列的最大宽度*/ const colWidth = data.map(row => row.map(val => { /*先判断是否为null/undefined*/ if (val == null) { return { wch: 20 }; } else if (val.toString().charCodeAt(0) > 255) { /*再判断是否为中文*/ return { wch: val.toString().length * 2 }; } else { return { wch: val.toString().length }; } }) ); /*以第一行为初始值*/ let result = colWidth[0]; for (let i = 1; i < colWidth.length; i++) { for (let j = 0; j < colWidth[i].length; j++) { if (result[j] != undefined) { if (result[j]["wch"] < colWidth[i][j]["wch"]) { result[j]["wch"] = colWidth[i][j]["wch"]; } } } } ws["!cols"] = result; } /* add worksheet to workbook */ wb.SheetNames.push(ws_name); wb.Sheets[ws_name] = ws; var dataInfo = wb.Sheets[wb.SheetNames[0]]; //设置主标题样式 // dataInfo["A1"].s = { // font: { // name: '宋体', // sz: 18, // color: {rgb: "ff0000"}, // border:true // }, // alignment: { // horizontal: "center", // vertical: "center", // }, // fill: { // fgColor: {rgb: "FFFFFF"}, // }, // }; // 这是表头行的样式 var tableTitleFont = { font: { name: "宋体", // color: {rgb: "ff0000"}, bold: true }, alignment: { horizontal: "center", vertical: "center" } }; if (tableheader == undefined) { for (var b in dataInfo) { if (b.indexOf(myRowFont) > -1) { if (b.length == 2) { dataInfo[b].s = tableTitleFont; } } } } if (tableheader == "2") { for (var b in dataInfo) { if (b.indexOf("1") > -1 || b.indexOf("2") > -1) { if (b.length == 2) { dataInfo[b].s = tableTitleFont; } } } } var wbout = XLSX.write(wb, { bookType: bookType, bookSST: false, type: "binary" }); saveAs( new Blob([s2ab(wbout)], { type: "application/octet-stream" }), `${filename}.${bookType}` ); }
Blob.js
/* eslint-disable */ /* Blob.js * A Blob implementation. * 2014-05-27 * * By Eli Grey, http://eligrey.com * By Devin Samarin, https://github.com/eboyjr * License: X11/MIT * See LICENSE.md */ /*global self, unescape */ /*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, plusplus: true */ /*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ (function (view) { "use strict"; view.URL = view.URL || view.webkitURL; if (view.Blob && view.URL) { try { new Blob; return; } catch (e) {} } // Internally we use a BlobBuilder implementation to base Blob off of // in order to support older browsers that only have BlobBuilder var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) { var get_class = function(object) { return Object.prototype.toString.call(object).match(/^[objects(.*)]$/)[1]; } , FakeBlobBuilder = function BlobBuilder() { this.data = []; } , FakeBlob = function Blob(data, type, encoding) { this.data = data; this.size = data.length; this.type = type; this.encoding = encoding; } , FBB_proto = FakeBlobBuilder.prototype , FB_proto = FakeBlob.prototype , FileReaderSync = view.FileReaderSync , FileException = function(type) { this.code = this[this.name = type]; } , file_ex_codes = ( "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR" ).split(" ") , file_ex_code = file_ex_codes.length , real_URL = view.URL || view.webkitURL || view , real_create_object_URL = real_URL.createObjectURL , real_revoke_object_URL = real_URL.revokeObjectURL , URL = real_URL , btoa = view.btoa , atob = view.atob , ArrayBuffer = view.ArrayBuffer , Uint8Array = view.Uint8Array ; FakeBlob.fake = FB_proto.fake = true; while (file_ex_code--) { FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; } if (!real_URL.createObjectURL) { URL = view.URL = {}; } URL.createObjectURL = function(blob) { var type = blob.type , data_URI_header ; if (type === null) { type = "application/octet-stream"; } if (blob instanceof FakeBlob) { data_URI_header = "data:" + type; if (blob.encoding === "base64") { return data_URI_header + ";base64," + blob.data; } else if (blob.encoding === "URI") { return data_URI_header + "," + decodeURIComponent(blob.data); } if (btoa) { return data_URI_header + ";base64," + btoa(blob.data); } else { return data_URI_header + "," + encodeURIComponent(blob.data); } } else if (real_create_object_URL) { return real_create_object_URL.call(real_URL, blob); } }; URL.revokeObjectURL = function(object_URL) { if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { real_revoke_object_URL.call(real_URL, object_URL); } }; FBB_proto.append = function(data/*, endings*/) { var bb = this.data; // decode data to a binary string if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { var str = "" , buf = new Uint8Array(data) , i = 0 , buf_len = buf.length ; for (; i < buf_len; i++) { str += String.fromCharCode(buf[i]); } bb.push(str); } else if (get_class(data) === "Blob" || get_class(data) === "File") { if (FileReaderSync) { var fr = new FileReaderSync; bb.push(fr.readAsBinaryString(data)); } else { // async FileReader won't work as BlobBuilder is sync throw new FileException("NOT_READABLE_ERR"); } } else if (data instanceof FakeBlob) { if (data.encoding === "base64" && atob) { bb.push(atob(data.data)); } else if (data.encoding === "URI") { bb.push(decodeURIComponent(data.data)); } else if (data.encoding === "raw") { bb.push(data.data); } } else { if (typeof data !== "string") { data += ""; // convert unsupported types to strings } // decode UTF-16 to binary string bb.push(unescape(encodeURIComponent(data))); } }; FBB_proto.getBlob = function(type) { if (!arguments.length) { type = null; } return new FakeBlob(this.data.join(""), type, "raw"); }; FBB_proto.toString = function() { return "[object BlobBuilder]"; }; FB_proto.slice = function(start, end, type) { var args = arguments.length; if (args < 3) { type = null; } return new FakeBlob( this.data.slice(start, args > 1 ? end : this.data.length) , type , this.encoding ); }; FB_proto.toString = function() { return "[object Blob]"; }; FB_proto.close = function() { this.size = this.data.length = 0; }; return FakeBlobBuilder; }(view)); view.Blob = function Blob(blobParts, options) { var type = options ? (options.type || "") : ""; var builder = new BlobBuilder(); if (blobParts) { for (var i = 0, len = blobParts.length; i < len; i++) { builder.append(blobParts[i]); } } return builder.getBlob(type); }; }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
参考项目
参考文档