diff --git a/美国版/Food Labeling Management App UniApp/src/pages/labels/bluetooth.vue b/美国版/Food Labeling Management App UniApp/src/pages/labels/bluetooth.vue
index aea8f5d..4928b79 100644
--- a/美国版/Food Labeling Management App UniApp/src/pages/labels/bluetooth.vue
+++ b/美国版/Food Labeling Management App UniApp/src/pages/labels/bluetooth.vue
@@ -163,7 +163,7 @@
2. On Android: enable Location (required for Bluetooth scan)
3. Place the printer within 10 m and tap Scan again
4. Devices with no name show as "Unknown Device"—you can still connect
- 5. D320FAX (d320fax_295c): use Bluetooth mode, tap Scan—shows paired + nearby devices, no filtering
+ 5. GP-D320FX (d320fx_xxxx): use Bluetooth mode, tap Scan—shows paired + nearby devices, no filtering
6. Restart the printer or app if not visible
@@ -260,8 +260,21 @@ async function refreshNativeDebug () {
function hasPreferredClassicDeviceInList () {
return devices.value.some((item: any) => {
const name = String(item?.name || '').toLowerCase()
- const type = String(item?.type || '').toLowerCase()
- return name.includes('virtual bt printer') || type === 'classic' || type === 'dual'
+ const driverKey = String(item?.driverKey || '').toLowerCase()
+ return name.includes('virtual bt printer') || driverKey === 'd320fax'
+ })
+}
+
+function upsertDevice (device: any) {
+ const described = describeDiscoveredPrinter(device)
+ const existing = devices.value.find(item => item.deviceId === described.deviceId)
+ if (!existing) {
+ discoveredIds.add(described.deviceId)
+ devices.value.push(described)
+ return
+ }
+ Object.assign(existing, described, {
+ RSSI: described.RSSI != null ? described.RSSI : existing.RSSI,
})
}
@@ -343,16 +356,13 @@ const stopDiscovery = () => {
const onDeviceFound = (res: any) => {
const foundDevices: any[] = res.devices || []
for (const d of foundDevices) {
- if (discoveredIds.has(d.deviceId)) continue
const name = (d.localName || d.name || '').trim()
- const displayName = name || 'Unknown Device'
- discoveredIds.add(d.deviceId)
- devices.value.push(describeDiscoveredPrinter({
+ upsertDevice({
deviceId: d.deviceId,
- name: displayName,
+ name: name || 'Unknown Device',
RSSI: d.RSSI,
type: 'ble',
- }))
+ })
}
devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100))
}
@@ -363,15 +373,13 @@ function mergeCachedBleDevices () {
success: (res: any) => {
const list = res.devices || []
for (const d of list) {
- if (discoveredIds.has(d.deviceId)) continue
const name = (d.localName || d.name || '').trim()
- discoveredIds.add(d.deviceId)
- devices.value.push(describeDiscoveredPrinter({
+ upsertDevice({
deviceId: d.deviceId,
name: name || 'Unknown Device',
RSSI: d.RSSI,
type: 'ble',
- }))
+ })
}
if (list.length > 0) devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100))
},
@@ -387,13 +395,11 @@ function addPairedDevices () {
debugInfo.value.pairedCount = (paired || []).length
debugInfo.value.foundVirtualPrinter = (paired || []).some((item: any) => String(item?.name || '').toLowerCase().includes('virtual bt printer'))
for (const p of paired) {
- if (discoveredIds.has(p.deviceId)) continue
- discoveredIds.add(p.deviceId)
- devices.value.push(describeDiscoveredPrinter({
+ upsertDevice({
deviceId: p.deviceId,
name: p.name || 'Unknown Device',
type: p.type || 'classic',
- }))
+ })
}
if (paired.length > 0) {
debugInfo.value.lastClassicEvent = 'paired devices loaded'
@@ -416,13 +422,11 @@ function startClassicScan () {
classic.startClassicDiscovery(
(dev: { name: string; deviceId: string; type: string }) => {
debugInfo.value.lastClassicEvent = 'device found'
- if (discoveredIds.has(dev.deviceId)) return
- discoveredIds.add(dev.deviceId)
- devices.value.push(describeDiscoveredPrinter({
+ upsertDevice({
deviceId: dev.deviceId,
name: dev.name || 'Unknown Device',
type: (dev.type as BtDevice['type']) || 'classic',
- }))
+ })
devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100))
},
() => {
@@ -542,7 +546,7 @@ const handleTestPrint = async () => {
if (msg === 'BUILTIN_PLUGIN_NOT_FOUND') {
uni.showModal({
title: 'Use Bluetooth Mode',
- 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.',
+ content: 'For GP-D320FX, switch to Bluetooth mode and tap Scan to connect (for example d320fx_xxxx). Built-in mode needs custom app packaging.',
showCancel: false,
success: () => { switchType('bluetooth') },
})
diff --git a/美国版/Food Labeling Management App UniApp/src/pages/more/printers.vue b/美国版/Food Labeling Management App UniApp/src/pages/more/printers.vue
index d83ea4a..d99d5e5 100644
--- a/美国版/Food Labeling Management App UniApp/src/pages/more/printers.vue
+++ b/美国版/Food Labeling Management App UniApp/src/pages/more/printers.vue
@@ -218,8 +218,8 @@ const normalizeDeviceName = (device: any) => {
const hasPreferredClassicDevice = () => {
return pairedDevices.value.some((item: any) => {
const name = String(item?.name || '').toLowerCase()
- const type = String(item?.type || '').toLowerCase()
- return name.includes('virtual bt printer') || type === 'classic' || type === 'dual'
+ const driverKey = String(item?.driverKey || '').toLowerCase()
+ return name.includes('virtual bt printer') || driverKey === 'd320fax'
})
}
diff --git a/美国版/Food Labeling Management App UniApp/src/utils/print/drivers/d320fax.ts b/美国版/Food Labeling Management App UniApp/src/utils/print/drivers/d320fax.ts
index 793fd4c..207fd2b 100644
--- a/美国版/Food Labeling Management App UniApp/src/utils/print/drivers/d320fax.ts
+++ b/美国版/Food Labeling Management App UniApp/src/utils/print/drivers/d320fax.ts
@@ -3,7 +3,6 @@ import type { PrinterCandidate, PrinterDriver } from '../types/printer'
const KEYWORDS = [
'd320fax',
- 'd320fx',
'virtual bt printer',
'gp-d320fax',
]
@@ -20,7 +19,7 @@ function score (device: PrinterCandidate): number {
export const d320faxDriver: PrinterDriver = {
key: 'd320fax',
brand: 'Gprinter',
- model: 'D320FAX/D320FX',
+ model: 'D320FAX',
displayName: 'Gprinter D320FAX',
protocol: 'tsc',
preferredConnection: 'classic',
diff --git a/美国版/Food Labeling Management App UniApp/src/utils/print/drivers/gpD320fx.ts b/美国版/Food Labeling Management App UniApp/src/utils/print/drivers/gpD320fx.ts
new file mode 100644
index 0000000..bd3b311
--- /dev/null
+++ b/美国版/Food Labeling Management App UniApp/src/utils/print/drivers/gpD320fx.ts
@@ -0,0 +1,42 @@
+import { buildTscLabelData, buildTscTestPrintData } from '../protocols/tscProtocol'
+import type { PrinterCandidate, PrinterDriver } from '../types/printer'
+
+const KEYWORDS = [
+ 'd320fx',
+ 'gp-d320fx',
+]
+
+function score (device: PrinterCandidate): number {
+ const text = `${device.name || ''} ${device.deviceId || ''}`.toLowerCase()
+ let total = 0
+ KEYWORDS.forEach((keyword) => {
+ if (text.includes(keyword)) total += 80
+ })
+ if (text.includes('gprinter')) total += 10
+ return total
+}
+
+export const gpD320fxDriver: PrinterDriver = {
+ key: 'gp-d320fx',
+ brand: 'Gprinter',
+ model: 'GP-D320FX',
+ displayName: 'Gprinter GP-D320FX',
+ protocol: 'tsc',
+ preferredConnection: 'ble',
+ preferredBleMtu: 512,
+ imageMaxWidthDots: 800,
+ imageDpi: 203,
+ keywords: KEYWORDS,
+ matches (device) {
+ return score(device)
+ },
+ resolveConnectionType (device) {
+ return device.type === 'classic' ? 'classic' : 'ble'
+ },
+ buildTestPrintData () {
+ return buildTscTestPrintData()
+ },
+ buildLabelData (payload) {
+ return buildTscLabelData(payload)
+ },
+}
diff --git a/美国版/Food Labeling Management App UniApp/src/utils/print/manager/driverRegistry.ts b/美国版/Food Labeling Management App UniApp/src/utils/print/manager/driverRegistry.ts
index 3e27fd0..f9e7600 100644
--- a/美国版/Food Labeling Management App UniApp/src/utils/print/manager/driverRegistry.ts
+++ b/美国版/Food Labeling Management App UniApp/src/utils/print/manager/driverRegistry.ts
@@ -1,10 +1,12 @@
import { d320faxDriver } from '../drivers/d320fax'
import { genericTscDriver } from '../drivers/genericTsc'
+import { gpD320fxDriver } from '../drivers/gpD320fx'
import { gpR3Driver } from '../drivers/gpR3'
import type { PrinterCandidate, PrinterDriver, ResolvedPrinterCandidate } from '../types/printer'
const printerDrivers: PrinterDriver[] = [
gpR3Driver,
+ gpD320fxDriver,
d320faxDriver,
genericTscDriver,
]
diff --git a/美国版/Food Labeling Management App UniApp/src/utils/print/manager/printerManager.ts b/美国版/Food Labeling Management App UniApp/src/utils/print/manager/printerManager.ts
index 981e4a1..2ba6218 100644
--- a/美国版/Food Labeling Management App UniApp/src/utils/print/manager/printerManager.ts
+++ b/美国版/Food Labeling Management App UniApp/src/utils/print/manager/printerManager.ts
@@ -41,7 +41,38 @@ function getPrinterTypeDisplayName (type: '' | 'bluetooth' | 'builtin'): string
function connectClassicBluetooth (device: PrinterCandidate, driver: PrinterDriver): Promise {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
- if (isNativeFastPrinterAvailable()) {
+ const shouldUseGenericClassicOnly = driver.key === 'gp-d320fx'
+
+ const connectClassicSocketFallback = () => {
+ try {
+ if (!classicBluetooth || typeof classicBluetooth.connDevice !== 'function') {
+ reject(new Error('Classic Bluetooth fallback is not available.'))
+ return
+ }
+ classicBluetooth.connDevice(device.deviceId, (ok: boolean) => {
+ if (ok) {
+ setBluetoothConnection({
+ deviceId: device.deviceId,
+ deviceName: device.name || 'Bluetooth Printer',
+ deviceType: 'classic',
+ transportMode: 'generic',
+ driverKey: driver.key,
+ mtu: driver.preferredBleMtu || 20,
+ })
+ resolve()
+ return
+ }
+ const message = typeof classicBluetooth.getLastError === 'function'
+ ? classicBluetooth.getLastError()
+ : ''
+ reject(new Error(message || 'Classic Bluetooth connection failed.'))
+ })
+ } catch (error: any) {
+ reject(error instanceof Error ? error : new Error(String(error || 'Classic Bluetooth connection failed.')))
+ }
+ }
+
+ if (!shouldUseGenericClassicOnly && isNativeFastPrinterAvailable()) {
connectNativeFastPrinterPlugin({
deviceId: device.deviceId,
deviceName: device.name || 'Bluetooth Printer',
@@ -50,15 +81,24 @@ function connectClassicBluetooth (device: PrinterCandidate, driver: PrinterDrive
deviceId: device.deviceId,
deviceName: device.name || 'Bluetooth Printer',
deviceType: 'classic',
+ transportMode: 'native-plugin',
driverKey: driver.key,
mtu: driver.preferredBleMtu || 20,
})
resolve()
}).catch((error: any) => {
+ if (driver.key === 'd320fax') {
+ connectClassicSocketFallback()
+ return
+ }
reject(error instanceof Error ? error : new Error(String(error || 'Classic Bluetooth connection failed.')))
})
return
}
+ if (driver.key === 'd320fax' || shouldUseGenericClassicOnly) {
+ connectClassicSocketFallback()
+ return
+ }
reject(new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.'))
// #endif
// #ifndef APP-PLUS
@@ -103,19 +143,44 @@ function findBleWriteCharacteristic (deviceId: string): Promise<{ serviceId: str
})
}
+function requestBleMtu (deviceId: string, preferredMtu: number): Promise {
+ return new Promise((resolve) => {
+ const targetMtu = Math.max(20, Math.min(512, Math.round(preferredMtu || 20)))
+ if (targetMtu <= 20 || typeof (uni as any).setBLEMTU !== 'function') {
+ resolve(20)
+ return
+ }
+ let settled = false
+ const done = (value: number) => {
+ if (settled) return
+ settled = true
+ clearTimeout(timer)
+ resolve(Math.max(20, Math.round(value || 20)))
+ }
+ const timer = setTimeout(() => done(20), 3000)
+ ;(uni as any).setBLEMTU({
+ deviceId,
+ mtu: targetMtu,
+ success: (res: any) => done(Number(res?.mtu) || targetMtu),
+ fail: () => done(20),
+ })
+ })
+}
+
function connectBlePrinter (device: PrinterCandidate, driver: PrinterDriver): Promise {
const finalizeExistingBleConnection = async () => {
const write = await findBleWriteCharacteristic(device.deviceId)
if (!write) {
throw new Error('No writable characteristic found. This device may not support printing.')
}
+ const negotiatedMtu = await requestBleMtu(device.deviceId, driver.preferredBleMtu || 20)
setBluetoothConnection({
deviceId: device.deviceId,
deviceName: device.name || 'Bluetooth Printer',
serviceId: write.serviceId,
characteristicId: write.characteristicId,
deviceType: 'ble',
- mtu: driver.preferredBleMtu || 20,
+ mtu: negotiatedMtu,
driverKey: driver.key,
})
}
@@ -145,6 +210,14 @@ function connectBlePrinter (device: PrinterCandidate, driver: PrinterDriver): Pr
export async function connectBluetoothPrinter (device: PrinterCandidate): Promise {
const driver = resolvePrinterDriver(device)
+ if (driver.key === 'gp-d320fx') {
+ try {
+ await connectBlePrinter(device, driver)
+ } catch (_) {
+ await connectClassicBluetooth(device, driver)
+ }
+ return driver
+ }
const resolvedType = driver.resolveConnectionType(device)
if (resolvedType === 'classic') {
await connectClassicBluetooth(device, driver)
@@ -218,19 +291,25 @@ function canUseNativeFastTemplatePrint (driver: PrinterDriver): boolean {
const connection = getBluetoothConnection()
return driver.protocol === 'tsc'
&& connection?.deviceType === 'classic'
+ && connection?.transportMode === 'native-plugin'
&& isNativeFastPrinterAvailable()
}
function getNativeClassicConnection () {
const connection = getBluetoothConnection()
- if (!connection || connection.deviceType !== 'classic') return null
+ if (!connection || connection.deviceType !== 'classic' || connection.transportMode !== 'native-plugin') return null
return connection
}
export async function testPrintCurrentPrinter (onProgress?: (percent: number) => void): Promise {
const driver = getCurrentPrinterDriver()
const connection = getBluetoothConnection()
- if (driver.protocol === 'tsc' && connection?.deviceType === 'classic' && !isNativeFastPrinterAvailable()) {
+ if (
+ driver.protocol === 'tsc'
+ && connection?.deviceType === 'classic'
+ && connection?.transportMode === 'native-plugin'
+ && !isNativeFastPrinterAvailable()
+ ) {
throw new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.')
}
if (canUseNativeFastTemplatePrint(driver)) {
@@ -319,7 +398,12 @@ export async function printSystemTemplateForCurrentPrinter (
): Promise {
const driver = getCurrentPrinterDriver()
const connection = getBluetoothConnection()
- if (driver.protocol === 'tsc' && connection?.deviceType === 'classic' && !isNativeFastPrinterAvailable()) {
+ if (
+ driver.protocol === 'tsc'
+ && connection?.deviceType === 'classic'
+ && connection?.transportMode === 'native-plugin'
+ && !isNativeFastPrinterAvailable()
+ ) {
throw new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.')
}
if (canUseNativeFastTemplatePrint(driver)) {
@@ -341,6 +425,7 @@ export async function printSystemTemplateForCurrentPrinter (
const structuredTemplate = adaptSystemLabelTemplate(template, data, {
dpi: driver.imageDpi || 203,
printQty: options.printQty || 1,
+ disableBitmapText: driver.key === 'gp-d320fx',
})
const bytes = driver.protocol === 'esc'
? buildEscPosTemplateData(structuredTemplate)
@@ -360,7 +445,7 @@ export function disconnectCurrentPrinter (): Promise {
if (type === 'bluetooth' && connection?.deviceType === 'classic') {
// #ifdef APP-PLUS
- if (isNativeFastPrinterAvailable()) {
+ if (connection.transportMode === 'native-plugin' && isNativeFastPrinterAvailable()) {
disconnectNativeFastPrinterPlugin().catch((e: any) => {
console.error('Disconnect native fast printer failed', e)
}).finally(() => {
diff --git a/美国版/Food Labeling Management App UniApp/src/utils/print/nativeBitmapPatch.ts b/美国版/Food Labeling Management App UniApp/src/utils/print/nativeBitmapPatch.ts
index c27d915..b566b52 100644
--- a/美国版/Food Labeling Management App UniApp/src/utils/print/nativeBitmapPatch.ts
+++ b/美国版/Food Labeling Management App UniApp/src/utils/print/nativeBitmapPatch.ts
@@ -10,6 +10,14 @@ const DESIGN_DPI = 96
const DEFAULT_THRESHOLD = 180
const TEXT_PADDING_DOTS = 6
+function normalizePrinterLikeText (str: string): string {
+ return String(str || '')
+ .normalize('NFKC')
+ .replace(/[\u2018\u2019]/g, '\'')
+ .replace(/[\u201C\u201D]/g, '"')
+ .replace(/[\u2013\u2014]/g, '-')
+}
+
type BitmapPatchItem = {
type: 'bitmap'
x: number
@@ -144,10 +152,11 @@ function splitTextLines (text: string, paint: any, maxWidth: number): string[] {
export function shouldRasterizeTextElement (text: string, type: string): boolean {
const normalizedType = String(type || '').toUpperCase()
- if (!text) return false
- if (normalizedType === 'TEXT_PRICE') return true
- if (/[€£¥¥$éÉáàâäãåæçèêëìíîïñòóôöõøùúûüýÿœšž]/.test(text)) return true
- return /[^\x20-\x7E]/.test(text)
+ const normalizedText = normalizePrinterLikeText(text)
+ if (!normalizedText) return false
+ if (normalizedType === 'TEXT_PRICE' && /[€£¥¥]/.test(normalizedText)) return true
+ if (/[€£¥¥éÉáàâäãåæçèêëìíîïñòóôöõøùúûüýÿœšž]/.test(normalizedText)) return true
+ return /[^\x20-\x7E]/.test(normalizedText)
}
export function createTextBitmapPatch (params: {
diff --git a/美国版/Food Labeling Management App UniApp/src/utils/print/printerConnection.ts b/美国版/Food Labeling Management App UniApp/src/utils/print/printerConnection.ts
index 8937397..e7334df 100644
--- a/美国版/Food Labeling Management App UniApp/src/utils/print/printerConnection.ts
+++ b/美国版/Food Labeling Management App UniApp/src/utils/print/printerConnection.ts
@@ -11,6 +11,7 @@ const STORAGE_BT_DEVICE_NAME = 'btDeviceName'
const STORAGE_BT_SERVICE_ID = 'btServiceId'
const STORAGE_BT_CHARACTERISTIC_ID = 'btCharacteristicId'
const STORAGE_BT_DEVICE_TYPE = 'btDeviceType' // 'ble' | 'classic'
+const STORAGE_BT_TRANSPORT_MODE = 'btTransportMode' // 'native-plugin' | 'generic'
const STORAGE_BLE_MTU = 'bleMTU'
const STORAGE_BUILTIN_PORT = 'builtinPort'
const STORAGE_PRINTER_DRIVER_KEY = 'printerDriverKey'
@@ -29,6 +30,7 @@ export const PrinterStorageKeys = {
btServiceId: STORAGE_BT_SERVICE_ID,
btCharacteristicId: STORAGE_BT_CHARACTERISTIC_ID,
btDeviceType: STORAGE_BT_DEVICE_TYPE,
+ btTransportMode: STORAGE_BT_TRANSPORT_MODE,
bleMTU: STORAGE_BLE_MTU,
driverKey: STORAGE_PRINTER_DRIVER_KEY,
} as const
@@ -43,6 +45,7 @@ export function setBluetoothConnection (info: {
serviceId?: string
characteristicId?: string
deviceType?: BtDeviceType
+ transportMode?: 'native-plugin' | 'generic'
mtu?: number
driverKey?: string
}) {
@@ -52,6 +55,10 @@ export function setBluetoothConnection (info: {
uni.setStorageSync(STORAGE_BT_SERVICE_ID, info.serviceId || '')
uni.setStorageSync(STORAGE_BT_CHARACTERISTIC_ID, info.characteristicId || '')
uni.setStorageSync(STORAGE_BT_DEVICE_TYPE, info.deviceType || 'ble')
+ uni.setStorageSync(
+ STORAGE_BT_TRANSPORT_MODE,
+ info.transportMode || (info.deviceType === 'classic' ? 'native-plugin' : 'generic')
+ )
uni.setStorageSync(STORAGE_BLE_MTU, info.mtu != null ? info.mtu : BLE_MTU_DEFAULT)
uni.setStorageSync(STORAGE_PRINTER_DRIVER_KEY, info.driverKey || '')
}
@@ -68,6 +75,7 @@ export function clearPrinter () {
uni.removeStorageSync(STORAGE_BT_SERVICE_ID)
uni.removeStorageSync(STORAGE_BT_CHARACTERISTIC_ID)
uni.removeStorageSync(STORAGE_BT_DEVICE_TYPE)
+ uni.removeStorageSync(STORAGE_BT_TRANSPORT_MODE)
uni.removeStorageSync(STORAGE_BLE_MTU)
uni.removeStorageSync(STORAGE_BUILTIN_PORT)
uni.removeStorageSync(STORAGE_PRINTER_DRIVER_KEY)
@@ -93,10 +101,12 @@ export function getBluetoothConnection (): {
serviceId: string
characteristicId: string
deviceType: BtDeviceType
+ transportMode: 'native-plugin' | 'generic'
mtu: number
} | null {
const deviceId = uni.getStorageSync(STORAGE_BT_DEVICE_ID)
const deviceType = (uni.getStorageSync(STORAGE_BT_DEVICE_TYPE) as BtDeviceType) || 'ble'
+ const transportMode = (uni.getStorageSync(STORAGE_BT_TRANSPORT_MODE) as 'native-plugin' | 'generic') || 'generic'
if (!deviceId) return null
if (deviceType === 'classic') {
return {
@@ -105,6 +115,7 @@ export function getBluetoothConnection (): {
serviceId: '',
characteristicId: '',
deviceType: 'classic',
+ transportMode,
mtu: BLE_MTU_DEFAULT,
}
}
@@ -117,6 +128,7 @@ export function getBluetoothConnection (): {
serviceId,
characteristicId,
deviceType: 'ble',
+ transportMode,
mtu: Number(uni.getStorageSync(STORAGE_BLE_MTU)) || BLE_MTU_DEFAULT,
}
}
@@ -209,15 +221,33 @@ function sendViaBle (
return Promise.reject(new Error('Bluetooth printer not connected.'))
}
const { deviceId, serviceId, characteristicId, mtu } = conn
+ const payloadSize = Math.max(20, Math.round((mtu || BLE_MTU_DEFAULT) > 23 ? (mtu || BLE_MTU_DEFAULT) - 3 : (mtu || BLE_MTU_DEFAULT)))
const chunks: number[][] = []
- for (let i = 0; i < data.length; i += mtu) {
- chunks.push(data.slice(i, i + mtu))
+ for (let i = 0; i < data.length; i += payloadSize) {
+ chunks.push(data.slice(i, i + payloadSize))
}
const total = chunks.length
let sent = 0
+ let completed = false
+ let timeoutId: ReturnType | null = setTimeout(() => {}, 0)
+ const writeDelayMs = payloadSize >= 180 ? 0 : (payloadSize > 20 ? 1 : 8)
+
+ const resetTimeout = (reject: (reason?: any) => void) => {
+ if (timeoutId) clearTimeout(timeoutId)
+ timeoutId = setTimeout(() => {
+ if (completed) return
+ completed = true
+ reject(new Error('BLE write timeout'))
+ }, 15000)
+ }
function sendNext (): Promise {
+ if (completed) {
+ return Promise.reject(new Error('BLE write timeout'))
+ }
if (sent >= total) {
+ completed = true
+ if (timeoutId) clearTimeout(timeoutId)
if (onProgress) onProgress(100)
return Promise.resolve()
}
@@ -228,17 +258,28 @@ function sendViaBle (
view.setUint8(j, chunk[j] & 0xff)
}
return new Promise((resolve, reject) => {
+ resetTimeout(reject)
uni.writeBLECharacteristicValue({
deviceId,
serviceId,
characteristicId,
value: buffer,
success: () => {
+ if (completed) return
sent++
if (onProgress) onProgress(Math.round((sent / total) * 100))
- setTimeout(() => sendNext().then(resolve).catch(reject), 10)
+ if (writeDelayMs <= 0) {
+ sendNext().then(resolve).catch(reject)
+ return
+ }
+ setTimeout(() => sendNext().then(resolve).catch(reject), writeDelayMs)
+ },
+ fail: (err: any) => {
+ if (completed) return
+ completed = true
+ if (timeoutId) clearTimeout(timeoutId)
+ reject(new Error(err.errMsg || 'BLE write failed'))
},
- fail: (err: any) => reject(new Error(err.errMsg || 'BLE write failed')),
})
})
}
@@ -256,9 +297,22 @@ function sendViaClassic (
return Promise.reject(new Error('Classic Bluetooth printer not connected.'))
}
return new Promise((resolve, reject) => {
+ let settled = false
+ const finish = (fn: () => void) => {
+ if (settled) return
+ settled = true
+ clearTimeout(timeoutId)
+ fn()
+ }
+ const timeoutId = setTimeout(() => {
+ finish(() => {
+ reject(buildClassicBluetoothError('Classic Bluetooth send timeout', conn.deviceId))
+ })
+ }, 15000)
+
try {
if (!classicBluetooth) {
- reject(new Error('Classic Bluetooth not available'))
+ finish(() => reject(new Error('Classic Bluetooth not available')))
return
}
const debugState = typeof classicBluetooth.getDebugState === 'function'
@@ -272,7 +326,7 @@ function sendViaClassic (
const errorMessage = typeof classicBluetooth.getLastError === 'function'
? classicBluetooth.getLastError()
: ''
- reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth connection is not ready', conn.deviceId))
+ finish(() => reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth connection is not ready', conn.deviceId)))
return
}
@@ -283,31 +337,35 @@ function sendViaClassic (
if (typeof classicBluetooth.sendByteDataAsync === 'function') {
classicBluetooth.sendByteDataAsync(sendData, (ok: boolean, errorMessage?: string) => {
- if (onProgress) onProgress(100)
- if (ok) {
- resolve()
- return
- }
- reject(buildClassicBluetoothError(
- errorMessage || classicBluetooth.getLastError?.() || 'Classic Bluetooth send failed',
- conn.deviceId
- ))
+ finish(() => {
+ if (onProgress) onProgress(100)
+ if (ok) {
+ resolve()
+ return
+ }
+ reject(buildClassicBluetoothError(
+ errorMessage || classicBluetooth.getLastError?.() || 'Classic Bluetooth send failed',
+ conn.deviceId
+ ))
+ })
})
return
}
const ok = classicBluetooth.sendByteData(sendData)
- if (onProgress) onProgress(100)
- if (ok) {
- resolve()
- return
- }
- const errorMessage = typeof classicBluetooth.getLastError === 'function'
- ? classicBluetooth.getLastError()
- : ''
- reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth send failed', conn.deviceId))
+ finish(() => {
+ if (onProgress) onProgress(100)
+ if (ok) {
+ resolve()
+ return
+ }
+ const errorMessage = typeof classicBluetooth.getLastError === 'function'
+ ? classicBluetooth.getLastError()
+ : ''
+ reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth send failed', conn.deviceId))
+ })
} catch (e: any) {
- reject(buildClassicBluetoothError(e?.message || String(e || 'Classic Bluetooth send exception'), conn.deviceId))
+ finish(() => reject(buildClassicBluetoothError(e?.message || String(e || 'Classic Bluetooth send exception'), conn.deviceId)))
}
})
// #endif
diff --git a/美国版/Food Labeling Management App UniApp/src/utils/print/systemTemplateAdapter.ts b/美国版/Food Labeling Management App UniApp/src/utils/print/systemTemplateAdapter.ts
index 093b7ff..f1a3d30 100644
--- a/美国版/Food Labeling Management App UniApp/src/utils/print/systemTemplateAdapter.ts
+++ b/美国版/Food Labeling Management App UniApp/src/utils/print/systemTemplateAdapter.ts
@@ -275,7 +275,10 @@ function buildTscTemplate (
template: SystemLabelTemplate,
data: LabelTemplateData,
dpi: number,
- printQty: number
+ printQty: number,
+ options: {
+ disableBitmapText?: boolean
+ } = {}
): StructuredTscTemplate {
const widthMm = roundNumber(toMillimeter(template.width, template.unit || 'inch'))
const heightMm = roundNumber(toMillimeter(template.height, template.unit || 'inch'))
@@ -293,7 +296,7 @@ function buildTscTemplate (
const scale = resolveTextScale(getConfigNumber(config, ['fontSize'], 14), dpi)
const align = resolveElementAlign(element, pageWidth)
- if (shouldRasterizeTextElement(text, type)) {
+ if (!options.disableBitmapText && shouldRasterizeTextElement(text, type)) {
const bitmapPatch = createTextBitmapPatch({
element,
text,
@@ -470,13 +473,16 @@ export function adaptSystemLabelTemplate (
options: {
dpi?: number
printQty?: number
+ disableBitmapText?: boolean
} = {}
): StructuredLabelTemplate {
const dpi = options.dpi || 203
const printQty = Math.max(1, Math.round(options.printQty || 1))
return {
key: template.id || template.name || 'system-label-template',
- tsc: buildTscTemplate(template, data, dpi, printQty),
+ tsc: buildTscTemplate(template, data, dpi, printQty, {
+ disableBitmapText: options.disableBitmapText,
+ }),
esc: buildEscTemplate(template, data, printQty),
}
}
diff --git a/美国版/Food Labeling Management App UniApp/src/utils/print/tscLabelBuilder.ts b/美国版/Food Labeling Management App UniApp/src/utils/print/tscLabelBuilder.ts
index 4b21651..63642f1 100644
--- a/美国版/Food Labeling Management App UniApp/src/utils/print/tscLabelBuilder.ts
+++ b/美国版/Food Labeling Management App UniApp/src/utils/print/tscLabelBuilder.ts
@@ -220,7 +220,7 @@ export function buildTscTemplateLabel (template: StructuredTscTemplate): number[
if (item.type === 'bitmap') {
const bytesPerRow = item.image.width / 8
const bitmapBytes = pixelsToTscBitmapBytes(item.image)
- add(`BITMAP ${item.x},${item.y},${bytesPerRow},${item.image.height},0,`)
+ addCommandBytes(out, `BITMAP ${item.x},${item.y},${bytesPerRow},${item.image.height},0,`)
for (let i = 0; i < bitmapBytes.length; i++) out.push(bitmapBytes[i])
out.push(0x0d, 0x0a)
return
@@ -275,7 +275,7 @@ export function buildTscImageLabel (
add('DENSITY 14')
add('SPEED 5')
add('CLS')
- add(`BITMAP ${x},${y},${bytesPerRow},${image.height},0,`)
+ addCommandBytes(out, `BITMAP ${x},${y},${bytesPerRow},${image.height},0,`)
for (let i = 0; i < bitmapBytes.length; i++) out.push(bitmapBytes[i])
out.push(0x0d, 0x0a)
add(`PRINT 1,${printQty}`)