Commit 499e4a42bb09bbd9e63a16e718ee038a46a5f339

Authored by “wangming”
1 parent 557c9c0e

Enhance Bluetooth functionality and localization support

- Added support for displaying paired Bluetooth devices in the UI.
- Improved Bluetooth scanning logic to include classic Bluetooth devices.
- Updated localization files for English and Chinese to include new strings related to Bluetooth devices.
- Refined error handling and user feedback during Bluetooth operations.
- Enhanced built-in printer connection logic to probe multiple ports for better reliability.
美国版/Food Labeling Management App UniApp/src/locales/en.ts
@@ -53,6 +53,11 @@ export default { @@ -53,6 +53,11 @@ export default {
53 connectFail: 'Connection failed', 53 connectFail: 'Connection failed',
54 bleNotAvailable: 'Bluetooth not available', 54 bleNotAvailable: 'Bluetooth not available',
55 noDevices: 'No devices found', 55 noDevices: 'No devices found',
  56 + pairedDevices: 'Paired Devices',
  57 + noPairedDevices: 'No paired Bluetooth devices',
  58 + classic: 'Classic',
  59 + ble: 'BLE',
  60 + dual: 'Dual',
56 printer1: { name: 'Kitchen Printer #1', location: 'Main Kitchen' }, 61 printer1: { name: 'Kitchen Printer #1', location: 'Main Kitchen' },
57 printer2: { name: 'Kitchen Printer #2', location: 'Main Kitchen' }, 62 printer2: { name: 'Kitchen Printer #2', location: 'Main Kitchen' },
58 printer3: { name: 'Prep Area Printer', location: 'Prep Station' }, 63 printer3: { name: 'Prep Area Printer', location: 'Prep Station' },
美国版/Food Labeling Management App UniApp/src/locales/zh.ts
@@ -53,6 +53,11 @@ export default { @@ -53,6 +53,11 @@ export default {
53 connectFail: '连接失败', 53 connectFail: '连接失败',
54 bleNotAvailable: '蓝牙未开启或不可用', 54 bleNotAvailable: '蓝牙未开启或不可用',
55 noDevices: '未发现设备', 55 noDevices: '未发现设备',
  56 + pairedDevices: '已配对设备',
  57 + noPairedDevices: '没有已配对的蓝牙设备',
  58 + classic: '经典',
  59 + ble: '低功耗',
  60 + dual: '双模',
56 printer1: { name: '厨房打印机 #1', location: '主厨房' }, 61 printer1: { name: '厨房打印机 #1', location: '主厨房' },
57 printer2: { name: '厨房打印机 #2', location: '主厨房' }, 62 printer2: { name: '厨房打印机 #2', location: '主厨房' },
58 printer3: { name: '准备区打印机', location: '准备站' }, 63 printer3: { name: '准备区打印机', location: '准备站' },
美国版/Food Labeling Management App UniApp/src/pages/labels/bluetooth.vue
@@ -234,7 +234,7 @@ const initBluetooth = (): Promise<void> => { @@ -234,7 +234,7 @@ const initBluetooth = (): Promise<void> => {
234 }) 234 })
235 } 235 }
236 236
237 -const startDiscovery = () => { 237 +const startBleScan = () => {
238 uni.startBluetoothDevicesDiscovery({ 238 uni.startBluetoothDevicesDiscovery({
239 allowDuplicatesKey: false, 239 allowDuplicatesKey: false,
240 success: () => { 240 success: () => {
@@ -242,29 +242,9 @@ const startDiscovery = () => { @@ -242,29 +242,9 @@ const startDiscovery = () => {
242 errorMsg.value = '' 242 errorMsg.value = ''
243 }, 243 },
244 fail: (err: any) => { 244 fail: (err: any) => {
245 - isScanning.value = false  
246 - errorMsg.value = 'Scan failed: ' + (err.errMsg || 'Unknown error') 245 + console.error('BLE startBluetoothDevicesDiscovery fail:', err)
247 }, 246 },
248 }) 247 })
249 - // 同时启动经典蓝牙扫描(发现 d320fax_295c 等未配对设备,不过滤任何设备)  
250 - // #ifdef APP-PLUS  
251 - const classic = getClassicBluetooth()  
252 - if (classic && classic.startClassicDiscovery) {  
253 - classic.startClassicDiscovery(  
254 - (dev: { name: string; deviceId: string; type: string }) => {  
255 - if (discoveredIds.has(dev.deviceId)) return  
256 - discoveredIds.add(dev.deviceId)  
257 - devices.value.push({  
258 - deviceId: dev.deviceId,  
259 - name: dev.name || 'Unknown',  
260 - type: (dev.type as BtDevice['type']) || 'classic',  
261 - })  
262 - devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100))  
263 - },  
264 - () => { /* 经典蓝牙扫描完成 */ },  
265 - )  
266 - }  
267 - // #endif  
268 } 248 }
269 249
270 const stopDiscovery = () => { 250 const stopDiscovery = () => {
@@ -383,15 +363,41 @@ const handleScan = async () => { @@ -383,15 +363,41 @@ const handleScan = async () => {
383 errorMsg.value = '' 363 errorMsg.value = ''
384 devices.value = [] 364 devices.value = []
385 discoveredIds.clear() 365 discoveredIds.clear()
  366 +
  367 + addPairedDevices()
  368 +
  369 + // #ifdef APP-PLUS
  370 + const classic = getClassicBluetooth()
  371 + if (classic && classic.startClassicDiscovery) {
  372 + try {
  373 + classic.startClassicDiscovery(
  374 + (dev: { name: string; deviceId: string; type: string }) => {
  375 + if (discoveredIds.has(dev.deviceId)) return
  376 + discoveredIds.add(dev.deviceId)
  377 + devices.value.push({
  378 + deviceId: dev.deviceId,
  379 + name: dev.name || 'Unknown',
  380 + type: (dev.type as BtDevice['type']) || 'classic',
  381 + })
  382 + },
  383 + () => {},
  384 + )
  385 + isScanning.value = true
  386 + } catch (e) {
  387 + console.error('Classic discovery failed', e)
  388 + }
  389 + }
  390 + // #endif
  391 +
  392 + if (devices.value.length > 0) {
  393 + uni.showToast({ title: `Found ${devices.value.length} device(s)`, icon: 'none' })
  394 + }
  395 +
386 try { 396 try {
387 if (!btAdapterReady.value) { 397 if (!btAdapterReady.value) {
388 await initBluetooth() 398 await initBluetooth()
389 } 399 }
390 - addPairedDevices()  
391 mergeCachedBleDevices() 400 mergeCachedBleDevices()
392 - if (devices.value.length > 0) {  
393 - uni.showToast({ title: `Found ${devices.value.length} device(s)`, icon: 'none' })  
394 - }  
395 await new Promise<void>((resolve) => { 401 await new Promise<void>((resolve) => {
396 uni.getLocation({ 402 uni.getLocation({
397 type: 'wgs84', 403 type: 'wgs84',
@@ -399,13 +405,15 @@ const handleScan = async () =&gt; { @@ -399,13 +405,15 @@ const handleScan = async () =&gt; {
399 fail: () => resolve(), 405 fail: () => resolve(),
400 }) 406 })
401 }) 407 })
402 - startDiscovery() 408 + startBleScan()
403 uni.showToast({ title: 'Scanning...', icon: 'none' }) 409 uni.showToast({ title: 'Scanning...', icon: 'none' })
404 setTimeout(() => { 410 setTimeout(() => {
405 if (isScanning.value) stopDiscovery() 411 if (isScanning.value) stopDiscovery()
406 }, 20000) 412 }, 20000)
407 } catch (_) { 413 } catch (_) {
408 - // error set in initBluetooth 414 + if (!isScanning.value && devices.value.length === 0) {
  415 + errorMsg.value = 'Bluetooth scan failed. Check if Bluetooth is enabled.'
  416 + }
409 } 417 }
410 } 418 }
411 419
@@ -563,7 +571,17 @@ onMounted(() =&gt; { @@ -563,7 +571,17 @@ onMounted(() =&gt; {
563 571
564 onUnmounted(() => { 572 onUnmounted(() => {
565 if (isScanning.value) stopDiscovery() 573 if (isScanning.value) stopDiscovery()
566 - uni.offBluetoothDeviceFound() 574 + try {
  575 + if (typeof uni.offBluetoothDeviceFound === 'function') {
  576 + uni.offBluetoothDeviceFound()
  577 + }
  578 + } catch (_) {}
  579 + try {
  580 + if (typeof uni.offBluetoothAdapterStateChange === 'function') {
  581 + uni.offBluetoothAdapterStateChange()
  582 + }
  583 + } catch (_) {}
  584 + uni.closeBluetoothAdapter({ complete: () => {} })
567 }) 585 })
568 </script> 586 </script>
569 587
美国版/Food Labeling Management App UniApp/src/pages/labels/preview.vue
@@ -265,8 +265,8 @@ const handlePrint = async () =&gt; { @@ -265,8 +265,8 @@ const handlePrint = async () =&gt; {
265 const msg = (e && e.message) ? e.message : 'Print failed' 265 const msg = (e && e.message) ? e.message : 'Print failed'
266 if (msg === 'BUILTIN_PLUGIN_NOT_FOUND' || (msg && msg.indexOf('Built-in printer') !== -1)) { 266 if (msg === 'BUILTIN_PLUGIN_NOT_FOUND' || (msg && msg.indexOf('Built-in printer') !== -1)) {
267 uni.showModal({ 267 uni.showModal({
268 - title: 'Use Bluetooth Mode',  
269 - content: 'For GP-D320FAX, go to Printer settings, switch to Bluetooth mode and tap Scan to connect (e.g. d320fax_295c or Virtual BT Printer). Built-in mode needs custom app packaging.', 268 + title: 'Built-in Print Not Available',
  269 + content: 'This device does not support TCP built-in printing. Please switch to Bluetooth mode: go to Printer Settings, tap Scan, and connect to your printer (look for a device name like "Virtual BT Printer" or your printer model). You may need to pair it first in Android Bluetooth settings.',
270 confirmText: 'Printer Settings', 270 confirmText: 'Printer Settings',
271 cancelText: 'Cancel', 271 cancelText: 'Cancel',
272 success: (res) => { 272 success: (res) => {
美国版/Food Labeling Management App UniApp/src/pages/more/printers.vue
@@ -45,6 +45,29 @@ @@ -45,6 +45,29 @@
45 <button class="btn-connect">{{ t('printers.connect') }}</button> 45 <button class="btn-connect">{{ t('printers.connect') }}</button>
46 </view> 46 </view>
47 47
  48 + <!-- Paired Devices -->
  49 + <!-- #ifdef APP-PLUS -->
  50 + <view class="section-header">
  51 + <text class="section-title">{{ t('printers.pairedDevices') }}</text>
  52 + </view>
  53 +
  54 + <view v-for="d in pairedDevices" :key="'paired-' + d.deviceId" class="printer-card device-item" @click="connectBt(d)">
  55 + <view class="printer-icon"><AppIcon name="bluetooth" size="md" color="blue" /></view>
  56 + <view class="printer-info">
  57 + <text class="printer-name">{{ d.name || 'Unknown Device' }}</text>
  58 + <text class="printer-loc">{{ d.deviceId }}</text>
  59 + </view>
  60 + <view class="device-meta">
  61 + <text class="device-type-tag" :class="'tag-' + (d.type || 'ble')">{{ getTypeLabel(d.type) }}</text>
  62 + <button class="btn-connect" :disabled="isConnecting">{{ t('printers.connect') }}</button>
  63 + </view>
  64 + </view>
  65 +
  66 + <view v-if="pairedDevices.length === 0" class="empty-state">
  67 + <text class="empty-text">{{ t('printers.noPairedDevices') }}</text>
  68 + </view>
  69 + <!-- #endif -->
  70 +
48 <!-- Bluetooth Scanning Area --> 71 <!-- Bluetooth Scanning Area -->
49 <view class="section-header"> 72 <view class="section-header">
50 <text class="section-title">{{ t('printers.nearby') }}</text> 73 <text class="section-title">{{ t('printers.nearby') }}</text>
@@ -62,7 +85,10 @@ @@ -62,7 +85,10 @@
62 <text class="printer-name">{{ d.name || 'Unknown Device' }}</text> 85 <text class="printer-name">{{ d.name || 'Unknown Device' }}</text>
63 <text class="printer-loc">{{ d.deviceId }}</text> 86 <text class="printer-loc">{{ d.deviceId }}</text>
64 </view> 87 </view>
65 - <button class="btn-connect" :disabled="isConnecting">{{ t('printers.connect') }}</button> 88 + <view class="device-meta">
  89 + <text v-if="d.type && d.type !== 'unknown'" class="device-type-tag" :class="'tag-' + d.type">{{ getTypeLabel(d.type) }}</text>
  90 + <button class="btn-connect" :disabled="isConnecting">{{ t('printers.connect') }}</button>
  91 + </view>
66 </view> 92 </view>
67 93
68 <view v-if="devices.length === 0 && !isScanning" class="empty-state"> 94 <view v-if="devices.length === 0 && !isScanning" class="empty-state">
@@ -90,51 +116,101 @@ import { @@ -90,51 +116,101 @@ import {
90 } from '../../utils/print/printerConnection' 116 } from '../../utils/print/printerConnection'
91 import { buildTestTscLabel } from '../../utils/print/tscLabelBuilder' 117 import { buildTestTscLabel } from '../../utils/print/tscLabelBuilder'
92 118
  119 +// #ifdef APP-PLUS
  120 +const classicBluetooth = (require('../../utils/print/bluetoothTool.js') as any).default
  121 +// #endif
  122 +
93 const { t } = useI18n() 123 const { t } = useI18n()
94 const isMenuOpen = ref(false) 124 const isMenuOpen = ref(false)
95 125
96 -// 状态管理  
97 const currentType = ref(getPrinterType()) 126 const currentType = ref(getPrinterType())
98 const currentBt = ref(getBluetoothConnection()) 127 const currentBt = ref(getBluetoothConnection())
99 const isScanning = ref(false) 128 const isScanning = ref(false)
100 const devices = ref<any[]>([]) 129 const devices = ref<any[]>([])
  130 +const pairedDevices = ref<any[]>([])
101 const isConnecting = ref(false) 131 const isConnecting = ref(false)
  132 +let bleListenerRegistered = false
102 133
103 -// Refresh current state  
104 const refreshStatus = () => { 134 const refreshStatus = () => {
105 currentType.value = getPrinterType() 135 currentType.value = getPrinterType()
106 currentBt.value = getBluetoothConnection() 136 currentBt.value = getBluetoothConnection()
107 } 137 }
108 138
109 -// 蓝牙搜索 139 +const getTypeLabel = (type?: string) => {
  140 + switch (type) {
  141 + case 'classic': return t('printers.classic')
  142 + case 'ble': return t('printers.ble')
  143 + case 'dual': return t('printers.dual')
  144 + default: return t('printers.ble')
  145 + }
  146 +}
  147 +
  148 +const addDeviceDedup = (device: any) => {
  149 + const existing = devices.value.find(d => d.deviceId === device.deviceId)
  150 + if (!existing) {
  151 + devices.value.push(device)
  152 + }
  153 +}
  154 +
  155 +// #ifdef APP-PLUS
  156 +const loadPairedDevices = () => {
  157 + try {
  158 + const list = classicBluetooth.getPairedDevices()
  159 + pairedDevices.value = list || []
  160 + } catch (e) {
  161 + console.error('Failed to load paired devices', e)
  162 + pairedDevices.value = []
  163 + }
  164 +}
  165 +// #endif
  166 +
110 const startScan = () => { 167 const startScan = () => {
111 if (isScanning.value) return 168 if (isScanning.value) return
112 - 169 +
113 devices.value = [] 170 devices.value = []
114 isScanning.value = true 171 isScanning.value = true
115 - 172 +
  173 + // #ifdef APP-PLUS
  174 + loadPairedDevices()
  175 + try {
  176 + classicBluetooth.startClassicDiscovery(
  177 + (device: any) => {
  178 + if (device.name) {
  179 + addDeviceDedup(device)
  180 + }
  181 + },
  182 + () => {}
  183 + )
  184 + } catch (e) {
  185 + console.error('Classic discovery failed', e)
  186 + }
  187 + // #endif
  188 +
116 uni.openBluetoothAdapter({ 189 uni.openBluetoothAdapter({
117 success: () => { 190 success: () => {
  191 + if (!bleListenerRegistered) {
  192 + bleListenerRegistered = true
  193 + uni.onBluetoothDeviceFound((res) => {
  194 + res.devices.forEach(device => {
  195 + if (device.name) {
  196 + addDeviceDedup({ ...device, type: 'ble' })
  197 + }
  198 + })
  199 + })
  200 + }
118 uni.startBluetoothDevicesDiscovery({ 201 uni.startBluetoothDevicesDiscovery({
119 allowDuplicatesKey: false, 202 allowDuplicatesKey: false,
120 - success: () => {  
121 - uni.onBluetoothDeviceFound((res) => {  
122 - res.devices.forEach(device => {  
123 - if (device.name && !devices.value.find(d => d.deviceId === device.deviceId)) {  
124 - devices.value.push(device)  
125 - }  
126 - })  
127 - })  
128 - }, 203 + success: () => {},
129 fail: (err) => { 204 fail: (err) => {
130 - isScanning.value = false  
131 - uni.showToast({ title: t('printers.bleNotAvailable'), icon: 'none' }) 205 + console.error('BLE startBluetoothDevicesDiscovery fail:', err)
132 } 206 }
133 }) 207 })
134 }, 208 },
135 fail: (err) => { 209 fail: (err) => {
136 - isScanning.value = false  
137 - uni.showToast({ title: t('printers.bleNotAvailable'), icon: 'none' }) 210 + console.error('openBluetoothAdapter fail:', err)
  211 + if (devices.value.length === 0 && pairedDevices.value.length === 0) {
  212 + uni.showToast({ title: t('printers.bleNotAvailable'), icon: 'none' })
  213 + }
138 } 214 }
139 }) 215 })
140 } 216 }
@@ -142,37 +218,73 @@ const startScan = () =&gt; { @@ -142,37 +218,73 @@ const startScan = () =&gt; {
142 const stopScan = () => { 218 const stopScan = () => {
143 isScanning.value = false 219 isScanning.value = false
144 uni.stopBluetoothDevicesDiscovery({ 220 uni.stopBluetoothDevicesDiscovery({
145 - success: () => console.log('Stop scan success'),  
146 - fail: (err) => console.error('Stop scan fail', err) 221 + success: () => console.log('Stop BLE scan success'),
  222 + fail: (err) => console.error('Stop BLE scan fail', err)
147 }) 223 })
  224 + // #ifdef APP-PLUS
  225 + try {
  226 + classicBluetooth.cancelClassicDiscovery()
  227 + } catch (e) {
  228 + console.error('Cancel classic discovery failed', e)
  229 + }
  230 + // #endif
148 } 231 }
149 232
150 -// 连接蓝牙设备  
151 const connectBt = (device: any) => { 233 const connectBt = (device: any) => {
152 if (isConnecting.value) return 234 if (isConnecting.value) return
153 isConnecting.value = true 235 isConnecting.value = true
154 stopScan() 236 stopScan()
155 - 237 +
156 uni.showLoading({ title: t('printers.connecting') }) 238 uni.showLoading({ title: t('printers.connecting') })
157 - 239 +
  240 + const deviceType = device.type || 'ble'
  241 +
  242 + if (deviceType === 'classic' || deviceType === 'dual') {
  243 + // #ifdef APP-PLUS
  244 + classicBluetooth.connDevice(device.deviceId, (success: boolean) => {
  245 + isConnecting.value = false
  246 + uni.hideLoading()
  247 + if (success) {
  248 + setBluetoothConnection({
  249 + deviceId: device.deviceId,
  250 + deviceName: device.name || 'Bluetooth Printer',
  251 + deviceType: 'classic'
  252 + })
  253 + currentType.value = 'bluetooth'
  254 + currentBt.value = getBluetoothConnection()
  255 + uni.showToast({ title: t('printers.connectSuccess') })
  256 + } else {
  257 + uni.showToast({ title: t('printers.connectFail'), icon: 'none' })
  258 + }
  259 + })
  260 + // #endif
  261 + // #ifndef APP-PLUS
  262 + isConnecting.value = false
  263 + uni.hideLoading()
  264 + uni.showToast({ title: t('printers.connectFail'), icon: 'none' })
  265 + // #endif
  266 + } else {
  267 + connectBle(device)
  268 + }
  269 +}
  270 +
  271 +const connectBle = (device: any) => {
158 uni.createBLEConnection({ 272 uni.createBLEConnection({
159 deviceId: device.deviceId, 273 deviceId: device.deviceId,
160 success: () => { 274 success: () => {
161 uni.getBLEDeviceServices({ 275 uni.getBLEDeviceServices({
162 deviceId: device.deviceId, 276 deviceId: device.deviceId,
163 success: (res) => { 277 success: (res) => {
164 - // 这里简化处理:寻找第一个包含写权限的特征值  
165 - // 实际项目中可能需要根据特定 UUID 匹配,官方示例中是遍历寻找  
166 findWriteCharacteristic(device.deviceId, res.services, device.name) 278 findWriteCharacteristic(device.deviceId, res.services, device.name)
167 }, 279 },
168 - fail: (err) => { 280 + fail: () => {
169 isConnecting.value = false 281 isConnecting.value = false
170 uni.hideLoading() 282 uni.hideLoading()
171 uni.showToast({ title: t('printers.connectFail'), icon: 'none' }) 283 uni.showToast({ title: t('printers.connectFail'), icon: 'none' })
172 } 284 }
173 }) 285 })
174 }, 286 },
175 - fail: (err) => { 287 + fail: () => {
176 isConnecting.value = false 288 isConnecting.value = false
177 uni.hideLoading() 289 uni.hideLoading()
178 uni.showToast({ title: t('printers.connectFail'), icon: 'none' }) 290 uni.showToast({ title: t('printers.connectFail'), icon: 'none' })
@@ -183,7 +295,7 @@ const connectBt = (device: any) =&gt; { @@ -183,7 +295,7 @@ const connectBt = (device: any) =&gt; {
183 const findWriteCharacteristic = (deviceId: string, services: any[], deviceName: string) => { 295 const findWriteCharacteristic = (deviceId: string, services: any[], deviceName: string) => {
184 let found = false 296 let found = false
185 let serviceIdx = 0 297 let serviceIdx = 0
186 - 298 +
187 const nextService = () => { 299 const nextService = () => {
188 if (serviceIdx >= services.length || found) { 300 if (serviceIdx >= services.length || found) {
189 if (!found) { 301 if (!found) {
@@ -193,7 +305,7 @@ const findWriteCharacteristic = (deviceId: string, services: any[], deviceName: @@ -193,7 +305,7 @@ const findWriteCharacteristic = (deviceId: string, services: any[], deviceName:
193 } 305 }
194 return 306 return
195 } 307 }
196 - 308 +
197 const service = services[serviceIdx++] 309 const service = services[serviceIdx++]
198 uni.getBLEDeviceCharacteristics({ 310 uni.getBLEDeviceCharacteristics({
199 deviceId, 311 deviceId,
@@ -221,26 +333,32 @@ const findWriteCharacteristic = (deviceId: string, services: any[], deviceName: @@ -221,26 +333,32 @@ const findWriteCharacteristic = (deviceId: string, services: any[], deviceName:
221 fail: () => nextService() 333 fail: () => nextService()
222 }) 334 })
223 } 335 }
224 - 336 +
225 nextService() 337 nextService()
226 } 338 }
227 339
228 -// 连接内置打印机  
229 const connectBuiltin = () => { 340 const connectBuiltin = () => {
230 setBuiltinPrinter() 341 setBuiltinPrinter()
231 currentType.value = 'builtin' 342 currentType.value = 'builtin'
232 uni.showToast({ title: t('printers.connectSuccess') }) 343 uni.showToast({ title: t('printers.connectSuccess') })
233 } 344 }
234 345
235 -// 断开连接  
236 const disconnect = () => { 346 const disconnect = () => {
  347 + // #ifdef APP-PLUS
  348 + if (currentBt.value?.deviceType === 'classic') {
  349 + try {
  350 + classicBluetooth.disConnDevice()
  351 + } catch (e) {
  352 + console.error('Disconnect classic bluetooth failed', e)
  353 + }
  354 + }
  355 + // #endif
237 clearPrinter() 356 clearPrinter()
238 currentType.value = '' 357 currentType.value = ''
239 currentBt.value = null 358 currentBt.value = null
240 uni.showToast({ title: t('printers.disconnected') }) 359 uni.showToast({ title: t('printers.disconnected') })
241 } 360 }
242 361
243 -// 测试打印  
244 const doTestPrint = async () => { 362 const doTestPrint = async () => {
245 try { 363 try {
246 const data = buildTestTscLabel() 364 const data = buildTestTscLabel()
@@ -250,7 +368,17 @@ const doTestPrint = async () =&gt; { @@ -250,7 +368,17 @@ const doTestPrint = async () =&gt; {
250 uni.showToast({ title: t('printers.testPrintSuccess') }) 368 uni.showToast({ title: t('printers.testPrintSuccess') })
251 } catch (e: any) { 369 } catch (e: any) {
252 uni.hideLoading() 370 uni.hideLoading()
253 - uni.showToast({ title: t('printers.testPrintFail') + ': ' + e.message, icon: 'none' }) 371 + const msg = (e && e.message) ? e.message : 'Print failed'
  372 + if (msg.indexOf('Built-in printer') !== -1 || msg === 'BUILTIN_PLUGIN_NOT_FOUND') {
  373 + uni.showModal({
  374 + title: 'Print Failed',
  375 + content: 'Built-in TCP printing is not available on this device. Please switch to Bluetooth mode and scan for your printer. Check Android Bluetooth settings to pair with "Virtual BT Printer" or your printer model first.',
  376 + confirmText: 'OK',
  377 + showCancel: false,
  378 + })
  379 + } else {
  380 + uni.showToast({ title: t('printers.testPrintFail') + ': ' + msg, icon: 'none' })
  381 + }
254 } 382 }
255 } 383 }
256 384
@@ -265,10 +393,22 @@ const goBack = () =&gt; { @@ -265,10 +393,22 @@ const goBack = () =&gt; {
265 393
266 onMounted(() => { 394 onMounted(() => {
267 refreshStatus() 395 refreshStatus()
  396 + // #ifdef APP-PLUS
  397 + loadPairedDevices()
  398 + // #endif
268 }) 399 })
269 400
270 onUnmounted(() => { 401 onUnmounted(() => {
271 if (isScanning.value) stopScan() 402 if (isScanning.value) stopScan()
  403 + if (bleListenerRegistered) {
  404 + bleListenerRegistered = false
  405 + try {
  406 + if (typeof uni.offBluetoothDeviceFound === 'function') {
  407 + uni.offBluetoothDeviceFound()
  408 + }
  409 + } catch (_) {}
  410 + }
  411 + uni.closeBluetoothAdapter({ complete: () => {} })
272 }) 412 })
273 </script> 413 </script>
274 414
@@ -316,4 +456,10 @@ onUnmounted(() =&gt; { @@ -316,4 +456,10 @@ onUnmounted(() =&gt; {
316 456
317 .empty-state { padding: 80rpx 0; text-align: center; } 457 .empty-state { padding: 80rpx 0; text-align: center; }
318 .empty-text { font-size: 28rpx; color: #9ca3af; } 458 .empty-text { font-size: 28rpx; color: #9ca3af; }
  459 +
  460 +.device-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 12rpx; flex-shrink: 0; }
  461 +.device-type-tag { display: inline-block; padding: 4rpx 16rpx; font-size: 20rpx; border-radius: 8rpx; font-weight: 500; }
  462 +.tag-ble { background: #eff6ff; color: #3b82f6; }
  463 +.tag-classic { background: #fef3c7; color: #d97706; }
  464 +.tag-dual { background: #ecfdf5; color: #059669; }
319 </style> 465 </style>
美国版/Food Labeling Management App UniApp/src/utils/print/printerConnection.ts
@@ -9,6 +9,9 @@ const STORAGE_BT_SERVICE_ID = &#39;btServiceId&#39; @@ -9,6 +9,9 @@ const STORAGE_BT_SERVICE_ID = &#39;btServiceId&#39;
9 const STORAGE_BT_CHARACTERISTIC_ID = 'btCharacteristicId' 9 const STORAGE_BT_CHARACTERISTIC_ID = 'btCharacteristicId'
10 const STORAGE_BT_DEVICE_TYPE = 'btDeviceType' // 'ble' | 'classic' 10 const STORAGE_BT_DEVICE_TYPE = 'btDeviceType' // 'ble' | 'classic'
11 const STORAGE_BLE_MTU = 'bleMTU' 11 const STORAGE_BLE_MTU = 'bleMTU'
  12 +const STORAGE_BUILTIN_PORT = 'builtinPort'
  13 +
  14 +const BUILTIN_PROBE_PORTS = [9100, 4000, 9000, 6000]
12 15
13 export type PrinterType = 'bluetooth' | 'builtin' 16 export type PrinterType = 'bluetooth' | 'builtin'
14 export type BtDeviceType = 'ble' | 'classic' 17 export type BtDeviceType = 'ble' | 'classic'
@@ -56,6 +59,7 @@ export function clearPrinter () { @@ -56,6 +59,7 @@ export function clearPrinter () {
56 uni.removeStorageSync(STORAGE_BT_CHARACTERISTIC_ID) 59 uni.removeStorageSync(STORAGE_BT_CHARACTERISTIC_ID)
57 uni.removeStorageSync(STORAGE_BT_DEVICE_TYPE) 60 uni.removeStorageSync(STORAGE_BT_DEVICE_TYPE)
58 uni.removeStorageSync(STORAGE_BLE_MTU) 61 uni.removeStorageSync(STORAGE_BLE_MTU)
  62 + uni.removeStorageSync(STORAGE_BUILTIN_PORT)
59 } 63 }
60 64
61 const BLE_MTU_DEFAULT = 20 65 const BLE_MTU_DEFAULT = 20
@@ -219,24 +223,46 @@ function sendViaBuiltin (data: number[]): Promise&lt;void&gt; { @@ -219,24 +223,46 @@ function sendViaBuiltin (data: number[]): Promise&lt;void&gt; {
219 const hexStr = Array.from(uint8) 223 const hexStr = Array.from(uint8)
220 .map(b => ('0' + (b & 0xff).toString(16)).slice(-2)) 224 .map(b => ('0' + (b & 0xff).toString(16)).slice(-2))
221 .join('') 225 .join('')
222 - return new Promise((resolve, reject) => {  
223 - moeTcp.connect({ ip: '127.0.0.1', port: 9100 }, (res: string) => {  
224 - try {  
225 - const r = typeof res === 'string' ? JSON.parse(res) : res  
226 - if (r.code !== 1) {  
227 - reject(new Error(r.msg || 'Built-in printer connection failed'))  
228 - return  
229 - }  
230 - moeTcp.sendHexStr({ message: hexStr })  
231 - setTimeout(() => { 226 +
  227 + const savedPort = parseInt(uni.getStorageSync(STORAGE_BUILTIN_PORT)) || 0
  228 + const ports = savedPort > 0
  229 + ? [savedPort, ...BUILTIN_PROBE_PORTS.filter(p => p !== savedPort)]
  230 + : [...BUILTIN_PROBE_PORTS]
  231 +
  232 + function tryPort (idx: number): Promise<void> {
  233 + if (idx >= ports.length) {
  234 + return Promise.reject(new Error(
  235 + 'Built-in printer: all ports failed (tried ' + BUILTIN_PROBE_PORTS.join(', ') +
  236 + '). Check if the printer service is running on this device.'
  237 + ))
  238 + }
  239 + const port = ports[idx]
  240 + return new Promise((resolve, reject) => {
  241 + console.log('[builtin] trying 127.0.0.1:' + port)
  242 + moeTcp.connect({ ip: '127.0.0.1', port }, (res: string) => {
  243 + try {
  244 + const r = typeof res === 'string' ? JSON.parse(res) : res
  245 + if (r.code !== 1) {
  246 + console.log('[builtin] port ' + port + ' failed: ' + (r.msg || ''))
  247 + try { moeTcp.disconnect() } catch (_) {}
  248 + tryPort(idx + 1).then(resolve).catch(reject)
  249 + return
  250 + }
  251 + console.log('[builtin] connected on port ' + port)
  252 + uni.setStorageSync(STORAGE_BUILTIN_PORT, String(port))
  253 + moeTcp.sendHexStr({ message: hexStr })
  254 + setTimeout(() => {
  255 + try { moeTcp.disconnect() } catch (_) {}
  256 + resolve()
  257 + }, 300)
  258 + } catch (e) {
232 try { moeTcp.disconnect() } catch (_) {} 259 try { moeTcp.disconnect() } catch (_) {}
233 - resolve()  
234 - }, 300)  
235 - } catch (e) {  
236 - reject(e)  
237 - } 260 + tryPort(idx + 1).then(resolve).catch(reject)
  261 + }
  262 + })
238 }) 263 })
239 - }) 264 + }
  265 + return tryPort(0)
240 } catch (e) { 266 } catch (e) {
241 return Promise.reject(e) 267 return Promise.reject(e)
242 } 268 }