feat: 更新目标创建功能及相关组件

- 在 CreateGoalModal 中新增目标创建表单,支持设置标题、描述、重复周期、频率、提醒时间和结束日期
- 更新 GoalCard 组件,增加显示结束日期的功能
- 修改 goals.tsx 文件,调整 CreateGoalModal 的导入路径
- 更新 eslint 配置,增加对 node_modules 的忽略设置,优化代码检查
This commit is contained in:
richarjiang
2025-08-26 15:35:10 +08:00
parent 3f89023447
commit 0610f287ee
4 changed files with 193 additions and 34 deletions

View File

@@ -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';

View File

@@ -118,7 +118,9 @@ export const GoalCard: React.FC<GoalCardProps> = ({
onPress={handleDelete}
activeOpacity={0.8}
>
<MaterialIcons name="delete" size={24} color="#EF4444" />
<MaterialIcons style={{
marginBottom: 10
}} name="delete" size={24} color="#EF4444" />
</TouchableOpacity>
);
};
@@ -187,6 +189,19 @@ export const GoalCard: React.FC<GoalCardProps> = ({
<View style={styles.infoItem}>
<Text style={styles.infoText}>{getRepeatTypeText(goal)}</Text>
</View>
{/* 结束日期 */}
{goal.endDate && (
<View style={styles.infoItem}>
<MaterialIcons
name="calendar-month"
size={12}
color="#9CA3AF"
style={{ marginRight: 4 }}
/>
<Text style={styles.infoText}>{goal.endDate}</Text>
</View>
)}
</View>
</View>

View File

@@ -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,
@@ -71,6 +71,11 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
initialData?.customRepeatRule?.dayOfMonth || [1, 15]
); // 默认1号和15号
// 结束日期选择状态
const [endDate, setEndDate] = useState<string | null>(initialData?.endDate || null);
const [showDatePicker, setShowDatePicker] = useState(false);
const [tempSelectedDate, setTempSelectedDate] = useState<Date | null>(null);
// 当 initialData 变化时更新表单状态
useEffect(() => {
if (initialData) {
@@ -84,6 +89,7 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
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<CreateGoalModalProps> = ({
setPriority(5);
setSelectedWeekdays([1, 2, 3, 4, 5]);
setSelectedMonthDays([1, 15]);
setEndDate(null);
};
// 处理关闭
@@ -139,6 +146,7 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
dayOfMonth: repeatType === 'monthly' ? selectedMonthDays : undefined, // 根据重复类型设置月几
},
startTime,
endDate: endDate || undefined,
};
console.log('goalData', goalData);
@@ -196,6 +204,55 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
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 (
<Modal
visible={visible}
@@ -522,6 +579,40 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
</View>
</View>
{/* 结束日期设置 */}
<View style={[styles.optionCard, { backgroundColor: colorTokens.card }]}>
<View style={styles.optionHeader}>
<View style={styles.optionIcon}>
<Image
source={require('@/assets/images/icons/icon-calender.png')}
style={styles.optionIconImage}
/>
</View>
<Text style={[styles.optionLabel, { color: colorTokens.text }]}>
</Text>
<TouchableOpacity
style={[styles.optionValue]}
onPress={showDatePickerModal}
>
<Text style={[styles.optionValueText, { color: colorTokens.textSecondary }]}>
{endDate ? formatDisplayDate(endDate) : '无限制'}
</Text>
<Text style={[styles.chevron, { color: colorTokens.textSecondary }]}>
</Text>
</TouchableOpacity>
</View>
{endDate && (
<TouchableOpacity
style={styles.clearEndDateButton}
onPress={() => setEndDate(null)}
>
<Text style={styles.clearEndDateText}></Text>
</TouchableOpacity>
)}
</View>
{/* 时间选择器弹窗 */}
<Modal
visible={showTimePicker}
@@ -561,6 +652,46 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
</View>
</Modal>
{/* 日期选择器弹窗 */}
<Modal
visible={showDatePicker}
transparent
animationType="fade"
onRequestClose={() => setShowDatePicker(false)}
>
<TouchableOpacity
style={styles.modalBackdrop}
activeOpacity={1}
onPress={handleCancelDate}
/>
<View style={styles.modalSheet}>
<DateTimePicker
value={tempSelectedDate || getCurrentEndDate()}
mode="date"
display={Platform.OS === 'ios' ? 'calendar' : 'default'}
onChange={handleDateChange}
minimumDate={new Date()}
locale="zh-CN"
/>
{Platform.OS === 'ios' && (
<View style={styles.modalActions}>
<TouchableOpacity
style={[styles.modalBtn]}
onPress={handleCancelDate}
>
<Text style={styles.modalBtnText}></Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.modalBtn, styles.modalBtnPrimary]}
onPress={handleConfirmDate}
>
<Text style={[styles.modalBtnText, styles.modalBtnTextPrimary]}></Text>
</TouchableOpacity>
</View>
)}
</View>
</Modal>
{/* 描述输入(可选) */}
<View style={[styles.optionCard, { backgroundColor: colorTokens.card }]}>
<TextInput
@@ -933,6 +1064,19 @@ const styles = StyleSheet.create({
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;

View File

@@ -5,6 +5,6 @@ const expoConfig = require('eslint-config-expo/flat');
module.exports = defineConfig([
expoConfig,
{
ignores: ['dist/*'],
ignores: ['dist/*', 'node_modules/*'],
},
]);