前端集成Lodop实现复杂业务单据打印技术实践

857 阅读4分钟

前端集成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的复杂打印场景实现方案,涵盖以下关键技术点:

  1. 多类型打印控制:通过精确的坐标计算实现标签打印、智能分页等复杂需求
  2. 打印任务管理:引入队列机制确保打印任务有序执行
  3. 动态模板系统:实现配置化模板生成,提升系统灵活性
  4. 稳定性增强:完善的异常处理机制和校准方案

扩展方向建议:

  • 结合WebSocket实现远程打印监控
  • 集成电子签名实现单据防伪
  • 开发可视化的模板设计器
  • 对接称重设备实现自动打标

通过以上技术方案,企业可以构建稳定可靠的打印服务系统,满足各类业务场景的打印需求,同时为后续的物联网集成打下坚实基础。