add banbanmini

This commit is contained in:
HycJack
2026-03-24 13:15:03 +08:00
parent 42567b7605
commit 0d0f995dc2
59 changed files with 26319 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationStyle: 'custom'
})

View File

@@ -0,0 +1,291 @@
.device-page {
min-height: 100%;
background: #F5F7FA;
padding-bottom: 160px;
padding-bottom: calc(160px + constant(safe-area-inset-bottom));
padding-bottom: calc(160px + env(safe-area-inset-bottom));
}
.page-header {
padding: 80px 32px 24px;
.page-title {
font-size: 48px;
font-weight: 700;
color: #1A1A1A;
display: block;
margin-bottom: 12px;
line-height: 1.2;
}
.page-subtitle {
font-size: 28px;
color: #666666;
line-height: 1.4;
}
}
.status-card {
background: linear-gradient(135deg, #FF8C42 0%, #FF6B35 100%);
border-radius: 28px;
padding: 40px 36px;
margin: 0 24px 32px;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
right: -20px;
top: 50%;
transform: translateY(-50%);
width: 200px;
height: 200px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
}
}
.status-badge {
display: inline-flex;
align-items: center;
background: rgba(255, 255, 255, 0.25);
border-radius: 20px;
padding: 10px 20px;
margin-bottom: 24px;
.status-dot {
width: 12px;
height: 12px;
background: #4CD964;
border-radius: 50%;
margin-right: 8px;
}
.status-text {
font-size: 24px;
color: #FFFFFF;
font-weight: 500;
}
}
.battery-section {
.battery-percent {
font-size: 72px;
font-weight: 700;
color: #FFFFFF;
line-height: 1;
margin-bottom: 12px;
display: block;
.percent {
font-size: 32px;
font-weight: 600;
margin-left: 4px;
}
}
.battery-label {
font-size: 26px;
color: rgba(255, 255, 255, 0.9);
}
}
.device-avatar {
position: absolute;
right: 36px;
top: 50%;
transform: translateY(-50%);
width: 120px;
height: 120px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 3px solid rgba(255, 255, 255, 0.3);
}
.robot-icon {
.robot-img {
width: 56px;
height: 56px;
background: #FFFFFF;
border-radius: 50%;
}
}
.section-title {
font-size: 32px;
font-weight: 700;
color: #1A1A1A;
margin: 0 24px 20px;
display: block;
}
.control-card {
background: #FFFFFF;
border-radius: 16px;
margin: 0 24px 24px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
overflow: hidden;
}
.control-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24px;
&.volume-item {
flex-direction: column;
align-items: stretch;
padding: 20px 24px 24px;
}
.volume-header {
display: flex;
align-items: center;
margin-bottom: 16px;
.control-left {
flex: 1;
}
}
.control-left {
display: flex;
align-items: center;
flex: 1;
.icon-bg {
width: 72px;
height: 72px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20px;
&.purple {
background: #F0E6FF;
}
&.orange {
background: #FFF0E6;
}
.control-icon-img {
width: 36px;
height: 36px;
}
}
.control-info {
display: flex;
flex-direction: column;
.control-name {
font-size: 32px;
font-weight: 600;
color: #1A1A1A;
margin-bottom: 6px;
}
.control-detail {
font-size: 26px;
color: #FF8C42;
font-weight: 500;
}
}
}
}
.volume-slider {
display: flex;
align-items: center;
padding: 0 8px;
.volume-icon {
width: 24px;
height: 24px;
flex-shrink: 0;
}
.slider {
flex: 1;
margin: 0 16px;
}
}
.divider {
height: 1px;
background: #F0F0F0;
margin: 0 24px;
}
.quick-card {
background: #FFFFFF;
border-radius: 24px;
margin: 0 24px 24px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
overflow: hidden;
}
.quick-grid {
display: flex;
justify-content: space-around;
padding: 24px 16px;
}
.quick-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 16px;
&:active {
opacity: 0.7;
}
}
.quick-icon {
width: 88px;
height: 88px;
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
margin-bottom: 12px;
&.bg-orange {
background: #FEF3E8;
}
&.bg-green {
background: #E8F8F0;
}
&.bg-blue {
background: #E8F0FE;
}
}
.quick-text {
font-size: 26px;
color: #666666;
font-weight: 500;
}
.loading {
min-height: 100%;
display: flex;
align-items: center;
justify-content: center;
text {
font-size: 32px;
color: #999999;
}
}

