vue2技术栈将表单内容转为PDF并下载

使用 html2canvas 和 jspdf 两个三方库实现。

html2canvas 是将表单内容以固定像素的方式截取变成图片

jspdf 是将图片转为 pdf

  1. 下载两个库

    npm install html2pdf.js
    npm install jspdf.js

  2. 创建一个 js 工具类

    import html2canvas from 'html2canvas'
    import jsPDF from 'jspdf'
    import { Message } from 'element-ui'

    /**

    • 导出PDF(自动分页,不截断文字)
    • @param {HTMLElement} element 要导出的DOM元素
    • @param {string} filename 文件名,默认 '报告.pdf'
      */
      export async function exportPDF(element, filename = '报告.pdf') {
      if (!element) {
      // console.error('导出失败:目标元素不存在')
      Message.error('导出失败:目标元素不存在')
      return
      }

    // 显示加载提示
    const loadingTip = document.createElement('div')
    loadingTip.textContent = '正在生成PDF,请稍候...'
    loadingTip.style.cssText = position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.7); color: white; padding: 12px 24px; border-radius: 8px; z-index: 9999; font-size: 14px; font-family: system-ui, -apple-system, sans-serif;
    document.body.appendChild(loadingTip)

    // 等待图片加载
    const images = element.querySelectorAll('img')
    await Promise.all(Array.from(images).map(img => {
    if (img.complete) return Promise.resolve()
    return new Promise((resolve) => {
    img.onload = resolve
    img.onerror = resolve
    })
    }))

    await new Promise(resolve => setTimeout(resolve, 100))

    // 获取尺寸
    const contentWidth = element.scrollWidth
    const contentHeight = element.scrollHeight

    // 初始化PDF (A4竖版)
    const pdf = new jsPDF('p', 'mm', 'a4')
    const pageWidth = pdf.internal.pageSize.getWidth()
    const pageHeight = pdf.internal.pageSize.getHeight()
    const margin = 10
    const pdfContentWidth = pageWidth - margin * 2
    const pdfContentHeight = pageHeight - margin * 2

    // 计算缩放比例
    const pxToMm = 1 / 3.78
    const scale = pdfContentWidth / (contentWidth * pxToMm)
    const maxPageHeightPx = pdfContentHeight / pxToMm / scale

    // 分页截图
    let currentY = 0
    let pageNum = 0

    while (currentY < contentHeight) {
    const captureHeight = Math.min(maxPageHeightPx, contentHeight - currentY)

    复制代码
     if (captureHeight < 10) break
    
     const canvas = await html2canvas(element, {
       scale: 2,
       useCORS: true,
       backgroundColor: '#ffffff',
       x: 0,
       y: currentY,
       width: contentWidth,
       height: captureHeight,
       scrollX: 0,
       scrollY: 0,
       onclone: (clonedDoc) => {
         // 隐藏带 pdf-ignore 类的元素
         clonedDoc.querySelectorAll('.pdf-ignore').forEach(el => {
           el.style.display = 'none'
         })
       }
     })
    
     const imgData = canvas.toDataURL('image/jpeg', 0.9)
    
     if (pageNum > 0) {
       pdf.addPage()
     }
    
     const imgHeight = captureHeight * scale * pxToMm
     pdf.addImage(imgData, 'JPEG', margin, margin, pdfContentWidth, imgHeight)
    
     currentY += captureHeight
     pageNum++

    }

    if (pageNum === 0) {
    // console.error('导出失败:没有可导出的内容')
    Message.error('导出失败:没有可导出的内容')
    return
    }

    // 移除加载提示
    if (loadingTip && loadingTip.parentNode) {
    document.body.removeChild(loadingTip)
    }

    pdf.save(filename)
    }

    /**

    • 打印表单(不改变原页面)
    • @param {HTMLElement} element 要打印的DOM元素
    • @param {string} title 打印标题
      */
      export function printForm(element, title = '打印文档') {
      if (!element) {
      console.error('打印失败:目标元素不存在')
      return
      }

    // 克隆要打印的内容(不影响原页面)
    const printContent = element.cloneNode(true)

    // 移除不需要打印的元素
    printContent.querySelectorAll('.pdf-ignore, .no-print, button, .el-button').forEach(el => {
    el.remove()
    })

    // 获取原页面所有样式
    const styles = document.querySelectorAll('link[rel="stylesheet"], style')
    let stylesHTML = ''
    styles.forEach(style => {
    if (style.tagName === 'LINK') {
    stylesHTML += <link href="${style.href}" rel="stylesheet">
    } else if (style.tagName === 'STYLE') {
    stylesHTML += <style>${style.innerHTML}</style>
    }
    })

    // 创建打印窗口
    const printWindow = window.open('', '_blank')

    printWindow.document.write(`
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>{title} {stylesHTML}
    <style>
    /* 重置body边距,保持原样 */
    body {
    margin: 0 !important;
    padding: 20px !important;
    background: white;
    }

    复制代码
         /* 打印时隐藏按钮类 */
         @media print {
           body {
             padding: 0 !important;
           }
           .pdf-ignore, .no-print, button {
             display: none !important;
           }
         }
       </style>
     </head>
     <body>
       ${printContent.outerHTML}
     </body>
     </html>

    `)

    printWindow.document.close()

    printWindow.onload = () => {
    printWindow.print()
    }
    }

    export default { exportPDF, printForm }

  3. 使用该工具类

引入该工具类

复制代码
import { printForm, exportPDF   }from '@/utils/htmlToPdf'  // 引入pdf工具类

定义一个按钮

复制代码
  <el-button type="primary" @click="exportPDF" size="small ">导出为PDF</el-button>

定义方法

使用一个div将需要转为pdf的区域全部包裹起来

复制代码
async exportPDF() {
   await exportPDF(this.$refs.formContainer, '测试报告')
},
相关推荐
庖丁AI1 天前
合同比对工具怎么选?Word、PDF 和扫描件差异对比思路
pdf·word
包子源1 天前
PDF 转 Word/Excel 全链路实战:Next.js + 阿里云文档智能
pdf·word·excel
敲代码的鱼哇1 天前
PDF 预览与签名批注写回 支持安卓 iOS 鸿蒙 UTS插件
android·ios·pdf·鸿蒙·harmony
开开心心_Every2 天前
能把网页藏在Word里的实用摸鱼工具
人工智能·科技·目标跟踪·pdf·计算机外设·语音识别·mllib
Raink老师2 天前
【AI面试临阵磨枪-64】设计多模态(文本 + 图片 + PDF)AI 助手系统
人工智能·pdf
优化控制仿真模型2 天前
【26年专四】英语专业四级TEM4历年真题及答案电子版PDF(2009-2025年)
经验分享·pdf
其实秋天的枫2 天前
【26年专四】英语专业四级TEM4历年真题及答案电子版PDF(2009-2025年)
经验分享·pdf