使用 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}</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, '测试报告')
},

更多推荐