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
相关产品推荐
相关产品推荐

