vue中使用html2canvas配合jspdf导出pdf(以及在导出时遇到的导出样式问题)

指定页面中导出为pdf格式并打包,使用html2canvas先转为图片格式,在利用jspdf转为pdf,最后下载打包为本地压缩包

yarn add html2canvas
yarn add jspdf
1. 注册一个插件并挂载
import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
export default {
    install(Vue, options) {
        Vue.prototype.getPdf = function (dom) {
            return new Promise((resolve, reject) => {
                html2Canvas(dom.$el, {
                    allowTaint: true,
                    scale: 2,
                    dpi: 300,
                }).then(function (canvas) {
                    let contentWidth = canvas.width;
                    let contentHeight = canvas.height;
                    let pdfWidth = 595.28; // A4 width in pixels
                    let pdfHeight = (pdfWidth / contentWidth) * contentHeight;
                    let pageData = canvas.toDataURL('image/jpeg', 1.0);

                    let PDF = new JsPDF('p', 'pt', 'a4');
                    let scale = pdfWidth / contentWidth;
                    PDF.addImage(pageData, 'JPEG', 0, 0, pdfWidth, pdfHeight);
                    let remainingHeight = pdfHeight;

                    while (remainingHeight < contentHeight) {
                        PDF.addPage();
                        let offsetY = -remainingHeight * scale;
                        PDF.addImage(pageData, 'JPEG', 0, offsetY, pdfWidth, pdfHeight);
                        remainingHeight += pdfHeight;
                    }
                    PDF.save('export.pdf'); //直接下载
                    // resolve(PDF.output('blob')); //转为blob格式 返回合并下载
                }).catch(reject);
            });
        };
    }
}
2. 页面使用
DownPDFAndFile(){
  this.getPdf('传入ref或者是id')
}

以上正常导出步骤 封装方法 使用 但是如果你的项目中用的是rem布局或者是适配方法,样式或者字体没有显示出来被盖掉请往下看

3.由于在项目中用rem做了适配导致样式变形

在这里插入图片描述

4. 我们可以在html2canvas生成图片时把对应的样式或者添加类名,在生成图片前修改样式,转换为pdf后恢复初始样式
import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
/**
 * @param {*} 添加样式类名
 */
function removeChildrenWithClass(parent, className) {
    var children = parent.childNodes;
    if (parent.classList && parent.classList.contains('form_view_item')) {
        parent.childNodes.forEach((item) => {
            item.className += ' is_to_print'
        })
    }
    for (var i = 0; i < children.length; i++) {
        if (children[i].classList && children[i].classList.contains('isprint')) {
            children[i].style.display = 'block'
            if (children[i].classList.contains('is_toprint')) children[i].style.display = 'block'
        }
        if (children[i].classList && children[i].classList.contains('form_view_title')) {
            children[i].style.marginTop = '25px'
        }
        if (children[i].classList && children[i].classList.contains(className)) {
            children[i].style.display = 'none'
        } else if (children[i].childNodes) {
            removeChildrenWithClass(children[i], className);
        }
    }
}
/**
 * @param {*} 恢复初始样式
 */
