合入小程序修改内容

This commit is contained in:
HycJack
2026-05-06 03:37:08 +08:00
parent 326e4bac28
commit 1e7e7cc05e
16 changed files with 1072 additions and 450 deletions

View File

@@ -1,17 +1,53 @@
import { View, Text, Switch, Slider, Image } from '@tarojs/components'
import { View, Text, Slider, Image, Switch } from '@tarojs/components'
import { useState } from 'react'
import Taro, { useDidShow } from '@tarojs/taro'
import { getToken } from '@/services/auth'
import { Binding, loadCurrentChildBindingContext } from '@/services/binding'
import { Child } from '@/services/child'
import { DeviceStatus, getDeviceStatus, setDeviceVolume } from '@/services/device'
import './index.scss'
function formatTime(value?: string | null): string {
if (!value) return '--'
const date = new Date(value)
if (Number.isNaN(date.getTime())) return value
const month = `${date.getMonth() + 1}`.padStart(2, '0')
const day = `${date.getDate()}`.padStart(2, '0')
const hour = `${date.getHours()}`.padStart(2, '0')
const minute = `${date.getMinutes()}`.padStart(2, '0')
return `${month}-${day} ${hour}:${minute}`
}
function formatCoordinates(status: DeviceStatus | null): string {
if (!status || status.lat === null || status.lat === undefined || status.lng === null || status.lng === undefined) {
return '--'
}
return `${status.lat.toFixed(6)}, ${status.lng.toFixed(6)}`
}
function getSignalLabel(value?: number | null): string {
if (value === null || value === undefined) return '--'
const normalized = Number(value)
if (!Number.isFinite(normalized)) return '--'
if (normalized <= 4) {
if (normalized >= 3) return '强'
if (normalized >= 2) return '中'
return '弱'
}
if (normalized >= 67) return '强'
if (normalized >= 34) return '中'
return '弱'
}
export default function Device() {
const [binding, setBinding] = useState<Binding | null>(null)
const [child, setChild] = useState<Child | null>(null)
const [deviceStatus, setDeviceStatus] = useState<DeviceStatus | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [sleepEnabled, setSleepEnabled] = useState(true)
const [volume, setVolume] = useState(50)
const [isDetailExpanded, setIsDetailExpanded] = useState(false)
const [isSavingVolume, setIsSavingVolume] = useState(false)
const [volumeValue, setVolumeValue] = useState(0)
useDidShow(() => {
void loadDeviceInfo()
@@ -28,6 +64,15 @@ export default function Device() {
const context = await loadCurrentChildBindingContext()
setChild(context.currentChild)
setBinding(context.currentBinding)
if (context.currentBinding?.device_id) {
const nextStatus = await getDeviceStatus(context.currentBinding.device_id)
setDeviceStatus(nextStatus)
setVolumeValue(nextStatus?.volume ?? 0)
} else {
setDeviceStatus(null)
setVolumeValue(0)
}
} catch (error: any) {
console.error('[device] load failed:', error)
Taro.showToast({
@@ -40,15 +85,45 @@ export default function Device() {
}
const handleSleepToggle = (e: any) => {
setSleepEnabled(e.detail.value)
const nextValue = Boolean(e.detail?.value)
setSleepEnabled(nextValue)
Taro.showToast({
title: e.detail.value ? '休眠展示已开启' : '休眠展示已关闭',
title: nextValue ? '休眠展示已开启' : '休眠展示已关闭',
icon: 'none',
})
}
const handleVolumeChange = (e: any) => {
setVolume(e.detail.value)
const handleToggleDetail = () => {
setIsDetailExpanded((current) => !current)
}
const handleVolumeChanging = (e: any) => {
setVolumeValue(Number(e.detail?.value || 0))
}
const handleVolumeCommit = async (e: any) => {
if (!binding?.device_id) return
const nextLevel = Number(e.detail?.value || 0)
const fallbackLevel = deviceStatus?.volume ?? volumeValue ?? 0
setVolumeValue(nextLevel)
setIsSavingVolume(true)
try {
await setDeviceVolume(nextLevel, binding.device_id)
setDeviceStatus((current) => (current ? { ...current, volume: nextLevel } : current))
Taro.showToast({
title: '音量指令已发送',
icon: 'success',
})
} catch (error: any) {
setVolumeValue(deviceStatus?.volume ?? fallbackLevel)
Taro.showToast({
title: error?.message || '音量设置失败',
icon: 'none',
})
} finally {
setIsSavingVolume(false)
}
}
if (isLoading) {
@@ -115,8 +190,12 @@ export default function Device() {
const childName = child.child_name || '未设置'
const pageTitle = `${childName} 的伴伴`
const battery = 82
const daysLeft = 4
const batteryValue = deviceStatus?.power ?? deviceStatus?.battery_pct ?? null
const batteryDisplay = batteryValue === null || batteryValue === undefined ? '--' : String(batteryValue)
const signalLabel = getSignalLabel(deviceStatus?.signal)
const versionLabel = deviceStatus?.version || '--'
const coordinateLabel = formatCoordinates(deviceStatus)
const volumeLabel = `${volumeValue}%`
return (
<View className='device-page'>
@@ -131,10 +210,20 @@ export default function Device() {
</View>
<View className='battery-section'>
<Text className='battery-percent'>
{battery}
<Text className='percent'>%</Text>
{batteryDisplay}
{batteryDisplay !== '--' && <Text className='percent'>%</Text>}
</Text>
<Text className='battery-label'> ( {daysLeft} )</Text>
<Text className='battery-label'></Text>
</View>
<View className='status-meta'>
<View className='status-pill'>
<Text className='status-pill-label'></Text>
<Text className='status-pill-value'>{signalLabel}</Text>
</View>
<View className='status-pill'>
<Text className='status-pill-label'></Text>
<Text className='status-pill-value'>{versionLabel}</Text>
</View>
</View>
<View className='device-avatar'>
<View className='robot-icon'>
@@ -143,31 +232,12 @@ export default function Device() {
</View>
</View>
<View className='detail-card'>
<View className='detail-row'>
<Text className='detail-label'></Text>
<Text className='detail-value'>{childName}</Text>
</View>
<View className='detail-row'>
<Text className='detail-label'></Text>
<Text className='detail-value'>{binding.device_id}</Text>
</View>
<View className='detail-row'>
<Text className='detail-label'></Text>
<Text className='detail-value'>{binding.bound_at || '--'}</Text>
</View>
</View>
<View className='section-title'></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'
/>
<Image className='control-icon-img' src={require('../../assets/tab-icons/moon.png')} mode='aspectFit' />
</View>
<View className='control-info'>
<Text className='control-name'></Text>
@@ -189,7 +259,10 @@ export default function Device() {
mode='aspectFit'
/>
</View>
<Text className='control-name'></Text>
<View className='control-info'>
<Text className='control-name'></Text>
<Text className='control-detail'>{isSavingVolume ? '发送中...' : `当前 ${volumeLabel}`}</Text>
</View>
</View>
</View>
<View className='volume-slider'>
@@ -202,8 +275,9 @@ export default function Device() {
className='slider'
min={0}
max={100}
value={volume}
onChange={handleVolumeChange}
value={volumeValue}
onChanging={handleVolumeChanging}
onChange={handleVolumeCommit}
activeColor='#FF8C42'
backgroundColor='#E5E5E5'
blockColor='#FF8C42'
@@ -216,6 +290,45 @@ export default function Device() {
</View>
</View>
</View>
<View className='fold-card'>
<View className='fold-header' onClick={handleToggleDetail}>
<View className='fold-heading'>
<Text className='fold-title'></Text>
<Text className='fold-subtitle'>{binding.device_id}</Text>
</View>
<Text className='fold-action'>{isDetailExpanded ? '收起' : '展开'}</Text>
</View>
{isDetailExpanded && (
<View className='detail-body'>
<View className='detail-row'>
<Text className='detail-label'></Text>
<Text className='detail-value'>{childName}</Text>
</View>
<View className='detail-row'>
<Text className='detail-label'></Text>
<Text className='detail-value'>{binding.device_id}</Text>
</View>
<View className='detail-row'>
<Text className='detail-label'></Text>
<Text className='detail-value'>{formatTime(binding.bound_at)}</Text>
</View>
<View className='detail-row'>
<Text className='detail-label'></Text>
<Text className='detail-value'>{coordinateLabel}</Text>
</View>
<View className='detail-row'>
<Text className='detail-label'></Text>
<Text className='detail-value'>{formatTime(deviceStatus?.settings_updated_at)}</Text>
</View>
<View className='detail-row'>
<Text className='detail-label'></Text>
<Text className='detail-value'>{formatTime(deviceStatus?.location_updated_at)}</Text>
</View>
</View>
)}
</View>
</View>
)
}