227 lines
8.2 KiB
TypeScript
227 lines
8.2 KiB
TypeScript
import { getPageStyle } from '@/utils/page-style'
|
|
import { View, Text, Picker, Image } from '@tarojs/components'
|
|
import { useState } from 'react'
|
|
import Taro, { useDidShow } from '@tarojs/taro'
|
|
import { getToken } from '@/services/auth'
|
|
import { loadCurrentChildBindingContext, Binding } from '@/services/binding'
|
|
import { Child } from '@/services/child'
|
|
import { DeviceStatus, getDeviceStatus, setDeviceSleepSchedule } from '@/services/device'
|
|
import { useSystemBanner } from '@/components/system-banner/use-system-banner'
|
|
import { usePageShare } from '@/utils/page-share'
|
|
import './index.scss'
|
|
|
|
function formatSleepRange(status: DeviceStatus | null): string {
|
|
const start = status?.disable_time_start?.trim()
|
|
const end = status?.disable_time_end?.trim()
|
|
if (!start || !end) return '未设置'
|
|
return `${start}-${end}`
|
|
}
|
|
|
|
function formatUpdatedAt(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}`
|
|
}
|
|
|
|
export default function SleepSchedule() {
|
|
usePageShare()
|
|
const systemBanner = useSystemBanner()
|
|
const [isLoading, setIsLoading] = useState(true)
|
|
const [isSaving, setIsSaving] = useState(false)
|
|
const [binding, setBinding] = useState<Binding | null>(null)
|
|
const [child, setChild] = useState<Child | null>(null)
|
|
const [deviceStatus, setDeviceStatus] = useState<DeviceStatus | null>(null)
|
|
const [startTime, setStartTime] = useState('22:00')
|
|
const [endTime, setEndTime] = useState('07:00')
|
|
|
|
useDidShow(() => {
|
|
void loadData()
|
|
})
|
|
|
|
const loadData = async () => {
|
|
if (!getToken()) {
|
|
Taro.reLaunch({ url: '/pages/login/index' })
|
|
return
|
|
}
|
|
|
|
setIsLoading(true)
|
|
try {
|
|
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)
|
|
setStartTime(nextStatus?.disable_time_start || '22:00')
|
|
setEndTime(nextStatus?.disable_time_end || '07:00')
|
|
} else {
|
|
setDeviceStatus(null)
|
|
setStartTime('22:00')
|
|
setEndTime('07:00')
|
|
}
|
|
} catch (error: any) {
|
|
console.error('[sleep-schedule] load failed:', error)
|
|
Taro.showToast({
|
|
title: error?.message || '加载失败,请稍后重试',
|
|
icon: 'none',
|
|
})
|
|
} finally {
|
|
setIsLoading(false)
|
|
}
|
|
}
|
|
|
|
const handleSave = async () => {
|
|
if (!binding?.device_id) {
|
|
Taro.showToast({ title: '当前还没有绑定设备', icon: 'none' })
|
|
return
|
|
}
|
|
if (startTime === endTime) {
|
|
Taro.showToast({ title: '开始和结束时间不能相同', icon: 'none' })
|
|
return
|
|
}
|
|
|
|
setIsSaving(true)
|
|
try {
|
|
const result = await setDeviceSleepSchedule(startTime, endTime, deviceStatus?.timezone || 'Asia/Shanghai', binding.device_id)
|
|
setDeviceStatus((current) => ({
|
|
...(current || { device_id: binding.device_id }),
|
|
...current,
|
|
sleep_mode: result.sleep_mode,
|
|
disable_time_start: result.start || startTime,
|
|
disable_time_end: result.end || endTime,
|
|
timezone: result.timezone,
|
|
}))
|
|
Taro.showToast({ title: '休眠时间已发送', icon: 'success' })
|
|
} catch (error: any) {
|
|
Taro.showToast({
|
|
title: error?.message || '设置失败,请重试',
|
|
icon: 'none',
|
|
})
|
|
} finally {
|
|
setIsSaving(false)
|
|
}
|
|
}
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<View className='sleep-page' style={getPageStyle(false)}>
|
|
<View className='loading'>
|
|
<Text>加载中...</Text>
|
|
</View>
|
|
{systemBanner}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
if (!child) {
|
|
return (
|
|
<View className='sleep-page' style={getPageStyle(false)}>
|
|
<View className='page-header'>
|
|
<Text className='page-title'>定时休眠</Text>
|
|
<Text className='page-subtitle'>先绑定设备并选择宝贝,再为对应设备设置休眠时间</Text>
|
|
</View>
|
|
<View className='empty-card'>
|
|
<Text className='empty-title'>还没有当前宝贝</Text>
|
|
<Text className='empty-desc'>请先去首页绑定设备,并在绑定流程中创建或选择宝贝。</Text>
|
|
</View>
|
|
{systemBanner}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
if (!binding) {
|
|
return (
|
|
<View className='sleep-page' style={getPageStyle(false)}>
|
|
<View className='page-header'>
|
|
<Text className='page-title'>定时休眠</Text>
|
|
<Text className='page-subtitle'>{child.child_name} 还没有绑定设备</Text>
|
|
</View>
|
|
<View className='empty-card'>
|
|
<Text className='empty-title'>当前没有可设置的设备</Text>
|
|
<Text className='empty-desc'>先去首页绑定设备,再回来设置休眠时间段。</Text>
|
|
<View className='primary-btn' onClick={() => Taro.navigateTo({ url: '/pages/bind/index' })}>
|
|
<Text className='primary-btn-text'>去绑定设备</Text>
|
|
</View>
|
|
</View>
|
|
{systemBanner}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const currentRange = formatSleepRange(deviceStatus)
|
|
const timezoneText = deviceStatus?.timezone || 'Asia/Shanghai'
|
|
const childName = child.child_name || '当前宝贝'
|
|
|
|
return (
|
|
<View className='sleep-page' style={getPageStyle(false)}>
|
|
<View className='page-header'>
|
|
<Text className='page-title'>定时休眠</Text>
|
|
<Text className='page-subtitle'>每天在设定时段内,设备将进入不可启动状态</Text>
|
|
</View>
|
|
|
|
<View className='hero-card'>
|
|
<View className='hero-copy'>
|
|
<Text className='hero-label'>当前设备</Text>
|
|
<Text className='hero-value'>{binding.device_id}</Text>
|
|
<Text className='hero-detail'>{childName} 的当前休眠时段:{currentRange}</Text>
|
|
</View>
|
|
<View className='hero-icon-wrap'>
|
|
<Image className='sleep-hero-mascot' src={require('../../assets/ui/dog-sleep.png')} mode='aspectFit' />
|
|
<Image className='hero-icon' src={require('../../assets/tab-icons/moon.png')} mode='aspectFit' />
|
|
</View>
|
|
</View>
|
|
|
|
<View className='section-title'>时间设置</View>
|
|
<View className='settings-card'>
|
|
<Picker mode='time' value={startTime} onChange={(event) => setStartTime(event.detail.value)}>
|
|
<View className='setting-row'>
|
|
<View>
|
|
<Text className='setting-label'>开始时间</Text>
|
|
<Text className='setting-hint'>设备从这个时刻开始进入休眠</Text>
|
|
</View>
|
|
<Text className='setting-value'>{startTime}</Text>
|
|
</View>
|
|
</Picker>
|
|
|
|
<View className='divider'></View>
|
|
|
|
<Picker mode='time' value={endTime} onChange={(event) => setEndTime(event.detail.value)}>
|
|
<View className='setting-row'>
|
|
<View>
|
|
<Text className='setting-label'>结束时间</Text>
|
|
<Text className='setting-hint'>到这个时刻后恢复正常使用</Text>
|
|
</View>
|
|
<Text className='setting-value'>{endTime}</Text>
|
|
</View>
|
|
</Picker>
|
|
</View>
|
|
|
|
<View className='summary-card'>
|
|
<View className='summary-row'>
|
|
<Text className='summary-label'>生效时区</Text>
|
|
<Text className='summary-value'>{timezoneText}</Text>
|
|
</View>
|
|
<View className='summary-row'>
|
|
<Text className='summary-label'>当前配置</Text>
|
|
<Text className='summary-value'>{currentRange}</Text>
|
|
</View>
|
|
<View className='summary-row'>
|
|
<Text className='summary-label'>最近更新时间</Text>
|
|
<Text className='summary-value'>{formatUpdatedAt(deviceStatus?.settings_updated_at)}</Text>
|
|
</View>
|
|
</View>
|
|
|
|
<View className={`save-btn ${isSaving ? 'disabled' : ''}`} onClick={isSaving ? undefined : handleSave}>
|
|
<Text className='save-btn-text'>{isSaving ? '发送中...' : '保存休眠时间'}</Text>
|
|
</View>
|
|
{systemBanner}
|
|
</View>
|
|
)
|
|
}
|