import { ThemedText } from '@/components/ThemedText'; import { HeaderBar } from '@/components/ui/HeaderBar'; import { Colors } from '@/constants/Colors'; import { TIMES_PER_DAY_OPTIONS } from '@/constants/Medication'; import { useAppDispatch } from '@/hooks/redux'; import { useColorScheme } from '@/hooks/useColorScheme'; import { medicationNotificationService } from '@/services/medicationNotifications'; import { updateMedicationAction } from '@/store/medicationsSlice'; import type { RepeatPattern } from '@/types/medication'; import { Ionicons } from '@expo/vector-icons'; import DateTimePicker from '@react-native-community/datetimepicker'; import { Picker } from '@react-native-picker/picker'; import dayjs from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; import { useLocalSearchParams, useRouter } from 'expo-router'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { ActivityIndicator, Alert, Modal, Platform, Pressable, ScrollView, StyleSheet, TouchableOpacity, View, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; const DEFAULT_TIME_PRESETS = ['08:00', '12:00', '18:00', '22:00']; // 辅助函数:从时间字符串创建 Date 对象 const createDateFromTime = (time: string) => { try { if (!time || typeof time !== 'string') { console.warn('[MEDICATION] Invalid time string provided:', time); return new Date(); } const parts = time.split(':'); if (parts.length !== 2) { console.warn('[MEDICATION] Invalid time format:', time); return new Date(); } const hour = parseInt(parts[0], 10); const minute = parseInt(parts[1], 10); if (isNaN(hour) || isNaN(minute) || hour < 0 || hour > 23 || minute < 0 || minute > 59) { console.warn('[MEDICATION] Invalid time values:', { hour, minute }); return new Date(); } const next = new Date(); next.setHours(hour, minute, 0, 0); if (isNaN(next.getTime())) { console.error('[MEDICATION] Failed to create valid date'); return new Date(); } return next; } catch (error) { console.error('[MEDICATION] Error in createDateFromTime:', error); return new Date(); } }; // 辅助函数:格式化时间 const formatTime = (date: Date) => dayjs(date).format('HH:mm'); // 辅助函数:获取默认时间 const getDefaultTimeByIndex = (index: number) => { return DEFAULT_TIME_PRESETS[index % DEFAULT_TIME_PRESETS.length]; }; export default function EditMedicationFrequencyScreen() { const params = useLocalSearchParams<{ medicationId?: string; medicationName?: string; repeatPattern?: RepeatPattern; timesPerDay?: string; medicationTimes?: string; }>(); const medicationId = Array.isArray(params.medicationId) ? params.medicationId[0] : params.medicationId; const medicationName = Array.isArray(params.medicationName) ? params.medicationName[0] : params.medicationName; const initialRepeatPattern = (Array.isArray(params.repeatPattern) ? params.repeatPattern[0] : params.repeatPattern) as RepeatPattern || 'daily'; const initialTimesPerDay = parseInt(Array.isArray(params.timesPerDay) ? params.timesPerDay[0] : params.timesPerDay || '1', 10); const initialTimes = params.medicationTimes ? (Array.isArray(params.medicationTimes) ? params.medicationTimes[0] : params.medicationTimes).split(',') : ['08:00']; const dispatch = useAppDispatch(); const router = useRouter(); const scheme = (useColorScheme() ?? 'light') as keyof typeof Colors; const colors = Colors[scheme]; const insets = useSafeAreaInsets(); const [repeatPattern, setRepeatPattern] = useState(initialRepeatPattern); const [timesPerDay, setTimesPerDay] = useState(initialTimesPerDay); const [medicationTimes, setMedicationTimes] = useState(initialTimes); const [saving, setSaving] = useState(false); // 时间选择器相关状态 const [timePickerVisible, setTimePickerVisible] = useState(false); const [timePickerDate, setTimePickerDate] = useState(new Date()); const [editingTimeIndex, setEditingTimeIndex] = useState(null); // 根据 timesPerDay 动态调整 medicationTimes useEffect(() => { setMedicationTimes((prev) => { if (timesPerDay > prev.length) { const next = [...prev]; while (next.length < timesPerDay) { next.push(getDefaultTimeByIndex(next.length)); } return next; } if (timesPerDay < prev.length) { return prev.slice(0, timesPerDay); } return prev; }); }, [timesPerDay]); // 打开时间选择器 const openTimePicker = useCallback( (index?: number) => { try { if (typeof index === 'number') { if (index >= 0 && index < medicationTimes.length) { setEditingTimeIndex(index); setTimePickerDate(createDateFromTime(medicationTimes[index])); } else { console.error('[MEDICATION] Invalid time index:', index); return; } } else { setEditingTimeIndex(null); setTimePickerDate(createDateFromTime(getDefaultTimeByIndex(medicationTimes.length))); } setTimePickerVisible(true); } catch (error) { console.error('[MEDICATION] Error in openTimePicker:', error); } }, [medicationTimes] ); // 确认时间选择 const confirmTime = useCallback( (date: Date) => { try { if (!date || isNaN(date.getTime())) { console.error('[MEDICATION] Invalid date provided to confirmTime'); setTimePickerVisible(false); setEditingTimeIndex(null); return; } const nextValue = formatTime(date); setMedicationTimes((prev) => { if (editingTimeIndex == null) { return [...prev, nextValue]; } return prev.map((time, idx) => (idx === editingTimeIndex ? nextValue : time)); }); setTimePickerVisible(false); setEditingTimeIndex(null); } catch (error) { console.error('[MEDICATION] Error in confirmTime:', error); setTimePickerVisible(false); setEditingTimeIndex(null); } }, [editingTimeIndex] ); // 删除时间 const removeTime = useCallback((index: number) => { setMedicationTimes((prev) => { if (prev.length === 1) { return prev; // 至少保留一个时间 } return prev.filter((_, idx) => idx !== index); }); // 同时更新 timesPerDay setTimesPerDay((prev) => Math.max(1, prev - 1)); }, []); // 添加时间 const addTime = useCallback(() => { openTimePicker(); // 同时更新 timesPerDay setTimesPerDay((prev) => prev + 1); }, [openTimePicker]); // 保存修改 const handleSave = useCallback(async () => { if (!medicationId || saving) return; setSaving(true); try { const updated = await dispatch( updateMedicationAction({ id: medicationId, repeatPattern, timesPerDay, medicationTimes, }) ).unwrap(); // 重新安排药品通知 try { await medicationNotificationService.scheduleMedicationNotifications(updated); } catch (error) { console.error('[MEDICATION] 安排药品通知失败:', error); } router.back(); } catch (err) { console.error('更新频率失败', err); Alert.alert('更新失败', '更新服药频率时出现问题,请稍后重试。'); } finally { setSaving(false); } }, [dispatch, medicationId, medicationTimes, repeatPattern, router, saving, timesPerDay]); const frequencyLabel = useMemo(() => { switch (repeatPattern) { case 'daily': return `每日 ${timesPerDay} 次`; case 'weekly': return `每周 ${timesPerDay} 次`; default: return `自定义 · ${timesPerDay} 次/日`; } }, [repeatPattern, timesPerDay]); if (!medicationId) { return ( 缺少必要参数 ); } return ( {/* 药品名称提示 */} {medicationName && ( {medicationName} )} {/* 频率选择 */} 服药频率 设置每日服药次数 重复模式 setRepeatPattern(value as RepeatPattern)} itemStyle={styles.pickerItem} style={styles.picker} > {/* */} 每日次数 setTimesPerDay(Number(value))} itemStyle={styles.pickerItem} style={styles.picker} > {TIMES_PER_DAY_OPTIONS.map((times) => ( ))} {frequencyLabel} {/* 提醒时间列表 */} 每日提醒时间 添加并管理每天的提醒时间 {medicationTimes.map((time, index) => ( openTimePicker(index)}> {time} removeTime(index)} disabled={medicationTimes.length === 1} hitSlop={12} > ))} 添加时间 {/* 底部保存按钮 */} {isLiquidGlassAvailable() ? ( {saving ? ( ) : ( <> 保存修改 )} ) : ( {saving ? ( ) : ( <> 保存修改 )} )} {/* 时间选择器 Modal */} { setTimePickerVisible(false); setEditingTimeIndex(null); }} > { setTimePickerVisible(false); setEditingTimeIndex(null); }} /> {editingTimeIndex !== null ? '修改提醒时间' : '添加提醒时间'} { if (Platform.OS === 'ios') { if (date) setTimePickerDate(date); } else { if (event.type === 'set' && date) { confirmTime(date); } else { setTimePickerVisible(false); setEditingTimeIndex(null); } } }} /> {Platform.OS === 'ios' && ( { setTimePickerVisible(false); setEditingTimeIndex(null); }} style={[styles.pickerBtn, { borderColor: colors.border }]} > 取消 confirmTime(timePickerDate)} style={[styles.pickerBtn, styles.pickerBtnPrimary, { backgroundColor: colors.primary }]} > 确定 )} ); } const styles = StyleSheet.create({ container: { flex: 1, }, content: { paddingHorizontal: 20, gap: 32, }, centered: { flex: 1, alignItems: 'center', justifyContent: 'center', }, emptyText: { fontSize: 16, textAlign: 'center', }, medicationNameCard: { flexDirection: 'row', alignItems: 'center', gap: 12, borderRadius: 20, paddingHorizontal: 18, paddingVertical: 14, }, medicationNameText: { fontSize: 16, fontWeight: '600', }, section: { gap: 16, }, sectionTitle: { fontSize: 20, fontWeight: '700', }, sectionDescription: { fontSize: 14, lineHeight: 20, }, pickerRow: { flexDirection: 'row', gap: 16, }, pickerColumn: { flex: 1, gap: 8, }, pickerLabel: { fontSize: 14, fontWeight: '600', textAlign: 'center', }, picker: { width: '100%', height: 150, }, pickerItem: { fontSize: 18, height: 150, }, frequencySummary: { flexDirection: 'row', alignItems: 'center', gap: 10, borderRadius: 16, paddingHorizontal: 16, paddingVertical: 14, }, frequencySummaryText: { fontSize: 16, fontWeight: '600', }, timeList: { gap: 12, }, timeItem: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', borderWidth: 1, borderRadius: 16, paddingHorizontal: 16, paddingVertical: 14, }, timeValue: { flexDirection: 'row', alignItems: 'center', gap: 10, }, timeText: { fontSize: 18, fontWeight: '600', }, addTimeButton: { borderWidth: 1, borderRadius: 16, paddingVertical: 14, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, }, addTimeLabel: { fontSize: 15, fontWeight: '600', }, footerBar: { position: 'absolute', left: 0, right: 0, bottom: 0, paddingHorizontal: 20, paddingTop: 16, borderTopWidth: StyleSheet.hairlineWidth, borderTopColor: 'rgba(15,23,42,0.06)', }, saveButton: { height: 56, borderRadius: 24, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, overflow: 'hidden', }, fallbackSaveButton: { backgroundColor: '#7a5af8', shadowColor: 'rgba(122, 90, 248, 0.4)', shadowOffset: { width: 0, height: 10 }, shadowOpacity: 1, shadowRadius: 20, elevation: 6, }, saveButtonText: { fontSize: 17, fontWeight: '700', color: '#fff', }, pickerBackdrop: { flex: 1, backgroundColor: 'rgba(15, 23, 42, 0.4)', }, pickerSheet: { position: 'absolute', left: 20, right: 20, bottom: 40, borderRadius: 24, padding: 20, shadowColor: '#000', shadowOffset: { width: 0, height: 10 }, shadowOpacity: 0.2, shadowRadius: 20, elevation: 8, }, pickerTitle: { fontSize: 20, fontWeight: '700', marginBottom: 16, textAlign: 'center', }, pickerActions: { flexDirection: 'row', gap: 12, marginTop: 16, }, pickerBtn: { flex: 1, paddingVertical: 14, borderRadius: 16, alignItems: 'center', borderWidth: 1, }, pickerBtnPrimary: { borderWidth: 0, }, pickerBtnText: { fontSize: 16, fontWeight: '600', }, });