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

React项目中QZ Tray打印发票出现多余空格的问题求助

React中QZ Tray打印发票出现多余空格的解决方案

问题描述

在React项目中使用QZ Tray库实现发票打印功能,发票在页面端显示结构正常,但通过QZ Tray打印时会出现多余空格,导致打印格式异常。以下是传入QZ Tray的发票DOM结构及打印处理函数:

发票DOM结构

{/* Invoice Header */}
<h6>Invoice # {invoice.id}</h6>
<h6
  style={{
    display: "flex",
    flexDirection: "column",
    textAlign: "center",
    fontSize: "0.75rem",
    color: "#1f2937",
  }}
>
  Punched By: {invoice.user?.fullName || ""}
</h6>

<h6
  style={{
    fontSize: "0.75rem",
    color: "#1f2937",
  }}
>
  Date: {dayjs(invoice.createdAt).format("D MMM, YYYY, h:mm A")}
</h6>

{/* Order Information */}
<h5
  style={{
    marginTop: "0.5rem",
    textAlign: "center",
    fontSize: "1.125rem",
    fontWeight: "bold",
    color: "#1f2937",
  }}
>
  Order # {invoice.orderNo.split("-")[1]}
</h5>

{/* Table Information */}
{invoice.tableId && (
  <h6 style={{ textAlign: "center", fontSize: "0.875rem", fontWeight: "bold", color: "#1f2937" }}>
    {`${invoice.table?.tableNo} ${invoice.table?.floor?.level}`}
  </h6>
)}

{/* Order Type & Customer Details */}
<div style={{ marginTop: "0.25rem" }}>
  {invoice.employee && invoice.employee.name && (
    <div style={{ display: "flex", justifyContent: "space-between" }}>
      <span style={{ fontSize: "0.75rem", fontWeight: "bold", color: "#1f2937" }}>Waiter:</span>
      <span style={{ fontSize: "0.75rem", fontWeight: "bold", color: "#1f2937" }}>
        {invoice.employee.name}
      </span>
    </div>
  )}
  <div style={{ display: "flex", justifyContent: "space-between" }}>
    <span style={{ fontSize: "0.75rem", fontWeight: "bold", color: "#1f2937" }}>Order Type:</span>
    <span style={{ fontSize: "0.75rem", fontWeight: "bold", color: "#1f2937" }}>{invoice.invoiceType}</span>
  </div>

  {invoice.invoiceType !== "DineIn" && invoice.customerId && (
    <>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <span style={{ fontSize: "0.75rem", fontWeight: "bold", color: "#1f2937" }}>Customer Name:</span>
        <span style={{ fontSize: "0.75rem", color: "#1f2937" }}>{invoice.customer?.name}</span>
      </div>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <span style={{ fontSize: "0.75rem", fontWeight: "bold", color: "#1f2937" }}>Phone No:</span>
        <span style={{ fontSize: "0.75rem", color: "#1f2937" }}>{invoice.customer?.contact}</span>
      </div>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <span style={{ fontSize: "0.75rem", fontWeight: "bold", color: "#1f2937" }}>Address:</span>
        <span style={{ fontSize: "0.75rem", color: "#1f2937" }}>{invoice.customer?.address}</span>
      </div>
    </>
  )}
</div>

{/* Products List */}
<div style={{ marginTop: "0.5rem" }}>
  <div
    style={{
      display: "flex",
      justifyContent: "space-between",
      fontSize: "0.875rem",
      fontWeight: "bold",
      color: "#1f2937",
    }}
  >
    <span style={{ width: "50%" }}>Product</span>
    <span style={{ width: "16.6667%", textAlign: "center" }}>Qty</span>
  </div>

  {getAppropriateItems().map((item, id) => (
    <div
      key={id}
      style={{
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between",
        borderBottom: "1px solid",
        padding: "0.25rem 0",
        fontSize: "11px",
        color: "#1f2937",
      }}
    >
      <span style={{ width: "50%" }}>
        {item.product.name}
        {item.description && (
          <>
            <br />
            {item.description}
          </>
        )}
      </span>
      <span style={{ width: "16.6667%", textAlign: "center" }}>{getAppropriateQuantity(item)}</span>
    </div>
  ))}
