当前位置: 首页 > 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…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...