Files
2026-06-02 15:28:25 +08:00

339 lines
11 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>