近期公司提出了一个新需求,希望将用户在前端填写的一系列数据生成一个报告给用户,报告大概有8个表格,表格涉及到分页。于是查询了资料,做出两个方案。
方案二请点击这里

方案一

html2canvas + jspdf

使用html2canvas将使用canvas将页面转为base64图片流,并插入jspdf插件中,保存并下载pdf

使用
  • 安装:
npm install --save html2canvas
npm install --save jspdf
  • 绘制页面,页面上一共有两个表格
<template>
  <div class="table-wrapper">
    <div class="tables">
      <div class="pdf-dom">
        <div class="title">A.1 xxx汇总表
        </div>
        <table class="table" border="1" cellspacing="0">
          <thead>
            <tr>
              <td>类别</td>
              <td>商品</td>
              <td>小计</td>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>T</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>外套</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>羽绒服</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>短袖</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>短裤</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>连衣裙</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>半身裙</td>
              <td>5</td>
              <td>5</td>
            </tr>
          </tbody>
        </table>
      </div>
      <div class="pdf-dom">
        <div class="title">A.2 xxx汇总表
        </div>
        <table class="table" border="1" cellspacing="0">
          <thead>
            <tr>
              <td>类别</td>
              <td>商品</td>
              <td>小计</td>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>T</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>外套</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>羽绒服</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>短袖</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>短裤</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>连衣裙</td>
              <td>5</td>
              <td>5</td>
            </tr>
            <tr>
              <td>半身裙</td>
              <td>5</td>
              <td>5</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>

    <div class="content">
      <button type="primary" @click="onExport">{{ $t('common.confirm') }}</button>
    </div>
  </div>
</template>

<script>
import { htmlToPdf } from '@/utils/htmlToPdf';

export default {
  methods: {
    async onExport() {
      await htmlToPdf('pdf-dom', '报告');
    },
  },
};
</script>

<style lang="less" scoped>
.pdf-dom {
  height: 1104px;
  width: 800px;
  padding: 20px;

  .title {
    font-size: 18px;
    font-weight: 600;
    margin-bottom: 10px;
    text-align: center;
  }

  .footer {
    text-align: end;
  }
}

.table {
  width: 100%;
  border: 1px solid #999;
  padding: 0;
  margin: 0;
  border-collapse: collapse;

  th {
    height: 45px;
    line-height: 45px;
    text-align: center;
    border-bottom: 1px solid #999;
    padding: 0;
    margin: 0;
  }

  td {
    height: 45px;
    line-height: 45px;
    text-align: center;
    border-bottom: 1px solid #999;
    padding: 0;
    margin: 0;
  }

  tr:hover {
    background-color: #f8f8f8;
  }
}
</style>
  • htmlToPdf.js
import html2Canvas from 'html2canvas';
import JsPDF from 'jspdf';

export const htmlToPdf = async (name, title) => {
  const startAt = new Date();
  const element = document.querySelectorAll(`.${name}`);
  let count = 0;
  const PDF = new JsPDF('', 'pt', 'a4');
  const pageArr = [];
  const opts = {
    width: '840',
    scale: 12, // 缩放比例,提高生成图片清晰度
    useCORS: true, // 允许加载跨域的图片
    allowTaint: false, // 允许图片跨域,和 useCORS 二者不可共同使用
    tainttest: true, // 检测每张图片已经加载完成
    logging: true, // 日志开关,发布的时候记得改成 false
  };
  let pdfData = '';
  for await (const [index] of Array.from(element).entries()) {
    const canvas = await html2Canvas(element[index], opts);
    // a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
    const contentWidth = canvas.width;
    const contentHeight = canvas.height;
    const imgWidth = 595.28;
    const imgHeight = (592.28 / contentWidth) * contentHeight;
    const pageData = canvas.toDataURL('image/jpeg', 1.0);
    // 一页pdf显示html页面生成的canvas高度;
    const pageHeight = (contentWidth / 592.28) * 841.89;
    // 未生成pdf的html页面高度
    const leftHeight = contentHeight;
    pageArr[index] = { pageData, pageHeight, leftHeight, imgWidth, imgHeight };
    if (++count === element.length) {
      // 转换完毕,可进行下一步处理 pageDataArr
      let counts = 0;
      for (const data of pageArr) {
        // 页面偏移
        let position = 0;
        // 转换完毕,save保存名称后浏览器会自动下载
        // 当内容未超过pdf一页显示的范围,无需分页
        if (data.leftHeight < data.pageHeight) {
          // addImage(pageData, 'JPEG', 左,上,宽度,高度)设置
          PDF.addImage(data.pageData, 'JPEG', 0, 0, data.imgWidth, data.imgHeight);
        } else {
          // 超过一页时,分页打印(每页高度841.89)
          while (data.leftHeight > 0) {
            PDF.addImage(data.pageData, 'JPEG', 0, position, data.imgWidth, data.imgHeight);
            data.leftHeight -= data.pageHeight;
            position -= 841.89;
            if (data.leftHeight > 0) {
              PDF.addPage();
            }
          }
        }
        if (++counts === pageArr.length) {
          pdfData = PDF.output('datauristring');
          const endAt = new Date();
          PDF.save(`${title}-${new Date().getTime()}.pdf`);
          console.log(`生成成功.......,时间:${(endAt.getTime() - startAt.getTime()) / 1000}s`);
        } else {
          // 未转换到最后一页时,pdf增加一页
          PDF.addPage();
        }
      }
    }
  }

  return pdfData;
};
  • 生成的pdf
    在这里插入图片描述
缺点
  1. 绘制页面时间很长(上图代码中的页面花费了5.386s)
  2. 由于导出的PDF是由图片生成的,所以PDF的文字无法进行复制
优点

简单的页面可以使用

方案二

Logo

前往低代码交流专区

更多推荐