前端将html导出pdf文件解决分页问题
- 这是借鉴了qq_251025116大佬的解决方案并优化升级完成的,原文链接
1.安装依赖
npm install jspdf html2canvas
2.使用方法
import htmlToPdffrom './index.js'const suc = () => {message.success('success');};//记得在需要打印的div上面添加 idlet dom = document.querySelector('#testPdf');let pdf = new htmlToPdf(dom, '测试', 'splitPages', ',', suc, 2);pdf.outPutPdfFn('测试');
3.使用说明
//splitPages 是需要添加的类名,需要再那个地方分页就在那个地方添加类名 splitPages//如:<SpeciesIdentification class="splitPages" :baseData="baseData" /><SpeciesIdentificationSecond class="splitPages" :baseData="baseData" /><RepetPic class="splitPages" :baseData="baseData" /><RepetTable class="splitPages" :baseData="baseData" /><GeneralAnnotation class="splitPages" :baseData="baseData" />
导出效果:

具体代码 :
import jsPDF from "jspdf";
import html2canvas from "html2canvas";/*
* 使用说明
* ele:需要导出pdf的容器元素(dom节点 不是id)
* pdfFileName: 导出文件的名字 通过调用outPutPdfFn方法也可传参数改变
* splitClassName: 避免分段截断的类名 当pdf有多页时需要传入此参数 , 避免pdf分页时截断元素 如表格<tr class="itemClass"></tr>
* breakClassName:自定义分页符类名,默认为break_page,添加改类名的标签被自动分页到下一页
* sucCallback:成功回调函数
* scale:增强图片清晰度 数字也打越清晰
* 调用方式 先 let pdf = new htmlToPdf(ele, 'pdf' ,'itemClass');
* 若想改变pdf名称 pdf.outPutPdfFn(fileName); outPutPdfFn方法返回一个promise 可以使用then方法处理pdf生成后的逻辑
* */class htmlToPdf {constructor(ele, pdfFileName, splitClassName = "itemClass", breakClassName = "break_page", sucCallback = () => { }, scale = 2) {this.ele = ele;this.pdfFileName = pdfFileName;this.splitClassName = splitClassName;this.breakClassName = breakClassName;this.sucCallback = sucCallback;this.scale = scale;this.A4_WIDTH = 595;this.A4_HEIGHT = 842;this.pageHeight = 0this.pageNum = 1};async getPDF(resolve) {let ele = this.ele;let pdfFileName = this.pdfFileNamelet eleW = ele.offsetWidth // 获得该容器的宽let eleH = ele.scrollHeight // 获得该容器的高let eleOffsetTop = ele.offsetTop // 获得该容器到文档顶部的距离let eleOffsetLeft = ele.offsetLeft // 获得该容器到文档最左的距离let canvas = document.createElement("canvas")let abs = 0let win_in = document.documentElement.clientWidth || document.body.clientWidth // 获得当前可视窗口的宽度(不包含滚动条)let win_out = window.innerWidth // 获得当前窗口的宽度(包含滚动条)if (win_out > win_in) {abs = (win_out - win_in) / 2 // 获得滚动条宽度的一半} canvas.width = eleW * 2 // 将画布宽&&高放大两倍canvas.height = eleH * 2let context = canvas.getContext("2d")context.scale(this.scale, this.scale) // 增强图片清晰度context.translate(- eleOffsetLeft - abs, - eleOffsetTop)html2canvas(ele, {useCORS: true // 允许canvas画布内可以跨域请求外部链接图片, 允许跨域请求。}).then(async canvas => {let contentWidth = canvas.widthlet contentHeight = canvas.height// 一页pdf显示html页面生成的canvas高度;this.pageHeight = (contentWidth / this.A4_WIDTH) * this.A4_HEIGHT// 这样写的目的在于保持宽高比例一致 this.pageHeight/canvas.width = a4纸高度/a4纸宽度// 宽度和canvas.width保持一致// 未生成pdf的html页面高度let leftHeight = contentHeight// 页面偏移let position = 0// a4纸的尺寸[595,842],单位像素,html页面生成的canvas在pdf中图片的宽高let imgWidth = this.A4_WIDTH - 10 // -10为了页面有右边距let imgHeight = (this.A4_WIDTH / contentWidth) * contentHeightlet pageData = canvas.toDataURL("image/jpeg", 1.0)let pdf = jsPDF("", "pt", "a4");// 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)// 当内容未超过pdf一页显示的范围,无需分页if (leftHeight < this.pageHeight) { // 在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示;// pdf.addImage(pageData, "JPEG", 5, 0, imgWidth, imgHeight)pdf.addImage(pageData, 'JPEG', 6, 40, imgWidth - 12, imgHeight - 80);} else { // 分页while (leftHeight > 0) {pdf.addImage(pageData, "JPEG", 6, position + 40, imgWidth - 12, imgHeight - 80)leftHeight -= this.pageHeightposition -= this.A4_HEIGHT// 避免添加空白页if (leftHeight > 0) {pdf.addPage()}}} pdf.save(pdfFileName + ".pdf", { returnPromise: true }).then(() => { // 去除添加的空div 防止页面混乱let doms = document.querySelectorAll('.emptyDiv')for (let i = 0; i < doms.length; i++) {doms[i].remove();}});this.ele.style.height = '';this.sucCallback()resolve();})};// 输出pdfasync outPutPdfFn(pdfFileName) {return new Promise((resolve, reject) => { // 进行分割操作,当dom内容已超出a4的高度,则将该dom前插入一个空dom,把他挤下去,分割let target = this.ele;this.pageHeight = target.scrollWidth / this.A4_WIDTH * this.A4_HEIGHT;this.ele.style.height = 'initial';pdfFileName ? this.pdfFileName = pdfFileName : null;this.pageNum = 1; // pdf页数this.domEach(this.ele)// 异步函数,导出成功后处理交互this.getPDF(resolve, reject);})};domEach(dom) {let childNodes = dom.childNodeschildNodes.forEach((childDom, index) => {if (this.hasClass(childDom, this.splitClassName)) {let node = childDom;let eleBounding = this.ele.getBoundingClientRect();let bound = node.getBoundingClientRect();let offset2Ele = bound.top - eleBounding.toplet currentPage = Math.ceil((bound.bottom - eleBounding.top) / this.pageHeight); // 当前元素应该在哪一页if (this.pageNum < currentPage) {this.pageNum++let divParent = childDom.parentNode; // 获取该div的父节点let newNode = document.createElement('div');newNode.className = 'emptyDiv';newNode.style.background = 'white';newNode.style.height = (this.pageHeight * (this.pageNum - 1) - offset2Ele + 30) + 'px'; // +30为了在换下一页时有顶部的边距newNode.style.width = '100%';let next = childDom.nextSibling;// 获取div的下一个兄弟节点// 判断兄弟节点是否存在if (next) { // 存在则将新节点插入到div的下一个兄弟节点之前,即div之后divParent.insertBefore(newNode, node);} else { // 不存在则直接添加到最后,appendChild默认添加到divParent的最后divParent.appendChild(newNode);}}}if (this.hasClass(childDom, this.breakClassName)) {this.pageNum++console.log('break_page');let eleBounding = this.ele.getBoundingClientRect();let bound = childDom.getBoundingClientRect();let offset2Ele = bound.top - eleBounding.top// 剩余高度let alreadyHeight = offset2Ele % this.pageHeightlet remainingHeight = this.pageHeight - alreadyHeight + 20childDom.style.height = remainingHeight + 'px'}if (childDom.childNodes.length) {this.domEach(childDom)}})}hasClass(element, cls) {return (`` + element.className + ``).indexOf(`` + cls + ``) > -1;}
}export default htmlToPdf;相关文章:
前端将html导出pdf文件解决分页问题
这是借鉴了qq_251025116大佬的解决方案并优化升级完成的,原文链接 1.安装依赖 npm install jspdf html2canvas2.使用方法 import htmlToPdffrom ./index.jsconst suc () > {message.success(success);};//记得在需要打印的div上面添加 idlet dom document.que…...
openssl3.2 - exp - 产生随机数
文章目录 openssl3.2 - exp - 产生随机数概述笔记END openssl3.2 - exp - 产生随机数 概述 要用到openssl产生的随机数, 查了资料. 如果用命令行产生随机数, 如下: openssl rand -hex -num 6 48bfd3a64f54单步跟进去, 看到主要就是调用了一个RAND_bytes(), 没其他了. 官方说…...
【三两波折】char *foo[]和char(*foo)[]有何不同?
1、先谈优先级 最高级别 —— 有四个,他们并不像运算符: []数组下标左到右结合()用于(表达式) or 函数名(形参表)左到右结合.读取结构体成员左到右结合->读取结构体成员(通过指针)左到右结合 第二级别…...
k8s(kubernetes)怎么查看pod服务对应哪些docker容器
Kubernetes(k8s)中的Pod是一组共享网络和存储资源的容器集合。每个Pod都包含一个或多个Docker容器,这些容器共享网络命名空间和存储卷,并在同一主机上运行。因此,可以将Pod视为一组紧密相关的Docker容器的逻辑主机&…...
[2023年]-hadoop面试真题(二)
[2023年]-hadoop面试真题(一) (北京) Maptask的个数由什么决定?(北京) 如何判定一个job的map和reduce的数量 ?(北京) MR中Shuffle过程 ?(北京) MR中处理数据流程 ?(…...
蓝桥杯备战刷题-滑动窗口
今天给大家带来的是滑动窗口的类型题,都是十分经典的。 1,无重复字符的最长子串 看例三,我们顺便来说一下子串和子序列的含义 子串是从字符串里面抽出来的一部分,不可以有间隔,顺序也不能打乱。 子序列也是从字符串里…...
LLM(十一)| Claude 3:Anthropic发布最新超越GPT-4大模型
2024年3月4日,Anthropic发布最新多模态大模型:Claude 3系列,共有Haiku、Sonnet和Opus三个版本。 Opus在研究生水平专家推理、基础数学、本科水平专家知识、代码等10个维度,超过OpenAI的GPT-4。 Haiku模型更注重效率,能…...
20-Java备忘录模式 ( Memento Pattern )
Java备忘录模式 摘要实现范例 备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象 备忘录模式属于行为型模式 摘要 1. 意图 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对…...
整合生成型AI战略:从宏观思维到小步实践
“整合生成型AI战略:从宏观思维到小步实践” 在这篇文章中,我们探讨了将生成型AI和大型语言模型融入企业核心业务的战略开发方法。我们的方法基于敏捷开发原则,技术专家和数据科学家需要采纳商业思维,而执行官则需理解生成型AI和…...
个人博客系列-后端项目-用户验证(5)
介绍 创建系统管理app,用于管理系统的用户,角色,权限,登录等功能,项目中将使用django-rest_framework进行用户认证和权限解析。这里将完成用户认证 用户验证 rest_framework.authentication模块中的认证类ÿ…...
css3中nth-child属性作用及用法剖析
hello宝子们...我们是艾斯视觉擅长ui设计和前端开发10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩! 标题:CSS3中nth-child属性作用及用法剖析 摘要:CSS3中的nth-child选择器允许我们根据元素位置来定位特定的元素…...
okHttp MediaType MIME格式详解
一、介绍 我们在做数据上传时,经常会用到Okhttp的开源库,okhttp开源库也遵循html提交的MIME数据格式。 所以我们经常会看到applicaiton/json这样的格式在传。 但是如果涉及到其他文件等就需要详细的数据格式,否则服务端无法解析 二、okHt…...
跨境电商三大趋势
跨境电商有着不断发展的三大趋势: 个性化定制:随着消费者需求的不断变化和个性化定制的潮流,跨境电商平台开始提供更多的定制化服务。消费者可以根据自己的需求选择产品的款式、材料和设计,从而获得更加个性化的产品体验。 无界销…...
【DevOps基础篇之k8s】如何通过Kubernetes CKA认证考试
【DevOps基础篇之k8s】如何通过Kubernetes CKA认证考试 目录 【DevOps基础篇之k8s】如何通过Kubernetes CKA认证考试核心概念资源监控生命周期管理Cluster维护安全认证问题排查其他推荐超级课程: Docker快速入门到精通Kubernetes入门到大师通关课这些是我在准备CK...
Mysql数据库-基本表操作
1.表操作 创建表:CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; field 表示列名 datatype 表示列的类型 character set 字符集,如果没有指定字符集ÿ…...
OceanBase社区版单节点安装搭建(Docker)
OceanBase社区版单节点安装搭建(Docker) 文章目录 OceanBase社区版单节点安装搭建(Docker)一、环境检查及Docker配置1.1 安装docker1.2 配置docker镜像源 二、OB镜像下载三、obd部署单节点数据库四、创建业务租户、数据库、表4.1 …...
Unity 关节:铰链、弹簧、固定、物理材质:摩檫力、 特效:拖尾、
组件-物理-关节:铰链(类似门轴) 自动动作、多少力可以将其断开、 弹簧可以连接另一个刚体(拖动即可) 固定一般是等待一个断裂力,造成四分五裂的效果。 物理材质 设置摩檫力,则可以创造冰面的…...
RIPEMD算法:多功能哈希算法的瑰宝
title: RIPEMD算法:多功能哈希算法的瑰宝 date: 2024/3/10 17:31:17 updated: 2024/3/10 17:31:17 tags: RIPEMD起源算法优势安全风险对比SHA优于MD5应用领域工作原理 一、RIPEMD算法的起源与历程 RIPEMD(RACE Integrity Primitives Evaluation Messag…...
如何学习ChatGPT?从入门到精通(附资料下载)
2023 ChatGPT从入门到精通视频教程(共30课).zip 学习ChatGPT需要涉及多个层面,包括理解其基本原理、掌握相关技术、以及进行实际的项目应用。以下是一些具体的学习步骤和建议: 理解ChatGPT的基本原理: 深入了解ChatGP…...
Linux安装MeterSphere并结合内网穿透实现公网远程访问本地服务
文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
