Files
pda-cli/src/common/scanMixin.js
T
xuan 36c0f9a3a0 feat(scan): 添加手机扫码功能支持
- 在 FabricOutScanBar 组件中添加手机扫码图标和事件处理
- 更新工作台页面的扫码逻辑以支持手机扫码回退
- 在销售拣货详情页添加手机扫码按钮和处理方法
- 重构扫码设置页面以支持扫码方式选择
- 添加手机扫码配置和判断逻辑到扫码配置模块
- 扩展扫码混入模块以支持手机扫码模式和设备检测
- 在多个业务页面中集成手机扫码功能和界面元素
- 更新扫码配置存储和读取逻辑以兼容新配置结构
2026-06-23 17:08:17 +08:00

363 lines
10 KiB
JavaScript

// common/scanMixin.js
// 多厂商PDA扫码器兼容方案 - 策略模式 + 配置表驱动
import {
getScanConfig,
hasShownNoConfigHint,
isPhoneScanConfig,
markNoConfigHintShown,
normalizeConfig,
PHONE_SCAN_CONFIG,
} from '@/common/scanConfig.js';
const PDA_DEVICE_KEYWORDS = [
'newland', 'nls', 'sunmi', 'urovo', 'zebra', 'honeywell', 'idata', 'seuic',
];
export default {
data() {
return {
scanReceivers: [],
isPageActive: false,
registeredBrands: [],
remoteScannerConfigs: [],
usePhoneScanFallback: false,
_scanCallback: null,
}
},
methods: {
getScannerConfigs() {
return [
{
brand: 'newland',
name: '东集',
action: 'com.android.server.scannerservice.broadcast',
dataKey: 'scannerdata',
needSetup: true,
setupAction: 'com.android.scanner.service_settings',
setupParams: {
action_barcode_broadcast: 'com.android.server.scannerservice.broadcast',
key_barcode_broadcast: 'scannerdata',
endchar: 'ENTER'
}
},
{
brand: 'sunmi',
name: '商米',
action: 'com.sunmi.scanner.ACTION_DATA_CODE_RECEIVED',
dataKey: 'data',
needSetup: false
},
{ ...PHONE_SCAN_CONFIG },
];
},
getPhoneScanConfig() {
return { ...PHONE_SCAN_CONFIG };
},
isPdaDevice() {
// #ifdef APP-PLUS
try {
const Build = plus.android.importClass('android.os.Build');
const manufacturer = String(Build.MANUFACTURER || '').toLowerCase();
const model = String(Build.MODEL || '').toLowerCase();
return PDA_DEVICE_KEYWORDS.some((keyword) => {
return manufacturer.includes(keyword) || model.includes(keyword);
});
} catch (error) {
console.log('检测设备类型失败', error);
return false;
}
// #endif
return false;
},
shouldUsePhoneScan() {
const builtinConfigs = this.getScannerConfigs();
const activeConfig = getScanConfig(builtinConfigs);
if (activeConfig && isPhoneScanConfig(activeConfig)) {
return true;
}
if (!activeConfig && !this.isPdaDevice()) {
return true;
}
return false;
},
getAllScannerConfigs() {
const builtinConfigs = this.getScannerConfigs();
if (!this.remoteScannerConfigs.length) {
return builtinConfigs;
}
const configMap = new Map();
builtinConfigs.forEach((item) => configMap.set(item.brand, item));
this.remoteScannerConfigs.forEach((item) => configMap.set(item.brand, item));
if (!configMap.has(PHONE_SCAN_CONFIG.brand)) {
configMap.set(PHONE_SCAN_CONFIG.brand, { ...PHONE_SCAN_CONFIG });
}
return Array.from(configMap.values());
},
getActiveScanConfig() {
return getScanConfig(this.getScannerConfigs());
},
getDefaultScanConfig() {
const configs = this.getAllScannerConfigs().filter((item) => !isPhoneScanConfig(item));
return configs.length ? configs[0] : null;
},
openPhoneScan(options = {}) {
const callback = options.callback || this._scanCallback;
// #ifdef APP-PLUS || MP-WEIXIN
uni.scanCode({
scanType: options.scanType || ['qrCode', 'barCode'],
success: (res) => {
this.handleScanResult(res.result, callback);
},
fail: (err) => {
console.error('手机扫码失败:', err);
if (options.fail) {
options.fail(err);
return;
}
uni.showToast({
title: '扫码失败',
icon: 'none',
});
},
});
return;
// #endif
// #ifdef H5
uni.showToast({
title: 'H5环境不支持扫码功能',
icon: 'none',
});
// #endif
},
triggerScan() {
if (this.usePhoneScanFallback) {
this.openPhoneScan();
}
},
registerScanBroadcast(scanCallback) {
this._scanCallback = scanCallback;
// #ifndef APP-PLUS
this.usePhoneScanFallback = true;
return true;
// #endif
// #ifdef APP-PLUS
if (this.shouldUsePhoneScan()) {
this.usePhoneScanFallback = true;
console.log('使用手机扫码模式');
return true;
}
this.usePhoneScanFallback = false;
const activeConfig = this.getActiveScanConfig();
if (activeConfig && !isPhoneScanConfig(activeConfig)) {
return this.registerScanBroadcastByConfig(activeConfig, scanCallback);
}
if (!hasShownNoConfigHint()) {
markNoConfigHintShown();
uni.showToast({
title: '请先在「我的-扫描设置」配置扫码枪',
icon: 'none',
duration: 2500,
});
}
const defaultConfig = this.getDefaultScanConfig();
if (!defaultConfig) {
console.error('未找到默认可用的扫码枪配置');
return false;
}
return this.registerScanBroadcastByConfig(defaultConfig, scanCallback);
// #endif
},
registerScanBroadcastByBrand(brand, scanCallback) {
// #ifdef APP-PLUS
const configs = this.getAllScannerConfigs();
const config = configs.find(c => c.brand === brand);
if (!config) {
console.error(`未找到厂商配置: ${brand}`);
return false;
}
if (isPhoneScanConfig(config)) {
this._scanCallback = scanCallback;
this.usePhoneScanFallback = true;
return true;
}
return this.registerScanBroadcastByConfig(config, scanCallback);
// #endif
},
registerScanBroadcastByConfig(config, scanCallback) {
// #ifdef APP-PLUS
const normalizedConfig = normalizeConfig(config, this.getScannerConfigs());
if (!normalizedConfig) {
console.error('扫码枪配置无效');
return false;
}
if (isPhoneScanConfig(normalizedConfig)) {
this._scanCallback = scanCallback;
this.usePhoneScanFallback = true;
return true;
}
try {
this.registerSingleBroadcast(normalizedConfig, scanCallback);
this.registeredBrands.push(normalizedConfig.brand);
this.usePhoneScanFallback = false;
console.log(`${normalizedConfig.name}扫码广播注册成功`);
return true;
} catch (error) {
console.error(`${normalizedConfig.name}扫码广播注册失败:`, error);
return false;
}
// #endif
},
registerSingleBroadcast(config, scanCallback) {
const main = plus.android.runtimeMainActivity();
if (config.needSetup && config.setupAction) {
try {
const Intent = plus.android.importClass('android.content.Intent');
const setupIntent = new Intent(config.setupAction);
Object.keys(config.setupParams || {}).forEach(key => {
setupIntent.putExtra(key, config.setupParams[key]);
});
main.sendBroadcast(setupIntent);
console.log(`${config.name}扫码配置完成`);
} catch (error) {
console.error(`${config.name}扫码配置失败:`, error);
}
}
const IntentFilter = plus.android.importClass('android.content.IntentFilter');
const filter = new IntentFilter();
filter.addAction(config.action);
const self = this;
const receiver = plus.android.implements(
'io.dcloud.feature.internal.reflect.BroadcastReceiver',
{
onReceive: (context, intent) => {
console.log(`${config.name}扫码广播接收,isPageActive:`, self.isPageActive);
if (!self.isPageActive) return;
try {
plus.android.importClass('android.content.Intent');
const scanResult = intent.getStringExtra(config.dataKey);
console.log(`${config.name}扫码结果:`, scanResult);
if (scanResult) {
self.handleScanResult(scanResult, scanCallback);
}
} catch (error) {
console.error(`处理${config.name}广播数据出错:`, error);
}
}
}
);
main.registerReceiver(receiver, filter);
this.scanReceivers.push({
brand: config.brand,
receiver: receiver
});
},
handleScanResult(scanResult, scanCallback) {
try {
let cleanCode = scanResult.replace(/\s+/g, '').replace(/[\r\n]/g, '');
console.log("扫码结果:", cleanCode);
if (scanCallback) {
scanCallback(cleanCode);
}
} catch (error) {
console.error("处理扫码结果时出错:", error);
}
},
unregisterScanBroadcast() {
// #ifdef APP-PLUS
if (this.scanReceivers.length === 0) return;
try {
const main = plus.android.runtimeMainActivity();
this.scanReceivers.forEach(item => {
try {
main.unregisterReceiver(item.receiver);
console.log(`${item.brand}扫码广播注销成功`);
} catch (err) {
console.error(`${item.brand}扫码广播注销失败:`, err);
}
});
this.scanReceivers = [];
this.registeredBrands = [];
console.log('所有扫码广播注销完成');
} catch (err) {
console.error('注销扫码广播失败:', err);
}
// #endif
},
handleGoodsScan(scanCode, successCallback, errorCallback) {
const trimmedCode = scanCode ? scanCode.toString().trim() : "";
if (!trimmedCode) {
this.playError && this.playError();
errorCallback && errorCallback("无效的二维码!");
return;
}
if (this.processScanCode) {
this.processScanCode(trimmedCode, successCallback, errorCallback);
} else {
successCallback && successCallback(trimmedCode);
}
}
},
onShow() {
this.isPageActive = true;
console.log('页面显示,设置 isPageActive 为 true');
},
onHide() {
this.isPageActive = false;
},
onUnload() {
this.isPageActive = false;
this.unregisterScanBroadcast();
}
}