前言
在# 用xlsx-style玩转Excel导出------拿走即用的Excel导出方法封装 一文中,我们封装了可以实现多种自定义需求的Excel导出方法。然而,在最近的需求开发中,发现此方法在自定义单元格颜色方面有缺陷,故重新修改记录一下。
缺陷
原方法,只能整体定义表头区域样式、内容区域样式,颗粒度未到达每一个单元格。
也就是说,通过原方法定义后,表头区域所有单元格的样式是一样的,内容区域所有单元格的样式也是一样的。
这就不能满足不同单元格展示不同样式的需求。
改进
加入setCellStyle
回调函数。
通过setCellStyle
函数获取单元格信息,并根据需要处理单元格样式逻辑。
js
/**
* 配置单元格样式
* @param { 单元格字段名 } key
* @param { 单元格内容信息 } info
* @param { 表头/内容区域区分 } type
*/
function setCellStyle(key, info, type) {
if(条件判断) {
return {
}
}
return {
border: {top: {style: 'thin'},bottom: {style: 'thin'},left: {style: 'thin'},right: {style: 'thin'},},
font: { name: '宋体', sz: 11, italic: false, underline: false },
alignment: { vertical: 'center', horizontal: 'left',},
fill: { fgColor: { rgb: 'FFFFFF' } },
}
}
1.表头
js
// 处理sheet表头
const _header = data.header.map((item, i) =>
Object.assign({}, {
key: item.dataIndex,
title: item.title,
// 定位单元格
position: getCharCol(i) + 1,
// 设置表头样式
s: setCellStyle ? setCellStyle(key, item, 'header') : defaultCellStyle.headerStyle,
})
).reduce((prev, next) =>
Object.assign({}, prev, {
[next.position]: { v: next.title, key: next.key, s: next.s },
}), {}
)
2.内容
js
data.dataSource.forEach((item, i) => {
data.header.forEach((obj, index) => {
const key = getCharCol(index) + (i + 2)
const key_t = obj.dataIndex
_data[key] = {
v: item[key_t],
s: setCellStyle? setCellStyle(key_t, item,'content') : defaultCellStyle.dataStyle,
}
})
})
完整代码
js
import XLSX from 'xlsx-style'
// 默认工作簿配置
const defaultWorkBook = {
bookType: 'xlsx',
bookSST: false,
type: 'binary',
}
// 默认样式配置
const borderAll = {
top: {
style: 'thin',
},
bottom: {
style: 'thin',
},
left: {
style: 'thin',
},
right: {
style: 'thin',
},
}
const defaultCellStyle = {
// 表头区域样式配置
headerStyle: {
border: borderAll,
font: { name: '宋体', sz: 11, italic: false, underline: false, bold: true },
alignment: { vertical: 'center', horizontal: 'center' },
fill: { fgColor: { rgb: 'FFFFFF' } },
},
// 内容区域样式配置
dataStyle: {
border: borderAll,
font: { name: '宋体', sz: 11, italic: false, underline: false },
alignment: { vertical: 'center', horizontal: 'left', wrapText: true },
fill: { fgColor: { rgb: 'FFFFFF' } },
},
}
function exportExcel(exportData, fileName = '未命名', setCellStyle, workBookConfig = defaultWorkBook) {
if (!(exportData && exportData.length)) {
return
}
// 定义工作簿对象
const wb = { SheetNames: [], Sheets: {} }
exportData.forEach((data, index) => {
// 处理sheet表头
const _header = data.header.map((item, i) =>
Object.assign({}, {
key: item.dataIndex,
title: item.title,
// 定位单元格
position: getCharCol(i) + 1,
// 设置表头样式
s: setCellStyle ? setCellStyle(key, item, 'header') : defaultCellStyle.headerStyle,
})
).reduce((prev, next) =>
Object.assign({}, prev, {
[next.position]: { v: next.title, key: next.key, s: next.s },
}), {}
)
// 处理sheet内容
const _data = {}
data.dataSource.forEach((item, i) => {
data.header.forEach((obj, index) => {
const key = getCharCol(index) + (i + 2)
const key_t = obj.dataIndex
_data[key] = {
v: item[key_t],
s: setCellStyle? setCellStyle(key_t, item, 'content') : defaultCellStyle.dataStyle,
}
})
})
const output = Object.assign({}, _header, _data)
const outputPos = Object.keys(output)
// 设置单元格宽度
const colWidth = data.header.map(item => { return { wpx: item.width || 80 } })
const merges = data.workSheetConfig && data.workSheetConfig.merges
const freeze = data.workSheetConfig && data.workSheetConfig.freeze
// 处理sheet名
wb.SheetNames[index] = data.sheetName ? data.sheetName : 'Sheet' + (index + 1)
// 处理sheet数据
wb.Sheets[wb.SheetNames[index]] = Object.assign({},
output, // 导出的内容
{
'!ref': `${outputPos[0]}:${outputPos[outputPos.length - 1]}`,
'!cols': [...colWidth],
'!merges': merges ? [...merges] : undefined,
'!freeze': freeze ? [...freeze] : undefined,
}
)
})
// 转成二进制对象
const tmpDown = new Blob(
[s2ab(XLSX.write(wb, workBookConfig))],
{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }
)
// 下载表格
downExcel(tmpDown, `${fileName + '.'}${workBookConfig.bookType === 'biff2' ? 'xls' : workBookConfig.bookType}`)
}
/**
* 生成ASCll值 从A开始
* @param {*} n
*/
function getCharCol(n) {
if (n > 25) {
let s = ''
let m = 0
while (n > 0) {
m = n % 26 + 1
s = String.fromCharCode(m + 64) + s
n = (n - m) / 26
}
return s
}
return String.fromCharCode(65 + n)
}
// 字符串转字符流---转化为二进制的数据流
function s2ab(s) {
if (typeof ArrayBuffer !== 'undefined') {
const buf = new ArrayBuffer(s.length)
const view = new Uint8Array(buf)
for (let i = 0; i !== s.length; ++i) { view[i] = s.charCodeAt(i) & 0xff }
return buf
} else {
const buf = new Array(s.length)
for (let i = 0; i !== s.length; ++i) { buf[i] = s.charCodeAt(i) & 0xff }
return buf
}
}
function downExcel(obj, fileName) {
const a_node = document.createElement('a')
a_node.download = fileName
// 兼容ie
if ('msSaveOrOpenBlob' in navigator) {
window.navigator.msSaveOrOpenBlob(obj, fileName)
} else {
// 新的对象URL指向执行的File对象或者是Blob对象.
a_node.href = URL.createObjectURL(obj)
}
a_node.click()
setTimeout(() => {
URL.revokeObjectURL(obj)
}, 100)
}
export {
exportExcel,
}