使用jspdf将html页面生成pdf文件

1、下载jspdf插件包

npm i jspdf

2、在utils文件夹下创建一个单独的文件(名字无具体要求)

// 页面导出为pdf格式,title表示为下载的标题,html表示要下载的页面
import html2Canvas from 'html2canvas' // 不用单独去下载这个包,下载jspdf包时就已经下载下来了,所以直接用就可以了
import JsPDF from 'jspdf'
import { Loading } from 'element-ui'
let noTableHeight = 0; //table外的元素高度
function htmlPdf(title, html, lableList, type) {// type传有效值pdf则为横版
  const loading = Loading.service({
    lock: true,
    text: '正在生成PDF文件',
    spinner: 'el-icon-loading',
    background: 'rgba(0, 0, 0, 0.7)'
  })
  if (lableList) {
    const pageHeight = Math.floor(277 * html.scrollWidth / 190); //计算pdf高度
    for (let i = 0; i < lableList.length; i++) { //循环获取的元素
      const multiple = Math.ceil((lableList[i].offsetTop + lableList[i].offsetHeight) / pageHeight); //元素的高度
      if (isSplit(lableList, i, multiple * pageHeight)) { //计算是否超出一页
        let _H = '' //向pdf插入空白块的内容高度
        _H = multiple * pageHeight - (lableList[i].offsetTop + lableList[i].offsetHeight);
        let newNode =  getFooterElement(_H);  //向pdf插入空白块的内容
        const divParent = lableList[i].parentNode; // 获取该div的父节点
        const next = lableList[i].nextSibling; // 获取div的下一个兄弟节点
        // 判断兄弟节点是否存在
        if (next) {
          // 存在则将新节点插入到div的下一个兄弟节点之前,即div之后
          divParent.insertBefore(newNode, next);
        } else {
          // 否则向节点添加最后一个子节点
          divParent.appendChild(newNode);
        }
      }
    }
  }
  html2Canvas(html, {
    allowTaint: false,
    taintTest: false,
    logging: false,
    useCORS: true,
    dpi: window.devicePixelRatio * 1, 
    scale: 4 // 按比例增加分辨率,图片模糊时数值调高点
  }).then(canvas => {
    let pdf = new JsPDF('p', 'mm', 'a4'); // A4纸,纵向
    let ctx = canvas.getContext('2d');
    let a4w = type ? 277 : 190; let a4h = type ? 190 : 277; // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
    let imgHeight = Math.floor(a4h * canvas.width / a4w); // 按A4显示比例换算一页图像的像素高度
    let renderedHeight = 0;
    while (renderedHeight < canvas.height) {
      let page = document.createElement('canvas');
      page.width = canvas.width;
      page.height = Math.min(imgHeight, canvas.height - renderedHeight);// 可能内容不足一页
 
      // 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
      page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0);
      pdf.addImage(page.toDataURL('image/jpeg', 1.0), 'JPEG', 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width)); // 添加图像到页面,保留10mm边距
 
      renderedHeight += imgHeight;
      if (renderedHeight < canvas.height) {
        pdf.addPage();// 如果后面还有内容,添加一个空页
      }
      // delete page;
    }
    loading.close()
    // 保存文件
    pdf.save(title + '.pdf');
  });
}
  // pdf截断需要一个空白位置来补充
function getFooterElement(remainingHeight, fillingHeight = 0) {
  const newNode = document.createElement('div');
  newNode.style.background = '#ffffff';
  newNode.style.width = 'calc(100% + 8px)';
  newNode.style.marginLeft = '0px';
  newNode.style.marginBottom = '0px';
  newNode.classList.add('divRemove');
  newNode.style.height = (remainingHeight + fillingHeight) + 'px'; 
  return newNode;
}
// 计算是否超出一页
function isSplit(nodes, index, pageHeight) {
    noTableHeight+= nodes[index].clientHeight
    return nodes[index].offsetTop + nodes[index].offsetHeight < pageHeight && nodes[index + 1] && nodes[index + 1].offsetTop + nodes[index + 1].offsetHeight  > pageHeight;
}
export default htmlPdf;
<el-button type="primary" @click="exportReport">输出报表</el-button>
<div class="output-report-pdf" ref="outputReportPDF" v-if="outputReportPDFShow">
	<div class="pdf-item" ref="outputReportPDFItem" v-for="(item, index) in reportPdfData" :key="index" :style="(index + 1) % 2 !== 0 ? 'border-bottom: none': ''">
		<div class="output-report-img">
			<el-image style="width: 100%; height: 100%" :src="item.img" fit="contain" @load="handleImage(index)"></el-image>
		</div>
	</div>
