LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

供应链系统中的 Web 打印方案的探索实践

admin
2026年1月30日 20:13 本文热度 69

一、前言

在供应链业务系统中,打印是一项难以被完全数字化替代的基础能力。尽管系统形态不断向 Web 化、平台化演进,但在仓储、物流、配送等线下环节,大量核心流程依然依赖纸质单据(如面单、送货单、运输单)完成流转与交接。

打印的稳定性与一致性,往往直接影响现场作业效率。一旦打印出现异常,问题会迅速放大,甚至直接阻断线下业务流程。

基于真实的供应链项目实践,本文将围绕浏览器打印这一常被低估的能力,系统性地梳理主流方案的技术取舍,并重点展开基于 window.print 的实现思路与工程实践。

二、供应链系统中的主流打印方案调研

围绕“如何将单据稳定、可靠地打印出来”,在实际项目中常见的 Web 打印方案大致可以分为三类:

  • 基于 DOM 的浏览器原生打印方案
  • 基于可视化模板的前端打印方案
  • 基于本地打印控件的软件方案

下面结合实际项目经验,对这三类方案进行简要分析。

1.基于DOM浏览器原生打印方案

这是目前最常见、也是前端介入成本最低的一种打印实现方式。

其核心思路是:

直接使用浏览器原生打印能力,将页面或指定 DOM 节点渲染为打印内容。

打印效果如下图:

实现方式

  1. 原生window.print()

// window.printwindow.print

  1. 使用 print-js 等开源库进行增强

