当前位置: 首页 > news >正文

前端将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大佬的解决方案并优化升级完成的&#xff0c;原文链接 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、先谈优先级 最高级别 —— 有四个&#xff0c;他们并不像运算符&#xff1a; []数组下标左到右结合()用于&#xff08;表达式&#xff09; or 函数名(形参表)左到右结合.读取结构体成员左到右结合->读取结构体成员&#xff08;通过指针&#xff09;左到右结合 第二级别…...

k8s(kubernetes)怎么查看pod服务对应哪些docker容器

Kubernetes&#xff08;k8s&#xff09;中的Pod是一组共享网络和存储资源的容器集合。每个Pod都包含一个或多个Docker容器&#xff0c;这些容器共享网络命名空间和存储卷&#xff0c;并在同一主机上运行。因此&#xff0c;可以将Pod视为一组紧密相关的Docker容器的逻辑主机&…...

[2023年]-hadoop面试真题(二)

[2023年]-hadoop面试真题(一) &#xff08;北京&#xff09; Maptask的个数由什么决定?&#xff08;北京&#xff09; 如何判定一个job的map和reduce的数量 ?&#xff08;北京&#xff09; MR中Shuffle过程 ?&#xff08;北京&#xff09; MR中处理数据流程 ?&#xff08;…...

蓝桥杯备战刷题-滑动窗口

今天给大家带来的是滑动窗口的类型题&#xff0c;都是十分经典的。 1&#xff0c;无重复字符的最长子串 看例三&#xff0c;我们顺便来说一下子串和子序列的含义 子串是从字符串里面抽出来的一部分&#xff0c;不可以有间隔&#xff0c;顺序也不能打乱。 子序列也是从字符串里…...

LLM(十一)| Claude 3:Anthropic发布最新超越GPT-4大模型

2024年3月4日&#xff0c;Anthropic发布最新多模态大模型&#xff1a;Claude 3系列&#xff0c;共有Haiku、Sonnet和Opus三个版本。 Opus在研究生水平专家推理、基础数学、本科水平专家知识、代码等10个维度&#xff0c;超过OpenAI的GPT-4。 Haiku模型更注重效率&#xff0c;能…...

20-Java备忘录模式 ( Memento Pattern )

Java备忘录模式 摘要实现范例 备忘录模式&#xff08;Memento Pattern&#xff09;保存一个对象的某个状态&#xff0c;以便在适当的时候恢复对象 备忘录模式属于行为型模式 摘要 1. 意图 在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对…...

整合生成型AI战略:从宏观思维到小步实践

“整合生成型AI战略&#xff1a;从宏观思维到小步实践” 在这篇文章中&#xff0c;我们探讨了将生成型AI和大型语言模型融入企业核心业务的战略开发方法。我们的方法基于敏捷开发原则&#xff0c;技术专家和数据科学家需要采纳商业思维&#xff0c;而执行官则需理解生成型AI和…...

个人博客系列-后端项目-用户验证(5)

介绍 创建系统管理app&#xff0c;用于管理系统的用户&#xff0c;角色&#xff0c;权限&#xff0c;登录等功能&#xff0c;项目中将使用django-rest_framework进行用户认证和权限解析。这里将完成用户认证 用户验证 rest_framework.authentication模块中的认证类&#xff…...

css3中nth-child属性作用及用法剖析

hello宝子们...我们是艾斯视觉擅长ui设计和前端开发10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩! 标题&#xff1a;CSS3中nth-child属性作用及用法剖析 摘要&#xff1a;CSS3中的nth-child选择器允许我们根据元素位置来定位特定的元素…...

okHttp MediaType MIME格式详解

一、介绍 我们在做数据上传时&#xff0c;经常会用到Okhttp的开源库&#xff0c;okhttp开源库也遵循html提交的MIME数据格式。 所以我们经常会看到applicaiton/json这样的格式在传。 但是如果涉及到其他文件等就需要详细的数据格式&#xff0c;否则服务端无法解析 二、okHt…...

跨境电商三大趋势

跨境电商有着不断发展的三大趋势&#xff1a; 个性化定制&#xff1a;随着消费者需求的不断变化和个性化定制的潮流&#xff0c;跨境电商平台开始提供更多的定制化服务。消费者可以根据自己的需求选择产品的款式、材料和设计&#xff0c;从而获得更加个性化的产品体验。 无界销…...

【DevOps基础篇之k8s】如何通过Kubernetes CKA认证考试

【DevOps基础篇之k8s】如何通过Kubernetes CKA认证考试 目录 【DevOps基础篇之k8s】如何通过Kubernetes CKA认证考试核心概念资源监控生命周期管理Cluster维护安全认证问题排查其他推荐超级课程: Docker快速入门到精通Kubernetes入门到大师通关课这些是我在准备CK...

Mysql数据库-基本表操作

1.表操作 创建表&#xff1a;CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; field 表示列名 datatype 表示列的类型 character set 字符集&#xff0c;如果没有指定字符集&#xff…...

OceanBase社区版单节点安装搭建(Docker)

OceanBase社区版单节点安装搭建&#xff08;Docker&#xff09; 文章目录 OceanBase社区版单节点安装搭建&#xff08;Docker&#xff09;一、环境检查及Docker配置1.1 安装docker1.2 配置docker镜像源 二、OB镜像下载三、obd部署单节点数据库四、创建业务租户、数据库、表4.1 …...

Unity 关节:铰链、弹簧、固定、物理材质:摩檫力、 特效:拖尾、

组件-物理-关节&#xff1a;铰链&#xff08;类似门轴&#xff09; 自动动作、多少力可以将其断开、 弹簧可以连接另一个刚体&#xff08;拖动即可&#xff09; 固定一般是等待一个断裂力&#xff0c;造成四分五裂的效果。 物理材质 设置摩檫力&#xff0c;则可以创造冰面的…...

RIPEMD算法:多功能哈希算法的瑰宝

title: RIPEMD算法&#xff1a;多功能哈希算法的瑰宝 date: 2024/3/10 17:31:17 updated: 2024/3/10 17:31:17 tags: RIPEMD起源算法优势安全风险对比SHA优于MD5应用领域工作原理 一、RIPEMD算法的起源与历程 RIPEMD&#xff08;RACE Integrity Primitives Evaluation Messag…...

如何学习ChatGPT?从入门到精通(附资料下载)

2023 ChatGPT从入门到精通视频教程&#xff08;共30课&#xff09;.zip 学习ChatGPT需要涉及多个层面&#xff0c;包括理解其基本原理、掌握相关技术、以及进行实际的项目应用。以下是一些具体的学习步骤和建议&#xff1a; 理解ChatGPT的基本原理&#xff1a; 深入了解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文件夹&#xff0c;并新增内容 3.创建package文件夹...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 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”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...