纯前端导出excel表格 使用 xlsx-style 可自定义样式,行高,打印属性,页边距和多表格导出打包成压缩包下载
1.使用环境
- vue: 2.6.10
- xlsx-style: 0.8.13
- file-saver: 2.0.5
2. 先修改依赖以实现需求
- 增加自定义行高功能
在node_modules里找到xlsx-style 找到 xlsx.js 搜索关键词 write_ws_xml_data 把此函数替换为
// 增加行高设置
var DEF_PPI = 96, PPI = DEF_PPI;
function px2pt(px) { return px * 96 / PPI; }
function pt2px(pt) { return pt * PPI / 96; }
function write_ws_xml_data(ws, opts, idx, wb) {
var o = [], r = [], range = safe_decode_range(ws['!ref']), cell="", ref, rr = "", cols = [], R=0, C=0, rows = ws['!rows'];
var dense = Array.isArray(ws);
var params = ({r:rr}), row, height = -1;
for(C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C);
for(R = range.s.r; R <= range.e.r; ++R) {
r = [];
rr = encode_row(R);
for(C = range.s.c; C <= range.e.c; ++C) {
ref = cols[C] + rr;
var _cell = dense ? (ws[R]||[])[C]: ws[ref];
if(_cell === undefined) continue;
if((cell = write_ws_xml_cell(_cell, ref, ws, opts, idx, wb)) != null) r.push(cell);
}
if(r.length > 0 || (rows && rows[R])) {
params = ({r:rr});
if(rows && rows[R]) {
row = rows[R];
if(row.hidden) params.hidden = 1;
height = -1;
if (row.hpx) height = px2pt(row.hpx);
else if (row.hpt) height = row.hpt;
if (height > -1) { params.ht = height; params.customHeight = 1; }
if (row.level) { params.outlineLevel = row.level; }
}
o[o.length] = (writextag('row', r.join(""), params));
}
}
if(rows) for(; R < rows.length; ++R) {
if(rows && rows[R]) {
params = ({r:R+1});
row = rows[R];
if(row.hidden) params.hidden = 1;
height = -1;
if (row.hpx) height = px2pt(row.hpx);
else if (row.hpt) height = row.hpt;
if (height > -1) { params.ht = height; params.customHeight = 1; }
if (row.level) { params.outlineLevel = row.level; }
o[o.length] = (writextag('row', "", params));
}
}
return o.join("");
}
- 增加缩放,设置纵横向 设置打印纸张大小 功能
搜索关键词 write_ws_xml_pagesetup 把此函数改为
// 打印属性设置
function write_ws_xml_pagesetup(setup) {
var pageSetup = writextag('pageSetup', null, {
paperSize: setup.pagesize || "9", // 纸张大小 9为A4纸
scale: setup.scale || '100', // 缩放大小
orientation: setup.orientation || 'portrait', // 纸张方向 landscape 为横向 portrait 为纵向
horizontalDpi : setup.horizontalDpi || '4294967292',
verticalDpi : setup.verticalDpi || '4294967292'
})
return pageSetup;
}
搜索关键字 write_ws_xml 替换为
function write_ws_xml(idx, opts, wb) {
var o = [XML_HEADER, WS_XML_ROOT];
var s = wb.SheetNames[idx], sidx = 0, rdata = "";
var ws = wb.Sheets[s];
if(ws === undefined) ws = {};
var ref = ws['!ref']; if(ref === undefined) ref = 'A1';
o[o.length] = (writextag('dimension', null, {'ref': ref}));
var sheetView = writextag('sheetView', null, {
showGridLines: opts.showGridLines == false ? '0' : '1',
tabSelected: opts.tabSelected === undefined ? '0' : opts.tabSelected,
workbookViewId: opts.workbookViewId === undefined ? '0' : opts.workbookViewId
});
o[o.length] = writextag('sheetViews', sheetView);
if(ws['!cols'] !== undefined && ws['!cols'].length > 0) o[o.length] = (write_ws_xml_cols(ws, ws['!cols']));
o[sidx = o.length] = '<sheetData/>';
if(ws['!ref'] !== undefined) {
rdata = write_ws_xml_data(ws, opts, idx, wb);
if(rdata.length > 0) o[o.length] = (rdata);
}
if(o.length>sidx+1) { o[o.length] = ('</sheetData>'); o[sidx]=o[sidx].replace("/>",">"); }
if(ws['!merges'] !== undefined && ws['!merges'].length > 0) o[o.length] = (write_ws_xml_merges(ws['!merges']));
if (ws['!margins'] !== undefined) o[o.length] = write_ws_xml_margins(ws['!margins']); // 页边距 !!! 必须要先设置页边距
if (ws['!pageSetup'] !== undefined) o[o.length] = write_ws_xml_pagesetup(ws['!pageSetup']); // 缩放 纵横向
if (ws['!rowBreaks'] !== undefined) o[o.length] = write_ws_xml_row_breaks(ws['!rowBreaks']);
if (ws['!colBreaks'] !== undefined) o[o.length] = write_ws_xml_col_breaks(ws['!colBreaks']);
if(o.length>2) { o[o.length] = ('</worksheet>'); o[1]=o[1].replace("/>",">"); }
return o.join("");
}
- 增加页边距设置
搜索关键词 write_ws_xml_pagesetup 在此函数下方 增加如下两个函数
function write_ws_xml_margins(margin) {
let margin2 = default_margins(margin);
return writextag('pageMargins', null, margin2);
}
function default_margins(margins, mode) {
if(!margins) return;
var defs = [0.7, 0.7, 0.75, 0.75, 0.3, 0.3];
if(mode == 'xlml') defs = [1, 1, 1, 1, 0.5, 0.5];
if(margins.left == null) margins.left = defs[0];
if(margins.right == null) margins.right = defs[1];
if(margins.top == null) margins.top = defs[2];
if(margins.bottom == null) margins.bottom = defs[3];
if(margins.header == null) margins.header = defs[4];
if(margins.footer == null) margins.footer = defs[5];
return margins
}
在 write_ws_xml 函数里增加 调用 ,在上方 ““增加缩放,设置纵横向 设置打印纸张大小 功能”” 功能里 已经增加过了 注意 页边距 必须在缩放之前设置 不然会导致导出错误!!!
if (ws['!margins'] !== undefined) o[o.length] = write_ws_xml_margins(ws['!margins']); // 页边距 !!! 必须要先设置页边距
3. 需要一个调用文件 Export2Excel.js 内容如下 (跟Bolb.js 同放于项目src/assets/js 文件夹内)
/* eslint-disable */
require('script-loader!file-saver')
// require('script-loader!../../assets/js/Blob');//该引入方式可能会报错
require('../../assets/js/Blob')
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)
}
export 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)
// XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], {range:1,defval:''});
return ws
}
function Workbook() {
if (!(this instanceof Workbook)) return new Workbook()
this.SheetNames = []
this.Sheets = {}
}
export 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_json_to_excel({
status = 1, // 状态 控制第几份模板
multiHeader = [], // 多级表头
header, // 表头
data, // 数据
sheetname, // sheet名称
filename, // 文件名
merges = [], // 合并列表
width = [], // 自定义列宽
height = [], // 自定义列高
isreturn = false, // 是否返回blob
autoWidth, // 自定义是否自动列高宽
bookType = 'xlsx' // 文件类型
} = {}) {
// console.log('header', header, 'data', data, 'sheetname', sheetname, 'filename', filename)
// console.log('multiHeader', multiHeader, 'merges', merges)
filename = filename || 'excel-list'
data = [...data]
//先在数据里追加表头
if (header.length > 0) {
for (var k = 0; k < sheetname.length; k++) {
data[k].unshift(header[k])
}
}
// 追加多级表头
if (multiHeader.length > 0) {
for (var k = 0; k < sheetname.length; k++) {
for (let i = multiHeader[k].length - 1; i > -1; i--) {
data[k].unshift(multiHeader[k][i])
}
}
}
// 处理空行
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < data[i].length; j++) {
if (data[i][j].length === 0) {
data[i].splice(j, 1)
}
}
}
var ws_name = sheetname || ['sheet1']
var wb = new Workbook(),
ws = []
// 处理合并单元格
for (var j = 0; j < sheetname.length; j++) {
ws.push(sheet_from_array_of_arrays(data[j]))
if (merges[j].length > 0) {
if (!ws[j]['!merges']) ws[j]['!merges'] = []
merges[j].forEach(item => {
ws[j]['!merges'].push(XLSX.utils.decode_range(item))
})
}
}
// 处理自定义宽高
for (let i = 0; i < autoWidth.length; i++) {
if (autoWidth[i]) {
let colWidth = []
colWidth.push(
data[i].map(row =>
row.map(val => {
/*先判断是否为null*/
if (val === null || val === '' || val === undefined) {
return {
wch: 18
}
} else if (val.toString().charCodeAt(0) > 255) {
/*再判断是否为中文*/
return {
wch: val.toString().length * 2 < 18 ? 18 : val.toString().length * 2
}
} else {
return {
wch: val.toString().length < 18 ? 18 : val.toString().length
}
}
})
)
)
// console.log('colWidth', i, autoWidth[i], data, colWidth)
// console.log('colHeight', colHeight)
/*以第一行为初始值*/
let result = []
if (colWidth[0][0]) {
result = colWidth[0][0]
for (let k = 1; k < colWidth[0].length; k++) {
for (let j = 0; j < colWidth[0][k].length; j++) {
if (result[j]['wch'] < colWidth[0][k][j]['wch']) {
result[j]['wch'] = colWidth[0][k][j]['wch']
}
}
}
}
ws[i]['!cols'] = result
let colHeight = []
colHeight.push(
data[i].map(row =>
row.map(val => {
if (val == null) {
return {
hpx: 25
}
} else if (val.toString().charCodeAt(0) > 255) {
return {
hpx: val.toString().length > 18 ? 25 : 25
}
} else {
return {
hpx: val.toString().length > 25 ? 25 : 25
}
}
})
)
)
let result1 = []
// for (var k = 0; k < colHeight.length; k++) {
if (colHeight[0][0]) {
result1 = colHeight[0][0]
// console.log('result1[k]', result1[0], 'colHeight[0][0]', colHeight[0][0])
for (let k = 1; k < colHeight[0].length; k++) {
for (let j = 0; j < colHeight[0][k].length; j++) {
if (result1[j]['hpx'] < colHeight[0][k][j]['hpx']) {
result1[j]['hpx'] = colHeight[0][k][j]['hpx']
}
}
}
// }
}
ws[i]['!rows'] = result1
} else {
if (width[i].length > 0) {
ws[i]['!cols'] = width[i]
} else {
let colWidth = []
colWidth.push(
data[i].map(row =>
row.map(val => {
/*先判断是否为null*/
if (val == null) {
return {
wch: 18
}
} else if (val.toString().charCodeAt(0) > 255) {
/*再判断是否为中文*/
return {
wch: val.toString().length * 2 < 18 ? 18 : val.toString().length * 2
}
} else {
return {
wch: val.toString().length < 18 ? 18 : val.toString().length
}
}
})
)
)
// console.log('colWidth', colWidth)
let result = []
if (colWidth[0][0]) {
result = colWidth[0][0]
// console.log('result[k]', result[k], 'colWidth[k][0]', colWidth[k][0])
for (let k = 1; k < colWidth[0].length; k++) {
for (let j = 0; j < colWidth[0][k].length; j++) {
if (result[j]['wch'] < colWidth[0][k][j]['wch']) {
result[j]['wch'] = colWidth[0][k][j]['wch']
}
}
}
}
ws[i]['!cols'] = result
}
if (height.length > 0) {
ws[i]['!rows'] = height[i]
} else {
let colHeight = []
colHeight.push(
data[i].map(row =>
row.map(val => {
if (val == null) {
return {
hpx: 80
}
} else if (val.toString().charCodeAt(0) > 255) {
return {
hpx: val.toString().length > 18 ? 80 : 40
}
} else {
return {
hpx: val.toString().length > 36 ? 80 : 40
}
}
})
)
)
let result1 = []
if (colHeight[0][0]) {
result1 = colHeight[0][0]
// console.log('result1[k]', result1[0], 'colHeight[0][0]', colHeight[0][0])
for (let k = 1; k < colHeight[0].length; k++) {
for (let j = 0; j < colHeight[0][k].length; j++) {
if (result1[j]['hpx'] < colHeight[0][k][j]['hpx']) {
result1[j]['hpx'] = colHeight[0][k][j]['hpx']
}
}
}
}
ws[i]['!rows'] = result1
}
}
}
let target = dohandingexcel(status, wb, ws_name, sheetname, ws, data, header)
wb = target.wb
ws = target.ws
console.log('wb', wb)
// '!pageSetup': {scale: 67, orientation: 'landscape'}, //数据表打印时的页面配置,scale表示缩放比例,orientation是打印时的纸张方向landscape为横向打印
// '!margins': {left:0.2, right: 0.2, top: 0.2, bottom: 0.2, header: 0.2, footer: 0.2}, //打印时的页面边距
// //缩放100%,打印方向为纵向
// worksheet['!pageSetup'] = {
// scale: '100',
// orientation: 'portrait'
// }
// // orientation 取值如下:
// // 'portrait' - 纵向
// // 'landscape' - 横向
// 导出Excel
var wbout = XLSX.write(wb, {
bookType: bookType,
bookSST: false,
type: 'binary'
})
let blobs = new Blob([s2ab(wbout)], {
type: 'application/octet-stream'
})
if (isreturn) {
return blobs
} else {
saveAs(blobs,
filename + '.' + bookType
)
}
}
// 自定义表格样式
// status 根据状态判断是哪种表格模板
function dohandingexcel(status, wb, ws_name, sheetname, ws, data, header) {
// console.log('status', status,'ws_name',ws_name)
// 模板样式自定义
const borderAll = {
//单元格外侧框线
top: {
style: 'thin'
},
bottom: {
style: 'thin'
},
left: {
style: 'thin'
},
right: {
style: 'thin'
}
}
let border = []
let SZ9 = []
let horight = []
let holeft = []
let A = ['A', 'C', 'E', 'G', 'I', 'K', 'M', 'O', 'Q', 'S', 'U', 'W', 'y'] // 奇数列
let B = ['B', 'D', 'F', 'H', 'J', 'L', 'N', 'P', 'R', 'T', 'V', 'X', 'Z'] // 偶数列
if (status === 1) {
// 第一种模板
// 设置页面大小 横纵向
// for (var j = 0; j < sheetname.length; j++) {
// ws[j]['!pageSetup'] = {
// paperSize: '9', // 纸张大小
// scale: '80', // 缩放
// orientation: 'portrait' // 横纵向
// }
// ws[j]['!margins'] = { // 页边距 会根据缩放改变 自行调整
// left: 0.1,
// right: 0.1,
// top: 0.1,
// bottom: 0.1,
// header: 0.1,
// footer: 0.1
// }
// }
let blue = ['A3', 'E2', 'C8', 'E8', 'G15', 'V21', 'W21'] // 字体为蓝色
let green = ['E13', 'G13', 'I15', 'I16', 'U21'] // 字体为绿色
let yellow = ['AP21'] // 字体为绿色
let italic = ['B2', 'B3', 'B4', 'B9', 'B10', 'B11', 'B17', 'B18', 'D17', 'D18', 'F5', 'F17', 'H17', 'H13', 'H5', 'J3', 'L5', 'J16', 'J17'] // 斜体
SZ9 = ['B2', 'B3', 'B4', 'B5', 'H5', 'H13', 'J16', 'J3', 'L5'] // 字体为9号
let SZ6 = ['B8', 'B9', 'B10', 'B11', 'B17', 'B18', 'D7', 'D17', 'D18', 'F17', 'H17', 'H14', 'J17'] // 字体为6
horight = ['B2', 'B3', 'B4', 'B5', 'B8', 'B9', 'B10', 'B11', 'D7', 'F5', 'H5', 'H13', 'H14', 'J3', 'J16', 'L5'] // 水平靠右
let wrapText = ['B8', 'B9', 'B10', 'B11', 'B18', 'D7', 'D17', 'D18', 'F17', 'H17', 'J17'] // 是否文字换行
border = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17', 'B18', 'B19',
'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17', 'D18', 'D19',
'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19',
'H2', 'H3', 'H5', 'H15', 'H16', 'H17', 'H18',
'J2', 'J3', 'J5', 'J15', 'J16', 'J17', 'J18',
'L5'] // 是否边框
for (var k = 0; k < header.length; k++) {
wb.SheetNames.push(ws_name[k])
wb.Sheets[ws_name[k]] = ws[k]
var dataInfo = wb.Sheets[wb.SheetNames[k]]
//给所以单元格加上边框
for (var i in dataInfo) {
if (i === '!ref' || i === '!merges' || i === '!cols' || i === 'A1' || i === '!rows') {
} else {
const index = Number(i.match(/\d+/g)[0])
if (k === 0) {
if ((dataInfo[i + ''].v !== '' && index < 22) || border.includes(i)) {
if (index === 21) { // 表头
dataInfo[i + ''].s = {
border: borderAll,
font: {
name: '微软雅黑',
sz: 11,
color: { rgb: '000000' },
bold: false,
italic: false
// underline: false
},
alignment: {
horizontal: 'center', // 水平 "left"||"center"||"right"
vertical: 'bottom', // 垂直 "bottom"||"center"||"top"
wrapText: false // 文字换行 false || true
// readingOrder: 2, // 文字阅读顺序 默认2
// textRotation: 180 // 旋转 255 是垂直排列文字
}
}
//黄色
if (yellow.includes(i)) {
dataInfo[i + ''].s.font.color = { rgb: 'FFC000' }
}
//绿色
if (green.includes(i)) {
dataInfo[i + ''].s.font.color = { rgb: '00B050' }
}
//蓝色
if (blue.includes(i)) {
dataInfo[i + ''].s.font.color = { rgb: '538DD5' }
}
} else {
dataInfo[i + ''].s = {
border: borderAll,
font: {
name: '微软雅黑',
sz: 11, // 字体大小
color: { rgb: '000000' }, // 字体颜色
bold: false, // 是否加粗
italic: false // 是否斜体
// underline: false
},
alignment: {
horizontal: 'right', // 水平 "left"||"center"||"right"
vertical: 'bottom' // 垂直 "bottom"||"center"||"top"
// readingOrder: 2, // 文字阅读顺序 默认2
// textRotation: 180 // 旋转 255 是垂直排列文字
}
}
dataInfo[i + ''].t = 's'
if (border.includes(i)) {
dataInfo[i + ''].s.border = borderAll
}
if (B.includes(i.split('')[0])) {
dataInfo[i + ''].s.alignment.horizontal = 'left'
}
//蓝色
if (blue.includes(i)) {
dataInfo[i + ''].s.font.color = { rgb: '538DD5' }
}
//绿色
if (green.includes(i)) {
dataInfo[i + ''].s.font.color = { rgb: '00B050' }
}
if (italic.includes(i)) {
dataInfo[i + ''].s.font.italic = true
}
if (SZ9.includes(i)) {
dataInfo[i + ''].s.font.sz = 9
}
if (SZ6.includes(i)) {
dataInfo[i + ''].s.font.sz = 6
}
if (horight.includes(i)) {
dataInfo[i + ''].s.alignment.horizontal = 'right'
}
if (wrapText.includes(i)) {
dataInfo[i + ''].s.alignment.wrapText = true
}
}
} else {
// 默认样式
dataInfo[i + ''].s = {
// border: borderAll,
font: {
name: '宋体',
sz: 11, // 字体大小
color: { rgb: '000000' }, // 字体颜色
bold: false, // 是否加粗
italic: false // 是否斜体
// underline: false
},
alignment: {
horizontal: 'left', // 水平 "left"||"center"||"right"
vertical: 'bottom' // 垂直 "bottom"||"center"||"top"
// readingOrder: 2, // 文字阅读顺序 默认2
// textRotation: 180 // 旋转 255 是垂直排列文字
}
}
dataInfo[i + ''].t = 's'
// if(i.includes('F')){
// dataInfo[i + ''].s.alignment.horizontal = 'left'
// }
// 黄色
}
}
if (k !== 0) {
// console.log('a13', k, index, dataInfo[i + ''].v)
if (dataInfo[i + ''].v !== '' && index === 2 && k !== 0) {
dataInfo[i + ''].s = {
border: borderAll,
font: {
name: '微软雅黑',
sz: 11,
color: { rgb: '000000' },
bold: false
// italic: false,
// underline: false
},
alignment: {
horizontal: 'center',
vertical: 'center'
}
}
}
}
}
}
// 标题行
// 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'
// ]
// arr.some(function(v) {
// let a = merges[0].split(":");
// if (v == a[1]) {
// dataInfo[v].s = {};
// return true;
// } else {
// dataInfo[v].s = {};
// }
// });
//设置主标题样式
let style = {
font: {
name: '微软雅黑',
sz: 22,
color: { rgb: '000000' },
bold: true
// italic: false,
// underline: false
},
alignment: {
horizontal: 'center',
vertical: 'center'
}
// fill: {
// fgColor: {rgb: "008000"},
// },
}
if (k === 0) {
dataInfo['A1'].s = style
} else {
dataInfo['A1'].s = {
font: {
name: '宋体',
sz: 14,
color: { rgb: '000000' },
bold: true
// italic: false,
// underline: false
},
alignment: {
horizontal: 'center',
vertical: 'center'
}
// fill: {
// fgColor: {rgb: "008000"},
// },
}
}
//excel标题样式
// for (var i = 0; i < header.length; i++) {
// dataInfo[arr[i]].s = style
// }
}
} else if (status === '3-1') {
// 第二种模板
// 设置页面大小 横纵向
for (var j = 0; j < sheetname.length; j++) {
ws[j]['!pageSetup'] = {
paperSize: '9', // 纸张大小
scale: '80', // 缩放
orientation: 'portrait' // 横纵向
}
ws[j]['!margins'] = { // 页边距 会根据缩放改变 自行调整
left: 0.1,
right: 0.1,
top: 0.1,
bottom: 0.1,
header: 0.1,
footer: 0.1
}
}
border = []
let bold = ['B1', 'H3', 'H4']
let SZ25 = ['B1']
let SZ30 = ['G1']
let fang = ['A1', 'A4', 'A5', 'G1', 'F3', 'F4', 'A5', 'B5', 'H5']
for (let k = 0; k < sheetname.length; k++) {
wb.SheetNames.push(ws_name[k])
wb.Sheets[ws_name[k]] = ws[k]
let length1 = data[k].length
var dataInfo3 = wb.Sheets[wb.SheetNames[k]]
// console.log('dataInfo3', dataInfo3)
for (var i in dataInfo3) {
if (i === '!ref' || i === '!merges' || i === '!cols' || i === '!rows' || i === '!pageSetup' || i === '!margins') {
} else {
const index = Number(i.match(/\d+/g)[0])
dataInfo3[i + ''].s = {
border: borderAll,
font: {
name: 'Arial',
sz: 9, // 字体大小
color: { rgb: '000000' }, // 字体颜色
bold: false, // 是否加粗
italic: false // 是否斜体
// underline: false // 下划线
},
alignment: {
horizontal: 'center', // 水平 "left"||"center"||"right"
vertical: 'center' // 垂直 "bottom"||"center"||"top"
// wrapText:'' // 换行 true
// readingOrder: 2, // 文字阅读顺序 默认2
// textRotation: 180 // 旋转 255 是垂直排列文字
}
}
if (i === 'A1') {
dataInfo3[i + ''].s.alignment.wrapText = true
}
if (i === 'B4') {
dataInfo3[i + ''].s.font.name = 39251
dataInfo3[i + ''].s.font.sz = 16
}
if (fang.includes(i)) {
dataInfo3[i + ''].s.font.name = '方正兰亭黑简体'
}
if (SZ25.includes(i)) {
dataInfo3[i + ''].s.font.sz = 25
}
if (SZ30.includes(i)) {
dataInfo3[i + ''].s.font.sz = 30
}
if (bold.includes(i)) {
dataInfo3[i + ''].s.font.bold = true
}
if (index > 5 && index < length1 - 3) {
// console.log('a', i, index, dataInfo3)
if (i.includes('B')) {
dataInfo3[i + ''].s.alignment.horizontal = 'left'
}
if (i.includes('H')) {
dataInfo3[i + ''].t = 'n' // 字段类型
dataInfo3[i + ''].s.font.bold = true // 加粗
}
if (i.includes('A')) {
dataInfo3[i + ''].s.font.bold = true // 加粗
}
}
if (index > length1 - 2) {
dataInfo3[i + ''].s.border = {}
}
if (index === length1 - 3 && (i.includes('A') || i.includes('D'))) {
dataInfo3[i + ''].s.font.bold = true
dataInfo3[i + ''].s.font.name = '方正兰亭黑简体'
}
if (index === length1 - 3 && (i.includes('B') || i.includes('F'))) {
dataInfo3[i + ''].t = 'n' // 字段类型
}
if (index === length1 - 2) {
dataInfo3[i + ''].s.alignment.horizontal = 'right'
}
}
}
}
}
return {
wb: wb,
ws: ws
}
}
// 备注:
//属性 描述
// v 原始数据
// w 格式化的文本
// t 单元格文本类型:b Boolean, n Number, e Error, s String, d Date
// f 公式
// r 富文本编码
// h 富文本的HTML
// c 与单元格关联的注释
// z 与单元格关联的数字格式的字符串(已废弃)
// l 单元格超链接,.Target holds link, .tooltip is tooltip
// s 单元格具体的样式设置
// 如: dataInfo.v 获取值
// style属性
// 顶级属性 子属性 描述 类型或可选值 默认值
// fill patternType 填充模式 “solid” or “none” -
// fgColor 前景色 COLOR_SPEC -
// bgColor 背景色 COLOR_SPEC {indexed: 64}
// font name 字体名称 string “Calibri”
// sz 字体大小 number 12
// color 字体颜色 COLOR_SPEC -
// bold 加粗 boolean -
// underline 下划线 boolean -
// italic 斜体 boolean -
// strike 瞄边 boolean -
// outline 轮廓 boolean -
// shadow 阴影 boolean -
// vertAlign 垂直对齐 boolean -
// numFmt - 数字格式化 “0” // 内置格式的整数索引
// "0.00%" // 匹配内置格式的字符串,见下文
// "0.0%" // 格式化为自定义格式的字符串
// "0.00%;0.000.00;\-;@" // 格式化的时候转义特殊字符
// "m/dd/yy" // 格式化为日期 -
// alignment vertical 垂直对齐 “bottom”、“center”、“top” -
// horizontal 水平对齐 “left”、“center”、“right” -
// wrapText 换行 boolean -
// readingOrder 文字方向 1、2 // for right-to-left -
// textRotation 旋转 Number from 0 to 180 or 255 0
// border top 上边框 { style: BORDER_STYLE, color: COLOR_SPEC } -
// bottom 下边框 { style: BORDER_STYLE, color: COLOR_SPEC } -
// left 左边框 { style: BORDER_STYLE, color: COLOR_SPEC } -
// right 右边框 { style: BORDER_STYLE, color: COLOR_SPEC } -
// diagonal 对角线 { style: BORDER_STYLE, color: COLOR_SPEC } -
// diagonalUp 上对角线 boolean -
// diagonalDown 下对角线 boolean -
// tips =>
// COLOR_SPEC 用于实现字体、边框以及填充的颜色,可以选择以下几种写法:
// { auto: 1} // 指定自动值
// { rgb: “FFFFAA00” } // 指定十六进制 ARGB 值
// { theme: “1”, tint: “-0.25”} 指定主题颜色的整数索引和色调值 (默认为 0)
// { indexed: 64} // fill.bgColor 的默认值
// BORDER_STYLE border_style 用于指定边框的样式,excel中边框样式有很多种,其中的可选值为:
// thin
// medium
// thick
// dotted
// hair
// dashed
// mediumDashed
// dashDot
// mediumDashDot
// dashDotDot
// mediumDashDotDot
// slantDashDot
// 单元格对象
//
// 每一个单元格是一个对象(Cell Object),主要有t、v、r、h、w等字段(详见这里):
//
// t:表示内容类型,s表示string类型,n表示number类型,b表示boolean类型,d表示date类型,等等
//
// v:表示原始值;
//
// f:表示公式,如B2+B3;
//
// h:HTML内容
//
// w:格式化后的内容
//
// r:富文本内容rich text
// ---------------------------------------------------------------------------------------------------------
// 导出表格 包含图片 待做
/* eslint-disable */
4.需要一个Blob.js 内容如下 (跟Export2Excel.js 同放于项目src/assets/js 文件夹内)
/* eslint-disable */
/* Blob.js*/
/*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
, origin = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/
;
FakeBlob.fake = FB_proto.fake = true;
while (file_ex_code--) {
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
}
// Polyfill URL
if (!real_URL.createObjectURL) {
URL = view.URL = function (uri) {
var
uri_info = document.createElementNS("http://www.w3.org/1999/xhtml", "a")
, uri_origin
;
uri_info.href = uri;
if (!("origin" in uri_info)) {
if (uri_info.protocol.toLowerCase() === "data:") {
uri_info.origin = null;
} else {
uri_origin = uri.match(origin);
uri_info.origin = uri_origin && uri_origin[1];
}
}
return uri_info;
};
}
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 = 0;
delete this.data;
};
return FakeBlobBuilder;
}(view));
view.Blob = function (blobParts, options) {
var type = options ? (options.type || "") : "";
var builder = new BlobBuilder();
if (blobParts) {
for (var i = 0, len = blobParts.length; i < len; i++) {
if (Uint8Array && blobParts[i] instanceof Uint8Array) {
builder.append(blobParts[i].buffer);
}
else {
builder.append(blobParts[i]);
}
}
}
var blob = builder.getBlob(type);
if (!blob.slice && blob.webkitSlice) {
blob.slice = blob.webkitSlice;
}
return blob;
};
var getPrototypeOf = Object.getPrototypeOf || function (object) {
return object.__proto__;
};
view.Blob.prototype = getPrototypeOf(new view.Blob());
}(
typeof self !== "undefined" && self
|| typeof window !== "undefined" && window
|| this
));
5. 调用
解释一下模板
const excelDatas = [
{
tHeader: ['项号', '序号', '编号' ...], // 表头
filterVal: ['GNo', 'Item', 'Code' ...], // 对应字段
tableDatas: [{GNo:1,Item:2,Code:3 ...}], // 对应json数据
width: [{ wch: 7.88 }, { wch: 7.5 }, { wch: 13.88 } ...], // 有几列就有几个宽 从左往右排
height: [{ hpx: 15.75 } ...], // 有几行就有几个高 从上往下排
multiHeader: [
['', '', '', '', '', '项目', '签名', '日期'],
['', '', '', '', '', '制单: ', '', ''],
['', '', '', '', '', '初审: ', '', ''],
['', '', '', '', '', '复审: ', '', ''],
['No :', '', 'Date', 'Code', 'From', '', 'To', ''],
[list[i].entry, '', '0:00:00', '', '中国', '', '', 'by Air'],
['', '', '', '', '', '', '', '']
], // 在表头之上 的自定义数据摆放 如果不是规则的数据 请在次添加
autowidth: false, // 单元格是否自动宽高 如自动宽高 则 width height 为空
merges: ['A1:D1', 'A5:H5', 'A6:H6', 'A13:B13', 'A14:B14', 'A16:C16', 'B8:D8', 'F8:H8', 'E13:F13', 'E14:F14', 'E15:F15', 'E16:F16', 'A15:C15', 'B17:C17'],// 合并单元格
sheetName: '清单'// sheet 名称
},
{
tHeader: [],
filterVal: [],
tableDatas: [],
width: [{ wch: 7.88 }, { wch: 7.5 }, { wch: 13.88 }, { wch: 11.63 }, { wch: 10.5 }, { wch: 11.38 }, { wch: 13.5 }, { wch: 10.38 }],
height: [{ hpx: 15.75 }, { hpx: 15 }, { hpx: 12.75 }, { hpx: 12.75 }, { hpx: 21 }, { hpx: 15 }, { hpx: 12.75 }, { hpx: 12.75 }, { hpx: 12.75 }, { hpx: 12.75 },
{ hpx: 12.75 }, { hpx: 12.75 }, { hpx: 12.75 }, { hpx: 12.75 }, { hpx: 12.75 }, { hpx: 12.75 }, { hpx: 12.75 }, { hpx: 12.75 }],
autowidth: false,
multiHeader: [
[list[i].entry, '', '', '', '', '', '', ''],
['', '', '', '', '', '', '', ''],
['', '', '', '', 'FOB', '', '(1/1)', ''],
['Item No..', 'Description of Goods', '', 'PO No.', 'Q\'TY(PCS)', 'ORIGIN', 'U/Price(USD)', 'TTL AMT(USD)'],
['', '', '', '', '', '', '', '']
],
merges: ['A1:D1', 'A5:H5', 'A6:H6', 'A13:B13', 'A14:B14', 'A16:C16', 'B8:D8', 'F8:H8', 'E13:F13', 'E14:F14', 'E15:F15', 'E16:F16', 'A15:C15', 'B17:C17'],
sheetName: '清单2' // 许可证信息
}
...
]
在使用页面内 预置2个函数
// 数据过滤,时间过滤
formatJson (filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => {
if (j === 'timestamp') {
return parseTime(v[j])
} else {
return v[j]
}
}))
},
json2excel (tableJson, filenames, bookTypes, status) {
var that = this
if (status === 1) {
// do something
}
// 这个是引用插件
import('@/assets/js/Export2Excel').then(excel => {
var tHeader = []
var dataArr = []
var sheetnames = []
var multiHeaderlsit = []
var mergeslsit = []
var widthlsit = []
var heightlsit = []
var autowidthlist = []
for (var i in tableJson) {
multiHeaderlsit.push(tableJson[i].multiHeader)
mergeslsit.push(tableJson[i].merges)
widthlsit.push(tableJson[i].width)
heightlsit.push(tableJson[i].height)
tHeader.push(tableJson[i].tHeader)
autowidthlist.push(tableJson[i].autowidth)
dataArr.push(that.formatJson(tableJson[i].filterVal, tableJson[i].tableDatas))
sheetnames.push(tableJson[i].sheetName)
}
// console.log('dataArr', dataArr)
excel.export_json_to_excel({
multiHeader: multiHeaderlsit,
header: tHeader,
data: dataArr,
width: widthlsit,
height: heightlsit,
merges: mergeslsit,
sheetname: sheetnames,
filename: filenames,
autoWidth: autowidthlist,
bookType: bookTypes,
isreturn: false,
status: status
})
// do something
})
},
示例:
// 导出一个表格
dohandingB (){
for (let i = 0; i < list.length; i++) {
const excelDatas = [
{
tHeader: ['项号', '序号', '编号' ...], // 表头
filterVal: ['GNo', 'Item', 'Code' ...], // 对应字段
tableDatas: [...list[i]], // 对应json数据列表
width: [{ wch: 7.88 }, { wch: 7.5 }, { wch: 13.88 } ...], // 有几列就有几个宽 从左往右排
height: [{ hpx: 15.75 } ...], // 有几行就有几个高 从上往下排
multiHeader: [],
merges: ['A1:J1'],
width: [],
height: [],
autowidth: true,
sheetName: '信息'// 基本信息&商品信息
},
// 多张sheet 多个对象
]
// 在这里excelDatas 可以追加新数据
const filename = '文件名'+ xxx
// 下方调用 如有多张表格导出需要增加队列(一个一个排队导出) 并延迟300毫秒才不会导出错误
this.json2excel(excelDatas, filename, 'xlsx', 1) // 1 为status 第一种模板
}
}
如果多个表格导出 可以打成压缩包再导出
需要在安装依赖 “jszip”: “3.10.0”, 和 “file-saver”: “2.0.5”,
页面引入 import JSZip from ‘jszip’ 和 import fileSaver from ‘file-saver’
exportALL () {
const that = this
this.zip = new JSZip()
const list1 = [
{
data: excelDatas, // 导入数据模板(见上方详情)
name: filename, //文件名
status: 2 //
},
...
]
const list2 = [...list1]
const list = list1.concat(list2)
this.excelDataslist3 = []
for (let i = 0; i < list.length; i++) {
if (i === 0) { // 如果是第一个 直接导出
that.isdone4 = false //队列开始标识
import('@/assets/js/Export2Excel').then(excel => {
var tHeader = []
var dataArr = []
var sheetnames = []
var multiHeaderlsit = []
var mergeslsit = []
var widthlsit = []
var heightlsit = []
var autowidthlist = []
for (var j in list[i].data) {
multiHeaderlsit.push(list[i].data[j].multiHeader)
mergeslsit.push(list[i].data[j].merges)
widthlsit.push(list[i].data[j].width)
heightlsit.push(list[i].data[j].height)
tHeader.push(list[i].data[j].tHeader)
autowidthlist.push(list[i].data[j].autowidth)
dataArr.push(that.formatJson(list[i].data[j].filterVal, list[i].data[j].tableDatas))
sheetnames.push(list[i].data[j].sheetName)
}
// console.log('dataArr', dataArr)
const a = excel.export_json_to_excel2({
multiHeader: multiHeaderlsit,
header: tHeader,
data: dataArr,
width: widthlsit,
height: heightlsit,
merges: mergeslsit,
sheetname: sheetnames,
filename: list[i].name,
autoWidth: autowidthlist,
isreturn: true,
bookType: 'xlsx',
status: list[i].status
}) // a 就是返回bolb数据
// console.log('a', a)
that.zip.file(list[i].name + '.xlsx', a, { binary: true })
that.isdone4 = true //队列每条结束标识
})
// console.log('this.zip', that.zip)
} else {
this.excelDataslist3.push({
data: list[i].data,
name: list[i].name,
status: list[i].status
})
}
}
},
// 监听 isdone4 的开关 可以知道队列的开始与结束 判断之后this.excelDataslist3为空 调用下方代码下载压缩包即可
this.zip.generateAsync({ type: 'blob' }).then(content => { // 生成二进制流
// console.log('content', content)
fileSaver.saveAs(content, '文件名' + '.zip') // 利用file-saver保存文件
})