/** * RFID输入框自动填入助手 * 全局监听RFID扫描按键(189、190、191),自动读取标签并填入当前焦点输入框 */ // #ifdef APP-PLUS let uhfModel = null; let globalEvent = null; let modal = null; let isPowerOn = false; let isScanning = false; // 是否正在扫描中 let scanKeyHandler = null; // 按键监听器引用 /** * 初始化RFID助手(只初始化插件引用和事件监听,不初始化RFID模块) * RFID模块由App.vue全局初始化,这里只负责监听按键和读取标签 * 注意:即使初始化失败,也不抛出异常,确保不影响页面使用 */ function initRFIDForInput() { try { uhfModel = uni.requireNativePlugin("iData-UHFPlugin-UHFModule"); globalEvent = uni.requireNativePlugin('globalEvent'); modal = uni.requireNativePlugin('modal'); if (!uhfModel) { console.warn('[RFIDInputHelper] ⚠ RFID插件未找到,RFID扫描功能不可用,但页面可正常使用'); return false; } // 监听上电事件(监听全局上电状态,RFID模块由App.vue初始化) try { globalEvent.addEventListener('POWEREvent', function(e) { console.log('[RFIDInputHelper] POWEREvent:', JSON.stringify(e)); if (e && e.status === 'connected') { isPowerOn = true; console.log('[RFIDInputHelper] ✓ RFID模块已上电'); } else { isPowerOn = false; console.log('[RFIDInputHelper] ✗ RFID模块未上电'); } }); } catch (eventError) { console.warn('[RFIDInputHelper] ⚠ 添加POWEREvent监听失败:', eventError); console.warn('[RFIDInputHelper] ⚠ RFID扫描功能可能不可用,但页面可正常使用'); } // 注意:RFID模块由App.vue在onLaunch时全局初始化 // 这里只负责监听按键和读取标签,不初始化RFID模块 return true; } catch (error) { console.error('[RFIDInputHelper] ⚠ 初始化失败:', error); console.warn('[RFIDInputHelper] ⚠ RFID扫描功能不可用,但页面可正常使用'); return false; } } /** * 检查当前页面是否是测试页面 */ function isTestPage() { try { const pages = getCurrentPages(); if (pages.length > 0) { const currentPage = pages[pages.length - 1]; const route = currentPage.route || ''; // 如果是测试页面,不启用全局RFID扫描 return route.includes('rfid/uhftest') || route.includes('rfid/uhftest2'); } return false; } catch (error) { console.error('[RFIDInputHelper] 检查页面失败:', error); return false; } } /** * 播放提示音 * @param {String} type - 提示音类型:'success'=成功(滴1声),'error'=错误(滴2声),'warning'=警告(滴3声),'short'=短促(快速滴1声) */ function playBeepSound(type = 'success') { // #ifdef APP-PLUS try { // 使用设备蜂鸣器播放提示音 if (typeof plus !== 'undefined' && plus.device && plus.device.beep) { let beepCount = 1; // 默认播放1次 // 根据类型设置不同的提示音 switch(type) { case 'success': beepCount = 1; // 成功:滴1声 break; case 'error': beepCount = 2; // 错误:滴2声 break; case 'warning': beepCount = 3; // 警告:滴3声 break; case 'short': beepCount = 1; // 短促:快速滴1声 break; default: beepCount = 1; } plus.device.beep(beepCount); // 播放指定次数 // 如果是错误或警告,可以添加震动反馈 if (type === 'error' || type === 'warning') { if (typeof plus !== 'undefined' && plus.device && plus.device.vibrate) { setTimeout(() => { plus.device.vibrate(200); // 震动200ms }, 100); } } } else { // 如果设备不支持beep,尝试使用vibrate(震动)作为替代 if (typeof plus !== 'undefined' && plus.device && plus.device.vibrate) { let vibrateDuration = 100; // 默认震动100ms if (type === 'error') { vibrateDuration = 300; // 错误:震动300ms } else if (type === 'warning') { vibrateDuration = 200; // 警告:震动200ms } plus.device.vibrate(vibrateDuration); } } } catch (error) { console.warn('[RFIDInputHelper] 播放提示音失败:', error); } // #endif } /** * 读取RFID标签(单标签读,无过滤) */ function readRFIDTag() { try { if (!uhfModel) { // 如果模块未初始化,尝试初始化 try { uhfModel = uni.requireNativePlugin("iData-UHFPlugin-UHFModule"); } catch (e) { console.warn('[RFIDInputHelper] ⚠ RFID模块未找到:', e); } if (!uhfModel) { console.warn('[RFIDInputHelper] RFID模块未找到'); if (modal) { modal.toast({ message: 'RFID模块未找到', duration: 1.5 }); } return; } } // 检查RFID是否已上电(RFID模块由App.vue全局初始化) if (!isPowerOn) { console.warn('[RFIDInputHelper] RFID未上电,无法扫描'); if (modal) { modal.toast({ message: 'RFID正在上电,请稍候...', duration: 1.5 }); } return; } } catch (error) { console.error('[RFIDInputHelper] ⚠ 读取RFID标签前检查失败:', error); console.warn('[RFIDInputHelper] ⚠ RFID扫描功能不可用,但页面可正常使用'); if (modal) { modal.toast({ message: 'RFID扫描功能不可用', duration: 1.5 }); } return; } if (isScanning) { console.log('[RFIDInputHelper] 正在扫描中,跳过'); return; } isScanning = true; console.log('[RFIDInputHelper] 开始读取RFID标签...'); /** * readBank:0->RESERVED,1->EPC,2->TID,3->USR * startBlock:读取的起始地址(Word类型) * len:读取的数据长度(Word类型,大于0) * pwd:访问密码,默认密码为:00000000 * 注意:Word类型,一个长度表示标签存储4位字符 * len=1表示读取4位,len=2表示读取8位,len=8表示读取32位 * 使用len=1读取4位数据(如果标签是4位) */ const readLen = 8; // 读取长度:1个Word = 4个字符 uhfModel.readTagWithoutFilter(1, 2, readLen, "00000000", (ret) => { isScanning = false; console.log('[RFIDInputHelper] readTagWithoutFilter result:', ret); if (ret && typeof ret === 'string') { // 解析返回结果,提取EPC(不追加,不截断,使用实际长度) // 返回格式可能是:success EPC:XXXXXXXX 或类似格式 let epc = ''; if (ret.includes('success') || ret.includes('EPC')) { // 尝试提取EPC值(提取完整的16进制字符串,不限制长度) const epcMatch = ret.match(/EPC[:\s]+([A-F0-9]+)/i); if (epcMatch && epcMatch[1]) { epc = epcMatch[1]; // 使用实际长度,不追加 } else { // 如果没有匹配到,尝试提取16进制字符串(不限制最小长度) const hexMatch = ret.match(/([A-F0-9]+)/i); if (hexMatch && hexMatch[1]) { epc = hexMatch[1]; // 使用实际长度,不追加 } } } if (epc) { // 根据读取长度截取数据(len=1表示4位,len=2表示8位) const maxLength = readLen * 4; // Word长度 × 4 = 字符数 if (epc.length > maxLength) { console.log(`[RFIDInputHelper] 数据过长(${epc.length}位),截取前${maxLength}位`); epc = epc.substring(0, maxLength); } // 去除前导0(如果插件返回了固定长度的数据,前面可能有0填充) // 但保留至少1位(如果全部是0,保留一个0) const originalLength = epc.length; epc = epc.replace(/^0+/, '') || '0'; if (originalLength !== epc.length) { console.log('[RFIDInputHelper] 去除前导0: 原始长度=' + originalLength + ', 去除后长度=' + epc.length); } console.log('[RFIDInputHelper] ✓ 读取到标签EPC (长度' + epc.length + '位):', epc); // 播放提示音(滴一声) // playBeepSound(); // 已注释,后续自己决定是否启用 // 自动填入当前焦点输入框(不追加,使用实际长度) fillCurrentInput(epc); modal.toast({ message: '已读取标签', duration: 1.5 }); } else { console.warn('[RFIDInputHelper] 无法解析EPC,返回结果:', ret); // 如果返回的是失败消息,直接显示 if (ret.includes('fail') || ret.includes('失败')) { modal.toast({ message: '读取失败,请重试', duration: 1.5 }); } else { // 尝试直接使用返回的字符串作为EPC(不限制最小长度) const cleanRet = ret.trim(); // 移除可能的"success"等前缀,只保留16进制字符 let hexOnly = cleanRet.replace(/[^A-F0-9]/gi, ''); if (hexOnly.length > 0) { // 去除前导0(如果插件返回了固定长度的数据,前面可能有0填充) const originalLength = hexOnly.length; hexOnly = hexOnly.replace(/^0+/, '') || '0'; if (originalLength !== hexOnly.length) { console.log('[RFIDInputHelper] 去除前导0: 原始长度=' + originalLength + ', 去除后长度=' + hexOnly.length); } // 播放提示音(滴一声) // playBeepSound(); // 已注释,后续自己决定是否启用 fillCurrentInput(hexOnly); // 使用实际长度,不追加 modal.toast({ message: '已读取标签', duration: 1.5 }); } else { modal.toast({ message: '读取失败,请重试', duration: 1.5 }); } } } } else if (ret && typeof ret === 'object') { // 如果返回的是对象格式 if (ret.code === 'success' && ret.data) { let epc = ''; if (typeof ret.data === 'string') { // 如果是字符串,提取16进制部分(不追加,不截断) epc = ret.data.trim().replace(/[^A-F0-9]/gi, ''); } else if (ret.data.epc) { epc = String(ret.data.epc).trim().replace(/[^A-F0-9]/gi, ''); } else { epc = String(ret.data).trim().replace(/[^A-F0-9]/gi, ''); } // 根据读取长度截取数据(len=1表示4位,len=2表示8位) if (epc) { const maxLength = readLen * 4; // Word长度 × 4 = 字符数 if (epc.length > maxLength) { console.log(`[RFIDInputHelper] 数据过长(${epc.length}位),截取前${maxLength}位`); epc = epc.substring(0, maxLength); } // 去除前导0(如果插件返回了固定长度的数据,前面可能有0填充) // 但保留至少1位(如果全部是0,保留一个0) const originalLength = epc.length; epc = epc.replace(/^0+/, '') || '0'; if (originalLength !== epc.length) { console.log('[RFIDInputHelper] 去除前导0: 原始长度=' + originalLength + ', 去除后长度=' + epc.length); } console.log('[RFIDInputHelper] ✓ 读取到标签EPC (长度' + epc.length + '位):', epc); // 播放提示音(滴一声) // playBeepSound(); // 已注释,后续自己决定是否启用 fillCurrentInput(epc); // 使用实际长度,不追加 modal.toast({ message: '已读取标签', duration: 1.5 }); } else { modal.toast({ message: '读取失败,请重试', duration: 1.5 }); } } else { modal.toast({ message: '读取失败,请重试', duration: 1.5 }); } } else { console.warn('[RFIDInputHelper] 读取失败:', ret); modal.toast({ message: '读取失败,请重试', duration: 1.5 }); } }); } /** * 自动填入当前焦点输入框 */ function fillCurrentInput(epc) { try { console.log('[RFIDInputHelper] 尝试填入EPC到输入框:', epc); // 获取当前页面实例 const pages = getCurrentPages(); if (pages.length === 0) { console.warn('[RFIDInputHelper] 没有页面实例'); return; } const currentPage = pages[pages.length - 1]; if (!currentPage || !currentPage.$vm) { console.warn('[RFIDInputHelper] 页面实例无效'); return; } const vm = currentPage.$vm; console.log('[RFIDInputHelper] 当前页面:', currentPage.route); // 定义输入框字段和对应的焦点状态字段(预定义的常见字段) // 格式:{ 字段名: 焦点状态字段名 } const predefinedFieldMap = [ { field: 'barcode', focusField: 'barcodeFocus' }, { field: 'matnr', focusField: 'matFocus' }, { field: 'sourceSite', focusField: 'sourceSiteFocus' }, { field: 'orderNo', focusField: 'orderNoFocus' }, { field: 'targetSite', focusField: 'targetSiteFocus' }, { field: 'locNo', focusField: 'locNoFocus' }, { field: 'batch', focusField: 'batchFocus' }, { field: 'userName', focusField: 'userNameFocus' }, { field: 'password', focusField: 'passwordFocus' } ]; // 自动检测所有可能的输入框字段(从vm.$data中查找所有字符串类型的属性) const autoDetectedFields = []; if (vm.$data) { for (let key in vm.$data) { // 跳过以$开头的Vue内部属性,跳过函数,跳过focus字段本身 if (key.startsWith('$') || typeof vm.$data[key] === 'function' || key.endsWith('Focus')) { continue; } // 如果是字符串类型,认为是可能的输入框字段 if (typeof vm.$data[key] === 'string' || vm.$data[key] === null || vm.$data[key] === undefined) { // 检查是否有对应的focusField(字段名 + Focus) const focusFieldName = key + 'Focus'; if (vm.$data[focusFieldName] !== undefined) { autoDetectedFields.push({ field: key, focusField: focusFieldName }); } } } } // 合并预定义字段和自动检测的字段(去重) const allFieldsMap = [...predefinedFieldMap]; for (let autoField of autoDetectedFields) { // 检查是否已存在 const exists = allFieldsMap.some(item => item.field === autoField.field); if (!exists) { allFieldsMap.push(autoField); } } // 调试:打印所有输入框的焦点状态 console.log('[RFIDInputHelper] ========== 检查输入框焦点状态 =========='); for (let item of allFieldsMap) { if (vm[item.field] !== undefined) { const focusValue = vm[item.focusField]; console.log(`[RFIDInputHelper] ${item.field}: focus=${focusValue}, value="${vm[item.field]}"`); } } // 只查找有焦点的输入框(光标所在的输入框) let focusedField = null; for (let item of allFieldsMap) { if (vm[item.field] !== undefined) { // 检查是否有焦点状态字段,并且焦点为true const focusValue = vm[item.focusField]; console.log(`[RFIDInputHelper] 检查 ${item.field}: focusField=${item.focusField}, focusValue=${focusValue}, type=${typeof focusValue}`); if (focusValue === true) { focusedField = item.field; console.log(`[RFIDInputHelper] ✓✓✓ 找到有焦点的输入框: ${item.field} (focus=${focusValue})`); break; } } } // 只填入有焦点的输入框,如果没有焦点则不填入 if (focusedField) { // 根据字段类型截断RFID数据长度 let finalEpc = epc; const fieldLengthMap = { 'barcode': 8, // 托盘码:保留前8位 'matnr': 16, // 物料码/物料号:保留前16位 'sourceSite': 9, // 暂存位:保留前9位 'locNo': 7 // 库位号:保留前7位 }; if (fieldLengthMap[focusedField]) { const maxLength = fieldLengthMap[focusedField]; if (finalEpc.length > maxLength) { console.log(`[RFIDInputHelper] ${focusedField}字段数据长度(${finalEpc.length}位)超过限制(${maxLength}位),截取前${maxLength}位`); finalEpc = finalEpc.substring(0, maxLength); } } console.log(`[RFIDInputHelper] 填入有焦点的输入框 ${focusedField}:`, finalEpc); vm[focusedField] = finalEpc; // 触发input事件,确保页面逻辑能响应 if (vm.$nextTick) { vm.$nextTick(() => { // 如果页面有对应的input处理方法,可以手动触发 if (focusedField === 'barcode' && typeof vm.barcodeInput === 'function') { vm.barcodeInput(); } else if (focusedField === 'matnr' && typeof vm.findMat === 'function') { vm.findMat(); } }); } } else { console.warn('[RFIDInputHelper] ✗ 未找到有焦点的输入框,不填入任何输入框'); console.warn('[RFIDInputHelper] 请先点击输入框获得焦点,然后再按扫码按键'); modal.toast({ message: '请先点击输入框获得焦点', duration: 1.5 }); } } catch (error) { console.error('[RFIDInputHelper] 自动填入失败:', error); } } /** * 监听扫码按键 */ function setupScanKeyListener() { if (scanKeyHandler) { console.log('[RFIDInputHelper] 按键监听器已存在'); return; } if (typeof plus === 'undefined' || !plus.key) { console.warn('[RFIDInputHelper] plus对象未初始化,无法添加按键监听'); return; } scanKeyHandler = function(e) { const keyCode = e.keyCode; // 扫码按键:189、190、191 if (keyCode === 189 || keyCode === 190 || keyCode === 191) { // 如果是测试页面,不处理(让测试页面自己处理) if (isTestPage()) { console.log('[RFIDInputHelper] 测试页面,跳过全局RFID扫描'); return; } console.log('[RFIDInputHelper] 扫码按键按下(键值' + keyCode + '),触发RFID扫描'); readRFIDTag(); } }; plus.key.addEventListener('keydown', scanKeyHandler); console.log('[RFIDInputHelper] ✓ 按键监听器已添加'); } /** * 移除按键监听 */ function removeScanKeyListener() { if (scanKeyHandler && typeof plus !== 'undefined' && plus.key) { plus.key.removeEventListener('keydown', scanKeyHandler); scanKeyHandler = null; console.log('[RFIDInputHelper] ✓ 按键监听器已移除'); } } /** * 关闭RFID模块(注意:RFID模块由App.vue管理,这里不关闭) */ function closeRFID() { // RFID模块由App.vue在onExit时关闭,这里不需要关闭 console.log('[RFIDInputHelper] RFID模块由App.vue管理,不在此处关闭'); } /** * 启动全局RFID输入助手 */ export function startRFIDInputHelper() { // #ifdef APP-PLUS console.log('[RFIDInputHelper] 启动RFID输入助手...'); // 初始化RFID模块 if (initRFIDForInput()) { // 延迟添加按键监听,确保初始化完成 setTimeout(() => { setupScanKeyListener(); }, 500); } // #endif } /** * 停止全局RFID输入助手 */ export function stopRFIDInputHelper() { // #ifdef APP-PLUS console.log('[RFIDInputHelper] 停止RFID输入助手...'); removeScanKeyListener(); // 注意:不关闭RFID模块,因为测试页面可能在使用 // closeRFID(); // #endif } // #endif