赞
踩
注:文章为记录为主,想直接看pdfmake 打印的请划到最下面部分
页面有一个表格,之前导出excel 表(用XLSX和XLSXS制作了样式之类的表格),现在需要制作带样式的pdf,而且格式如下,并且最上面的三行需要在每一页的顶部出现作为表头
效果图,(换页前面三行也会有)
script 要打印的盒子:ref="printSection" ,这个表格是用 el-table 来写的,而且表格中存在嵌套children,一共嵌套了3层
如上图:大写数字(一、二)是第一层,附和大写字母(A、B)是第二层,其他(没有序号的的行)是第三层
- <div id="pdfDom" ref="printSection">
- <el-table
- empty-text="暂无报告"
- v-loading="loading"
- :data="staffJieyuList"
- @selection-change="handleSelectionChange"
- default-expand-all
- row-key="row"
- :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
- >
- <el-table-column align="center" :label="subTitle">
- <el-table-column :label="searchInformation.lysuoName">
- <el-table-column prop="id" align="center" label="序号">
- </el-table-column>
- <el-table-column prop="name" align="center" label="项目">
- </el-table-column>
- <el-table-column prop="amount" align="center" label="金额">
- </el-table-column>
- <el-table-column prop="contractNum" align="center" label="备注">
- </el-table-column>
- </el-table-column>
- </el-table-column>
- </el-table>
- </div>

上面表格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;
- }),
- },
- ],
- },
- 第二个数组...
- ]

我要打印的主要是四列:
序号id 、项目name、金额amount、备注contractNum(打印出来的看最上面的图)
打印可以直接调用: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;
- });
- }
效果图:
- 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('<style media="print">@page {size: landscape;}</style>');
- // 向 iframe 中注入 printContent 样式
- iframeDoc.write(
- `<link href="./print.css" media="print" rel="stylesheet" />`
- );
- // 写入内容
- iframeDoc.write("<div>" + printContent + "</div>");
- setTimeout(function () {
- iframe.contentWindow.print();
- document.body.removeChild(iframe);
- }, 50);
- },

样式没研究,打印出来的效果图如下,连边框线也没加,更别说需要算哪里的背景色,字体居中和靠左了
用 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 < pageHeight) {
- PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
- } else {
- while (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);
页面使用该组件:
- <el-col :span="1.5">
- <el-button type="primary" plain icon="" size="mini" @click="printDiv" >打印</el-button>
- </el-col>
-
- printDiv() {
- this.$nextTick(() => {
- this.getPdf(this.exportPDFtitle);
- });
- },
pdfMake 文档:
先解决下载安装问题:(版本:"pdfmake": "^0.2.10",我是直接下载默认最新的版本)
npm install pdfmake --save
下载字体和导入字体:
找到 node_modules 的pdfmake (下载的插件) 文件,新建 examples/fonts文件夹,导入需要的字体,这里的字体需要去找对应的字体文件,我的字体是本机电脑找的,我在我的C盘找到一些,这里用的是 仿宋
把这个文件复制,下载,放到下面examples/fonts文件夹下,复制出来的名字是大写,我改为:simfang.ttf 小写
控制台定位到pdfmake文件夹下,运行命令:node build-vfs.js "./examples/fonts"
这样就算完成了(可以导入多种字体,我这里没有字体要求,只用了仿宋)
前置问题和解决方案: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' 等属性
页面使用:(全局跟上面一样引入,这里是单独页面使用)
- 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;
打印按钮:
- <el-col :span="1.5">
- <el-button type="primary" plain icon="" size="mini" @click="printDiv" >打印PDF</el-button>
- </el-col>
下面附上完整的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);
- },

效果:
小结:用pdfmake插件制作pdf确实方便,而且样式等配置也比较齐全,(上面写的不好的地方欢欢迎指正,相互讨论学习)纯前端用XLSX和XLSXS制作导出excel 之前没有记录,有空再做记录
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。