前端集成Lodop实现复杂业务单据打印技术实践
一、技术选型与准备工作
1.1 打印技术方案对比
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
浏览器打印 | 无需插件、跨平台 | 格式控制差、无法精准分页 | 简单报表打印 |
Lodop | 精准控制、功能强大 | 需要安装插件 | 复杂业务单据打印 |
PDF打印 | 格式统一、跨平台 | 依赖阅读器、动态内容受限 | 标准化文档输出 |
原生驱动打印 | 直接控制打印机 | 开发复杂度高 | 特种设备对接 |
1.2 环境搭建步骤
<!-- 引入Lodop基础依赖 -->
<script src="http://localhost:8000/CLodopfuncs.js"></script>
<script src="http://localhost:8000/LodopFuncs.js"></script>
<!-- 兼容性检测 -->
<script>
function checkLodop() {
try {
const LODOP = getCLodop();
if (!LODOP) {
console.error("未检测到CLodop服务,请先安装打印插件");
window.open('http://www.lodop.net/download.html');
return false;
}
return true;
} catch (e) {
console.error("Lodop初始化异常:", e);
return false;
}
}
</script>
二、核心打印功能实现
2.1 连续打印实现(批量订单)
function printContinuousOrders(orders) {
const LODOP = getCLodop();
LODOP.PRINT_INIT("批量订单打印");
LODOP.SET_PRINT_PAGESIZE(1, '210mm', '297mm', 'A4');
orders.forEach((order, index) => {
LODOP.ADD_PRINT_HTM(
"10mm",
"10mm",
"190mm",
"277mm",
generateOrderHtml(order)
);
if (index < orders.length - 1) {
LODOP.NewPage();
}
});
LODOP.PREVIEW();
}
// 动态生成订单HTML模板
function generateOrderHtml(order) {
return `
<div class="order-box">
<h2>订单编号:${order.no}</h2>
<table border="1">
<tr>
<th>商品名称</th>
<th>规格</th>
<th>数量</th>
<th>单价</th>
</tr>
${order.items.map(item => `
<tr>
<td>${item.name}</td>
<td>${item.spec}</td>
<td>${item.quantity}</td>
<td>${item.price.toFixed(2)}</td>
</tr>
`).join('')}
</table>
<div class="total">总金额:¥${order.total.toFixed(2)}</div>
</div>
`;
}
2.2 智能分页打印(自动分页处理)
function smartPagePrint(htmlContent) {
const LODOP = getCLodop();
const PAGE_HEIGHT = 277; // A4可打印高度(mm)
LODOP.PRINT_INIT("智能分页打印");
LODOP.SET_PRINT_PAGESIZE(1, '210mm', '297mm', 'A4');
let currentY = 10;
let pageIndex = 1;
LODOP.ADD_PRINT_HTM(
`${currentY}mm`, "10mm", "190mm", "277mm",
htmlContent
);
// 自动分页检测
while(true) {
const contentHeight = LODOP.GET_PRINT_HEIGHT(pageIndex);
if (contentHeight <= PAGE_HEIGHT) break;
LODOP.SET_PRINT_MODE("CONTENT_SPLIT", pageIndex, 1);
pageIndex++;
}
LODOP.SET_PRINT_MODE("FULL_PAGE", true);
LODOP.PREVIEW();
}
2.3 标签打印实现(100x150mm标签)
function printLabels(labels) {
const LODOP = getCLodop();
const LABEL_WIDTH = 100;
const LABEL_HEIGHT = 150;
LODOP.PRINT_INIT("标签打印");
LODOP.SET_PRINT_PAGESIZE(3, `${LABEL_WIDTH}mm`, `${LABEL_HEIGHT}mm`, '自定义标签');
// 设置标签模板
LODOP.ADD_PRINT_RECT("1mm", "1mm", "98mm", "148mm", 0, 1);
labels.forEach((label, index) => {
const row = Math.floor(index / 3);
const col = index % 3;
LODOP.ADD_PRINT_TEXT(
"10mm",
`${10 + col*(LABEL_WIDTH+5)}mm`,
"90mm",
"20mm",
label.title
);
LODOP.ADD_PRINT_BARCODE(
"30mm",
`${10 + col*(LABEL_WIDTH+5)}mm`,
"90mm",
"40mm",
"128Auto",
label.barcode
);
if ((index + 1) % 6 === 0) {
LODOP.NewPage();
}
});
LODOP.SET_PRINT_MODE("POS_BASEON_PAPER", true);
LODOP.PREVIEW();
}
三、高级功能实现
3.1 打印任务队列管理
class PrintQueue {
constructor() {
this.queue = [];
this.isPrinting = false;
}
addTask(taskFn) {
this.queue.push(taskFn);
if (!this.isPrinting) this.processNext();
}
processNext() {
if (this.queue.length === 0) {
this.isPrinting = false;
return;
}
this.isPrinting = true;
const task = this.queue.shift();
try {
task(() => {
setTimeout(() => this.processNext(), 500);
});
} catch (e) {
console.error('打印任务失败:', e);
this.processNext();
}
}
}
// 使用示例
const queue = new PrintQueue();
queue.addTask((done) => {
printContinuousOrders(orderList);
LODOP.On_Return = (taskID, value) => {
if(value) done();
};
});
3.2 打印模板动态生成
function createDynamicTemplate(data, templateConfig) {
const { fields, layout, styles } = templateConfig;
return `
<style>
.template-container {
width: ${layout.width}mm;
height: ${layout.height}mm;
font-family: ${styles.fontFamily};
padding: ${layout.padding}mm;
}
${styles.customCSS}
</style>
<div class="template-container">
${fields.map(field => `
<div class="field ${field.className}"
style="position: absolute;
left: ${field.x}mm;
top: ${field.y}mm;
width: ${field.width}mm;
height: ${field.height}mm;">
${data[field.key]}
</div>
`).join('')}
</div>
`;
}
// 配置示例
const config = {
layout: {
width: 210,
height: 297,
padding: 5
},
fields: [
{
key: 'orderNo',
x: 10,
y: 15,
width: 50,
height: 10,
className: 'order-number'
},
// 更多字段配置...
],
styles: {
fontFamily: 'SimSun',
customCSS: `
.order-number {
font-size: 14pt;
font-weight: bold;
}
`
}
};
四、常见问题解决方案
4.1 打印偏移校准
function calibratePrinter() {
const LODOP = getCLodop();
// 生成校准模板
LODOP.PRINT_INIT("打印机校准");
LODOP.ADD_PRINT_LINE("0mm", "0mm", "0mm", "210mm", 0, 1);
LODOP.ADD_PRINT_LINE("0mm", "0mm", "297mm", "0mm", 0, 1);
LODOP.ADD_PRINT_TEXT("145mm", "95mm", "20mm", "10mm", "中线校准标记");
// 保存偏移量
LODOP.SET_PRINT_MODE("OFFSET_PERCENT", (dx, dy) => {
localStorage.setItem('PRINT_OFFSET_X', dx);
localStorage.setItem('PRINT_OFFSET_Y', dy);
});
LODOP.PREVIEW();
}
// 应用校准参数
function applyCalibration() {
const dx = localStorage.getItem('PRINT_OFFSET_X') || 0;
const dy = localStorage.getItem('PRINT_OFFSET_Y') || 0;
LODOP.SET_PRINT_MODE("POS_BASEON_PAPER", true);
LODOP.SET_PRINT_MODE("OFFSET_PERCENT", dx, dy);
}
4.2 打印异常处理
function safePrint(printFn) {
try {
if (!checkLodop()) return;
printFn();
LODOP.On_Return = (taskID, success) => {
if (!success) {
console.error(`打印任务失败,错误码:${LODOP.GetStatus()}`);
showErrorMessage(LODOP.GetStatusDes());
}
};
} catch (e) {
console.error("打印过程发生异常:", e);
showErrorMessage("打印系统异常,请联系管理员");
}
}
function showErrorMessage(msg) {
const errorBox = document.createElement('div');
errorBox.className = 'print-error';
errorBox.innerHTML = `
<div class="error-header">打印错误</div>
<div class="error-content">${msg}</div>
<button onclick="this.parentElement.remove()">关闭</button>
`;
document.body.appendChild(errorBox);
}
五、性能优化实践
5.1 打印缓存策略
const templateCache = new Map();
function getCachedTemplate(templateKey, generator) {
if (templateCache.has(templateKey)) {
return templateCache.get(templateKey);
}
const html = generator();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
templateCache.set(templateKey, doc);
return doc;
}
// 使用示例
function printOrder(order) {
const cachedDoc = getCachedTemplate('order', () =>
generateOrderHtml(defaultOrder));
const clone = cachedDoc.cloneNode(true);
// 填充动态数据...
const html = clone.documentElement.outerHTML;
LODOP.ADD_PRINT_HTM("10mm", "10mm", "190mm", "277mm", html);
}
5.2 批量数据分块打印
async function chunkedPrint(data, chunkSize = 50) {
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
await new Promise(resolve => {
printContinuousOrders(chunk);
LODOP.On_Return = (taskID, success) => {
if (success) resolve();
};
});
// 防止内存泄漏
LODOP.CLEAR_PRINT_STYLE();
if (i + chunkSize < data.length) {
LODOP.NewPageA();
}
}
}
六、总结与扩展
本文详细讲解了基于Lodop的复杂打印场景实现方案,涵盖以下关键技术点:
- 多类型打印控制:通过精确的坐标计算实现标签打印、智能分页等复杂需求
- 打印任务管理:引入队列机制确保打印任务有序执行
- 动态模板系统:实现配置化模板生成,提升系统灵活性
- 稳定性增强:完善的异常处理机制和校准方案
扩展方向建议:
- 结合WebSocket实现远程打印监控
- 集成电子签名实现单据防伪
- 开发可视化的模板设计器
- 对接称重设备实现自动打标
通过以上技术方案,企业可以构建稳定可靠的打印服务系统,满足各类业务场景的打印需求,同时为后续的物联网集成打下坚实基础。