diff --git a/src/family/pages/AlertsAndCare.tsx b/src/family/pages/AlertsAndCare.tsx new file mode 100644 index 0000000..dda034a --- /dev/null +++ b/src/family/pages/AlertsAndCare.tsx @@ -0,0 +1,415 @@ +import React, { useState, useEffect } from 'react'; +import { + AlertTriangle, + AlertCircle, + Clock, + Heart, + MessageCircle, + Send, + CheckCircle, + Phone, +} from 'lucide-react'; +import * as messageService from '../services/messageService'; + +interface Alert { + id: string; + level: 'low' | 'medium' | 'high'; + type: 'medication' | 'emotion' | 'inactive' | 'emergency' | 'sos_emergency' | 'contact_family'; + message: string; + timestamp: string; + handled: boolean; +} + +/** + * 家属端通知与远程关怀界面 + * 显示通知时间线,支持发送祝福消息 + */ +export const AlertsAndCare: React.FC = () => { + const [showMessageComposer, setShowMessageComposer] = useState(false); + const [messageText, setMessageText] = useState(''); + const [deliveryTiming, setDeliveryTiming] = useState('asap'); + const [alerts, setAlerts] = useState([]); + const [loading, setLoading] = useState(true); + const [filterType, setFilterType] = useState<'all' | 'unhandled' | 'high'>('all'); + const [stats, setStats] = useState<{ + total: number; + unhandled: number; + high: number; + }>({ total: 0, unhandled: 0, high: 0 }); + const familyId = 'family_001'; // 实际使用时从用户上下文获取 + + // 加载消息/告警数据 + useEffect(() => { + loadAlerts(); + loadStats(); + // 每10秒轮询一次新消息 + const interval = setInterval(() => { + loadAlerts(); + loadStats(); + }, 10000); + return () => clearInterval(interval); + }, [filterType]); + + const loadStats = async () => { + try { + const statsData = await messageService.getAlertStats(familyId); + + // 计算各个筛选项的数量 + // total 应该是所有消息的总数,而不是今天的消息数 + const totalFromLevels = Object.values(statsData.level_stats || {}).reduce((sum, count) => sum + count, 0); + const total = totalFromLevels; + const unhandled = statsData.status_stats?.unhandled || 0; + const high = statsData.level_stats?.high || 0; + + setStats({ total, unhandled, high }); + } catch (error) { + console.error('加载统计失败:', error); + } + }; + + const loadAlerts = async () => { + try { + setLoading(true); + + // 根据筛选类型设置API参数 + const options: any = {}; + if (filterType === 'unhandled') { + options.handled = false; + } else if (filterType === 'high') { + options.level = 'high'; + } + + const { alerts: data } = await messageService.getFamilyAlerts(familyId, options); + + // 转换数据格式 + const convertedAlerts: Alert[] = data.map((alert) => ({ + id: alert.id.toString(), + level: alert.level, + type: alert.alert_type, + message: alert.message, + timestamp: new Date(alert.created_at).toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + }), + handled: alert.handled, + })); + + setAlerts(convertedAlerts); + } catch (error) { + console.error('加载消息失败:', error); + // 如果API失败,使用模拟数据 + setAlerts([ + { + id: '1', + level: 'high', + type: 'emotion', + message: '检测到连续负面情绪,已触发安抚流程', + timestamp: '2023-11-13 14:30', + handled: false, + }, + { + id: '2', + level: 'medium', + type: 'medication', + message: '晚药延迟 15 分钟服用', + timestamp: '2023-11-13 20:15', + handled: true, + }, + { + id: '3', + level: 'low', + type: 'inactive', + message: '下午时段互动较少(仅 1 次对话)', + timestamp: '2023-11-13 17:00', + handled: true, + }, + ]); + } finally { + setLoading(false); + } + }; + + const getLevelConfig = (level: Alert['level']) => { + switch (level) { + case 'high': + return { + bg: 'bg-red-50', + border: 'border-red-400', + text: 'text-red-700', + icon: AlertTriangle, + label: '🚨 紧急', + }; + case 'medium': + return { + bg: 'bg-orange-50', + border: 'border-orange-400', + text: 'text-orange-700', + icon: Phone, + label: '📞 普通', + }; + case 'low': + return { + bg: 'bg-yellow-50', + border: 'border-yellow-400', + text: 'text-yellow-700', + icon: Clock, + label: '💡 提示', + }; + } + }; + + const getTypeIcon = (type: Alert['type']) => { + switch (type) { + case 'sos_emergency': + return AlertTriangle; + case 'contact_family': + return Phone; + case 'emotion': + return Heart; + case 'medication': + return Clock; + case 'inactive': + return MessageCircle; + default: + return AlertCircle; + } + }; + + const handleMarkAsHandled = async (alertId: string) => { + try { + await messageService.handleAlert(Number(alertId)); + // 重新加载数据和统计 + await loadAlerts(); + await loadStats(); + } catch (error) { + console.error('标记为已处理失败:', error); + } + }; + + const handleSendMessage = () => { + console.log('Send message:', { messageText, deliveryTiming }); + setShowMessageComposer(false); + setMessageText(''); + }; + + // 显示加载状态 + if (loading && alerts.length === 0) { + return ( +
+
+
+

正在加载消息...

+
+
+ ); + } + + return ( +
+ {/* 顶部导航 */} +
+
+
+

通知

+
+ + {/* 筛选器 */} +
+ + + +
+
+
+ + {/* 主要内容区 - 手机优化 */} +
+ {/* 通知列表 */} +
+ {alerts.map((alert) => { + const config = getLevelConfig(alert.level); + const Icon = config.icon; + + return ( +
+
+ +
+
+ + {config.label} + + + {alert.timestamp} + + {alert.handled && ( + + + 已处理 + + )} +
+

+ {alert.message} +

+ {!alert.handled && ( +
+ +
+ )} +
+
+
+ ); + })} +
+ + {/* 空状态 */} + {alerts.length === 0 && ( +
+ +

暂无通知

+
+ )} +
+ + {/* 消息编辑器弹窗 */} + {showMessageComposer && ( +
+
+

发送祝福消息

+ + {/* 消息类型选择 */} +
+ +
+ + +
+
+ + {/* 消息内容 */} +
+ +