)
企业级合同导出方案uniapphtml2canvas全平台PDF生成实战在电子签约和在线合同签署场景中将合同页面高质量导出为PDF是企业数字化流程中的刚需功能。传统方案往往面临跨平台兼容性差、多页拼接困难、字体渲染不一致等痛点。本文将深入解析基于uniapp框架配合html2canvas库的完整解决方案覆盖从基础实现到企业级优化的全链路实践。1. 技术选型与基础环境搭建uniapp作为跨端开发框架配合html2canvas实现页面截图是当前最成熟的方案组合。相比原生截屏API这种方案具有三大核心优势样式保留完整精准还原CSS3样式和复杂布局跨平台一致性统一处理Android/iOS的渲染差异扩展性强支持添加水印、签名等二次处理基础环境配置步骤如下# 创建uniapp项目如已存在可跳过 vue create -p dcloudio/uni-preset-vue contract-export # 安装html2canvas1.4.1版本推荐 npm install html2canvas --save关键依赖版本建议依赖项推荐版本必要性说明uniapp3.6.18确保renderjs稳定性html2canvas1.4.1修复iOS14兼容问题pdf-lib1.17.1多页PDF生成需要2. 核心实现从页面截图到PDF生成2.1 页面结构设计与截图准备合同页面的HTML结构需要特别注意可截图性设计template !-- 必须设置具体宽高且不能使用百分比 -- view idcontract-container stylewidth: 794px; height: 1123px; background: white; !-- 合同内容区域 -- view classclause v-foritem in clauses :keyitem.id text classclause-title{{ item.title }}/text text classclause-content{{ item.content }}/text /view !-- 分页标识关键 -- view classpage-break stylepage-break-after: always;/view /view /template2.2 renderjs截图核心代码在renderjs模块中实现高质量截图script modulecanvasExporter langrenderjs import html2canvas from html2canvas; import { PDFDocument, rgb } from pdf-lib; export default { methods: { async exportToPDF(id, fileName contract.pdf) { const container document.getElementById(id); const pdfDoc await PDFDocument.create(); // 多页处理逻辑 const pages container.querySelectorAll(.page-break); for(let i 0; i pages.length; i) { const canvas await html2canvas(container, { scrollY: -window.scrollY, windowWidth: 794, width: 794, height: 1123, scale: 2, // 视网膜屏高清处理 logging: false, useCORS: true, allowTaint: true }); const pngImage await pdfDoc.embedPng(canvas.toDataURL()); const page pdfDoc.addPage([794, 1123]); page.drawImage(pngImage, { x: 0, y: 0, width: 794, height: 1123, }); } const pdfBytes await pdfDoc.save(); this.saveAsFile(pdfBytes, fileName); }, saveAsFile(data, fileName) { // 实现文件保存逻辑 } } } /script3. 企业级优化方案3.1 跨平台字体渲染一致性方案不同平台字体渲染差异会导致合同内容偏移解决方案字体预加载策略/* 全局CSS */ font-face { font-family: ContractFont; src: url(/static/fonts/SourceHanSansCN-Regular.woff2) format(woff2); } .contract-text { font-family: ContractFont, -apple-system, sans-serif; }动态字体检测机制// 在页面mounted时检测字体是否加载成功 mounted() { document.fonts.load(16px ContractFont).then(() { this.fontLoaded true; }).catch(() { this.loadFallbackFont(); }); }3.2 高清输出解决方案针对不同DPI设备的高清输出方案设备类型scale参数输出分辨率适用场景普通屏幕1794×1123基础合同Retina屏21588×2246高精度签署印刷需求32382×3369纸质存档const getOptimalScale () { const dpi window.devicePixelRatio || 1; return dpi 3 ? 3 : dpi 2 ? 2 : 1; }4. 性能优化与异常处理4.1 大合同内存优化处理超长合同时的内存管理策略分块渲染技术async function renderByChunks(container, chunkSize 3) { const sections [...container.children]; const chunks []; while(sections.length) { chunks.push(sections.splice(0, chunkSize)); } for(const chunk of chunks) { const tempDiv document.createElement(div); chunk.forEach(el tempDiv.appendChild(el.cloneNode(true))); const canvas await html2canvas(tempDiv); // 处理canvas... tempDiv.remove(); // 及时释放内存 } }Web Worker支持// worker.js self.addEventListener(message, async (e) { const { html, options } e.data; const canvas await html2canvas(html, options); self.postMessage(canvas.toDataURL()); }); // 主线程调用 const worker new Worker(/workers/render.worker.js); worker.postMessage({ html: document.getElementById(content), options });4.2 常见异常处理方案建立健壮的错误处理机制const errorCodes { 1001: DOM元素未找到, 1002: 字体加载超时, 1003: 内存不足, 1004: 跨域资源阻止 }; async function safeExport() { try { if(!this.$refs.container) { throw { code: 1001 }; } const result await exportToPDF(container); return { success: true, data: result }; } catch(err) { console.error([PDF导出失败] ${errorCodes[err.code] || err.message}); return { success: false, error: { code: err.code || -1, message: errorCodes[err.code] || 未知错误 } }; } }5. 扩展功能实现5.1 电子签名集成方案在导出前添加签名层async function addSignatureToPDF(pdfBytes, signatureImage) { const pdfDoc await PDFDocument.load(pdfBytes); const pages pdfDoc.getPages(); const pngSignature await pdfDoc.embedPng(signatureImage); const lastPage pages[pages.length - 1]; lastPage.drawImage(pngSignature, { x: 500, y: 100, width: 200, height: 80, opacity: 0.9 }); return await pdfDoc.save(); }5.2 合同水印防伪系统动态生成防伪水印function generateWatermark(text) { const canvas document.createElement(canvas); canvas.width 300; canvas.height 200; const ctx canvas.getContext(2d); ctx.font 14px ContractFont; ctx.fillStyle rgba(200,200,200,0.3); ctx.rotate(-0.3); ctx.fillText(text, 10, 100); return canvas.toDataURL(); }实际项目中我们发现iOS 15系统对canvas的透明背景处理存在兼容性问题。解决方案是在html2canvas配置中显式声明背景html2canvas(element, { backgroundColor: #FFFFFF, // 其他配置... })