HTML导出PDF以及解决内容被分割问题

开发环境:vue cil3.0脚手架,nodejs,npm

1、安装插件:html2canvas、jspdf

npm install html2canvas --save
npm install jspdf --save

2、新建js文件,命名jspdf.js

// 导出页面为PDF格式
import html2Canvas from 'html2canvas';
import JsPDF from 'jspdf';

export default {
    install(Vue, options) {
        Vue.prototype.getPdf = function (title, dom) {
            // 注册getPdf方法,传入两个参数,此处使用了promise处理导出后的操作
            /*
            title: 导出文件名
            dom: 需要导出dom的id
             */
            return new Promise((resolve, reject) => {
                html2Canvas(document.querySelector(dom), {
                    useCORS: true, // 由于打印时,会访问dom上的一些图片等资源,解决跨域问题!!重要
                    allowTaint: true // 允许跨域
                }).then(function (canvas) {
                    let contentWidth = canvas.width;
                    let contentHeight = canvas.height;
                    // 根据A4纸的大小,计算出dom相应比例的尺寸
                    let pageHeight = contentWidth / 592.28 * 841.89;
                    let leftHeight = contentHeight;
                    let position = 0;
                    let imgWidth = 595.28;
                    // 根据a4比例计算出需要分割的实际dom位置
                    let imgHeight = 592.28 / contentWidth * contentHeight;
                    // canvas绘图生成image数据,1.0是质量参数
                    let pageData = canvas.toDataURL('image/jpeg', 1.0);
                    // a4大小
                    let PDF = new JsPDF('', 'pt', 'a4');
                    // 当内容达到a4纸的高度时,分割,将一整块画幅分割出一页页的a4大小,导出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 -= 841.89;
                            if (leftHeight > 0) {
                                PDF.addPage();
                            }
                        }
                    }
                    // 导出
                    PDF.save(title + '.pdf');
                    resolve(true);
                })
                    .catch(() => {
                        reject(false);
                    });
            });
        };
    }
};

3、main.js引入

import htmlToPdf from './api/jspdf'
Vue.use(htmlToPdf)
 // 在组件中可以调用this.getPdf(title, dom)方法导出

4、组件中使用

dom结构

<div class="content" id="content">
    <div class="item">内容</div>
    <div class="item">内容</div>
    <!--    每一块dom的class类设置成item(自定义)以此处理内容分割  -->
    <div class="item">内容</div>
    <div class="item">内容</div>
</div>

组件内编写导出方法,methods方法 outPutPdfFn,(其中isSplit方法判断是否要分割)

 outPutPdfFn () {
                let vm = this;
                const A4_WIDTH = 592.28;
                const A4_HEIGHT = 841.89;
                // $myLoading 自定义等待动画组件,实现导出事件的异步等待交互
                this.$myLoading('正在导出pdf,请稍候。。。', true);
                vm.$nextTick(() => {
                    // dom的id。
                    let target = document.getElementById('content');
                    let pageHeight = target.scrollWidth / A4_WIDTH * A4_HEIGHT;
                    // 获取分割dom,此处为class类名为item的dom
                    let lableListID = document.getElementsByClassName('item');
                    // 进行分割操作,当dom内容已超出a4的高度,则将该dom前插入一个空dom,把他挤下去,分割
                    for (let i = 0; i < lableListID.length; i++) {
                        let multiple = Math.ceil((lableListID[i].offsetTop + lableListID[i].offsetHeight) / pageHeight);
                        if (this.isSplit(lableListID, i, multiple * pageHeight)) {
                            let divParent = lableListID[i].parentNode; // 获取该div的父节点
                            let newNode = document.createElement('div');
                            newNode.className = 'emptyDiv';
                            newNode.style.background = '#ffffff';
                            let _H = multiple * pageHeight - (lableListID[i].offsetTop + lableListID[i].offsetHeight);
                            newNode.style.height = _H + 30 + 'px';
                            newNode.style.width = '100%';
                            let next = lableListID[i].nextSibling; // 获取div的下一个兄弟节点
                            // 判断兄弟节点是否存在
                            console.log(next);
                            if (next) {
                                // 存在则将新节点插入到div的下一个兄弟节点之前,即div之后
                                divParent.insertBefore(newNode, next);
                            } else {
                                // 不存在则直接添加到最后,appendChild默认添加到divParent的最后
                                divParent.appendChild(newNode);
                            }
                        }
                    }
                    // 传入title和dom标签,此处是 #content
                    // 异步函数,导出成功后处理交互
                    this.getPdf('巡检报告单-' + this.formDataLabel.deviceCode + '-' + this.formDataLabel.createDate.substring(0, 10), '#content').then(() => {
                        // 自定义等待动画关闭
                        this.$myLoading(false);
                        this.$message({
                            type: 'success',
                            message: '导出成功'
                        });
                        this.detailSHow = false;
                    })
                        .catch(() => {
                            this.$myLoading(false);
                            this.$message({
                                type: 'error',
                                message: '导出失败,请重试'
                            });
                        });
                });
            }
isSplit (nodes, index, pageHeight) {
                // 计算当前这块dom是否跨越了a4大小,以此分割
                if (nodes[index].offsetTop + nodes[index].offsetHeight < pageHeight && nodes[index + 1] && nodes[index + 1].offsetTop + nodes[index + 1].offsetHeight > pageHeight) {
                    return true;
                }
                return false;
            },

5、导出结果

左边:实际dom

右边:导出效果,进行了分割

 

  • 17
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值