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> {{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导出 以导出横向,A4大小的pdf为例 规律:1. html2canvas 中,在保持jsPDF中的宽高不变的情况下,设置html2canvas中的 width 和 height 值越小&a…...
Linux学习之firewallD
systemctl status firewalld.service查看一下firewalld服务的状态,发现状态是inactive (dead)。 systemctl start firewalld.service启动firewalld,systemctl status firewalld.service查看一下firewalld服务的状态,发现状态是active (runni…...
【JS学习】Object.assign 用法介绍
Object.assign 是ES6中的一个方法。该方法能够实现对象的浅复制以及对象合并。Object.assign 并不会修改目标对象本身,而是返回一个新的对象,其中包含了所有源对象的属性。 例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 原因: 小程序配置 / 全局配置 (qq.com) 解决: 登录小程序后台申请接口 按照流程申请即可 在项目根目录中找到 manifest.json 文件,在左侧导航栏选择源码视图&a…...
机器学习、cv、nlp的一些前置知识
为节省篇幅,不标注文章来源和文章的问题场景。大部分是我的通俗理解。 文章目录 向量关于向量的偏导数:雅可比矩阵二阶导数矩阵:海森矩阵随机变量随机场伽马函数beta分布数学术语坐标上升法协方差训练集,验证集,测试集…...
Steam 灵感的游戏卡悬停效果
先看效果: 再看代码(查看更多): <!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绕过本节思维导图相关链接&工具站&项目工具前置知识:CDN配置:配置1:加速域名-需要启用加速的域名配置2:加速区域-需要启用加速的地区配置3:加速类型-需要启用加速的资源…...
genism word2vec方法
文章目录 概述使用示例模型的保存与使用训练参数详解([原链接](https://blog.csdn.net/weixin_44852067/article/details/130221655))语料库训练 概述 word2vec是按句子来处理的Sentences(句子们) 使用示例 from gensim.models import Word2Vec #sent…...
vue3自定义样式-路由-axios拦截器
基于vue,vite和elementPlus 基于elementPlus自定义样式 history模式的路由 在根目录配置jsconfig.json,添加json的配置项。输入自动联想到src目录,是根路径的别名拦截器 如果存在多个接口地址,可以配置多个axios实例 数据持久化之后&#x…...
【mysql】事务的四种特性的理解
🌇个人主页:平凡的小苏 📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风…...
C++中List的实现
前言 数据结构中,我们了解到了链表,但是我们使用时需要自己去实现链表才能用,但是C出现了list将这一切皆变为现。list可以看作是一个带头双向循环的链表结构,并且可以在任意的正确范围内进行增删查改数据的容器。list容器一样也是…...
ElementUI 树形表格的使用以及表单嵌套树形表格的校验问题等汇总
目录 一、树形表格如何添加序号体现层级关系 二、树形表格展开收缩图标位置放置,设置指定列 三、表单嵌套树形表格的校验问题以及如何给校验rules传参 普通表格绑定如下:这种方法只能校验表格的第一层,树形需要递归设置子级节点prop。 树…...
解决“Unable to start embedded Tomcat“错误的完整指南
系列文章目录 文章目录 系列文章目录前言一、查看错误信息二、确认端口是否被占用三、检查依赖版本兼容性四、清理临时文件夹五、检查应用程序配置六、检查依赖冲突七、查看异常堆栈信息八、升级或降级Spring Boot版本总结前言 在使用Spring Boot开发应用程序时,有时可能会遇…...
JVS开源基础框架:平台基本信息介绍
JVS是面向软件开发团队可以快速实现应用的基础开发脚手架,主要定位于企业信息化通用底座,采用微服务分布式框架,提供丰富的基础功能,集成众多业务引擎,它灵活性强,界面化配置对开发者友好,底层容…...
C++ - max_element
在C中,要找到一个数组中的最大元素,可以使用 std::max_element 函数。以下是使用步骤: 包含 <algorithm> 头文件,这里定义了 std::max_element 函数。声明一个数组,并初始化它。使用 std::max_element 函数来查找…...
聚隆转债上市价格预测
聚隆转债 基本信息 转债名称:聚隆转债,评级:A,发行规模:2.185亿元。 正股名称:南京聚隆,今日收盘价:16.64元,转股价格: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系统的服务器出现卡顿问题时,以下是一些常见的故障排除步骤: 1.检查网络连接:确保服务器的网络连接正常。检查网络设备、交换机、防火墙等设备,确保它们正常运行。尝试通过其他计算机访问服务器…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...
数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...