</div>

<script>
// 引入刚才创建的文件
import htmlPdf from '@/utils/htmlToPdf'

data() {
	return {
		outputReportPDFShow: false
	}
},
methods: {
	exportReport() {
		 this.reportPdfData = '从后端获取到的数据'
         this.outputReportPDFShow = true
	},
	// el-image加在完图片时触发
	handleImage(index) {
		// 等最后一张图片在页面中加在完再输出成pdf,要不然pdf文件里的图片会不显示
		if (index + 1 === this.reportPdfData.length) {
			 const name = '测试'
			 // name:保存的文件名称;this.$refs.outputReportPDF:要输出成pdf的总模块;this.$refs.outputReportPDFItem:总模块中的每个item,用于判断是否需要在换页时加个空白框隔开
             htmlPdf(name, this.$refs.outputReportPDF, this.$refs.outputReportPDFItem)
             this.outputReportPDFShow = false
		}
	}
}
</script>

<style scoped lang="scss">
.output-report-pdf {
	.pdf-item {
		// 其它样式根据具体要求设置,但是每个小模块的宽高有要求,因为要计算小模块是否需要放到下一页显示
		width: 570px; // pdf的高度:277 * width / 190, width最好是190的倍数;出现小数时容易出错
		height: 415px; // 高度最好不要超过一页pdf的高度
		border: 1px solid #000000;
                     
		.output-report-img {
			width: 100%;
			height: 100%;
		}
	}           
}
</style>

每页pdf的高度计算公式
在这里插入图片描述

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

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

相关文章

内网安全--隧道技术代理技术

注:本文仅做技术交流,请勿非法破坏... 目录 项目: 1-Ngrok 用法 2-Frp 用法 3-Nps 用法 4-Spp 用法 工具: windows下: Proxifier(推荐~) Sockscap ccproxy Linux下: Proxychains 用法 http://t.csdnimg.cn/88Ew7 隧道技术&#xff1a;解决不出网协议上线的问…

SFML 小demo

文章目录 项目搭建代码实现main.cppobject.hsnake.hcommon.h 使用 demo 做到最后的话其实就只是验证了以前自己的一个想法&#xff0c;但是没有做成一个真正的游戏&#xff0c;可以算是一个 demo 而已吧&#xff0c;没做游戏的界面和关卡&#xff0c;不过完成了核心显式机制和功…

UE5-人物角色动画蓝图

这里主要从零给角色创建移动的蓝图&#xff0c;包含多种状态 创建 首先在角色骨骼网格体上右键创建动画蓝图 进入&#xff0c;在AnimGraph界面创建一个状态机&#xff08;stateMachine&#xff09; Idle 进入状态机&#xff0c;拉出来创建一个newState&#xff0c;这里命名…

【C++修行之道】类和对象(五)日期类的实现、const成员、取地址及const和取地址操作符重载

目录 一、 日期类的实现 Date.h 1.1 GetMonthDay函数&#xff08;获取某年某月的天数&#xff09; 问&#xff1a;这个函数为什么不和其他的函数一样放在Date.cpp文件中实现呢&#xff1f; 1.2 CheckDate函数&#xff08;检查日期有效性&#xff09;、Print函数&#xff08;…

手机建站介绍

随着科技的不断进步和移动互联网的普及&#xff0c;手机应用已经成为人们生活中最不可或缺的一部分。而手机建站作为一种新兴技术&#xff0c;在这一领域也有着广泛的应用。本文将为大家介绍手机建站的概念、优势和应用。 什么是手机建站&#xff1f; 手机建站是指将传统的网络…

【启明智显彩屏应用】Model3A 7寸触摸屏在真空包装机上的应用解决方案

一、项目背景与需求 随着工业自动化水平的提升&#xff0c;对真空包装机的操作界面和控制精度要求也越来越高。为满足这一需求&#xff0c;我们提出了基于Model3A工业级HMI&#xff08;人机界面&#xff09;芯片方案的7寸触摸屏解决方案&#xff0c;旨在提高真空包装机的操作便…

VSCode中使用LaTeX编辑文章

工欲善其事必先利其器&#xff0c;成功在VSCode中使用LaTeX&#xff0c;遂做记录。 1. 先准备VScode的安装 下载地址&#xff1a;VScode地址 正常安装即可&#xff0c;一路next安装下去即可。 2. 准备安装latex 国内使用清华源&#xff0c;下载地址:https://mirrors.tuna.t…

Django学习三:views业务层中通过models对实体对象进行的增、删、改、查操作。

文章目录 前言一、Django ORM介绍二、项目快速搭建三、操作1、view.pya、增加操作b、删除操作c、修改操作d、查询操作 2、urls.py 前言 上接博文&#xff1a;Django学习二&#xff1a;配置mysql&#xff0c;创建model实例&#xff0c;自动创建数据库表&#xff0c;对mysql数据…

FreeRTOS基础(十一):消息队列

本文将详细全方位的讲解FreeRTOS的消息队列&#xff0c;其实在FreeRTOS中消息队列的重要性也不言而喻&#xff0c;与FreeRTOS任务调度同等重要&#xff0c;因为后面的各种信号量基本都是基于消息队列的。 目录 一、消息队列的简介 1.1 产生的原因 1.2 消息队列的解决办法 …

Django 部署指南

部署 Django 应用程序涉及将我们的应用程序从开发环境部署到生产环境&#xff0c;并确保它可以在生产服务器上安全运行和扩展。其实了解几种部署方案&#xff0c;相信你对将来的项目更得心应手。 1、问题背景 Django 是一款流行的 Python Web 框架&#xff0c;但对于新手来说&…

618网购节,电商能挡住恶意网络爬虫的攻击吗?

目录 爬虫盗取电商数据的步骤 电商平台如何发现网络爬虫&#xff1f; 如何拦截违法网络爬虫 2023年&#xff0c;杭州中院审结了两起涉及“搬店软件”的不正当竞争案件。本案的原告是国内某大型知名电子商务平台的运营主体&#xff0c;而被告则是开发了一款名为“某搬家快速商品…

【数据结构】——线性表(顺序表)——内有代码详解

目录 一、引言 二、线性表 2.1 定义 2.2 特点 三、顺序表 3.1 顺序表的概念 3.2 顺序表的特点 3.3 顺序表的定义 3.3.1 静态定义 3.3.2 动态定义 3.4 顺序表的初始化 3.4.1 静态初始化 3.4.2 动态初始化 3.5 顺序表的销毁 3.6 顺序表元素的打印 3.7 顺序表的插入…

百度AI大底座

“百度AI大底座”是源自百度多年产业深度实践积累、结合AI全栈技术科研成果打造的国内首个全栈自研的AI基础设施&#xff0c; 面向企业和产业AI开发与应用提供端到端自主可控、自我进化的解决方案&#xff0c;能够快捷、低成本地实现“AI能力的随用随 取”。AI大底座可助力企业…

Python 学习flask创建项目

1、使用pycharm创建flask项目 2、运行访问地址 3、可以看到访问地址内容 4、可以增加路由&#xff0c;尝试访问获取参数

树莓派4B_OpenCv学习笔记4:测试摄像头_imread加载显示图像_imwrite保存图片

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1&#xff1a; 今日对之前的测试CSI摄像头函数进行一些理解说明&#x…

Shell脚本文本处理三剑客(grep、awk、sed)和正则表达式

一、正则表达式 1.正则表达式基础 正则表达式&#xff08;regular expression&#xff09;描述了一种字符串匹配的模式&#xff08;pattern&#xff09;&#xff0c;可以用来检查一个串是否含有某种子串&#xff0c;将匹配的子串替换或者从某个串中取出符号某个条件的子串等&…

【微信小程序】页面事件

下拉刷新 上拉触底 上拉触底距离指的是触发上拉触底事件时&#xff0c;滚动条距离页面底部的距离。 可以在全局或页面的json配置文件中&#xff0c;通过onReachBottomDistance属性来配置上拉触底的距离。 小程序默认的触底距离是50x,在实际开发中&#xff0c;可以根据自己的需…

【C++】─篇文章带你熟练掌握 map 与 set 的使用

目录 一、关联式容器二、键值对三、pair3.1 pair的常用接口说明3.1.1 [无参构造函数](https://legacy.cplusplus.com/reference/utility/pair/pair/)3.1.2 [有参构造函数 / 拷贝构造函数](https://legacy.cplusplus.com/reference/utility/pair/pair/)3.1.3 [有参构造函数](htt…

vue3 基于el-tree增加、删除节点(非TypeScript 写法)

话不多说&#xff0c;直接贴代码 <template><div class"custom-tree-container"><!-- <p>Using render-content</p><el-tree style"max-width: 600px" :data"dataSource" show-checkbox node-key"id" …

智能网联汽车信息安全风险识别与应对策略研究综述

摘要&#xff1a;随着智能网联汽车技术的飞速发展&#xff0c;其信息安全问题逐渐成为公众关注的焦点。本文概述了智能网联汽车技术的发展背景和信息安全风险的来源&#xff0c;采用STRIDE威胁分析方法对智能网联汽车的四层模型进行风险识别&#xff0c;进一步探讨了抗女巫攻击…