餐饮收银、便利店结帐、排队取号、外卖厨房联------热敏小票是 B/S 系统里打印量最大的一类场景。开发者搜「网页打印小票」「热敏打印机 Web 接入」,往往卡在三个问题上:
window.print()必弹对话框,前台收银不能接受- 纸宽是 58mm / 80mm,不是 A4,版式一调就乱
- 出纸发虚、条码扫不出,热敏头和矢量/HTML 抗锯齿对不上
本文专门讲:如何用 npm 包 web-print-pdf 做热敏小票静默打印------HTML 直打、PDF 直打、批量厨房联、锯齿优化,一次讲清。
1. 热敏小票和 A4 打印有什么不同
| 维度 | A4 办公打印 | 热敏小票 |
|---|---|---|
| 纸宽 | 210mm | 常见 58mm、80mm |
| 纸长 | 固定 | 连续纸,高度随内容变 |
| 色彩 | 黑白/彩色 | 几乎只有黑白 |
| 内容来源 | Word/PDF 多 | HTML 模板、后端 PDF 多 |
| 用户操作 | 可弹窗确认 | 必须静默,收银员不能多点一步 |
| 典型设备 | 激光/喷墨 | 芯烨、佳博、EPSON TM 系列等 |
结论:把小票当成「窄版 HTML 页面 + 指定纸宽 + 静默下发」,而不是把 A4 参数硬套上去。
2. 方案选型:为什么不自己 window.print
| 方式 | 对话框 | 指定热敏机 | 58mm 纸宽 | 适合小票 |
|---|---|---|---|---|
window.print() |
必弹 | 用户手选 | 难控 | ❌ |
| 浏览器 PDF 下载再打开 | 多步 | 否 | 难控 | ❌ |
| web-print-pdf + 本地客户端 | 可静默 | ✅ | ✅ 自定义 width | ✅ |
web-print-pdf 通过 WebSocket 调本机打印客户端:浏览器只发 HTML/PDF 和参数,指定打印机、纸宽、份数在本地一次完成,适合收银台、档口、KDS 厨打。
3. 小票最常用:printHtml + 自定义纸宽
热敏小票多数是 HTML 模板 (Vue/React 里拼一段 div 即可)。关键是 pdfOptions 里用 width 指定纸宽 ,不要设 paperFormat: 'A4'。
3.1 58mm 收银小票(最常见)
javascript
import webPrintPdf from "web-print-pdf";
const receiptHtml = `
<div style="font-family: monospace; font-size: 12px; line-height: 1.4;">
<div style="text-align:center;font-weight:bold;">××便利店××</div>
<div style="text-align:center;">流水号:20260831001</div>
<hr/>
商品A x2 ¥10.00
商品B x1 ¥ 5.50
<hr/>
<div style="text-align:right;font-weight:bold;">合计:¥15.50</div>
<div style="text-align:center;margin-top:8px;">谢谢惠顾</div>
</div>
`;
await webPrintPdf.printHtml(
receiptHtml,
{
width: "58mm", // 58mm 热敏纸宽,勿用 A4
printBackground: true,
margin: { top: "2mm", bottom: "2mm", left: "1mm", right: "1mm" },
},
{
printerName: "XP-58", // getPrinterList() 动态取更稳
scaleMode: "shrink",
copies: 1,
},
{ action: "print" }
);
3.2 80mm 厨房联 / 外卖联
纸宽改成 80mm,字号可略大,其余结构相同:
javascript
pdfOptions: {
width: "80mm",
printBackground: true,
margin: { top: "3mm", bottom: "3mm", left: "2mm", right: "2mm" },
}
3.3 HTML 排版建议(少踩坑)
css
/* 模板内可内联,也可 printHtml 前注入 style */
* { box-sizing: border-box; }
body { margin: 0; padding: 0; }
table { width: 100%; border-collapse: collapse; font-size: 12px; }
td { padding: 2px 0; vertical-align: top; }
hr { border: none; border-top: 1px dashed #000; margin: 4px 0; }
- 用 等宽或宋体/黑体,避免过细字体
- 金额、数量用 table 两列/三列,比空格对齐稳
- 二维码、条码区域留足 quiet zone(留白)
- 高度不用死写:内容多长纸就多长,客户端按 HTML 实际高度生成 PDF
4. 后端已生成 PDF:printPdf / printPdfByUrl
若小票是 Java / Node 生成的 PDF(带 Logo、规范条码),走 PDF 打印接口:
javascript
// 本地 PDF
await webPrintPdf.printPdf(
pdfPath,
{
printerName: "XP-58",
paperFormat: "58mm", // 与驱动里纸张名一致;或用驱动显示的自定义名
scaleMode: "shrink",
sharp: true, // 热敏锯齿明显时开启,见第 12 篇
},
{ action: "print" }
);
// 远程 PDF URL
await webPrintPdf.printPdfByUrl(url, pdfOptions, printOptions, extraOptions);
注意 :printPdfByUrl 场景下,pdfOptions.width/height 不会改 PDF 页尺寸 (只负责下载);控纸宽看 printOptions.paperFormat 或驱动默认纸。
5. 热敏锯齿:sharp: true
小票上的 一维码、细线、小号字 在热敏机上容易「毛边、麻点」。根因是抗锯齿灰边遇上点阵只有开/关。
在 printOptions 里加:
javascript
printOptions: {
sharp: true, // 送印前二值化,适合热敏
scaleMode: "shrink",
paperFormat: "58mm",
}
- 默认
sharp: false,不影响老业务 - 输出硬黑白,小票场景保持
colorful: false即可 - 与纸宽、scaleMode 搭配使用;paperFormat 务必配对,误用 A4 会裁切
更细的机理与排坑见同系列《网页静默打印PDF之热敏打印机抗锯齿优化》。
6. 厨打 / 多单:batchPrint
一张订单打 收银联 + 厨房联 + 打包联 ,用 batchPrint 一次下发,避免前台 for 循环三次:
javascript
await webPrintPdf.batchPrint(
[
{ data: cashierHtml, type: "printHtml" },
{ data: kitchenHtml, type: "printHtml" },
{ data: packHtml, type: "printHtml" },
],
{ width: "58mm", printBackground: true, margin: { top: "2mm", bottom: "2mm", left: "1mm", right: "1mm" } },
{ printerName: "XP-58", scaleMode: "shrink" },
{ action: "print" }
);
不同联打不同打印机:拆成多次调用,或在 batch 任务里为每项指定不同 printOptions(按业务封装)。
7. 参数对照:小票场景怎么填
| 参数 | 小票建议 | 说明 |
|---|---|---|
pdfOptions.width |
58mm / 80mm |
HTML 转 PDF 时用;优先于 paperFormat |
pdfOptions.paperFormat |
不设或留空 | 设了 A4 会把小票拉成 A4 版式 |
pdfOptions.margin |
上下 2--3mm,左右 1--2mm | 留一点安全边,防贴边裁切 |
printOptions.printerName |
驱动里的热敏机名 | 用 getPrinterList() 动态取 |
printOptions.paperFormat |
驱动纸张列表同名 | PDF 直打时用 |
printOptions.scaleMode |
shrink(默认) |
缩进可打印区,避免进纸侧切字 |
printOptions.sharp |
条码/细线差时 true |
热敏抗锯齿 |
extraOptions.action |
print |
收银必须静默;preview 仅调试 |
8. 常见问题
8.1 小票只有半宽、居中一条
原因 :paperFormat: 'A4' 或 print 阶段纸宽未配对。
处理 :HTML 场景用 width: '58mm' 且不设 A4;PDF 场景 printOptions.paperFormat 对齐驱动。
8.2 字体太小或太大
原因 :HTML 用 px,热敏有效宽度只有 58mm。
处理:正文 12px 左右、标题 14--16px;实机打一张再微调。
8.3 条码扫不出
原因 :条空对比度不够、quiet zone 不足、或热敏锯齿。
处理 :加 sharp: true;条码模块用 SVG/图片并留边;避免 1px 细线。
8.4 连续点击重复出纸
原因 :前端未防抖,或厨打重复提交。
处理 :按钮 disable + 订单号幂等;批量用 batchPrint 一次提交。
8.5 58 和 80 混部
同一套 HTML 可抽 paperWidth 配置项,按门店打印机型号传入 width: '58mm' 或 '80mm'。
9. 和 ESC/POS 指令打印的边界
不少热敏机支持 ESC/POS 指令(文本行、切刀、开钱箱)。web-print-pdf 路线是:
- HTML/PDF → 本地客户端 → 系统打印队列 → 驱动 → 热敏机
- 优势:版式自由、Logo/二维码/表格好做、与 Web 模板一体
- 不替代:需要 切刀、钱箱、蜂鸣 等硬件指令时,仍需 ESC/POS SDK 或厂商控件
实际项目里常见 收银小票走 web-print-pdf,开钱箱走 USB/串口指令 的组合。
10. 上线前检查清单
| # | 项 |
|---|---|
| 1 | 目标门店热敏型号实机各打 10 张 |
| 2 | width 与物理纸宽一致(58 / 80) |
| 3 | printerName 来自 getPrinterList(),非写死 |
| 4 | 条码手机扫 10 次成功率 |
| 5 | 长小票(>30 行)不断纸、不裁尾 |
| 6 | 弱网时 requestTimeout 足够(建议 ≥15s) |
| 7 | 厨打高峰 batchPrint 顺序与份数正确 |
11. 小结
热敏小票的核心不是「能打印」,而是:
- 静默 ------
action: 'print',收银员零多余点击 - 纸宽 ------HTML 用
width: '58mm'/'80mm',别用 A4 - 清晰 ------条码扫不稳时加
sharp: true - 稳定 ------
printerName动态取,scaleMode: 'shrink'防贴边裁切
接入从 web-print-pdf 开始:printHtml 覆盖 80% 小票场景,printPdf / printPdfByUrl 接后端 PDF,batchPrint 接厨打多单。先把一张 58mm 测试小票打顺,再复制到各业务模板,比一上来抽象「打印服务」省很多时间。