From 0610f287eef322698cec44eb7344bd29529fff14 Mon Sep 17 00:00:00 2001 From: richarjiang Date: Tue, 26 Aug 2025 15:35:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E7=9B=AE=E6=A0=87?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E5=8A=9F=E8=83=BD=E5=8F=8A=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 CreateGoalModal 中新增目标创建表单,支持设置标题、描述、重复周期、频率、提醒时间和结束日期 - 更新 GoalCard 组件,增加显示结束日期的功能 - 修改 goals.tsx 文件,调整 CreateGoalModal 的导入路径 - 更新 eslint 配置,增加对 node_modules 的忽略设置,优化代码检查 --- app/(tabs)/goals.tsx | 2 +- components/GoalCard.tsx | 29 ++- components/{ => model}/CreateGoalModal.tsx | 194 ++++++++++++++++++--- eslint.config.js | 2 +- 4 files changed, 193 insertions(+), 34 deletions(-) rename components/{ => model}/CreateGoalModal.tsx (82%) diff --git a/app/(tabs)/goals.tsx b/app/(tabs)/goals.tsx index 41a647b..525ca5e 100644 --- a/app/(tabs)/goals.tsx +++ b/app/(tabs)/goals.tsx @@ -1,10 +1,10 @@ -import { CreateGoalModal } from '@/components/CreateGoalModal'; import GoalTemplateModal from '@/components/GoalTemplateModal'; import { GoalsPageGuide } from '@/components/GoalsPageGuide'; import { GuideTestButton } from '@/components/GuideTestButton'; import { TaskCard } from '@/components/TaskCard'; import { TaskFilterTabs, TaskFilterType } from '@/components/TaskFilterTabs'; import { TaskProgressCard } from '@/components/TaskProgressCard'; +import { CreateGoalModal } from '@/components/model/CreateGoalModal'; import { useGlobalDialog } from '@/components/ui/DialogProvider'; import { Colors } from '@/constants/Colors'; import { TAB_BAR_BOTTOM_OFFSET, TAB_BAR_HEIGHT } from '@/constants/TabBar'; diff --git a/components/GoalCard.tsx b/components/GoalCard.tsx index 07d6b70..971ef3f 100644 --- a/components/GoalCard.tsx +++ b/components/GoalCard.tsx @@ -11,11 +11,11 @@ interface GoalCardProps { showStatus?: boolean; } -export const GoalCard: React.FC = ({ - goal, - onPress, +export const GoalCard: React.FC = ({ + goal, + onPress, onDelete, - showStatus = true + showStatus = true }) => { const swipeableRef = useRef(null); @@ -70,7 +70,7 @@ export const GoalCard: React.FC = ({ // 根据目标类别或标题返回不同的图标 const title = goal.title.toLowerCase(); const category = goal.category?.toLowerCase(); - + if (title.includes('运动') || title.includes('健身') || title.includes('跑步')) { return 'fitness-center'; } else if (title.includes('喝水') || title.includes('饮水')) { @@ -118,7 +118,9 @@ export const GoalCard: React.FC = ({ onPress={handleDelete} activeOpacity={0.8} > - + ); }; @@ -149,7 +151,7 @@ export const GoalCard: React.FC = ({ {goal.title} - + {/* 底部信息行 */} {/* 积分 */} @@ -187,6 +189,19 @@ export const GoalCard: React.FC = ({ {getRepeatTypeText(goal)} + + {/* 结束日期 */} + {goal.endDate && ( + + + {goal.endDate} + + )} diff --git a/components/CreateGoalModal.tsx b/components/model/CreateGoalModal.tsx similarity index 82% rename from components/CreateGoalModal.tsx rename to components/model/CreateGoalModal.tsx index 1efc825..d119dce 100644 --- a/components/CreateGoalModal.tsx +++ b/components/model/CreateGoalModal.tsx @@ -4,7 +4,7 @@ import { CreateGoalRequest, GoalPriority, RepeatType } from '@/types/goals'; import { Ionicons } from '@expo/vector-icons'; import DateTimePicker from '@react-native-community/datetimepicker'; import { LinearGradient } from 'expo-linear-gradient'; -import React, { useState, useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { Alert, Image, @@ -61,7 +61,7 @@ export const CreateGoalModal: React.FC = ({ 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] @@ -71,6 +71,11 @@ export const CreateGoalModal: React.FC = ({ 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) { @@ -84,6 +89,7 @@ export const CreateGoalModal: React.FC = ({ setPriority(initialData.priority || 5); setSelectedWeekdays(initialData.customRepeatRule?.weekdays || [1, 2, 3, 4, 5]); setSelectedMonthDays(initialData.customRepeatRule?.dayOfMonth || [1, 15]); + setEndDate(initialData.endDate || null); } }, [initialData]); @@ -99,6 +105,7 @@ export const CreateGoalModal: React.FC = ({ setPriority(5); setSelectedWeekdays([1, 2, 3, 4, 5]); setSelectedMonthDays([1, 15]); + setEndDate(null); }; // 处理关闭 @@ -139,12 +146,13 @@ export const CreateGoalModal: React.FC = ({ dayOfMonth: repeatType === 'monthly' ? selectedMonthDays : undefined, // 根据重复类型设置月几 }, startTime, + endDate: endDate || undefined, }; console.log('goalData', goalData); onSubmit(goalData); - + // 通知父组件提交成功 if (onSuccess) { onSuccess(); @@ -196,6 +204,55 @@ export const CreateGoalModal: React.FC = ({ 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 ( = ({ setShowRepeatTypePicker(true)}> - @@ -268,10 +325,10 @@ export const CreateGoalModal: React.FC = ({ ? `${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 + ? 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 } @@ -295,16 +352,16 @@ export const CreateGoalModal: React.FC = ({ /> {/* 关闭按钮 */} - setShowRepeatTypePicker(false)} > × - + {/* 标题 */} 目标重复周期 - + {/* 重复类型选择 */} {REPEAT_TYPE_OPTIONS.map((option) => ( @@ -325,7 +382,7 @@ export const CreateGoalModal: React.FC = ({ ))} - + {/* 周几选择 - 仅在选择每周时显示 */} {repeatType === 'weekly' && ( @@ -396,7 +453,7 @@ export const CreateGoalModal: React.FC = ({ )} - + {/* 完成按钮 */} = ({ - + 频率 @@ -479,10 +536,10 @@ export const CreateGoalModal: React.FC = ({ - + 提醒 @@ -500,10 +557,10 @@ export const CreateGoalModal: React.FC = ({ - + 时间 @@ -522,6 +579,40 @@ export const CreateGoalModal: React.FC = ({ + {/* 结束日期设置 */} + + + + + + + 结束日期 + + + + {endDate ? formatDisplayDate(endDate) : '无限制'} + + + › + + + + {endDate && ( + setEndDate(null)} + > + 清除结束日期 + + )} + + {/* 时间选择器弹窗 */} = ({ + {/* 日期选择器弹窗 */} + setShowDatePicker(false)} + > + + + + {Platform.OS === 'ios' && ( + + + 取消 + + + 确定 + + + )} + + + {/* 描述输入(可选) */}