vue导出PDF(vue2,插件:pdfmake ,表格)

48,051次阅读
没有评论

共计 6620 个字符,预计需要花费 17 分钟才能阅读完成。

:文章为记录为主,想直接看 pdfmake 打印的请划到最下面部分

需求

页面有一个表格,之前导出 excel 表(用 XLSX 和 XLSXS 制作了样式之类的表格),现在需要制作带样式的 pdf, 而且格式如下,并且最上面的三行需要在每一页的顶部出现作为表头

效果图,(换页前面三行也会有)

vue 导出 PDF(vue2,插件:pdfmake,表格)

script 要打印的盒子:ref=”printSection”,这个表格是用 el-table 来写的,而且表格中存在嵌套 children,一共嵌套了 3 层

如上图:大写数字(一、二)是第一层,附和大写字母(A、B)是第二层,其他(没有序号的的行)是第三层

上面表格 data 对应的 staffJieyuList 数据的格式如下:

          staffJieyuList = [ // 这里是第一层数组,里面数据是对象格式
           {
            id: "一",
            row: 1,
            name: "业务收入",
            amount: "",
            contractNum: "",
             // 这里是第二层数组,里面数据是对象格式
            children: [
              {
                id: "",
                row: 101,
                name: "本月合计",
                amount: data.income,
              },
              {
                id: "",
                row: 102,
                name: "本年累计",
                amount: data.incomeYear,
              },
              {
                id: "附",
                row: 103,
                name: "累计本年未收款明细",
                amount: "",
                // 这里是第三层数组,里面数据是对象格式
                children: data.incomeUnpaidList.map((ele, index) => {
                  let i = {
                    id: "",
                    row: "104" + index,
                    name: ele.fapiaoName + "(占比:" + ele.portion + ")",
                    amount: ele.amount,
                  };
                  return i;
                }),
              },
            ],
          },
            第二个数组...
           ]

vue 导出 PDF(vue2,插件:pdfmake,表格)

我要打印的主要是四列:

序号 id、项目 name、金额 amount、备注 contractNum(打印出来的看最上面的图)

一.window.print();

打印可以直接调用:window.print(); 但是这里只调用了一次,取消打印之后不再弹出,而且打印的内容也不符合我的要求

    printDiv3() {console.log("触发 ---");
      const printContents = this.$refs.printSection.innerHTML;
      const originalContents = document.body.innerHTML;
      document.body.innerHTML = printContents;
      window.print();
      this.$nextTick(() => {document.body.innerHTML = originalContents;});
    }

效果图:

vue 导出 PDF(vue2,插件:pdfmake,表格)

二. 用 iframe 来打印

