vue项目中导出PDF的两种方式

摘要:
button@click=“handleDown”>通过jsPDF<下载;通过浏览器下载&lt//获取容器宽度leteleH=ele.offsetHeight//获取容器的高度leteleOffsetTop=ele.offetTop;高倍率两倍画布。高度=标高*2;

参考大家导出的方式,基本上是如下两种:

1.使用 html2Canvas + jsPDF  导出PDF, 这种方式什么都好,就是下载的pdf太模糊了。对要求好的pdf这种方式真是不行啊!

2.调用浏览器自身的方法。window.print() 来打印(打印时可选下载),这种方式打印出来很清楚,但纯在浏览器兼容问题。
谷歌浏览器比较好用点。

两种导出pdf清晰度对比:

--------------左边 html2canvas + jspdf;-----------------------------------------------右边window.print() 浏览器方式------------

 vue项目中导出PDF的两种方式第1张              vue项目中导出PDF的两种方式第2张

方式一:html2canvas+jspdf

html2Canvas + jsPDF 导出普遍存在一个问题就是不太清楚的问题。目前我没有在网上找到合适 这种方式打印出清晰的PDF解决方案。

如果不是要求太高的这种方式还是可以用的。

这种方式实现的方法:

首先npm 下载两插件

npm i html2canvas jspdf --save-dev

在.vue文件中定义 要到导出 DOM 元素

<template>
    <div class="pdf-demo">

        <button @click="handleDown">jsPDF方式下载</button>
        <button @click="handleWindowPrint( '#demo', '电子合同' )">浏览器方式下载</button>

        <div id="demo" >

                #demo 中的内容导出成 PDF


        </div>


    </div>
</template>        

然后创建一个 htmlToPdf.js 文件 ,内容如下:

vue项目中导出PDF的两种方式第3张vue项目中导出PDF的两种方式第4张
import html2canvas from 'html2canvas';
import JsPDF from 'jspdf';

/**
 * @param  ele          要生成 pdf 的DOM元素(容器)
 * @param  padfName     PDF文件生成后的文件名字
 * */

function downloadPDF(ele, pdfName){

    let eleW = ele.offsetWidth;// 获得该容器的宽
    let eleH = ele.offsetHeight;// 获得该容器的高


    let eleOffsetTop = ele.offsetTop;  // 获得该容器到文档顶部的距离
    let eleOffsetLeft = ele.offsetLeft; // 获得该容器到文档最左的距离

    var canvas = document.createElement("canvas");
    var abs = 0;

    let win_in = document.documentElement.clientWidth || document.body.clientWidth; // 获得当前可视窗口的宽度(不包含滚动条)
    let win_out = window.innerWidth; // 获得当前窗口的宽度(包含滚动条)

    if(win_out>win_in){
        // abs = (win_o - win_i)/2;    // 获得滚动条长度的一半
        abs = (win_out - win_in)/2;    // 获得滚动条宽度的一半
        // console.log(a, '新abs');
    }

    canvas.width = eleW * 2;    // 将画布宽&&高放大两倍
    canvas.height = eleH * 2;




    var context = canvas.getContext("2d");

    context.scale(2, 2);

    context.translate(-eleOffsetLeft -abs, -eleOffsetTop);
    // 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此
    // translate的时候,要把这个差值去掉

    // html2canvas(element).then( (canvas)=>{ //报错
    // html2canvas(element[0]).then( (canvas)=>{
    html2canvas( ele, {
        dpi: 300,
        // allowTaint: true,  //允许 canvas 污染, allowTaint参数要去掉,否则是无法通过toDataURL导出canvas数据的
        useCORS:true  //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。
    } ).then( (canvas)=>{

        var contentWidth = canvas.width;
        var contentHeight = canvas.height;
        //一页pdf显示html页面生成的canvas高度;
        var pageHeight = contentWidth / 592.28 * 841.89;
        //未生成pdf的html页面高度
        var leftHeight = contentHeight;
        //页面偏移
        var position = 0;
        //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
        var imgWidth = 595.28;
        var imgHeight = 595.28/contentWidth * contentHeight;

        var pageData = canvas.toDataURL('image/jpeg', 1.0);



        var pdf = new JsPDF('', 'pt', 'a4');

        //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
        //当内容未超过pdf一页显示的范围,无需分页
        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();
                }
            }
        }

        //可动态生成
        pdf.save(pdfName);
    })


}


export default {
    downloadPDF
}
View Code

在 要导出 的PDF的.vue文件中, 如下是使用

引入:

import htmlToPdf from 'yourPath /htmlToPdf';

点击     jsPDF方式下载  按钮 执行如下函数,即可导出pdf

        methods: {

            handleDown(){
                //导出PDF
                htmlToPdf.downloadPDF( document.querySelector('#demo'),'我的PDF');
            },


        }    

方式二:window.print()方法

  调用浏览的window.print() 方法, 这种方式实现的主要思路为: 点击 打印(下载)按钮, 

>将要<body>标签内的内容替换成要打印的 DOM元素, 这样就可以避免打印 不想要打印的内容了。(如果项目中使用的UI框架,此时<head>标签内会自动移入一些样式,最后打印时把head内的不必要的内容也替换掉。)

>然后通过setTimeout()方法异步 调用 window.print(), 因为如果你要打印的 DOM元素 不是写的 行内样式 的话浏览器渲染DOM时,是需要时间的,如果直接同步调取 window.print() 方法会造成 内部样式表和外部样式表的样式丢失。

