使用html2canvas+jspdf将页面转为pdf并下载

发布时间 2023-03-23 18:38:40作者: 潇潇mini

需求:我有一个页面,是由多个模块组成的,现在我需要把页面转为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的来,但是目前感觉只实现了上面的预留,下面还不行,看下次还能怎么改。