585 lines
13 KiB
Vue
585 lines
13 KiB
Vue
<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>
|