import React, { useEffect, useState, useCallback } from 'react'; import { StyleSheet, View, FlatList, RefreshControl } from 'react-native'; import { Text, Card, ActivityIndicator, Surface, Searchbar, IconButton } from 'react-native-paper'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useNavigation } from '@react-navigation/native'; import { getDevices } from '../api/devices'; import { formatRelativeTime } from '../utils/format'; import type { Device } from '../types'; export default function DevicesScreen() { const navigation = useNavigation(); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [devices, setDevices] = useState([]); const [filteredDevices, setFilteredDevices] = useState([]); const [search, setSearch] = useState(''); const [error, setError] = useState(null); const loadData = useCallback(async (isRefresh = false) => { if (isRefresh) setRefreshing(true); try { const data = await getDevices(); setDevices(data); setFilteredDevices(data); setError(null); } catch (err: any) { setError(err?.message || '加载失败'); } finally { setLoading(false); setRefreshing(false); } }, []); useEffect(() => { loadData(); }, [loadData]); useEffect(() => { if (!search.trim()) { setFilteredDevices(devices); } else { const lower = search.toLowerCase(); setFilteredDevices( devices.filter( (d) => d.name.toLowerCase().includes(lower) || d.deviceKey.toLowerCase().includes(lower) || (d.kind || '').toLowerCase().includes(lower), ), ); } }, [search, devices]); const isControllable = (device: Device) => { const kind = (device.kind || device.type || '').toLowerCase(); return kind === 'actuator' || kind === 'controller' || kind === 'fan' || kind === 'ac' || kind === 'dehumidifier'; }; const getDeviceIcon = (device: Device) => { const kind = (device.kind || device.type || '').toLowerCase(); const name = device.name.toLowerCase(); if (name.includes('fan') || kind.includes('fan')) return 'fan'; if (name.includes('ac') || name.includes('空调') || kind.includes('ac')) return 'air-conditioner'; if (name.includes('humid') || name.includes('除湿') || kind.includes('humid')) return 'water-percent'; if (name.includes('light') || name.includes('灯') || kind.includes('light')) return 'lightbulb'; if (name.includes('sensor') || kind.includes('sensor')) return 'thermometer'; if (name.includes('camera') || kind.includes('camera')) return 'video'; return 'router-wireless'; }; const renderItem = ({ item }: { item: Device }) => { const isOnline = item.onlineStatus === 'online' || item.status === 'online'; const controllable = isControllable(item); return ( navigation.navigate('DeviceControl', { deviceKey: item.deviceKey, deviceName: item.name }) : undefined} > {item.name} {item.deviceKey} {item.kind || item.type || '设备'} {item.model ? · {item.model} : null} {item.lastSeen ? ( 最后在线: {formatRelativeTime(item.lastSeen)} ) : null} {isOnline ? '在线' : '离线'} {controllable ? ( ) : null} ); }; if (loading) { return ( ); } return ( item.id} renderItem={renderItem} contentContainerStyle={styles.list} refreshControl={ loadData(true)} /> } ListEmptyComponent={ error ? ( {error} ) : ( 暂无设备 ) } /> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#FAF8F5', }, center: { flex: 1, justifyContent: 'center', alignItems: 'center', }, searchContainer: { padding: 16, paddingBottom: 8, }, searchbar: { borderRadius: 14, backgroundColor: '#FFFFFF', }, list: { padding: 16, paddingTop: 8, paddingBottom: 32, }, card: { marginBottom: 12, borderRadius: 14, backgroundColor: '#FFFFFF', }, cardContent: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 4, }, deviceLeft: { flexDirection: 'row', alignItems: 'center', flex: 1, }, iconBox: { borderRadius: 12, marginRight: 8, }, deviceInfo: { flex: 1, }, deviceName: { color: '#2D2A26', fontWeight: '500', }, deviceKey: { color: '#B8B3AA', fontSize: 12, marginTop: 2, }, tagRow: { flexDirection: 'row', alignItems: 'center', marginTop: 2, }, deviceKind: { color: '#8C8780', }, deviceModel: { color: '#B8B3AA', }, deviceTime: { color: '#B8B3AA', marginTop: 2, }, deviceRight: { flexDirection: 'row', alignItems: 'center', }, statusBadge: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 14, elevation: 0, }, statusText: { color: 'white', fontSize: 12, fontWeight: '500', }, emptyContainer: { alignItems: 'center', paddingTop: 64, }, emptyText: { color: '#B8B3AA', fontSize: 16, }, });