import { useState, useMemo, useRef, useEffect } from 'react'; import { useHealthStore } from '@/stores/health'; import { useAuthStore } from '@/stores/auth'; import { usePageData } from '@/hooks/usePageData'; import { trackPageView } from '@/services/analytics'; import * as appointmentApi from '@/services/appointment'; import * as followupApi from '@/services/followup'; import { listPendingSuggestions, type AiSuggestionItem } from '@/services/ai-analysis'; import { notificationService } from '@/services/notification'; import { listPatientAlerts } from '@/services/alert'; export interface ReminderItem { id: string; text: string; type: 'ai' | 'appointment' | 'followup'; tag: string; } function buildSuggestionText(s: AiSuggestionItem): string { const riskMap: Record = { high: '高风险', medium: '中风险', low: '低风险' }; const typeMap: Record = { vital_sign_anomaly: '体征异常', lab_result_anomaly: '化验异常', medication_adherence: '用药提醒', lifestyle: '生活建议', }; const risk = riskMap[s.risk_level] || ''; const type = typeMap[s.suggestion_type] || '健康建议'; return `${type}:发现${risk}指标,建议关注`; } export function useHomeData() { const user = useAuthStore((s) => s.user); const currentPatient = useAuthStore((s) => s.currentPatient); const todaySummary = useHealthStore((s) => s.todaySummary); const loading = useHealthStore((s) => s.loading); const refreshToday = useHealthStore((s) => s.refreshToday); const [reminders, setReminders] = useState([]); const [unreadCount, setUnreadCount] = useState(0); const [remindersLoading, setRemindersLoading] = useState(false); const [alertCount, setAlertCount] = useState(0); const fetchData = async () => { const patientId = useAuthStore.getState().currentPatient?.id; if (!patientId) return; refreshToday(); loadReminders(patientId); loadUnread(); loadAlertCount(patientId); trackPageView('home'); }; const { trigger, refresh } = usePageData(fetchData, { throttleMs: 5000, enablePullDown: true, enabled: !!user, }); // currentPatient 从 null 变为有值时重新触发加载 // 解决 loadPatients 异步完成前 useDidShow 已触发 fetchData 并因 patientId 为空提前返回的问题 const prevPatientRef = useRef(null); useEffect(() => { const pid = currentPatient?.id ?? null; if (pid && pid !== prevPatientRef.current) { prevPatientRef.current = pid; trigger(); } }, [currentPatient?.id, trigger]); const loadUnread = async () => { try { const res = await notificationService.getUnreadCount() as { data?: { count?: number } | number }; const d = res.data; setUnreadCount(typeof d === 'object' && d ? (d.count ?? 0) : 0); } catch (err) { console.warn('[home] 获取未读消息数失败:', err); } }; const loadReminders = async (patientId: string) => { setRemindersLoading(true); try { const items: ReminderItem[] = []; const [apptRes, taskRes, suggestRes] = await Promise.allSettled([ appointmentApi.listAppointments(patientId, 1), followupApi.listTasks(patientId, 'pending'), listPendingSuggestions(), ]); if (suggestRes.status === 'fulfilled') { for (const s of suggestRes.value.slice(0, 1)) { items.push({ id: s.id, text: buildSuggestionText(s), type: 'ai', tag: 'AI 建议' }); } } if (apptRes.status === 'fulfilled') { for (const a of apptRes.value.data.slice(0, 1)) { if (a.status === 'pending' || a.status === 'confirmed') { items.push({ id: a.id, text: `${a.appointment_date} ${a.start_time} — ${a.doctor_name || '医护'} ${a.department || '门诊'}`, type: 'appointment', tag: '预约', }); } } } if (taskRes.status === 'fulfilled') { for (const t of taskRes.value.data.slice(0, 1)) { items.push({ id: t.id, text: `${t.follow_up_type} · 截止 ${t.planned_date}`, type: 'followup', tag: '随访', }); } } setReminders(items.slice(0, 3)); } catch (err) { console.warn('[home] 加载提醒列表失败:', err); setReminders([]); } finally { setRemindersLoading(false); } }; const loadAlertCount = async (patientId: string) => { try { const res = await listPatientAlerts(patientId, { status: 'pending', page: 1, page_size: 1 }); setAlertCount(res.total ?? 0); } catch { setAlertCount(0); } }; const summary = todaySummary || {}; const indicators = [!!summary.blood_pressure, !!summary.heart_rate, !!summary.blood_sugar, !!summary.weight]; const completedCount = indicators.filter(Boolean).length; const progressPercent = Math.round((completedCount / 4) * 100); const indicatorCapsules = useMemo(() => [ { label: '血压', done: !!summary.blood_pressure }, { label: '心率', done: !!summary.heart_rate }, { label: '血糖', done: !!summary.blood_sugar }, { label: '体重', done: !!summary.weight }, ], [summary.blood_pressure, summary.heart_rate, summary.blood_sugar, summary.weight]); const healthItems = useMemo(() => [ { label: '血压', value: summary.blood_pressure ? `${summary.blood_pressure.systolic}/${summary.blood_pressure.diastolic}` : '—', unit: 'mmHg', status: summary.blood_pressure?.status, indicator: 'systolic_bp_morning' }, { label: '心率', value: summary.heart_rate ? `${summary.heart_rate.value}` : '—', unit: 'bpm', status: summary.heart_rate?.status, indicator: 'heart_rate' }, { label: '血糖', value: summary.blood_sugar ? `${summary.blood_sugar.value}` : '—', unit: 'mmol/L', status: summary.blood_sugar?.status, indicator: 'blood_sugar' }, { label: '体重', value: summary.weight ? `${summary.weight.value}` : '—', unit: 'kg', status: summary.weight?.status, indicator: 'weight' }, ], [summary.blood_pressure, summary.heart_rate, summary.blood_sugar, summary.weight]); const hour = new Date().getHours(); const greeting = hour < 12 ? '上午好' : hour < 18 ? '下午好' : '晚上好'; const displayName = user?.display_name || currentPatient?.name || user?.username || (user?.phone ? `${user.phone.slice(-4)}` : '') || '用户'; return { user, currentPatient, todaySummary: summary, loading, reminders, unreadCount, remindersLoading, alertCount, indicatorCapsules, healthItems, completedCount, progressPercent, greeting, displayName, trigger, refresh, }; }