import { Colors } from '@/constants/Colors'; import { useColorScheme } from '@/hooks/useColorScheme'; import { CreateGoalRequest, GoalPriority, RepeatType, UpdateGoalRequest } from '@/types/goals'; import { Ionicons } from '@expo/vector-icons'; import DateTimePicker from '@react-native-community/datetimepicker'; import { LinearGradient } from 'expo-linear-gradient'; import React, { useEffect, useState } from 'react'; import { Alert, Image, Modal, Platform, ScrollView, StyleSheet, Switch, Text, TextInput, TouchableOpacity, View, } from 'react-native'; import WheelPickerExpo from 'react-native-wheel-picker-expo'; interface CreateGoalModalProps { visible: boolean; onClose: () => void; onSubmit: (goalData: CreateGoalRequest) => void; onUpdate?: (goalId: string, goalData: UpdateGoalRequest) => void; onSuccess?: () => void; loading?: boolean; initialData?: Partial; editGoalId?: string; } const REPEAT_TYPE_OPTIONS: { value: RepeatType; label: string }[] = [ { value: 'daily', label: '每日' }, { value: 'weekly', label: '每周' }, { value: 'monthly', label: '每月' }, ]; const FREQUENCY_OPTIONS = Array.from({ length: 30 }, (_, i) => i + 1); export const CreateGoalModal: React.FC = ({ visible, onClose, onSubmit, onUpdate, onSuccess, loading = false, initialData, editGoalId, }) => { const theme = (useColorScheme() ?? 'light') as 'light' | 'dark'; const colorTokens = Colors[theme]; // 表单状态 const [title, setTitle] = useState(initialData?.title || ''); const [description, setDescription] = useState(initialData?.description || ''); const [repeatType, setRepeatType] = useState(initialData?.repeatType || 'daily'); const [frequency, setFrequency] = useState(initialData?.frequency || 1); const [hasReminder, setHasReminder] = useState(initialData?.hasReminder || false); const [showFrequencyPicker, setShowFrequencyPicker] = useState(false); const [showRepeatTypePicker, setShowRepeatTypePicker] = useState(false); const [reminderTime, setReminderTime] = useState(initialData?.reminderTime || '20:00'); const [category, setCategory] = useState(initialData?.category || ''); const [priority, setPriority] = useState(initialData?.priority || 5); const [showTimePicker, setShowTimePicker] = useState(false); const [tempSelectedTime, setTempSelectedTime] = useState(null); // 周几选择状态 const [selectedWeekdays, setSelectedWeekdays] = useState( initialData?.customRepeatRule?.weekdays || [1, 2, 3, 4, 5] ); // 默认周一到周五 // 每月日期选择状态 const [selectedMonthDays, setSelectedMonthDays] = useState( initialData?.customRepeatRule?.dayOfMonth || [1, 15] ); // 默认1号和15号 // 结束日期选择状态 const [endDate, setEndDate] = useState(initialData?.endDate || null); const [showDatePicker, setShowDatePicker] = useState(false); const [tempSelectedDate, setTempSelectedDate] = useState(null); // 当 initialData 变化时更新表单状态 useEffect(() => { if (initialData) { setTitle(initialData.title || ''); setDescription(initialData.description || ''); setRepeatType(initialData.repeatType || 'daily'); setFrequency(initialData.frequency || 1); setHasReminder(initialData.hasReminder || false); setReminderTime(initialData.reminderTime || '20:00'); setCategory(initialData.category || ''); setPriority(initialData.priority || 5); setSelectedWeekdays(initialData.customRepeatRule?.weekdays || [1, 2, 3, 4, 5]); setSelectedMonthDays(initialData.customRepeatRule?.dayOfMonth || [1, 15]); setEndDate(initialData.endDate || null); } }, [initialData]); // 重置表单 const resetForm = () => { setTitle(''); setDescription(''); setRepeatType('daily'); setFrequency(1); setHasReminder(false); setReminderTime('20:00'); setCategory(''); setPriority(5); setSelectedWeekdays([1, 2, 3, 4, 5]); setSelectedMonthDays([1, 15]); setEndDate(null); }; // 处理关闭 const handleClose = () => { if (!loading) { resetForm(); onClose(); } }; // 处理提交 const handleSubmit = () => { if (!title.trim()) { Alert.alert('提示', '请输入目标标题'); return; } // 计算startTime:从reminderTime中获取小时和分钟,转换为当天的分钟数 let startTime: number | undefined; if (reminderTime) { const [hours, minutes] = reminderTime.split(':').map(Number); startTime = hours * 60 + minutes; } // 根据是否是编辑模式决定数据结构 if (editGoalId && onUpdate) { // 更新模式:使用 UpdateGoalRequest 结构 const updateData: UpdateGoalRequest = { title: title.trim(), description: description.trim() || undefined, repeatType, frequency, category: category.trim() || undefined, priority, hasReminder, reminderTime: hasReminder ? reminderTime : undefined, customRepeatRule: { weekdays: repeatType === 'weekly' ? selectedWeekdays : [1, 2, 3, 4, 5, 6, 0], dayOfMonth: repeatType === 'monthly' ? selectedMonthDays : undefined, }, endDate: endDate || undefined, }; console.log('updateData', updateData); onUpdate(editGoalId, updateData); } else { // 创建模式:使用 CreateGoalRequest 结构 const goalData: CreateGoalRequest = { title: title.trim(), description: description.trim() || undefined, repeatType, frequency, category: category.trim() || undefined, priority, hasReminder, reminderTime: hasReminder ? reminderTime : undefined, customRepeatRule: { weekdays: repeatType === 'weekly' ? selectedWeekdays : [1, 2, 3, 4, 5, 6, 0], dayOfMonth: repeatType === 'monthly' ? selectedMonthDays : undefined, }, startTime, endDate: endDate || undefined, }; console.log('goalData', goalData); onSubmit(goalData); } // 通知父组件提交成功 if (onSuccess) { onSuccess(); } }; // 时间选择器 const handleTimeChange = (event: any, selectedDate?: Date) => { if (Platform.OS === 'android') { // Android: 用户点击系统确认按钮后自动关闭 if (event.type === 'set' && selectedDate) { const hours = selectedDate.getHours().toString().padStart(2, '0'); const minutes = selectedDate.getMinutes().toString().padStart(2, '0'); setReminderTime(`${hours}:${minutes}`); } setShowTimePicker(false); } else { // iOS: 只在用户点击自定义确认按钮时更新 if (selectedDate) { setTempSelectedTime(selectedDate); } } }; const handleConfirmTime = () => { setShowTimePicker(false); if (tempSelectedTime) { const hours = tempSelectedTime.getHours().toString().padStart(2, '0'); const minutes = tempSelectedTime.getMinutes().toString().padStart(2, '0'); setReminderTime(`${hours}:${minutes}`); } setTempSelectedTime(null); }; const handleCancelTime = () => { setShowTimePicker(false); setTempSelectedTime(null); }; const showTimePickerModal = () => { setShowTimePicker(true); }; // 获取当前时间对应的Date对象 const getCurrentTimeDate = () => { const [hours, minutes] = reminderTime.split(':').map(Number); const date = new Date(); date.setHours(hours, minutes, 0, 0); return date; }; // 日期选择器处理 const handleDateChange = (event: any, selectedDate?: Date) => { if (Platform.OS === 'android') { if (event.type === 'set' && selectedDate) { const isoDate = selectedDate.toISOString().split('T')[0]; setEndDate(isoDate); } setShowDatePicker(false); } else { if (selectedDate) { setTempSelectedDate(selectedDate); } } }; const handleConfirmDate = () => { setShowDatePicker(false); if (tempSelectedDate) { const isoDate = tempSelectedDate.toISOString().split('T')[0]; setEndDate(isoDate); } setTempSelectedDate(null); }; const handleCancelDate = () => { setShowDatePicker(false); setTempSelectedDate(null); }; const showDatePickerModal = () => { setShowDatePicker(true); }; // 获取当前结束日期对应的Date对象 const getCurrentEndDate = () => { if (endDate) { return new Date(endDate + 'T00:00:00'); } const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); return tomorrow; }; // 格式化显示日期 const formatDisplayDate = (dateString: string) => { const date = new Date(dateString + 'T00:00:00'); return `${date.getFullYear()}年${(date.getMonth() + 1).toString().padStart(2, '0')}月${date.getDate().toString().padStart(2, '0')}日`; }; return ( {/* 渐变背景 */} {/* 装饰性圆圈 */} {/* 头部 */} {editGoalId ? '编辑目标' : '创建新目标'} {/* 目标标题输入 */} {/* 图标 */} {/* 装饰图案 */} {/* 目标重复周期 */} setShowRepeatTypePicker(true)}> 重复周期 {repeatType === 'weekly' && selectedWeekdays.length > 0 ? selectedWeekdays.length <= 3 ? `${REPEAT_TYPE_OPTIONS.find(opt => opt.value === repeatType)?.label} ${selectedWeekdays.map(day => ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][day]).join(' ')}` : `${REPEAT_TYPE_OPTIONS.find(opt => opt.value === repeatType)?.label} ${selectedWeekdays.slice(0, 2).map(day => ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][day]).join(' ')}等${selectedWeekdays.length}天` : repeatType === 'monthly' && selectedMonthDays.length > 0 ? selectedMonthDays.length <= 3 ? `${REPEAT_TYPE_OPTIONS.find(opt => opt.value === repeatType)?.label} ${selectedMonthDays.map(day => `${day}号`).join(' ')}` : `${REPEAT_TYPE_OPTIONS.find(opt => opt.value === repeatType)?.label} ${selectedMonthDays.slice(0, 2).map(day => `${day}号`).join(' ')}等${selectedMonthDays.length}天` : REPEAT_TYPE_OPTIONS.find(opt => opt.value === repeatType)?.label } {/* 重复周期选择器弹窗 */} setShowRepeatTypePicker(false)} > setShowRepeatTypePicker(false)} /> {/* 关闭按钮 */} setShowRepeatTypePicker(false)} > × {/* 标题 */} 目标重复周期 {/* 重复类型选择 */} {REPEAT_TYPE_OPTIONS.map((option) => ( setRepeatType(option.value)} > {option.label} ))} {/* 周几选择 - 仅在选择每周时显示 */} {repeatType === 'weekly' && ( {[ { value: 0, label: '周日' }, { value: 1, label: '周一' }, { value: 2, label: '周二' }, { value: 3, label: '周三' }, { value: 4, label: '周四' }, { value: 5, label: '周五' }, { value: 6, label: '周六' }, ].map((weekday) => ( { if (selectedWeekdays.includes(weekday.value)) { setSelectedWeekdays(selectedWeekdays.filter(day => day !== weekday.value)); } else { setSelectedWeekdays([...selectedWeekdays, weekday.value]); } }} > {weekday.label} ))} )} {/* 每月日期选择 - 仅在选择每月时显示 */} {repeatType === 'monthly' && ( {Array.from({ length: 31 }, (_, i) => i + 1).map((day) => ( { if (selectedMonthDays.includes(day)) { setSelectedMonthDays(selectedMonthDays.filter(d => d !== day)); } else { setSelectedMonthDays([...selectedMonthDays, day]); } }} activeOpacity={0.8} > {day} ))} )} {/* 完成按钮 */} setShowRepeatTypePicker(false)} > 完成 {/* 频率设置 */} setShowFrequencyPicker(true)}> 频率 {frequency} {/* 频率选择器弹窗 */} setShowFrequencyPicker(false)} > setShowFrequencyPicker(false)} /> ({ label: num.toString(), value: num }))} onChange={({ item }) => setFrequency(item.value)} backgroundColor={colorTokens.card} // selectedStyle={{ borderColor: colorTokens.primary, borderWidth: 2 }} haptics /> {Platform.OS === 'ios' && ( setShowFrequencyPicker(false)} > 取消 setShowFrequencyPicker(false)} > 确定 )} {/* 提醒设置 */} 提醒 {/* 时间设置 */} 时间 {reminderTime} {/* 结束日期设置 */} 结束日期 {endDate ? formatDisplayDate(endDate) : '无限制'} {endDate && ( setEndDate(null)} > 清除结束日期 )} {/* 时间选择器弹窗 */} setShowTimePicker(false)} > {Platform.OS === 'ios' && ( 取消 确定 )} {/* 日期选择器弹窗 */} setShowDatePicker(false)} > {Platform.OS === 'ios' && ( 取消 确定 )} {/* 描述输入(可选) */} {/* 保存按钮 */} {loading ? (editGoalId ? '更新中...' : '保存中...') : (editGoalId ? '更新' : '保存')} ); }; const styles = StyleSheet.create({ gradientBackground: { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, opacity: 0.6, }, decorativeCircle1: { position: 'absolute', top: -20, right: -20, width: 60, height: 60, borderRadius: 30, backgroundColor: '#0EA5E9', opacity: 0.1, }, decorativeCircle2: { position: 'absolute', bottom: -15, left: -15, width: 40, height: 40, borderRadius: 20, backgroundColor: '#0EA5E9', opacity: 0.05, }, container: { flex: 1, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 20, paddingTop: 20, paddingBottom: 20, }, cancelButton: { fontSize: 24, fontWeight: '600', }, title: { fontSize: 18, fontWeight: '600', }, content: { flex: 1, paddingHorizontal: 20, }, section: { }, iconTitleContainer: { flexDirection: 'row', alignItems: 'flex-start', backgroundColor: '#FFFFFF', borderRadius: 16, padding: 16, }, iconPlaceholder: { width: 60, height: 60, borderRadius: 30, backgroundColor: '#F3F4F6', alignItems: 'center', justifyContent: 'center', marginRight: 16, }, iconText: { fontSize: 12, color: '#9CA3AF', fontWeight: '500', }, titleInput: { flex: 1, fontSize: 16, fontWeight: '500', minHeight: 60, textAlignVertical: 'top', }, decorationContainer: { alignItems: 'flex-end', paddingRight: 20, }, decoration: { width: 80, height: 60, backgroundColor: '#E0E7FF', borderRadius: 40, opacity: 0.6, }, optionCard: { borderRadius: 16, marginBottom: 12, overflow: 'hidden', }, optionHeader: { flexDirection: 'row', alignItems: 'center', padding: 16, }, optionIcon: { width: 32, height: 32, borderRadius: 16, backgroundColor: '#F3F4F6', alignItems: 'center', justifyContent: 'center', marginRight: 12, }, optionIconText: { fontSize: 16, }, optionIconImage: { width: 20, height: 20, resizeMode: 'contain', }, optionLabel: { flex: 1, fontSize: 16, fontWeight: '500', }, optionValue: { flexDirection: 'row', alignItems: 'center', }, optionValueText: { fontSize: 16, fontWeight: '500', marginRight: 8, }, chevron: { fontSize: 20, fontWeight: '300', }, descriptionInput: { padding: 16, fontSize: 16, minHeight: 80, textAlignVertical: 'top', }, footer: { padding: 20, paddingBottom: 40, }, saveButton: { backgroundColor: '#6366F1', borderRadius: 16, paddingVertical: 16, alignItems: 'center', }, saveButtonText: { color: '#FFFFFF', fontSize: 18, fontWeight: '600', }, modalBackdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.35)', }, modalSheet: { position: 'absolute', left: 0, right: 0, bottom: 0, padding: 16, backgroundColor: '#FFFFFF', justifyContent: 'center', alignItems: 'center', borderTopLeftRadius: 16, borderTopRightRadius: 16, }, modalActions: { flexDirection: 'row', justifyContent: 'flex-end', marginTop: 8, gap: 12, }, modalBtn: { paddingHorizontal: 14, paddingVertical: 10, borderRadius: 10, backgroundColor: '#F1F5F9', }, modalBtnPrimary: { backgroundColor: '#6366F1', }, modalBtnText: { color: '#334155', fontWeight: '700', }, modalBtnTextPrimary: { color: '#FFFFFF', fontWeight: '700', }, // 重复类型选择器弹窗样式 repeatTypeModalSheet: { position: 'absolute', left: 20, right: 20, top: '50%', transform: [{ translateY: -300 }], backgroundColor: '#FFFFFF', borderRadius: 16, padding: 24, alignItems: 'center', maxHeight: 500, }, modalCloseButton: { position: 'absolute', top: 16, left: 16, width: 24, height: 24, alignItems: 'center', justifyContent: 'center', }, modalCloseButtonText: { fontSize: 20, color: '#666666', fontWeight: '300', }, repeatTypeModalTitle: { fontSize: 18, fontWeight: '600', color: '#333333', marginBottom: 20, textAlign: 'center', }, repeatTypeOptions: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'center', gap: 8, marginBottom: 20, }, repeatTypeButton: { paddingHorizontal: 16, paddingVertical: 10, borderRadius: 20, backgroundColor: '#F5F5F5', borderWidth: 1, borderColor: '#E5E5E5', marginHorizontal: 4, }, repeatTypeButtonSelected: { backgroundColor: '#E6E6FA', borderColor: '#8A2BE2', }, repeatTypeButtonText: { fontSize: 14, color: '#666666', fontWeight: '500', }, repeatTypeButtonTextSelected: { color: '#8A2BE2', fontWeight: '600', }, weekdaySelection: { width: '100%', marginBottom: 20, }, weekdayOptions: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'center', gap: 6, }, weekdayButton: { paddingHorizontal: 12, paddingVertical: 8, borderRadius: 16, backgroundColor: '#F5F5F5', borderWidth: 1, borderColor: '#E5E5E5', marginHorizontal: 2, }, weekdayButtonSelected: { backgroundColor: '#8A2BE2', borderColor: '#8A2BE2', }, weekdayButtonText: { fontSize: 12, color: '#666666', fontWeight: '500', }, weekdayButtonTextSelected: { color: '#FFFFFF', fontWeight: '600', }, // 每月日期选择样式 - 日历网格布局 monthDaySelection: { width: '100%', marginBottom: 20, }, monthDayGrid: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'flex-start', paddingHorizontal: 8, }, monthDayGridItem: { width: 36, height: 36, borderRadius: 18, backgroundColor: '#F5F5F5', alignItems: 'center', justifyContent: 'center', marginBottom: 10, marginRight: 8, }, monthDayGridItemSelected: { backgroundColor: '#8A2BE2', }, monthDayGridText: { fontSize: 12, fontWeight: '600', color: '#666666', }, monthDayGridTextSelected: { color: '#FFFFFF', }, repeatTypeCompleteButton: { width: '100%', backgroundColor: '#8A2BE2', borderRadius: 20, paddingVertical: 14, alignItems: 'center', marginTop: 10, }, repeatTypeCompleteButtonText: { color: '#FFFFFF', fontSize: 16, fontWeight: '600', }, // 清除结束日期按钮样式 clearEndDateButton: { paddingHorizontal: 16, paddingVertical: 8, alignItems: 'center', borderTopWidth: 1, borderTopColor: '#F1F5F9', }, clearEndDateText: { color: '#EF4444', fontSize: 14, fontWeight: '500', }, }); export default CreateGoalModal;