需要

1、前端自定义 html 模板,导出 pdf
2、多个 pdf 导出压缩到一个文件 zip 外面

原理

1、html2Canvas 将 html 模板转为 canvas
2、canvas 转为 img 图片
3、生成的图片增加到 pdf 外面

用到的插件

html2canvasjspdfjszipfile-saver

模板

<template>  <div class="table-class">    <div class="name-style">{{ row.meetingName }}会议纪要</div>    <table border="0" class="table-style">      <tbody>        <tr>          <td>会议主题</td>          <td colspan="5">{{ row.meetingTheme }}</td>        </tr>        <tr>          <td>会议工夫</td>          <td>{{ meetingTime || '-' }}</td>          <td>会议地点</td>          <td>{{ row.meetingPlace }}</td>          <td>会议级别</td>          <td>{{ matterLevelObj[row.meetingLevel]||"-" }}</td>        </tr>        <tr>          <td>主持人</td>          <td>{{ row.host }}</td>          <td>记录人</td>          <td>{{ row.recorder }}</td>          <td>抄报</td>          <td>{{ row.ccleader }}</td>        </tr>        <tr>          <td>审批人</td>          <td colspan="5">{{ row.approverName || '-' }}</td>        </tr>        <tr>          <td>参加人</td>          <td colspan="5">{{ row.participant }}</td>        </tr>        <tr>          <td>备注</td>          <td colspan="5">{{ row.remark }}</td>        </tr>        <tr>          <td colspan="6" class="td-bg">会议内容</td>        </tr>        <tr>          <td colspan="6">{{ row.remark }}</td>        </tr>        <tr>          <td colspan="6" class="td-bg">会议事项</td>        </tr>        <tr>          <td>序号</td>          <td>类型</td>          <td>工作事项</td>          <td>负责人</td>          <td>预计实现工夫</td>          <td>工作打算</td>        </tr>        <tr v-for="(item,i) of row.meetMatters" :key="i">          <td>{{ i+1 }}</td>          <td>{{ item.type }}</td>          <td>{{ item.workItem }}</td>          <td>{{ item.principalName }}</td>          <td>{{ item.planEndtime }}</td>          <td>{{ item.workPlan }}</td>        </tr>      </tbody>    </table>  </div></template><script>  import mixin from '../mixin';  export default {    name: 'ExportPdf',    mixins: [mixin],    props: {      row: {        type: Object,        default: () => {},      },    },    computed: {      meetingTime() {        const start = this.row?.meetingStartTime;        const end = this.row?.meetingEndTime;        if (start && end) {          return start.substr(0, start.length - 3) + '~' + end.substr(0, end.length - 3);        } else {          return '';        }      },    },  };</script><style lang="scss" scoped>  .table-class {    background-color: #fff;    width: 1000px;    margin: auto;    padding: 40px;    box-sizing: border-box;    .name-style {      text-align: center;      font-size: 20px;      font-weight: bold;      margin-bottom: 20px;    }  }  .table-style {    border-collapse: collapse;    width: 100%;    text-align: center;    td,    th {      padding: 10px;      font-size: 15px;      border: 1px solid black;    }    .td-bg {      background: #ccc;    }  }</style>

异步导出函数

async exportMeeting (type) {      // type:'1' 抉择导出 'all':导出所有      try {        this.allLoading = true        let selectedData = []        if (type === '1') {          const ids = this.multipleSelection.map(el => el.id)          selectedData = await this.getMeetingAll(ids)        } else if (type === 'all') {          selectedData = await this.getMeetingAll()        }        this.loadingText = '正在拼命导出...'        const zip = new JSZip()        const promises = []        this.isShowPdf = true        for (let i = 0; i < selectedData.length; i++) {          const element = selectedData[i]          this.data = element          // 期待每一个转为pdf          const p = await htmlToPdf.getPdf(document.getElementById('pdf'), element.meetingName)          promises.push(p)        }        // 等到所有的promise执行实现顺次压缩到zip中        Promise.all(promises).then(async (pdfs) => {          for (let i = 0; i < pdfs.length; i++) {            const { PDF, name } = pdfs[i]            // 如果只是导出一个pdf,则导出pdf格局            if (pdfs.length === 1) {              PDF.save(`${name}-${new Date().getTime()}.pdf`)              this.allLoading = false              this.loadingText = '正在申请数据'            } else {              // 否则增加到压缩包外面              await zip.file(`${name}-${new Date().getTime()}.pdf`, PDF.output('blob'))            }          }          if (pdfs.length > 1) {            zip.generateAsync({ type: 'blob' }).then(content => {              FileSaver.saveAs(content, '销项治理平台会议纪要.zip')            })          }        }).finally(() => {          this.allLoading = false          this.loadingText = '正在申请数据'        })      } catch (e) {        this.allLoading = false        this.loadingText = '正在申请数据'        throw new Error(e)      }    },

获取 pdf 的公共封装函数

// 导出pdfimport html2Canvas from 'html2canvas';import JsPDF from 'jspdf';export default {  getPdf: (el, pdfName) => {    // 滚轮滑动造成的,次要是html2canvas是依据body进行截图,若内容高度高于body时,就会呈现这样的问题    // 解决方案:(在生成截图前,先把滚动条置顶)    window.pageYoffset = 0;    document.documentElement.scrollTop = 0;    document.body.scrollTop = 0;    return new Promise((resolve, reject) => {      // 在点击保留图片时,此时要保留的资源较多,造成模块并没有齐全加载结束,就曾经生成了截图。      // 解决方案:(提早)      setTimeout(() => {        // 这句挺重要        html2Canvas(el, {          scale: 4,          dpi: 300,          useCORS: true,          // allowTaint: true        })          .then((canvas) => {            const contentWidth = canvas.width;            const contentHeight = canvas.height;            const pageHeight = (contentWidth / 592.28) * 841.89;            let leftHeight = contentHeight; //            let position = 0;            const imgWidth = 595.28;            const imgHeight = (592.28 / contentWidth) * contentHeight;            const pageData = canvas.toDataURL('image/jpeg', 1.0);            const PDF = new JsPDF('', 'pt', 'a4');            if (leftHeight < pageHeight) {              // 在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示;              PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);              // pdf.addImage(pageData, 'JPEG', 20, 40, imgWidth, imgHeight);            } else {              // 分页              while (leftHeight > 0) {                PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);                leftHeight -= pageHeight;                position -= 841.89;                // 防止增加空白页                if (leftHeight > 0) {                  PDF.addPage();                }              }            }            resolve({ PDF, name: pdfName });          })          .catch((e) => {            reject(e);          });      }, 500);    });  },};

遇到的问题

  • 增加到 zip 时,获取 pdf 的内容
zip.file(`${name}-${new Date().getTime()}.pdf`, PDF.output('blob')); //第二个参数PDF.output('blob')
  • 增加到 zip 时,同名文件会被笼罩
// 通过给文件名增加一个工夫戳解决zip.file(`${name}-${new Date().getTime()}.pdf`, PDF.output('blob')); //`${name}-${new Date().getTime()}.pdf`