This commit is contained in:
2026-08-18 14:46:36 +08:00
parent 9e94385dfc
commit a084fe1fd4
13 changed files with 647 additions and 235 deletions
+226 -161
View File
@@ -49,22 +49,30 @@ const PAPER_PRESETS = {
pageWidth: 800, // 100mm @200dpi = 800 点
gapSense: true, // 自动检测标签间隙,避免固定 GAP 值不匹配
fields: [
{ x: 192, y: 100 }, // 名称
{ x: 192, y: 150 }, // 类别
{ x: 192, y: 200 }, // 代码
{ x: 480, y: 200 }, // 形态
{ x: 192, y: 250 }, // 主要成分
{ x: 192, y: 350 }, // 有害成分
{ x: 192, y: 448 }, // 注意
{ x: 216, y: 536 }, // 识别码
{ x: 256, y: 584 }, // 单位
{ x: 288, y: 632 }, // 联系
{ x: 192, y: 680 }, // 日期
{ x: 480, y: 680 }, // 重量
{ x: 150, y: 728 }, // 备注
{ x: 480, y: 448 } // 标识
{ x: 192, y: 140 }, // 名称(识别码以上,下2mm
{ x: 192, y: 190 }, // 类别(识别码以上,下2mm
{ x: 192, y: 240 }, // 代码(识别码以上,下2mm
{ x: 480, y: 240 }, // 形态(识别码以上,下2mm
{ x: 192, y: 290 }, // 主要成分(识别码以上,下2mm
{ x: 192, y: 390 }, // 有害成分(识别码以上,下2mm
{ x: 192, y: 488 }, // 注意(识别码以上,下2mm
{ x: 216, y: 576 }, // 识别码(含,下2mm
{ x: 256, y: 624 }, // 单位(识别码以下,下1mm
{ x: 288, y: 672 }, // 联系(识别码以下,下1mm
{ x: 192, y: 720 }, // 日期(识别码以下,下1mm
{ x: 480, y: 720 }, // 重量(识别码以下,下1mm
{ x: 150, y: 768 } // 备注(识别码以下,下1mm
],
qrCode: { x: 624, y: 584, size: 4 }
// 标识点:正方形四角布局(1=左上、2=右上、3=左下、4=右下),无中心点
// 由 data.biaoshi 传入位置编号数组驱动,使用 √ 字符绘制
biaoshiPositions: {
1: { x: 608, y: 250 }, // 向左 2mm16点)
2: { x: 704, y: 250 },
3: { x: 608, y: 410 }, // 向左 2mm16点),向下 2mm40点)
4: { x: 704, y: 410 } // 向下 2mm40点)
},
biaoshiMag: [2, 2],
qrCode: { x: 604, y: 624, size: 4 } // 向左 1mm20点),向上 1mm20点)
},
tspl: {
startX: 20,
@@ -91,14 +99,21 @@ const PAPER_PRESETS = {
{ x: 288, y: 399, mag: [2, 2], bold: 0.5, font: 0 }, // 主要成分(150:375+24,加粗)
{ x: 288, y: 549, mag: [2, 2], bold: 0.5, font: 0 }, // 有害成分(150:525+24,加粗)
{ x: 288, y: 696, mag: [2, 2], bold: 0.0, font: 1 }, // 注意(150:672+24
{ x: 308, y: 836, mag: [2, 2], bold: 0.0, font: 1 }, // 识别码(注意以下:x-16 左2mm, y+8 下1mm
{ x: 308, y: 836, mag: [1, 1], bold: 0.0, font: 1 }, // 识别码(注意以下:x-16 左2mm, y+8 下1mm
{ x: 368, y: 908, mag: [2, 2], bold: 0.0, font: 1 }, // 单位(注意以下)
{ x: 416, y: 980, mag: [2, 2], bold: 0.0, font: 1 }, // 联系(注意以下)
{ x: 272, y: 1052, mag: [2, 2], bold: 0.5, font: 0 }, // 日期(注意以下,加粗)
{ x: 704, y: 1052, mag: [2, 2], bold: 0.0, font: 1 }, // 重量(注意以下)
{ x: 209, y: 1124, mag: [2, 2], bold: 0.0, font: 1 }, // 备注(注意以下)
{ x: 720, y: 549, mag: [4, 4], bold: 0.0, font: 1 } // 标识(150:525+244 倍放大)
{ x: 209, y: 1124, mag: [2, 2], bold: 0.0, font: 1 } // 备注(注意以下)
],
// 标识点:正方形四角布局(1=左上、2=右上、3=左下、4=右下),无中心点
biaoshiPositions: {
1: { x: 904, y: 399 }, // 左上(累计左移 4mm / 32点)
2: { x: 1064, y: 399 }, // 右上(累计左移 4mm / 32点)
3: { x: 904, y: 551 }, // 左下(累计左移 4mm + 下移 4mm / 各32点)
4: { x: 1064, y: 551 } // 右下(累计左移 4mm + 下移 4mm / 各32点)
},
biaoshiMag: [4, 4],
qrCode: { x: 936, y: 900, size: 4 } // 位置用 150 原坐标(876+24),size 4 不越界
},
tspl: {
@@ -128,9 +143,16 @@ const PAPER_PRESETS = {
{ x: 552, y: 1328, mag: [2, 2], bold: 0.0, font: 1 }, // 联系
{ x: 352, y: 1432, mag: [2, 2], bold: 0.5, font: 0 }, // 日期(加粗)
{ x: 912, y: 1432, mag: [2, 2], bold: 0.0, font: 1 }, // 重量
{ x: 240, y: 1528, mag: [2, 2], bold: 0.0, font: 1 }, // 备注
{ x: 1432, y: 544, mag: [4, 4], bold: 0.0, font: 1 } // 标识(√ 勾选,4 倍放大)
{ x: 240, y: 1528, mag: [2, 2], bold: 0.0, font: 1 } // 备注
],
// 标识点:正方形四角布局(1=左上、2=右上、3=左下、4=右下),无中心点
biaoshiPositions: {
1: { x: 1220, y: 544 },
2: { x: 1460, y: 544 },
3: { x: 1220, y: 724 },
4: { x: 1460, y: 724 }
},
biaoshiMag: [4, 4],
qrCode: { x: 1220, y: 1224, size: 8, prefix: 'BARCODE QR' }
},
tspl: {
@@ -190,6 +212,13 @@ class BluetoothPrinterService {
resolve();
},
fail: (err) => {
// 10001 表示蓝牙适配器已打开,视为成功(部分机型重复 open 会返回该码)
if (err && err.errCode === 10001) {
this.adapterOpen = true;
this._registerConnectionListener();
resolve();
return;
}
console.error('蓝牙初始化失败', err);
reject(err);
}
@@ -328,122 +357,90 @@ class BluetoothPrinterService {
* @returns {Promise<void>}
*/
async _connect(deviceId, savedServiceId = '', savedWriteCharId = '') {
return new Promise(async (resolve, reject) => {
// 首先尝试使用已保存的服务配置
if (savedServiceId && savedWriteCharId) {
try {
console.log('尝试使用已保存的服务配置:', { savedServiceId, savedWriteCharId });
// 建立连接
await new Promise((res, rej) => {
uni.createBLEConnection({
deviceId,
timeout: 15000,
success: res,
fail: rej
});
});
// MTU 协商(蓝牙 5.0 理论最大 512,实际由设备/系统决定)
try {
const mtuRes = await new Promise((res) => {
uni.setBLEMTU({
deviceId,
mtu: 512,
success: res,
fail: () => res({ mtu: 23 })
});
});
this._mtu = (mtuRes.mtu || 512) - 3;
console.log(`[打印] MTU 协商: 请求 512, 实际=${mtuRes.mtu}, 写入块=${this._mtu}`);
} catch (e) {
console.warn('[打印] MTU 协商异常, 使用默认值 20', e);
this._mtu = 20;
}
// 等待服务发现(像 configPrinter 一样)
await this._delay(800);
// ✅ 直接使用已保存的配置,不做任何验证
// 原因:调用 getBLEDeviceCharacteristics 后该特征值会变得不可写(10007)
console.log('使用已保存配置连接成功');
this.deviceId = deviceId;
this.serviceId = savedServiceId;
this.writeCharId = savedWriteCharId;
// 更新 store
this._saveConfig({
deviceId,
serviceId: savedServiceId,
writeCharId: savedWriteCharId
});
resolve();
return;
} catch (err) {
// 连接失败,回退到自动发现
console.warn('使用已保存配置连接失败,回退到自动发现:', err);
await this._disconnectDevice(deviceId);
}
}
// 自动发现服务逻辑(保底方案)
// 建立 BLE 连接(与 configPrinter 一致,只用 deviceId 直连,秒连)
await new Promise((res, rej) => {
uni.createBLEConnection({
deviceId,
timeout: 15000,
success: async () => {
// 等待服务发现
await this._delay(800);
// 获取服务列表
const { services } = await this._getServices(deviceId);
if (!services || services.length === 0) {
reject(new Error('未找到蓝牙服务'));
return;
}
// 查找第一个有可写特征值的服务
let foundService = null;
let writeChar = null;
for (const service of services) {
try {
const { characteristics } = await this._getCharacteristics(deviceId, service.uuid);
const writableChar = characteristics.find(c =>
c.properties.write || c.properties.writeNoResponse
);
if (writableChar) {
foundService = service;
writeChar = writableChar;
break;
}
} catch (e) {
// 跳过无效服务
continue;
}
}
if (!writeChar) {
reject(new Error('未找到可写入的特征值'));
return;
}
this.deviceId = deviceId;
this.serviceId = foundService.uuid;
this.writeCharId = writeChar.uuid;
// 保存配置
this._saveConfig({
deviceId,
serviceId: foundService.uuid,
writeCharId: writeChar.uuid
});
resolve();
},
fail: reject
success: res,
fail: rej
});
});
// MTU 协商(蓝牙 5.0 理论最大 512,实际由设备/系统决定)
try {
const mtuRes = await new Promise((res) => {
uni.setBLEMTU({
deviceId,
mtu: 512,
success: res,
fail: () => res({ mtu: 23 })
});
});
this._mtu = (mtuRes.mtu || 512) - 3;
console.log(`[打印] MTU 协商: 请求 512, 实际=${mtuRes.mtu}, 写入块=${this._mtu}`);
} catch (e) {
console.warn('[打印] MTU 协商异常, 使用默认值 20', e);
this._mtu = 20;
}
// 等待服务发现(像 configPrinter 一样)
await this._delay(800);
// ✅ 直接使用已保存的配置,不做任何验证
// 原因:调用 getBLEDeviceCharacteristics 后该特征值会变得不可写(10007)
// 若未提供已保存配置,回退到自动发现
if (savedServiceId && savedWriteCharId) {
console.log('使用已保存配置连接成功:', { savedServiceId, savedWriteCharId });
this.deviceId = deviceId;
this.serviceId = savedServiceId;
this.writeCharId = savedWriteCharId;
this._saveConfig({
deviceId,
serviceId: savedServiceId,
writeCharId: savedWriteCharId
});
return;
}
console.warn('未提供已保存配置,回退到自动发现');
const { services } = await this._getServices(deviceId);
if (!services || services.length === 0) {
throw new Error('未找到蓝牙服务');
}
let foundService = null;
let writeChar = null;
for (const service of services) {
try {
const { characteristics } = await this._getCharacteristics(deviceId, service.uuid);
const writableChar = characteristics.find(c =>
c.properties.write || c.properties.writeNoResponse
);
if (writableChar) {
foundService = service;
writeChar = writableChar;
break;
}
} catch (e) {
continue;
}
}
if (!writeChar) {
throw new Error('未找到可写入的特征值');
}
this.deviceId = deviceId;
this.serviceId = foundService.uuid;
this.writeCharId = writeChar.uuid;
this._saveConfig({
deviceId,
serviceId: foundService.uuid,
writeCharId: writeChar.uuid
});
}
/**
@@ -567,12 +564,7 @@ class BluetoothPrinterService {
try {
await this._writeData(blockBuffer);
} catch (err) {
const isLastBlock = i >= lines.length;
if (isLastBlock) {
// 尾块容错:与 _writeData 保持一致,最后一个块写入失败不抛异常
console.warn(`[打印] QR 块(最后一个块)发送失败,视为尾包处理:`, err);
return;
}
// QR 块是打印指令的组成数据,发送失败会导致整页数据不完整,必须报错
console.error(`[打印] QR 块发送失败:`, err);
throw err;
}
@@ -580,18 +572,35 @@ class BluetoothPrinterService {
// 普通行,逐行发送
const lineStr = line + '\n';
const buffer = this._stringToBuffer(lineStr);
const isCriticalLine = trimmed === 'PRINT' || trimmed === 'FORM';
try {
await this._writeData(buffer);
} catch (err) {
const isLastLine = i === lines.length - 1;
if (isLastLine) {
// 尾行容错:与 _writeData 保持一致,最后一行写入失败不抛异常
console.warn(`[打印] 第 ${i + 1} 行(最后一行)发送失败,视为尾包处理:`, err);
return;
if (isCriticalLine) {
// 关键打印指令:最多重试 3 次,不容忍失败
let delivered = false;
for (let retry = 0; retry < 3; retry++) {
try {
await this._writeData(buffer);
delivered = true;
break;
} catch (writeErr) {
if (retry < 2) {
const wait = 300 + retry * 300; // 300, 600ms
console.warn(`[打印] 关键行 "${trimmed}" 写入失败,${wait}ms后重试(${retry + 1}/3):`, writeErr.message || writeErr);
await this._delay(wait);
}
}
}
if (!delivered) {
throw new Error(`关键打印指令 "${trimmed}" 发送失败,打印机未收到执行命令,请重试打印`);
}
} else {
// 普通行:任意一行发送失败都视为打印数据不完整,必须报错
try {
await this._writeData(buffer);
} catch (err) {
console.error(`[打印] 第 ${i + 1} 行发送失败:`, err);
throw err;
}
console.error(`[打印] 第 ${i + 1} 行发送失败:`, err);
throw err;
}
i++;
}
@@ -611,6 +620,9 @@ class BluetoothPrinterService {
* @returns {Promise<boolean>}
*/
async printWasteLabel(data) {
// 正式打印入口:强制从 store 加载正式配置(含纸张尺寸/currentPreset/指令类型),
// 避免打印测试页的临时设置(纸张/指令类型仅在内存生效)泄漏到正式打印
this._loadConfig();
const command = this.generateWasteLabel(data);
return this.printText(command);
}
@@ -672,14 +684,14 @@ class BluetoothPrinterService {
'mingcheng', 'leibie', 'daima', 'xingtai',
'zhuyaochengfen', 'youhaichengfen', 'zhuyi',
'shibiema', 'danwei', 'lianxi', 'riqi',
'zhongliang', 'beizhu', 'biaoshi'
'zhongliang', 'beizhu'
];
const fieldLabels = {
mingcheng: '名称', leibie: '类别', daima: '代码', xingtai: '形态',
zhuyaochengfen: '主要成分', youhaichengfen: '有害成分', zhuyi: '注意',
shibiema: '识别码', danwei: '单位', lianxi: '联系', riqi: '日期',
zhongliang: '重量', beizhu: '备注', biaoshi: '●'
zhongliang: '重量', beizhu: '备注'
};
// 优先使用预设布局,否则使用默认配置
@@ -688,8 +700,7 @@ class BluetoothPrinterService {
{ x: 192, y: 100 }, { x: 192, y: 150 }, { x: 192, y: 200 }, { x: 480, y: 200 },
{ x: 192, y: 250 }, { x: 192, y: 350 }, { x: 192, y: 448 },
{ x: 216, y: 536 }, { x: 256, y: 584 }, { x: 288, y: 632 },
{ x: 192, y: 680 }, { x: 480, y: 680 }, { x: 150, y: 728 },
{ x: 480, y: 448 }
{ x: 192, y: 680 }, { x: 480, y: 680 }, { x: 150, y: 728 }
];
const labelHeight = (layout && layout.labelHeight) || 800;
@@ -716,6 +727,9 @@ class BluetoothPrinterService {
// (100x100 标签字段无这些配置,避免输出多余的 SETMAG/SETBOLD
if (f.mag) {
commands.push(`SETMAG ${f.mag[0]} ${f.mag[1]}`);
} else {
// 无 mag 时显式复位成 1x1,避免继承上一个 T(如标识点的 2x2)的放大状态
commands.push(`SETMAG 1 1`);
}
if (f.bold != null) {
commands.push(`SETBOLD ${f.bold}`);
@@ -725,6 +739,22 @@ class BluetoothPrinterService {
}
});
// 标识点:根据 data.biaoshi 传入的位置编号数组,在正方形四角绘制 √
const biaoshiArr = Array.isArray(data.biaoshi) ? data.biaoshi : [];
if (biaoshiArr.length > 0) {
const positions = (layout && layout.biaoshiPositions) || {};
const mag = (layout && layout.biaoshiMag) || [2, 2];
commands.push(`SETMAG ${mag[0]} ${mag[1]}`);
biaoshiArr.forEach(posNum => {
const pos = positions[posNum];
if (pos) {
commands.push(`T 1 0 ${pos.x} ${pos.y}`);
}
});
// 标识 √ 画完后复位字号,避免影响后续二维码/字段(SETMAG 为累积状态)
commands.push(`SETMAG 1 1`);
}
if (data.qrcode) {
const qrc = (layout && layout.qrCode) || { x: 624, y: 584, size: 4, prefix: 'B QR' };
const qrPrefix = qrc.prefix || 'B QR';
@@ -870,6 +900,33 @@ PRINT`;
});
}
/**
* 临时设置纸张尺寸(仅内存生效,不保存配置)
* 用于打印测试页面,不影响正式配置与 store
* @param {number} width - 宽度(mm)
* @param {number} height - 高度(mm)
*/
setPaperSizeTemporary(width, height) {
this.paperWidth = width;
this.paperHeight = height;
const presetKey = `${width}x${height}`;
if (PAPER_PRESETS[presetKey]) {
this.currentPreset = presetKey;
} else {
this.currentPreset = null;
}
}
/**
* 临时设置指令类型(仅内存生效,不保存配置)
* 用于打印测试页面,不影响正式配置与 store
* @param {'CPCL' | 'TSPL'} type
*/
setCommandTypeTemporary(type) {
this.commandType = type;
}
/**
* 获取所有可用的预设纸张尺寸
* @returns {Array<{key: string, label: string, width: number, height: number}>}
@@ -912,6 +969,13 @@ PRINT`;
* @returns {object|null}
*/
_getCurrentLayout() {
// 一致性修正:currentPreset 与当前纸张尺寸不符时,按尺寸重新推导
// (配置页只保存宽高、未保存 currentPreset 时也能正确选布局)
const presetKey = `${this.paperWidth}x${this.paperHeight}`;
if (this.currentPreset !== presetKey && PAPER_PRESETS[presetKey]) {
this.currentPreset = presetKey;
}
if (!this.currentPreset || !PAPER_PRESETS[this.currentPreset]) {
return null;
}
@@ -1003,7 +1067,7 @@ PRINT`;
* 3. 失败时切换 writeType 重试(null → writeNoResponse → write
* 4. 失败时增加延迟(BLE 协议栈繁忙)后再次重试
*/
async _writeData(buffer, attempt = 0) {
async _writeData(buffer, attempt = 0, _lastError = null) {
const dataLen = buffer.byteLength;
const mtu = this._mtu || 20;
@@ -1017,11 +1081,13 @@ PRINT`;
const maxAttempts = 3; // 总共最多重试 3 次
if (attempt >= maxAttempts) {
// 尾包/单包容错:数据已基本送达(最后一次 writeBLECharacteristicValue 触发 errCode=10007
// 通常是打印机刚处理完上一批数据时协议栈返回 property not support,并非真正写入失败)。
// 此处不再抛异常,静默视为成功以保证整个打印流程不中断
console.warn(`[打印] 写入重试 ${maxAttempts} 次仍失败,视为尾包/打印机忙导致,数据可能已送达`);
return { success: true, silent: true, reason: 'max attempts reached, treat as success (tail-packet tolerance)' };
const errCode = _lastError ? _lastError.errCode : 'unknown';
const errMsg = _lastError ? _lastError.errMsg : '';
// 三种 writeType(null/writeNoResponse/write) 全部尝试失败 = 数据未送达
// 10007 多为"特征值不支持该写入方式/属性不支持",连续失败说明特征值
// 不可写或 serviceId/writeCharId 配置错误,绝不能静默视为成功,否则会
// 误报"打印成功"而打印机实际无反应。
throw new Error(`蓝牙写入失败(已重试${maxAttempts}次): errCode=${errCode}, errMsg=${errMsg},请重试或重新配对打印机`);
}
// 选择本次尝试的 writeType
@@ -1056,7 +1122,7 @@ PRINT`;
// 延迟后重试(递增延迟,给 BLE 协议栈恢复时间)
const delay = 100 + attempt * 150; // 100ms, 250ms, 400ms
setTimeout(() => {
this._writeData(buffer, attempt + 1).then(resolve).catch(reject);
this._writeData(buffer, attempt + 1, err).then(resolve).catch(reject);
}, delay);
}
});
@@ -1220,9 +1286,8 @@ PRINT`;
if (delivered) {
continue;
}
// 5 次重试全部失败:仍按原策略静默成功(避免上层误报),但通过 console.error 醒目提示数据可能不完整
console.error(`打印数据可能不完整】尾包 5 次重试均失败(offset=${offset}, size=${end - offset}B)末尾 ${end - offset} 字节可能丢失,请检查打印结果!`);
return;
// 5 次重试(含强制 write 模式)全部失败:数据确实未送达,必须报错
throw new Error(`尾包发送失败,打印数据不完整末尾 ${end - offset} 字节可能丢失,请重试打印`);
}
console.error(`分片发送失败 at offset ${offset}:`, err);
throw err;
+124
View File
@@ -0,0 +1,124 @@
/**
* PDA 设备扫码实现(hbyapi 广播协议,基于 QS5502 开发 demo
*
* 设备自身带有物理扫码按键,按下即触发扫描头;
* 本模块负责「监听扫码结果广播」+「页面扫码按钮主动触发」。
*
* 协议(来自 QS5502 demo MainActivity.java):
* 触发扫描:发送广播 hbyapi.intent.key_scan_down(模拟按键按下)
* 接收结果:监听广播 com.qs.scancode,从 extra 键 data 获取扫码字符串
*
* 用法(与 scan-*.js 同构):
* const impl = new ScanPdaImpl()
* impl.init()
* impl.register((code) => {...})
* impl.startScan() // 主动触发(物理键按下同样会回传,无需手动调用)
*/
class ScanPdaImpl {
constructor() {
this._main = null
this._receiver = null
this._filter = null
this._callback = null
this._isScanning = false
}
/**
* 初始化:获取主 Activity 引用
*/
init() {
this._main = plus.android.runtimeMainActivity()
if (!this._main) {
throw new Error('[Scan-PDA] 获取主 Activity 失败')
}
}
/**
* 注册扫码结果回调(注册 com.qs.scancode 广播接收器)
*/
register(callback) {
this._callback = callback
if (this._receiver) return
var self = this
var IntentFilter = plus.android.importClass('android.content.IntentFilter')
this._filter = new IntentFilter()
this._filter.addAction('com.qs.scancode')
this._receiver = plus.android.implements(
'io.dcloud.feature.internal.reflect.BroadcastReceiver',
{
onReceive: function (context, intent) {
plus.android.importClass(intent)
var codeStr = intent.getStringExtra('data')
if (!codeStr) return
console.log('[Scan-PDA] 扫码结果:', codeStr)
self._isScanning = false
if (self._callback) {
self._callback(codeStr)
}
}
}
)
this._main.registerReceiver(this._receiver, this._filter)
}
/**
* 注销监听(页面卸载时调用)
*/
unregister() {
if (this._receiver && this._main) {
this._main.unregisterReceiver(this._receiver)
this._receiver = null
}
this._callback = null
}
/**
* 开始扫描:发送 key_scan_down 广播主动触发扫描头
* 设备物理扫码键按下时同样会回传结果,无需手动调用此方法
*/
startScan() {
if (!this._main) return
var Intent = plus.android.importClass('android.content.Intent')
var intent = new Intent()
intent.setAction('hbyapi.intent.key_scan_down')
this._main.sendBroadcast(intent)
this._isScanning = true
console.log('[Scan-PDA] 已触发扫描广播')
}
/**
* 停止扫描
* demo 仅演示 key_scan_down;部分同协议机型支持 key_scan_up 停止,
* 这里 try-catch 发送,无副作用
*/
stopScan() {
this._isScanning = false
if (!this._main) return
try {
var Intent = plus.android.importClass('android.content.Intent')
var intent = new Intent()
intent.setAction('hbyapi.intent.key_scan_up')
this._main.sendBroadcast(intent)
} catch (e) {
console.log('[Scan-PDA] 发送停止广播失败(可忽略):', e.message)
}
}
isScanning() {
return this._isScanning
}
/**
* 完全销毁(应用退出时调用)
*/
destroy() {
this.unregister()
this._main = null
this._filter = null
}
}
export default ScanPdaImpl
+11 -6
View File
@@ -1,13 +1,13 @@
/**
* 统一扫码模块
* 支持 安卓设备 / iData 专用 / 通用 Android 手动配置
* 支持 安卓设备 / iData 专用 / 通用 Android PDA / 带物理扫码键 PDAhbyapi 广播)
*
* === 使用方式 ===
*
* import scan from '@/utils/scan.js'
*
* onLoad() {
* scan.setMode('uniapp') // 'uniapp' | 'idata' | 'android'(默认 uniapp
* scan.setMode('uniapp') // 'uniapp' | 'idata' | 'android' | 'pda'(默认 uniapp
* scan.init()
* scan.register((codeStr) => {
* console.log('扫码结果:', codeStr)
@@ -18,7 +18,7 @@
* }
*
* === API ===
* scan.setMode(mode) - 设置模式:'uniapp' | 'idata' | 'android'
* scan.setMode(mode) - 设置模式:'uniapp' | 'idata' | 'android' | 'pda'
* scan.init() - 初始化扫码模块
* scan.register(cb) - 注册回调:cb(codeStr)
* scan.unregister() - 注销回调(页面级)
@@ -32,7 +32,8 @@
var MODE = {
UNIAPP: 'uniapp',
IDATA: 'idata',
ANDROID: 'android'
ANDROID: 'android',
PDA: 'pda'
}
// 当前模式,默认 uniapp(安卓设备)
@@ -46,9 +47,9 @@ var impl = null
* @param {'uniapp'|'idata'|'android'} mode
*/
function setMode(mode) {
var validModes = [MODE.UNIAPP, MODE.IDATA, MODE.ANDROID]
var validModes = [MODE.UNIAPP, MODE.IDATA, MODE.ANDROID, MODE.PDA]
if (validModes.indexOf(mode) === -1) {
console.error('[Scan] 不支持的模式: ' + mode + ',可选: uniapp, idata, android')
console.error('[Scan] 不支持的模式: ' + mode + ',可选: uniapp, idata, android, pda')
return
}
// 如果已初始化,先销毁再重新设置模式
@@ -80,6 +81,10 @@ function init() {
}
var ScanIdataImpl = require('./scan-idata.js').default
impl = new ScanIdataImpl(iDataModule)
} else if (currentMode === MODE.PDA) {
// 带物理扫码键 PDA 设备:hbyapi 广播协议(发送 key_scan_down 触发,监听 com.qs.scancode 接收)
var ScanPdaImpl = require('./scan-pda.js').default
impl = new ScanPdaImpl()
} else {
// 带扫码PDA设备:使用 Android 系统扫码 API
try {