From 9e94385dfc5f164be784d335aee098e3ce4da374 Mon Sep 17 00:00:00 2001 From: zhangzhen Date: Thu, 23 Jul 2026 20:10:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D200mm=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manifest.json | 4 +- utils/BluetoothPrinterService.new.js | 125 +++++++++++++++++++-------- 2 files changed, 90 insertions(+), 39 deletions(-) diff --git a/manifest.json b/manifest.json index 9d73877..f75769d 100644 --- a/manifest.json +++ b/manifest.json @@ -2,8 +2,8 @@ "name" : "危废数据终端", "appid" : "__UNI__F18DD0A", "description" : "", - "versionName" : "1.0.9", - "versionCode" : 109, + "versionName" : "1.1.0", + "versionCode" : 110, "transformPx" : false, /* 5+App特有相关 */ "app-plus" : { diff --git a/utils/BluetoothPrinterService.new.js b/utils/BluetoothPrinterService.new.js index cf2b1ba..282349a 100644 --- a/utils/BluetoothPrinterService.new.js +++ b/utils/BluetoothPrinterService.new.js @@ -46,6 +46,8 @@ const PAPER_PRESETS = { fontSize: 28, lineHeight: 45, cpcl: { + pageWidth: 800, // 100mm @200dpi = 800 点 + gapSense: true, // 自动检测标签间隙,避免固定 GAP 值不匹配 fields: [ { x: 192, y: 100 }, // 名称 { x: 192, y: 150 }, // 类别 @@ -78,23 +80,26 @@ const PAPER_PRESETS = { fontSize: 36, lineHeight: 60, cpcl: { + pageWidth: 1200, // PAGE-WIDTH(150mm @200dpi = 1200 点),防止右半边被截断 + gapSense: true, // 自动检测标签间隙,避免固定 GAP 值不匹配 + // 字体大小沿用 200x200(mag/bold),位置用 150 原坐标,整体下移 3mm(24 点 @200dpi) fields: [ - { x: 288, y: 150 }, // 名称 - { x: 288, y: 225 }, // 类别 - { x: 288, y: 300 }, // 代码 - { x: 720, y: 300 }, // 形态 - { x: 288, y: 375 }, // 主要成分 - { x: 288, y: 525 }, // 有害成分 - { x: 288, y: 672 }, // 注意 - { x: 324, y: 804 }, // 识别码 - { x: 384, y: 876 }, // 单位 - { x: 432, y: 948 }, // 联系 - { x: 288, y: 1020 }, // 日期 - { x: 720, y: 1020 }, // 重量 - { x: 225, y: 1092 }, // 备注 - { x: 720, y: 525 } // 标识 + { x: 288, y: 174, mag: [2, 2], bold: 0.0, font: 1 }, // 名称(150:150+24) + { x: 288, y: 249, mag: [2, 2], bold: 0.0, font: 1 }, // 类别(150:225+24) + { x: 288, y: 324, mag: [2, 2], bold: 0.5, font: 0 }, // 代码(150:300+24,加粗) + { x: 720, y: 324, mag: [2, 2], bold: 0.0, font: 1 }, // 形态(150:300+24) + { x: 288, y: 399, mag: [2, 2], bold: 0.5, font: 0 }, // 主要成分(150:375+24,加粗) + { x: 288, y: 549, mag: [2, 2], bold: 0.5, font: 0 }, // 有害成分(150:525+24,加粗) + { x: 288, y: 696, mag: [2, 2], bold: 0.0, font: 1 }, // 注意(150:672+24) + { x: 308, y: 836, mag: [2, 2], bold: 0.0, font: 1 }, // 识别码(注意以下:x-16 左2mm, y+8 下1mm) + { x: 368, y: 908, mag: [2, 2], bold: 0.0, font: 1 }, // 单位(注意以下) + { x: 416, y: 980, mag: [2, 2], bold: 0.0, font: 1 }, // 联系(注意以下) + { x: 272, y: 1052, mag: [2, 2], bold: 0.5, font: 0 }, // 日期(注意以下,加粗) + { x: 704, y: 1052, mag: [2, 2], bold: 0.0, font: 1 }, // 重量(注意以下) + { x: 209, y: 1124, mag: [2, 2], bold: 0.0, font: 1 }, // 备注(注意以下) + { x: 720, y: 549, mag: [4, 4], bold: 0.0, font: 1 } // 标识(150:525+24,4 倍放大) ], - qrCode: { x: 936, y: 876, size: 4 } + qrCode: { x: 936, y: 900, size: 4 } // 位置用 150 原坐标(876+24),size 4 不越界 }, tspl: { startX: 30, @@ -512,7 +517,7 @@ class BluetoothPrinterService { console.log(`[打印] 共 ${lines.length} 行, ${dataSize} bytes`); // 行数少时一次发送(更快),行数多时按行发送(防止指令被拆包) - const LINE_THRESHOLD = 22; + const LINE_THRESHOLD = 10; if (lines.length <= LINE_THRESHOLD) { console.log('[打印] 行数较少,一次发送'); await this._writeData(buffer); @@ -533,25 +538,67 @@ class BluetoothPrinterService { const lines = content.split('\n'); console.log(`[打印] 按行发送,共 ${lines.length} 行`); - for (let i = 0; i < lines.length; i++) { - const line = lines[i] + '\n'; - const buffer = this._stringToBuffer(line); + let i = 0; + while (i < lines.length) { + const line = lines[i]; + const trimmed = line.trim(); - try { - await this._writeData(buffer); - // 每行之间延迟 80ms,给打印机处理时间 - if (i < lines.length - 1) { - await this._delay(80); + // CPCL QR 码块:从 B QR / BARCODE QR 到 ENDQR,必须作为整体一次发送 + // 否则行间 80ms 延迟会导致打印机无法将数据与 QR 命令关联,二维码无法生成 + const isQRStart = /^(B\s+QR|BARCODE\s+QR)\b/i.test(trimmed); + + if (isQRStart) { + // 收集完整 QR 块(含结束标记 ENDQR) + const qrBlockLines = []; + while (i < lines.length) { + qrBlockLines.push(lines[i]); + if (lines[i].trim() === 'ENDQR') { + i++; + break; + } + i++; } - } catch (err) { - const isLastLine = i === lines.length - 1; - if (isLastLine) { - // 尾行容错:与 _writeData 保持一致,最后一行写入失败不抛异常 - console.warn(`[打印] 第 ${i + 1} 行(最后一行)发送失败,视为尾包处理:`, err); - return; + + // 整体发送 QR 块,中间不加延迟 + const blockStr = qrBlockLines.join('\n') + '\n'; + const blockBuffer = this._stringToBuffer(blockStr); + console.log(`[打印] 发送 QR 块(${qrBlockLines.length} 行, ${blockBuffer.byteLength}B)`); + + try { + await this._writeData(blockBuffer); + } catch (err) { + const isLastBlock = i >= lines.length; + if (isLastBlock) { + // 尾块容错:与 _writeData 保持一致,最后一个块写入失败不抛异常 + console.warn(`[打印] QR 块(最后一个块)发送失败,视为尾包处理:`, err); + return; + } + console.error(`[打印] QR 块发送失败:`, err); + throw err; } - console.error(`[打印] 第 ${i + 1} 行发送失败:`, err); - throw err; + } else { + // 普通行,逐行发送 + const lineStr = line + '\n'; + const buffer = this._stringToBuffer(lineStr); + + try { + await this._writeData(buffer); + } catch (err) { + const isLastLine = i === lines.length - 1; + if (isLastLine) { + // 尾行容错:与 _writeData 保持一致,最后一行写入失败不抛异常 + console.warn(`[打印] 第 ${i + 1} 行(最后一行)发送失败,视为尾包处理:`, err); + return; + } + console.error(`[打印] 第 ${i + 1} 行发送失败:`, err); + throw err; + } + i++; + } + + // 行间/块间延迟 80ms,给打印机处理时间 + if (i < lines.length) { + await this._delay(80); } } @@ -665,11 +712,15 @@ class BluetoothPrinterService { fieldOrder.forEach((field, index) => { if (data[field]) { const f = conf[index] || {}; - const mag = f.mag || [1, 1]; // 默认 1 倍(兼容 100x100) - const bold = (f.bold != null) ? f.bold : 0.0; - const font = f.font || 1; - commands.push(`SETMAG ${mag[0]} ${mag[1]}`); - commands.push(`SETBOLD ${bold}`); + // 仅当字段显式配置了 mag / bold 时才输出,否则走打印机默认值 + // (100x100 标签字段无这些配置,避免输出多余的 SETMAG/SETBOLD) + if (f.mag) { + commands.push(`SETMAG ${f.mag[0]} ${f.mag[1]}`); + } + if (f.bold != null) { + commands.push(`SETBOLD ${f.bold}`); + } + const font = (f.font != null) ? f.font : 1; commands.push(`T ${font} 0 ${f.x} ${f.y} ${data[field]}`); } });