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();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/369313.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

90.网游逆向分析与插件开发-游戏窗口化助手-项目需求与需求拆解

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;实现物品使用策略的功能-CSDN博客 项目需求&#xff1a; 在游戏窗口化时&#xff0c;可以在游戏之外弹出一个窗口&#xff0c;可以隐藏或者显示游戏窗口&#xff0c;显示游戏人物的基本状态&#xff…

天地伟业接入视频汇聚/云存储平台EasyCVR详细步骤

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

Quartus IP学习之ISSP(In-System Sources Probes)

一、ISSP IP概要&#xff1a; ISSP&#xff1a;In-System Sources & Probes Intel FPGA IP 作用&#xff1a; 分为In-System Sources与In-System Probesn-System Sources&#xff0c;输入端&#xff0c;等价于拨码开关&#xff0c;通过输入板载FPGA上的拨码开关状态改变…

Python基础知识:Python序列以及序列的索引、切片、相乘和相加

索引 索引就是序列中的每个元素所在的位置&#xff0c;可以通过从左往右的正数索引&#xff0c;也可以通过从右往左的负数索引。 从左往右的正数索引&#xff1a;在python序列中&#xff0c;第一个元素的索引值为0&#xff0c;第二个元素的索引值为1&#xff0c;以此类推&…

urllib2和cookielib的线程安全性

使用 urllib2 和 cookielib 发送 HTTP 请求和处理 cookies 的一些基本技巧。你可以根据具体需求进一步定制和扩展这些代码。注意&#xff0c;在 Python 3 中&#xff0c;urllib2 被拆分成 urllib.request 和 http.cookiejar 模块&#xff0c;但使用方式类似。下面就是我遇到的这…

Docker存储空间清理

不知不觉服务器存储空间被Docker掏空了… 查看Docker空间占用情况 使用docker system df命令&#xff0c;可以加 -v 查看详情 清理Docker不需要的内容 使用docker system prune -a命令清理Docker 所有停止的容器所有没有被使用的networks所有没容器的镜像所有build cache …

手工方式安装19.22RU

使用手工方式打RU19.22 参考文档&#xff1a; Supplemental Readme - Grid Infrastructure Release Update 12.2.0.1.x / 18c /19c (Doc ID 2246888.1) 操作步骤&#xff1a; 1 Stop the CRS managed resources running from DB homes. 2 Run the pre root script. 3 Patch G…

Korean Male Character- 19th Century

韩国男性角色- 19世纪资产包! 此包包含5个独特的角色模型和8个动画资源。 5个角色模型 1. 韩国男性_1 2. 韩国男选手_2 3. 韩国男_3 4. 韩国男式_框架 5. 韩国男选手_Ricksaw 共 8 个动画 :■ 站 ■ 步行 ■ 前俯 ■ 坐 ■ 木架_展台 ■ 独木桥_行走 ■ 框架_支架 ■…

Juc06_Lock8锁问题、字节码层面看Synchronized、反编译synchronized锁

1、Lock8 8锁问题 标准访问有 a、b 两个线程&#xff0c;请问先打印邮件还是短信sendEmail 方法暂停 3 秒钟&#xff0c;请问先打印邮件还是短信新增一个普通的 hello 方法&#xff0c;请问先打印邮件还是 hello有两部手机&#xff0c;请问先打印邮件还是短信两个静态同步方法…

中国大学生计算机设计大赛与大数据应用主题赛

中国大学生计算机设计大赛 与大数据应用主题赛 中国大学生计算机设计大赛&#xff08;简称“大赛”或4C&#xff09;始筹于2007年&#xff0c;首届于2008年&#xff0c;已经举办了16届80场赛事。是我国高校面向本科生最早的赛事之一&#xff0c;由教育部计算机类教指委发起举…

CTF-MISC图片求正确宽高破解和修改总结

CTF-MISC图片求正确宽高破解和修改总结 文章目录 CTF-MISC图片求正确宽高破解和修改总结图片宽高破解根据像素求宽高脚本求正确宽高 如何修改宽高010 Editor修改 图片宽高破解 根据像素求宽高 以青少年CTFmisc 简简单单的题目为例&#xff1a; 我鼠标右侧属性查看相册 右键属…

【项目日记(八)】第三层: 页缓存的具体实现(下)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:项目日记-高并发内存池⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你做项目   &#x1f51d;&#x1f51d; 开发环境: Visual Studio 2022 项目日…

国内外b2b网站大全,免费b2b网站平台有哪些?

B2B网站大全平台主要是提供各类B2B网站的免费收录和分类&#xff0c;涵盖了外贸、机械、建筑、化工、五金、电工、农林、仪器、食品、能源、服装、冶金、电子等&#xff0c;这些B2B网站可以帮助中小企业和中国制造业提供在线采购、供求信息&#xff0c;招标信息。 免费B2B网站分…

[Tomcat问题]--使用Tomcat 10.x部署项目时,出现实例化Servlet类[xxx]异常

[Tomcat问题]–使用Tomcat 10.x部署项目时&#xff0c;出现实例化Servlet类[xxx]异常 本片博文在知乎同步更新 环境 OS: Windows 11 23H2Java Version: java 21.0.1 2023-10-17 LTSIDE: IntelliJ IDEA 2023.3.3Maven: Apache Maven 3.9.6Tomcat: Tomcat 10.1.18 ReleasedSer…

2024年【安全员-B证】考试资料及安全员-B证模拟考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年安全员-B证考试资料为正在备考安全员-B证操作证的学员准备的理论考试专题&#xff0c;每个月更新的安全员-B证模拟考试题库祝您顺利通过安全员-B证考试。 1、【多选题】《中华人民共和国安全生产法》规定&#…

C语言:详解操作符(上)

摘要&#xff1a; 本篇&#xff0c;我们将学习C语言中操作符的相关内容&#xff0c;操作符是C语言中重要的元素之一&#xff0c;在我们的代码中处处都有&#xff0c;下面我们将详细介绍操作符的相关内容&#xff0c;并结合一些代码例题加深印象。 目录 一、操作符的分类及介绍 …

黑马头条D1 导学

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 知…

ChatGPT实战100例 - (15) 还不会写 Stable Diffusion (SD) 绘画提示词?没关系,ChatGPT帮你搞定

文章目录 ChatGPT实战100例 - (15) 还不会写 Stable Diffusion (SD) 绘画提示词&#xff1f;没关系&#xff0c;ChatGPT帮你搞定一、把场景描述转为镜头语言二、把镜头语言转换为Prompt三、把Prompt转换为图片 ChatGPT实战100例 - (15) 还不会写 Stable Diffusion (SD) 绘画提示…

指针详解(3)

各位少年&#xff0c;大家好&#xff0c;我是博主那一脸阳光&#xff0c;今天介绍 二级指针 指针数组&#xff0c;还有个指针数组模拟二维数组。 前言&#xff1a;在浩瀚的C语言编程宇宙中&#xff0c;指针犹如一把打开内存世界大门的独特钥匙&#xff0c;它不仅是理解程序运行…

牛客寒假算法基础集训营1 E本题主要考察了贪心

示例1 输入 3 4 3 2 4 5 8 1 2 1 4 2 4 3 1 3 1 1 2 3 6 6 1 2 3 4 5 6 2 3 2 3 3 4 4 5 5 6 6 1 输出 1 1 4 #include<bits/stdc.h> using namespace std; int u0,m,n; int num100,tempnum1;//tempnum表示当前这一组合鸡的排名&#xff0c;num是最终排名 pair<i…