使用渲染dom传递给xlsx或将dom转canvas在传给jspdf数据量大都会造成页面负载过大
所以导pdf和xlsx都使用数据传递给pdfMake,xlsx-js-style,pdf涉及分页与合并单元格
一.pdf
npm并引入pdfMake和其字体包(记录时使用版本0.2.10
import pdfMake from "pdfmake/build/pdfmake";
import pdfFonts from "pdfmake/build/vfs_fonts";
官网地址pdfmake
pdfMake支持表格和列表等传递数据类型导出pdf,支持水印与合并行列单元格,支持字体颜色和简单样式设置
1.引入中文字体包
pdfMake自带的roboto字体不支持中文,所以需要自己下载一个支持中文的ttf文件,这里使用
SourceHanSansCN-Normal.ttf,网上下载该文件资源,在node_modules的pdfMake包中新建examples
文件夹,在examples中新建一个fonts文件夹,将需要的字体包复制进去。
在pdfMake的目录下打开控制台或者cmd等命令行运行命令,pdfmake会构建字体的编码到pdfmake下的/build/vfs_fonts.js中,这个文件应该包含四种roboto字体
node build-vfs.js "./examples/fonts"
2.引入字体出现错误
pdfmake Font 'Roboto' in style 'bold' is not defined in the font section of或
File 'Roboto-Regular.ttf' not found in virtual file system,等问题
3.使用pdfMake的数据格式
contect = {
style: 'tableExample',
color: '#444',
table: {
//每行宽度
widths: [200, 'auto', 'auto'],
//表头的行数,设置为表头分页时会携带
headerRows: 2,
// keepWithHeaderRows: 1,
//table的数据,合并Colspan ,rowspan
body: [
[{text: 'Header with Colspan = 2', style: 'tableHeader', colSpan: 2, alignment: 'center'}, {}, {text: 'Header 3', style: 'tableHeader', alignment: 'center'}],
[{text: 'Header 1', style: 'tableHeader', alignment: 'center'}, {text: 'Header 2', style: 'tableHeader', alignment: 'center'}, {text: 'Header 3', style: 'tableHeader', alignment: 'center'}],
['Sample value 1', 'Sample value 2', 'Sample value 3'],
[{rowSpan: 3, text: 'rowSpan set to 3\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor'}, 'Sample value 2', 'Sample value 3'],
['', 'Sample value 2', 'Sample value 3'],
['Sample value 1', 'Sample value 2', 'Sample value 3'],
['Sample value 1', {colSpan: 2, rowSpan: 2, text: 'Both:\nrowSpan and colSpan\ncan be defined at the same time'}, ''],
['Sample value 1', '', ''],
]
}
},
let pdfMakeData = {
// watermark: { text: '乐恒', font: 'SourceHanSansCN' } || '',
pageSize: pageSize,
// pageOrientation: 'portrait',
pageMargins: [
cardLeft,
cardTop,
cardLeft,
cardTop,
],
content: content,
//简单的样式
styles: {
tableExample: {
margin: [0, 5, 0, 15],
font: 'SourceHanSansCN',
color: 'black',
},
},
defaultStyle: {
defaultStyle: {
font: 'SourceHanSansCN'
},
}
}
//业务逻辑结束,根据需要构建pdfMake需要的content
//添加vfs字体
pdfMake.vfs = pdfFonts;
//添加字体对象,Roboto是必须添加的否则报错,剩余添加需要的字体这里是SourceHanSansCN
pdfMake.fonts = {
Roboto: {
normal: 'Roboto-Regular.ttf',
bold: 'Roboto-Medium.ttf',
italics: 'Roboto-Italic.ttf',
bolditalics: 'Roboto-MediumItalic.ttf'
},
SourceHanSansCN: {
normal: 'SourceHanSansCN-Normal.ttf',
bold: 'SourceHanSansCN-Normal.ttf',
italics: 'SourceHanSansCN-Normal.ttf',
bolditalics: 'SourceHanSansCN-Normal.ttf'
}
};
//下载
pdfMake.createPdf(pdfMakeData).download(getFileName());
4.使用pdfMake下载
function downPDF({ dataList, labelList, spanObj }) {
//设置一些活参数有用的是oneRowNum每页需要多少列,oneCellWidth每个单元格宽度,纸张型号宽高
let cardTop = 10, cardLeft = 7, textLeft = 9, textTop = 14, oneColumnNum = 35, oneRowNum = 7, oneCellWidth = 73.7, oneCellHeight = 8, oneStrLen = 5, pdfWidth = 203, pdfHeight = 290, strLen = 8, pageSize = 'A4'
//我的业务逻辑实现开始(分页,合并单元格
let startIndex = 0, endIndex = oneRowNum
let content = []
let columnPages = Math.ceil(dataList.length / oneRowNum) + 1
for (let i = 1; i < columnPages; i++) {
let currentColumnData = dataList.slice((i - 1) * oneRowNum, i * oneRowNum)
let currentLabelData = labelList.slice((i - 1) * oneRowNum, i * oneRowNum)
let ownObj = {
table: {
headerRows:spanObj.rowIndex[1],
widths: new Array(currentLabelData.length).fill(oneCellWidth),
body: [
// [{text: 'Header 1'}, {text: 'Header 2', style: 'tableHeader'}, {text: 'Header 3', style: 'tableHeader'}],
// ['Sample value 1', 'Sample value 2', 'Sample value 3'],
],
},
style: 'tableExample'
}
let headerArr = []
currentLabelData.forEach((item) => {
headerArr.push({ text: item })
})
let columnArr = new Array()
currentColumnData.forEach((item, index) => {
item.forEach((cItem, cIndex) => {
columnArr[cIndex] = columnArr[cIndex] || []
columnArr[cIndex].push(cItem)
})
})
if (i === 1) {
spanObj.columnIndex
.forEach((item, index) => {
for (let cindex = 0; cindex < columnArr.length; cindex++) {
const cItem = columnArr[cindex];
let cChild = cItem[item]
if (cChild) {
let repeatIndex = columnArr.filter((fItem) => {
return fItem[item] === cChild
}).length
cItem[item] = { text: cChild, rowSpan: repeatIndex }
index = repeatIndex + index - 1
}
}
})
}
columnArr.unshift(headerArr)
if (spanObj.rowIndex[0].length !== 0) {
let rowHead = []
let endLen = i + 1 === columnPages ? (dataList.length - (i - 1) * oneRowNum) : oneRowNum
for (let keyIndex = 0; keyIndex < endLen; keyIndex++) {
let rowCurrent = (i - 1) * oneRowNum + keyIndex
let rowEnd = 0
for (const key in spanObj.rowIndex[0]) {
let children = spanObj.rowIndex[0][key]
rowEnd = rowEnd + children.length
if (rowEnd > rowCurrent) {
let obj = { text: key }
rowHead.push(obj)
break;
}
}
}
for (let index = 0; index < rowHead.length; index++) {
const cItem = rowHead[index];
let repeatIndex = rowHead.filter((fItem) => {
return fItem.text === cItem.text
}).length
rowHead[index].colSpan = repeatIndex
index = repeatIndex + index - 1
}
columnArr.unshift(rowHead)
}
ownObj.table.body = columnArr
content.push(ownObj)
}
console.log('content', JSON.parse(JSON.stringify(content)))
//业务逻辑结束,根据需要构建pdfMake需要的content
//添加vfs字体
pdfMake.vfs = pdfFonts;
//添加字体对象,Roboto是必须添加的否则报错,剩余添加需要的字体这里是SourceHanSansCN
pdfMake.fonts = {
Roboto: {
normal: 'Roboto-Regular.ttf',
bold: 'Roboto-Medium.ttf',
italics: 'Roboto-Italic.ttf',
bolditalics: 'Roboto-MediumItalic.ttf'
},
SourceHanSansCN: {
normal: 'SourceHanSansCN-Normal.ttf',
bold: 'SourceHanSansCN-Normal.ttf',
italics: 'SourceHanSansCN-Normal.ttf',
bolditalics: 'SourceHanSansCN-Normal.ttf'
}
};
//创建打印数据对象
let pdfMakeData = {
// watermark: { text: '乐恒', font: 'SourceHanSansCN' } || '',
pageSize: pageSize,
// pageOrientation: 'portrait',
pageMargins: [
cardLeft,
cardTop,
cardLeft,
cardTop,
],
content: content,
//简单的样式
styles: {
tableExample: {
margin: [0, 5, 0, 15],
font: 'SourceHanSansCN',
color: 'black',
},
},
defaultStyle: {
defaultStyle: {
font: 'SourceHanSansCN'
},
}
}
//下载
pdfMake.createPdf(pdfMakeData).download(getFileName());
}
GitHub - bpampuch/pdfmake: Client/server side PDF printing in pure JavaScript
二.xlsx
1.npm下载并引入xlsx-js-style,它比xlsx支持自定义表格style,即用数据和属性可以构建xlsx的下载不传dom只操作数据
import XLSX from "xlsx-js-style";
2.下载函数,整理获取到的table数据,根据需求填充,需求只涉及二级表头的合并行与前几列的合并,并非整个表格合并
//表格数据数据,表头数据数组,合并行列数组
function downXlsx({ dataList, labelList, spanObj }) {
console.log(dataList, labelList, spanObj)
//添加合并列的单元格
let mergesArr = []
spanObj.columnIndex
.forEach((item, index) => {
for (let cindex = 0; cindex < dataList.length; cindex++) {
const cItem = dataList[cindex];
let cChild = cItem[item]
if (cChild) {
let repeatIndex = dataList.filter((fItem) => {
return fItem[item] === cChild
}).length
mergesArr.push({ s: { r: cindex + 1, c: item }, e: { r: cindex + repeatIndex, c: item } })
cindex = repeatIndex + cindex - 1
}
}
})
//添加表头为第一列
dataList.unshift(labelList)
//添加合并列的单元格(二级表头
if (spanObj.rowIndex[0].length !== 0) {
let rowHead = []
for (let keyIndex = 0; keyIndex < labelList.length; keyIndex++) {
let rowEnd = 0
for (const key in spanObj.rowIndex[0]) {
let children = spanObj.rowIndex[0][key]
rowEnd = rowEnd + children.length
if (rowEnd > keyIndex) {
rowHead.push(key)
break;
}
}
}
for (let index = 0; index < rowHead.length; index++) {
const cItem = rowHead[index];
let repeatIndex = rowHead.filter((fItem) => {
return fItem === cItem
}).length
mergesArr.push({ s: { r: 0, c: index }, e: { r: 0, c: index + repeatIndex-1 } })
index = repeatIndex + index - 1
}
dataList.unshift(rowHead)
}
//创建xlsx,下载
const wb = XLSX.utils.book_new();
const ws = XLSX.utils.json_to_sheet(dataList, {
skipHeader: true,
});
ws["!merges"] = mergesArr;
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, getFileName());
}
spanObj的数据格式
//datalist每个元素的数据是table的行数据
datalist:[[],[],[]]
labelList:["名称","事件","ey位m","温度℃""]
spanObj :{
//多级表头,一级表头属性下的二级表头内容
"rowIndex": [
{
"基础信息": [
{
"label": "名称",
"prop": "MingCheng",
"width": "150",
"click": "rezhan"
},
{
"label": "事件",
"prop": "事件",
"width": "150",
"click": "rezhan"
},
],
"xx数据": [
{
"label": "ey位m",
"prop": "shuiXiangYeWei",
"width": "100",
"fixed": 1
},
],
"其他数据": [
{
"label": "温度℃",
"prop": "shiWaiWenDu",
"width": "110",
"fixed": 1
},
]
},
2
],
//第0列需要合并,合并相同内容
"columnIndex": [0]
}
html简单demo,fonts里有字体运行index即可