feat: init

This commit is contained in:
2025-11-07 20:54:09 +08:00
parent d45e556c20
commit 17570c7821
11 changed files with 3682 additions and 1071 deletions

584
src/pages/device/index.vue Normal file
View File

@@ -0,0 +1,584 @@
<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>