欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue環(huán)境下數(shù)據(jù)導出為PDF的最全指南

 更新時間:2025年05月19日 08:12:02   作者:百錦再@新空間  
在現(xiàn)代Web應用開發(fā)中,將數(shù)據(jù)導出為PDF是一項常見且重要的功能需求,本文將全面探討Vue環(huán)境下實現(xiàn)PDF導出的7種主要方法,大家可以根據(jù)自己的需求進行選擇

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)文章

  • vue如何限制只能輸入正負數(shù)及小數(shù)

    vue如何限制只能輸入正負數(shù)及小數(shù)

    這篇文章主要介紹了vue如何限制只能輸入正負數(shù)及小數(shù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-07-07
  • vuejs使用遞歸組件實現(xiàn)樹形目錄的方法

    vuejs使用遞歸組件實現(xiàn)樹形目錄的方法

    本篇文章主要介紹了vuejs使用遞歸組件實現(xiàn)樹形目錄的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • axios中post請求json和application/x-www-form-urlencoded詳解

    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-10
  • vue router路由嵌套不顯示問題的解決方法

    vue router路由嵌套不顯示問題的解決方法

    這篇文章主要為大家詳細介紹了vue router路由嵌套不顯示的問題,具有一定的參考價值,感興趣的小伙伴們可以參考一下vue-router 路由嵌套不顯示問題
    2017-06-06
  • nuxt3中server routes的使用詳解

    nuxt3中server routes的使用詳解

    本文主要介紹了nuxt3中server routes的使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-05-05
  • Vue.js組件tabs實現(xiàn)選項卡切換效果

    Vue.js組件tabs實現(xiàn)選項卡切換效果

    這篇文章主要為大家詳細介紹了Vue.js組件tabs實現(xiàn)選項卡切換效果的相關(guān)資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • vue升級之路之vue-router的使用教程

    vue升級之路之vue-router的使用教程

    自動安裝的vue-router,會在src 文件夾下有個一個 router -> index.js 文件 在 index.js 中創(chuàng)建 routers 對象,引入所需的組件并配置路徑。這篇文章主要介紹了vue-router的使用,需要的朋友可以參考下
    2018-08-08
  • vue用ant design中table表格,點擊某行時觸發(fā)的事件操作

    vue用ant design中table表格,點擊某行時觸發(fā)的事件操作

    這篇文章主要介紹了vue用ant design中table表格,點擊某行時觸發(fā)的事件操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • vue項目打包后,由于html被緩存導致出現(xiàn)白屏的處理方案

    vue項目打包后,由于html被緩存導致出現(xiàn)白屏的處理方案

    這篇文章主要介紹了vue項目打包后,由于html被緩存導致出現(xiàn)白屏的處理方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • vue中對時間戳的處理方式

    vue中對時間戳的處理方式

    這篇文章主要介紹了vue中對時間戳的處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06

最新評論