Files
webUI/src/pages/device/index.vue
2025-11-07 20:54:09 +08:00

585 lines
13 KiB
Vue
Raw 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="floral-container">
<!-- 夜空装饰背景 -->
<view class="night-sky-decoration">
<view class="star star-1"></view>
<view class="star star-2"></view>
<view class="star star-3"></view>
<view class="star star-4"></view>
<view class="star star-5"></view>
<view class="star star-6"></view>
<view class="star star-7"></view>
<view class="star star-8"></view>
</view>
<!-- 顶部自定义导航栏 -->
<view class="custom-navbar fixed-navbar">
<view class="navbar-stars">
<view class="navbar-star star-left"></view>
<view class="navbar-star star-right"></view>
</view>
<text class="navbar-title">我的设备</text>
</view>
<!-- 设备信息卡片 -->
<view class="device-card">
<view class="device-header">
<view class="device-icon" :class="{'connected': deviceConnected}">
{{ deviceConnected ? '🌟' : '⭕' }}
</view>
<view class="device-status">
<text class="status-text">{{ deviceConnected ? '已连接' : '未连接' }}</text>
<text class="status-indicator" :class="{'active': deviceConnected}"></text>
</view>
</view>
<view class="device-info">
<view class="info-row">
<text class="info-label">设备名称</text>
<text class="info-value">{{ deviceName }}</text>
</view>
<view class="info-row">
<text class="info-label">设备ID</text>
<text class="info-value">{{ deviceId }}</text>
</view>
<view class="info-row">
<text class="info-label">设备SN</text>
<text class="info-value">{{ deviceSN }}</text>
</view>
<view class="info-row" v-if="deviceConnected">
<text class="info-label">连接时间</text>
<text class="info-value">{{ connectTime }}</text>
</view>
</view>
<view class="device-actions">
<button
class="floral-btn"
:class="{'disconnect-btn': deviceConnected}"
@click="toggleConnection"
>
{{ deviceConnected ? '断开连接' : '连接设备' }}
</button>
<button
class="floral-btn outline refresh-btn"
@click="refreshDeviceStatus"
>
🔄 刷新状态
</button>
</view>
</view>
<!-- 未登录提示 -->
<view class="login-tip" v-if="!userStore.isLoggedIn">
<view class="tip-icon">🔐</view>
<text class="tip-text">请先登录以查看设备信息</text>
<button class="floral-btn small-btn" @click="goToLogin">去登录</button>
</view>
<!-- 设备功能列表 -->
<view class="feature-list" v-if="deviceConnected && userStore.isLoggedIn">
<view class="feature-title">设备功能</view>
<view class="feature-item">
<view class="feature-icon">📡</view>
<view class="feature-content">
<text class="feature-name">远程控制</text>
<text class="feature-desc">通过小程序远程控制设备</text>
</view>
</view>
<view class="feature-item">
<view class="feature-icon">🎤</view>
<view class="feature-content">
<text class="feature-name">语音交互</text>
<text class="feature-desc">与AI语音助手对话</text>
</view>
</view>
<view class="feature-item">
<view class="feature-icon">🔔</view>
<view class="feature-content">
<text class="feature-name">消息通知</text>
<text class="feature-desc">接收设备状态通知</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useUserStore } from '@/stores/user.js';
// 状态管理
const userStore = useUserStore();
const deviceConnected = ref(false);
const deviceName = ref('小智AI设备');
const deviceSN = ref('未连接');
const deviceId = ref('30:ed:a0:12:99:60');
const connectTime = ref('');
// 生命周期
onMounted(() => {
userStore.init();
// refreshDeviceStatus(); // API已注释不需要自动刷新
});
// 刷新设备状态
const refreshDeviceStatus = () => {
if (!userStore.isLoggedIn) {
uni.showToast({
title: '请先登录',
icon: 'none'
});
return;
}
// 暂时注释掉API请求使用本地模拟数据
uni.showToast({
title: '刷新成功(本地模拟)',
icon: 'success'
});
/* API请求已注释
uni.showLoading({
title: '获取设备状态...'
});
uni.request({
url: 'https://www.aixsy.com.cn/api/device/status',
method: 'GET',
header: {
'Authorization': userStore.token ? (userStore.token.startsWith('Bearer ') ? userStore.token : 'Bearer ' + userStore.token) : ''
},
success: (res) => {
console.log('设备状态响应:', res);
if (res.statusCode === 200 && res.data.data) {
const data = res.data.data;
deviceConnected.value = data.connected || false;
deviceSN.value = data.sn || '未知SN';
if (deviceConnected.value) {
connectTime.value = data.connectTime || new Date().toLocaleString();
}
uni.showToast({
title: '刷新成功',
icon: 'success'
});
}
},
fail: (err) => {
console.error('获取设备状态失败:', err);
uni.showToast({
title: '刷新失败',
icon: 'none'
});
},
complete: () => {
uni.hideLoading();
}
});
*/
};
// 连接/断开设备
const toggleConnection = () => {
if (!userStore.isLoggedIn) {
uni.showToast({
title: '请先登录',
icon: 'none'
});
return;
}
// 暂时注释掉API请求使用本地模拟数据
deviceConnected.value = !deviceConnected.value;
if (deviceConnected.value) {
connectTime.value = new Date().toLocaleString();
deviceSN.value = 'XZ' + Math.random().toString(36).substr(2, 8).toUpperCase();
uni.showToast({
title: '连接成功(本地模拟)',
icon: 'success'
});
} else {
connectTime.value = '';
deviceSN.value = '未连接';
uni.showToast({
title: '已断开连接(本地模拟)',
icon: 'success'
});
}
/* API请求已注释
uni.showLoading({
title: deviceConnected.value ? '断开连接中...' : '连接设备中...'
});
const token = userStore.token;
const requestData = {
deviceId: deviceId.value
};
uni.request({
url: `https://www.aixsy.com.cn/api/device/${deviceConnected.value ? 'disconnect' : 'connect'}`,
method: 'POST',
header: {
'Content-Type': 'application/json',
'Authorization': token ? (token.startsWith('Bearer ') ? token : 'Bearer ' + token) : ''
},
data: requestData,
success: (res) => {
console.log('设备操作成功:', res);
if (res.statusCode === 200) {
deviceConnected.value = !deviceConnected.value;
if (deviceConnected.value) {
connectTime.value = new Date().toLocaleString();
deviceSN.value = res.data.data?.sn || 'XZ' + Math.random().toString(36).substr(2, 8).toUpperCase();
uni.showToast({
title: '连接成功',
icon: 'success'
});
} else {
connectTime.value = '';
deviceSN.value = '未连接';
uni.showToast({
title: '已断开连接',
icon: 'success'
});
}
}
},
fail: (err) => {
console.error('设备操作失败:', err);
uni.showToast({
title: deviceConnected.value ? '断开失败' : '连接失败',
icon: 'none'
});
},
complete: () => {
uni.hideLoading();
}
});
*/
};
// 去登录
const goToLogin = () => {
uni.switchTab({
url: '/pages/mine/mine'
});
};
</script>
<style lang="scss" scoped>
.floral-container {
min-height: 100vh;
background: linear-gradient(180deg, #1a0b2e 0%, #2d1b4e 50%, #1a0b2e 100%);
padding-bottom: 100rpx;
position: relative;
overflow: hidden;
}
/* 夜空装饰 */
.night-sky-decoration {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 0;
}
.star {
position: absolute;
width: 6rpx;
height: 6rpx;
background: #f9e076;
border-radius: 50%;
animation: twinkle 2s infinite;
box-shadow: 0 0 10rpx #f9e076;
}
@keyframes twinkle {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
.star-1 { top: 10%; left: 15%; animation-delay: 0s; }
.star-2 { top: 20%; left: 80%; animation-delay: 0.3s; }
.star-3 { top: 30%; left: 45%; animation-delay: 0.6s; }
.star-4 { top: 50%; left: 25%; animation-delay: 0.9s; }
.star-5 { top: 60%; left: 70%; animation-delay: 1.2s; }
.star-6 { top: 70%; left: 40%; animation-delay: 1.5s; }
.star-7 { top: 15%; left: 60%; animation-delay: 0.4s; }
.star-8 { top: 85%; left: 55%; animation-delay: 1.8s; }
/* 导航栏 */
.custom-navbar {
position: sticky;
top: 0;
left: 0;
right: 0;
height: 88rpx;
background: rgba(26, 11, 46, 0.95);
backdrop-filter: blur(20rpx);
display: flex;
align-items: center;
justify-content: center;
padding: 0 30rpx;
z-index: 1000;
border-bottom: 1rpx solid rgba(249, 224, 118, 0.1);
}
.navbar-stars {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}
.navbar-star {
position: absolute;
width: 8rpx;
height: 8rpx;
background: #f9e076;
border-radius: 50%;
box-shadow: 0 0 15rpx #f9e076;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.5; transform: scale(1); }
50% { opacity: 1; transform: scale(1.2); }
}
.star-left {
top: 50%;
left: 30rpx;
transform: translateY(-50%);
}
.star-right {
top: 50%;
right: 30rpx;
transform: translateY(-50%);
}
.navbar-title {
font-size: 36rpx;
font-weight: bold;
color: #f9e076;
text-shadow: 0 0 20rpx rgba(249, 224, 118, 0.5);
}
/* 设备卡片 */
.device-card {
margin: 40rpx 30rpx;
background: linear-gradient(135deg, rgba(249, 224, 118, 0.1) 0%, rgba(249, 224, 118, 0.05) 100%);
border-radius: 30rpx;
padding: 40rpx;
border: 2rpx solid rgba(249, 224, 118, 0.2);
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.3);
position: relative;
z-index: 1;
}
.device-header {
display: flex;
align-items: center;
margin-bottom: 30rpx;
padding-bottom: 30rpx;
border-bottom: 1rpx solid rgba(249, 224, 118, 0.1);
}
.device-icon {
font-size: 80rpx;
margin-right: 30rpx;
transition: all 0.3s;
}
.device-icon.connected {
animation: rotate 2s infinite linear;
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.device-status {
display: flex;
align-items: center;
gap: 20rpx;
}
.status-text {
font-size: 32rpx;
font-weight: bold;
color: #f9e076;
}
.status-indicator {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background: rgba(249, 224, 118, 0.3);
}
.status-indicator.active {
background: #4caf50;
animation: blink 1s infinite;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
.device-info {
margin-bottom: 30rpx;
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid rgba(249, 224, 118, 0.05);
}
.info-row:last-child {
border-bottom: none;
}
.info-label {
font-size: 28rpx;
color: rgba(249, 224, 118, 0.6);
}
.info-value {
font-size: 28rpx;
color: #f9e076;
font-weight: 500;
}
.device-actions {
display: flex;
gap: 20rpx;
}
.floral-btn {
flex: 1;
height: 80rpx;
line-height: 80rpx;
background: linear-gradient(135deg, #f9e076 0%, #f5d042 100%);
color: #1a0b2e;
font-weight: bold;
font-size: 28rpx;
border-radius: 40rpx;
border: none;
box-shadow: 0 8rpx 20rpx rgba(249, 224, 118, 0.3);
transition: all 0.3s;
}
.floral-btn:active {
transform: scale(0.95);
box-shadow: 0 4rpx 10rpx rgba(249, 224, 118, 0.3);
}
.disconnect-btn {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
color: white;
box-shadow: 0 8rpx 20rpx rgba(255, 107, 107, 0.3);
}
.outline {
background: transparent;
color: #f9e076;
border: 2rpx solid #f9e076;
box-shadow: none;
}
.refresh-btn {
flex: 0 0 auto;
width: 180rpx;
}
/* 未登录提示 */
.login-tip {
margin: 40rpx 30rpx;
padding: 60rpx 40rpx;
background: rgba(249, 224, 118, 0.05);
border-radius: 30rpx;
border: 2rpx dashed rgba(249, 224, 118, 0.3);
display: flex;
flex-direction: column;
align-items: center;
gap: 20rpx;
position: relative;
z-index: 1;
}
.tip-icon {
font-size: 80rpx;
}
.tip-text {
font-size: 28rpx;
color: rgba(249, 224, 118, 0.8);
}
.small-btn {
width: 200rpx;
margin-top: 20rpx;
}
/* 功能列表 */
.feature-list {
margin: 40rpx 30rpx;
position: relative;
z-index: 1;
}
.feature-title {
font-size: 32rpx;
font-weight: bold;
color: #f9e076;
margin-bottom: 30rpx;
padding-left: 20rpx;
border-left: 6rpx solid #f9e076;
}
.feature-item {
display: flex;
align-items: center;
gap: 30rpx;
padding: 30rpx;
margin-bottom: 20rpx;
background: linear-gradient(135deg, rgba(249, 224, 118, 0.08) 0%, rgba(249, 224, 118, 0.03) 100%);
border-radius: 20rpx;
border: 1rpx solid rgba(249, 224, 118, 0.1);
}
.feature-icon {
font-size: 60rpx;
}
.feature-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 10rpx;
}
.feature-name {
font-size: 30rpx;
font-weight: bold;
color: #f9e076;
}
.feature-desc {
font-size: 24rpx;
color: rgba(249, 224, 118, 0.6);
}
</style>