750 lines
20 KiB
JavaScript
750 lines
20 KiB
JavaScript
/**
|
|
* 蓝牙打印服务类
|
|
* 功能:
|
|
* - 统一管理蓝牙打印机连接
|
|
* - 支持 CPCL 和 TSPL 两种指令格式
|
|
* - 自动与 Vuex store 同步配置
|
|
* - 提供指令生成模板
|
|
*
|
|
* 使用方法:
|
|
* import printerService from '@/utils/BluetoothPrinterService.js';
|
|
*
|
|
* // 初始化并连接
|
|
* await printerService.init();
|
|
* await printerService.autoConnect();
|
|
*
|
|
* // 打印操作
|
|
* await printerService.printText('Hello World');
|
|
* await printerService.printWasteLabel(wasteData);
|
|
*/
|
|
|
|
import * as iconv from 'iconv-lite';
|
|
|
|
// 获取 Vuex store 实例(延迟加载避免循环依赖)
|
|
let store = null;
|
|
const getStore = () => {
|
|
if (!store) {
|
|
store = require('@/store').default;
|
|
}
|
|
return store;
|
|
};
|
|
|
|
class BluetoothPrinterService {
|
|
constructor() {
|
|
// 蓝牙状态
|
|
this.adapterOpen = false;
|
|
this.connected = false;
|
|
this.connecting = false;
|
|
this.deviceId = '';
|
|
this.deviceName = '';
|
|
this.serviceId = '';
|
|
this.writeCharId = '';
|
|
|
|
// 打印配置
|
|
this.commandType = 'CPCL'; // 'CPCL' | 'TSPL'
|
|
this.paperWidth = 100;
|
|
this.paperHeight = 100;
|
|
|
|
// 回调函数
|
|
this.connectionChangeCallbacks = [];
|
|
|
|
// 连接状态监听器
|
|
this._connectionListener = null;
|
|
}
|
|
|
|
// ==================== 初始化 ====================
|
|
|
|
/**
|
|
* 初始化蓝牙适配器
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async init() {
|
|
if (this.adapterOpen) return;
|
|
|
|
return new Promise((resolve, reject) => {
|
|
uni.openBluetoothAdapter({
|
|
success: () => {
|
|
this.adapterOpen = true;
|
|
this._registerConnectionListener();
|
|
resolve();
|
|
},
|
|
fail: (err) => {
|
|
console.error('蓝牙初始化失败', err);
|
|
uni.showToast({ title: '蓝牙初始化失败,请检查蓝牙是否开启', icon: 'none' });
|
|
reject(err);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 注册连接状态变化监听
|
|
*/
|
|
_registerConnectionListener() {
|
|
if (this._connectionListener) return;
|
|
|
|
this._connectionListener = (res) => {
|
|
if (!res.connected && this.deviceId === res.deviceId) {
|
|
// 只更新连接状态,保留配置信息以便下次自动连接
|
|
this.connected = false;
|
|
this._notifyConnectionChange(false);
|
|
// 注意:不再调用 _updateStoreConnection,不再清空 store 中的配置
|
|
}
|
|
};
|
|
|
|
uni.onBLEConnectionStateChange(this._connectionListener);
|
|
}
|
|
|
|
/**
|
|
* 销毁,清理资源
|
|
*/
|
|
destroy() {
|
|
if (this._connectionListener) {
|
|
// uni.offBLEConnectionStateChange 无法精确移除单个监听
|
|
// 业务层需要在页面卸载时主动调用 disconnect
|
|
}
|
|
this.disconnect();
|
|
}
|
|
|
|
// ==================== 连接管理 ====================
|
|
|
|
/**
|
|
* 从 store 加载配置
|
|
*/
|
|
_loadConfig() {
|
|
try {
|
|
const state = getStore().state;
|
|
if (state.print) {
|
|
const config = state.print.printerConfig;
|
|
if (config) {
|
|
this.deviceId = config.deviceId || '';
|
|
this.deviceName = config.deviceName || '';
|
|
this.serviceId = config.serviceId || '';
|
|
this.writeCharId = config.writeCharId || '';
|
|
this.paperWidth = config.paperWidth || 100;
|
|
this.paperHeight = config.paperHeight || 100;
|
|
return config;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.warn('加载打印配置失败', e);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 保存配置到 store
|
|
*/
|
|
_saveConfig(config) {
|
|
try {
|
|
getStore().dispatch('print/savePrinterConfig', config);
|
|
} catch (e) {
|
|
console.warn('保存打印配置失败', e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新 store 连接状态
|
|
*/
|
|
_updateStoreConnection(connected, device = {}) {
|
|
try {
|
|
if (connected) {
|
|
getStore().dispatch('print/connectPrinter', {
|
|
deviceId: this.deviceId,
|
|
name: this.deviceName,
|
|
...device
|
|
});
|
|
} else {
|
|
getStore().dispatch('print/disconnectPrinter');
|
|
}
|
|
} catch (e) {
|
|
console.warn('更新连接状态失败', e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 自动连接已保存的打印机
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async autoConnect() {
|
|
if (this.connecting || this.connected) {
|
|
return this.connected;
|
|
}
|
|
|
|
const config = this._loadConfig();
|
|
if (!config || !config.deviceId) {
|
|
console.warn('无可用打印机配置');
|
|
return false;
|
|
}
|
|
|
|
this.connecting = true;
|
|
this._notifyConnectionChange(false, 'connecting');
|
|
|
|
try {
|
|
await this.init();
|
|
// 传递已保存的 serviceId 和 writeCharId
|
|
await this._connect(config.deviceId, config.serviceId, config.writeCharId);
|
|
|
|
this.connected = true;
|
|
this.connecting = false;
|
|
this._notifyConnectionChange(true);
|
|
uni.showToast({ title: '打印机连接成功', icon: 'success' });
|
|
return true;
|
|
} catch (err) {
|
|
this.connected = false;
|
|
this.connecting = false;
|
|
this._notifyConnectionChange(false, 'failed');
|
|
console.error('自动连接失败', err);
|
|
uni.showToast({ title: '打印机连接失败', icon: 'none' });
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 连接指定设备
|
|
* @param {string} deviceId - 设备ID
|
|
* @param {string} savedServiceId - 已保存的服务ID(可选)
|
|
* @param {string} savedWriteCharId - 已保存的特征值ID(可选)
|
|
* @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
|
|
});
|
|
});
|
|
|
|
// 等待服务发现
|
|
await this._delay(800);
|
|
|
|
// 验证保存的服务是否存在
|
|
const { services } = await this._getServices(deviceId);
|
|
const targetService = services.find(s => s.uuid === savedServiceId);
|
|
|
|
if (!targetService) {
|
|
console.warn('保存的服务不存在,尝试自动发现');
|
|
await this._disconnectDevice(deviceId);
|
|
throw new Error('SAVED_SERVICE_NOT_FOUND');
|
|
}
|
|
|
|
// 验证特征值是否存在且可写
|
|
const { characteristics } = await this._getCharacteristics(deviceId, savedServiceId);
|
|
const targetChar = characteristics.find(c =>
|
|
c.uuid === savedWriteCharId &&
|
|
(c.properties.write || c.properties.writeNoResponse)
|
|
);
|
|
|
|
if (!targetChar) {
|
|
console.warn('保存的特征值不可用,尝试自动发现');
|
|
await this._disconnectDevice(deviceId);
|
|
throw new Error('SAVED_CHAR_NOT_FOUND');
|
|
}
|
|
|
|
// 使用保存的配置成功
|
|
console.log('使用已保存配置连接成功');
|
|
this.deviceId = deviceId;
|
|
this.serviceId = savedServiceId;
|
|
this.writeCharId = savedWriteCharId;
|
|
|
|
// 更新 store
|
|
this._saveConfig({
|
|
deviceId,
|
|
serviceId: savedServiceId,
|
|
writeCharId: savedWriteCharId
|
|
});
|
|
|
|
resolve();
|
|
return;
|
|
} catch (err) {
|
|
// 如果不是"保存的配置无效"错误,则直接拒绝
|
|
if (err.message !== 'SAVED_SERVICE_NOT_FOUND' && err.message !== 'SAVED_CHAR_NOT_FOUND') {
|
|
await this._disconnectDevice(deviceId);
|
|
reject(err);
|
|
return;
|
|
}
|
|
// 否则继续自动发现
|
|
console.log('回退到自动发现服务...');
|
|
}
|
|
}
|
|
|
|
// 自动发现服务逻辑(保底方案)
|
|
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
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 断开设备连接(内部方法)
|
|
*/
|
|
async _disconnectDevice(deviceId) {
|
|
try {
|
|
await new Promise((resolve, reject) => {
|
|
uni.closeBLEConnection({
|
|
deviceId,
|
|
success: resolve,
|
|
fail: reject
|
|
});
|
|
});
|
|
} catch (e) {
|
|
console.warn('断开设备失败', e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 断开连接
|
|
* 注意:只做物理断开,保留配置信息以便下次自动连接
|
|
*/
|
|
async disconnect() {
|
|
if (!this.deviceId) return;
|
|
|
|
try {
|
|
await new Promise((resolve, reject) => {
|
|
uni.closeBLEConnection({
|
|
deviceId: this.deviceId,
|
|
success: resolve,
|
|
fail: reject
|
|
});
|
|
});
|
|
} catch (e) {
|
|
console.warn('断开连接失败', e);
|
|
}
|
|
|
|
// 只更新连接状态,保留 deviceId、deviceName、serviceId、writeCharId 等配置
|
|
this.connected = false;
|
|
this._notifyConnectionChange(false);
|
|
// 注意:不再清空配置,不再调用 _updateStoreConnection
|
|
}
|
|
|
|
/**
|
|
* 检查是否已连接
|
|
*/
|
|
isConnected() {
|
|
return this.connected;
|
|
}
|
|
|
|
// ==================== 打印操作 ====================
|
|
|
|
/**
|
|
* 打印文本内容
|
|
* @param {string} content - 打印内容(支持 CPCL/TSPL 指令)
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async printText(content) {
|
|
if (!this.connected) {
|
|
uni.showToast({ title: '打印机未连接,请先连接打印机', icon: 'none' });
|
|
throw new Error('打印机未连接');
|
|
}
|
|
|
|
const buffer = this._stringToBuffer(content);
|
|
await this._writeData(buffer);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 打印废物标签
|
|
* @param {Object} data - 废物数据
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async printWasteLabel(data) {
|
|
const command = this.generateWasteLabel(data);
|
|
return this.printText(command);
|
|
}
|
|
|
|
/**
|
|
* 打印二维码
|
|
* @param {string} content - 二维码内容
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async printQRCode(content) {
|
|
const command = this.generateQRCodeTemplate(content);
|
|
return this.printText(command);
|
|
}
|
|
|
|
/**
|
|
* 打印条码
|
|
* @param {string} content - 条码内容
|
|
* @param {string} type - 条码类型,默认 128
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async printBarcode(content, type = '128') {
|
|
const command = this.generateBarcodeTemplate(content, type);
|
|
return this.printText(command);
|
|
}
|
|
|
|
// ==================== 指令生成 ====================
|
|
|
|
/**
|
|
* 获取纸张尺寸命令
|
|
*/
|
|
getSizeCommand() {
|
|
if (this.commandType === 'CPCL') {
|
|
return `SIZE ${this.paperWidth}mm ${this.paperHeight}mm`;
|
|
} else {
|
|
return `SIZE ${this.paperWidth} mm ${this.paperHeight} mm`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 生成废物标签指令
|
|
* @param {Object} data - 废物数据
|
|
*/
|
|
generateWasteLabel(data) {
|
|
const sizeCmd = this.getSizeCommand();
|
|
const gapCmd = this.commandType === 'CPCL' ? 'GAP 2mm' : 'GAP 2 mm';
|
|
|
|
if (this.commandType === 'CPCL') {
|
|
return this._generateCPCLWasteLabel(data, sizeCmd, gapCmd);
|
|
} else {
|
|
return this._generateTSPLWasteLabel(data, sizeCmd, gapCmd);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 生成 CPCL 废物标签
|
|
*/
|
|
_generateCPCLWasteLabel(data, sizeCmd, gapCmd) {
|
|
const fieldOrder = [
|
|
'mingcheng', 'leibie', 'daima', 'xingtai',
|
|
'zhuyaochengfen', 'youhaichengfen', 'zhuyi',
|
|
'shibiema', 'danwei', 'lianxi', 'riqi',
|
|
'zhongliang', 'beizhu', 'biaoshi'
|
|
];
|
|
|
|
const fieldLabels = {
|
|
mingcheng: '名称', leibie: '类别', daima: '代码', xingtai: '形态',
|
|
zhuyaochengfen: '主要成分', youhaichengfen: '有害成分', zhuyi: '注意',
|
|
shibiema: '识别码', danwei: '单位', lianxi: '联系', riqi: '日期',
|
|
zhongliang: '重量', beizhu: '备注', biaoshi: '●'
|
|
};
|
|
|
|
const conf = [
|
|
{ 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: 630, y: 540 }
|
|
];
|
|
|
|
let commands = [];
|
|
commands.push('! 0 200 200 800 1');
|
|
commands.push(sizeCmd);
|
|
|
|
fieldOrder.forEach((field, index) => {
|
|
if (data[field]) {
|
|
const { x, y } = conf[index];
|
|
const label = fieldLabels[field] || field;
|
|
commands.push(`T 1 0 ${x} ${y} ${data[field]}`);
|
|
}
|
|
});
|
|
|
|
if (data.qrcode) {
|
|
commands.push(`B QR 624 584 M 2 U 4`);
|
|
commands.push(`L0,${data.qrcode}`);
|
|
commands.push('ENDQR');
|
|
}
|
|
|
|
commands.push('FORM');
|
|
commands.push('PRINT');
|
|
return commands.join('\n');
|
|
}
|
|
|
|
/**
|
|
* 生成 TSPL 废物标签
|
|
*/
|
|
_generateTSPLWasteLabel(data, sizeCmd, gapCmd) {
|
|
const fieldOrder = [
|
|
'mingcheng', 'leibie', 'daima', 'xingtai',
|
|
'zhuyaochengfen', 'youhaichengfen', 'zhuyi',
|
|
'shibiema', 'danwei', 'lianxi', 'riqi',
|
|
'zhongliang', 'beizhu'
|
|
];
|
|
|
|
const fieldLabels = {
|
|
mingcheng: '名称', leibie: '类别', daima: '代码', xingtai: '形态',
|
|
zhuyaochengfen: '主要成分', youhaichengfen: '有害成分', zhuyi: '注意',
|
|
shibiema: '识别码', danwei: '单位', lianxi: '联系', riqi: '日期',
|
|
zhongliang: '重量', beizhu: '备注'
|
|
};
|
|
|
|
let commands = [];
|
|
commands.push(sizeCmd);
|
|
commands.push(gapCmd);
|
|
commands.push('CLS');
|
|
|
|
fieldOrder.forEach((field, index) => {
|
|
if (data[field]) {
|
|
const y = 20 + (index * 25);
|
|
const label = fieldLabels[field] || field;
|
|
const content = `${label}: ${data[field]}`;
|
|
commands.push(`TEXT 20 ${y} "4" 0 1 1 "${content}"`);
|
|
}
|
|
});
|
|
|
|
if (data.qrcode) {
|
|
commands.push(`QRCODE 150 400 L 5 0 A 0 "${data.qrcode}"`);
|
|
}
|
|
|
|
commands.push('PRINT');
|
|
return commands.join('\n');
|
|
}
|
|
|
|
/**
|
|
* 生成二维码模板
|
|
* @param {string} content - 二维码内容
|
|
*/
|
|
generateQRCodeTemplate(content) {
|
|
const sizeCmd = this.getSizeCommand();
|
|
const gapCmd = this.commandType === 'CPCL' ? 'GAP 2mm' : 'GAP 2 mm';
|
|
|
|
if (this.commandType === 'CPCL') {
|
|
return `! 0 200 200 240 1
|
|
${sizeCmd}
|
|
${gapCmd}
|
|
CENTER
|
|
QR 300 10 M 2 A 0
|
|
MA,${content}
|
|
ENDQR
|
|
PRINT`;
|
|
} else {
|
|
return `${sizeCmd}
|
|
${gapCmd}
|
|
CLS
|
|
QRCODE 50 50 L 5 0 A 0 "${content}"
|
|
PRINT`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 生成条码模板
|
|
* @param {string} content - 条码内容
|
|
* @param {string} type - 条码类型
|
|
*/
|
|
generateBarcodeTemplate(content, type = '128') {
|
|
const sizeCmd = this.getSizeCommand();
|
|
const gapCmd = this.commandType === 'CPCL' ? 'GAP 2mm' : 'GAP 2 mm';
|
|
|
|
if (this.commandType === 'CPCL') {
|
|
return `! 0 200 200 240 1
|
|
${sizeCmd}
|
|
${gapCmd}
|
|
CENTER
|
|
BARCODE 128 1 1 60 1 0 10 40 ${content}
|
|
PRINT`;
|
|
} else {
|
|
return `${sizeCmd}
|
|
${gapCmd}
|
|
CLS
|
|
BARCODE 50 50 "${type}" 50 1 0 2 2 "${content}"
|
|
PRINT`;
|
|
}
|
|
}
|
|
|
|
// ==================== 配置管理 ====================
|
|
|
|
/**
|
|
* 设置指令类型
|
|
* @param {'CPCL' | 'TSPL'} type
|
|
*/
|
|
setCommandType(type) {
|
|
this.commandType = type;
|
|
this._saveConfig({ commandType: type });
|
|
}
|
|
|
|
/**
|
|
* 设置纸张尺寸
|
|
* @param {number} width - 宽度(mm)
|
|
* @param {number} height - 高度(mm)
|
|
*/
|
|
setPaperSize(width, height) {
|
|
this.paperWidth = width;
|
|
this.paperHeight = height;
|
|
this._saveConfig({ paperWidth: width, paperHeight: height });
|
|
}
|
|
|
|
/**
|
|
* 获取当前配置
|
|
*/
|
|
getConfig() {
|
|
return {
|
|
connected: this.connected,
|
|
deviceId: this.deviceId,
|
|
deviceName: this.deviceName,
|
|
serviceId: this.serviceId,
|
|
writeCharId: this.writeCharId,
|
|
commandType: this.commandType,
|
|
paperWidth: this.paperWidth,
|
|
paperHeight: this.paperHeight
|
|
};
|
|
}
|
|
|
|
// ==================== 回调管理 ====================
|
|
|
|
/**
|
|
* 注册连接状态变化回调
|
|
* @param {Function} callback - (connected: boolean, status?: string) => void
|
|
*/
|
|
onConnectionChange(callback) {
|
|
this.connectionChangeCallbacks.push(callback);
|
|
}
|
|
|
|
/**
|
|
* 移除连接状态变化回调
|
|
*/
|
|
offConnectionChange(callback) {
|
|
const index = this.connectionChangeCallbacks.indexOf(callback);
|
|
if (index > -1) {
|
|
this.connectionChangeCallbacks.splice(index, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 通知连接状态变化
|
|
*/
|
|
_notifyConnectionChange(connected, status) {
|
|
this.connectionChangeCallbacks.forEach(cb => {
|
|
try {
|
|
cb(connected, status);
|
|
} catch (e) {
|
|
console.warn('连接状态回调执行失败', e);
|
|
}
|
|
});
|
|
}
|
|
|
|
// ==================== 工具方法 ====================
|
|
|
|
/**
|
|
* 字符串转 ArrayBuffer (GBK编码)
|
|
*/
|
|
_stringToBuffer(str) {
|
|
try {
|
|
const uint8Array = iconv.encode(str, 'gbk');
|
|
return uint8Array.buffer;
|
|
} catch (e) {
|
|
console.warn('GBK编码失败,使用回退方案', e);
|
|
const buffer = new ArrayBuffer(str.length);
|
|
const dataView = new DataView(buffer);
|
|
for (let i = 0; i < str.length; i++) {
|
|
dataView.setUint8(i, str.charCodeAt(i) & 0xFF);
|
|
}
|
|
return buffer;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 写入数据到蓝牙设备
|
|
*/
|
|
async _writeData(buffer) {
|
|
return new Promise((resolve, reject) => {
|
|
uni.writeBLECharacteristicValue({
|
|
deviceId: this.deviceId,
|
|
serviceId: this.serviceId,
|
|
characteristicId: this.writeCharId,
|
|
value: buffer,
|
|
writeType: 'writeNoResponse',
|
|
success: resolve,
|
|
fail: reject
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 延迟
|
|
*/
|
|
_delay(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
/**
|
|
* 获取设备服务列表
|
|
*/
|
|
_getServices(deviceId) {
|
|
return new Promise((resolve, reject) => {
|
|
uni.getBLEDeviceServices({
|
|
deviceId,
|
|
success: resolve,
|
|
fail: reject
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 获取服务特征值列表
|
|
*/
|
|
_getCharacteristics(deviceId, serviceId) {
|
|
return new Promise((resolve, reject) => {
|
|
uni.getBLEDeviceCharacteristics({
|
|
deviceId,
|
|
serviceId,
|
|
success: resolve,
|
|
fail: reject
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
// 导出单例
|
|
export default new BluetoothPrinterService();
|