使用html2canvas+jspdf将页面转为pdf,根据模块分页并下载
需求:我有一个页面,是由多个模块组成的,现在我需要把页面转为pdf并下载,但是因为pdf自动换页以后会把我的模块给截开,不好看甚至内容被裁开,所以我需要判断当前页面加上这个模块以后是不是会超出当前页,如果超出,就直接把整个模块换到第二页去显示。
页面大致如下:
页面代码大致如下:
<template> <div class="child-detail-div"> <div class="title pdf-item">黄浦区适龄幼儿入园报名信息(本市)</div> <div class="pdf-item"> <div class="table-title">报名园所</div> <Descriptions :data="formModel" :columns="refChildGardenColumns" :border="true" ></Descriptions> </div> <div class="pdf-item"> <div class="table-title">幼儿基础信息</div> <Descriptions :data="formModel.zsChildBaseInfoDto" :columns="childBaseColumns" :border="true" ></Descriptions> </div> <div class="pdf-item"> <template v-for="(item, index) in formModel.zsChildGuarderInfoDtos"> <div class="table-title" :key="'title-' + index"> 家庭成员信息 <span v-if="formModel.zsChildGuarderInfoDtos.length > 1">{{ index + 1 }}</span> <span v-if="item.defaultGuarder" class="font-15"> (主要监护人) </span> </div> <Descriptions :key="index" :data="item" :columns="familyColumns" :border="true" ></Descriptions> </template> </div> <div class="pdf-item"> <div class="table-title">验证材料</div> <Descriptions :data="formModel.childAttachmentInfoDto" :columns="childAttachmentColumns" :border="true" :column="1" ></Descriptions> </div>
<div slot="footer" class="footer-button">
<ssb-primary-button @click.native="getPdf"
>导出至PDF
</ssb-primary-button>
<ssb-default-button @click.native="dialogVisible = false"
>取消
</ssb-default-button>
</div>
</div> </template> <script lang="ts"> import viewTS from "./index"; export default viewTS; </script>
如果项目中没安装html2canvas和jspdf,安装命令如下:
npm install html2canvas
npm install jspdf
1、新建文件htmlToPdf.ts
// 导出页面为PDF格式 import html2Canvas from 'html2canvas' import JsPDF from 'jspdf' const a4width = 592.28; // A4的宽度,以毫米为单位 const a4Height = 841.89; // A4的高度,以毫米为单位 export async function getPdf(title: string) { // const pdfDom: any = document.querySelector('#pdfDom'); const itemDom: any = document.querySelectorAll('.pdf-item'); const PDF = new JsPDF(undefined, 'pt', 'a4'); let position = 24; //图像的纵坐标,即左上角的y坐标 let pageItemHight = 0; //页面中item的高度 const imgData = await getImages(itemDom); imgData.forEach((itemCanvas: any) => { const contentWidth = itemCanvas.width; const contentHeight = itemCanvas.height; //一页pdf显示html页面生成的canvas高度 // const pageHeight = contentWidth / a4width * a4Height - 64; const pageHeight = PDF.internal.pageSize.height; const imgWidth = a4width; const imgHeight = a4width / contentWidth * contentHeight; const itemPageData = itemCanvas.toDataURL('image/jpeg', 1.0); //计算页面画面高度 pageItemHight += imgHeight; //如果加上当前item已超过当前页面高度,则另开页面 if ((pageItemHight + 48) > pageHeight) { PDF.addPage(); //添加新页面 position = 24; } //将图像添加到PDF文档中的函数(图像的URL或base64编码的数据,指定图像的格式,图像的横坐标,图像的纵坐标,图像的宽度,图像的高度) PDF.addImage(itemPageData, 'JPEG', 24, position, imgWidth - 48, imgHeight); position += imgHeight; //图像的纵坐标,即左上角的y坐标 }); PDF.save(title + '.pdf'); } function getImages(itemDom: any) { const promises: any[] = []; itemDom.forEach((item: any) => { const promise = html2Canvas(item, { allowTaint: true }).then(function (canvas) { if (canvas.height > 0) { return canvas; // 返回Promise对象,以便集中处理 } }); promises.push(promise); }); return Promise.all(promises); // 返回一个新的Promise对象 }
2、vue页面对应的ts文件中引入上面写的ts文件
import { getPdf } from '@/core/utils/htmlToPdf';
然后就可以直接通过getPdf方法调用了,示例如下:
getPdf() { getPdf("黄浦区适龄幼儿入园报名信息"); //下载文件名 }
因为页面中的“验证材料”模块明显已经不能跟前面的信息完全在第一个页面显示了,所以这个时候当前模块就会如下图一样被挤压到第二页显示:
注意事项:
1、由于我是通过“pdf-item”这个类来手动标识的需要生成pdf的模块,所以在vue页面中一定要加这个,不然的话是不会给加到pdf中去的
2、我的position=24代表我从距离左边24mm的位置开始画图的,如果有不想跟我一样,可以自己改
3、“pageItemHight + 48”跟我前面的“position=24”有关,如果改了position的值,这里也要记得position*2的改
4、“imgWidth - 48”是为了左右各预留24mm的位置,如果想改左右预留位置就直接改48这个数值就好
遗留问题:
我想的是除了左右预留24mm的位置外,上下也能预留24的来,但是目前感觉只实现了上面的预留,下面还不行,看下次还能怎么改。