1、场景
由前端实现的报表(含echarts图和table表格),需要将整个报表进行导出下载,但不包括页面中的导航栏及其他元素。
2、问题
1)在导出pdf时 ,如果页面长度超过一页,插件无法只能识别分页,会将内容切割,如下所示:
2)目的:实现当前页放不下该页面元素时,插入空白元素将该元素下挤,实现分页但不分割内容
3、解决方案
通过计算dom元素的高度逐一拆分,动态增加dom元素使布局满足分页但不分割内容,效果如下:
注意:
1)计算高度时,切莫忘记margin;
2)由于通过dom.children 获取子元素,逐一累计并拆分,在添加空白元素节点后,获取的子集也会增加,注意循环中的计数单位自增,对空白元素做标记,如果进入到循环则排除;
3)由于本案例中是将页面元素直接做的变化,再pdf下载后,需移除所有增加的空白元素,恢复页面布局;
4)html2PDF插件中的dom元素参数,只能传页面中有的元素,如若隐藏或深度克隆但未添加到html中的元素,则识别不到,控制台会报错(隐藏了的元素也无法导出);
5)如果将克隆的元素添加到页面中,会丢失echarts图表,故此方案的操作对象为要导出的原dom对象;
6)若页面中的有重叠的元素,会影响高度的计算,修改getCurrentHeight即可;
4、代码如下:
1)methods
printingPage() { //pdf下载事件
$store.commit("smartReport/setData", { key: "loading", value: true });//开启loading
let pdfDomTitle = PDFRef.value.querySelector(".title-bd");
let btnsDom = pdfDomTitle.querySelectorAll(".report-btn");//获取需要隐藏的btns
let pdfTitle = pdfDomTitle.querySelector(".title").innerHTML;//获取报表title
btnsDom.forEach((item) => {
item.style.display = "none";
});
pdfPaging("A4", PDFRef.value); //新增空白元素拼凑分页
html2PDF(PDFRef.value, {
jsPDF: {
unit: "px",
format: "A4",
},
imageType: "image/jpeg",
success: function (pdf) {
pdfRemoveAllPaging(); //恢复默认:去掉分页,恢复默认布局
btnsDom.forEach((item) => {
item.style.display = "block";
});
pdf.save(pdfTitle + ".pdf");//下载保存
$store.commit("smartReport/setData", {//隐藏loading
key: "loading",
value: false,
});
},
});
},
};
- pdfUtil.js
/**
* pdfUtil.js
* @type: 纸张类型:A4,A3
* @dom:元素
* **/
//厘米
let pageHeightByType = {
'A4': 29.7,
'A3': 42
}
//粗略估计 2020 A4纸张在1920屏幕下对应的高度
let pageHeight = 2020; //纸张高度:
let countHeight = 0; //累计高度
let countFunTimes = 0;//记录方法调用次数
//开始分页
export function pdfPaging(type, dom) {
countFunTimes++;
let domH = getCurrentHeight(dom);//当前元素的高度
if (countFunTimes == 1) { //第一次进入该方法:判断纸张大小;是否需要分页
pageHeight = pageHeightByType[type.toUpperCase()];
pageHeight = pageHeight ? pageHeight : 29.7;//默认为A4大小
pageHeight = getPageHeight(pageHeight)
if (pageHeight > domH) {//内容高度未超过一页,不需要分页
countHeight = domH
return;
}
}
//内容高度超过一页,需要分页
let children = dom.children;
//getAllChildrenHeight(dom)<=domH 排除行内布局,仅适用于不换行的行内布局
if (children.length > 0 && getAllChildrenHeight(dom) <= (domH + 1)) {
for (let i = 0; i < children.length; i++) {
let child = children[i]
if (child.getAttribute("class") == 'blockDIvId_pdf_self') continue;
let childH = getCurrentHeight(child);
if (childH > pageHeight) {
pdfPaging(1, child)
} else if (pageHeight > childH + countHeight) { //该元素高度childH+未分页元素高度countHeight 小于 页面高度
countHeight += childH;
} else if (child.children.length >= 2) {
pdfPaging(1, child)
} else { //该元素高度childH+未分页元素高度countHeight 超过 页面高度,在该元素之前插入空白div
addChildBefore(child, pageHeight - countHeight);
i++;//添加空白元素后,影响了children数组
countHeight = childH;
}
}
} else { //仅一个元素,无法拆分
let divH = domH > pageHeight ? domH % pageHeight : countHeight
addChildBefore(dom, pageHeight - divH);
return
}
}
function getCurrentHeight(curNode) {//获取当前元素高度
let childStyle = curNode.currentStyle || window.getComputedStyle(curNode);
let childMarginHeight = parseInt(childStyle.marginTop) + parseInt(childStyle.marginBottom)
let childH = +curNode.offsetHeight + childMarginHeight;
// console.log(childH, childMarginHeight)
return childH
}
function getPageHeight(pageHeight) { //获取pdf页面高度
let heightUint = 2020 / 29.7;//粗略估计 1910 为 A4纸张在1920屏幕下对应的高度
let widthScale = window.innerWidth; // 当前窗口的宽度
// 尺寸换算
const comparedHeight = widthScale / 1920;
// 通过比例计算高度,
return comparedHeight * heightUint * pageHeight;
}
function addChildBefore(curNode, height) {//在curNode节点前添加节点
let blockDIv = document.createElement("div");
blockDIv.style.height = height + 'px';
blockDIv.style.background = 'white';
blockDIv.setAttribute('class', 'blockDIvId_pdf_self');
curNode.parentNode.insertBefore(blockDIv, curNode); //在当前元素前面添加
countHeight = 0;
}
//移除所有分页、参数恢复默认设置
export function pdfRemoveAllPaging() {
let divArr = document.getElementsByClassName("blockDIvId_pdf_self");
for (let i = 0; i < divArr.length; i++) {
let divItem = divArr[i];
divItem.parentNode.removeChild(divItem);
i--;
}
countFunTimes = 0;//恢复默认设置
countHeight = 0;
}
//判断是否为行内布局,仅适用于不换行的行内布局
function getAllChildrenHeight(dom) {
let children = dom.children;
let h = 0;
for (let i = 0; i < children.length; i++) {
h += getCurrentHeight(children[i]) - 1;//减一是为了避免小数点引起的误差,height会默认向上取整
}
return h;
}
【 完 】
【 如有其他方案,欢迎留言或私信交流 】