别再复制粘贴了!Vue3项目里封装一个通用的Lodop打印Hook(支持标签和A4纸)
·
Vue3工程化实践:打造高复用性的Lodop打印Hook解决方案
在电商后台、WMS仓库管理等系统中,打印功能往往是业务闭环的关键节点。面对频繁变化的打印需求和多样化的模板类型,如何避免在每个页面重复编写相似的Lodop调用代码?本文将带你从工程化角度,构建一个既保持业务灵活性又能最大化复用的打印Hook方案。
1. 设计哲学:从功能实现到架构抽象
优秀的Hook设计应该像乐高积木一样——标准化接口背后是高度灵活的组装能力。在规划useLodop时,我们需要考虑三个核心维度:
- 控制反转 :将插件初始化、打印机管理等基础能力封装在Hook内部,对外暴露简洁的打印API
- 模板解耦 :通过策略模式处理不同类型的打印需求(标签/A4),业务组件只需关注数据准备
- 错误边界 :建立统一的异常处理机制,避免每个调用处重复编写插件检测逻辑
// 典型调用示例 - 业务组件中
const { print } = useLodop()
const handlePrint = async () => {
await print({
type: 'label',
content: base64Data,
config: { size: '100x150mm' }
})
}
2. 核心实现:分层架构的Hook设计
2.1 基础服务层
这一层处理与Lodop插件的原始交互,相当于整个Hook的基础设施:
class LodopService {
private instance: any
constructor() {
this.initEngine()
}
private initEngine() {
this.instance = getLodop()
if (!this.instance) {
throw new Error('LODOP_NOT_INSTALLED')
}
}
getPrinters() {
return Array.from({ length: this.instance.GET_PRINTER_COUNT() }, (_, i) => ({
name: this.instance.GET_PRINTER_NAME(i),
index: i
}))
}
// 其他原生API包装...
}
2.2 能力抽象层
基于基础服务构建可复用的打印能力单元:
const usePrintCapability = () => {
const service = new LodopService()
const printImage = (options: ImagePrintOptions) => {
const { content, size, printer } = options
service.instance.ADD_PRINT_IMAGE(0, 0, '100%', '100%', content)
// 更多图像打印配置...
}
const printTable = (options: TablePrintOptions) => {
// 表格打印实现...
}
return { printImage, printTable }
}
2.3 业务聚合层
最终暴露给业务组件的API层,实现"开箱即用":
export const useLodop = () => {
const { printImage, printTable } = usePrintCapability()
const [currentPrinter, setPrinter] = useState<string>()
const print = async (options: PrintOptions) => {
try {
switch (options.type) {
case 'label':
return await printImage({ ...options, printer: currentPrinter })
case 'a4':
return await printTable(options)
}
} catch (error) {
// 统一错误处理
showErrorToast(error)
return false
}
}
return { print, selectPrinter: setPrinter }
}
3. 高级技巧:动态模板与性能优化
3.1 模板引擎集成
对于经常变化的打印模板,可以引入轻量级模板引擎:
const renderTemplate = (tpl: string, data: object) => {
return tpl.replace(/\{\{(.*?)\}\}/g, (_, key) => data[key.trim()])
}
// 使用示例
const html = renderTemplate(`
<div class="header">{{title}}</div>
<table>{{tableContent}}</table>
`, { title: '出货单', tableContent: generateTable(items) })
3.2 打印队列管理
高频打印场景下的防抖处理:
const createPrintQueue = () => {
let queue: Promise<boolean> = Promise.resolve(true)
return (task: () => Promise<boolean>) => {
queue = queue.then(() => task())
return queue
}
}
// 在Hook中使用
const { enqueue } = usePrintQueue()
const safePrint = (options) => enqueue(() => print(options))
4. 工程化实践:从Hook到生态
4.1 单元测试策略
针对打印Hook的测试要点:
describe('useLodop', () => {
beforeAll(() => {
// 模拟Lodop环境
global.window.LODOP = mockLodop
})
it('should handle printer selection', async () => {
const { result } = renderHook(() => useLodop())
await act(() => result.current.selectPrinter('HP-LaserJet'))
expect(result.current.currentPrinter).toBe('HP-LaserJet')
})
it('should reject when plugin not installed', async () => {
global.window.LODOP = undefined
const { result } = renderHook(() => useLodop())
await expect(result.current.print({ type: 'label' }))
.rejects.toThrow('LODOP_NOT_INSTALLED')
})
})
4.2 与状态管理方案集成
如何将打印状态融入全局Store:
// 在Pinia/Vuex中
export const usePrintStore = defineStore('print', () => {
const jobs = ref<PrintJob[]>([])
const { print } = useLodop()
const addJob = async (options: PrintOptions) => {
const job = { ...options, status: 'pending' }
jobs.value.push(job)
try {
await print(options)
job.status = 'completed'
} catch (e) {
job.status = 'failed'
}
}
return { jobs, addJob }
})
5. 实战中的经验结晶
在大型WMS系统中实施这套方案后,我们总结出几个关键点:
- 打印机缓存策略 :将用户选择的打印机索引持久化到localStorage,避免每次都需要重新选择
- 模板版本控制 :当打印模板需要频繁更新时,可以考虑为模板添加版本号,通过接口动态获取最新模板
- 批量打印优化 :对于大批量标签打印,建议先合并所有内容到一个打印任务,而不是多次调用PRINT()
// 批量打印示例
const printBatchLabels = (items: LabelItem[]) => {
LODOP.PRINT_INIT('')
items.forEach((item, index) => {
LODOP.ADD_PRINT_IMAGE(index * 100, 0, '100%', '100%', item.base64)
if (index < items.length - 1) {
LODOP.NewPage()
}
})
LODOP.PRINT()
}
通过这种分层架构的设计,我们的打印相关代码量减少了70%,而应对新打印需求的速度提升了数倍。当需要支持新的打印类型时,只需要在能力抽象层添加新的策略实现,业务组件几乎不需要修改。
更多推荐



所有评论(0)