chen.llin
2026-01-17 d2dbf327d56fd6d931fff23a75f0e5a38f2aa316
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
/**
 * 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