如何用React.js与NodeJs实现热敏打印机发票小票的创建与打印
实现指定样式热敏发票小票的方案
一、前端小票样式精准还原
针对80mm宽度的热敏纸(换算为288px左右),用React构建结构+CSS控制排版,完全匹配目标小票样式:
核心思路
- 固定容器宽度为
288px,内部用等宽字体(如Courier New)保证文字、数字对齐 - 拆分小票模块:抬头信息、交易基础数据、商品列表、结算汇总、底部提示
- 关键金额用加粗样式突出,模块间用虚线/实线分隔
示例代码
import React from 'react'; import './Receipt.css'; const Receipt = ({ orderData }) => { return ( <div className="receipt-container"> <div className="receipt-header"> <h2>XX连锁超市</h2> <p>地址:XX市XX区XX路XX号 | 电话:0XX-XXXXXXX</p> </div> <div className="receipt-info"> <p>单号:{orderData.orderNo}</p> <p>时间:{orderData.createTime}</p> <p>收银员:{orderData.cashier}</p> </div> <div className="receipt-items"> <div className="item-row item-header"> <span>商品名称</span> <span>单价</span> <span>数量</span> <span>金额</span> </div> {orderData.items.map(item => ( <div key={item.id} className="item-row"> <span>{item.name}</span> <span>{item.price.toFixed(2)}</span> <span>{item.quantity}</span> <span>{(item.price * item.quantity).toFixed(2)}</span> </div> ))} </div> <div className="receipt-summary"> <p>小计:¥{orderData.subtotal.toFixed(2)}</p> <p>折扣:¥{orderData.discount.toFixed(2)}</p> <p>实收:¥{orderData.received.toFixed(2)}</p> <p>找零:¥{orderData.change.toFixed(2)}</p> </div> <div className="receipt-footer"> <p>感谢您的光临,欢迎下次再来!</p> </div> </div> ); }; export default Receipt;
对应的CSS:
.receipt-container { width: 288px; font-family: 'Courier New', monospace; padding: 10px; } .receipt-header { text-align: center; margin-bottom: 10px; } .receipt-info { margin-bottom: 15px; font-size: 12px; } .item-row { display: flex; justify-content: space-between; margin-bottom: 5px; font-size: 12px; } .item-header { font-weight: bold; border-bottom: 1px dashed #000; padding-bottom: 3px; } .receipt-summary { margin-top: 15px; border-top: 1px dashed #000; padding-top: 10px; font-size: 12px; text-align: right; } .receipt-footer { text-align: center; margin-top: 15px; font-size: 12px; }
二、热敏小票打印方案
1. 浏览器原生打印(适配普通USB热敏打印机)
通过@media print隐藏页面其他元素,仅保留小票容器,调用浏览器打印功能:
@media print { body * { visibility: hidden; } .receipt-container, .receipt-container * { visibility: visible; } .receipt-container { position: absolute; left: 0; top: 0; } @page { margin: 0; /* 去掉默认页眉页脚 */ } }
React中封装打印触发函数:
const printReceipt = () => { window.print(); }; // 在组件中添加打印按钮 <button onClick={printReceipt}>打印小票</button>
2. 后端直接发送打印指令(适配网络热敏打印机)
如果使用带网口/WiFi的热敏打印机,用Node.js的escpos库直接发送ESC/POS打印指令:
const escpos = require('escpos'); const printReceiptViaEscpos = (orderData) => { // 连接网络打印机(替换为实际IP和端口) const device = new escpos.Network('192.168.1.100', 9100); const printer = new escpos.Printer(device); device.open(err => { if (err) throw err; printer .font('a') .align('ct') .style('bu') .size(1, 1) .text('XX连锁超市') .size(0, 0) .text('地址:XX市XX区XX路XX号 | 电话:0XX-XXXXXXX') .align('lt') .text(`单号:${orderData.orderNo}`) .text(`时间:${orderData.createTime}`) .text(`收银员:${orderData.cashier}`) .text('----------------------------') .align('lt') .text('商品名称 单价 数量 金额') .text('----------------------------') .then(() => { orderData.items.forEach(item => { const name = item.name.padEnd(16, ' '); const price = item.price.toFixed(2).padStart(6, ' '); const quantity = item.quantity.toString().padStart(4, ' '); const amount = (item.price * item.quantity).toFixed(2).padStart(6, ' '); printer.text(`${name}${price}${quantity}${amount}`); }); }) .then(() => { printer .text('----------------------------') .align('rt') .text(`小计:¥${orderData.subtotal.toFixed(2)}`) .text(`折扣:¥${orderData.discount.toFixed(2)}`) .text(`实收:¥${orderData.received.toFixed(2)}`) .text(`找零:¥${orderData.change.toFixed(2)}`) .align('ct') .text('感谢您的光临,欢迎下次再来!') .cut() .close(); }); }); };
三、Node.js后端数据支撑
设计订单模型并提供接口:
const express = require('express'); const router = express.Router(); const Order = require('../models/Order'); // 获取订单详情 router.get('/order/:id', async (req, res) => { try { const order = await Order.findById(req.params.id).populate('items'); res.json(order); } catch (err) { res.status(500).json({ message: err.message }); } }); // 触发网络打印机打印 router.post('/print/:id', async (req, res) => { try { const order = await Order.findById(req.params.id).populate('items'); printReceiptViaEscpos(order); res.json({ message: '打印任务已提交' }); } catch (err) { res.status(500).json({ message: err.message }); } }); module.exports = router;
内容的提问来源于stack exchange,提问作者Joeduciel
相关产品推荐
相关产品推荐

