feat: 新增任务管理功能及相关组件
- 将目标页面改为任务列表,支持任务的创建、完成和跳过功能 - 新增任务卡片和任务进度卡片组件,展示任务状态和进度 - 实现任务数据的获取和管理,集成Redux状态管理 - 更新API服务,支持任务相关的CRUD操作 - 编写任务管理功能实现文档,详细描述功能和组件架构
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
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 } from 'react';
|
||||
import {
|
||||
Alert,
|
||||
@@ -15,6 +17,7 @@ import {
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import WheelPickerExpo from 'react-native-wheel-picker-expo';
|
||||
|
||||
interface CreateGoalModalProps {
|
||||
visible: boolean;
|
||||
@@ -30,7 +33,7 @@ const REPEAT_TYPE_OPTIONS: { value: RepeatType; label: string }[] = [
|
||||
{ value: 'custom', label: '自定义' },
|
||||
];
|
||||
|
||||
const FREQUENCY_OPTIONS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
const FREQUENCY_OPTIONS = Array.from({ length: 30 }, (_, i) => i + 1);
|
||||
|
||||
export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
|
||||
visible,
|
||||
@@ -47,6 +50,8 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
|
||||
const [repeatType, setRepeatType] = useState<RepeatType>('daily');
|
||||
const [frequency, setFrequency] = useState(1);
|
||||
const [hasReminder, setHasReminder] = useState(false);
|
||||
const [showFrequencyPicker, setShowFrequencyPicker] = useState(false);
|
||||
const [showRepeatTypePicker, setShowRepeatTypePicker] = useState(false);
|
||||
const [reminderTime, setReminderTime] = useState('20:00');
|
||||
const [category, setCategory] = useState('');
|
||||
const [priority, setPriority] = useState<GoalPriority>(5);
|
||||
@@ -163,12 +168,21 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
|
||||
onRequestClose={handleClose}
|
||||
>
|
||||
<View style={[styles.container, { backgroundColor: colorTokens.background }]}>
|
||||
{/* 渐变背景 */}
|
||||
<LinearGradient
|
||||
colors={['#F0F9FF', '#E0F2FE']}
|
||||
style={styles.gradientBackground}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
/>
|
||||
|
||||
{/* 装饰性圆圈 */}
|
||||
<View style={styles.decorativeCircle1} />
|
||||
<View style={styles.decorativeCircle2} />
|
||||
{/* 头部 */}
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity onPress={handleClose} disabled={loading}>
|
||||
<Text style={[styles.cancelButton, { color: colorTokens.text }]}>
|
||||
←
|
||||
</Text>
|
||||
<Ionicons name="close" size={24} color={colorTokens.text} />
|
||||
</TouchableOpacity>
|
||||
<Text style={[styles.title, { color: colorTokens.text }]}>
|
||||
创建新目标
|
||||
@@ -180,13 +194,13 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
|
||||
{/* 目标标题输入 */}
|
||||
<View style={styles.section}>
|
||||
<View style={styles.iconTitleContainer}>
|
||||
<View style={styles.iconPlaceholder}>
|
||||
{/* <View style={styles.iconPlaceholder}>
|
||||
<Text style={styles.iconText}>图标</Text>
|
||||
</View>
|
||||
</View> */}
|
||||
<TextInput
|
||||
style={[styles.titleInput, { color: colorTokens.text }]}
|
||||
placeholder="写点什么..."
|
||||
placeholderTextColor={colorTokens.textSecondary}
|
||||
// placeholderTextColor={colorTokens.textSecondary}
|
||||
value={title}
|
||||
onChangeText={setTitle}
|
||||
multiline
|
||||
@@ -201,44 +215,132 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
|
||||
|
||||
{/* 目标重复周期 */}
|
||||
<View style={[styles.optionCard, { backgroundColor: colorTokens.card }]}>
|
||||
<View style={styles.optionHeader}>
|
||||
<View style={styles.optionIcon}>
|
||||
<Text style={styles.optionIconText}>🔄</Text>
|
||||
</View>
|
||||
<Text style={[styles.optionLabel, { color: colorTokens.text }]}>
|
||||
目标重复周期
|
||||
</Text>
|
||||
<TouchableOpacity style={styles.optionValue}>
|
||||
<TouchableOpacity style={styles.optionValue} onPress={() => setShowRepeatTypePicker(true)}>
|
||||
<View style={styles.optionHeader}>
|
||||
<View style={styles.optionIcon}>
|
||||
<Text style={styles.optionIconText}>🔄</Text>
|
||||
</View>
|
||||
<Text style={[styles.optionLabel, { color: colorTokens.text }]}>
|
||||
目标重复周期
|
||||
</Text>
|
||||
<Text style={[styles.optionValueText, { color: colorTokens.textSecondary }]}>
|
||||
{REPEAT_TYPE_OPTIONS.find(opt => opt.value === repeatType)?.label}
|
||||
</Text>
|
||||
<Text style={[styles.chevron, { color: colorTokens.textSecondary }]}>
|
||||
›
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* 重复周期选择器弹窗 */}
|
||||
<Modal
|
||||
visible={showRepeatTypePicker}
|
||||
transparent
|
||||
animationType="fade"
|
||||
onRequestClose={() => setShowRepeatTypePicker(false)}
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={styles.modalBackdrop}
|
||||
activeOpacity={1}
|
||||
onPress={() => setShowRepeatTypePicker(false)}
|
||||
/>
|
||||
<View style={styles.modalSheet}>
|
||||
<View style={{ height: 200 }}>
|
||||
<WheelPickerExpo
|
||||
height={200}
|
||||
width={150}
|
||||
initialSelectedIndex={REPEAT_TYPE_OPTIONS.findIndex(opt => opt.value === repeatType)}
|
||||
items={REPEAT_TYPE_OPTIONS.map(opt => ({ label: opt.label, value: opt.value }))}
|
||||
onChange={({ item }) => setRepeatType(item.value)}
|
||||
backgroundColor={colorTokens.card}
|
||||
haptics
|
||||
/>
|
||||
</View>
|
||||
{Platform.OS === 'ios' && (
|
||||
<View style={styles.modalActions}>
|
||||
<TouchableOpacity
|
||||
style={[styles.modalBtn]}
|
||||
onPress={() => setShowRepeatTypePicker(false)}
|
||||
>
|
||||
<Text style={styles.modalBtnText}>取消</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.modalBtn, styles.modalBtnPrimary]}
|
||||
onPress={() => setShowRepeatTypePicker(false)}
|
||||
>
|
||||
<Text style={[styles.modalBtnText, styles.modalBtnTextPrimary]}>确定</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
{/* 频率设置 */}
|
||||
<View style={[styles.optionCard, { backgroundColor: colorTokens.card }]}>
|
||||
<View style={styles.optionHeader}>
|
||||
<View style={styles.optionIcon}>
|
||||
<Text style={styles.optionIconText}>📊</Text>
|
||||
</View>
|
||||
<Text style={[styles.optionLabel, { color: colorTokens.text }]}>
|
||||
频率
|
||||
</Text>
|
||||
<TouchableOpacity style={styles.optionValue}>
|
||||
<TouchableOpacity style={styles.optionValue} onPress={() => setShowFrequencyPicker(true)}>
|
||||
|
||||
<View style={styles.optionHeader}>
|
||||
<View style={styles.optionIcon}>
|
||||
<Text style={styles.optionIconText}>📊</Text>
|
||||
</View>
|
||||
<Text style={[styles.optionLabel, { color: colorTokens.text }]}>
|
||||
频率
|
||||
</Text>
|
||||
<Text style={[styles.optionValueText, { color: colorTokens.textSecondary }]}>
|
||||
{frequency}
|
||||
</Text>
|
||||
<Text style={[styles.chevron, { color: colorTokens.textSecondary }]}>
|
||||
›
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* 频率选择器弹窗 */}
|
||||
<Modal
|
||||
visible={showFrequencyPicker}
|
||||
transparent
|
||||
animationType="fade"
|
||||
onRequestClose={() => setShowFrequencyPicker(false)}
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={styles.modalBackdrop}
|
||||
activeOpacity={1}
|
||||
onPress={() => setShowFrequencyPicker(false)}
|
||||
/>
|
||||
<View style={styles.modalSheet}>
|
||||
<View style={{ height: 200 }}>
|
||||
<WheelPickerExpo
|
||||
height={200}
|
||||
width={150}
|
||||
initialSelectedIndex={frequency - 1}
|
||||
items={FREQUENCY_OPTIONS.map(num => ({ label: num.toString(), value: num }))}
|
||||
onChange={({ item }) => setFrequency(item.value)}
|
||||
backgroundColor={colorTokens.card}
|
||||
// selectedStyle={{ borderColor: colorTokens.primary, borderWidth: 2 }}
|
||||
haptics
|
||||
/>
|
||||
</View>
|
||||
{Platform.OS === 'ios' && (
|
||||
<View style={styles.modalActions}>
|
||||
<TouchableOpacity
|
||||
style={[styles.modalBtn]}
|
||||
onPress={() => setShowFrequencyPicker(false)}
|
||||
>
|
||||
<Text style={styles.modalBtnText}>取消</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.modalBtn, styles.modalBtnPrimary]}
|
||||
onPress={() => setShowFrequencyPicker(false)}
|
||||
>
|
||||
<Text style={[styles.modalBtnText, styles.modalBtnTextPrimary]}>确定</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
{/* 提醒设置 */}
|
||||
<View style={[styles.optionCard, { backgroundColor: colorTokens.card }]}>
|
||||
<View style={styles.optionHeader}>
|
||||
@@ -354,6 +456,34 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
@@ -378,15 +508,13 @@ const styles = StyleSheet.create({
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
section: {
|
||||
marginBottom: 24,
|
||||
},
|
||||
iconTitleContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderRadius: 16,
|
||||
padding: 20,
|
||||
marginBottom: 16,
|
||||
padding: 16,
|
||||
},
|
||||
iconPlaceholder: {
|
||||
width: 60,
|
||||
@@ -492,6 +620,8 @@ const styles = StyleSheet.create({
|
||||
bottom: 0,
|
||||
padding: 16,
|
||||
backgroundColor: '#FFFFFF',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user