You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 17:03:25