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

vue中使用html2canvas+jsPDF实现pdf的导出

导入依赖

html2canvas依赖

npm install html2canvas

jspdf依赖

npm install jspdf

pdf导出

以导出横向,A4大小的pdf为例
规律:1. html2canvas 中,在保持jsPDF中的宽高不变的情况下,设置html2canvas中的 width 和 height 值越小,导出的pdf越显示不全(会被放大,只能看到局部),反之值越大,导出的pdf越显示完整(值也不能过大,过大在pdf中就显示的越小)。
2. jsPDF 中,在保持html2canvas中的宽高不变的情况下,pdf.addImage(pageData, ‘JPEG’, 5, yPosition, width, height) width 和 height 值越小,导出的pdf越显示完整,反之导出的pdf越显示不全。
总结:html2canvas 与 jsPDF 设置刚好相反,合理设置大小,才能使数据撑满整个pdf。

index.vue执行导出pdf页面

<a-spin :spinning="pdfConfirmLoading"><div ref="pdfDiv"><pdf-template :ref="'pdfTemplate' + i" v-for="i in arrNum"></pdf-template></div>
</a-spin><script>import {printPdf} from './utils/index'import pdfTemplate from './template/pdfTemplate'export default {name: 'courseTableList',components: {pdfTemplate},data() {return {pdfVisible: false,disableSubmit: false,arrNum: 1,pdfConfirmLoading: false,}},created() {},methods: {//导出pdfasync handleExportPdf() {this.handleElement()},handleElement() {if(this.selectionRows.length > 0) {this.pdfConfirmLoading = trueconst groupedData = this.selectionRows.reduce((result, item) => {// 检查是否已存在以该teacher为键的分组if (!result[item.instructor]) {result[item.instructor] = [];}// 将当前项添加到对应的分组中result[item.instructor].push(item);return result;}, {});// 转换成数组形式const groupedArray = Object.values(groupedData);let len = groupedArray.lengththis.arrNum = lenlet that = thisthis.pdfVisible = truethis.$nextTick(() => {for (let index = 0; index < groupedArray.length; index++) {const item = groupedArray[index];let num = index + 1let pageFooter = num + ' of ' + len;that.$refs['pdfTemplate' + num][0].setDataList(item, pageFooter);}this.pdfConfirmLoading = false})} else {this.$message.warning("请勾选数据")}},//确认导出pdfasync confirmExportPdf() {this.pdfConfirmLoading = trueawait printPdf(this.$refs.pdfDiv, "courseSchedule")this.pdfConfirmLoading = falsethis.pdfVisible = false},}}
</script>

pdfTemplate.vue模板,根据需求自定义创建

<template><div class="content" ref="pdfContent" :key="JSON.stringify(datasource)"><!-- 头部 --><div class="header"><div class="header_row1"><div class="header_row1_v"><div class="header_row1_left"><span>Regd. User: YiZhong College</span></div><div class="header_row1_right"><span>Christine Xia</span></div></div><div class="header_row1_middle"><span>Timetables</span></div></div><div class="header_row2"><div class="header_row2_v"><div class="header_row2_left"><span>School: FD - YiZhong Cambridge International School</span></div><div class="header_row2_right"><span>FDCC1</span></div></div><div class="header_row2_middle"><span>FD 2022-23 Teacher's Timetables</span></div></div><div class="header_line"></div></div><!-- 表格 --><div class="table_middle"><a-table :columns="columns" :data-source="datasource" bordered :pagination="false"><!-- 时间段 --><tamplate slot="timePeriodSlot" slot-scope="text, record"><span>{{record.period}}<br>&nbsp;&nbsp;{{text}} ~ {{record.endTime}}</span></tamplate></a-table></div><!-- 页脚 --><div class="footer"><div class="footer_line"></div><div class="footer_end"><div class="footer_end_datetime">{{this.currentTime}}</div><div class="footer_end_pages">{{this.pageNum}}</div></div></div></div>
</template><script>let columns = [{title: '教师',dataIndex: 'startTime',scopedSlots: { customRender: 'timePeriodSlot' },customCell: () => {return {style: {'min-width': '120px',},};},},{title: "Monday",children: [{title: '',dataIndex: 'monday',key: 'monday',customCell: () => {return {style: {'min-width': '180px',},};},},],},{title: "Tues",children: [{title: '',dataIndex: 'tuesday',key: 'tuesday',scopedSlots: { customRender: 'childrenRender' },customCell: () => {return {style: {'min-width': '180px',},};},},],},{title: "Wed",children: [{title: '',dataIndex: 'wednesday',key: 'wednesday',scopedSlots: { customRender: 'childrenRender' },customCell: () => {return {style: {'min-width': '180px',},};},},],},{title: "Thurs",children: [{title: '',dataIndex: 'thursday',key: 'thursday',scopedSlots: { customRender: 'childrenRender' },customCell: () => {return {style: {'min-width': '180px',},};},},],},{title: "Friday",children: [{title: '',dataIndex: 'friday',key: 'friday',scopedSlots: { customRender: 'childrenRender' },customCell: () => {return {style: {'min-width': '180px',},};},},],},
];export default {name: "pdfExport",data() {return {currentTime: '',pageNum: 'Page 1 of 1',datasource: [],}},computed: {columns() {return columns}},created() {this.getCurrentTime();},methods: {setDataList(list, pageFooter) {this.datasource = listif(this.datasource.length > 0) {this.columns[0].title = this.datasource[0].instructor}this.sortByStartTime()if(pageFooter) {this.pageNum = pageFooter}},sortByStartTime() {this.datasource.sort((a, b) => {const timeA = new Date(`2023/01/01 ${a.startTime}`);const timeB = new Date(`2023/01/01 ${b.startTime}`);return timeA - timeB;});},getCurrentTime() {const date = new Date();const month = date.getMonth() + 1;const day = date.getDate();const year = date.getFullYear();const hours = date.getHours();const minutes = date.getMinutes();const seconds = date.getSeconds();// 格式化为指定格式的字符串const dateString = `${this.formatNumber(month)}/${this.formatNumber(day)}/${year}`;const timeString = `${this.formatNumber(hours)}:${this.formatNumber(minutes)}:${this.formatNumber(seconds)}`;this.currentTime = `${dateString} ${timeString}`;},formatNumber(number) {return number < 10 ? `0${number}` : number;}}
}
</script><style scoped lang="less">.content {padding: 15px;color: black;}/**头部属性样式设置*/.header_row1 {display: flex;justify-content: center;font-size: 16px;margin-bottom: 3px;position: relative;}.header_row1 .header_row1_middle {font-weight: 700;position: absolute;}.header_row2 {display: flex;justify-content: center;font-size: 16px;margin-bottom: 15px;position: relative;}.header_row2 .header_row2_middle {font-size: 22px;font-weight: 700;position: absolute;}.header_row1_left,.header_row1_right,.header_row2_left,.header_row2_right {display: inline-block;}.header_row1_v,.header_row2_v {width: 100%;}.header_row1_left,.header_row2_left {float: left;}.header_row1_right,.header_row2_right {float: right;}.header_line {border: 1px solid black;margin-bottom: 5px;}/**中间属性样式设置*//* 将表格的标题行背景设置为白色 */.ant-table-thead {::v-deep & > tr > th {background: #fff;}}/* 将所有边框设置为黑色 */.table_middle {/deep/ .ant-table {color: black;font-size: 16px;}/deep/ .ant-table-bordered .ant-table-thead > tr:first-child > th:first-child {border-left: none;border-top: none;font-weight: 700;text-align: left;}/deep/ .ant-table-bordered .ant-table-thead > tr:not(:last-child) > th {border-bottom: 2px solid black;border-top: 2px solid black;font-weight: normal;text-align: center;}/deep/ .ant-table-bordered .ant-table-thead > tr > th{border-right: 2px solid black;border-bottom: 2px solid black;}/deep/.ant-table-bordered .ant-table-tbody > tr > td {border-right: 2px solid black;border-bottom: 2px solid black;}/deep/ .ant-table-bordered .ant-table-tbody > tr > td:first-child {border-left: 2px solid black;}/deep/ .ant-table-bordered.ant-table-empty .ant-table-placeholder {border: 2px solid black;border-top: 1px solid black;}}/**页脚属性样式设置*/.footer .footer_line {border: 1px solid black;}.footer {font-size: 16px;}.footer_line {margin-top: 75px;margin-bottom: 3px;}.footer_end {display: flex;justify-content: space-between;}
</style>

index.js pdf导出单页方法

export const printPdf = (dom, name = '文件') => {const printEle = domlet width = printEle.scrollWidth;let height = printEle.scrollHeight;html2canvas(printEle, {allowTaint: true, //允许跨域useCORS: true,width: width,height: height,background: '#FFFFFF', //如果指定的div没有设置背景色会默认成黑色scale: 2 // 按比例增加分辨率}).then(canvas => {let contentWidth = canvas.widthlet contentHeight = canvas.height//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高let pdf = new jsPDF('l', 'pt', 'a4')let imgWidth = pdf.internal.pageSize.getWidth()let imgHeight = (imgWidth / contentWidth) * contentHeightlet pageData = canvas.toDataURL('image/jpeg', 1.0)//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)//当内容未超过pdf一页显示的范围,无需分页pdf.addImage(pageData, 'JPEG', 5, 5, imgWidth, imgHeight)pdf.save(`${name}.pdf`)})
}

index.js pdf导出分页方法

import html2canvas from 'html2canvas'
import { jsPDF } from 'jspdf'export const printPdf = (dom, name = '文件') => {let contents = dom.getElementsByClassName("content")return exportToPDF(contents, name)
}//导出pdf
async function exportToPDF(contents, name = 'exportPdf') {// 创建一个空的横向A4大小的PDF文档对象const pdf = new jsPDF('l', 'pt', 'a4');let yPosition = 5; // 当前y坐标位置for (let i = 0; i < contents.length; i++) {const content = contents[i];let width = content.scrollWidth;let height = content.scrollHeight;// 使用html2canvas将content转换为canvasconst canvas = await html2canvas(content, {allowTaint: true, //允许跨域useCORS: true,width: width * 1.02,height: height,background: '#FFFFFF', //如果指定的div没有设置背景色会默认成黑色scale: 2 // 按比例增加分辨率})// 如果是下一个content的内容,则创建新的页码if (i > 0) {pdf.addPage();yPosition = 5;}let pageData = canvas.toDataURL('image/jpeg', 1.0)//宽度使用pdf的宽度let imgWidth = pdf.internal.pageSize.getWidth()//高度根据宽度的比列计算let imgHeight = (imgWidth / canvas.width) * canvas.height// 将canvas添加到PDF中pdf.addImage(pageData, 'JPEG', 5, yPosition, imgWidth, imgHeight)yPosition += pdf.internal.pageSize.getHeight();}// 输出PDF文件pdf.save(`${name}.pdf`)return new Promise((resolve, reject) => {resolve()})
}

相关文章:

vue中使用html2canvas+jsPDF实现pdf的导出

导入依赖 html2canvas依赖 npm install html2canvasjspdf依赖 npm install jspdfpdf导出 以导出横向&#xff0c;A4大小的pdf为例 规律&#xff1a;1. html2canvas 中&#xff0c;在保持jsPDF中的宽高不变的情况下&#xff0c;设置html2canvas中的 width 和 height 值越小&a…...

Linux学习之firewallD

systemctl status firewalld.service查看一下firewalld服务的状态&#xff0c;发现状态是inactive (dead)。 systemctl start firewalld.service启动firewalld&#xff0c;systemctl status firewalld.service查看一下firewalld服务的状态&#xff0c;发现状态是active (runni…...

【JS学习】Object.assign 用法介绍

Object.assign 是ES6中的一个方法。该方法能够实现对象的浅复制以及对象合并。Object.assign 并不会修改目标对象本身&#xff0c;而是返回一个新的对象&#xff0c;其中包含了所有源对象的属性。 例1 2个对象合并 const target { a: 1, b: 2 }; const source { b: 3, c: 4…...

【uni-app报错】获取用户收货地址uni.chooseAddress()报错问题

chooseAddress:fail the api need to be declared in …e requiredPrivateInf 原因&#xff1a; 小程序配置 / 全局配置 (qq.com) 解决&#xff1a; 登录小程序后台申请接口 按照流程申请即可 在项目根目录中找到 manifest.json 文件&#xff0c;在左侧导航栏选择源码视图&a…...

机器学习、cv、nlp的一些前置知识

为节省篇幅&#xff0c;不标注文章来源和文章的问题场景。大部分是我的通俗理解。 文章目录 向量关于向量的偏导数&#xff1a;雅可比矩阵二阶导数矩阵&#xff1a;海森矩阵随机变量随机场伽马函数beta分布数学术语坐标上升法协方差训练集&#xff0c;验证集&#xff0c;测试集…...

Steam 灵感的游戏卡悬停效果

先看效果&#xff1a; 再看代码&#xff08;查看更多&#xff09;&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Steam 灵感的游戏卡悬停效果</title><style>* {margin: …...

[Openwrt]一步一步搭建MT7981A uboot、atf、openwrt-21.02开发环境操作说明

安装ubuntu-18.04 软件安装包 ubuntu-18.04-desktop-amd64.iso 修改ubuntu管理员密码 sudo passwd [sudo] password for w1804: Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully 更新ubuntu源 备份源 sudo cp /etc/apt/so…...

Unity C# 之 Azure 微软SSML语音合成TTS流式获取音频数据以及表情嘴型 Animation 的简单整理

Unity C# 之 Azure 微软SSML语音合成TTS流式获取音频数据以及表情嘴型 Animation 的简单整理 目录 Unity C# 之 Azure 微软SSML语音合成TTS流式获取音频数据以及表情嘴型 Animation 的简单整理 一、简单介绍 二、实现原理 三、注意事项 四、实现步骤 五、关键代码 一、简…...

安全学习DAY16_信息打点-CDN绕过

信息打点-CDN绕过 文章目录 信息打点-CDN绕过本节思维导图相关链接&工具站&项目工具前置知识&#xff1a;CDN配置&#xff1a;配置1&#xff1a;加速域名-需要启用加速的域名配置2&#xff1a;加速区域-需要启用加速的地区配置3&#xff1a;加速类型-需要启用加速的资源…...

genism word2vec方法

文章目录 概述使用示例模型的保存与使用训练参数详解&#xff08;[原链接](https://blog.csdn.net/weixin_44852067/article/details/130221655)&#xff09;语料库训练 概述 word2vec是按句子来处理的Sentences(句子们) 使用示例 from gensim.models import Word2Vec #sent…...

vue3自定义样式-路由-axios拦截器

基于vue,vite和elementPlus 基于elementPlus自定义样式 history模式的路由 在根目录配置jsconfig.json&#xff0c;添加json的配置项。输入自动联想到src目录&#xff0c;是根路径的别名拦截器 如果存在多个接口地址&#xff0c;可以配置多个axios实例 数据持久化之后&#x…...

【mysql】事务的四种特性的理解

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…...

C++中List的实现

前言 数据结构中&#xff0c;我们了解到了链表&#xff0c;但是我们使用时需要自己去实现链表才能用&#xff0c;但是C出现了list将这一切皆变为现。list可以看作是一个带头双向循环的链表结构&#xff0c;并且可以在任意的正确范围内进行增删查改数据的容器。list容器一样也是…...

ElementUI 树形表格的使用以及表单嵌套树形表格的校验问题等汇总

目录 一、树形表格如何添加序号体现层级关系 二、树形表格展开收缩图标位置放置&#xff0c;设置指定列 三、表单嵌套树形表格的校验问题以及如何给校验rules传参 普通表格绑定如下&#xff1a;这种方法只能校验表格的第一层&#xff0c;树形需要递归设置子级节点prop。 树…...

解决“Unable to start embedded Tomcat“错误的完整指南

系列文章目录 文章目录 系列文章目录前言一、查看错误信息二、确认端口是否被占用三、检查依赖版本兼容性四、清理临时文件夹五、检查应用程序配置六、检查依赖冲突七、查看异常堆栈信息八、升级或降级Spring Boot版本总结前言 在使用Spring Boot开发应用程序时,有时可能会遇…...

JVS开源基础框架:平台基本信息介绍

JVS是面向软件开发团队可以快速实现应用的基础开发脚手架&#xff0c;主要定位于企业信息化通用底座&#xff0c;采用微服务分布式框架&#xff0c;提供丰富的基础功能&#xff0c;集成众多业务引擎&#xff0c;它灵活性强&#xff0c;界面化配置对开发者友好&#xff0c;底层容…...

C++ - max_element

在C中&#xff0c;要找到一个数组中的最大元素&#xff0c;可以使用 std::max_element 函数。以下是使用步骤&#xff1a; 包含 <algorithm> 头文件&#xff0c;这里定义了 std::max_element 函数。声明一个数组&#xff0c;并初始化它。使用 std::max_element 函数来查找…...

聚隆转债上市价格预测

聚隆转债 基本信息 转债名称&#xff1a;聚隆转债&#xff0c;评级&#xff1a;A&#xff0c;发行规模&#xff1a;2.185亿元。 正股名称&#xff1a;南京聚隆&#xff0c;今日收盘价&#xff1a;16.64元&#xff0c;转股价格&#xff1a;18.27元。 当前转股价值 转债面值 / 转…...

pytest自动生成测试类 demo

一、 pytest自动生成测试类 demo # -*- coding:utf-8 -*- # Author: 喵酱 # time: 2023 - 08 -15 # File: test4.py # desc: import pytest import unittest# 动态生成测试类def create_test_class(class_name:str, test_cases:list) -> type:"""生成测试类…...

服务器卡顿了该如何处理

服务器卡顿了该如何处理 当Windows系统的服务器出现卡顿问题时&#xff0c;以下是一些常见的故障排除步骤&#xff1a; 1.检查网络连接&#xff1a;确保服务器的网络连接正常。检查网络设备、交换机、防火墙等设备&#xff0c;确保它们正常运行。尝试通过其他计算机访问服务器…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

通过MicroSip配置自己的freeswitch服务器进行调试记录

之前用docker安装的freeswitch的&#xff0c;启动是正常的&#xff0c; 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...

【C++】纯虚函数类外可以写实现吗?

1. 答案 先说答案&#xff0c;可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...

Python训练营-Day26-函数专题1:函数定义与参数

题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一个名为 calculate_circle_area 的函数&#xff0c;该函数接收圆的半径 radius 作为参数&#xff0c;并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求&#xff1a;函数接收一个位置参数 radi…...