Files
uni-pda/pages/settings/devices/scaleTest.vue
T
2026-06-02 15:28:25 +08:00

589 lines
13 KiB
Vue

<template>
<view class="container">
<!-- 状态栏 -->
<view class="status-section">
<view class="status-item">
<text class="status-label">称重设备</text>
<text class="status-value" :class="{ connected: connected }">
{{ connected ? '已连接' : '未连接' }}
</text>
</view>
<view class="status-item" v-if="connected">
<text class="status-label">设备名称</text>
<text class="status-value device-name">{{ scaleConfig.deviceName }}</text>
</view>
</view>
<!-- 重量显示 -->
<view class="weight-display-section">
<view class="weight-box" :class="{ stable: currentWeight && currentWeight.stable }">
<text class="weight-value">{{ displayWeight }}</text>
<text class="weight-unit">{{ currentWeight && currentWeight.unit || 'kg' }}</text>
</view>
<view class="weight-status">
<text class="status-indicator" :class="{ active: currentWeight && currentWeight.stable }">
{{ currentWeight && currentWeight.stable ? '稳定' : '变化中' }}
</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="section action-section" v-if="!connected">
<view class="connecting-text" v-if="isConnecting">{{ connectingText }}</view>
<button class="btn btn-primary" :loading="isConnecting" @click="handleConnect">
{{ isConnecting ? '连接中...' : '重新连接' }}
</button>
<button class="btn btn-outline" @click="goToConfig">配置设备</button>
</view>
<!-- 调试信息 -->
<view class="section log-section" v-if="showLogs">
<view class="section-header">
<text class="section-title">调试日志</text>
<text class="section-action" @click="clearLogs">清空</text>
</view>
<scroll-view class="log-list" scroll-y>
<text class="log-item" v-for="(log, index) in logs" :key="index" :class="log.level">
[{{ log.time }}] {{ log.message }}
</text>
</scroll-view>
</view>
<!-- 断开连接 -->
<view class="section" v-if="connected">
<button class="btn btn-outline" @click="handleDisconnect">断开连接</button>
</view>
<!-- 校准弹窗 -->
<view class="modal-mask" v-if="showCalibrate" @click="hideCalibrateModal">
<view class="modal-content" @click.stop>
<view class="modal-header">
<text class="modal-title">砝码校准</text>
</view>
<view class="modal-body">
<text class="calibrate-tip">请在秤上放置标准砝码</text>
<input
class="calibrate-input"
type="digit"
v-model="calibrateWeight"
placeholder="输入砝码重量(kg)"
/>
</view>
<view class="modal-footer">
<button class="btn btn-outline" @click="hideCalibrateModal">取消</button>
<button class="btn btn-primary" @click="confirmCalibrate">开始校准</button>
</view>
</view>
</view>
</view>
</template>
<script>
import scaleService from '@/utils/BluetoothScaleService.js';
import { mapGetters, mapActions } from 'vuex';
export default {
data() {
return {
isConnecting: false,
isTaring: false,
isCalibrating: false,
connected: false,
connectingText: '正在检查配置...',
scaleConfig: {},
currentWeight: null,
showCalibrate: false,
calibrateWeight: '1',
showLogs: false,
logs: []
}
},
computed: {
...mapGetters('scale', ['formattedWeight']),
displayWeight() {
if (!this.currentWeight || this.currentWeight.value === null) {
return '0.00';
}
return this.currentWeight.value.toFixed(2);
}
},
onLoad() {
this.loadScaleConfig();
this.checkAndConnect();
},
onShow() {
// 如果之前未连接,尝试重新连接
if (!this.connected && !this.isConnecting) {
this.checkAndConnect();
}
// 更新当前重量显示
if (scaleService.isConnected) {
this.currentWeight = scaleService.getCurrentWeight();
}
},
onUnload() {
// 退出页面时断开连接
this.handleDisconnect();
},
beforeDestroy() {
scaleService.offWeightChange(this.handleWeightChange);
scaleService.offConnectionChange(this.handleConnectionChange);
scaleService.offError(this.handleError);
},
mounted() {
// 注册重量变化回调
scaleService.onWeightChange(this.handleWeightChange);
// 注册连接状态回调
scaleService.onConnectionChange(this.handleConnectionChange);
// 注册错误回调
scaleService.onError(this.handleError);
},
methods: {
// ...mapActions('scale', ['setWeightValue']),
handleWeightChange(weight) {
this.currentWeight = weight;
// 同步到store
// this.setWeightValue(weight);
},
handleConnectionChange(connected) {
this.connected = connected;
if (connected) {
this.loadScaleConfig();
}
},
handleError(error) {
this.addLog(`错误: ${error.message}`, 'error');
},
addLog(message, level = 'info') {
const now = new Date();
const time = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
this.logs.unshift({ time, message, level });
// 只保留最近50条日志
if (this.logs.length > 50) {
this.logs.pop();
}
},
clearLogs() {
this.logs = [];
},
loadScaleConfig() {
const storeState = uni.getStorageSync('scaleConfig') || {};
const serviceConfig = scaleService.getConfig();
this.scaleConfig = {
deviceId: serviceConfig.deviceId || storeState.deviceId || '',
serviceId: serviceConfig.serviceId || storeState.serviceId || '',
notifyCharId: serviceConfig.notifyCharId || storeState.characteristicId || '',
writeCharId: serviceConfig.writeCharId || storeState.writeCharacteristicId || '',
deviceName: serviceConfig.deviceName || storeState.deviceName || ''
};
},
/**
* 检查配置并自动连接
*/
async checkAndConnect() {
this.loadScaleConfig();
// 检查是否有保存的配置
if (!this.scaleConfig.deviceId) {
this.connectingText = '未配置称重设备,请先配置设备';
return;
}
if (!this.scaleConfig.serviceId || !this.scaleConfig.notifyCharId) {
this.connectingText = '配置不完整,请重新配置设备';
return;
}
// 如果已连接,直接返回
if (scaleService.isConnected) {
this.connected = true;
return;
}
// 自动连接
this.isConnecting = true;
this.connectingText = '正在连接...';
try {
const success = await scaleService.autoConnect();
this.connected = success;
if (success) {
this.connectingText = '连接成功';
this.addLog('自动连接成功');
} else {
this.connectingText = '连接失败,请检查设备是否开启';
this.addLog('自动连接失败', 'error');
}
} catch (err) {
console.error('【称重】连接异常:', err);
this.connectingText = '连接异常: ' + (err.message || '未知错误');
this.addLog(`连接异常: ${err.message}`, 'error');
} finally {
this.isConnecting = false;
}
},
/**
* 手动连接
*/
async handleConnect() {
if (this.isConnecting) return;
this.isConnecting = true;
this.connectingText = '正在连接...';
this.loadScaleConfig();
if (!this.scaleConfig.deviceId) {
uni.showToast({ title: '请先配置称重设备', icon: 'none' });
this.connectingText = '未配置称重设备';
this.isConnecting = false;
return;
}
try {
const success = await scaleService.autoConnect();
this.connected = success;
if (success) {
uni.showToast({ title: '连接成功', icon: 'success' });
this.addLog('手动连接成功');
} else {
if (!this.scaleConfig.serviceId || !this.scaleConfig.notifyCharId) {
uni.showToast({ title: '配置不完整,请重新配置', icon: 'none' });
this.connectingText = '配置不完整';
} else {
uni.showToast({ title: '连接失败,请检查设备', icon: 'none' });
this.connectingText = '连接失败';
}
}
} catch (err) {
console.error('【称重】连接异常:', err);
uni.showToast({ title: '连接异常', icon: 'none' });
this.connectingText = '连接异常';
this.addLog(`连接异常: ${err.message}`, 'error');
} finally {
this.isConnecting = false;
}
},
/**
* 断开连接
*/
async handleDisconnect() {
try {
await scaleService.disconnect();
this.connected = false;
this.connectingText = '已断开连接';
this.addLog('已断开连接');
} catch (err) {
console.warn('【称重】断开连接失败:', err);
this.connected = false;
}
},
/**
* 跳转配置页面
*/
goToConfig() {
uni.navigateTo({
url: '/pages/settings/devices/scale'
});
},
/**
* 去皮操作
*/
async handleTare() {
if (this.isTaring) return;
this.isTaring = true;
try {
await scaleService.tare();
uni.showToast({ title: '去皮成功', icon: 'success' });
this.addLog('去皮操作成功');
} catch (err) {
console.error('【称重】去皮失败:', err);
uni.showToast({ title: '去皮失败', icon: 'none' });
this.addLog(`去皮失败: ${err.message}`, 'error');
} finally {
this.isTaring = false;
}
},
/**
* 显示校准弹窗
*/
showCalibrateModal() {
this.showCalibrate = true;
this.calibrateWeight = '1';
},
/**
* 隐藏校准弹窗
*/
hideCalibrateModal() {
this.showCalibrate = false;
},
/**
* 确认校准
*/
async confirmCalibrate() {
const weight = parseFloat(this.calibrateWeight);
if (isNaN(weight) || weight <= 0) {
uni.showToast({ title: '请输入有效的校准重量', icon: 'none' });
return;
}
this.isCalibrating = true;
this.showCalibrate = false;
try {
await scaleService.calibrate(weight);
uni.showToast({ title: '校准成功', icon: 'success' });
this.addLog(`校准成功: ${weight}kg`);
} catch (err) {
console.error('【称重】校准失败:', err);
uni.showToast({ title: '校准失败', icon: 'none' });
this.addLog(`校准失败: ${err.message}`, 'error');
} finally {
this.isCalibrating = false;
}
}
}
}
</script>
<style scoped>
.container {
padding: 20rpx;
background: #f5f5f5;
min-height: 100vh;
}
.status-section {
display: flex;
background: #fff;
padding: 30rpx;
border-radius: 12rpx;
margin-bottom: 20rpx;
}
.status-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.status-label {
font-size: 26rpx;
color: #999;
margin-bottom: 12rpx;
}
.status-value {
font-size: 32rpx;
font-weight: bold;
color: #666;
}
.status-value.connected {
color: #34c759;
}
.device-name {
font-size: 26rpx;
color: #34c759;
max-width: 300rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.weight-display-section {
background: #fff;
padding: 40rpx;
border-radius: 12rpx;
margin-bottom: 20rpx;
text-align: center;
}
.weight-box {
display: flex;
justify-content: center;
align-items: baseline;
padding: 30rpx;
background: #f8f9fa;
border-radius: 16rpx;
border: 2rpx solid #e9ecef;
transition: all 0.3s;
}
.weight-box.stable {
border-color: #34c759;
background: #f0fff4;
}
.weight-value {
font-size: 120rpx;
font-weight: bold;
color: #333;
line-height: 1;
}
.weight-unit {
font-size: 40rpx;
color: #666;
margin-left: 10rpx;
}
.weight-status {
margin-top: 20rpx;
}
.status-indicator {
display: inline-block;
padding: 8rpx 24rpx;
background: #fff3cd;
color: #856404;
border-radius: 20rpx;
font-size: 24rpx;
}
.status-indicator.active {
background: #d4edda;
color: #155724;
}
.section {
background: #fff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.section-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
}
.section-action {
font-size: 26rpx;
color: #1890ff;
}
.connecting-text {
text-align: center;
color: #666;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.action-section button {
width: 100%;
margin-bottom: 20rpx;
}
.action-section button:last-child {
margin-bottom: 0;
}
.btn-group {
display: flex;
gap: 20rpx;
}
.log-section {
background: #1e1e1e;
}
.log-section .section-title,
.log-section .section-action {
color: #fff;
}
.log-list {
height: 300rpx;
background: #2d2d2d;
border-radius: 8rpx;
padding: 16rpx;
}
.log-item {
display: block;
font-size: 22rpx;
color: #8c8c8c;
margin-bottom: 8rpx;
font-family: monospace;
}
.log-item.error {
color: #ff4d4f;
}
.btn {
border-radius: 8rpx;
font-size: 28rpx;
}
.btn-primary {
background: #1890ff;
color: #fff;
}
.btn-outline {
background: #fff;
color: #1890ff;
border: 1rpx solid #1890ff;
}
.btn::after {
border: none;
}
/* 模态框 */
.modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.modal-content {
width: 600rpx;
background: #fff;
border-radius: 16rpx;
overflow: hidden;
}
.modal-header {
padding: 30rpx;
text-align: center;
border-bottom: 1rpx solid #f0f0f0;
}
.modal-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.modal-body {
padding: 40rpx 30rpx;
}
.calibrate-tip {
display: block;
text-align: center;
color: #666;
font-size: 28rpx;
margin-bottom: 30rpx;
}
.calibrate-input {
width: 100%;
height: 80rpx;
border: 1rpx solid #d9d9d9;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
box-sizing: border-box;
}
.modal-footer {
display: flex;
gap: 20rpx;
padding: 30rpx;
border-top: 1rpx solid #f0f0f0;
}
.modal-footer .btn {
flex: 1;
}
</style>