>调取完 打印后 使用 window.location.reload() 刷新页面,恢复body 、head 表情内的内容。

具体实现代码如下:

点击    浏览器方式下载  按钮 执行如下函数,即可导出pdf(打印界面可以选择  另存为PDF文件)

        methods: {

            //浏览器方式打印
            //ele 为  css ID选择器
            //fileName    生成的PDF文件名
            handleWindowPrint(ele, fileName){

  
                //去除页面不必要的 head 标签内  内容, 避免影响打印页 , title 为保存为 pdf 的文件时的 文件名
                document.head.innerHTML = '<meta charset="utf-8">
' +
                    ' <title> ' + fileName + '</title>
' +
                    ' <link rel="shortcut icon" href="http://192.168.29.50:8081/favicon.ico" type="image/x-icon" />
' +
                    ' <meta name="format-detection" content="telephone=no">
' +
                    ' <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
' +
                    ' <meta name="viewport" content="width=device-width,initial-scale=1.0">
' +
                    ' <link rel="stylesheet" href="http://t.zoukankan.com/static/css/contract.css"/>';  //生成pdf的外部css样式
               
                //要打印的 内容 html
                document.body.innerHTML =  document.querySelector( ele ).outerHTML;

                // window.print();

                //转异步 等待dom元素渲染(样式)完毕在打印
                setTimeout( ()=>{
                    //打印
                    window.print();
                    //刷新页面
                    window.location.reload();
                },20 )

            },

        },       

但是上面的方法会破坏掉vue的机制,打印完完毕后必须刷新页面才能恢复。所以通过动态创建iframe来打印(推荐的方法):

修改后的js代码:

let handleWindowPrint = (ele, fileName, vm)=>{

    var oIframe = document.createElement('iframe');
    var oScript = document.createElement('script');

    document.body.appendChild(oIframe);

    var titleText = document.head.getElementsByTagName('title')[0].innerText;
    document.head.getElementsByTagName('title')[0].innerText = `${fileName}`;

    oIframe.contentDocument.head.innerHTML = `<meta charset="utf-8">
                                              <title>${fileName}</title>
                                              <meta name="format-detection" content="telephone=no">
                                              <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
                                              <meta name="viewport" content="width=device-width,initial-scale=1.0">`;

    // oIframe.contentWindow.document.body.innerHTML = document.querySelector(ele).outerHTML;
    oIframe.contentDocument.body.innerHTML = document.querySelector(ele).outerHTML;
    oScript.innerHTML = 'window.print();';
    oIframe.contentDocument.body.appendChild(oScript);
    document.body.removeChild(oIframe);
    document.head.getElementsByTagName('title')[0].innerText = titleText;

};

export default {
    handleWindowPrint
}

这样打印完毕后,就不会影响网页的使用。

后来发现有人写vue插件了vue-print-nb,传送门

 

 大致就是这样子了。

vue项目中导出PDF的两种方式第5张

github  Demo 地址(后来没有增加iframe方法,请自行更换):   https://github.com/Ta0hua/vue-pdf-demo

 参考文章:

window.print()局部打印三种方式

window.print()打印页面指定内容(使用iframe保证原页面不失效)

JS操作iframe元素.     

总结iframe onload事件无法监听的问题

免责声明:文章转载自《vue项目中导出PDF的两种方式》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇vant upload图片上传 (file文件)文件上传漏洞(绕过姿势)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

JAVASCRIPT加密方法,JS加密解密综述(7种)

一:最简单的加密解密 对于JAVASCRIPT函数escape()和unescape()想必是比较了解啦(很多网页加密在用它们),分别是编码和解码字符串,比如例子代码 用escape()函数加密后变为如下格式: alert%28%22%u9ED1%u5BA2%u9632%u7EBF%22%29%3B 如何?还看的懂吗?当然其中的ASCII字符"alert...

Spring-boot 数据源 事务 多数据源 以及 多数据源事务 问题 简单笔记

<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>${dynamic.versio...

使用com.aspose.words将word模板转为PDF乱码解决方案(window下正常)

  最近在做电子签名过程中,需要将合成的电子签名的word文件(正常)转换为pdf文件时,在开发平台window下转换没有问题,中文也不会出现乱码。但是将项目部署到正式服务器(Linux)上,转换出来的pdf中文就出现乱码。在网上找了很久,才找到原因,现在将解决办法分享给大家 1、问题原因分析   在windos下没有问题但是在Linux下有问题,就说明不...

Shell脚本开发环境的配置和优化实践

1. 配置vim编辑器 1-1. 为什么不使用vi而是vim vi适合编辑普通文本,不适用编写脚本代码,例如:缺少高亮显示代码、自动缩进等重要功能; vim相当于高级编辑器,可以提高开发效率。 1-2. 设置vim为默认编辑器 [root@oldboy scripts]# echo 'alias vi=vim' >>...

新浪实时股票数据接口1

股票数据的获取目前有如下两种方法可以获取:1. http/javascript接口取数据 2. web-service接口 1.http/javascript接口取数据 1.1Sina股票数据接口 以大秦铁路(股票代码:601006)为例,如果要获取它的最新行情,只需访问新浪的股票数据接口:http://hq.sinajs.cn/list=sh601006...

【HDOJ】3686 Traffic Real Time Query System

这题做了几个小时,基本思路肯定是求两点路径中的割点数目,思路是tarjan缩点,然后以割点和连通块作为新节点见图。转化为lca求解。结合点——双连通分量与LCA。 1 /* 3686 */ 2 #include <iostream> 3 #include <sstream> 4 #include <string...