使用 html2canvas 和 jspdf 两个三方库实现。
html2canvas 是将表单内容以固定像素的方式截取变成图片
jspdf 是将图片转为 pdf
-
下载两个库
npm install html2pdf.js
npm install jspdf.js -
创建一个 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 = 0while (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 }
-
使用该工具类
引入该工具类
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, '测试报告')
},