function restoreOriginalState(parent, className) {
    var children = parent.childNodes;
    if (parent.classList && parent.classList.contains('form_view_item')) {
        parent.childNodes.forEach((item) => {
            item.classList.remove('is_to_print');
        });
    }
    for (var i = 0; i < children.length; i++) {
        if (children[i].classList && children[i].classList.contains(className)) {
            children[i].style.display = '';
        }
        if (children[i].classList && children[i].classList.contains('form_view_title')) {
            children[i].style.marginTop = '';
        }
        if (children[i].classList && children[i].classList.contains('isprint')) {
            children[i].style.display = 'none';
        }
        if (children[i].childNodes) {
            restoreOriginalState(children[i], className);
        }
    }
}
export default {
    install(Vue, options) {
        Vue.prototype.getPdf = function (dom) {
            return new Promise((resolve, reject) => {
                removeChildrenWithClass(dom.$el, 'noprint');
                html2Canvas(dom.$el, {
                    allowTaint: true,
                    //如果不要求清晰度可以去掉
                    scale: 2,  //按比例增加分辨率 
                    dpi: 300, //将分辨率提高到特定的 DPI
                }).then(function (canvas) {
                    restoreOriginalState(dom.$el, 'noprint')
                    let contentWidth = canvas.width;
                    let contentHeight = canvas.height;
                    let pdfWidth = 595.28; // A4 width in pixels
                    let pdfHeight = (pdfWidth / contentWidth) * contentHeight;
                    let pageData = canvas.toDataURL('image/jpeg', 1.0);

                    let PDF = new JsPDF('p', 'pt', 'a4');
                    let scale = pdfWidth / contentWidth;
                    PDF.addImage(pageData, 'JPEG', 0, 0, pdfWidth, pdfHeight);
                    let remainingHeight = pdfHeight;

                    while (remainingHeight < contentHeight) {
                        PDF.addPage();
                        let offsetY = -remainingHeight * scale;
                        PDF.addImage(pageData, 'JPEG', 0, offsetY, pdfWidth, pdfHeight);
                        remainingHeight += pdfHeight;
                    }
                    PDF.save('export.pdf'); //直接下载
                    // resolve(PDF.output('blob')); //转为blob格式 返回合并下载
                }).catch(reject);
            });
        };
    }
}
5. 修改完后导出正常

在这里插入图片描述

6. 如果需求是直接下载以下就忽略,如果需求是打包为本地压缩包请继续
// 引入使用attachDownload方法
// 下载事件
async DownPDFAndFile() {
      const pdfinfo = await this.getPdf('传入ref或者是id')
      const pdfList = [
      //参数字段自行修改 为attachDownload 中的字段对应
				  {
				     id: "...",
				     data: pdfinfo,
				  },
      	];
      const ImageList = []
      const VideoList = []
      const ImageAndVideoAndPdfList = [...pdfList,...ImageList,...VideoList]
      const { downloadStatus } = await this.is_downFile(ImageAndVideoAndPdfList);
      // downloadStatus 这个状态为下载状态 true为完成 可以自行添加业务
    },
// 下载方法
async is_downFile(list) {
      if (list.length > 0) {
        const config = {
          downloadList: list,
          suffix: "病历编辑.zip",
        };
        const { downloadStatus } = await attachDownload(config);
        return { downloadStatus };
      }
    },
7. 打包下载使用的是JSZip 和 FileSaver
yarn add jszip
yarn add file-saver
// attachDownload.js
import JSZip from "jszip";
import FileSaver from "file-saver";
export async function attachDownload(config) {
    const { downloadList, suffix } = config
    const zip = new JSZip();
    const cache = {};
    let downloadStatus = false
    const downloadPromises = downloadList.map(async (item) => {
        try {
            if (item.url) {
                let data;
                if(item.type=='.pdf'){
                    data = item.data;
                }else{
                    data = await getImgArrayBuffer(item.url);
                }
                zip.folder(suffix).file(`${item.Title}_${item.FileID}` + item.type, data, { binary: true });
                cache[item.id] = data;
            } else {
                throw new Error(`文件${item.fileName}地址错误,下载失败`);
            }
        } catch (error) {
            console.error("文件获取失败", error);
        }
    });
    try {
        await Promise.all(downloadPromises);
        const content = await zip.generateAsync({ type: "blob" });
        FileSaver.saveAs(content, suffix);
        downloadStatus = true
        return {
            downloadStatus
        }
    } catch (error) {
        console.error("文件压缩失败", error);
    }
}
async function getImgArrayBuffer(url) {
    const response = await fetch(url);
    if (!response.ok) {
        throw new Error(`请求失败: ${response.status}`);
    }
    return await response.blob();
}
  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值