Commit ca4ab0f779b01bd40535f2a27f1c1493e720fd84
Merge branch 'main' of http://39.98.150.180/wangming/Food-Labeling-Management-Platform
Showing
10 changed files
with
271 additions
and
66 deletions
美国版/Food Labeling Management App UniApp/src/pages/labels/bluetooth.vue
| @@ -163,7 +163,7 @@ | @@ -163,7 +163,7 @@ | ||
| 163 | <text class="tips-item">2. On Android: enable Location (required for Bluetooth scan)</text> | 163 | <text class="tips-item">2. On Android: enable Location (required for Bluetooth scan)</text> |
| 164 | <text class="tips-item">3. Place the printer within 10 m and tap Scan again</text> | 164 | <text class="tips-item">3. Place the printer within 10 m and tap Scan again</text> |
| 165 | <text class="tips-item">4. Devices with no name show as "Unknown Device"—you can still connect</text> | 165 | <text class="tips-item">4. Devices with no name show as "Unknown Device"—you can still connect</text> |
| 166 | - <text class="tips-item">5. D320FAX (d320fax_295c): use Bluetooth mode, tap Scan—shows paired + nearby devices, no filtering</text> | 166 | + <text class="tips-item">5. GP-D320FX (d320fx_xxxx): use Bluetooth mode, tap Scan—shows paired + nearby devices, no filtering</text> |
| 167 | <text class="tips-item tips-item-last">6. Restart the printer or app if not visible</text> | 167 | <text class="tips-item tips-item-last">6. Restart the printer or app if not visible</text> |
| 168 | </view> | 168 | </view> |
| 169 | </view> | 169 | </view> |
| @@ -260,8 +260,21 @@ async function refreshNativeDebug () { | @@ -260,8 +260,21 @@ async function refreshNativeDebug () { | ||
| 260 | function hasPreferredClassicDeviceInList () { | 260 | function hasPreferredClassicDeviceInList () { |
| 261 | return devices.value.some((item: any) => { | 261 | return devices.value.some((item: any) => { |
| 262 | const name = String(item?.name || '').toLowerCase() | 262 | const name = String(item?.name || '').toLowerCase() |
| 263 | - const type = String(item?.type || '').toLowerCase() | ||
| 264 | - return name.includes('virtual bt printer') || type === 'classic' || type === 'dual' | 263 | + const driverKey = String(item?.driverKey || '').toLowerCase() |
| 264 | + return name.includes('virtual bt printer') || driverKey === 'd320fax' | ||
| 265 | + }) | ||
| 266 | +} | ||
| 267 | + | ||
| 268 | +function upsertDevice (device: any) { | ||
| 269 | + const described = describeDiscoveredPrinter(device) | ||
| 270 | + const existing = devices.value.find(item => item.deviceId === described.deviceId) | ||
| 271 | + if (!existing) { | ||
| 272 | + discoveredIds.add(described.deviceId) | ||
| 273 | + devices.value.push(described) | ||
| 274 | + return | ||
| 275 | + } | ||
| 276 | + Object.assign(existing, described, { | ||
| 277 | + RSSI: described.RSSI != null ? described.RSSI : existing.RSSI, | ||
| 265 | }) | 278 | }) |
| 266 | } | 279 | } |
| 267 | 280 | ||
| @@ -343,16 +356,13 @@ const stopDiscovery = () => { | @@ -343,16 +356,13 @@ const stopDiscovery = () => { | ||
| 343 | const onDeviceFound = (res: any) => { | 356 | const onDeviceFound = (res: any) => { |
| 344 | const foundDevices: any[] = res.devices || [] | 357 | const foundDevices: any[] = res.devices || [] |
| 345 | for (const d of foundDevices) { | 358 | for (const d of foundDevices) { |
| 346 | - if (discoveredIds.has(d.deviceId)) continue | ||
| 347 | const name = (d.localName || d.name || '').trim() | 359 | const name = (d.localName || d.name || '').trim() |
| 348 | - const displayName = name || 'Unknown Device' | ||
| 349 | - discoveredIds.add(d.deviceId) | ||
| 350 | - devices.value.push(describeDiscoveredPrinter({ | 360 | + upsertDevice({ |
| 351 | deviceId: d.deviceId, | 361 | deviceId: d.deviceId, |
| 352 | - name: displayName, | 362 | + name: name || 'Unknown Device', |
| 353 | RSSI: d.RSSI, | 363 | RSSI: d.RSSI, |
| 354 | type: 'ble', | 364 | type: 'ble', |
| 355 | - })) | 365 | + }) |
| 356 | } | 366 | } |
| 357 | devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100)) | 367 | devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100)) |
| 358 | } | 368 | } |
| @@ -363,15 +373,13 @@ function mergeCachedBleDevices () { | @@ -363,15 +373,13 @@ function mergeCachedBleDevices () { | ||
| 363 | success: (res: any) => { | 373 | success: (res: any) => { |
| 364 | const list = res.devices || [] | 374 | const list = res.devices || [] |
| 365 | for (const d of list) { | 375 | for (const d of list) { |
| 366 | - if (discoveredIds.has(d.deviceId)) continue | ||
| 367 | const name = (d.localName || d.name || '').trim() | 376 | const name = (d.localName || d.name || '').trim() |
| 368 | - discoveredIds.add(d.deviceId) | ||
| 369 | - devices.value.push(describeDiscoveredPrinter({ | 377 | + upsertDevice({ |
| 370 | deviceId: d.deviceId, | 378 | deviceId: d.deviceId, |
| 371 | name: name || 'Unknown Device', | 379 | name: name || 'Unknown Device', |
| 372 | RSSI: d.RSSI, | 380 | RSSI: d.RSSI, |
| 373 | type: 'ble', | 381 | type: 'ble', |
| 374 | - })) | 382 | + }) |
| 375 | } | 383 | } |
| 376 | if (list.length > 0) devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100)) | 384 | if (list.length > 0) devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100)) |
| 377 | }, | 385 | }, |
| @@ -387,13 +395,11 @@ function addPairedDevices () { | @@ -387,13 +395,11 @@ function addPairedDevices () { | ||
| 387 | debugInfo.value.pairedCount = (paired || []).length | 395 | debugInfo.value.pairedCount = (paired || []).length |
| 388 | debugInfo.value.foundVirtualPrinter = (paired || []).some((item: any) => String(item?.name || '').toLowerCase().includes('virtual bt printer')) | 396 | debugInfo.value.foundVirtualPrinter = (paired || []).some((item: any) => String(item?.name || '').toLowerCase().includes('virtual bt printer')) |
| 389 | for (const p of paired) { | 397 | for (const p of paired) { |
| 390 | - if (discoveredIds.has(p.deviceId)) continue | ||
| 391 | - discoveredIds.add(p.deviceId) | ||
| 392 | - devices.value.push(describeDiscoveredPrinter({ | 398 | + upsertDevice({ |
| 393 | deviceId: p.deviceId, | 399 | deviceId: p.deviceId, |
| 394 | name: p.name || 'Unknown Device', | 400 | name: p.name || 'Unknown Device', |
| 395 | type: p.type || 'classic', | 401 | type: p.type || 'classic', |
| 396 | - })) | 402 | + }) |
| 397 | } | 403 | } |
| 398 | if (paired.length > 0) { | 404 | if (paired.length > 0) { |
| 399 | debugInfo.value.lastClassicEvent = 'paired devices loaded' | 405 | debugInfo.value.lastClassicEvent = 'paired devices loaded' |
| @@ -416,13 +422,11 @@ function startClassicScan () { | @@ -416,13 +422,11 @@ function startClassicScan () { | ||
| 416 | classic.startClassicDiscovery( | 422 | classic.startClassicDiscovery( |
| 417 | (dev: { name: string; deviceId: string; type: string }) => { | 423 | (dev: { name: string; deviceId: string; type: string }) => { |
| 418 | debugInfo.value.lastClassicEvent = 'device found' | 424 | debugInfo.value.lastClassicEvent = 'device found' |
| 419 | - if (discoveredIds.has(dev.deviceId)) return | ||
| 420 | - discoveredIds.add(dev.deviceId) | ||
| 421 | - devices.value.push(describeDiscoveredPrinter({ | 425 | + upsertDevice({ |
| 422 | deviceId: dev.deviceId, | 426 | deviceId: dev.deviceId, |
| 423 | name: dev.name || 'Unknown Device', | 427 | name: dev.name || 'Unknown Device', |
| 424 | type: (dev.type as BtDevice['type']) || 'classic', | 428 | type: (dev.type as BtDevice['type']) || 'classic', |
| 425 | - })) | 429 | + }) |
| 426 | devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100)) | 430 | devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100)) |
| 427 | }, | 431 | }, |
| 428 | () => { | 432 | () => { |
| @@ -542,7 +546,7 @@ const handleTestPrint = async () => { | @@ -542,7 +546,7 @@ const handleTestPrint = async () => { | ||
| 542 | if (msg === 'BUILTIN_PLUGIN_NOT_FOUND') { | 546 | if (msg === 'BUILTIN_PLUGIN_NOT_FOUND') { |
| 543 | uni.showModal({ | 547 | uni.showModal({ |
| 544 | title: 'Use Bluetooth Mode', | 548 | title: 'Use Bluetooth Mode', |
| 545 | - content: 'For GP-D320FAX, switch to Bluetooth mode and tap Scan to connect (e.g. d320fax_295c or Virtual BT Printer). Built-in mode needs custom app packaging.', | 549 | + content: 'For GP-D320FX, switch to Bluetooth mode and tap Scan to connect (for example d320fx_xxxx). Built-in mode needs custom app packaging.', |
| 546 | showCancel: false, | 550 | showCancel: false, |
| 547 | success: () => { switchType('bluetooth') }, | 551 | success: () => { switchType('bluetooth') }, |
| 548 | }) | 552 | }) |
美国版/Food Labeling Management App UniApp/src/pages/more/printers.vue
| @@ -218,8 +218,8 @@ const normalizeDeviceName = (device: any) => { | @@ -218,8 +218,8 @@ const normalizeDeviceName = (device: any) => { | ||
| 218 | const hasPreferredClassicDevice = () => { | 218 | const hasPreferredClassicDevice = () => { |
| 219 | return pairedDevices.value.some((item: any) => { | 219 | return pairedDevices.value.some((item: any) => { |
| 220 | const name = String(item?.name || '').toLowerCase() | 220 | const name = String(item?.name || '').toLowerCase() |
| 221 | - const type = String(item?.type || '').toLowerCase() | ||
| 222 | - return name.includes('virtual bt printer') || type === 'classic' || type === 'dual' | 221 | + const driverKey = String(item?.driverKey || '').toLowerCase() |
| 222 | + return name.includes('virtual bt printer') || driverKey === 'd320fax' | ||
| 223 | }) | 223 | }) |
| 224 | } | 224 | } |
| 225 | 225 |
美国版/Food Labeling Management App UniApp/src/utils/print/drivers/d320fax.ts
| @@ -3,7 +3,6 @@ import type { PrinterCandidate, PrinterDriver } from '../types/printer' | @@ -3,7 +3,6 @@ import type { PrinterCandidate, PrinterDriver } from '../types/printer' | ||
| 3 | 3 | ||
| 4 | const KEYWORDS = [ | 4 | const KEYWORDS = [ |
| 5 | 'd320fax', | 5 | 'd320fax', |
| 6 | - 'd320fx', | ||
| 7 | 'virtual bt printer', | 6 | 'virtual bt printer', |
| 8 | 'gp-d320fax', | 7 | 'gp-d320fax', |
| 9 | ] | 8 | ] |
| @@ -20,7 +19,7 @@ function score (device: PrinterCandidate): number { | @@ -20,7 +19,7 @@ function score (device: PrinterCandidate): number { | ||
| 20 | export const d320faxDriver: PrinterDriver = { | 19 | export const d320faxDriver: PrinterDriver = { |
| 21 | key: 'd320fax', | 20 | key: 'd320fax', |
| 22 | brand: 'Gprinter', | 21 | brand: 'Gprinter', |
| 23 | - model: 'D320FAX/D320FX', | 22 | + model: 'D320FAX', |
| 24 | displayName: 'Gprinter D320FAX', | 23 | displayName: 'Gprinter D320FAX', |
| 25 | protocol: 'tsc', | 24 | protocol: 'tsc', |
| 26 | preferredConnection: 'classic', | 25 | preferredConnection: 'classic', |
美国版/Food Labeling Management App UniApp/src/utils/print/drivers/gpD320fx.ts
0 → 100644
| 1 | +import { buildTscLabelData, buildTscTestPrintData } from '../protocols/tscProtocol' | ||
| 2 | +import type { PrinterCandidate, PrinterDriver } from '../types/printer' | ||
| 3 | + | ||
| 4 | +const KEYWORDS = [ | ||
| 5 | + 'd320fx', | ||
| 6 | + 'gp-d320fx', | ||
| 7 | +] | ||
| 8 | + | ||
| 9 | +function score (device: PrinterCandidate): number { | ||
| 10 | + const text = `${device.name || ''} ${device.deviceId || ''}`.toLowerCase() | ||
| 11 | + let total = 0 | ||
| 12 | + KEYWORDS.forEach((keyword) => { | ||
| 13 | + if (text.includes(keyword)) total += 80 | ||
| 14 | + }) | ||
| 15 | + if (text.includes('gprinter')) total += 10 | ||
| 16 | + return total | ||
| 17 | +} | ||
| 18 | + | ||
| 19 | +export const gpD320fxDriver: PrinterDriver = { | ||
| 20 | + key: 'gp-d320fx', | ||
| 21 | + brand: 'Gprinter', | ||
| 22 | + model: 'GP-D320FX', | ||
| 23 | + displayName: 'Gprinter GP-D320FX', | ||
| 24 | + protocol: 'tsc', | ||
| 25 | + preferredConnection: 'ble', | ||
| 26 | + preferredBleMtu: 512, | ||
| 27 | + imageMaxWidthDots: 800, | ||
| 28 | + imageDpi: 203, | ||
| 29 | + keywords: KEYWORDS, | ||
| 30 | + matches (device) { | ||
| 31 | + return score(device) | ||
| 32 | + }, | ||
| 33 | + resolveConnectionType (device) { | ||
| 34 | + return device.type === 'classic' ? 'classic' : 'ble' | ||
| 35 | + }, | ||
| 36 | + buildTestPrintData () { | ||
| 37 | + return buildTscTestPrintData() | ||
| 38 | + }, | ||
| 39 | + buildLabelData (payload) { | ||
| 40 | + return buildTscLabelData(payload) | ||
| 41 | + }, | ||
| 42 | +} |
美国版/Food Labeling Management App UniApp/src/utils/print/manager/driverRegistry.ts
| 1 | import { d320faxDriver } from '../drivers/d320fax' | 1 | import { d320faxDriver } from '../drivers/d320fax' |
| 2 | import { genericTscDriver } from '../drivers/genericTsc' | 2 | import { genericTscDriver } from '../drivers/genericTsc' |
| 3 | +import { gpD320fxDriver } from '../drivers/gpD320fx' | ||
| 3 | import { gpR3Driver } from '../drivers/gpR3' | 4 | import { gpR3Driver } from '../drivers/gpR3' |
| 4 | import type { PrinterCandidate, PrinterDriver, ResolvedPrinterCandidate } from '../types/printer' | 5 | import type { PrinterCandidate, PrinterDriver, ResolvedPrinterCandidate } from '../types/printer' |
| 5 | 6 | ||
| 6 | const printerDrivers: PrinterDriver[] = [ | 7 | const printerDrivers: PrinterDriver[] = [ |
| 7 | gpR3Driver, | 8 | gpR3Driver, |
| 9 | + gpD320fxDriver, | ||
| 8 | d320faxDriver, | 10 | d320faxDriver, |
| 9 | genericTscDriver, | 11 | genericTscDriver, |
| 10 | ] | 12 | ] |
美国版/Food Labeling Management App UniApp/src/utils/print/manager/printerManager.ts
| @@ -41,7 +41,38 @@ function getPrinterTypeDisplayName (type: '' | 'bluetooth' | 'builtin'): string | @@ -41,7 +41,38 @@ function getPrinterTypeDisplayName (type: '' | 'bluetooth' | 'builtin'): string | ||
| 41 | function connectClassicBluetooth (device: PrinterCandidate, driver: PrinterDriver): Promise<void> { | 41 | function connectClassicBluetooth (device: PrinterCandidate, driver: PrinterDriver): Promise<void> { |
| 42 | return new Promise((resolve, reject) => { | 42 | return new Promise((resolve, reject) => { |
| 43 | // #ifdef APP-PLUS | 43 | // #ifdef APP-PLUS |
| 44 | - if (isNativeFastPrinterAvailable()) { | 44 | + const shouldUseGenericClassicOnly = driver.key === 'gp-d320fx' |
| 45 | + | ||
| 46 | + const connectClassicSocketFallback = () => { | ||
| 47 | + try { | ||
| 48 | + if (!classicBluetooth || typeof classicBluetooth.connDevice !== 'function') { | ||
| 49 | + reject(new Error('Classic Bluetooth fallback is not available.')) | ||
| 50 | + return | ||
| 51 | + } | ||
| 52 | + classicBluetooth.connDevice(device.deviceId, (ok: boolean) => { | ||
| 53 | + if (ok) { | ||
| 54 | + setBluetoothConnection({ | ||
| 55 | + deviceId: device.deviceId, | ||
| 56 | + deviceName: device.name || 'Bluetooth Printer', | ||
| 57 | + deviceType: 'classic', | ||
| 58 | + transportMode: 'generic', | ||
| 59 | + driverKey: driver.key, | ||
| 60 | + mtu: driver.preferredBleMtu || 20, | ||
| 61 | + }) | ||
| 62 | + resolve() | ||
| 63 | + return | ||
| 64 | + } | ||
| 65 | + const message = typeof classicBluetooth.getLastError === 'function' | ||
| 66 | + ? classicBluetooth.getLastError() | ||
| 67 | + : '' | ||
| 68 | + reject(new Error(message || 'Classic Bluetooth connection failed.')) | ||
| 69 | + }) | ||
| 70 | + } catch (error: any) { | ||
| 71 | + reject(error instanceof Error ? error : new Error(String(error || 'Classic Bluetooth connection failed.'))) | ||
| 72 | + } | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + if (!shouldUseGenericClassicOnly && isNativeFastPrinterAvailable()) { | ||
| 45 | connectNativeFastPrinterPlugin({ | 76 | connectNativeFastPrinterPlugin({ |
| 46 | deviceId: device.deviceId, | 77 | deviceId: device.deviceId, |
| 47 | deviceName: device.name || 'Bluetooth Printer', | 78 | deviceName: device.name || 'Bluetooth Printer', |
| @@ -50,15 +81,24 @@ function connectClassicBluetooth (device: PrinterCandidate, driver: PrinterDrive | @@ -50,15 +81,24 @@ function connectClassicBluetooth (device: PrinterCandidate, driver: PrinterDrive | ||
| 50 | deviceId: device.deviceId, | 81 | deviceId: device.deviceId, |
| 51 | deviceName: device.name || 'Bluetooth Printer', | 82 | deviceName: device.name || 'Bluetooth Printer', |
| 52 | deviceType: 'classic', | 83 | deviceType: 'classic', |
| 84 | + transportMode: 'native-plugin', | ||
| 53 | driverKey: driver.key, | 85 | driverKey: driver.key, |
| 54 | mtu: driver.preferredBleMtu || 20, | 86 | mtu: driver.preferredBleMtu || 20, |
| 55 | }) | 87 | }) |
| 56 | resolve() | 88 | resolve() |
| 57 | }).catch((error: any) => { | 89 | }).catch((error: any) => { |
| 90 | + if (driver.key === 'd320fax') { | ||
| 91 | + connectClassicSocketFallback() | ||
| 92 | + return | ||
| 93 | + } | ||
| 58 | reject(error instanceof Error ? error : new Error(String(error || 'Classic Bluetooth connection failed.'))) | 94 | reject(error instanceof Error ? error : new Error(String(error || 'Classic Bluetooth connection failed.'))) |
| 59 | }) | 95 | }) |
| 60 | return | 96 | return |
| 61 | } | 97 | } |
| 98 | + if (driver.key === 'd320fax' || shouldUseGenericClassicOnly) { | ||
| 99 | + connectClassicSocketFallback() | ||
| 100 | + return | ||
| 101 | + } | ||
| 62 | reject(new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.')) | 102 | reject(new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.')) |
| 63 | // #endif | 103 | // #endif |
| 64 | // #ifndef APP-PLUS | 104 | // #ifndef APP-PLUS |
| @@ -103,19 +143,44 @@ function findBleWriteCharacteristic (deviceId: string): Promise<{ serviceId: str | @@ -103,19 +143,44 @@ function findBleWriteCharacteristic (deviceId: string): Promise<{ serviceId: str | ||
| 103 | }) | 143 | }) |
| 104 | } | 144 | } |
| 105 | 145 | ||
| 146 | +function requestBleMtu (deviceId: string, preferredMtu: number): Promise<number> { | ||
| 147 | + return new Promise((resolve) => { | ||
| 148 | + const targetMtu = Math.max(20, Math.min(512, Math.round(preferredMtu || 20))) | ||
| 149 | + if (targetMtu <= 20 || typeof (uni as any).setBLEMTU !== 'function') { | ||
| 150 | + resolve(20) | ||
| 151 | + return | ||
| 152 | + } | ||
| 153 | + let settled = false | ||
| 154 | + const done = (value: number) => { | ||
| 155 | + if (settled) return | ||
| 156 | + settled = true | ||
| 157 | + clearTimeout(timer) | ||
| 158 | + resolve(Math.max(20, Math.round(value || 20))) | ||
| 159 | + } | ||
| 160 | + const timer = setTimeout(() => done(20), 3000) | ||
| 161 | + ;(uni as any).setBLEMTU({ | ||
| 162 | + deviceId, | ||
| 163 | + mtu: targetMtu, | ||
| 164 | + success: (res: any) => done(Number(res?.mtu) || targetMtu), | ||
| 165 | + fail: () => done(20), | ||
| 166 | + }) | ||
| 167 | + }) | ||
| 168 | +} | ||
| 169 | + | ||
| 106 | function connectBlePrinter (device: PrinterCandidate, driver: PrinterDriver): Promise<void> { | 170 | function connectBlePrinter (device: PrinterCandidate, driver: PrinterDriver): Promise<void> { |
| 107 | const finalizeExistingBleConnection = async () => { | 171 | const finalizeExistingBleConnection = async () => { |
| 108 | const write = await findBleWriteCharacteristic(device.deviceId) | 172 | const write = await findBleWriteCharacteristic(device.deviceId) |
| 109 | if (!write) { | 173 | if (!write) { |
| 110 | throw new Error('No writable characteristic found. This device may not support printing.') | 174 | throw new Error('No writable characteristic found. This device may not support printing.') |
| 111 | } | 175 | } |
| 176 | + const negotiatedMtu = await requestBleMtu(device.deviceId, driver.preferredBleMtu || 20) | ||
| 112 | setBluetoothConnection({ | 177 | setBluetoothConnection({ |
| 113 | deviceId: device.deviceId, | 178 | deviceId: device.deviceId, |
| 114 | deviceName: device.name || 'Bluetooth Printer', | 179 | deviceName: device.name || 'Bluetooth Printer', |
| 115 | serviceId: write.serviceId, | 180 | serviceId: write.serviceId, |
| 116 | characteristicId: write.characteristicId, | 181 | characteristicId: write.characteristicId, |
| 117 | deviceType: 'ble', | 182 | deviceType: 'ble', |
| 118 | - mtu: driver.preferredBleMtu || 20, | 183 | + mtu: negotiatedMtu, |
| 119 | driverKey: driver.key, | 184 | driverKey: driver.key, |
| 120 | }) | 185 | }) |
| 121 | } | 186 | } |
| @@ -145,6 +210,14 @@ function connectBlePrinter (device: PrinterCandidate, driver: PrinterDriver): Pr | @@ -145,6 +210,14 @@ function connectBlePrinter (device: PrinterCandidate, driver: PrinterDriver): Pr | ||
| 145 | 210 | ||
| 146 | export async function connectBluetoothPrinter (device: PrinterCandidate): Promise<PrinterDriver> { | 211 | export async function connectBluetoothPrinter (device: PrinterCandidate): Promise<PrinterDriver> { |
| 147 | const driver = resolvePrinterDriver(device) | 212 | const driver = resolvePrinterDriver(device) |
| 213 | + if (driver.key === 'gp-d320fx') { | ||
| 214 | + try { | ||
| 215 | + await connectBlePrinter(device, driver) | ||
| 216 | + } catch (_) { | ||
| 217 | + await connectClassicBluetooth(device, driver) | ||
| 218 | + } | ||
| 219 | + return driver | ||
| 220 | + } | ||
| 148 | const resolvedType = driver.resolveConnectionType(device) | 221 | const resolvedType = driver.resolveConnectionType(device) |
| 149 | if (resolvedType === 'classic') { | 222 | if (resolvedType === 'classic') { |
| 150 | await connectClassicBluetooth(device, driver) | 223 | await connectClassicBluetooth(device, driver) |
| @@ -218,19 +291,25 @@ function canUseNativeFastTemplatePrint (driver: PrinterDriver): boolean { | @@ -218,19 +291,25 @@ function canUseNativeFastTemplatePrint (driver: PrinterDriver): boolean { | ||
| 218 | const connection = getBluetoothConnection() | 291 | const connection = getBluetoothConnection() |
| 219 | return driver.protocol === 'tsc' | 292 | return driver.protocol === 'tsc' |
| 220 | && connection?.deviceType === 'classic' | 293 | && connection?.deviceType === 'classic' |
| 294 | + && connection?.transportMode === 'native-plugin' | ||
| 221 | && isNativeFastPrinterAvailable() | 295 | && isNativeFastPrinterAvailable() |
| 222 | } | 296 | } |
| 223 | 297 | ||
| 224 | function getNativeClassicConnection () { | 298 | function getNativeClassicConnection () { |
| 225 | const connection = getBluetoothConnection() | 299 | const connection = getBluetoothConnection() |
| 226 | - if (!connection || connection.deviceType !== 'classic') return null | 300 | + if (!connection || connection.deviceType !== 'classic' || connection.transportMode !== 'native-plugin') return null |
| 227 | return connection | 301 | return connection |
| 228 | } | 302 | } |
| 229 | 303 | ||
| 230 | export async function testPrintCurrentPrinter (onProgress?: (percent: number) => void): Promise<PrinterDriver> { | 304 | export async function testPrintCurrentPrinter (onProgress?: (percent: number) => void): Promise<PrinterDriver> { |
| 231 | const driver = getCurrentPrinterDriver() | 305 | const driver = getCurrentPrinterDriver() |
| 232 | const connection = getBluetoothConnection() | 306 | const connection = getBluetoothConnection() |
| 233 | - if (driver.protocol === 'tsc' && connection?.deviceType === 'classic' && !isNativeFastPrinterAvailable()) { | 307 | + if ( |
| 308 | + driver.protocol === 'tsc' | ||
| 309 | + && connection?.deviceType === 'classic' | ||
| 310 | + && connection?.transportMode === 'native-plugin' | ||
| 311 | + && !isNativeFastPrinterAvailable() | ||
| 312 | + ) { | ||
| 234 | throw new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.') | 313 | throw new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.') |
| 235 | } | 314 | } |
| 236 | if (canUseNativeFastTemplatePrint(driver)) { | 315 | if (canUseNativeFastTemplatePrint(driver)) { |
| @@ -319,7 +398,12 @@ export async function printSystemTemplateForCurrentPrinter ( | @@ -319,7 +398,12 @@ export async function printSystemTemplateForCurrentPrinter ( | ||
| 319 | ): Promise<PrinterDriver> { | 398 | ): Promise<PrinterDriver> { |
| 320 | const driver = getCurrentPrinterDriver() | 399 | const driver = getCurrentPrinterDriver() |
| 321 | const connection = getBluetoothConnection() | 400 | const connection = getBluetoothConnection() |
| 322 | - if (driver.protocol === 'tsc' && connection?.deviceType === 'classic' && !isNativeFastPrinterAvailable()) { | 401 | + if ( |
| 402 | + driver.protocol === 'tsc' | ||
| 403 | + && connection?.deviceType === 'classic' | ||
| 404 | + && connection?.transportMode === 'native-plugin' | ||
| 405 | + && !isNativeFastPrinterAvailable() | ||
| 406 | + ) { | ||
| 323 | throw new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.') | 407 | throw new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.') |
| 324 | } | 408 | } |
| 325 | if (canUseNativeFastTemplatePrint(driver)) { | 409 | if (canUseNativeFastTemplatePrint(driver)) { |
| @@ -341,6 +425,7 @@ export async function printSystemTemplateForCurrentPrinter ( | @@ -341,6 +425,7 @@ export async function printSystemTemplateForCurrentPrinter ( | ||
| 341 | const structuredTemplate = adaptSystemLabelTemplate(template, data, { | 425 | const structuredTemplate = adaptSystemLabelTemplate(template, data, { |
| 342 | dpi: driver.imageDpi || 203, | 426 | dpi: driver.imageDpi || 203, |
| 343 | printQty: options.printQty || 1, | 427 | printQty: options.printQty || 1, |
| 428 | + disableBitmapText: driver.key === 'gp-d320fx', | ||
| 344 | }) | 429 | }) |
| 345 | const bytes = driver.protocol === 'esc' | 430 | const bytes = driver.protocol === 'esc' |
| 346 | ? buildEscPosTemplateData(structuredTemplate) | 431 | ? buildEscPosTemplateData(structuredTemplate) |
| @@ -360,7 +445,7 @@ export function disconnectCurrentPrinter (): Promise<void> { | @@ -360,7 +445,7 @@ export function disconnectCurrentPrinter (): Promise<void> { | ||
| 360 | 445 | ||
| 361 | if (type === 'bluetooth' && connection?.deviceType === 'classic') { | 446 | if (type === 'bluetooth' && connection?.deviceType === 'classic') { |
| 362 | // #ifdef APP-PLUS | 447 | // #ifdef APP-PLUS |
| 363 | - if (isNativeFastPrinterAvailable()) { | 448 | + if (connection.transportMode === 'native-plugin' && isNativeFastPrinterAvailable()) { |
| 364 | disconnectNativeFastPrinterPlugin().catch((e: any) => { | 449 | disconnectNativeFastPrinterPlugin().catch((e: any) => { |
| 365 | console.error('Disconnect native fast printer failed', e) | 450 | console.error('Disconnect native fast printer failed', e) |
| 366 | }).finally(() => { | 451 | }).finally(() => { |
美国版/Food Labeling Management App UniApp/src/utils/print/nativeBitmapPatch.ts
| @@ -10,6 +10,14 @@ const DESIGN_DPI = 96 | @@ -10,6 +10,14 @@ const DESIGN_DPI = 96 | ||
| 10 | const DEFAULT_THRESHOLD = 180 | 10 | const DEFAULT_THRESHOLD = 180 |
| 11 | const TEXT_PADDING_DOTS = 6 | 11 | const TEXT_PADDING_DOTS = 6 |
| 12 | 12 | ||
| 13 | +function normalizePrinterLikeText (str: string): string { | ||
| 14 | + return String(str || '') | ||
| 15 | + .normalize('NFKC') | ||
| 16 | + .replace(/[\u2018\u2019]/g, '\'') | ||
| 17 | + .replace(/[\u201C\u201D]/g, '"') | ||
| 18 | + .replace(/[\u2013\u2014]/g, '-') | ||
| 19 | +} | ||
| 20 | + | ||
| 13 | type BitmapPatchItem = { | 21 | type BitmapPatchItem = { |
| 14 | type: 'bitmap' | 22 | type: 'bitmap' |
| 15 | x: number | 23 | x: number |
| @@ -144,10 +152,11 @@ function splitTextLines (text: string, paint: any, maxWidth: number): string[] { | @@ -144,10 +152,11 @@ function splitTextLines (text: string, paint: any, maxWidth: number): string[] { | ||
| 144 | 152 | ||
| 145 | export function shouldRasterizeTextElement (text: string, type: string): boolean { | 153 | export function shouldRasterizeTextElement (text: string, type: string): boolean { |
| 146 | const normalizedType = String(type || '').toUpperCase() | 154 | const normalizedType = String(type || '').toUpperCase() |
| 147 | - if (!text) return false | ||
| 148 | - if (normalizedType === 'TEXT_PRICE') return true | ||
| 149 | - if (/[€£¥¥$éÉáàâäãåæçèêëìíîïñòóôöõøùúûüýÿœšž]/.test(text)) return true | ||
| 150 | - return /[^\x20-\x7E]/.test(text) | 155 | + const normalizedText = normalizePrinterLikeText(text) |
| 156 | + if (!normalizedText) return false | ||
| 157 | + if (normalizedType === 'TEXT_PRICE' && /[€£¥¥]/.test(normalizedText)) return true | ||
| 158 | + if (/[€£¥¥éÉáàâäãåæçèêëìíîïñòóôöõøùúûüýÿœšž]/.test(normalizedText)) return true | ||
| 159 | + return /[^\x20-\x7E]/.test(normalizedText) | ||
| 151 | } | 160 | } |
| 152 | 161 | ||
| 153 | export function createTextBitmapPatch (params: { | 162 | export function createTextBitmapPatch (params: { |
美国版/Food Labeling Management App UniApp/src/utils/print/printerConnection.ts
| @@ -11,6 +11,7 @@ const STORAGE_BT_DEVICE_NAME = 'btDeviceName' | @@ -11,6 +11,7 @@ const STORAGE_BT_DEVICE_NAME = 'btDeviceName' | ||
| 11 | const STORAGE_BT_SERVICE_ID = 'btServiceId' | 11 | const STORAGE_BT_SERVICE_ID = 'btServiceId' |
| 12 | const STORAGE_BT_CHARACTERISTIC_ID = 'btCharacteristicId' | 12 | const STORAGE_BT_CHARACTERISTIC_ID = 'btCharacteristicId' |
| 13 | const STORAGE_BT_DEVICE_TYPE = 'btDeviceType' // 'ble' | 'classic' | 13 | const STORAGE_BT_DEVICE_TYPE = 'btDeviceType' // 'ble' | 'classic' |
| 14 | +const STORAGE_BT_TRANSPORT_MODE = 'btTransportMode' // 'native-plugin' | 'generic' | ||
| 14 | const STORAGE_BLE_MTU = 'bleMTU' | 15 | const STORAGE_BLE_MTU = 'bleMTU' |
| 15 | const STORAGE_BUILTIN_PORT = 'builtinPort' | 16 | const STORAGE_BUILTIN_PORT = 'builtinPort' |
| 16 | const STORAGE_PRINTER_DRIVER_KEY = 'printerDriverKey' | 17 | const STORAGE_PRINTER_DRIVER_KEY = 'printerDriverKey' |
| @@ -29,6 +30,7 @@ export const PrinterStorageKeys = { | @@ -29,6 +30,7 @@ export const PrinterStorageKeys = { | ||
| 29 | btServiceId: STORAGE_BT_SERVICE_ID, | 30 | btServiceId: STORAGE_BT_SERVICE_ID, |
| 30 | btCharacteristicId: STORAGE_BT_CHARACTERISTIC_ID, | 31 | btCharacteristicId: STORAGE_BT_CHARACTERISTIC_ID, |
| 31 | btDeviceType: STORAGE_BT_DEVICE_TYPE, | 32 | btDeviceType: STORAGE_BT_DEVICE_TYPE, |
| 33 | + btTransportMode: STORAGE_BT_TRANSPORT_MODE, | ||
| 32 | bleMTU: STORAGE_BLE_MTU, | 34 | bleMTU: STORAGE_BLE_MTU, |
| 33 | driverKey: STORAGE_PRINTER_DRIVER_KEY, | 35 | driverKey: STORAGE_PRINTER_DRIVER_KEY, |
| 34 | } as const | 36 | } as const |
| @@ -43,6 +45,7 @@ export function setBluetoothConnection (info: { | @@ -43,6 +45,7 @@ export function setBluetoothConnection (info: { | ||
| 43 | serviceId?: string | 45 | serviceId?: string |
| 44 | characteristicId?: string | 46 | characteristicId?: string |
| 45 | deviceType?: BtDeviceType | 47 | deviceType?: BtDeviceType |
| 48 | + transportMode?: 'native-plugin' | 'generic' | ||
| 46 | mtu?: number | 49 | mtu?: number |
| 47 | driverKey?: string | 50 | driverKey?: string |
| 48 | }) { | 51 | }) { |
| @@ -52,6 +55,10 @@ export function setBluetoothConnection (info: { | @@ -52,6 +55,10 @@ export function setBluetoothConnection (info: { | ||
| 52 | uni.setStorageSync(STORAGE_BT_SERVICE_ID, info.serviceId || '') | 55 | uni.setStorageSync(STORAGE_BT_SERVICE_ID, info.serviceId || '') |
| 53 | uni.setStorageSync(STORAGE_BT_CHARACTERISTIC_ID, info.characteristicId || '') | 56 | uni.setStorageSync(STORAGE_BT_CHARACTERISTIC_ID, info.characteristicId || '') |
| 54 | uni.setStorageSync(STORAGE_BT_DEVICE_TYPE, info.deviceType || 'ble') | 57 | uni.setStorageSync(STORAGE_BT_DEVICE_TYPE, info.deviceType || 'ble') |
| 58 | + uni.setStorageSync( | ||
| 59 | + STORAGE_BT_TRANSPORT_MODE, | ||
| 60 | + info.transportMode || (info.deviceType === 'classic' ? 'native-plugin' : 'generic') | ||
| 61 | + ) | ||
| 55 | uni.setStorageSync(STORAGE_BLE_MTU, info.mtu != null ? info.mtu : BLE_MTU_DEFAULT) | 62 | uni.setStorageSync(STORAGE_BLE_MTU, info.mtu != null ? info.mtu : BLE_MTU_DEFAULT) |
| 56 | uni.setStorageSync(STORAGE_PRINTER_DRIVER_KEY, info.driverKey || '') | 63 | uni.setStorageSync(STORAGE_PRINTER_DRIVER_KEY, info.driverKey || '') |
| 57 | } | 64 | } |
| @@ -68,6 +75,7 @@ export function clearPrinter () { | @@ -68,6 +75,7 @@ export function clearPrinter () { | ||
| 68 | uni.removeStorageSync(STORAGE_BT_SERVICE_ID) | 75 | uni.removeStorageSync(STORAGE_BT_SERVICE_ID) |
| 69 | uni.removeStorageSync(STORAGE_BT_CHARACTERISTIC_ID) | 76 | uni.removeStorageSync(STORAGE_BT_CHARACTERISTIC_ID) |
| 70 | uni.removeStorageSync(STORAGE_BT_DEVICE_TYPE) | 77 | uni.removeStorageSync(STORAGE_BT_DEVICE_TYPE) |
| 78 | + uni.removeStorageSync(STORAGE_BT_TRANSPORT_MODE) | ||
| 71 | uni.removeStorageSync(STORAGE_BLE_MTU) | 79 | uni.removeStorageSync(STORAGE_BLE_MTU) |
| 72 | uni.removeStorageSync(STORAGE_BUILTIN_PORT) | 80 | uni.removeStorageSync(STORAGE_BUILTIN_PORT) |
| 73 | uni.removeStorageSync(STORAGE_PRINTER_DRIVER_KEY) | 81 | uni.removeStorageSync(STORAGE_PRINTER_DRIVER_KEY) |
| @@ -93,10 +101,12 @@ export function getBluetoothConnection (): { | @@ -93,10 +101,12 @@ export function getBluetoothConnection (): { | ||
| 93 | serviceId: string | 101 | serviceId: string |
| 94 | characteristicId: string | 102 | characteristicId: string |
| 95 | deviceType: BtDeviceType | 103 | deviceType: BtDeviceType |
| 104 | + transportMode: 'native-plugin' | 'generic' | ||
| 96 | mtu: number | 105 | mtu: number |
| 97 | } | null { | 106 | } | null { |
| 98 | const deviceId = uni.getStorageSync(STORAGE_BT_DEVICE_ID) | 107 | const deviceId = uni.getStorageSync(STORAGE_BT_DEVICE_ID) |
| 99 | const deviceType = (uni.getStorageSync(STORAGE_BT_DEVICE_TYPE) as BtDeviceType) || 'ble' | 108 | const deviceType = (uni.getStorageSync(STORAGE_BT_DEVICE_TYPE) as BtDeviceType) || 'ble' |
| 109 | + const transportMode = (uni.getStorageSync(STORAGE_BT_TRANSPORT_MODE) as 'native-plugin' | 'generic') || 'generic' | ||
| 100 | if (!deviceId) return null | 110 | if (!deviceId) return null |
| 101 | if (deviceType === 'classic') { | 111 | if (deviceType === 'classic') { |
| 102 | return { | 112 | return { |
| @@ -105,6 +115,7 @@ export function getBluetoothConnection (): { | @@ -105,6 +115,7 @@ export function getBluetoothConnection (): { | ||
| 105 | serviceId: '', | 115 | serviceId: '', |
| 106 | characteristicId: '', | 116 | characteristicId: '', |
| 107 | deviceType: 'classic', | 117 | deviceType: 'classic', |
| 118 | + transportMode, | ||
| 108 | mtu: BLE_MTU_DEFAULT, | 119 | mtu: BLE_MTU_DEFAULT, |
| 109 | } | 120 | } |
| 110 | } | 121 | } |
| @@ -117,6 +128,7 @@ export function getBluetoothConnection (): { | @@ -117,6 +128,7 @@ export function getBluetoothConnection (): { | ||
| 117 | serviceId, | 128 | serviceId, |
| 118 | characteristicId, | 129 | characteristicId, |
| 119 | deviceType: 'ble', | 130 | deviceType: 'ble', |
| 131 | + transportMode, | ||
| 120 | mtu: Number(uni.getStorageSync(STORAGE_BLE_MTU)) || BLE_MTU_DEFAULT, | 132 | mtu: Number(uni.getStorageSync(STORAGE_BLE_MTU)) || BLE_MTU_DEFAULT, |
| 121 | } | 133 | } |
| 122 | } | 134 | } |
| @@ -209,15 +221,33 @@ function sendViaBle ( | @@ -209,15 +221,33 @@ function sendViaBle ( | ||
| 209 | return Promise.reject(new Error('Bluetooth printer not connected.')) | 221 | return Promise.reject(new Error('Bluetooth printer not connected.')) |
| 210 | } | 222 | } |
| 211 | const { deviceId, serviceId, characteristicId, mtu } = conn | 223 | const { deviceId, serviceId, characteristicId, mtu } = conn |
| 224 | + const payloadSize = Math.max(20, Math.round((mtu || BLE_MTU_DEFAULT) > 23 ? (mtu || BLE_MTU_DEFAULT) - 3 : (mtu || BLE_MTU_DEFAULT))) | ||
| 212 | const chunks: number[][] = [] | 225 | const chunks: number[][] = [] |
| 213 | - for (let i = 0; i < data.length; i += mtu) { | ||
| 214 | - chunks.push(data.slice(i, i + mtu)) | 226 | + for (let i = 0; i < data.length; i += payloadSize) { |
| 227 | + chunks.push(data.slice(i, i + payloadSize)) | ||
| 215 | } | 228 | } |
| 216 | const total = chunks.length | 229 | const total = chunks.length |
| 217 | let sent = 0 | 230 | let sent = 0 |
| 231 | + let completed = false | ||
| 232 | + let timeoutId: ReturnType<typeof setTimeout> | null = setTimeout(() => {}, 0) | ||
| 233 | + const writeDelayMs = payloadSize >= 180 ? 0 : (payloadSize > 20 ? 1 : 8) | ||
| 234 | + | ||
| 235 | + const resetTimeout = (reject: (reason?: any) => void) => { | ||
| 236 | + if (timeoutId) clearTimeout(timeoutId) | ||
| 237 | + timeoutId = setTimeout(() => { | ||
| 238 | + if (completed) return | ||
| 239 | + completed = true | ||
| 240 | + reject(new Error('BLE write timeout')) | ||
| 241 | + }, 15000) | ||
| 242 | + } | ||
| 218 | 243 | ||
| 219 | function sendNext (): Promise<void> { | 244 | function sendNext (): Promise<void> { |
| 245 | + if (completed) { | ||
| 246 | + return Promise.reject(new Error('BLE write timeout')) | ||
| 247 | + } | ||
| 220 | if (sent >= total) { | 248 | if (sent >= total) { |
| 249 | + completed = true | ||
| 250 | + if (timeoutId) clearTimeout(timeoutId) | ||
| 221 | if (onProgress) onProgress(100) | 251 | if (onProgress) onProgress(100) |
| 222 | return Promise.resolve() | 252 | return Promise.resolve() |
| 223 | } | 253 | } |
| @@ -228,17 +258,28 @@ function sendViaBle ( | @@ -228,17 +258,28 @@ function sendViaBle ( | ||
| 228 | view.setUint8(j, chunk[j] & 0xff) | 258 | view.setUint8(j, chunk[j] & 0xff) |
| 229 | } | 259 | } |
| 230 | return new Promise((resolve, reject) => { | 260 | return new Promise((resolve, reject) => { |
| 261 | + resetTimeout(reject) | ||
| 231 | uni.writeBLECharacteristicValue({ | 262 | uni.writeBLECharacteristicValue({ |
| 232 | deviceId, | 263 | deviceId, |
| 233 | serviceId, | 264 | serviceId, |
| 234 | characteristicId, | 265 | characteristicId, |
| 235 | value: buffer, | 266 | value: buffer, |
| 236 | success: () => { | 267 | success: () => { |
| 268 | + if (completed) return | ||
| 237 | sent++ | 269 | sent++ |
| 238 | if (onProgress) onProgress(Math.round((sent / total) * 100)) | 270 | if (onProgress) onProgress(Math.round((sent / total) * 100)) |
| 239 | - setTimeout(() => sendNext().then(resolve).catch(reject), 10) | 271 | + if (writeDelayMs <= 0) { |
| 272 | + sendNext().then(resolve).catch(reject) | ||
| 273 | + return | ||
| 274 | + } | ||
| 275 | + setTimeout(() => sendNext().then(resolve).catch(reject), writeDelayMs) | ||
| 276 | + }, | ||
| 277 | + fail: (err: any) => { | ||
| 278 | + if (completed) return | ||
| 279 | + completed = true | ||
| 280 | + if (timeoutId) clearTimeout(timeoutId) | ||
| 281 | + reject(new Error(err.errMsg || 'BLE write failed')) | ||
| 240 | }, | 282 | }, |
| 241 | - fail: (err: any) => reject(new Error(err.errMsg || 'BLE write failed')), | ||
| 242 | }) | 283 | }) |
| 243 | }) | 284 | }) |
| 244 | } | 285 | } |
| @@ -256,9 +297,22 @@ function sendViaClassic ( | @@ -256,9 +297,22 @@ function sendViaClassic ( | ||
| 256 | return Promise.reject(new Error('Classic Bluetooth printer not connected.')) | 297 | return Promise.reject(new Error('Classic Bluetooth printer not connected.')) |
| 257 | } | 298 | } |
| 258 | return new Promise((resolve, reject) => { | 299 | return new Promise((resolve, reject) => { |
| 300 | + let settled = false | ||
| 301 | + const finish = (fn: () => void) => { | ||
| 302 | + if (settled) return | ||
| 303 | + settled = true | ||
| 304 | + clearTimeout(timeoutId) | ||
| 305 | + fn() | ||
| 306 | + } | ||
| 307 | + const timeoutId = setTimeout(() => { | ||
| 308 | + finish(() => { | ||
| 309 | + reject(buildClassicBluetoothError('Classic Bluetooth send timeout', conn.deviceId)) | ||
| 310 | + }) | ||
| 311 | + }, 15000) | ||
| 312 | + | ||
| 259 | try { | 313 | try { |
| 260 | if (!classicBluetooth) { | 314 | if (!classicBluetooth) { |
| 261 | - reject(new Error('Classic Bluetooth not available')) | 315 | + finish(() => reject(new Error('Classic Bluetooth not available'))) |
| 262 | return | 316 | return |
| 263 | } | 317 | } |
| 264 | const debugState = typeof classicBluetooth.getDebugState === 'function' | 318 | const debugState = typeof classicBluetooth.getDebugState === 'function' |
| @@ -272,7 +326,7 @@ function sendViaClassic ( | @@ -272,7 +326,7 @@ function sendViaClassic ( | ||
| 272 | const errorMessage = typeof classicBluetooth.getLastError === 'function' | 326 | const errorMessage = typeof classicBluetooth.getLastError === 'function' |
| 273 | ? classicBluetooth.getLastError() | 327 | ? classicBluetooth.getLastError() |
| 274 | : '' | 328 | : '' |
| 275 | - reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth connection is not ready', conn.deviceId)) | 329 | + finish(() => reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth connection is not ready', conn.deviceId))) |
| 276 | return | 330 | return |
| 277 | } | 331 | } |
| 278 | 332 | ||
| @@ -283,31 +337,35 @@ function sendViaClassic ( | @@ -283,31 +337,35 @@ function sendViaClassic ( | ||
| 283 | 337 | ||
| 284 | if (typeof classicBluetooth.sendByteDataAsync === 'function') { | 338 | if (typeof classicBluetooth.sendByteDataAsync === 'function') { |
| 285 | classicBluetooth.sendByteDataAsync(sendData, (ok: boolean, errorMessage?: string) => { | 339 | classicBluetooth.sendByteDataAsync(sendData, (ok: boolean, errorMessage?: string) => { |
| 286 | - if (onProgress) onProgress(100) | ||
| 287 | - if (ok) { | ||
| 288 | - resolve() | ||
| 289 | - return | ||
| 290 | - } | ||
| 291 | - reject(buildClassicBluetoothError( | ||
| 292 | - errorMessage || classicBluetooth.getLastError?.() || 'Classic Bluetooth send failed', | ||
| 293 | - conn.deviceId | ||
| 294 | - )) | 340 | + finish(() => { |
| 341 | + if (onProgress) onProgress(100) | ||
| 342 | + if (ok) { | ||
| 343 | + resolve() | ||
| 344 | + return | ||
| 345 | + } | ||
| 346 | + reject(buildClassicBluetoothError( | ||
| 347 | + errorMessage || classicBluetooth.getLastError?.() || 'Classic Bluetooth send failed', | ||
| 348 | + conn.deviceId | ||
| 349 | + )) | ||
| 350 | + }) | ||
| 295 | }) | 351 | }) |
| 296 | return | 352 | return |
| 297 | } | 353 | } |
| 298 | 354 | ||
| 299 | const ok = classicBluetooth.sendByteData(sendData) | 355 | const ok = classicBluetooth.sendByteData(sendData) |
| 300 | - if (onProgress) onProgress(100) | ||
| 301 | - if (ok) { | ||
| 302 | - resolve() | ||
| 303 | - return | ||
| 304 | - } | ||
| 305 | - const errorMessage = typeof classicBluetooth.getLastError === 'function' | ||
| 306 | - ? classicBluetooth.getLastError() | ||
| 307 | - : '' | ||
| 308 | - reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth send failed', conn.deviceId)) | 356 | + finish(() => { |
| 357 | + if (onProgress) onProgress(100) | ||
| 358 | + if (ok) { | ||
| 359 | + resolve() | ||
| 360 | + return | ||
| 361 | + } | ||
| 362 | + const errorMessage = typeof classicBluetooth.getLastError === 'function' | ||
| 363 | + ? classicBluetooth.getLastError() | ||
| 364 | + : '' | ||
| 365 | + reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth send failed', conn.deviceId)) | ||
| 366 | + }) | ||
| 309 | } catch (e: any) { | 367 | } catch (e: any) { |
| 310 | - reject(buildClassicBluetoothError(e?.message || String(e || 'Classic Bluetooth send exception'), conn.deviceId)) | 368 | + finish(() => reject(buildClassicBluetoothError(e?.message || String(e || 'Classic Bluetooth send exception'), conn.deviceId))) |
| 311 | } | 369 | } |
| 312 | }) | 370 | }) |
| 313 | // #endif | 371 | // #endif |
美国版/Food Labeling Management App UniApp/src/utils/print/systemTemplateAdapter.ts
| @@ -297,7 +297,10 @@ function buildTscTemplate ( | @@ -297,7 +297,10 @@ function buildTscTemplate ( | ||
| 297 | template: SystemLabelTemplate, | 297 | template: SystemLabelTemplate, |
| 298 | data: LabelTemplateData, | 298 | data: LabelTemplateData, |
| 299 | dpi: number, | 299 | dpi: number, |
| 300 | - printQty: number | 300 | + printQty: number, |
| 301 | + options: { | ||
| 302 | + disableBitmapText?: boolean | ||
| 303 | + } = {} | ||
| 301 | ): StructuredTscTemplate { | 304 | ): StructuredTscTemplate { |
| 302 | const widthMm = roundNumber(toMillimeter(template.width, template.unit || 'inch')) | 305 | const widthMm = roundNumber(toMillimeter(template.width, template.unit || 'inch')) |
| 303 | const heightMm = roundNumber(toMillimeter(template.height, template.unit || 'inch')) | 306 | const heightMm = roundNumber(toMillimeter(template.height, template.unit || 'inch')) |
| @@ -324,7 +327,7 @@ function buildTscTemplate ( | @@ -324,7 +327,7 @@ function buildTscTemplate ( | ||
| 324 | const scale = resolveTextScale(getConfigNumber(config, ['fontSize'], 14), dpi) | 327 | const scale = resolveTextScale(getConfigNumber(config, ['fontSize'], 14), dpi) |
| 325 | const align = resolveElementAlign(element, pageWidth) | 328 | const align = resolveElementAlign(element, pageWidth) |
| 326 | 329 | ||
| 327 | - if (shouldRasterizeTextElement(text, type)) { | 330 | + if (!options.disableBitmapText && shouldRasterizeTextElement(text, type)) { |
| 328 | const bitmapPatch = createTextBitmapPatch({ | 331 | const bitmapPatch = createTextBitmapPatch({ |
| 329 | element, | 332 | element, |
| 330 | text, | 333 | text, |
| @@ -510,13 +513,16 @@ export function adaptSystemLabelTemplate ( | @@ -510,13 +513,16 @@ export function adaptSystemLabelTemplate ( | ||
| 510 | options: { | 513 | options: { |
| 511 | dpi?: number | 514 | dpi?: number |
| 512 | printQty?: number | 515 | printQty?: number |
| 516 | + disableBitmapText?: boolean | ||
| 513 | } = {} | 517 | } = {} |
| 514 | ): StructuredLabelTemplate { | 518 | ): StructuredLabelTemplate { |
| 515 | const dpi = options.dpi || 203 | 519 | const dpi = options.dpi || 203 |
| 516 | const printQty = Math.max(1, Math.round(options.printQty || 1)) | 520 | const printQty = Math.max(1, Math.round(options.printQty || 1)) |
| 517 | return { | 521 | return { |
| 518 | key: template.id || template.name || 'system-label-template', | 522 | key: template.id || template.name || 'system-label-template', |
| 519 | - tsc: buildTscTemplate(template, data, dpi, printQty), | 523 | + tsc: buildTscTemplate(template, data, dpi, printQty, { |
| 524 | + disableBitmapText: options.disableBitmapText, | ||
| 525 | + }), | ||
| 520 | esc: buildEscTemplate(template, data, printQty), | 526 | esc: buildEscTemplate(template, data, printQty), |
| 521 | } | 527 | } |
| 522 | } | 528 | } |
美国版/Food Labeling Management App UniApp/src/utils/print/tscLabelBuilder.ts
| @@ -220,7 +220,7 @@ export function buildTscTemplateLabel (template: StructuredTscTemplate): number[ | @@ -220,7 +220,7 @@ export function buildTscTemplateLabel (template: StructuredTscTemplate): number[ | ||
| 220 | if (item.type === 'bitmap') { | 220 | if (item.type === 'bitmap') { |
| 221 | const bytesPerRow = item.image.width / 8 | 221 | const bytesPerRow = item.image.width / 8 |
| 222 | const bitmapBytes = pixelsToTscBitmapBytes(item.image) | 222 | const bitmapBytes = pixelsToTscBitmapBytes(item.image) |
| 223 | - add(`BITMAP ${item.x},${item.y},${bytesPerRow},${item.image.height},0,`) | 223 | + addCommandBytes(out, `BITMAP ${item.x},${item.y},${bytesPerRow},${item.image.height},0,`) |
| 224 | for (let i = 0; i < bitmapBytes.length; i++) out.push(bitmapBytes[i]) | 224 | for (let i = 0; i < bitmapBytes.length; i++) out.push(bitmapBytes[i]) |
| 225 | out.push(0x0d, 0x0a) | 225 | out.push(0x0d, 0x0a) |
| 226 | return | 226 | return |
| @@ -275,7 +275,7 @@ export function buildTscImageLabel ( | @@ -275,7 +275,7 @@ export function buildTscImageLabel ( | ||
| 275 | add('DENSITY 14') | 275 | add('DENSITY 14') |
| 276 | add('SPEED 5') | 276 | add('SPEED 5') |
| 277 | add('CLS') | 277 | add('CLS') |
| 278 | - add(`BITMAP ${x},${y},${bytesPerRow},${image.height},0,`) | 278 | + addCommandBytes(out, `BITMAP ${x},${y},${bytesPerRow},${image.height},0,`) |
| 279 | for (let i = 0; i < bitmapBytes.length; i++) out.push(bitmapBytes[i]) | 279 | for (let i = 0; i < bitmapBytes.length; i++) out.push(bitmapBytes[i]) |
| 280 | out.push(0x0d, 0x0a) | 280 | out.push(0x0d, 0x0a) |
| 281 | add(`PRINT 1,${printQty}`) | 281 | add(`PRINT 1,${printQty}`) |