1. 项目背景与需求解析
在电商系统开发中,打印功能一直是刚需但实现起来又颇为棘手的环节。传统方案要么依赖浏览器原生打印(样式难控制),要么采用重量级报表工具(学习成本高)。最近在FastAdmin项目中集成vue-print-designer插件的实践,完美解决了这个痛点。
这个方案特别适合需要处理以下场景的开发者:
- 订单小票、物流面单等固定格式打印
- 多联单据的套打定位需求
- 需要前端灵活调整打印模板的运营场景
- 对浏览器兼容性要求较高的传统企业环境
2. 技术选型对比分析
2.1 常见打印方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 浏览器默认打印 | 零成本 | 样式不可控,分页问题严重 | 简单内容打印 |
| PDF导出打印 | 格式稳定 | 需要后端配合,动态内容处理复杂 | 正式文档输出 |
| Lodop等ActiveX控件 | 功能强大 | 依赖Windows环境,有安全风险 | 企业内部系统 |
| vue-print-designer | 纯前端实现,可视化设计 | 复杂报表支持有限 | Web端常规打印需求 |
2.2 为什么选择vue-print-designer
选择这个插件主要基于三点考量:
- 轻量集成:纯Vue组件,不依赖后端服务,打包后仅增加约200KB体积
- 所见即所得:内置的可视化设计器让非技术人员也能调整模板
- 精准套打:通过毫米级定位支持多联单据打印,实测误差<0.5mm
提示:对于需要条形码/二维码的场景,建议配合vue-barcode组件使用
3. FastAdmin集成实战
3.1 环境准备
首先确保FastAdmin已配置好Vue环境:
# 安装依赖 npm install vue-print-designer print-js --save3.2 核心配置步骤
- 在
src/assets/js/backend.js中添加全局引用:
import PrintDesigner from 'vue-print-designer' Vue.use(PrintDesigner)- 创建打印模板配置文件
print-templates.js:
export default { order: { pageSize: 'A5', margins: { top: 10, left: 10 }, elements: [ { type: 'text', content: '订单号:{{order_no}}', style: { left: '20mm', top: '15mm', fontSize: '14pt' } }, { type: 'table', data: '{{products}}', columns: [ { field: 'name', title: '商品名称', width: '60mm' }, { field: 'price', title: '单价', width: '20mm' } ], style: { left: '15mm', top: '30mm' } } ] } }- 在控制器中注入模板数据:
public function printOrder() { $order = $this->model->find(input('id')); $this->success('', [ 'template' => 'order', 'data' => [ 'order_no' => $order['order_no'], 'products' => $order['products'] ] ]); }3.3 前端调用示例
<template> <print-designer :template="template" :data="printData" @before-print="handleBeforePrint" /> </template> <script> export default { methods: { handleBeforePrint() { // 打印前回调 this.$modal.msgSuccess('正在生成打印数据...') } } } </script>4. 高级功能实现
4.1 套打精准定位技巧
实现三联单打印的关键点:
- 使用
pageSize: 'custom'设置自定义纸张尺寸 - 通过
margins调整整体偏移量 - 元素定位建议采用mm单位,示例:
{ type: 'text', content: '客户联', style: { left: '5mm', top: '5mm', color: '#FF0000' } }4.2 动态内容处理
对于需要计算的内容,可以使用模板函数:
elements: [ { type: 'text', content(data) { return `合计:${data.products.reduce((sum,item) => sum + item.price, 0)}元` }, style: { left: '80mm', top: '100mm' } } ]4.3 打印样式优化
通过CSS注入解决常见问题:
/* 强制分页 */ .page-break { page-break-after: always; } /* 隐藏打印按钮 */ @media print { .no-print { display: none; } }5. 踩坑经验与解决方案
5.1 常见问题排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 打印内容偏移 | 边距设置不当 | 调整margins参数 |
| 动态数据不显示 | 数据未预加载 | 使用v-if控制渲染时机 |
| 分页异常 | 元素高度超出页面 | 添加page-break样式 |
| 部分浏览器样式失效 | 私有前缀缺失 | 增加-webkit-print-color-adjust |
5.2 性能优化建议
- 大数据量分页:当打印数据超过100条时,建议后端分页处理
- 图片懒加载:先显示占位符,打印前再加载实际图片
- 模板缓存:将设计好的模板保存到localStorage
// 保存模板 localStorage.setItem('printTemplate', JSON.stringify(template)) // 读取模板 const template = JSON.parse(localStorage.getItem('printTemplate'))6. 扩展应用场景
6.1 与FastAdmin表格联动
在列表操作栏添加打印按钮:
{table: { operate: [ { name: 'print', title: '打印', class: 'btn btn-xs btn-print', icon: 'fa fa-print', click: function(data) { // 调用打印方法 } } ] }}6.2 批量打印实现
通过Promise.all处理多订单打印:
async function batchPrint(ids) { const requests = ids.map(id => axios.get(`/order/print?id=${id}`) ) const results = await Promise.all(requests) results.forEach(res => { this.$refs.printDesigner.print(res.data) }) }6.3 服务端渲染方案
对于需要精确控制的场景,可以结合PDF.js:
// 服务端生成PDF use TCPDF; $pdf = new TCPDF(); $pdf->AddPage(); $pdf->writeHTML($html); $pdf->Output('order.pdf', 'I');在实际项目中,这套方案已经稳定支持日均5000+订单的打印需求。特别要注意的是,当遇到特殊纸张规格时,建议先用空白纸测试定位,避免浪费正式单据。有个小技巧是在设计器背景放置实际扫描的票据图片作为参照,可以快速校准元素位置。