当前位置: 首页 > 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()})
}
http://www.lryc.cn/news/126587.html

相关文章:

  • Linux学习之firewallD
  • 【JS学习】Object.assign 用法介绍
  • 【uni-app报错】获取用户收货地址uni.chooseAddress()报错问题
  • 机器学习、cv、nlp的一些前置知识
  • Steam 灵感的游戏卡悬停效果
  • [Openwrt]一步一步搭建MT7981A uboot、atf、openwrt-21.02开发环境操作说明
  • Unity C# 之 Azure 微软SSML语音合成TTS流式获取音频数据以及表情嘴型 Animation 的简单整理
  • 安全学习DAY16_信息打点-CDN绕过
  • genism word2vec方法
  • vue3自定义样式-路由-axios拦截器
  • 【mysql】事务的四种特性的理解
  • C++中List的实现
  • ElementUI 树形表格的使用以及表单嵌套树形表格的校验问题等汇总
  • 解决“Unable to start embedded Tomcat“错误的完整指南
  • JVS开源基础框架:平台基本信息介绍
  • C++ - max_element
  • 聚隆转债上市价格预测
  • pytest自动生成测试类 demo
  • 服务器卡顿了该如何处理
  • 常量对象 只能调用 常成员函数
  • Progressive-Hint Prompting Improves Reasoning in Large Language Models
  • mysql中INSERT INTO ... ON DUPLICATE KEY UPDATE的用法,以及与REPLACE INTO 语句用法的异同
  • wireshark 实用过滤表达式(针对ip、协议、端口、长度和内容)
  • MATLAB图形窗口固定
  • 【数据结构】_7.二叉树概念与基本操作
  • Flink之Partitioner(分区规则)
  • tk切换到mac的code分享
  • spark的standalone 分布式搭建
  • 浅析基于视频汇聚与AI智能分析的新零售方案设计
  • SpringMVC之异常处理