SEC_Warehouse/src/pages/device/index.tsx

183 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
)
}