Vue环境下数据导出PDF的全面指南
文章目录
- 1. 前言
- 2. 原生浏览器打印方案
- 2.1 使用window.print()实现
- 2.2 使用CSS Paged Media模块
- 3. 常用第三方库方案
- 3.1 使用jsPDF
- 3.2 使用html2canvas + jsPDF
- 3.3 使用pdfmake
- 3.4 使用vue-pdf
- 4. 服务器端导出方案
- 4.1 前端请求服务器生成PDF
- 4.2 使用无头浏览器生成PDF
- 5. 方法对比与选择指南
- 5.1 功能对比表
- 5.2 选择建议
- 5.3 性能优化建议
- 6. 最佳实践示例
- 6.1 完整的Vue PDF导出组件
1. 前言
在现代Web应用开发中,将数据导出为PDF是一项常见且重要的功能需求。PDF作为一种通用的文档格式,具有跨平台、保持格式一致、易于打印等优势。Vue.js作为当前流行的前端框架,提供了多种实现PDF导出的方法。本文将全面探讨Vue环境下实现PDF导出的7种主要方法,包括原生浏览器API、常用第三方库方案以及服务器端导出方案,每种方法都将提供详细的代码示例和优劣分析。
2. 原生浏览器打印方案
2.1 使用window.print()实现
这种方法利用浏览器自带的打印功能,通过CSS媒体查询控制打印样式。
实现原理:
- 创建专门用于打印的组件或视图
- 使用@media print定义打印样式
- 调用window.print()触发浏览器打印对话框
代码示例:
<template><div><button @click="print">打印PDF</button><div ref="printContent" class="print-container"><h1>销售报表</h1><table><thead><tr><th>日期</th><th>产品</th><th>数量</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: '产品A', quantity: 10, amount: 1000 },{ id: 2, date: '2023-01-02', product: '产品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>
优点:
- 零依赖,不增加项目体积
- 实现简单,适合简单打印需求
- 用户可以选择"另存为PDF"(大多数现代浏览器支持)
缺点:
- 依赖用户操作,无法自动化
- 打印样式控制有限
- 无法生成复杂的PDF文档
- 不同浏览器表现可能不一致
2.2 使用CSS Paged Media模块
对于更专业的打印需求,可以使用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;}
}
优点:
- 更专业的打印控制
- 支持页码、页眉页脚等高级功能
- 仍然是纯CSS方案,无JavaScript依赖
缺点:
- 浏览器支持不一致(特别是复杂特性)
- 学习曲线较陡
- 仍然依赖浏览器打印功能
3. 常用第三方库方案
3.1 使用jsPDF
jsPDF是最流行的JavaScript PDF生成库之一,可以直接在浏览器中生成PDF。
安装:
npm install jspdf
基础实现:
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 = 30columns.forEach((col, index) => {doc.text(col.label, 14 + index * 40, y)})// 添加数据行doc.setFontSize(10)data.forEach((row, rowIndex) => {y = 40 + rowIndex * 10if (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: '产品A', price: 100, stock: 50 },{ id: 2, name: '产品B', price: 200, stock: 30 }]const columns = [{ label: 'ID', field: 'id' },{ label: '产品名称', field: 'name' },{ label: '价格', field: 'price' },{ label: '库存', field: 'stock' }]generatePDF('产品列表', data, columns)}
}
高级功能示例:
function generateAdvancedPDF() {const doc = new jsPDF({orientation: 'landscape',unit: 'mm',format: 'a4'})// 设置元数据doc.setProperties({title: '高级报表',subject: '2023年度销售数据',author: '销售系统',keywords: '销售, 报表, 2023',creator: '公司销售系统'})// 添加公司logoconst 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 = [['产品', 'Q1', 'Q2', 'Q3', 'Q4', '总计']]const salesData = [['产品A', 1000, 1500, 1200, 1800, 5500],['产品B', 800, 900, 1000, 1200, 3900],['产品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')
}
优点:
- 纯前端实现,不依赖服务器
- 功能丰富,支持文本、表格、简单图形等
- 支持自定义字体
- 活跃的社区和良好的文档
缺点:
- 复杂布局实现较困难
- 原生不支持复杂表格样式(需要插件)
- 中文支持需要额外配置字体
3.2 使用html2canvas + jsPDF
这种方案先将HTML转换为canvas,再将canvas转为PDF,适合需要精确复制页面样式的场景。
安装:
npm install html2canvas jspdf
基础实现:
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 = pageWidthconst imgHeight = (canvas.height * imgWidth) / canvas.widthpdf.addImage(imgData, 'PNG', 0, 0, imgWidth, imgHeight)pdf.save(filename)
}// Vue组件中使用
methods: {async exportPage() {const element = this.$refs.pdfContentawait 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.widthif (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')
}
优点:
- 精确复制页面样式和布局
- 支持复杂HTML结构和CSS样式
- 可以处理动态生成的内容
- 支持多页导出
缺点:
- 生成的PDF本质是图片,文字不可选择
- 大页面可能导致内存问题
- 性能较差,特别是复杂页面
- 分页控制较困难
3.3 使用pdfmake
pdfmake是一个声明式的PDF生成库,使用JSON格式定义文档结构。
安装:
npm install pdfmake
基础实现:
import pdfMake from 'pdfmake/build/pdfmake'
import pdfFonts from 'pdfmake/build/vfs_fonts'pdfMake.vfs = pdfFonts.pdfMake.vfsexport function generatePDFWithPdfMake(data, filename = 'document.pdf') {const docDefinition = {content: [{ text: '销售报表', style: 'header' },'\n\n',{table: {headerRows: 1,widths: ['*', 'auto', 'auto', 'auto'],body: [['产品', '季度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: '产品A', q1: 1000, q2: 1500, q3: 1200 },{ product: '产品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: '公司机密 - 未经授权禁止复制',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%。主要增长来自产品线A和B。',style: 'paragraph'},'\n',{style: 'tableExample',table: {widths: ['*', '*', '*', '*'],body: [[{ text: '产品线', style: 'tableHeader' },{ text: 'Q1', style: 'tableHeader' },{ text: 'Q2', style: 'tableHeader' },{ text: 'Q3', style: 'tableHeader' }],['产品A', '320万', '350万', '380万'],['产品B', '280万', '300万', '320万'],['产品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: '成本构成示意图',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()
}
优点:
- 声明式API,易于理解和使用
- 强大的布局和样式控制能力
- 内置分页、页眉页脚支持
- 支持表格、列表、图片等多种元素
缺点:
- 中文支持需要额外配置字体
- 学习曲线较陡(特别是复杂布局)
- 文档结构可能变得复杂
- 性能不如原生jsPDF
3.4 使用vue-pdf
vue-pdf是一个Vue专用的PDF生成和显示组件库。
安装:
npm install @tato30/vue-pdf
基础实现:
<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: '研发' },{ id: 2, name: '李四', department: '市场' }]}},methods: {async generatePDF() {const pdf = new VuePdf()// 添加内容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>
优点:
- 专为Vue设计,API更符合Vue习惯
- 轻量级,易于集成
- 支持基本的PDF生成功能
缺点:
- 功能相对有限
- 社区支持不如jsPDF或pdfmake
- 文档较少
4. 服务器端导出方案
4.1 前端请求服务器生成PDF
这种方案将PDF生成逻辑放在服务器端,前端只负责触发和下载。
前端代码:
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 = urllink.setAttribute('download', 'server_export.pdf')document.body.appendChild(link)link.click()document.body.removeChild(link)})
}// Vue组件中使用
methods: {async exportFromServer() {try {this.loading = trueawait 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// 创建PDF文档const doc = new PDFDocument({ size: format || 'A4' })// 设置响应头res.setHeader('Content-Type', 'application/pdf')res.setHeader('Content-Disposition', 'attachment; filename="report.pdf"')// 管道传输到响应doc.pipe(res)// 添加内容doc.fontSize(25).text(`${year}年度${getReportTitle(reportType)}`, {align: 'center'})doc.moveDown()doc.fontSize(12).text('生成日期: ' + new Date().toLocaleDateString())// 添加表格数据const data = await getReportData(reportType, year)drawTable(doc, data)// 结束并发送doc.end()} catch (error) {console.error('PDF生成错误:', error)res.status(500).send('PDF生成失败')}
})function drawTable(doc, data) {const startY = 150const rowHeight = 30const colWidth = 150const headers = ['产品', 'Q1', 'Q2', 'Q3', 'Q4', '总计']// 表头doc.font('Helvetica-Bold')headers.forEach((header, i) => {doc.text(header, 50 + i * colWidth, startY, {width: colWidth,align: 'center'})})// 表格内容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'))
优点:
- 处理大数据量更高效
- 减轻前端压力
- 可以复用服务器端数据处理逻辑
- 更安全,业务逻辑不暴露在客户端
- 支持更复杂的PDF生成(如使用专业PDF库)
缺点:
- 增加服务器负载
- 需要网络请求,可能有延迟
- 实现复杂度更高
4.2 使用无头浏览器生成PDF
对于需要精确复制网页样式的场景,可以在服务器端使用无头浏览器(如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 = nulltry {const { url, options } = req.bodybrowser = await puppeteer.launch({headless: true,args: ['--no-sandbox', '--disable-setuid-sandbox']})const page = await browser.newPage()// 如果是URL,直接导航;如果是HTML内容,设置内容if (url.startsWith('http')) {await page.goto(url, { waitUntil: 'networkidle2' })} else {await page.setContent(url)}// 生成PDFconst pdf = await page.pdf({format: 'A4',printBackground: true,margin: {top: '20mm',right: '20mm',bottom: '20mm',left: '20mm'},...options})// 发送PDFres.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'))
优点:
- 精确复制网页样式和布局
- 支持所有现代CSS特性
- 可以处理动态内容
- 支持截图、PDF等多种输出
缺点:
- 资源消耗大(需要运行浏览器实例)
- 性能较差
- 部署复杂度高
5. 方法对比与选择指南
5.1 功能对比表
方法 | 生成方式 | 样式保真度 | 复杂度 | 性能 | 适用场景 |
---|---|---|---|---|---|
浏览器打印 | 客户端 | 中等 | 低 | 高 | 简单打印需求 |
jsPDF | 客户端 | 低 | 中 | 高 | 编程式生成简单PDF |
html2canvas+jsPDF | 客户端 | 高 | 中 | 中 | 精确复制页面样式 |
pdfmake | 客户端 | 中 | 中 | 中 | 结构化文档生成 |
vue-pdf | 客户端 | 低 | 低 | 高 | 简单Vue集成 |
服务器生成 | 服务端 | 可高可低 | 高 | 依赖实现 | 复杂/大数据量PDF |
无头浏览器 | 服务端 | 极高 | 很高 | 低 | 精确复制复杂页面 |
5.2 选择建议
- 简单打印需求:使用浏览器打印方案,成本最低
- 编程式生成简单PDF:选择jsPDF,纯前端实现
- 精确复制页面样式:html2canvas+jsPDF组合或服务器端无头浏览器方案
- 结构化文档生成:pdfmake提供更直观的声明式API
- Vue项目快速集成:考虑vue-pdf组件
- 大数据量或复杂处理:优先服务器端方案
- 最高样式保真度:无头浏览器方案(如Puppeteer)
5.3 性能优化建议
- 分页处理:对于大数据集,实现分页或分块生成
- 懒加载资源:只在需要时加载PDF生成库
- Web Worker:将耗时的生成过程放在Worker线程
- 服务器缓存:对频繁请求的相同内容缓存生成的PDF
- 按需生成:提供预览功能,只在用户确认后生成完整PDF
- 资源优化:压缩图片等资源减少PDF体积
6. 最佳实践示例
6.1 完整的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: '导出数据'}},data() {return {showModal: false,filename: 'export',pageSize: 'A4',orientation: 'portrait',includeHeaderFooter: true,onlySelected: false,exporting: false,progress: 0}},methods: {async exportPDF() {this.exporting = truethis.progress = 0try {// 确定导出数据const exportData = this.onlySelected && this.selectedRows.length > 0? this.selectedRows: this.tableData// 创建PDFconst 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 = falsethis.progress = 0}},async addTableToPDF(doc, data) {const columns = this.tableColumns.filter(col => !col.hidden)const headers = columns.map(col => col.label)// 准备表格数据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) : ''})})// 自动调整列宽const pageWidth = doc.internal.pageSize.getWidth() - 20 // 左右边距const colWidth = pageWidth / columns.lengthconst colWidths = columns.map(() => colWidth)// 分页添加表格let startY = 40let remainingData = tableDatawhile (remainingData.length > 0) {// 计算当前页能容纳的行数const pageHeight = doc.internal.pageSize.getHeight()const rowHeight = 7const 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
相关文章:

Vue环境下数据导出PDF的全面指南
文章目录 1. 前言2. 原生浏览器打印方案2.1 使用window.print()实现2.2 使用CSS Paged Media模块 3. 常用第三方库方案3.1 使用jsPDF3.2 使用html2canvas jsPDF3.3 使用pdfmake3.4 使用vue-pdf 4. 服务器端导出方案4.1 前端请求服务器生成PDF4.2 使用无头浏览器生成PDF 5. 方法…...

Linux中的DNS的安装与配置
DNS简介 DNS(DomainNameSystem)是互联网上的一项服务,它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网。 DNS使用的是53端口 通常DNS是以UDP这个较快速的数据传输协议来查询的,但是没有查…...

linux服务器与时间服务器同步时间
内网部署服务器,需要同步时间 使用系统内置的systemctl-timesyncd进行时间同步 1.编辑配置文件 sudo nano /etc/systemd/timesyncd.conf修改添加内容入下 [Time] NTP10.100.13.198 FallbackNTP#说明 #NTP10.100.13.198:你的主 NTP 时间服务器 IP #Fall…...

【数据结构篇】排序1(插入排序与选择排序)
注:本文以排升序为例 常见的排序算法: 目录: 一 直接插入排序: 1.1 基本思想: 1.2 代码: 1.3 复杂度: 二 希尔排序(直接插入排序的优化): 2.1 基本思想…...

《Linux服务与安全管理》| DNS服务器安装和配置
《Linux服务与安全管理》| DNS服务器安装和配置 目录 《Linux服务与安全管理》| DNS服务器安装和配置 第一步:使用dnf命令安装BIND服务 第二步:查看服务器server01的网络配置 第三步:配置全局配置文件 第四步:修改bind的区域…...
【NLP】34. 数据专题:如何打造高质量训练数据集
构建大语言模型的秘密武器:如何打造高质量训练数据集? 在大语言模型(LLM)如 GPT、BERT、T5 爆发式发展的背后,我们常常关注模型架构的演化,却忽视了一个更基础也更关键的问题:训练数据从哪里来…...

Notepad++ 学习(三)使用python插件编写脚本:实现跳转指定标签页(自主研发)
目录 一、先看成果二、安装Python Script插件三、配置Python脚本四、使用脚本跳转标签页方法一:通过菜单运行方法二:设置快捷键(推荐) 五、注意事项六、进阶使用 官网地址: https://notepad-plus-plus.org/Python Scri…...

Stable Diffusion 学习笔记02
模型下载网站: 1,LiblibAI-哩布哩布AI - 中国领先的AI创作平台 2,Civitai: The Home of Open-Source Generative AI 模型的安装: 将下载的sd模型放置在sd1.5的文件内即可,重启客户端可用。 外挂VAE模型:…...

python:pymysql概念、基本操作和注入问题讲解
python:pymysql分享目录 一、概念二、数据准备三、安装pymysql四、pymysql使用(一)使用步骤(二)查询操作(三)增(四)改(五)删 五、关于pymysql注入…...
Scala语言基础与函数式编程详解
Scala语言基础与函数式编程详解 本文系统梳理Scala语言基础、函数式编程核心、集合与迭代器、模式匹配、隐式机制、泛型与Spark实战,并对每个重要专业术语进行简明解释,配合实用记忆口诀与典型代码片段,助你高效学习和应用Scala。 目录 Scal…...
类的加载过程详解
类的加载过程详解 Java类的加载过程分为加载(Loading)、链接(Linking) 和 初始化(Initialization) 三个阶段。其中链接又分为验证(Verification)、准备(Preparation&…...

机器学习-人与机器生数据的区分模型测试 - 模型融合与检验
模型融合 # 先用普通Pipeline训练 from sklearn.pipeline import Pipeline#from sklearn2pmml.pipeline import PMMLPipeline train_pipe Pipeline([(scaler, StandardScaler()),(ensemble, VotingClassifier(estimators[(rf, RandomForestClassifier(n_estimators200, max_de…...

机器学习 day03
文章目录 前言一、特征降维1.特征选择2.主成分分析(PCA) 二、KNN算法三、模型的保存与加载 前言 通过今天的学习,我掌握了机器学习中的特征降维的概念以及用法,KNN算法的基本原理及用法,模型的保存和加载 一、特征降维…...
《社交应用动态表情:RN与Flutter实战解码》
React Native依托于JavaScript和React,为动态表情的实现开辟了一条独特的道路。其核心优势在于对原生模块的便捷调用,这为动态表情的展示和交互提供了强大支持。在社交应用中,当用户点击发送动态表情时,React Native能够迅速调用相…...

嵌入式软件--stm32 DAY 6 USART串口通讯(下)
1.寄存器轮询_收发字符串 通过寄存器轮询方式实现了收发单个字节之后,我们趁热打铁,争上游,进阶到字符串。字符串就是多个字符。很明显可以循环收发单个字节实现。 然后就是接收字符串。如果接受单个字符的函数放在while里,它也可…...

问题处理——在ROS2(humble)+Gazebo+rqt下,无法显示仿真无人机的相机图像
文章目录 前言一、问题展示二、解决方法:1.下载对应版本的PX42.下载对应版本的Gazebo3.启动 总结 前言 在ROS2的环境下,进行无人机仿真的过程中,有时需要调取无人机的相机图像信息,但是使用rqt,却发现相机图像无法显示…...
69、微服务保姆教程(十二)容器化与云原生
容器化与云原生 在微服务架构中,容器化和云原生技术是将应用程序部署到生产环境的核心技术。通过容器化技术,可以将应用程序及其依赖项打包成一个容器镜像,确保在任何环境中都能一致运行。而云原生技术则通过自动化的容器编排系统(如 Kubernetes),实现应用的动态扩展、自…...

朱老师,3518e系列,第六季
第一节:概述。 首先是 将 他写好的 rtsp 源码上传,用于分析。 已经拷贝完。 第二节: h264 编码概念。 编解码 可以用cpu, 也可以用 bsp cpu 编解码的效果不好。做控制比较好。 h264 由 VCL, NAL 组成。 NAL 关心的是 压缩…...

ElasticSearch-集群
本篇文章依据ElasticSearch权威指南进行实操和记录 1,空集群 即不包含任何节点的集群 集群大多数分为两类,主节点和数据节点 主节点 职责:主节点负责管理集群的状态,例如分配分片、添加和删除节点、监控节点故障等。它们不直接…...

一文掌握工业相机选型计算
目录 一、基本概念 1.1 物方和像方 1.2 工作距离和视场 1.3 放大倍率 1.4 相机芯片尺寸 二、公式计算 三、实例应用 一、基本概念 1.1 物方和像方 在光学领域,物方(Object Space)是与像方(Image Space)相对的…...
记录心态和工作变化
忙中带闲的工作 其实工作挺忙的, 总是在赶各种功能点. 好巧的是iOS那边因为上架的问题耽搁了一些时间, 从而让Android的进度有了很大的调整空间. 更巧的是后端那边因为对客户端的需求不是很熟悉, 加上Android海外这块的业务他也是第一次接触. 所以需要给他留一些时间把各个环节…...
深入理解 TypeScript 中的 unknown 类型:安全处理未知数据的最佳实践
在 TypeScript 的类型体系中,unknown 是一个极具特色的类型。它与 any 看似相似,却在安全性上有着本质差异。本文将从设计理念、核心特性、使用场景及最佳实践等方面深入剖析 unknown,帮助开发者在处理动态数据时既能保持灵活性,又…...

LabVIEW机械振动信号分析与故障诊断
利用 LabVIEW 开发机械振动信号分析与故障诊断系统,融合小波变换、时频分布、高阶统计量(双谱)等先进信号处理技术,实现对齿轮、发动机等机械部件的非平稳非高斯振动信号的特征提取与故障诊断。系统通过虚拟仪器技术将理论算法转化…...
Helm配置之为特定Deployment配置特定Docker仓库(覆盖全局配置)
文章目录 Helm配置之为特定Deployment配置特定Docker仓库(覆盖全局配置)需求方法1:使用Helm覆盖值方法2: 在Lens中临时修改Deployment配置步骤 1: 创建 Docker Registry Secret步骤 2: 在 Deployment 中引用 Secret参考资料Helm配置之为特定Deployment配置特定Docker仓库(覆…...

【Spring】Spring中的适配器模式
欢迎来到啾啾的博客🐱。 记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。 欢迎评论交流,感谢您的阅读😄。 目录 适配器模式Spring MVC的适配器模式 适配器模式 适配器模式(Adapter Pattern&a…...
GO学习指南
GO学习指南 主题一 go语言基础知识讲解 go语言面向对象编程 go语言接口详解 go语言协程 主题二 web基础知识 后续内容请大家持续关注,每月一主题,让各位读者能零基础、零成本学习go语言...

2、ubuntu系统配置OpenSSH | 使用vscode或pycharm远程连接
1、OpenSSH介绍 OpenSSH(Open Secure Shell)是一套基于SSH协议的开源工具,用于在计算机网络中提供安全的加密通信。它被广泛用于远程系统管理、文件传输和网络服务的安全隧道搭建,是保护网络通信免受窃听和攻击的重要工具。 1.1…...
MySQL面试知识点详解
一、MySQL基础架构 1. MySQL逻辑架构 MySQL采用分层架构设计,主要分为: 连接层:处理客户端连接、授权认证等 服务层:包含查询解析、分析、优化、缓存等 引擎层:负责数据存储和提取(InnoDB、MyISAM等&am…...
小白入门:GitHub 远程仓库使用全攻略
一、Git 核心概念 1. 三个工作区域 工作区(Working Directory):实际编辑文件的地方。 暂存区(Staging Area):准备提交的文件集合(使用git add操作)。 本地仓库(Local…...

RPC与SOAP的区别
一.RPC(远程过程调用)和SOAP(简单对象访问协议)均用于实现分布式系统中的远程通信,但两者在设计理念、协议实现及应用场景上存在显著差异。 二.对比 1.设计理念 2.协议规范 3.技术特性 4.典型应用场景 5.总结 三.总结…...