一、简介
使用 Lay-Excel插件,原本使用 xlsx.js 实现的,但是后来发现xlsx.js 中样式实现存在bug无法解决,于是改用lay-excel,lay-excel其实也是对xlsx.js 的封装,而本文则是对其进一步封装!!! 代码复制调接口直接可用!!!
二、代码
下载插件
npm install lay-excel
import excel from 'lay-excel'
import { Message } from 'element-ui'
/**
* Date: 2022-04-08 星期五
* Time: 17:01
* Author: Dily_Su
* Remark:
* excel 导出
* url 文件下载
*/
/**
* 导出当前页面Table中的数据
* @param classOrId table Class or Id (唯一性)
* @param fileName 文件名
*/
export function exportExcelByClassId(classOrId, fileName) {
const tableData = excel.tableToJson(document.querySelector(classOrId))
if (tableData.body.length === 0) {
return Message.error('表格数据为空,不能导出')
}
let datalist = []
if (Array.isArray(tableData.head[0])) {
tableData.head.forEach(e => {
datalist.push(headLinkData(e, tableData.body))
})
const temp = datalist.map((e, index) => Object.keys(e[0]).length)
datalist = datalist[temp.indexOf(Math.max(...temp))]
} else {
datalist = headLinkData(tableData.head, tableData.body)
}
// 加表头
const head = {}
// eslint-disable-next-line no-return-assign
Object.keys(datalist[datalist.length - 1]).forEach(e => head[e] = e)
datalist.unshift(head)
exportExcelByList(datalist, fileName)
}
/**
* 单 sheet 导出,无标题
* @param {[object]} dataList 数组,默认sheet名为sheet1
* @param {string} fileName 文件名
* @param {[string]} mergeList 合并单元格列表
* @param {number} contextIndex 正文开始索引,默认:1
*/
export function exportExcelByList(dataList, fileName, mergeList, contextIndex) {
const index = contextIndex || 1
const config = {
extend: {
'!cols': getColConfig(dataList.slice(index)),
'!merges': getMergeConfig(mergeList || [])
}
}
dataList = excel.filterExportData(setCellStyleWithOutTitle(dataList, index), Object.keys(dataList[index]))
excel.exportExcel({ sheet1: dataList }, fileName + '.xlsx', null, config)
}
/**
* 单 sheet 导出
* @param {[object]} dataList 数组,默认sheet名为sheet1
* @param {string} fileName 文件名
* @param {[string]} mergeList 合并单元格列表(除去标题)
* @param {number} contextIndex 正文开始索引,默认:1
*/
export function exportExcelByListWithTitle(dataList, fileName, mergeList, contextIndex) {
const title = {}
const index = contextIndex || 1
title[Object.keys(dataList[index])[0]] = fileName
dataList.unshift(title)
// 文件标题
const config = {
extend: {
// 合并单元数组
'!cols': getColConfig(dataList.slice(index)),
'!merges': getMergeConfig(mergeList ? ['A1:' + excel.numToTitle(Object.keys(dataList[index]).length) + '1'].concat(mergeList) : ['A1:' + excel.numToTitle(Object.keys(dataList[index]).length) + '1'])
}
}
dataList = excel.filterExportData(setCellStyleWithTitle(dataList, index), Object.keys(dataList[index]))
excel.exportExcel({ sheet1: dataList }, fileName + '.xlsx', null, config)
}
/**
* 多 sheet 导出
* @param {object} data object:key为 sheetName,value为 Data
* @param {string}fileName 文件名
*/
export function exportExcelByListWithSheets(data, fileName) {
// skipHeader 用于控制List中的属性名是否生成表头
const title = {}
title[Object.keys(data[data.length - 1])[0]] = fileName
data.unshift(title)
excel.exportExcel(data, fileName + '.xlsx', '.xlsx')
}
/**
* 设置单元格样式
* @param {[object]} dataList 数据
* @param {number} contextIndex 正文开始索引,默认:1
* @returns {*}
*/
export function setCellStyleWithTitle(dataList, contextIndex) {
let dataTitle = dataList.slice(0, 1)
const dataBody = dataList.slice(1)
// 样式
// 设置表标题样式
const titleConfig = {
s: {
font: {
sz: 20,
bold: true
},
alignment: {
horizontal: 'center',
vertical: 'center'
},
border: {
top: { style: 'thin', color: { rgb: 'FF000000' }},
bottom: { style: 'thin', color: { rgb: 'FF000000' }},
left: { style: 'thin', color: { rgb: 'FF000000' }},
right: { style: 'thin', color: { rgb: 'FF000000' }}
}
}
}
dataTitle = excel.setExportCellStyle(dataTitle, excel.getDefaultRange(dataTitle), titleConfig, null)
return dataTitle.concat(setCellStyleWithOutTitle(dataBody, contextIndex))
}
/**
* 设置单元格样式
* @param {[object]} dataList 数据
* @param {number} contextIndex 正文开始索引,默认:1
* @returns {*}
*/
export function setCellStyleWithOutTitle(dataList, contextIndex) {
let dataHeader
let dataBody
// 样式
// 设置表标题样式
const headerConfig = {
s: {
font: {
bold: true
},
alignment: {
horizontal: 'center',
vertical: 'center'
},
border: {
top: { style: 'thin', color: { rgb: 'FF000000' }},
bottom: { style: 'thin', color: { rgb: 'FF000000' }},
left: { style: 'thin', color: { rgb: 'FF000000' }},
right: { style: 'thin', color: { rgb: 'FF000000' }}
}
}
}
// 设置数据样式
const bodyConfig = {
s: {
alignment: {
horizontal: 'center',
vertical: 'center'
},
border: {
top: { style: 'thin', color: { rgb: 'FF000000' }},
bottom: { style: 'thin', color: { rgb: 'FF000000' }},
left: { style: 'thin', color: { rgb: 'FF000000' }},
right: { style: 'thin', color: { rgb: 'FF000000' }}
}
}
}
if (dataList.length < 2) {
return excel.setExportCellStyle(dataList, excel.getDefaultRange(dataList), headerConfig, null)
} else {
dataHeader = dataList.slice(0, contextIndex)
dataBody = dataList.slice(contextIndex)
dataHeader = excel.setExportCellStyle(dataHeader, excel.getDefaultRange(dataHeader), headerConfig, null)
dataBody = excel.setExportCellStyle(dataBody, excel.getDefaultRange(dataBody), bodyConfig, null)
return dataHeader.concat(dataBody)
}
}
/**
*
* @param {[string]} rangeList
* @returns {{extend: {"!merges": type[]}}}
*/
export function getMergeConfig(rangeList) {
const typeList = rangeList.map(range => range.split(':'))
return excel.makeMergeConfig(typeList)
}
/**
*
* @param dataList 数据
* @returns {$ObjMap} 自适应行号
*/
export function getColConfig(dataList) {
const keys = Object.keys(dataList[dataList.length - 1])
const temp = {}
keys.forEach((k, index) => {
temp[excel.numToTitle(index + 1)] = []
temp[excel.numToTitle(index + 1)].push(gerStringLength(k))
dataList.forEach(e => {
temp[excel.numToTitle(index + 1)].push(gerStringLength(e[k]))
})
})
const data = {}
Object.keys(temp).forEach(e => {
if (e) {
data[e] = Math.max(...temp[e]) * 10
}
})
return excel.makeColConfig(data, 80)
}
/**
* 计算字符串长度
* @param str
* @returns {number} 长度
*/
function gerStringLength(str) {
if (!str) {
return 10
} else if (str.toString().charCodeAt(0) > 255) {
/* 再判断是否为中文*/
return str.toString().length * 2
} else {
return str.toString().length
}
}
/**
*
* @param headList 表头,数组
* @param dataArray 数据,二维数组
* @returns [*]
*/
function headLinkData(headList, dataArray) {
const data = []
dataArray.forEach(d => {
const temp = {}
headList.filter(e => e && e.length > 0).forEach((e, index) => {
temp[e] = d[index]
})
data.push(temp)
})
return data
// return data.filter(e => Object.values(e).every(v => v || v.toString().length > 0))
}
/**
* 下载文件
* @param url 路径
* @param name 文件名
*/
export function downloadFileByUrl(url, name) {
const link = document.createElement('a')
// 这里是将url转成blob地址,
fetch(url)
.then((res) => res.blob())
.then((blob) => {
// 将链接地址字符内容转变成blob地址
link.href = URL.createObjectURL(blob)
link.download = name
document.body.appendChild(link)
link.click()
})
}
/**
* 将Index 转换为 字符
* @param index index
* @returns {string} 字符坐标
*/
export function index2Char(index) {
const base = '0ABCDEFGHIJKLMNOPQRSTUVWXYZ'
let result = ''
let n = +index
if (n > 26) {
while (n > 25) {
let nowChar
if ((n % 26) === 0) {
nowChar = base.charAt(26)
n = Math.floor(n / 26) - 1
} else {
nowChar = base.charAt(n % 26)
n = Math.floor(n / 26)
}
result = nowChar + result
}
result = (base.charAt(n % 26)) + result
} else {
result = (base.charAt(n)) + result
}
return result
}
/**
* 将字符横坐标转换为index
* @param str 字符坐标
* @returns {number} index
*/
export function char2Index(str) {
const base = '0ABCDEFGHIJKLMNOPQRSTUVWXYZ'
if (str.length > 1) {
return base.indexOf(str.substring(0, 1)) * 26 + base.indexOf(str.substring(1))
} else {
return base.indexOf(str)
}
}
三、官方文档
Lay-Excelhttp://excel.wj2015.com/_book/docs/%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B.html