Vue環(huán)境下數(shù)據(jù)導出為PDF的最全指南
1. 前言
在現(xiàn)代Web應用開發(fā)中,將數(shù)據(jù)導出為PDF是一項常見且重要的功能需求。PDF作為一種通用的文檔格式,具有跨平臺、保持格式一致、易于打印等優(yōu)勢。Vue.js作為當前流行的前端框架,提供了多種實現(xiàn)PDF導出的方法。本文將全面探討Vue環(huán)境下實現(xiàn)PDF導出的7種主要方法,包括原生瀏覽器API、常用第三方庫方案以及服務器端導出方案,每種方法都將提供詳細的代碼示例和優(yōu)劣分析。
2. 原生瀏覽器打印方案
2.1 使用window.print()實現(xiàn)
這種方法利用瀏覽器自帶的打印功能,通過CSS媒體查詢控制打印樣式。
實現(xiàn)原理:
- 創(chuàng)建專門用于打印的組件或視圖
- 使用@media print定義打印樣式
- 調(diào)用window.print()觸發(fā)瀏覽器打印對話框
代碼示例:
<template> <div> <button @click="print">打印PDF</button> <div ref="printContent" class="print-container"> <h1>銷售報表</h1> <table> <thead> <tr> <th>日期</th> <th>產(chǎn)品</th> <th>數(shù)量</th> <th>金額</th> </tr> </thead> <tbody> <tr v-for="item in salesData" :key="item.id"> <td>{{ item.date }}</td> <td>{{ item.product }}</td> <td>{{ item.quantity }}</td> <td>{{ formatCurrency(item.amount) }}</td> </tr> </tbody> </table> </div> </div> </template> <script> export default { data() { return { salesData: [ { id: 1, date: '2023-01-01', product: '產(chǎn)品A', quantity: 10, amount: 1000 }, { id: 2, date: '2023-01-02', product: '產(chǎn)品B', quantity: 5, amount: 2500 } ] } }, methods: { print() { window.print() }, formatCurrency(value) { return '¥' + value.toLocaleString() } } } </script> <style> @media print { body * { visibility: hidden; } .print-container, .print-container * { visibility: visible; } .print-container { position: absolute; left: 0; top: 0; width: 100%; } /* 打印分頁控制 */ table { page-break-inside: auto; } tr { page-break-inside: avoid; page-break-after: auto; } thead { display: table-header-group; } tfoot { display: table-footer-group; } } ???????/* 屏幕樣式與打印樣式分離 */ .print-container { display: none; } @media print { .print-container { display: block; } } </style>
優(yōu)點:
- 零依賴,不增加項目體積
- 實現(xiàn)簡單,適合簡單打印需求
- 用戶可以選擇"另存為PDF"(大多數(shù)現(xiàn)代瀏覽器支持)
缺點:
- 依賴用戶操作,無法自動化
- 打印樣式控制有限
- 無法生成復雜的PDF文檔
- 不同瀏覽器表現(xiàn)可能不一致
2.2 使用CSS Paged Media模塊
對于更專業(yè)的打印需求,可以使用CSS Paged Media模塊定義分頁、頁眉頁腳等。
代碼示例:
@page { size: A4; margin: 1cm; @top-left { content: "公司名稱"; font-size: 10pt; } @top-right { content: "頁碼 " counter(page) " / " counter(pages); font-size: 10pt; } @bottom-center { content: "機密文件"; font-size: 8pt; color: #999; } } @media print { h1 { page-break-before: always; } table { page-break-inside: avoid; } .page-break { page-break-after: always; } }
優(yōu)點:
- 更專業(yè)的打印控制
- 支持頁碼、頁眉頁腳等高級功能
- 仍然是純CSS方案,無JavaScript依賴
缺點:
- 瀏覽器支持不一致(特別是復雜特性)
- 學習曲線較陡
- 仍然依賴瀏覽器打印功能
3. 常用第三方庫方案
3.1 使用jsPDF
jsPDF是最流行的JavaScript PDF生成庫之一,可以直接在瀏覽器中生成PDF。
安裝:
npm install jspdf
基礎(chǔ)實現(xiàn):
import jsPDF from 'jspdf' export function generatePDF(title, data, columns, filename = 'export.pdf') { const doc = new jsPDF() // 添加標題 doc.setFontSize(18) doc.text(title, 14, 15) // 添加表頭 doc.setFontSize(12) let y = 30 columns.forEach((col, index) => { doc.text(col.label, 14 + index * 40, y) }) // 添加數(shù)據(jù)行 doc.setFontSize(10) data.forEach((row, rowIndex) => { y = 40 + rowIndex * 10 if (y > 280) { // 接近頁面底部 doc.addPage() y = 20 } columns.forEach((col, colIndex) => { doc.text(String(row[col.field]), 14 + colIndex * 40, y) }) }) // 保存文件 doc.save(filename) } // Vue組件中使用 methods: { exportPDF() { const data = [ { id: 1, name: '產(chǎn)品A', price: 100, stock: 50 }, { id: 2, name: '產(chǎn)品B', price: 200, stock: 30 } ] const columns = [ { label: 'ID', field: 'id' }, { label: '產(chǎn)品名稱', field: 'name' }, { label: '價格', field: 'price' }, { label: '庫存', field: 'stock' } ] generatePDF('產(chǎn)品列表', data, columns) } }
高級功能示例:
function generateAdvancedPDF() { const doc = new jsPDF({ orientation: 'landscape', unit: 'mm', format: 'a4' }) // 設(shè)置元數(shù)據(jù) doc.setProperties({ title: '高級報表', subject: '2023年度銷售數(shù)據(jù)', author: '銷售系統(tǒng)', keywords: '銷售, 報表, 2023', creator: '公司銷售系統(tǒng)' }) // 添加公司logo const imgData = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...' doc.addImage(imgData, 'PNG', 10, 10, 50, 15) // 添加標題 doc.setFont('helvetica', 'bold') doc.setFontSize(22) doc.setTextColor(40, 40, 40) doc.text('2023年度銷售報表', 105, 20, { align: 'center' }) // 添加表格 const headers = [['產(chǎn)品', 'Q1', 'Q2', 'Q3', 'Q4', '總計']] const salesData = [ ['產(chǎn)品A', 1000, 1500, 1200, 1800, 5500], ['產(chǎn)品B', 800, 900, 1000, 1200, 3900], ['產(chǎn)品C', 500, 600, 700, 900, 2700] ] doc.autoTable({ head: headers, body: salesData, startY: 40, theme: 'grid', headStyles: { fillColor: [22, 160, 133], textColor: 255, fontStyle: 'bold' }, alternateRowStyles: { fillColor: [240, 240, 240] }, margin: { top: 40 }, didDrawPage: function(data) { // 頁腳 doc.setFontSize(10) doc.setTextColor(150) const pageCount = doc.internal.getNumberOfPages() doc.text(`第 ${doc.internal.getCurrentPageInfo().pageNumber} 頁 / 共 ${pageCount} 頁`, data.settings.margin.left, doc.internal.pageSize.height - 10 ) } }) // 添加折線圖(使用jsPDF的簡單繪圖功能) doc.setDrawColor(100, 100, 255) doc.setFillColor(100, 100, 255) const points = [ { x: 60, y: 150, q: 'Q1' }, { x: 90, y: 130, q: 'Q2' }, { x: 120, y: 140, q: 'Q3' }, { x: 150, y: 120, q: 'Q4' } ] // 繪制折線 points.forEach((point, i) => { if (i > 0) { doc.line(points[i-1].x, points[i-1].y, point.x, point.y) } doc.circle(point.x, point.y, 2, 'F') doc.text(point.q, point.x, point.y + 10) }) doc.save('advanced_report.pdf') }
優(yōu)點:
- 純前端實現(xiàn),不依賴服務器
- 功能豐富,支持文本、表格、簡單圖形等
- 支持自定義字體
- 活躍的社區(qū)和良好的文檔
缺點:
- 復雜布局實現(xiàn)較困難
- 原生不支持復雜表格樣式(需要插件)
- 中文支持需要額外配置字體
3.2 使用html2canvas + jsPDF
這種方案先將HTML轉(zhuǎn)換為canvas,再將canvas轉(zhuǎn)為PDF,適合需要精確復制頁面樣式的場景。
安裝:
npm install html2canvas jspdf
基礎(chǔ)實現(xiàn):
import html2canvas from 'html2canvas' import jsPDF from 'jspdf' export async function exportHTMLToPDF(element, filename = 'export.pdf', options = {}) { const canvas = await html2canvas(element, { scale: 2, // 提高分辨率 logging: false, useCORS: true, allowTaint: true, ...options }) const imgData = canvas.toDataURL('image/png') const pdf = new jsPDF({ orientation: canvas.width > canvas.height ? 'landscape' : 'portrait', unit: 'mm' }) const pageWidth = pdf.internal.pageSize.getWidth() const pageHeight = pdf.internal.pageSize.getHeight() const imgWidth = pageWidth const imgHeight = (canvas.height * imgWidth) / canvas.width pdf.addImage(imgData, 'PNG', 0, 0, imgWidth, imgHeight) pdf.save(filename) } ???????// Vue組件中使用 methods: { async exportPage() { const element = this.$refs.pdfContent await exportHTMLToPDF(element, 'page_export.pdf', { scale: 3 // 更高分辨率 }) } }
高級功能示例:
async function exportMultiPagePDF() { const elements = document.querySelectorAll('.report-page') const pdf = new jsPDF('p', 'mm', 'a4') for (let i = 0; i < elements.length; i++) { const element = elements[i] const canvas = await html2canvas(element, { scale: 2, logging: true }) const imgData = canvas.toDataURL('image/png') const imgWidth = pdf.internal.pageSize.getWidth() const imgHeight = (canvas.height * imgWidth) / canvas.width if (i > 0) { pdf.addPage() } pdf.addImage(imgData, 'PNG', 0, 0, imgWidth, imgHeight) // 添加頁腳 pdf.setFontSize(10) pdf.setTextColor(150) pdf.text( `第 ${i + 1} 頁`, pdf.internal.pageSize.getWidth() / 2, pdf.internal.pageSize.getHeight() - 10, { align: 'center' } ) } pdf.save('multi_page_report.pdf') }
優(yōu)點:
- 精確復制頁面樣式和布局
- 支持復雜HTML結(jié)構(gòu)和CSS樣式
- 可以處理動態(tài)生成的內(nèi)容
- 支持多頁導出
缺點:
- 生成的PDF本質(zhì)是圖片,文字不可選擇
- 大頁面可能導致內(nèi)存問題
- 性能較差,特別是復雜頁面
- 分頁控制較困難
3.3 使用pdfmake
pdfmake是一個聲明式的PDF生成庫,使用JSON格式定義文檔結(jié)構(gòu)。
安裝:
npm install pdfmake
基礎(chǔ)實現(xiàn):
import pdfMake from 'pdfmake/build/pdfmake' import pdfFonts from 'pdfmake/build/vfs_fonts' pdfMake.vfs = pdfFonts.pdfMake.vfs export function generatePDFWithPdfMake(data, filename = 'document.pdf') { const docDefinition = { content: [ { text: '銷售報表', style: 'header' }, '\n\n', { table: { headerRows: 1, widths: ['*', 'auto', 'auto', 'auto'], body: [ ['產(chǎn)品', '季度1', '季度2', '季度3'], ...data.map(item => [ item.product, { text: item.q1, alignment: 'right' }, { text: item.q2, alignment: 'right' }, { text: item.q3, alignment: 'right' } ]) ] } } ], styles: { header: { fontSize: 18, bold: true, alignment: 'center' } }, defaultStyle: { font: 'SimSun' } } pdfMake.createPdf(docDefinition).download(filename) } ???????// Vue組件中使用 methods: { exportData() { const data = [ { product: '產(chǎn)品A', q1: 1000, q2: 1500, q3: 1200 }, { product: '產(chǎn)品B', q1: 800, q2: 900, q3: 1000 } ] generatePDFWithPdfMake(data, 'sales_report.pdf') } }
高級功能示例:
function generateAdvancedPdfMakeDocument() { const docDefinition = { pageSize: 'A4', pageMargins: [40, 60, 40, 60], header: function(currentPage, pageCount) { return { text: `第 ${currentPage} 頁 / 共 ${pageCount} 頁`, alignment: 'right', margin: [0, 20, 20, 0] } }, footer: function(currentPage, pageCount) { return { text: '公司機密 - 未經(jīng)授權(quán)禁止復制', alignment: 'center', fontSize: 8, margin: [0, 0, 0, 20] } }, content: [ { columns: [ { width: 100, image: 'logo', fit: [80, 80] }, { width: '*', text: '2023年度財務報告', style: 'reportTitle' } ] }, '\n\n', { text: '1. 銷售概覽', style: 'sectionHeader' }, { text: '本年度公司整體銷售額達到¥1,200萬,同比增長15%。主要增長來自產(chǎn)品線A和B。', style: 'paragraph' }, '\n', { style: 'tableExample', table: { widths: ['*', '*', '*', '*'], body: [ [ { text: '產(chǎn)品線', style: 'tableHeader' }, { text: 'Q1', style: 'tableHeader' }, { text: 'Q2', style: 'tableHeader' }, { text: 'Q3', style: 'tableHeader' } ], ['產(chǎn)品A', '320萬', '350萬', '380萬'], ['產(chǎn)品B', '280萬', '300萬', '320萬'], ['產(chǎn)品C', '150萬', '160萬', '170萬'], [ { text: '總計', style: 'tableHeader' }, { text: '750萬', colSpan: 3, style: 'tableHeader' }, {}, {} ] ] } }, '\n\n', { text: '2. 成本分析', style: 'sectionHeader', pageBreak: 'before' }, { canvas: [ { type: 'rect', x: 0, y: 0, w: 500, h: 20, color: '#eeeeee' }, { type: 'rect', x: 0, y: 0, w: 350, h: 20, color: '#cc0000' } ] }, { text: '成本構(gòu)成示意圖', alignment: 'center', italics: true, fontSize: 10, margin: [0, 5, 0, 0] } ], styles: { reportTitle: { fontSize: 24, bold: true, alignment: 'center', margin: [0, 20, 0, 0] }, sectionHeader: { fontSize: 16, bold: true, margin: [0, 15, 0, 5] }, paragraph: { fontSize: 12, lineHeight: 1.5, margin: [0, 0, 0, 10] }, tableExample: { margin: [0, 5, 0, 15] }, tableHeader: { bold: true, fontSize: 13, color: 'black', fillColor: '#dddddd' } }, images: { logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...' } } pdfMake.createPdf(docDefinition).open() }
優(yōu)點:
- 聲明式API,易于理解和使用
- 強大的布局和樣式控制能力
- 內(nèi)置分頁、頁眉頁腳支持
- 支持表格、列表、圖片等多種元素
缺點:
- 中文支持需要額外配置字體
- 學習曲線較陡(特別是復雜布局)
- 文檔結(jié)構(gòu)可能變得復雜
- 性能不如原生jsPDF
3.4 使用vue-pdf
vue-pdf是一個Vue專用的PDF生成和顯示組件庫。
安裝:
npm install @tato30/vue-pdf
基礎(chǔ)實現(xiàn):
<template> <div> <button @click="generatePDF">生成PDF</button> <div ref="pdfContent"> <h1>員工信息</h1> <table> <tr> <th>ID</th> <th>姓名</th> <th>部門</th> </tr> <tr v-for="emp in employees" :key="emp.id"> <td>{{ emp.id }}</td> <td>{{ emp.name }}</td> <td>{{ emp.department }}</td> </tr> </table> </div> </div> </template> <script> import VuePdf from '@tato30/vue-pdf' ???????export default { components: { VuePdf }, data() { return { employees: [ { id: 1, name: '張三', department: '研發(fā)' }, { id: 2, name: '李四', department: '市場' } ] } }, methods: { async generatePDF() { const pdf = new VuePdf() // 添加內(nèi)容 pdf.addText('員工信息', { fontSize: 18, align: 'center' }) pdf.addBreak(20) // 添加表格 pdf.addTable({ headers: ['ID', '姓名', '部門'], rows: this.employees.map(emp => [emp.id, emp.name, emp.department]), styles: { cellPadding: 5, headerColor: '#eeeeee' } }) // 生成并下載 pdf.download('員工信息.pdf') } } } </script>
優(yōu)點:
- 專為Vue設(shè)計,API更符合Vue習慣
- 輕量級,易于集成
- 支持基本的PDF生成功能
缺點:
- 功能相對有限
- 社區(qū)支持不如jsPDF或pdfmake
- 文檔較少
4. 服務器端導出方案
4.1 前端請求服務器生成PDF
這種方案將PDF生成邏輯放在服務器端,前端只負責觸發(fā)和下載。
前端代碼:
export function requestPDFExport(params) { return axios({ url: '/api/export-pdf', method: 'POST', data: params, responseType: 'blob' }).then(response => { const blob = new Blob([response.data], { type: 'application/pdf' }) const url = window.URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.setAttribute('download', 'server_export.pdf') document.body.appendChild(link) link.click() document.body.removeChild(link) }) } ???????// Vue組件中使用 methods: { async exportFromServer() { try { this.loading = true await requestPDFExport({ reportType: 'sales', year: 2023, format: 'A4' }) } catch (error) { console.error('導出失敗:', error) } finally { this.loading = false } } }
Node.js服務器端示例(使用pdfkit):
const express = require('express') const PDFDocument = require('pdfkit') const app = express() app.post('/api/export-pdf', async (req, res) => { try { const { reportType, year, format } = req.body // 創(chuàng)建PDF文檔 const doc = new PDFDocument({ size: format || 'A4' }) // 設(shè)置響應頭 res.setHeader('Content-Type', 'application/pdf') res.setHeader('Content-Disposition', 'attachment; filename="report.pdf"') // 管道傳輸?shù)巾憫? doc.pipe(res) // 添加內(nèi)容 doc.fontSize(25).text(`${year}年度${getReportTitle(reportType)}`, { align: 'center' }) doc.moveDown() doc.fontSize(12).text('生成日期: ' + new Date().toLocaleDateString()) // 添加表格數(shù)據(jù) const data = await getReportData(reportType, year) drawTable(doc, data) // 結(jié)束并發(fā)送 doc.end() } catch (error) { console.error('PDF生成錯誤:', error) res.status(500).send('PDF生成失敗') } }) function drawTable(doc, data) { const startY = 150 const rowHeight = 30 const colWidth = 150 const headers = ['產(chǎn)品', 'Q1', 'Q2', 'Q3', 'Q4', '總計'] // 表頭 doc.font('Helvetica-Bold') headers.forEach((header, i) => { doc.text(header, 50 + i * colWidth, startY, { width: colWidth, align: 'center' }) }) // 表格內(nèi)容 doc.font('Helvetica') data.forEach((row, rowIndex) => { const y = startY + (rowIndex + 1) * rowHeight // 繪制行背景 if (rowIndex % 2 === 0) { doc.fillColor('#f5f5f5') .rect(50, y - 10, colWidth * headers.length, rowHeight) .fill() doc.fillColor('black') } // 繪制單元格文本 Object.values(row).forEach((value, colIndex) => { doc.text(String(value), 50 + colIndex * colWidth, y, { width: colWidth, align: colIndex > 0 ? 'right' : 'left' }) }) }) } app.listen(3000, () => console.log('Server running on port 3000'))
優(yōu)點:
- 處理大數(shù)據(jù)量更高效
- 減輕前端壓力
- 可以復用服務器端數(shù)據(jù)處理邏輯
- 更安全,業(yè)務邏輯不暴露在客戶端
- 支持更復雜的PDF生成(如使用專業(yè)PDF庫)
缺點:
- 增加服務器負載
- 需要網(wǎng)絡(luò)請求,可能有延遲
- 實現(xiàn)復雜度更高
4.2 使用無頭瀏覽器生成PDF
對于需要精確復制網(wǎng)頁樣式的場景,可以在服務器端使用無頭瀏覽器(如Puppeteer)生成PDF。
Node.js服務器示例(使用Puppeteer):
const express = require('express') const puppeteer = require('puppeteer') const app = express() app.post('/api/export-pdf', async (req, res) => { let browser = null try { const { url, options } = req.body browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] }) const page = await browser.newPage() // 如果是URL,直接導航;如果是HTML內(nèi)容,設(shè)置內(nèi)容 if (url.startsWith('http')) { await page.goto(url, { waitUntil: 'networkidle2' }) } else { await page.setContent(url) } // 生成PDF const pdf = await page.pdf({ format: 'A4', printBackground: true, margin: { top: '20mm', right: '20mm', bottom: '20mm', left: '20mm' }, ...options }) // 發(fā)送PDF res.setHeader('Content-Type', 'application/pdf') res.setHeader('Content-Disposition', 'attachment; filename="export.pdf"') res.send(pdf) } catch (error) { console.error('PDF生成錯誤:', error) res.status(500).send('PDF生成失敗') } finally { if (browser) { await browser.close() } } }) ???????app.listen(3000, () => console.log('Server running on port 3000'))
優(yōu)點:
- 精確復制網(wǎng)頁樣式和布局
- 支持所有現(xiàn)代CSS特性
- 可以處理動態(tài)內(nèi)容
- 支持截圖、PDF等多種輸出
缺點:
- 資源消耗大(需要運行瀏覽器實例)
- 性能較差
- 部署復雜度高
5. 方法對比與選擇指南
5.1 功能對比表
方法 | 生成方式 | 樣式保真度 | 復雜度 | 性能 | 適用場景 |
---|---|---|---|---|---|
瀏覽器打印 | 客戶端 | 中等 | 低 | 高 | 簡單打印需求 |
jsPDF | 客戶端 | 低 | 中 | 高 | 編程式生成簡單PDF |
html2canvas+jsPDF | 客戶端 | 高 | 中 | 中 | 精確復制頁面樣式 |
pdfmake | 客戶端 | 中 | 中 | 中 | 結(jié)構(gòu)化文檔生成 |
vue-pdf | 客戶端 | 低 | 低 | 高 | 簡單Vue集成 |
服務器生成 | 服務端 | 可高可低 | 高 | 依賴實現(xiàn) | 復雜/大數(shù)據(jù)量PDF |
無頭瀏覽器 | 服務端 | 極高 | 很高 | 低 | 精確復制復雜頁面 |
5.2 選擇建議
簡單打印需求:使用瀏覽器打印方案,成本最低
編程式生成簡單PDF:選擇jsPDF,純前端實現(xiàn)
精確復制頁面樣式:html2canvas+jsPDF組合或服務器端無頭瀏覽器方案
結(jié)構(gòu)化文檔生成:pdfmake提供更直觀的聲明式API
Vue項目快速集成:考慮vue-pdf組件
大數(shù)據(jù)量或復雜處理:優(yōu)先服務器端方案
最高樣式保真度:無頭瀏覽器方案(如Puppeteer)
5.3 性能優(yōu)化建議
分頁處理:對于大數(shù)據(jù)集,實現(xiàn)分頁或分塊生成
懶加載資源:只在需要時加載PDF生成庫
Web Worker:將耗時的生成過程放在Worker線程
服務器緩存:對頻繁請求的相同內(nèi)容緩存生成的PDF
按需生成:提供預覽功能,只在用戶確認后生成完整PDF
資源優(yōu)化:壓縮圖片等資源減少PDF體積
6. 最佳實踐示例
完整的Vue PDF導出組件
<template> <div class="pdf-export"> <button @click="showModal = true" class="export-button" > <i class="icon-pdf"></i> 導出PDF </button> <div v-if="showModal" class="export-modal"> <div class="modal-content"> <h3>PDF導出選項</h3> <div class="form-group"> <label>文件名</label> <input v-model="filename" type="text" placeholder="請輸入文件名"> </div> <div class="form-group"> <label>紙張大小</label> <select v-model="pageSize"> <option value="A4">A4</option> <option value="A3">A3</option> <option value="Letter">Letter</option> </select> </div> <div class="form-group"> <label>方向</label> <select v-model="orientation"> <option value="portrait">縱向</option> <option value="landscape">橫向</option> </select> </div> <div class="form-group"> <label> <input v-model="includeHeaderFooter" type="checkbox"> 包含頁眉頁腳 </label> </div> <div class="form-group"> <label> <input v-model="onlySelected" type="checkbox"> 僅導出選中項 </label> </div> <div class="button-group"> <button @click="showModal = false">取消</button> <button @click="exportPDF" :disabled="exporting"> {{ exporting ? '導出中...' : '確認導出' }} </button> </div> <div v-if="progress > 0" class="progress-bar"> <div class="progress" :style="{ width: progress + '%' }"></div> </div> </div> </div> </div> </template> <script> import { jsPDF } from 'jspdf' import html2canvas from 'html2canvas' export default { name: 'PdfExport', props: { tableData: { type: Array, required: true }, selectedRows: { type: Array, default: () => [] }, tableColumns: { type: Array, default: () => [] }, title: { type: String, default: '導出數(shù)據(jù)' } }, data() { return { showModal: false, filename: 'export', pageSize: 'A4', orientation: 'portrait', includeHeaderFooter: true, onlySelected: false, exporting: false, progress: 0 } }, methods: { async exportPDF() { this.exporting = true this.progress = 0 try { // 確定導出數(shù)據(jù) const exportData = this.onlySelected && this.selectedRows.length > 0 ? this.selectedRows : this.tableData // 創(chuàng)建PDF const doc = new jsPDF({ orientation: this.orientation, unit: 'mm', format: this.pageSize }) // 添加標題 doc.setFont('helvetica', 'bold') doc.setFontSize(16) doc.text(this.title, 105, 20, { align: 'center' }) // 添加生成時間 doc.setFont('helvetica', 'normal') doc.setFontSize(10) doc.text(`生成時間: ${new Date().toLocaleString()}`, 105, 30, { align: 'center' }) // 添加表格 await this.addTableToPDF(doc, exportData) // 添加頁腳 if (this.includeHeaderFooter) { const pageCount = doc.internal.getNumberOfPages() for (let i = 1; i <= pageCount; i++) { doc.setPage(i) doc.setFontSize(8) doc.text( `第 ${i} 頁 / 共 ${pageCount} 頁`, doc.internal.pageSize.getWidth() - 20, doc.internal.pageSize.getHeight() - 10 ) } } // 保存文件 doc.save(`${this.filename}.pdf`) this.$emit('export-success') this.showModal = false } catch (error) { console.error('導出失敗:', error) this.$emit('export-error', error) } finally { this.exporting = false this.progress = 0 } }, async addTableToPDF(doc, data) { const columns = this.tableColumns.filter(col => !col.hidden) const headers = columns.map(col => col.label) // 準備表格數(shù)據(jù) const tableData = data.map(row => { return columns.map(col => { let value = row[col.prop] if (col.formatter) { value = col.formatter(row, col) } return value !== undefined ? String(value) : '' }) }) // 自動調(diào)整列寬 const pageWidth = doc.internal.pageSize.getWidth() - 20 // 左右邊距 const colWidth = pageWidth / columns.length const colWidths = columns.map(() => colWidth) // 分頁添加表格 let startY = 40 let remainingData = tableData while (remainingData.length > 0) { // 計算當前頁能容納的行數(shù) const pageHeight = doc.internal.pageSize.getHeight() const rowHeight = 7 const maxRows = Math.floor((pageHeight - startY - 20) / rowHeight) const chunk = remainingData.slice(0, maxRows) remainingData = remainingData.slice(maxRows) // 添加表格部分 doc.autoTable({ head: [headers], body: chunk, startY, margin: { left: 10, right: 10 }, styles: { fontSize: 8, cellPadding: 2, overflow: 'linebreak' }, columnStyles: columns.reduce((styles, col, index) => { styles[index] = { cellWidth: colWidths[index], halign: col.align || 'left' } return styles }, {}) }) // 更新進度 this.progress = ((tableData.length - remainingData.length) / tableData
到此這篇關(guān)于Vue環(huán)境下數(shù)據(jù)導出為PDF的最全指南的文章就介紹到這了,更多相關(guān)Vue數(shù)據(jù)導出為PDF內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
axios中post請求json和application/x-www-form-urlencoded詳解
Axios是專注于網(wǎng)絡(luò)數(shù)據(jù)請求的庫,相比于原生的XMLHttpRequest對象,axios簡單易用,下面這篇文章主要給大家介紹了關(guān)于axios中post請求json和application/x-www-form-urlencoded的相關(guān)資料,需要的朋友可以參考下2022-10-10vue用ant design中table表格,點擊某行時觸發(fā)的事件操作
這篇文章主要介紹了vue用ant design中table表格,點擊某行時觸發(fā)的事件操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10vue項目打包后,由于html被緩存導致出現(xiàn)白屏的處理方案
這篇文章主要介紹了vue項目打包后,由于html被緩存導致出現(xiàn)白屏的處理方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03