import React, { useEffect, useState, useCallback } from 'react'; import { StyleSheet, View, FlatList, RefreshControl, Alert } from 'react-native'; import { Text, Card, useTheme, ActivityIndicator, Surface, SegmentedButtons, Button, Snackbar } from 'react-native-paper'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useNavigation } from '@react-navigation/native'; import { getAlarms, ackAlarm, getAlarmClip } from '../api/alarms'; import { extractErrorMessage } from '../api/client'; import { formatRelativeTime, getSeverityLabel } from '../utils/format'; import type { Alarm } from '../types'; export default function AlertsScreen() { const theme = useTheme(); const navigation = useNavigation(); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [alarms, setAlarms] = useState([]); const [filter, setFilter] = useState('open'); const [error, setError] = useState(null); const [snackMsg, setSnackMsg] = useState(''); const [snackVisible, setSnackVisible] = useState(false); const [ackingId, setAkingId] = useState(null); const getSevColor = (severity?: string) => { switch (severity?.toLowerCase()) { case 'danger': case 'critical': case 'high': return '#D4847A'; case 'warn': case 'warning': case 'medium': return '#D4B87A'; case 'info': case 'low': return '#6B8F71'; default: return '#B8B3AA'; } }; const loadData = useCallback(async (isRefresh = false) => { if (isRefresh) setRefreshing(true); try { const data = await getAlarms({ openOnly: filter === 'open' }); setAlarms(data); setError(null); } catch (err: any) { setError(err?.message || '加载失败'); } finally { setLoading(false); setRefreshing(false); } }, [filter]); useEffect(() => { setLoading(true); loadData(); }, [loadData]); const handleAck = async (id: string) => { setAkingId(id); try { await ackAlarm(id); setAlarms((prev) => prev.map((a) => (a.id === id ? { ...a, acknowledged: true, open: false } : a)), ); setSnackMsg('告警已确认'); setSnackVisible(true); } catch (err: any) { Alert.alert('操作失败', extractErrorMessage(err)); } finally { setAkingId(null); } }; const handleViewClip = async (alarm: Alarm) => { try { const clip = await getAlarmClip(alarm.id); if (clip.playbackUrl) { Alert.alert( '告警片段', `开始时间: ${formatRelativeTime(clip.startAt)}\n结束时间: ${formatRelativeTime(clip.endAt)}`, ); } else { Alert.alert('提示', '暂无关联的视频片段'); } } catch (err: any) { Alert.alert('获取片段失败', extractErrorMessage(err)); } }; const renderItem = ({ item }: { item: Alarm }) => { const sevColor = getSevColor(item.severity || item.level); return ( {getSeverityLabel(item.severity || item.level)} {item.title || item.message || item.content || item.code || '告警'} {item.open ? ( 未处理 ) : ( 已关闭 )} {item.message && item.title ? ( {item.message} ) : null} {item.deviceKey ? 设备: {item.deviceKey} : null} {item.metric ? 指标: {item.metric} : null} {item.value !== undefined ? 值: {item.value} : null} {formatRelativeTime(item.triggeredAt || item.createdAt)} {item.open && !item.acknowledged ? ( ) : null} ); }; if (loading) { return ( ); } return ( item.id} renderItem={renderItem} contentContainerStyle={styles.list} refreshControl={ loadData(true)} /> } ListEmptyComponent={ error ? ( {error} ) : ( 暂无告警 ) } /> setSnackVisible(false)} duration={2000} > {snackMsg} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#FAF8F5', }, center: { flex: 1, justifyContent: 'center', alignItems: 'center', }, filterContainer: { padding: 16, paddingBottom: 8, }, list: { padding: 16, paddingTop: 8, paddingBottom: 32, }, card: { marginBottom: 12, borderRadius: 14, backgroundColor: '#FFFFFF', }, cardContent: { paddingVertical: 12, }, alarmHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 4, }, alarmTitleRow: { flexDirection: 'row', alignItems: 'center', gap: 8, flex: 1, }, sevBadge: { paddingHorizontal: 8, paddingVertical: 3, borderRadius: 10, elevation: 0, }, sevBadgeText: { color: 'white', fontSize: 11, fontWeight: '500', }, alarmTitle: { color: '#2D2A26', fontWeight: '500', flex: 1, }, openBadge: { backgroundColor: '#F9E8E5', paddingHorizontal: 8, paddingVertical: 3, borderRadius: 10, elevation: 0, }, openBadgeText: { color: '#D4847A', fontSize: 11, fontWeight: '500', }, alarmMessage: { color: '#8C8780', marginTop: 4, }, alarmMeta: { flexDirection: 'row', flexWrap: 'wrap', gap: 12, marginTop: 8, }, metaText: { fontSize: 12, color: '#B8B3AA', }, alarmFooter: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: 8, }, timeText: { fontSize: 12, color: '#B8B3AA', }, footerActions: { flexDirection: 'row', gap: 4, }, emptyContainer: { alignItems: 'center', paddingTop: 64, }, emptyText: { color: '#B8B3AA', fontSize: 16, }, });