printDiv2() {const printContent = document.querySelector("#pdfDom").innerHTML;
      const iframe = document.createElement("iframe");
      iframe.setAttribute("style", "position: absolute; width: 0; height: 0;");
      document.body.appendChild(iframe);
      const iframeDoc = iframe.contentWindow.document;
      // 设置打印展示方式 - 横向展示
      iframeDoc.write('');
      // 向 iframe 中注入 printContent 样式
      iframeDoc.write(``
      );
      // 写入内容
      iframeDoc.write("
" + printContent + "
"); setTimeout(function () {iframe.contentWindow.print(); document.body.removeChild(iframe); }, 50); },

样式没研究,打印出来的效果图如下,连边框线也没加,更别说需要算哪里的背景色,字体居中和靠左了

vue 导出 PDF(vue2,插件:pdfmake,表格)

 三.JsPDF 插件

用 JsPDF 插件来导出 pdf (配合插件 html2canvas) 导出的第二页也会被截断,样式我也没有研究,感兴趣的朋友可以研究下,文件:htmlToPdf.js

// 导出页面为 PDF 格式
import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
export default {install(Vue, options) {Vue.prototype.getPdf = function (flieName) {
            // 当下载 pdf 时,若不在页面顶部会造成 PDF 样式不对, 所以先回到页面顶部再下载
            let top = document.getElementById('pdfDom');
            if (top != null) {top.scrollIntoView();
                top = null;
            }
            let title = flieName;
            html2Canvas(document.querySelector('#pdfDom'), {allowTaint: true}).then(function (canvas) {
                // 获取 canvas 画布的宽高
                let contentWidth = canvas.width;
                let contentHeight = canvas.height;
                // 一页 pdf 显示 html 页面生成的 canvas 高度;
                let pageHeight = contentWidth / 841.89 * 592.28;
                // 未生成 pdf 的 html 页面高度
                let leftHeight = contentHeight;
                // 页面偏移
                let position = 0;
                // html 页面生成的 canvas 在 pdf 中图片的宽高(本例为:横向 a4 纸[841.89,592.28],纵向需调换尺寸)let imgWidth = 841.89;
                let imgHeight = 841.89 / contentWidth * contentHeight;
                // console.log('pdf 宽 - 高 -pageHeight--imgHeight',contentWidth,contentHeight,pageHeight,imgHeight);
                let pageData = canvas.toDataURL('image/jpeg', 1.0);
                let PDF = new JsPDF('l', 'pt', 'a4');
                // 两个高度需要区分: 一个是 html 页面的实际高度,和生成 pdf 的页面高度
                // 当内容未超过 pdf 一页显示的范围,无需分页
                if (leftHeight  0) {PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
                        leftHeight -= pageHeight;
                        position -= 592.28;
                        // 避免添加空白页
                        if (leftHeight> 0) {PDF.addPage();
                        }
                    }
                }
                PDF.save(title + '.pdf')
            })
        }
    }
}

引入和使用(这里是全局注册了的)

main.js

import htmlToPdf from '@/utils/htmlToPdf';

Vue.use(htmlToPdf);

 页面使用该组件:


  打印


printDiv() {this.$nextTick(() => {this.getPdf(this.exportPDFtitle);
      });
    },

vue 导出 PDF(vue2,插件:pdfmake,表格)

四. pdfmake 导出表格

pdfMake 文档:

先解决 下载安装 问题:(版本:”pdfmake”: “^0.2.10”, 我是直接下载默认最新的版本)

npm install pdfmake  –save   

下载字体和导入字体:

找到 node_modules 的 pdfmake (下载的插件) 文件,新建 examples/fonts 文件夹,导入需要的字体,这里的字体需要去找对应的字体文件,我的字体是本机电脑找的,我在我的 C 盘找到一些,这里用的是 仿宋

vue 导出 PDF(vue2,插件:pdfmake,表格)

vue 导出 PDF(vue2,插件:pdfmake,表格)

把这个文件复制,下载,放到下面 examples/fonts 文件夹下,复制出来的名字是大写,我改为:simfang.ttf 小写

vue 导出 PDF(vue2,插件:pdfmake,表格)

控制台定位到 pdfmake 文件夹下,运行命令:node build-vfs.js “./examples/fonts”

vue 导出 PDF(vue2,插件:pdfmake,表格)

这样就算完成了(可以导入多种字体,我这里没有字体要求,只用了仿宋)

前置问题和解决方案:pdfmake 要求的表格的内容传入需要时一个个的对象数组, 如下图:(红色框是我完成需求用到的一些属性)

要解决的问题:

1). 之前嵌套的数组(树结构)要导出为一维数组(我发现导出的表格只需要 4 列,而且是固定的,只需要给每一条的数据固定 4 个属性就可以,表头也可以单独写,非常方便)

下面是 嵌套的数组转为一维数组的代码(使用递归)

// 嵌套的多维数组 生成 一维数组
    flattenData(staffJieyuList) {
      // 初始化一维数组
      const flattenedData = [];

      // 递归函数,处理对象及其子数组
      function processObject(obj) {
        const flattenedObj = {
          id: obj.id || "", // 如果 id 不存在,则用空字符串填充
          name: obj.name || "", // 如果 name 不存在,则用空字符串填充
          amount: obj.amount || "", // 如果 amount 不存在,则用空字符串填充
          contractNum: obj.contractNum || "", // 如果 contractNum 不存在,则用空字符串填充
        };

        // 将当前对象添加到一维数组中
        flattenedData.push(flattenedObj);

        // 处理子数组
        if (obj.children && obj.children.length> 0) {obj.children.forEach((child) => {processObject(child);
          });
        }
      }

      // 遍历主列表中的每个对象
      staffJieyuList.forEach((item) => {processObject(item);
      });

      // 返回转换后的一维数组
      return flattenedData;
    },

当然上面的代码可以拆分为两个函数:(函数都在 methods: {} 中)