官网地址:传送门(https://printjs.crabbly.com/)

// printJsprintJS({  printable'print-area',  type'html',  style'@page { size: A4; margin: 10mm; }',  targetStyles: ['*']});

优点

  • 零额外依赖,完全基于浏览器原生能力
  • 浏览器兼容性优秀:Chrome、Firefox、Safari、Edge 均支持
  • 开发与维护成本低,对现代前端技术栈(React / Vue)非常友好
  • 页面即模板,业务变更成本低,适合供应链中高频调整的单据场景

缺点

  • 样式控制高度依赖 CSS,对分页、跨页控制要求较高
  • 批量打印需自行实现调度与队列逻辑
  • 打印体验受浏览器限制
    • 页眉页脚难以完全控制
    • 无法绕开系统打印弹窗

2.基于可视化模板的前端打印方案(HiPrint)

以 HiPrint 为代表的方案,通常基于 jQuery,通过拖拽方式设计打印模板,再将业务数据动态渲染到模板中进行打印。HiPrint 官网地址:传送门(http://hiprint.io/)

配置示例效果如下图:

实现方式

// 基于jQuery的打印设计器$('#element').hiPrint({  designertrue// 开启可视化设计  templates: [...] // 预设模板});

优点

  • 所见即所得的可视化模板设计
    • 拖拽式配置
    • 非开发人员也可参与模板调整
  • 布局组件丰富
    • 表格、文本、条码、图片等组件齐全
    • 支持动态数据绑定
  • 模板管理能力完善
    • 模板导入 / 导出
    • 支持模板版本管理

缺点

  • 技术栈依赖性强:强依赖 jQuery,与 React / Vue 整合成本高
  • 性能问题明显
    • 模板复杂时渲染速度下降
    • 大数据量场景容易卡顿
  • 样式隔离难度大
    • 容易与现有系统样式冲突
    • 需要额外处理样式覆盖与作用域

3.基于本地打印控件的软件方案(LODOP)

LODOP 是一类 依赖本地打印控件或客户端程序 的打印方案,通过浏览器与本地程序通信完成打印。在早期政务、财务、制造业系统中使用较多。官网地址:传送门(https://www.lodop.net/)

实现方式

  • 先去Lodop官网下载相应的安装包
  • 解压安装后将LodopFuncs.js放在项目中
  • 具体页面引入 Lodop

import { getLodop } from "@/plugins/LodopFuncs";
let LODOP = getLodop()

  • 执行打印指令

// 需要先安装LODOP软件LODOP.PRINT_INIT("打印任务");LODOP.ADD_PRINT_TEXT(5010020030"供应链单据");LODOP.PRINT();

优点

  • 打印控制能力极强
    • 支持毫米级定位
    • 可直接控制打印机指令
  • 对专业打印设备支持完善

缺点

  • 浏览器兼容性极差
    • 依赖 ActiveX
    • 现代浏览器与移动端基本不支持
    • 只支持windows 系统
  • 部署与运维成本高
    • 每台客户端需安装软件
    • 需要管理员权限
  • 安全性与可持续性问题
    • ActiveX 存在安全隐患
    • 已逐步被浏览器厂商淘汰

4.方案对比一览表

维度window.printjQuery HiPrintLODOP
开发成本极低中等
打印精度一般(受限于浏览器)良好极高
跨浏览器优秀良好极差
部署成本零部署高(需安装客户端)
维护成本中等

5.方案选型结论

结合当前业务特点:

  • 需要快速适配不同客户与环境
  • 系统主体基于 React 技术栈
  • 单据模板相对稳定,支持页面预览

最终选择了 基于 DOM 的浏览器打印方案,并以 window.print 作为核心实现方式

为什么直接使用 PrintJs ?

可能会有人疑惑,既然是 DOM 打印,为什么不使用 print-js 进行封装?主要原因如下:

  • print-js 需要能够直接获取到打印节点(dom 的id 或者原始的html)
    • 必须将预览节点在打印前挂载到页面上。
  • iframe 隔离成本高
    • 需要单独引入工程化依赖包CSS 资源。如: Antd等
  • 样式维护成本高
    • 额外样式需要么通过字符串方式注入,要么将工程的less 转换成css 引入,不利于后续维护。

总的来说,print-js 在工程化配置项目依旧不是那么方便还用。

三、window.print 的能力边界

window.print() 本质上是触发浏览器打印对话框。当打印动作触发时,浏览器会将当前页面最终渲染的结果输出为打印内容,或者导出为 PDF 文件。

也正因为这一点,window.print 天生存在一个非常重要的限制:

它无法直接指定“只打印某一个 DOM 区域”。

浏览器并不知道你只想打印某一个 div,它只会按照当前页面的渲染结果,完整地执行一次打印流程。
这意味着,如果页面上存在导航栏、按钮、筛选条件等元素,它们同样会被一并打印出来。

四、指定区域打印的实现思路

既然 window.print 无法直接指定打印区域,那么解决问题的思路就非常清晰了:

在触发打印之前,主动控制页面最终的渲染结果,让真正参与渲染的内容只剩下需要打印的部分。

顺着这个思路,第一个最直观的方案自然就出现了。

1.innerHTML 直接替换页面内容

既然浏览器只会打印当前页面的渲染结果,那么我们是否可以在打印前,直接用需要打印的内容替换整个页面

实现方式也很简单:

  • 缓存当前页面内容
  • 将 document.body.innerHTML 替换为打印区域内容
  • 执行 window.print()
  • 打印完成后再恢复页面

代码示例如下:

// 获取预览组件的htmlconst getPreviewContainerHtml = (component) => {  const div = document.createElement('div');
  flushSync(() => {    ReactDOM.render(component, div);  })
  return div.innerHtml}
const prinit = () => {  const oldHtml = document.body.innerHtml;  const printHtml = getPreviewContainerHtml(<PreviewContainer />);  document.body.innerHtml = printHtml;  window.print()  document.body.innerHtml = oldHtml;}

这种方案在早期项目中非常常见。但随着实践的深入,很快就会发现一个问题:
直接替换 innerHTML 会彻底打破原有页面的渲染结构。

在 React / Vue 等现代前端框架中,这种方式会导致:

  • 组件状态丢失
  • 事件绑定失效
  • 页面需要强制刷新才能恢复

因此,这种方案虽然思路直接,但在复杂系统中并不安全。

2.新开标签页打印

既然直接替换当前页面会破坏原有渲染结构,那么很自然就会想到:

如果不动当前页面,而是新开一个标签页,专门用来承载打印内容,是否就可以解决这个问题?

新开标签页后,我们可以:

  • 在新页面中渲染完整的打印内容
  • 调用新页面的 window.print()
  • 当前业务页面完全不受影响

从技术角度看,这个方案确实可以稳定地打印出我们想要的内容。但在真实业务场景中,很快又会暴露出新的问题:

  • 浏览器可能拦截弹窗
  • 用户需要多一步切换窗口的操作
  • 打印流程被打断,体验不够流畅

对于仓库、物流等高频打印场景来说,这种额外的交互成本是不可忽略的

于是问题进一步演化为:

能否在不新开标签页、不破坏当前页面的前提下,在当前页签内完成指定内容的打印?

3.iframe 当前页签打印

顺着这个问题继续往下推,自然而然就会引出 iframe 打印方案

其核心思路是:

  • 在当前页面中动态创建一个隐藏的 iframe
  • 将需要打印的内容完整渲染到 iframe 中
  • 调用 iframe 内部的 print() 方法完成打印

示例代码如下:

const print = () => {  const iframe = document.createElement('iframe');  iframe.src = `预览页地址`;  iframe.setAttribute(    'style',    'visibility: hidden; height: 0; width: 0; position: absolute; border: 0'  );
  window.addEventListener('onafterprint'() => {    console.log('打印完成');    iframe.remove();  });
  document.body.appendChild(iframe);
  iframe.onload = () => {    setTimeout(function () {      iframe.contentWindow?.print();    }, 1000);  };};

从浏览器的角度来看,iframe 本身就是一个完整的页面环境,因此可以独立完成打印流程,而不会影响主页面的渲染状态。

这一方案在实践中具备明显优势:

  • 不破坏当前页面状态
  • 不依赖新窗口或弹窗
  • 对 React / Vue 等现代框架友好
  • 非常适合供应链系统中的高频打印场景

4.批量打印

到目前为止,我们已经可以稳定地完成 单个单据的打印。但在供应链场景中,一个非常常见的需求是:

一次性打印多张面单、送货单或运输单。

在浏览器环境下,这一需求首先会受到一个客观限制:

  • 浏览器在同一时间只能处理一个打印任务
  • 调用 window.print() 时,浏览器会弹出系统打印对话框
  • 在用户完成打印设置并关闭对话框之前,页面线程会被阻塞

这意味着,通过循环多次调用 window.print() 来实现批量打印并不可行

批量渲染内容 + CSS 分页

基于上述限制,一个更合理的思路是:在一次打印动作中,承载所有需要打印的单据内容。具体做法是:

  • 遍历需要打印的单据数据
  • 将每一张单据渲染为独立的打印容器
  • 通过 CSS 控制分页规则
  • 最终只触发一次 window.print()

 示例代码如下:

// 批量创建打印容器printPageList.map(() => {  return <PrintContent className="single-order-page" />    })
// css 配置/* 确保每个订单在新页开始 */.single-order-page {  page-break-before: always;  page-break-inside: avoid;}
/* 第一个订单不需要分页 */.single-order-page:first-child {  page-break-before: avoid;}

通过这种方式,浏览器会将每个单据视为一页内容,顺序稳定、实现简单,非常适合版式统一的批量单据打印场景

大批量场景下的性能考量

需要注意的是,当单据数量非常大(例如超过 50 张)时,一次性渲染所有打印内容,可能会带来内存占用和页面卡顿的问题。在这种情况下,可以考虑引入串行队列式的打印方案

该方案的核心思路是:

  • 构建一个打印任务队列
  • 每次只渲染并打印一张单据
  • 当前打印结束后,再进入下一张单据的处理

但需要特别注意的是,浏览器无法准确感知打印完成的时机,因此该方案通常需要额外的控制手段,例如:

  • 由用户交互驱动(如点击“下一张”)
  • 通过时间间隔进行节流控制

这种方式更适合作为大批量或特殊场景下的补充方案,而不是默认选择。

五、核心 CSS 打印配置:从“能打”到“好用”

在基于浏览器的打印方案中,CSS 决定了最终的输出效果。合理的打印样式配置,往往可以显著减少版式错乱、内容截断等问题,也是浏览器打印能否真正落地的重要前提。

下面结合实际项目,整理几类最常用、也最容易被忽视的打印样式配置。

1.使用 @media print 定义专用打印样式

浏览器提供了 @media print 媒体查询,用于在打印场景下覆盖页面的默认样式

通过该方式,可以做到:

  • 页面正常浏览时样式不受影响
  • 打印时只应用与单据相关的样式规则

@media print {  /* 打印场景下隐藏页面中的非单据内容 */  .no-print {    display: none !important;  }}

2.页面尺寸与边距控制

如果不对打印页面的尺寸和边距进行控制,常见问题包括:

  • 内容被裁切
  • 页面留白过多
  • 不同浏览器下表现不一致

通过 @page 可以明确指定打印纸张规格与边距:

@media print {  @page {    size: A4;    margin10mm;  }}

3.避免表格内容被强制拆页

表格是供应链单据中最常见的结构,同时也是打印问题的高发区域。
如果不加控制,浏览器可能会在任意位置拆分页,导致一行数据被拆成两页。

@media print {  table {    border-collapse: collapse;    /* 避免整个表格在打印时被拆分 */    page-break-inside: avoid;  }
  tr {    /* 避免单行数据跨页 */    page-break-inside: avoid;  }}

4.主动控制分页位置

在部分业务场景中,需要对分页位置进行明确控制,例如:

  • 一张送货单单独占一页
  • 合计信息必须在新页展示

此时可以通过自定义分页样式来实现:

@media print {  .page-break {    /* 在该元素前强制分页 */    page-break-before: always;  }}

5.打印颜色与背景处理

浏览器在打印时,默认会忽略背景颜色与部分样式,这在以下场景中尤为明显:

  • 面单背景色
  • 标签类单据
  • 状态标识区域

@media print {  body {    /* 强制按页面样式输出颜色 */    -webkit-print-color-adjust: exact;    print-color-adjust: exact;  }}

注:Window 电脑如果不设置 -webkit-print-color-adjust: exact 打印输出水印内容, 会出现空白现象。

六、总结

浏览器打印在供应链系统中看似只是一个基础能力,但在真实项目中,往往涉及方案选型、技术栈适配、用户体验以及长期维护成本等多方面的权衡。

本文结合具体的业务背景,对主流 Web 打印方案进行了梳理,并重点分析了基于 window.print 的实现思路及其工程边界。

可以看到,window.print 本身并不复杂,真正的挑战更多来自页面渲染控制、样式管理以及打印流程的工程化处理。这也使得浏览器打印更像是一项系统能力的组合,而非单一 API 的简单调用。

在当前阶段,window.print 配合 iframe 依然是 Web 打印方案中性价比最高、可控性最强的一种实现方式。随着业务复杂度的提升,打印方案仍有进一步演进与细化的空间。


阅读原文:原文链接


该文章在 2026/2/4 15:51:02 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved