vue3将页面导出成PDF文件(完美解决图片、表格内容分割问题)

2023-09-13 14:26:46

vue3将页面导出成PDF文件(完美解决图片、表格内容分割问题)

1、安装依赖

npm install --save html2canvas  // 页面转图片
npm install jspdf --save  // 图片转pdf

2、在utils中创建htmlToPDF.js文件

// 页面导出为pdf格式 //title表示为下载的标题,html表示document.querySelector('#myPrintHtml')
import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
var noTableHeight = 0 //table外的元素高度

export const htmlPdf = (title, html, fileList, type) => {// type传有效值pdf则为横版
  if (fileList) {
    const pageHeight = Math.floor(277 * html.scrollWidth / 190) + 20 //计算pdf高度
    for (let i = 0; i < fileList.length; i++) { //循环获取的元素
      const multiple = Math.ceil((fileList[i].offsetTop + fileList[i].offsetHeight) / pageHeight) //元素的高度
      if (isSplit(fileList, i, multiple * pageHeight)) { //计算是否超出一页
        var _H = '' //向pdf插入空白块的内容高度
        if (fileList[i].localName !== 'tr') { //判断是不是表格里的内容
          _H = multiple * pageHeight - (fileList[i].offsetTop + fileList[i].offsetHeight)
        } else {
          _H = multiple * pageHeight - (fileList[i].offsetTop + fileList[i].offsetHeight + noTableHeight) + 20
        }
        var newNode = getFooterElement(_H)  //向pdf插入空白块的内容
        const divParent = fileList[i].parentNode // 获取该div的父节点
        const next = fileList[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: 1 // 按比例增加分辨率
  }).then(canvas => {
    var pdf = new JsPDF('p', 'mm', 'a4') // A4纸,纵向
    var ctx = canvas.getContext('2d')
    var a4w = type ? 277 : 190; var a4h = type ? 190 : 277 // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
    var imgHeight = Math.floor(a4h * canvas.width / a4w) // 按A4显示比例换算一页图像的像素高度
    var renderedHeight = 0
    while (renderedHeight < canvas.height) {
      var 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;
    }
    // 保存文件
    pdf.save(title + '.pdf')
  })
}
// pdf截断需要一个空白位置来补充
const getFooterElement = (remainingHeight, fillingHeight = 0) => {
  const newNode = document.createElement('div')
  newNode.style.background = '#ffffff'
  newNode.style.width = 'calc(100% + 8px)'
  newNode.style.marginLeft = '-4px'
  newNode.style.marginBottom = '0px'
  newNode.classList.add('divRemove')
  newNode.style.height = (remainingHeight + fillingHeight) + 'px'
  return newNode
}
const isSplit = (nodes, index, pageHeight) => {
  // 判断是不是tr 如果不是高度存起来
  // 表格里的内容要特殊处理
  // tr.offsetTop 是tr到table表格的高度
  // 所以计算高速时候要把表格外的高度加起来
  // 生成的pdf没有表格了这里可以不做处理 直接计算就行
  if (nodes[index].localName !== 'tr') {  //判断元素是不是tr
    noTableHeight += nodes[index].clientHeight
  }

  if (nodes[index].localName !== 'tr') {
    return nodes[index].offsetTop + nodes[index].offsetHeight < pageHeight && nodes[index + 1] && nodes[index + 1].offsetTop + nodes[index + 1].offsetHeight > pageHeight
  } else {
    return nodes[index].offsetTop + nodes[index].offsetHeight + noTableHeight < pageHeight && nodes[index + 1] && nodes[index + 1].offsetTop + nodes[index + 1].offsetHeight + noTableHeight > pageHeight
  }
}

3、在vue中引入并使用

<template>
	<div>
    	<button class="primary-btn" @click="handleExport">导出</button>
        <div class="check-wrapper" id="pdfRef">
      		<div class="page1-box pdfRef">11</div>
      		<div class="page2-box pdfRef">11</div>
      		<div class="page3-box pdfRef">11</div>
      		<div class="page4-box pdfRef">11</div>
      		<div class="page5-box pdfRef">11</div>
      		<div class="page6-box pdfRef">11</div>
      		<div class="page7-box pdfRef">11</div>
      		<div class="page8-box pdfRef">11</div>
      	</div>
	</div>
</template>
<script setup>
	// 引入方法
	import { htmlPdf } from "@/utils/htmlToPDF.js"  
	// 导出成PDF
	const handleExport = (name) => {
	  var fileName= '投资评审报告'
	  const fileList = document.getElementsByClassName('pdfRef')   // 很重要
	  htmlPdf(fileName, document.querySelector('#pdfRef'), fileList)
	}
</script>
更多推荐

Hbase分布式集群部署

目录一、环境说明二、部署Hbase2.1解压Hbase2.2移动解压包2.3修改hbase-env.sh文件2.4修改环境变量2.5修改hbase-site.xml文件2.6修改regionservers文件2.7分发hbase2.7.1分发hbase包2.7.2分发环境配置2.8启动hbase服务2.8.1环境生效2

【机器学习教程】二、逻辑回归:从概率到分类的利器

引言在机器学习领域中,逻辑回归(LogisticRegression)是一种经典的分类算法,被广泛应用于各种实际问题中。尽管名字中带有"回归"一词,但逻辑回归实际上是一种分类模型,它通过将输入数据映射到一个概率范围内来进行二分类或多分类任务。逻辑回归具有许多优点,如简单易懂、计算效率高以及对大规模数据集的可扩展性。本文

日志技术-Logback

日志技术将系统执行的信息,方便的记录到指定位置(控制台、文件、数据库)可以随时以开关的形式开关日志,无需入侵到源代码去修改日志接口:设计日志框架的统一标准注:有人对JCL接口不满意,就有了SLF4J。有人对log4j性能不满意,就有了LogBack,LogBack是基于SLF4J开发的依赖:下载:下载时注意不同版本JD

爬虫使用Selenium生成Cookie

在爬虫的世界中,有时候我们需要模拟登录来获取特定网站的数据,而使用Selenium登录并生成Cookie是一种常见且有效的方法。本文将为你介绍如何使用Selenium进行登录,并生成Cookie以便后续的爬取操作。让我们一起探索吧!一、Selenium简介1.定义:Selenium是一套自动化测试工具,可以模拟用户在浏

基于Python开发的AI智能联系人管理程序(源码+可执行程序+程序配置说明书+程序使用说明书)

一、项目简介本项目是一套基于Python开发的AI智能联系人管理程序,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者。包含:项目源码、项目文档等,该项目附带全部源码可作为毕设使用。项目都经过严格调试,确保可以运行!二、开发环境要求本系统的软件开发及运行环境具体如下。操作系统:Windo

GaussDB数据库SQL系列-UNION & UNION ALL

目录一、前言二、GaussDBUNION/UNIONALL1、GaussDBUNION操作符2、语法定义三、GaussDB实验示例1、创建实验表2、合并且除重(UNION)3、合并不除重(UNIONALL)4、合并带有WHERE子句SQL结果集(UNIONALL)5、业务逻辑除重后合并(UNIONALL)四、Gauss

vue2实现自定义主题webpack-theme-color-replacer

需求:根据element的自定义主题色,之后改变element的全局所有颜色,解决页面刷新后主题色失效问题,这个需要把颜色存入到浏览器的存储中,如果换个浏览器就得重新选择了哈,如果需要在不同的浏览器保持一致的主题,需要跟后端沟通之前还写过一个简单的,有需要的可以去看:vue实现element-ui自定义主题色切换(简单

Python异常处理之分享

异常处理在项目开发中,异常处理是不可或缺的。异常处理帮助人们debug,通过更加丰富的信息,让人们更容易找到bug的所在。异常处理还可以提高程序的容错性。我们之前在讲循环对象的时候,曾提到一个StopIteration的异常,该异常是在循环对象穷尽所有元素时的报错。我们以它为例,来说明基本的异常处理。一个包含异常的程序

Python-requests库入门指南

介绍Python编写的HTTP库,能够发送HTTP和HTTPS请求,并且获取响应。在测试服务器响应方面经常使用。下载pipinstallrequests使用常用的格式requests.get(url,params=None,**kwargs)requests.post(url,data=None,json=None,*

ChatGPT在电子健康记录和医疗信息查询中的应用前景如何?

电子健康记录(EHRs)和医疗信息查询在现代医疗保健系统中起着至关重要的作用。它们有助于提高患者护理的质量,提高医疗保健的效率,减少错误,促进患者参与,并促进医学研究和数据驱动的决策。ChatGPT作为一种人工智能技术,在这一领域具有巨大的潜力,可以改善EHR的创建、维护和利用,以及医疗信息查询的效率和准确性。以下是C

命令模式-

定义:又叫动作模式或事务模式。指的是将一个请求封装成一个对象,使发出请求的责任和执行请求的责任分割开,然后可以使用不同的请求把客户端参数化,这样可以使得两者之间通过命令对象进行沟通,从而方便将命令对象进行储存、传递、调用、增加与管理。应用场景:1、对于很多数的请求-响应模式的功能,比较适合使用命令模式,命令模式对实现记

热文推荐