183 lines
7.2 KiB
TypeScript
183 lines
7.2 KiB
TypeScript
import { useState, useCallback } from 'react'
|
||
import { useDidShow } from '@tarojs/taro'
|
||
import { View, Text } from '@tarojs/components'
|
||
import { useAppSelector, useAppDispatch } from '@/store/hooks'
|
||
import { setDeviceList, type Device } from '@/store/deviceSlice'
|
||
import { Navbar } from '@/components/navbar'
|
||
import { Loading } from '@/components/loading'
|
||
import { StateView } from '@/components/state-view'
|
||
import { BottomSheet } from '@/components/bottom-sheet'
|
||
import { getDeviceList } from '@/api/device'
|
||
import { DEVICE_ICON_MAP, type DeviceModel } from '@/constants/enums'
|
||
import { ROUTES } from '@/constants/routes'
|
||
import { navigateTo } from '@/utils/navigation'
|
||
import { getFamilies, type Family } from '@/api/family'
|
||
import styles from './index.module.scss'
|
||
|
||
const FALLBACK_ICON = '⌂'
|
||
|
||
export default function DeviceList() {
|
||
const dispatch = useAppDispatch()
|
||
const scene = useAppSelector((s) => s.scene.current)
|
||
const deviceList = useAppSelector((s) => s.device.list)
|
||
|
||
const [loading, setLoading] = useState(true)
|
||
const [loadError, setLoadError] = useState(false)
|
||
|
||
const [families, setFamilies] = useState<Family[]>([])
|
||
const [currentFamily, setCurrentFamily] = useState<Family | null>(null)
|
||
const [familySheetOpen, setFamilySheetOpen] = useState(false)
|
||
const [pendingFamily, setPendingFamily] = useState<Family | null>(null)
|
||
|
||
// 在线状态需每次进入页面都新鲜:useDidShow 覆盖
|
||
// 首次进入、从详情页 navigateBack 返回、tab 切换回三种场景
|
||
const loadDevices = useCallback(async () => {
|
||
setLoading(true)
|
||
setLoadError(false)
|
||
try {
|
||
const data = await getDeviceList(scene?.id ?? null)
|
||
dispatch(setDeviceList(data))
|
||
} catch {
|
||
setLoadError(true)
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}, [dispatch, scene?.id])
|
||
|
||
const loadFamilies = useCallback(async () => {
|
||
try {
|
||
const list = await getFamilies()
|
||
setFamilies(list)
|
||
// 若当前未选中,默认选第一条
|
||
setCurrentFamily((prev) => prev ?? list[0] ?? null)
|
||
} catch {
|
||
// 静默:家庭圈加载失败不阻断设备列表
|
||
}
|
||
}, [])
|
||
|
||
useDidShow(() => {
|
||
loadDevices()
|
||
loadFamilies()
|
||
})
|
||
|
||
const goDetail = (device: Device) => {
|
||
const name = encodeURIComponent(device.name)
|
||
navigateTo(`${ROUTES.DEVICE_DETAIL}?id=${device.id}&name=${name}&model=${device.model}&online=${device.online ? 1 : 0}`)
|
||
}
|
||
const goAdd = () => navigateTo(ROUTES.DEVICE_ADD)
|
||
const goRoom = () => navigateTo(ROUTES.SCENE_SELECT)
|
||
|
||
const iconOf = (model: DeviceModel) => DEVICE_ICON_MAP[model] ?? FALLBACK_ICON
|
||
|
||
const openFamilySheet = () => {
|
||
setPendingFamily(currentFamily)
|
||
setFamilySheetOpen(true)
|
||
}
|
||
const closeFamilySheet = () => {
|
||
setFamilySheetOpen(false)
|
||
setPendingFamily(null)
|
||
}
|
||
const confirmFamilySheet = () => {
|
||
if (pendingFamily) setCurrentFamily(pendingFamily)
|
||
closeFamilySheet()
|
||
}
|
||
|
||
return (
|
||
<View className={styles.page}>
|
||
<Navbar title="智能设备" autoBack={false} variant="hero" />
|
||
|
||
{/* 绿色衬底:承接浮卡的上浮区,避免 -40px 压到导航标题 */}
|
||
<View className={styles.heroSpace} />
|
||
|
||
{/* 设备应用家庭信息卡 */}
|
||
<View className={styles.familyCard}>
|
||
<View className={styles.familyRow}>
|
||
<Text className={styles.familyLabel}>设备应用家庭</Text>
|
||
<Text className={styles.familyValue}>{currentFamily?.name ?? scene?.name ?? '未选择'}</Text>
|
||
</View>
|
||
<View className={styles.familyRow}>
|
||
<Text className={styles.familyLabel}>关爱对象</Text>
|
||
<Text className={styles.familyValue}>{currentFamily?.caredLabel ?? '--'}</Text>
|
||
</View>
|
||
<View className={styles.familyRow}>
|
||
<Text className={styles.familyLabel}>关爱地址</Text>
|
||
<Text className={styles.familyValue}>{currentFamily?.address ?? '--'}</Text>
|
||
</View>
|
||
<View className={styles.switchBtn} hoverClass={styles.switchBtnPressed} onClick={openFamilySheet}>
|
||
<Text className={styles.switchBtnText}>切换家庭圈</Text>
|
||
</View>
|
||
</View>
|
||
|
||
{/* 房间管理 + 添加设备 两张并排 */}
|
||
<View className={styles.funcRow}>
|
||
<View className={styles.funcCard} hoverClass={styles.funcCardPressed} onClick={goRoom}>
|
||
<Text className={styles.funcIcon}>⌂</Text>
|
||
<Text className={styles.funcText}>房间管理</Text>
|
||
</View>
|
||
<View className={styles.funcCard} hoverClass={styles.funcCardPressed} onClick={goAdd}>
|
||
<Text className={styles.funcIcon}>+</Text>
|
||
<Text className={styles.funcText}>添加设备</Text>
|
||
</View>
|
||
</View>
|
||
|
||
{/* 设备网格 */}
|
||
{loading ? (
|
||
<View className={styles.stateWrap}><Loading /></View>
|
||
) : loadError ? (
|
||
<StateView status="error" onRetry={loadDevices} />
|
||
) : deviceList.length === 0 ? (
|
||
<StateView status="error" mode="list" text="暂无设备" retryText="添加设备" onRetry={goAdd} />
|
||
) : (
|
||
<View className={styles.grid}>
|
||
{deviceList.map((device) => (
|
||
<View key={device.id} className={styles.card} onClick={() => goDetail(device)}>
|
||
<View className={`${styles.icon} ${!device.online ? styles.iconOffline : ''}`}>
|
||
<Text className={styles.iconText}>{iconOf(device.model)}</Text>
|
||
</View>
|
||
<Text className={styles.name}>{device.name}</Text>
|
||
<View className={styles.statusRow}>
|
||
<View className={`${styles.dot} ${device.online ? styles.dotOn : styles.dotOff}`} />
|
||
<Text className={`${styles.statusText} ${!device.online ? styles.statusTextOff : ''}`}>
|
||
{device.online ? '在线' : '离线'}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
))}
|
||
</View>
|
||
)}
|
||
|
||
{/* 切换家庭圈弹层 */}
|
||
<BottomSheet visible={familySheetOpen} title="切换家庭圈" onClose={closeFamilySheet}>
|
||
<View className={styles.sheetList}>
|
||
{families.map((family) => {
|
||
const active = pendingFamily?.id === family.id
|
||
return (
|
||
<View
|
||
key={family.id}
|
||
className={`${styles.sheetItem} ${active ? styles.sheetItemActive : ''}`}
|
||
onClick={() => setPendingFamily(family)}
|
||
>
|
||
<View className={styles.sheetHead}>
|
||
<Text className={styles.sheetName}>{family.name}</Text>
|
||
{family.role && (
|
||
<View className={styles.sheetRole}>
|
||
<Text className={styles.sheetRoleText}>{family.role}</Text>
|
||
</View>
|
||
)}
|
||
</View>
|
||
<View className={styles.sheetMeta}>
|
||
<Text className={styles.sheetMetaItem}>关爱对象 {family.caredLabel}</Text>
|
||
<Text className={styles.sheetMetaItem}>设备数量 {family.deviceCount}台</Text>
|
||
</View>
|
||
</View>
|
||
)
|
||
})}
|
||
</View>
|
||
<View className={styles.sheetConfirm} hoverClass={styles.sheetConfirmPressed} onClick={confirmFamilySheet}>
|
||
<Text className={styles.sheetConfirmText}>确定</Text>
|
||
</View>
|
||
</BottomSheet>
|
||
</View>
|
||
)
|
||
}
|