news 2026/4/18 10:01:29

商城后台管理系统 03 Vue项目-实现表格导出EXCEL表格

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
商城后台管理系统 03 Vue项目-实现表格导出EXCEL表格
1, 安装-- 使用 npm 安装:
npm install file-saver xlsx -S npm install script-loader -D npm install -S file-saver npm install --save xlsx@0.10.0 npm install -D script-loader npm install xlsx-style --save npm install file-saver -S npm install xlsx -S npm install -D script-loader npm install --save script-loader!src/vendor/Blob

Blob.js的GitHub地址是:

https://github.com/eligrey/Blob.js

一个常用的Export2Excel.js源码如下(通常放在项目的src/utils目录下):

https://github.com/PanJiaChen/vue-element-admin/blob/master/src/vendor/Export2Excel.js

src/common/js/util.js
/** * export2Excel(columns, list) 导出 excel * columns Array = [{},{},...] 表头 = [{title: '', key: ''}] 固定的 title是名称 key是字段 * list = [] table的数据 [{},{}] 基本上都是数组对象 去遍历数组对象就可以了 * name 表名 */ export function export2Excel(columns,list) { require.ensure([], () => { const { export_json_to_excel } = require('../../excel/Export2Excel'); let tHeader = [] let filterVal = [] console.log(columns) if (!columns) { return; } columns.forEach(item => { tHeader.push(item.title) filterVal.push(item.key) }) const data = list.map(v => filterVal.map(j => v[j])) export_json_to_excel(tHeader,data,'数据列表'); }) }

其中Export2ExcelPro.js 是可以导出二级标题, Export2Excel.js是导入一级标题

excelExport.js是封装了调用方法。

