Vue 类似合同套打展示的PDF并下载
<div> <div style="width: 95%;margin:15px auto 0 auto" class="doc-container" id="doc-container" ></div> <button slot="right" effect="text" type="primary" @click="aDownload">下载</button> </div>
需要安装 pizzip,html2pdf.js 的依赖;
npm install pizzip html2pdf.js
import PizZip from 'pizzip'
import html2pdf from 'html2pdf.js'
//base64Content是base64,replaceData是套打的数据流;通过接口后端返回的 async handleDoc(base64Content, replaceData) { try { const imageOpts = { getImage(tag) { const imageBinaryContent = atob( tag.replace(/^data:image\/(png|jpg|gif|bmp|svg|svg\+xml);base64,/, '') ) const imageByteArray = new Uint8Array(imageBinaryContent.length) for (let i = 0; i < imageBinaryContent.length; i++) { imageByteArray[i] = imageBinaryContent.charCodeAt(i) } return imageByteArray }, getSize() { return [80, 80] } } const binaryContent = atob(base64Content) const byteArray = new Uint8Array(binaryContent.length) for (let i = 0; i < binaryContent.length; i++) { byteArray[i] = binaryContent.charCodeAt(i) } // 使用 PizZip 加载字节数组 const zip = new PizZip(byteArray) const doc = new window.docxtemplater() doc.loadZip(zip) // Attach ImageModule with options doc.attachModule(new ImageModule(imageOpts)) // Render the document (Replace {first_name} by John, {last_name} by Doe, ...) doc.render(replaceData) this.outBlob = doc.getZip().generate({ type: 'blob', mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }) this.docx = URL.createObjectURL(this.outBlob) setTimeout(() => { docx.renderAsync(this.outBlob, document.getElementById('doc-container')).then(x => { console.log('docx: finished') this.addWatermarksSystematically(); }) }, 100) } catch (err) { rt.showErrorToast(err.message || this.$_t('service.Error', 'Error')) rt.hideLoadingToast() } }, //添加水印 addWatermarksSystematically() { const container = document.getElementById('doc-container') if (!container) return // 清除旧水印 const oldWatermarks = container.querySelectorAll('.watermark-element') oldWatermarks.forEach(wm => wm.remove()) // 计算页面数量(更精确的方法) const pageHeight = 1122 // A4纸像素高度近似值(根据您的scale调整) const contentHeight = container.scrollHeight let pageCount = Math.ceil(contentHeight / pageHeight) pageCount = Math.floor(pageCount / 2) // 均匀分布水印 for (let i = 0; i < pageCount; i++) { const top = pageHeight * (i + 0.5) // 每页中间位置 this.addWatermark('doc-container', this.languageMap[this.language].title, `${top}px`) } }, addWatermark(containerId, watermarkText, top) { const container = document.getElementById(containerId) if (!container) return const watermark = document.createElement('div') watermark.className = 'watermark-element' // 添加类名便于管理 // 增强样式 Object.assign(watermark.style, { position: 'absolute', top: top, left: '0', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none', zIndex: '2147483647', // 最大z-index color: 'rgba(200, 200, 200, 0.7)', fontSize: '80px', // 增大字号 transform: 'rotate(-45deg)', fontFamily: 'Arial,', overflow: 'hidden' }) watermark.textContent = watermarkText container.style.position = 'relative' container.appendChild(watermark) // 强制重绘 void watermark.offsetHeight // 显示水印 watermark.style.opacity = '1' }, aDownload() { try { // 显示加载中状态 console.log('正在生成PDF,请稍候...') // 配置选项 const opt = { margin: 10, filename: `contract_${new Date().getTime()}.pdf`, image: { type: 'jpeg', quality: 0.98 }, html2canvas: { scale: 2, // 提高分辨率 logging: false, useCORS: true, // 解决图片跨域问题 allowTaint: true }, jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait', // 确保中文支持 hotfixes: ['px_scaling'] } } // 获取要导出的元素 const element = document.getElementById('doc-container') // 生成PDF await html2pdf() .set(opt) .from(element) .save() console.log('PDF导出成功!') } catch (error) { console.error('导出PDF失败:', error) } },