View File

@@ -0,0 +1,174 @@
import { View, Text, Switch, Slider, Image } from '@tarojs/components'
import { useState, useEffect } from 'react'
import Taro from '@tarojs/taro'
import './index.scss'
interface DeviceData {
id: string
name: string
status: 'online' | 'offline'
battery: number
daysLeft: number
sleepEnabled: boolean
sleepTime: string
volume: number
}
export default function Device() {
const [device, setDevice] = useState<DeviceData | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [hasDevice, setHasDevice] = useState(false)
useEffect(() => {
// 检查登录状态
const isLoggedIn = Taro.getStorageSync('isLoggedIn')
if (!isLoggedIn) {
Taro.redirectTo({ url: '/pages/login/index' })
return
}
// 检查设备绑定状态
const deviceInfo = Taro.getStorageSync('deviceInfo')
const hasDeviceFlag = Taro.getStorageSync('hasDevice')
setHasDevice(!!hasDeviceFlag)
if (deviceInfo && hasDeviceFlag) {
setDevice({
id: deviceInfo.id,
name: deviceInfo.name || '小夏的伴伴',
status: deviceInfo.status || 'online',
battery: deviceInfo.battery || 82,
daysLeft: 4,
sleepEnabled: true,
sleepTime: '每天 22:00 - 07:00',
volume: 50
})
}
setIsLoading(false)
}, [])
const handleSleepToggle = (e: any) => {
if (!device) return
setDevice({ ...device, sleepEnabled: e.detail.value })
Taro.showToast({
title: e.detail.value ? '已开启定时休眠' : '已关闭定时休眠',
icon: 'success'
})
}
const handleVolumeChange = (e: any) => {
if (!device) return
setDevice({ ...device, volume: e.detail.value })
}
const switchTab = (url: string) => {
Taro.switchTab({ url })
}
if (isLoading) {
return (
<View className='device-page'>
<View className='loading'>
<Text>...</Text>
</View>
</View>
)
}
// 未绑定设备,跳转到绑定页面
if (!hasDevice || !device) {
Taro.redirectTo({ url: '/pages/bind/index' })
return null
}
return (
<View className='device-page'>
<View className='page-header'>
<Text className='page-title'>{device.name}</Text>
<Text className='page-subtitle'></Text>
</View>
<View className='status-card'>
<View className='status-badge'>
<View className='status-dot'></View>
<Text className='status-text'>4G 线</Text>
</View>
<View className='battery-section'>
<Text className='battery-percent'>{device.battery}<Text className='percent'>%</Text></Text>
<Text className='battery-label'> ( {device.daysLeft} )</Text>
</View>
<View className='device-avatar'>
<View className='robot-icon'>
<Image className='robot-img' src={require('../../assets/tab-icons/robot.png')} mode='aspectFit' />
</View>
</View>
</View>
<View className='section-title'></View>
<View className='control-card'>
<View className='control-item'>
<View className='control-left'>
<View className='icon-bg purple'>
<Image
className='control-icon-img'
src={require('../../assets/tab-icons/moon.png')}
mode='aspectFit'
/>
</View>
<View className='control-info'>
<Text className='control-name'></Text>
<Text className='control-detail'>{device.sleepTime}</Text>
</View>
</View>
<Switch
checked={device.sleepEnabled}
onChange={handleSleepToggle}
color='#FF8C42'
/>
</View>
<View className='divider'></View>
<View className='control-item volume-item'>
<View className='volume-header'>
<View className='control-left'>
<View className='icon-bg orange'>
<Image
className='control-icon-img'
src={require('../../assets/tab-icons/orange-speaker.png')}
mode='aspectFit'
/>
</View>
<Text className='control-name'></Text>
</View>
</View>
<View className='volume-slider'>
<Image
className='volume-icon'
src={require('../../assets/tab-icons/unspeaker.png')}
mode='aspectFit'
/>
<Slider
className='slider'
min={0}
max={100}
value={device.volume}
onChange={handleVolumeChange}
activeColor='#FF8C42'
backgroundColor='#E5E5E5'
blockColor='#FF8C42'
/>
<Image
className='volume-icon'
src={require('../../assets/tab-icons/orange-speaker.png')}
mode='aspectFit'
/>
</View>
</View>
</View>
</View>
)
}