</div>

{/* Note and Footer */}
<p style={{ paddingTop: "0.25rem", fontSize: "0.75rem", color: "#1f2937" }}>
  <b>Note:</b> {invoice.kotNote}
</p>
<p
  style={{
    marginTop: "0.5rem",
    textAlign: "center",
    fontSize: "0.75rem",
    fontWeight: "bold",
    color: "#1f2937",
  }}
>
  Powered by: XYZ
</p>

打印处理函数

const handlePrint = async ref => {
    try {
      
      // 避免重复连接
      if (!qz.websocket.isActive()) {
        await qz.websocket.connect();
      }

      // 获取可用打印机列表
      const printers = await qz.printers.find();
      console.log("Available Printers:", printers);

      // 创建打印机配置(按需调整打印机名称)
      const config = qz.configs.create("POS-80");
      await qz.print(config, [
        {
          type: "pixel",
          format: "html",
          flavor: "plain",
          data: ref.innerHTML,
        },
      ]);

      console.log("打印完成");
    } catch (error) {
      console.error("打印错误:", error);
    } finally {
      // 确保断开连接
      if (qz.websocket.isActive()) {
        await qz.websocket.disconnect();
      }
    }
};

解决方案

1. 清理HTML中的多余空白字符

页面DOM的innerHTML会包含代码中的换行、缩进等空白字符,QZ Tray解析plain类型的HTML时会将这些空白渲染为空格。通过正则表达式清理多余空白:

修改打印函数中的data字段:

data: ref.innerHTML.replace(/[\n\t]+/g, '').replace(/\s{2,}/g, ' ').trim()

2. 调整QZ Tray打印配置

  • 将flavor从plain改为html,使用标准HTML解析引擎:
{
  type: "pixel",
  format: "html",
  flavor: "html", // 修改此处
  data: ref.innerHTML.replace(/[\n\t]+/g, '').replace(/\s{2,}/g, ' ').trim(),
}
  • 针对POS打印机指定页面尺寸和DPI,避免自动缩放:
const config = qz.configs.create("POS-80", {
  pageSize: { width: 80, height: 'auto', unit: 'mm' },
  dpi: 203 // 常见POS打印机DPI参数
});

3. 添加打印专属重置样式

给发票根容器添加重置样式,消除默认margin、padding和行高差异:

<div style={{ margin: 0, padding: 0, lineHeight: '1.2', width: '100%', fontFamily: 'monospace' }}>
  {/* 原发票DOM内容 */}
</div>

使用等宽字体可让排版更规整,避免字符宽度差异导致的空格问题。

4. 优化Flex布局兼容性

QZ Tray渲染引擎对flex布局的支持可能存在差异,可明确指定flex属性或替换为兼容性更好的布局:

<div style={{ 
  display: "flex", 
  justifyContent: "space-between",
  width: '100%',
  flexWrap: 'nowrap'
}}>

5. 使用React渲染生成干净HTML

替代直接使用ref.innerHTML,用ReactDOMServer.renderToString生成无多余空白的HTML:

import ReactDOMServer from 'react-dom/server';

// 打印函数中生成干净HTML
const invoiceHtml = ReactDOMServer.renderToString(
  <div style={{ margin: 0, padding: 0, lineHeight: '1.2' }}>
    {/* 原发票JSX内容 */}
  </div>
);

// 传入QZ Tray
await qz.print(config, [
  {
    type: "pixel",
    format: "html",
    flavor: "html",
    data: invoiceHtml,
  },
]);

内容的提问来源于stack exchange,提问作者Ali Shan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 15:38:09