339 lines
11 KiB
Vue
339 lines
11 KiB
Vue
<template>
|
||
<view class="container">
|
||
<view class="status-bar">
|
||
<text>打印机: {{ printerConnected ? '已连接' : '未连接' }}</text>
|
||
<text v-if="lastPrinterMac && !printerConnected">上次设备: {{ lastPrinterMac }}</text>
|
||
</view>
|
||
<view class="status-bar">
|
||
<text>电子秤: {{ scaleConnected ? '已连接' : '未连接' }}</text>
|
||
<text>重量: {{ weightValue }} g</text>
|
||
</view>
|
||
|
||
<button type="primary" @click="initAndAutoConnect">启动蓝牙并自动连接</button>
|
||
<button @click="manualScanPrinter">手动扫描打印机</button>
|
||
<button @click="manualScanScale">手动扫描电子秤</button>
|
||
<button @click="printTest">打印测试</button>
|
||
<button @click="disconnectAll">断开所有连接</button>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import bluetoothManager from '@/utils/BluetoothManager.js';
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
// 打印机相关
|
||
printerDeviceId: null,
|
||
printerConnected: false,
|
||
lastPrinterMac: '',
|
||
// 电子秤相关
|
||
scaleDeviceId: null,
|
||
scaleConnected: false,
|
||
weightValue: '--',
|
||
// UUID 配置(请替换为实际值)
|
||
printerServiceUUID: '0000EEE2-0000-1000-8000-00805F9B34FB',
|
||
printerWriteUUID: '0000EEE3-0000-1000-8000-00805F9B34FB',
|
||
scaleServiceUUID: 'FFF0',
|
||
scaleNotifyUUID: 'FFF1',
|
||
};
|
||
},
|
||
onLoad() {
|
||
const config = uni.getStorageSync('bleDeviceConfig');
|
||
if (config && config.deviceId) {
|
||
this.printerServiceUUID = config.serviceId;
|
||
this.printerWriteUUID = config.writeCharId;
|
||
this.lastPrinterMac = config.deviceId;
|
||
// 如果配置了 notify UUID,也可用于电子秤等
|
||
this.initAndAutoConnect();
|
||
} else {
|
||
// 没有配置,引导用户去配置页面
|
||
uni.showModal({
|
||
title: '未配置设备',
|
||
content: '请先配置蓝牙设备',
|
||
success: () => {
|
||
uni.navigateTo({ url: '/pages/testpages/bleconfig' });
|
||
},
|
||
});
|
||
}
|
||
},
|
||
onUnload() {
|
||
// 页面卸载时释放所有连接(可选,也可保持连接)
|
||
bluetoothManager.closeAll();
|
||
},
|
||
methods: {
|
||
/**
|
||
* 初始化蓝牙并自动连接上次的打印机
|
||
*/
|
||
async initAndAutoConnect() {
|
||
try {
|
||
await bluetoothManager.init();
|
||
console.log('蓝牙初始化成功');
|
||
|
||
// 设置全局回调
|
||
bluetoothManager.setConnectionChangeCallback(this.onConnectionChange);
|
||
bluetoothManager.setDataReceivedCallback(this.onDataReceived);
|
||
|
||
if (this.lastPrinterMac) {
|
||
uni.showLoading({ title: '正在连接上次打印机...' });
|
||
try {
|
||
const info = await bluetoothManager.connectKnownDevice(
|
||
this.lastPrinterMac,
|
||
this.printerServiceUUID,
|
||
this.printerWriteUUID
|
||
);
|
||
this.printerDeviceId = this.lastPrinterMac;
|
||
this.printerConnected = true;
|
||
uni.hideLoading();
|
||
uni.showToast({ title: '打印机已自动连接', icon: 'success' });
|
||
} catch (err) {
|
||
uni.hideLoading();
|
||
console.error('自动连接失败', err);
|
||
uni.showModal({
|
||
title: '连接失败',
|
||
content: `上次使用的打印机 (${this.lastPrinterMac}) 未找到,是否重新扫描?`,
|
||
success: (res) => {
|
||
if (res.confirm) this.manualScanPrinter();
|
||
},
|
||
});
|
||
}
|
||
}
|
||
} catch (err) {
|
||
console.error('蓝牙初始化失败', err);
|
||
uni.showToast({ title: '请打开蓝牙并授权', icon: 'none' });
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 手动扫描打印机(用于首次或自动连接失败后的补救)
|
||
*/
|
||
async manualScanPrinter() {
|
||
try {
|
||
await bluetoothManager.init();
|
||
bluetoothManager.setDeviceFoundCallback(this.onPrinterFound);
|
||
await bluetoothManager.stopScan();
|
||
await bluetoothManager.startScan([this.printerServiceUUID]);
|
||
uni.showToast({ title: '扫描打印机中...', icon: 'none' });
|
||
// 20秒后自动停止扫描
|
||
setTimeout(() => {
|
||
bluetoothManager.stopScan();
|
||
}, 20000);
|
||
} catch (err) {
|
||
console.error('扫描启动失败', err);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 手动扫描电子秤
|
||
*/
|
||
async manualScanScale() {
|
||
try {
|
||
await bluetoothManager.init();
|
||
bluetoothManager.setDeviceFoundCallback(this.onScaleFound);
|
||
await bluetoothManager.stopScan();
|
||
await bluetoothManager.startScan([this.scaleServiceUUID]);
|
||
uni.showToast({ title: '扫描电子秤中...', icon: 'none' });
|
||
setTimeout(() => {
|
||
bluetoothManager.stopScan();
|
||
}, 20000);
|
||
} catch (err) {
|
||
console.error('扫描电子秤失败', err);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 打印机发现回调
|
||
*/
|
||
onPrinterFound(device) {
|
||
const name = device.name || '';
|
||
// 根据名称过滤器(可修改为你的打印机名称关键字)
|
||
if (name.includes('QR380A-15E0_Ble')) {
|
||
bluetoothManager.stopScan(); // 发现后停止扫描
|
||
uni.showModal({
|
||
title: '发现打印机',
|
||
content: `设备: ${name}\nMAC: ${device.deviceId}\n是否连接?`,
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
this.connectAndSavePrinter(device.deviceId, name);
|
||
} else {
|
||
// 用户取消,可以继续扫描(可选)
|
||
this.manualScanPrinter();
|
||
}
|
||
},
|
||
});
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 电子秤发现回调
|
||
*/
|
||
onScaleFound(device) {
|
||
const name = device.name || '';
|
||
if (name.includes('Scale') || name.includes('Weight')) {
|
||
bluetoothManager.stopScan();
|
||
uni.showModal({
|
||
title: '发现电子秤',
|
||
content: `设备: ${name}\nMAC: ${device.deviceId}\n是否连接?`,
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
this.connectScale(device.deviceId);
|
||
}
|
||
},
|
||
});
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 连接打印机并保存 MAC
|
||
*/
|
||
async connectAndSavePrinter(deviceId, deviceName) {
|
||
try {
|
||
uni.showLoading({ title: '连接打印机中...' });
|
||
const info = await bluetoothManager.connectKnownDevice(
|
||
deviceId,
|
||
this.printerServiceUUID,
|
||
this.printerWriteUUID
|
||
);
|
||
this.printerDeviceId = deviceId;
|
||
this.printerConnected = true;
|
||
// 保存 MAC
|
||
uni.setStorageSync('savedPrinterMac', deviceId);
|
||
this.lastPrinterMac = deviceId;
|
||
uni.hideLoading();
|
||
uni.showToast({ title: `打印机 ${deviceName} 已连接`, icon: 'success' });
|
||
} catch (err) {
|
||
uni.hideLoading();
|
||
console.error('连接打印机失败', err);
|
||
uni.showToast({ title: '连接失败', icon: 'none' });
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 连接电子秤(不需要保存,按需连接)
|
||
*/
|
||
async connectScale(deviceId) {
|
||
try {
|
||
uni.showLoading({ title: '连接电子秤...' });
|
||
const info = await bluetoothManager.connectKnownDevice(
|
||
deviceId,
|
||
this.scaleServiceUUID,
|
||
null, // 写入特征值可选
|
||
this.scaleNotifyUUID
|
||
);
|
||
this.scaleDeviceId = deviceId;
|
||
this.scaleConnected = true;
|
||
uni.hideLoading();
|
||
uni.showToast({ title: '电子秤已连接', icon: 'success' });
|
||
} catch (err) {
|
||
uni.hideLoading();
|
||
console.error('连接电子秤失败', err);
|
||
uni.showToast({ title: '连接失败', icon: 'none' });
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 全局连接状态变化回调
|
||
*/
|
||
onConnectionChange(deviceId, connected) {
|
||
if (deviceId === this.printerDeviceId) {
|
||
this.printerConnected = connected;
|
||
if (!connected) {
|
||
uni.showToast({ title: '打印机断开', icon: 'none' });
|
||
// 可选:自动重连(这里不做,避免频繁请求)
|
||
}
|
||
}
|
||
if (deviceId === this.scaleDeviceId) {
|
||
this.scaleConnected = connected;
|
||
if (!connected) {
|
||
uni.showToast({ title: '电子秤断开', icon: 'none' });
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 全局数据接收回调(接收电子秤重量数据)
|
||
*/
|
||
onDataReceived(deviceId, value) {
|
||
if (deviceId === this.scaleDeviceId) {
|
||
// 将 ArrayBuffer 转为字符串(根据电子秤协议解析)
|
||
const data = this.ab2str(value);
|
||
console.log('电子秤原始数据:', data);
|
||
// 示例:假设秤返回 "1234g" 或 "12.34kg"
|
||
const weight = this.parseWeight(data);
|
||
if (weight !== null) {
|
||
this.weightValue = weight;
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 打印测试
|
||
*/
|
||
async printTest() {
|
||
if (!this.printerConnected) {
|
||
uni.showToast({ title: '打印机未连接', icon: 'none' });
|
||
return;
|
||
}
|
||
try {
|
||
// 示例:发送纯文本(实际需组装打印机指令,如 ESC/POS 或 CPCL)
|
||
const text = '测试打印\n自动连接成功\n时间: ' + new Date().toLocaleString() + '\n\n';
|
||
const buffer = this.str2ab(text);
|
||
await bluetoothManager.writeData(this.printerDeviceId, buffer);
|
||
uni.showToast({ title: '打印指令已发送', icon: 'success' });
|
||
} catch (err) {
|
||
console.error('打印失败', err);
|
||
uni.showToast({ title: '打印失败', icon: 'none' });
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 断开所有连接
|
||
*/
|
||
async disconnectAll() {
|
||
await bluetoothManager.closeAll();
|
||
this.printerDeviceId = null;
|
||
this.printerConnected = false;
|
||
this.scaleDeviceId = null;
|
||
this.scaleConnected = false;
|
||
this.weightValue = '--';
|
||
uni.showToast({ title: '已断开所有连接', icon: 'success' });
|
||
},
|
||
|
||
// ---------- 辅助函数 ----------
|
||
str2ab(str) {
|
||
const buf = new ArrayBuffer(str.length);
|
||
const view = new Uint8Array(buf);
|
||
for (let i = 0; i < str.length; i++) {
|
||
view[i] = str.charCodeAt(i);
|
||
}
|
||
return buf;
|
||
},
|
||
ab2str(buffer) {
|
||
return String.fromCharCode.apply(null, new Uint8Array(buffer));
|
||
},
|
||
parseWeight(dataStr) {
|
||
// 修改为你的电子秤实际解析规则
|
||
const match = dataStr.match(/(\d+(?:\.\d+)?)/);
|
||
return match ? match[1] : null;
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style>
|
||
.container {
|
||
padding: 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
}
|
||
.status-bar {
|
||
background: #f5f5f5;
|
||
padding: 10px;
|
||
border-radius: 8px;
|
||
margin-bottom: 10px;
|
||
}
|
||
button {
|
||
margin: 5px 0;
|
||
}
|
||
</style> |