// 辅助函数,处理对象及其子数组
processObject(obj, flattenedData) {
  const flattenedObj = {
    id: obj.id || "",
    name: obj.name || "",
    amount: obj.amount || "",
    contractNum: obj.contractNum || "",
  };

  // 将当前对象添加到一维数组中
  flattenedData.push(flattenedObj);

  // 处理子数组
  if (obj.children && obj.children.length> 0) {obj.children.forEach((child) => {this.processObject(child, flattenedData);
    });
  }
}

// 调用辅助函数来生成一维数组
flattenDataWithHelper(staffJieyuList) {
  // 初始化一维数组
  const flattenedData = [];

  // 遍历主列表中的每个对象
  staffJieyuList.forEach((item) => {this.processObject(item, flattenedData);
  });

  // 返回转换后的一维数组
  return flattenedData;
}

2). 表头是 3 行,直接用下图的属性 headerRows: 3, 表头占一行,可以直接:colSpan: 4, 其余的列用{} 来占位

[{text: 我是表头内容, colSpan: 4,}, {}, {}, {}],

3). 颜色,背景色,文字显示位置,需要  alignment: ‘center’, fillColor: ‘#91aadf’ 等属性

vue 导出 PDF(vue2,插件:pdfmake,表格)

页面使用:(全局跟上面一样引入,这里是单独页面使用)

import pdfMake from "pdfmake/build/pdfmake";
import pdfFonts from "pdfmake/build/vfs_fonts";
pdfMake.fonts = {
  simfang: {
    normal: "simfang.ttf",
    bold: "simfang.ttf",
    italics: "simfang.ttf",
    bolditalics: "simfang.ttf",
  },
};
pdfMake.vfs = pdfFonts.pdfMake.vfs;

打印按钮:


  打印 PDF

下面附上完整的 pdfmake 导出表格的代码:

printDiv() {
      // 调用函数,获得所需的一维数组
      const flattenedArray = this.flattenData(this.staffJieyuList);
      console.log("flattenedArray----", flattenedArray);

      var fileName = this.exportPDFtitle + ".pdf";

      // 匹配大写字母、大写阿拉伯数字或者特定的中文字符的正则表达式
      const pattern = /^(附 |[A-Z]|[一二三四五六七八九十])+$/;
      // 除了 1 - 3 行,还有下面匹配的这些 文字居中,项目这里列的其他内容要往左
      const pattern2 = /^([一二三四五六七八九十])+$/
      const docDefinition = {
        content: [
          {
            table: {
              headerRows: 3,
              widths: [50, "auto", 60, "auto"],
              body: [[{ text: this.subTitle, colSpan: 4, alignment: 'center', fillColor: '#91aadf'}, {}, {}, {}],
                [{text: this.searchInformation.lysuoName, colSpan: 4, alignment: 'left', fillColor: '#91aadf'}, {}, {}, {}],
                [{ text: "序号",  alignment: 'center', fillColor: '#91aadf'},
                {text: "项目",  alignment: 'center', fillColor: '#91aadf'},
                {text: "金额",  alignment: 'center', fillColor: '#91aadf'},
                {text: "备注",  alignment: 'center', fillColor: '#91aadf'},
                ],
                ...flattenedArray.map((item) => {
                  const backgroundColor =
                  pattern.test(item.id) ? "#d9e1f4" : "#ffffff";
                  const fontPosition = 
                  pattern2.test(item.id) ? "center" : "left"
                  return [{ text: item.id, fillColor: backgroundColor, alignment: 'center',},
                    {text: item.name, fillColor: backgroundColor, alignment: fontPosition,},
                    {text: item.amount, fillColor: backgroundColor, alignment: 'center',},
                    {text: item.contractNum, fillColor: backgroundColor, alignment: 'center',},
                  ];
                }),
              ],
            },
          },
        ],
        defaultStyle: {font: "simfang",},
      };
      pdfMake.createPdf(docDefinition).download(fileName);
    },

效果:

vue 导出 PDF(vue2,插件:pdfmake,表格)

 vue 导出 PDF(vue2,插件:pdfmake,表格)

小结:用 pdfmake 插件制作 pdf 确实方便,而且样式等配置也比较齐全,(上面写的不好的地方欢欢迎指正,相互讨论学习)纯前端用 XLSX 和 XLSXS 制作导出 excel 之前没有记录,有空再做记录

原文地址: vue 导出 PDF(vue2,插件:pdfmake,表格)

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-10-27发表,共计6620字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)