• Vue 前端 实现 HTML 转 PDF 并导出(方案一:html2canvas + jspdf 前端直接实现)


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

    方案一

    html2canvas + jspdf

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

    使用

    • 安装:
    npm install --save html2canvas
    npm install --save jspdf
    
    • 1
    • 2
    • 绘制页面,页面上一共有两个表格
    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 生成的pdf
      在这里插入图片描述

    缺点

    1. 绘制页面时间很长(上图代码中的页面花费了5.386s)
    2. 由于导出的PDF是由图片生成的,所以PDF的文字无法进行复制

    优点

    简单的页面可以使用

    方案二

  • 相关阅读:
    【华为机试真题 JAVA】运维日志排序-100
    C++智能指针
    Shell脚本练习题(附详细解题过程)
    软件工程填空题(50题)
    传输层 TCP主要特点和TCP连接
    记一次MySQL执行修改语句超时问题
    Java23种设计模式-结构型模式之适配器模式
    什么是分子优化(Molecule Optimization)以及相关论文
    22-06-24 linux(01) linux环境搭建和命令行
    pyautogui 记录
  • 原文地址:https://blog.csdn.net/RosaChampagne/article/details/126159055