Export2Excel.js 是导入一级标题
// require('script-loader!file-saver');//保存文件用 // // require('script-loader!@/excel/Blob');//转二进制用 // require('./Blob');//转二进制用 // require('script-loader!xlsx/dist/xlsx.core.min');//xlsx核心 import { saveAs } from "file-saver"; // import XLSX from 'xlsx' import * as 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") } function formatJson(jsonData) { } export function export_json_to_excel(th, jsonData, defaultTitle) { /* original data */ var data = jsonData; data.unshift(th); var ws_name = "SheetJS"; var wb = new Workbook(), ws = sheet_from_array_of_arrays(data); /* 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' }); var title = defaultTitle || '列表' saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), title + ".xlsx") }
excelExport.js 是封装了调用方法。
import { export_json_to_excel } from '../vendor/Export2Excel' import { export_json_to_excelPro } from '../vendor/Export2ExcelPro' import { export_json_to_excelProPlus } from '../vendor/Export2ExcelPro' function exportExcel({ header, filterVal, filename, tableData }) { var data = formatJson(filterVal, tableData) export_json_to_excel(header, data, filename) } function exportExcelPro({ multiHeader, multiHeader2, data, merges, filename, filterVal }) { var data = formatJson(filterVal, data) export_json_to_excelPro({ multiHeader, multiHeader2, data, merges, filename, filterVal }) } function exportExcelProPlus({ header, multiHeader, multiHeader2, data, merges, filename, filterVal }) { var data = formatJson(filterVal, data) export_json_to_excelProPlus({ header, multiHeader, multiHeader2, data, merges, filename, filterVal }) } function formatJson(filterVal, tableData) { return tableData.map(v => { return filterVal.map(j => { return v[j] }) }) } let export_method = { exportExcel: exportExcel, exportExcelPro: exportExcelPro, exportExcelProPlus: exportExcelProPlus } export default export_method
其中Export2ExcelPro.js 是可以导出二级标题
/* eslint-disable */ import { saveAs } from 'file-saver' import XLSX from 'xlsx-style' 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, fontStyle) { var ws = {} var range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 } } let _opts = [] if (opts) { _opts = opts } 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 (_opts) { if (R == _opts[0] && C == _opts[1]) { cell = { v: data[R][C], s: defaultCellStyle } } } //给某一单元格的字体自定义样式 if (fontStyle) { if (R == data.length - 3 && C == 1) { cell = { v: data[R][C], s: fontCellStyle } } } 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 } let fontCellStyle = { font: { name: '宋体', sz: 18, color: { rgb: 'ff0000' }, bold: true }, alignment: { //对齐方式 horizontal: 'center', //水平居中 vertical: 'center' //竖直居中 } } let defaultCellStyle = { alignment: { horizontal: 'center', vertical: 'center', indent: 0 }, border: { diagonalDown: true, //斜线方向 diagonal: { color: { rgb: '303133' }, style: 'thin' } //diagonalDown与diagonal必须同时使用 } } 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' ) } /*自定义表头样式*/ let arr = ['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1', 'J1', 'K1', 'L1', 'M1', 'N1', 'O1', 'P1', 'Q1', 'R1', 'S1', 'T1', 'U1', 'V1', 'W1', 'X1', 'Y1', 'Z1'] let arr1 = ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2', 'J2', 'K2', 'L2', 'M2', 'N2', 'O2', 'P2', 'Q2', 'R2', 'S2', 'T2', 'U2', 'V2', 'W2', 'X2', 'Y2', 'Z2'] let arr2 = ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3', 'J3', 'K3', 'L3', 'M3', 'N3', 'O3', 'P3', 'Q3', 'R3', 'S3', 'T3', 'U3', 'V3', 'W3', 'X3', 'Y3', 'Z3'] let style = { font: { color: { rgb: '000000' }, bold: true }, border: { color: { auto: 1 }, top: { style: 'thin' }, bottom: { style: 'thin' }, left: { style: 'thin' }, right: { style: 'thin' } }, alignment: { horizontal: 'center', vertical: 'center' }, fill: { fgColor: { rgb: 'DCDFE6' } } } /*二级表头*/ export function export_json_to_excelPro({ multiHeader = [], // 第一行表头 multiHeader2 = [], // 第二行表头 data, filename, //文件名 merges = [], // 合并 autoWidth = true, bookType = 'xlsx', diagonal = [], //斜线 fontStyle = false } = {}) { /* original data */ filename = filename || '列表' data = [...data] /*data.unshift(header);*/ var ws_name = 'SheetJS' data.unshift(multiHeader) data.unshift(multiHeader2) var wb = new Workbook() //加样式,如斜线 if (diagonal) { var ws = sheet_from_array_of_arrays(data, diagonal, fontStyle) } else { var 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: 10 } } 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]['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]] for (var i = 0; i < multiHeader.length; i++) { dataInfo[arr[i]].s = style } for (var j = 0; j < multiHeader2.length; j++) { dataInfo[arr1[j]].s = style } var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' }) saveAs(new Blob([s2ab(wbout)], { type: 'application/octet-stream' }), `${filename}.${bookType}`) } /*三级表头的*/ export function export_json_to_excelProPlus({ multiHeader = [], // 第一行表头 multiHeader2 = [], // 第二行表头 header, // 第三行表头 data, filename, //文件名 merges = [], // 合并 autoWidth = true, bookType = 'xlsx' } = {}) { /* original data */ filename = filename || '列表' data = [...data] var ws_name = 'SheetJS' data.unshift(multiHeader) data.unshift(multiHeader2) data.unshift(header) var wb = new Workbook() var 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: 10 } } 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]['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]] for (var i = 0; i < multiHeader.length; i++) { dataInfo[arr[i]].s = style } for (var j = 0; j < multiHeader2.length; j++) { dataInfo[arr1[j]].s = style } for (var k = 0; k < header.length; k++) { dataInfo[arr2[k]].s = style } var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' }) saveAs(new Blob([s2ab(wbout)], { type: 'application/octet-stream' }), `${filename}.${bookType}`) }
Blob.js
/* 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(/^\[object\s(.*)\]$/)[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));
Vue项目-实现表格导出EXCEL表格 实现代码如下
1, src/views/User/index.vue <template> <div class="user"> <div class="hetong"> <!-- 1, 查看合同 --> 签约合同内容:<el-button type="primary" size="small" @click="look">查看合同</el-button> </div> <VuePdf ref="myPdf" /> <!-- 2, 查看发票 --> <div class="money"> <el-row :gutter="20"> <el-col :span="8"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>卡片名称</span> <el-button @click="download" style="float: right; padding: 3px 0;" type=" text">下载发票</el-button> </div> <div class="text item"> <img ref="img" style="width: 400px;" :src="imgUrl" alt="" /> </div> </el-card> </el-col> <el-col :span="8"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>卡片名称</span> <el-button @click="downs" style="float: right; padding: 3px 0;" type=" text">下载不同源发票</el-button> </div> <div class="text item"> <img ref="myimg" style="width: 400px;" src="../../assets/images/88.png" alt="" /> </div> </el-card> </el-col> <el-col :span="8"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>卡片名称</span> <el-button @click="down()" style="float: right; padding: 3px 0;" type=" text">下载本地发票</el-button> </div> <div class="text item"> <img ref="img" style="width: 400px;" :src="imgUrl" alt="" /> </div> </el-card> </el-col> </el-row> </div> <!-- 3, 导出表格 --> <div class="table"> <div class="header"> <div class="title">用户信息</div> <el-button @click="exportData" size="small">导出表格</el-button> </div> <el-table border :data="tableData" style="width: 100%;"> <el-table-column prop="date" label="日期" width="180"> </el-table-column> <el-table-column prop="name" label="姓名" width="180"> </el-table-column> <el-table-column prop="address" label="地址"> </el-table-column> </el-table> </div> </div> </template> <script> import VuePdf from '@/views/User/VuePdf'; import img from '@/assets/images/88.png'; import { export2Excel } from '@/common/js/util.js'; export default { data() { return { imgUrl: img, // 定义表头 columns: [{ title: "日期", key: "date" }, { title: "姓名", key: "name" }, { title: "地址", key: "address" }], tableData: [{ date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路1518弄' }, { date: '2016-05-04', name: '王小虎', address: '上海市普陀区金沙江路1516弄' }, { date: '2016-05-01', name: '王小虎', address: '上海市普陀区金沙江路1514弄' }, { date: '2016-05-03', name: '王小虎', address: '上海市普陀区金沙江路1512弄' }] }; }, components: { VuePdf }, methods: { // 查看合同 look() { this.$refs.myPdf.dialogVisible = true }, download() { // 1, 新窗口打开网址 右键保存 look() open() let url = this.$refs.img; console.log(url.src); window.location.href = url.src; }, // 2, 必须同源才能下载 可以直接下载 down() { var alink = document.createElement("a"); alink.href = this.imgUrl; console.log(this.imgUrl); alink.download = "pic"; // 图片名 alink.click(); }, // 解决图片不同源下载问题 downloadImage(imgsrc, name) { // 下载图片地址和图片名 创建图片 var image = new Image(); // <img /> // 解决跨域 canvas 污染问题 image.setAttribute("crossOrigin", "anonymous"); // 读图片 image.onload = function() { var canvas = document.createElement("canvas"); canvas.width = image.width; canvas.height = image.height; var context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); // 得到图片的 base64 编码数据 图片格式 默认为 image/png var url = canvas.toDataURL("image/png"); // 生成一个 a 元素 var a = document.createElement("a"); // 创建一个单击事件 var event = new MouseEvent("click"); // 设置图片名称 a.download = name || "photo"; // 将生成的URL设置为a.href属性 a.href = url; // 触发a的单击事件 a.dispatchEvent(event); }; // 给图片赋值 image.src = imgsrc; }, downs() { this.downloadImage(this.$refs.myimg.src, "pic"); }, // 导出 exportData() { // export2Excel('表头','需要导出的数据') export2Excel(this.columns, this.tableData, '用户列表'); } }, }; </script> <style lang="less" scoped> .user { margin: 10px; } .hetong, .table { padding: 10px; border: 1px solid #eee; background: #fff; color: #666; } .money { margin: 10px 0; } .header { display: flex; padding: 10px; .title { flex: 1; color: #333; font-weight: bold; } } </style> 2, src/common/js/util.js /** * export2Excel(columns, list) 导出 excel * columns Array = [{},{},...] 表头 = [{title: '', key: ''}] 固定的 title是名称 key是字段 * list = [] table的数据 [{},{}] 基本上都是数组对象 去遍历数组对象就可以了 * name 表名 */ export function export2Excel(columns, list, name) { require.ensure([], () => { const { export_json_to_excel } = require('@/utils/excel/Export2Excel'); let tHeader = [] let filterVal = [] console.log(columns) if (!columns) { return; } columns.forEach(item => { tHeader.push(item.title) filterVal.push(item.key) }) const data = list.map(v => filterVal.map(j => v[j])) export_json_to_excel(tHeader, data, name); }) } 3, src/utils/excel/Blob.js /* Blob.js * A Blob, File, FileReader & URL implementation. * 2019-04-19 * 下载: https://github.com/eligrey/Blob.js/blob/master/Blob.js * 访问 GitHub 仓库:https://github.com/eligrey/Blob.js * 点击 Blob.js 文件 → 选择右侧的 Raw 按钮 * 右键页面选择"另存为",保存文件到本地(如 src/utils/excel/Blob.js) * By Eli Grey, http://eligrey.com * By Jimmy Wärting, https://github.com/jimmywarting * License: MIT * See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md */ ; (function() { var global = typeof window === 'object' ? window : typeof self === 'object' ? self : this var BlobBuilder = global.BlobBuilder || global.WebKitBlobBuilder || global.MSBlobBuilder || global.MozBlobBuilder global.URL = global.URL || global.webkitURL || function(href, a) { a = document.createElement('a') a.href = href return a } var origBlob = global.Blob var createObjectURL = URL.createObjectURL var revokeObjectURL = URL.revokeObjectURL var strTag = global.Symbol && global.Symbol.toStringTag var blobSupported = false var blobSupportsArrayBufferView = false var arrayBufferSupported = !!global.ArrayBuffer var blobBuilderSupported = BlobBuilder && BlobBuilder.prototype.append && BlobBuilder.prototype.getBlob try { // Check if Blob constructor is supported blobSupported = new Blob(['ä']).size === 2 // Check if Blob constructor supports ArrayBufferViews // Fails in Safari 6, so we need to map to ArrayBuffers there. blobSupportsArrayBufferView = new Blob([new Uint8Array([1, 2])]).size === 2 } catch (e) {} /** * Helper function that maps ArrayBufferViews to ArrayBuffers * Used by BlobBuilder constructor and old browsers that didn't * support it in the Blob constructor. */ function mapArrayBufferViews(ary) { return ary.map(function(chunk) { if (chunk.buffer instanceof ArrayBuffer) { var buf = chunk.buffer // if this is a subarray, make a copy so we only // include the subarray region from the underlying buffer if (chunk.byteLength !== buf.byteLength) { var copy = new Uint8Array(chunk.byteLength) copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength)) buf = copy.buffer } return buf } return chunk }) } function BlobBuilderConstructor(ary, options) { options = options || {} var bb = new BlobBuilder() mapArrayBufferViews(ary).forEach(function(part) { bb.append(part) }) return options.type ? bb.getBlob(options.type) : bb.getBlob() } function BlobConstructor(ary, options) { return new origBlob(mapArrayBufferViews(ary), options || {}) } if (global.Blob) { BlobBuilderConstructor.prototype = Blob.prototype BlobConstructor.prototype = Blob.prototype } /********************************************************/ /* String Encoder fallback */ /********************************************************/ function stringEncode(string) { var pos = 0 var len = string.length var Arr = global.Uint8Array || Array // Use byte array when possible var at = 0 // output position var tlen = Math.max(32, len + (len >> 1) + 7) // 1.5x size var target = new Arr((tlen >> 3) << 3) // ... but at 8 byte offset while (pos < len) { var value = string.charCodeAt(pos++) if (value >= 0xd800 && value <= 0xdbff) { // high surrogate if (pos < len) { var extra = string.charCodeAt(pos) if ((extra & 0xfc00) === 0xdc00) { ++pos value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000 } } if (value >= 0xd800 && value <= 0xdbff) { continue // drop lone surrogate } } // expand the buffer if we couldn't write 4 bytes if (at + 4 > target.length) { tlen += 8 // minimum extra tlen *= (1.0 + (pos / string.length) * 2) // take 2x the remaining tlen = (tlen >> 3) << 3 // 8 byte offset var update = new Uint8Array(tlen) update.set(target) target = update } if ((value & 0xffffff80) === 0) { // 1-byte target[at++] = value // ASCII continue } else if ((value & 0xfffff800) === 0) { // 2-byte target[at++] = ((value >> 6) & 0x1f) | 0xc0 } else if ((value & 0xffff0000) === 0) { // 3-byte target[at++] = ((value >> 12) & 0x0f) | 0xe0 target[at++] = ((value >> 6) & 0x3f) | 0x80 } else if ((value & 0xffe00000) === 0) { // 4-byte target[at++] = ((value >> 18) & 0x07) | 0xf0 target[at++] = ((value >> 12) & 0x3f) | 0x80 target[at++] = ((value >> 6) & 0x3f) | 0x80 } else { // FIXME: do we care continue } target[at++] = (value & 0x3f) | 0x80 } return target.slice(0, at) } /********************************************************/ /* String Decoder fallback */ /********************************************************/ function stringDecode(buf) { var end = buf.length var res = [] var i = 0 while (i < end) { var firstByte = buf[i] var codePoint = null var bytesPerSequence = (firstByte > 0xEF) ? 4 : (firstByte > 0xDF) ? 3 : (firstByte > 0xBF) ? 2 : 1 if (i + bytesPerSequence <= end) { var secondByte, thirdByte, fourthByte, tempCodePoint switch (bytesPerSequence) { case 1: if (firstByte < 0x80) { codePoint = firstByte } break case 2: secondByte = buf[i + 1] if ((secondByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) if (tempCodePoint > 0x7F) { codePoint = tempCodePoint } } break case 3: secondByte = buf[i + 1] thirdByte = buf[i + 2] if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { codePoint = tempCodePoint } } break case 4: secondByte = buf[i + 1] thirdByte = buf[i + 2] fourthByte = buf[i + 3] if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { codePoint = tempCodePoint } } } } if (codePoint === null) { // we did not generate a valid codePoint so insert a // replacement char (U+FFFD) and advance only 1 byte codePoint = 0xFFFD bytesPerSequence = 1 } else if (codePoint > 0xFFFF) { // encode to utf16 (surrogate pair dance) codePoint -= 0x10000 res.push(codePoint >>> 10 & 0x3FF | 0xD800) codePoint = 0xDC00 | codePoint & 0x3FF } res.push(codePoint) i += bytesPerSequence } var len = res.length var str = '' var i = 0 while (i < len) { str += String.fromCharCode.apply(String, res.slice(i, i += 0x1000)) } return str } // string -> buffer var textEncode = typeof TextEncoder === 'function' ? TextEncoder.prototype.encode.bind(new TextEncoder()) : stringEncode // buffer -> string var textDecode = typeof TextDecoder === 'function' ? TextDecoder.prototype.decode.bind(new TextDecoder()) : stringDecode function FakeBlobBuilder() { function isDataView(obj) { return obj && DataView.prototype.isPrototypeOf(obj) } function bufferClone(buf) { var view = new Array(buf.byteLength) var array = new Uint8Array(buf) var i = view.length while (i--) { view[i] = array[i] } return view } function array2base64(input) { var byteToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' var output = [] for (var i = 0; i < input.length; i += 3) { var byte1 = input[i] var haveByte2 = i + 1 < input.length var byte2 = haveByte2 ? input[i + 1] : 0 var haveByte3 = i + 2 < input.length var byte3 = haveByte3 ? input[i + 2] : 0 var outByte1 = byte1 >> 2 var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4) var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6) var outByte4 = byte3 & 0x3F if (!haveByte3) { outByte4 = 64 if (!haveByte2) { outByte3 = 64 } } output.push( byteToCharMap[outByte1], byteToCharMap[outByte2], byteToCharMap[outByte3], byteToCharMap[outByte4] ) } return output.join('') } var create = Object.create || function(a) { function c() {} c.prototype = a return new c() } if (arrayBufferSupported) { var viewClasses = [ '[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]' ] var isArrayBufferView = ArrayBuffer.isView || function(obj) { return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 } } function concatTypedarrays(chunks) { var size = 0 var i = chunks.length while (i--) { size += chunks[i].length } var b = new Uint8Array(size) var offset = 0 for (i = 0, l = chunks.length; i < l; i++) { var chunk = chunks[i] b.set(chunk, offset) offset += chunk.byteLength || chunk.length } return b } /********************************************************/ /* Blob constructor */ /********************************************************/ function Blob(chunks, opts) { chunks = chunks || [] opts = opts == null ? {} : opts for (var i = 0, len = chunks.length; i < len; i++) { var chunk = chunks[i] if (chunk instanceof Blob) { chunks[i] = chunk._buffer } else if (typeof chunk === 'string') { chunks[i] = textEncode(chunk) } else if (arrayBufferSupported && (ArrayBuffer.prototype.isPrototypeOf(chunk) || isArrayBufferView( chunk))) { chunks[i] = bufferClone(chunk) } else if (arrayBufferSupported && isDataView(chunk)) { chunks[i] = bufferClone(chunk.buffer) } else { chunks[i] = textEncode(String(chunk)) } } this._buffer = global.Uint8Array ? concatTypedarrays(chunks) : [].concat.apply([], chunks) this.size = this._buffer.length this.type = opts.type || '' if (/[^\u0020-\u007E]/.test(this.type)) { this.type = '' } else { this.type = this.type.toLowerCase() } } Blob.prototype.arrayBuffer = function() { return Promise.resolve(this._buffer) } Blob.prototype.text = function() { return Promise.resolve(textDecode(this._buffer)) } Blob.prototype.slice = function(start, end, type) { var slice = this._buffer.slice(start || 0, end || this._buffer.length) return new Blob([slice], { type: type }) } Blob.prototype.toString = function() { return '[object Blob]' } /********************************************************/ /* File constructor */ /********************************************************/ function File(chunks, name, opts) { opts = opts || {} var a = Blob.call(this, chunks, opts) || this a.name = name.replace(/\//g, ':') a.lastModifiedDate = opts.lastModified ? new Date(opts.lastModified) : new Date() a.lastModified = +a.lastModifiedDate return a } File.prototype = create(Blob.prototype) File.prototype.constructor = File if (Object.setPrototypeOf) { Object.setPrototypeOf(File, Blob) } else { try { File.__proto__ = Blob } catch (e) {} } File.prototype.toString = function() { return '[object File]' } /********************************************************/ /* FileReader constructor */ /********************************************************/ function FileReader() { if (!(this instanceof FileReader)) { throw new TypeError( "Failed to construct 'FileReader': Please use the 'new' operator, this DOM object constructor cannot be called as a function." ) } var delegate = document.createDocumentFragment() this.addEventListener = delegate.addEventListener this.dispatchEvent = function(evt) { var local = this['on' + evt.type] if (typeof local === 'function') local(evt) delegate.dispatchEvent(evt) } this.removeEventListener = delegate.removeEventListener } function _read(fr, blob, kind) { if (!(blob instanceof Blob)) { throw new TypeError("Failed to execute '" + kind + "' on 'FileReader': parameter 1 is not of type 'Blob'.") } fr.result = '' setTimeout(function() { this.readyState = FileReader.LOADING fr.dispatchEvent(new Event('load')) fr.dispatchEvent(new Event('loadend')) }) } FileReader.EMPTY = 0 FileReader.LOADING = 1 FileReader.DONE = 2 FileReader.prototype.error = null FileReader.prototype.onabort = null FileReader.prototype.onerror = null FileReader.prototype.onload = null FileReader.prototype.onloadend = null FileReader.prototype.onloadstart = null FileReader.prototype.onprogress = null FileReader.prototype.readAsDataURL = function(blob) { _read(this, blob, 'readAsDataURL') this.result = 'data:' + blob.type + ';base64,' + array2base64(blob._buffer) } FileReader.prototype.readAsText = function(blob) { _read(this, blob, 'readAsText') this.result = textDecode(blob._buffer) } FileReader.prototype.readAsArrayBuffer = function(blob) { _read(this, blob, 'readAsText') // return ArrayBuffer when possible this.result = (blob._buffer.buffer || blob._buffer).slice() } FileReader.prototype.abort = function() {} /********************************************************/ /* URL */ /********************************************************/ URL.createObjectURL = function(blob) { return blob instanceof Blob ? 'data:' + blob.type + ';base64,' + array2base64(blob._buffer) : createObjectURL.call(URL, blob) } URL.revokeObjectURL = function(url) { revokeObjectURL && revokeObjectURL.call(URL, url) } /********************************************************/ /* XHR */ /********************************************************/ var _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send if (_send) { XMLHttpRequest.prototype.send = function(data) { if (data instanceof Blob) { this.setRequestHeader('Content-Type', data.type) _send.call(this, textDecode(data._buffer)) } else { _send.call(this, data) } } } global.FileReader = FileReader global.File = File global.Blob = Blob } function fixFileAndXHR() { var isIE = !!global.ActiveXObject || ( '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style ) // Monkey patched // IE don't set Content-Type header on XHR whose body is a typed Blob // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6047383 var _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send if (isIE && _send) { XMLHttpRequest.prototype.send = function(data) { if (data instanceof Blob) { this.setRequestHeader('Content-Type', data.type) _send.call(this, data) } else { _send.call(this, data) } } } try { new File([], '') } catch (e) { try { var klass = new Function('class File extends Blob {' + 'constructor(chunks, name, opts) {' + 'opts = opts || {};' + 'super(chunks, opts || {});' + 'this.name = name.replace(/\//g, ":");' + 'this.lastModifiedDate = opts.lastModified ? new Date(opts.lastModified) : new Date();' + 'this.lastModified = +this.lastModifiedDate;' + '}};' + 'return new File([], ""), File' )() global.File = klass } catch (e) { var klass = function(b, d, c) { var blob = new Blob(b, c) var t = c && void 0 !== c.lastModified ? new Date(c.lastModified) : new Date() blob.name = d.replace(/\//g, ':') blob.lastModifiedDate = t blob.lastModified = +t blob.toString = function() { return '[object File]' } if (strTag) { blob[strTag] = 'File' } return blob } global.File = klass } } } if (blobSupported) { fixFileAndXHR() global.Blob = blobSupportsArrayBufferView ? global.Blob : BlobConstructor } else if (blobBuilderSupported) { fixFileAndXHR() global.Blob = BlobBuilderConstructor } else { FakeBlobBuilder() } if (strTag) { File.prototype[strTag] = 'File' Blob.prototype[strTag] = 'Blob' FileReader.prototype[strTag] = 'FileReader' } var blob = global.Blob.prototype var stream function promisify(obj) { return new Promise(function(resolve, reject) { obj.onload = obj.onerror = function(evt) { obj.onload = obj.onerror = null evt.type === 'load' ? resolve(obj.result || obj) : reject(new Error('Failed to read the blob/file')) } }) } try { new ReadableStream({ type: 'bytes' }) stream = function stream() { var position = 0 var blob = this return new ReadableStream({ type: 'bytes', autoAllocateChunkSize: 524288, pull: function(controller) { var v = controller.byobRequest.view var chunk = blob.slice(position, position + v.byteLength) return chunk.arrayBuffer() .then(function(buffer) { var uint8array = new Uint8Array(buffer) var bytesRead = uint8array.byteLength position += bytesRead v.set(uint8array) controller.byobRequest.respond(bytesRead) if (position >= blob.size) controller.close() }) } }) } } catch (e) { try { new ReadableStream({}) stream = function stream(blob) { var position = 0 var blob = this return new ReadableStream({ pull: function(controller) { var chunk = blob.slice(position, position + 524288) return chunk.arrayBuffer().then(function(buffer) { position += buffer.byteLength var uint8array = new Uint8Array(buffer) controller.enqueue(uint8array) if (position == blob.size) controller.close() }) } }) } } catch (e) { try { new Response('').body.getReader().read() stream = function stream() { return (new Response(this)).body } } catch (e) { stream = function stream() { throw new Error('Include https://github.com/MattiasBuelens/web-streams-polyfill') } } } } if (!blob.arrayBuffer) { blob.arrayBuffer = function arrayBuffer() { var fr = new FileReader() fr.readAsArrayBuffer(this) return promisify(fr) } } if (!blob.text) { blob.text = function text() { var fr = new FileReader() fr.readAsText(this) return promisify(fr) } } if (!blob.stream) { blob.stream = stream } })() 4, src/utils/excel/Export2Excel.js /** * 下载: https://github.com/PanJiaChen/vue-element-admin/blob/master/src/vendor/Export2Excel.js * 点击右侧 Raw 按钮 → 右键"另存为" → 保存到 src/utils/excel/Export2Excel.js * 常用于 Vue 项目的前端 Excel 导出工具,依赖 xlsx 和 file-saver。 * 安装依赖包(在项目根目录运行命令): * npm install xlsx file-saver --save * npm install script-loader --save-dev * eslint-disable * */ import { saveAs } from 'file-saver' // import XLSX from 'xlsx' 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({ multiHeader = [], header, data, filename, merges = [], autoWidth = true, bookType = 'xlsx' } = {}) { /* original data */ filename = filename || 'excel-list' data = [...data] // data = [{}, {}, ...] data.unshift(header); 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': 10 }; } /*再判断是否为中文*/ 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]['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 wbout = XLSX.write(wb, { bookType: bookType, bookSST: false, type: 'binary' }); saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), `${filename}.${bookType}`); }

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 7:38:31

LangFlow中日志追踪与性能监控机制介绍

LangFlow中日志追踪与性能监控机制介绍 在构建基于大语言模型&#xff08;LLM&#xff09;的应用时&#xff0c;开发者常常面临一个共同的困境&#xff1a;工作流看似运行正常&#xff0c;但输出结果却不尽如人意。是提示词写得不够清晰&#xff1f;还是解析器出了问题&#xf…

作者头像 李华
网站建设 2026/4/17 20:13:56

BilibiliSummary终极指南:5秒读懂B站视频核心内容

你是否曾经面对B站上数小时的视频内容感到无从下手&#xff1f;BilibiliSummary就是专为你设计的智能视频摘要工具&#xff0c;这款基于Vue 3和TypeScript开发的Chrome扩展插件&#xff0c;能在短短5秒内为你提取视频精华&#xff0c;让你在信息爆炸的时代依然游刃有余。 【免费…

作者头像 李华
网站建设 2026/4/18 3:44:45

解锁PS3全部潜能:webMAN MOD终极指南,打造完美游戏娱乐中心

如果你正在寻找一个能够彻底改变PS3使用体验的解决方案&#xff0c;webMAN MOD正是你需要的终极工具。这款功能强大的PS3自制系统插件将你的游戏主机转变为功能齐全的娱乐中心&#xff0c;提供游戏加载、文件管理、远程控制等综合服务&#xff0c;让PS3发挥出前所未有的强大性能…

作者头像 李华
网站建设 2026/4/18 3:44:10

Docker环境下的VeraCrypt加密存储架构探索

Docker环境下的VeraCrypt加密存储架构探索 【免费下载链接】VeraCrypt Disk encryption with strong security based on TrueCrypt 项目地址: https://gitcode.com/GitHub_Trending/ve/VeraCrypt 你是否曾想过&#xff0c;在容器化部署的浪潮中&#xff0c;那些存储在Do…

作者头像 李华
网站建设 2026/4/18 1:31:49

Mesop Select组件默认值设置:从困惑到精通的开发心路

Mesop Select组件默认值设置&#xff1a;从困惑到精通的开发心路 【免费下载链接】mesop 项目地址: https://gitcode.com/GitHub_Trending/me/mesop "为什么我的选择框总是空的&#xff1f;"——这是很多Mesop开发者初次接触Select组件时的心声。作为一个看似…

作者头像 李华
网站建设 2026/4/18 3:46:43

终极色彩调色板生成器:一键创建完美渐变色系

终极色彩调色板生成器&#xff1a;一键创建完美渐变色系 【免费下载链接】tints-and-shades &#x1f308; Display tints and shades of a given hex color in 10% increments. 项目地址: https://gitcode.com/gh_mirrors/ti/tints-and-shades 想要快速生成专业的色彩调…

作者头像 李华