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

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

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

两种导出pdf清晰度对比:

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

               

 

 

 

方式一: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 文件 ,内容如下:

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">\n' +
                    ' <title> ' + fileName + '</title>\n' +
                    ' <link rel="shortcut icon" href="http://192.168.29.50:8081/favicon.ico" type="image/x-icon" />\n' +
                    ' <meta name="format-detection" content="telephone=no">\n' +
                    ' <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">\n' +
                    ' <meta name="viewport" content="width=device-width,initial-scale=1.0">\n' +
                    ' <link rel="stylesheet" href="./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,传送门

 

 大致就是这样子了。

 

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

 参考文章:

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

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

JS操作iframe元素.     

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

转载于:https://www.cnblogs.com/taohuaya/p/10805585.html

Logo

前往低代码交流专区

更多推荐