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

这篇具有很好参考价值的文章主要介绍了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: {
      //导出pdf
      async handleExportPdf() {
        this.handleElement()
      },

      handleElement() {
        if(this.selectionRows.length > 0) {
          this.pdfConfirmLoading = true
          const 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.length

          this.arrNum = len

          let that = this
          this.pdfVisible = true
          this.$nextTick(() => {
            for (let index = 0; index < groupedArray.length; index++) {
              const item = groupedArray[index];
              let num = index + 1
              let pageFooter = num + ' of ' + len;
              that.$refs['pdfTemplate' + num][0].setDataList(item, pageFooter);
            }

            this.pdfConfirmLoading = false
          })
        } else {
          this.$message.warning("请勾选数据")
        }
      },

      //确认导出pdf
      async confirmExportPdf() {
        this.pdfConfirmLoading = true
        await printPdf(this.$refs.pdfDiv, "courseSchedule")
        this.pdfConfirmLoading = false
        this.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 = list
      if(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 = dom

  let 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.width
    let 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) * contentHeight
    let 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导出分页方法文章来源地址https://www.toymoban.com/news/detail-658601.html

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转换为canvas
    const 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的导出的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Html转PDF,前端JS实现Html页面导出PDF(html2canvas+jspdf)

    一、背景介绍 ​ 当我们在不想改变后端代码的同时想是纯html页面导出PDF,那么(html2canvas+jspdf)就是无疑最好的选择,导出时它不占用我们服务器的资源,而是由用户本地自行执行js文件下载PDF,不占用我们系统的带宽,所以这无非是最好的选择方式。 二、疑问 1、为什么要

    2024年01月23日
    浏览(39)
  • Vue基于html2canvas和jspdf生成pdf文件,解决jspdf中文乱码及自动换行等问题

    在做项目时有这么一个需求,需要将当前页面指定区域的内容导出pdf到本地。借助了两个插件分别是html2canvas.js和pdf.js来实现。使用过程中遇到的问题及解决方法 解决一些问题: 导出按A4纸大小排列 预留页面边距的问题 内容过多自动分页的问题 直接使用jspdf中文乱码的问题

    2024年01月25日
    浏览(45)
  • 【无标题】使用html2canvas和jspdf生成的pdf在不同大小的屏幕下文字大小不一样

    问题:使用html2canvas和jspdf生成的pdf在不同大小的屏幕下文字大小不一样,在mac下,一切正常,看起来很舒服,但是当我把页面放在扩展屏幕下(27寸),再生成一个pdf,虽然排版一样,但是文字就变得非常小 下面的是在mac下的,上面是在扩展屏幕下的,最开始我以为是文字大

    2024年02月16日
    浏览(36)
  • 纯前端--原生js将html页面变成pdf文件(html2canvas+jsPDF)

    1、将文档放在本地,用原生js进行引用和使用。 ① 新建一个名为 html2canvas.min.js 的文件,并且将线上的内容进行复制。 ② 引入 js 文件: 2、使用 npm 进行安装使用: 待续。。。 github 中文网站 CDN Jspdf.es.js:ES 2015 模块格式。 Jspdf.umd.js:UMD模块格式,用于 AMD 或脚本标签加载

    2024年02月08日
    浏览(40)
  • 前端生成pdf之html2canvas+jsPDF,以及解决图片不显示bug

    开发背景: 需要给页面中相应的内容生成pdf,查找文档后发现要用到两个插件。html2canvas 以及 jsPDF html2canvas 给dom结构转化为canvas,然后生成各种类型图片 jsPDF 把canvas 生成的图片url 转化为pdf 参数 image:表示要插入的图片资源,可以是图片文件的路径或者base64编码字符串。

    2024年02月02日
    浏览(35)
  • html2Canvas+jsPDF 下载PDF 遇到跨域的对象存储的图片无法显示

        一、问题原因  对象存储的域名和你网址的域名不一样,此时用Canvas相关插件 将DOM元素转化为PDF,就会出现跨域错误。 二、解决办法  两步 1. 图片元素上设置属性  crossorigin=\\\"anonymous\\\"  支持原生img和eleme组件  2. 存储桶设置资源跨域访问 阿里腾讯云为例:↓ 阿里云OS

    2024年02月15日
    浏览(40)
  • vue使用html2canvas优化---节点过滤

    当你使用html2canvas对某个节点进行截图时,项目小dom节点少那还没什么性能问题,如果是个大项目,有成百上千个dom节点,那将是非常头疼的事情(产品经理:小张啊,你这个截图功能为什么需要这个长的时间,这让客户怎么用,重新改。小张:********...)。不多bb了,直接开

    2024年02月12日
    浏览(37)
  • vue-element使用html2canvas实现网页指定区域(指定dom元素)截图

    直接上代码: ** 如果要截取的dom元素、区域涉及到v-if或者v-show的条件表达式时,截取的方法请在nextTick里面调用----例如: this.$nextTick(() = { this.saveImageNew() }) 否之获取dom元素的时候会获取不到!!!!!!!!

    2024年02月04日
    浏览(48)
  • 前端vue基于html2canva jspdf 实现前端页面加水印 并导出页面PDF

    随着技术的发展,开发的复杂度也越来越高,传统开发方式将一个系统做成了整块应用,经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改,造成牵一发而动全身。通过组件化开发,可以有效实现单独开发,单独维护,而且他们之间可以随

    2024年02月03日
    浏览(29)
  • Vue使用html2canvas将DOM节点生成对应的PDF

    要通过Vue使用html2canvas将DOM节点生成对应的PDF,您需要安装html2canvas和jspdf这两个库。html2canvas用于将DOM节点转换为Canvas,而jspdf用于将Canvas转换为PDF。以下是一个简单的示例代码,展示了如何使用html2canvas和jspdf生成PDF文件: 首先,安装html2canvas和jspdf依赖: 然后,在Vue组件中

    2024年02月11日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包