Commit 499e4a42bb09bbd9e63a16e718ee038a46a5f339
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.
Showing
6 changed files
with
282 additions
and
82 deletions
美国版/Food Labeling Management App UniApp/src/locales/en.ts
| ... | ... | @@ -53,6 +53,11 @@ export default { |
| 53 | 53 | connectFail: 'Connection failed', |
| 54 | 54 | bleNotAvailable: 'Bluetooth not available', |
| 55 | 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 | 61 | printer1: { name: 'Kitchen Printer #1', location: 'Main Kitchen' }, |
| 57 | 62 | printer2: { name: 'Kitchen Printer #2', location: 'Main Kitchen' }, |
| 58 | 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 | 53 | connectFail: '连接失败', |
| 54 | 54 | bleNotAvailable: '蓝牙未开启或不可用', |
| 55 | 55 | noDevices: '未发现设备', |
| 56 | + pairedDevices: '已配对设备', | |
| 57 | + noPairedDevices: '没有已配对的蓝牙设备', | |
| 58 | + classic: '经典', | |
| 59 | + ble: '低功耗', | |
| 60 | + dual: '双模', | |
| 56 | 61 | printer1: { name: '厨房打印机 #1', location: '主厨房' }, |
| 57 | 62 | printer2: { name: '厨房打印机 #2', location: '主厨房' }, |
| 58 | 63 | printer3: { name: '准备区打印机', location: '准备站' }, | ... | ... |
美国版/Food Labeling Management App UniApp/src/pages/labels/bluetooth.vue
| ... | ... | @@ -234,7 +234,7 @@ const initBluetooth = (): Promise<void> => { |
| 234 | 234 | }) |
| 235 | 235 | } |
| 236 | 236 | |
| 237 | -const startDiscovery = () => { | |
| 237 | +const startBleScan = () => { | |
| 238 | 238 | uni.startBluetoothDevicesDiscovery({ |
| 239 | 239 | allowDuplicatesKey: false, |
| 240 | 240 | success: () => { |
| ... | ... | @@ -242,29 +242,9 @@ const startDiscovery = () => { |
| 242 | 242 | errorMsg.value = '' |
| 243 | 243 | }, |
| 244 | 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 | 250 | const stopDiscovery = () => { |
| ... | ... | @@ -383,15 +363,41 @@ const handleScan = async () => { |
| 383 | 363 | errorMsg.value = '' |
| 384 | 364 | devices.value = [] |
| 385 | 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 | 396 | try { |
| 387 | 397 | if (!btAdapterReady.value) { |
| 388 | 398 | await initBluetooth() |
| 389 | 399 | } |
| 390 | - addPairedDevices() | |
| 391 | 400 | mergeCachedBleDevices() |
| 392 | - if (devices.value.length > 0) { | |
| 393 | - uni.showToast({ title: `Found ${devices.value.length} device(s)`, icon: 'none' }) | |
| 394 | - } | |
| 395 | 401 | await new Promise<void>((resolve) => { |
| 396 | 402 | uni.getLocation({ |
| 397 | 403 | type: 'wgs84', |
| ... | ... | @@ -399,13 +405,15 @@ const handleScan = async () => { |
| 399 | 405 | fail: () => resolve(), |
| 400 | 406 | }) |
| 401 | 407 | }) |
| 402 | - startDiscovery() | |
| 408 | + startBleScan() | |
| 403 | 409 | uni.showToast({ title: 'Scanning...', icon: 'none' }) |
| 404 | 410 | setTimeout(() => { |
| 405 | 411 | if (isScanning.value) stopDiscovery() |
| 406 | 412 | }, 20000) |
| 407 | 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(() => { |
| 563 | 571 | |
| 564 | 572 | onUnmounted(() => { |
| 565 | 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 | 586 | </script> |
| 569 | 587 | ... | ... |
美国版/Food Labeling Management App UniApp/src/pages/labels/preview.vue
| ... | ... | @@ -265,8 +265,8 @@ const handlePrint = async () => { |
| 265 | 265 | const msg = (e && e.message) ? e.message : 'Print failed' |
| 266 | 266 | if (msg === 'BUILTIN_PLUGIN_NOT_FOUND' || (msg && msg.indexOf('Built-in printer') !== -1)) { |
| 267 | 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 | 270 | confirmText: 'Printer Settings', |
| 271 | 271 | cancelText: 'Cancel', |
| 272 | 272 | success: (res) => { | ... | ... |
美国版/Food Labeling Management App UniApp/src/pages/more/printers.vue
| ... | ... | @@ -45,6 +45,29 @@ |
| 45 | 45 | <button class="btn-connect">{{ t('printers.connect') }}</button> |
| 46 | 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 | 71 | <!-- Bluetooth Scanning Area --> |
| 49 | 72 | <view class="section-header"> |
| 50 | 73 | <text class="section-title">{{ t('printers.nearby') }}</text> |
| ... | ... | @@ -62,7 +85,10 @@ |
| 62 | 85 | <text class="printer-name">{{ d.name || 'Unknown Device' }}</text> |
| 63 | 86 | <text class="printer-loc">{{ d.deviceId }}</text> |
| 64 | 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 | 92 | </view> |
| 67 | 93 | |
| 68 | 94 | <view v-if="devices.length === 0 && !isScanning" class="empty-state"> |
| ... | ... | @@ -90,51 +116,101 @@ import { |
| 90 | 116 | } from '../../utils/print/printerConnection' |
| 91 | 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 | 123 | const { t } = useI18n() |
| 94 | 124 | const isMenuOpen = ref(false) |
| 95 | 125 | |
| 96 | -// 状态管理 | |
| 97 | 126 | const currentType = ref(getPrinterType()) |
| 98 | 127 | const currentBt = ref(getBluetoothConnection()) |
| 99 | 128 | const isScanning = ref(false) |
| 100 | 129 | const devices = ref<any[]>([]) |
| 130 | +const pairedDevices = ref<any[]>([]) | |
| 101 | 131 | const isConnecting = ref(false) |
| 132 | +let bleListenerRegistered = false | |
| 102 | 133 | |
| 103 | -// Refresh current state | |
| 104 | 134 | const refreshStatus = () => { |
| 105 | 135 | currentType.value = getPrinterType() |
| 106 | 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 | 167 | const startScan = () => { |
| 111 | 168 | if (isScanning.value) return |
| 112 | - | |
| 169 | + | |
| 113 | 170 | devices.value = [] |
| 114 | 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 | 189 | uni.openBluetoothAdapter({ |
| 117 | 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 | 201 | uni.startBluetoothDevicesDiscovery({ |
| 119 | 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 | 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 | 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 = () => { |
| 142 | 218 | const stopScan = () => { |
| 143 | 219 | isScanning.value = false |
| 144 | 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 | 233 | const connectBt = (device: any) => { |
| 152 | 234 | if (isConnecting.value) return |
| 153 | 235 | isConnecting.value = true |
| 154 | 236 | stopScan() |
| 155 | - | |
| 237 | + | |
| 156 | 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 | 272 | uni.createBLEConnection({ |
| 159 | 273 | deviceId: device.deviceId, |
| 160 | 274 | success: () => { |
| 161 | 275 | uni.getBLEDeviceServices({ |
| 162 | 276 | deviceId: device.deviceId, |
| 163 | 277 | success: (res) => { |
| 164 | - // 这里简化处理:寻找第一个包含写权限的特征值 | |
| 165 | - // 实际项目中可能需要根据特定 UUID 匹配,官方示例中是遍历寻找 | |
| 166 | 278 | findWriteCharacteristic(device.deviceId, res.services, device.name) |
| 167 | 279 | }, |
| 168 | - fail: (err) => { | |
| 280 | + fail: () => { | |
| 169 | 281 | isConnecting.value = false |
| 170 | 282 | uni.hideLoading() |
| 171 | 283 | uni.showToast({ title: t('printers.connectFail'), icon: 'none' }) |
| 172 | 284 | } |
| 173 | 285 | }) |
| 174 | 286 | }, |
| 175 | - fail: (err) => { | |
| 287 | + fail: () => { | |
| 176 | 288 | isConnecting.value = false |
| 177 | 289 | uni.hideLoading() |
| 178 | 290 | uni.showToast({ title: t('printers.connectFail'), icon: 'none' }) |
| ... | ... | @@ -183,7 +295,7 @@ const connectBt = (device: any) => { |
| 183 | 295 | const findWriteCharacteristic = (deviceId: string, services: any[], deviceName: string) => { |
| 184 | 296 | let found = false |
| 185 | 297 | let serviceIdx = 0 |
| 186 | - | |
| 298 | + | |
| 187 | 299 | const nextService = () => { |
| 188 | 300 | if (serviceIdx >= services.length || found) { |
| 189 | 301 | if (!found) { |
| ... | ... | @@ -193,7 +305,7 @@ const findWriteCharacteristic = (deviceId: string, services: any[], deviceName: |
| 193 | 305 | } |
| 194 | 306 | return |
| 195 | 307 | } |
| 196 | - | |
| 308 | + | |
| 197 | 309 | const service = services[serviceIdx++] |
| 198 | 310 | uni.getBLEDeviceCharacteristics({ |
| 199 | 311 | deviceId, |
| ... | ... | @@ -221,26 +333,32 @@ const findWriteCharacteristic = (deviceId: string, services: any[], deviceName: |
| 221 | 333 | fail: () => nextService() |
| 222 | 334 | }) |
| 223 | 335 | } |
| 224 | - | |
| 336 | + | |
| 225 | 337 | nextService() |
| 226 | 338 | } |
| 227 | 339 | |
| 228 | -// 连接内置打印机 | |
| 229 | 340 | const connectBuiltin = () => { |
| 230 | 341 | setBuiltinPrinter() |
| 231 | 342 | currentType.value = 'builtin' |
| 232 | 343 | uni.showToast({ title: t('printers.connectSuccess') }) |
| 233 | 344 | } |
| 234 | 345 | |
| 235 | -// 断开连接 | |
| 236 | 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 | 356 | clearPrinter() |
| 238 | 357 | currentType.value = '' |
| 239 | 358 | currentBt.value = null |
| 240 | 359 | uni.showToast({ title: t('printers.disconnected') }) |
| 241 | 360 | } |
| 242 | 361 | |
| 243 | -// 测试打印 | |
| 244 | 362 | const doTestPrint = async () => { |
| 245 | 363 | try { |
| 246 | 364 | const data = buildTestTscLabel() |
| ... | ... | @@ -250,7 +368,17 @@ const doTestPrint = async () => { |
| 250 | 368 | uni.showToast({ title: t('printers.testPrintSuccess') }) |
| 251 | 369 | } catch (e: any) { |
| 252 | 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 = () => { |
| 265 | 393 | |
| 266 | 394 | onMounted(() => { |
| 267 | 395 | refreshStatus() |
| 396 | + // #ifdef APP-PLUS | |
| 397 | + loadPairedDevices() | |
| 398 | + // #endif | |
| 268 | 399 | }) |
| 269 | 400 | |
| 270 | 401 | onUnmounted(() => { |
| 271 | 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 | 413 | </script> |
| 274 | 414 | |
| ... | ... | @@ -316,4 +456,10 @@ onUnmounted(() => { |
| 316 | 456 | |
| 317 | 457 | .empty-state { padding: 80rpx 0; text-align: center; } |
| 318 | 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 | 465 | </style> | ... | ... |
美国版/Food Labeling Management App UniApp/src/utils/print/printerConnection.ts
| ... | ... | @@ -9,6 +9,9 @@ const STORAGE_BT_SERVICE_ID = 'btServiceId' |
| 9 | 9 | const STORAGE_BT_CHARACTERISTIC_ID = 'btCharacteristicId' |
| 10 | 10 | const STORAGE_BT_DEVICE_TYPE = 'btDeviceType' // 'ble' | 'classic' |
| 11 | 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 | 16 | export type PrinterType = 'bluetooth' | 'builtin' |
| 14 | 17 | export type BtDeviceType = 'ble' | 'classic' |
| ... | ... | @@ -56,6 +59,7 @@ export function clearPrinter () { |
| 56 | 59 | uni.removeStorageSync(STORAGE_BT_CHARACTERISTIC_ID) |
| 57 | 60 | uni.removeStorageSync(STORAGE_BT_DEVICE_TYPE) |
| 58 | 61 | uni.removeStorageSync(STORAGE_BLE_MTU) |
| 62 | + uni.removeStorageSync(STORAGE_BUILTIN_PORT) | |
| 59 | 63 | } |
| 60 | 64 | |
| 61 | 65 | const BLE_MTU_DEFAULT = 20 |
| ... | ... | @@ -219,24 +223,46 @@ function sendViaBuiltin (data: number[]): Promise<void> { |
| 219 | 223 | const hexStr = Array.from(uint8) |
| 220 | 224 | .map(b => ('0' + (b & 0xff).toString(16)).slice(-2)) |
| 221 | 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 | 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 | 266 | } catch (e) { |
| 241 | 267 | return Promise.reject(e) |
| 242 | 268 | } | ... | ... |