feat: 优化目标创建成功提示及任务筛选功能

- 将目标创建成功后的提示从系统默认的 Alert.alert 改为使用自定义确认弹窗,提升用户体验和视觉一致性
- 在任务筛选中新增“已跳过”选项,支持用户更好地管理任务状态
- 更新任务卡片和进度卡片,展示跳过任务的数量和状态
- 调整相关组件样式,确保界面一致性和美观性
- 编写相关文档,详细描述新功能和使用方法
This commit is contained in:
2025-08-22 22:19:49 +08:00
parent 7d28b79d86
commit 75806df660
9 changed files with 436 additions and 37 deletions

View File

@@ -6,16 +6,16 @@ import DateTimePicker from '@react-native-community/datetimepicker';
import { LinearGradient } from 'expo-linear-gradient';
import React, { useState } from 'react';
import {
Alert,
Modal,
Platform,
ScrollView,
StyleSheet,
Switch,
Text,
TextInput,
TouchableOpacity,
View,
Alert,
Modal,
Platform,
ScrollView,
StyleSheet,
Switch,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import WheelPickerExpo from 'react-native-wheel-picker-expo';
@@ -23,6 +23,7 @@ interface CreateGoalModalProps {
visible: boolean;
onClose: () => void;
onSubmit: (goalData: CreateGoalRequest) => void;
onSuccess?: () => void;
loading?: boolean;
}
@@ -39,6 +40,7 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
visible,
onClose,
onSubmit,
onSuccess,
loading = false,
}) => {
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
@@ -65,7 +67,7 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
setRepeatType('daily');
setFrequency(1);
setHasReminder(false);
setReminderTime('19:00');
setReminderTime('20:00');
setCategory('');
setPriority(5);
};
@@ -113,6 +115,11 @@ export const CreateGoalModal: React.FC<CreateGoalModalProps> = ({
console.log('goalData', goalData);
onSubmit(goalData);
// 通知父组件提交成功
if (onSuccess) {
onSuccess();
}
};
// 时间选择器

View File

@@ -100,7 +100,7 @@ export const TaskCard: React.FC<TaskCardProps> = ({
showConfirm(
{
title: '确认跳过任务',
message: `确定要跳过任务"${task.title}"吗?跳过后将无法恢复。`,
message: `确定要跳过任务"${task.title}"吗?\n\n跳过后的任务将不会显示在任务列表中无法恢复。`,
confirmText: '跳过',
cancelText: '取消',
destructive: true,
@@ -189,11 +189,24 @@ export const TaskCard: React.FC<TaskCardProps> = ({
style={[
styles.progressFill,
{
width: `${Math.min(task.progressPercentage, 100)}%`,
backgroundColor: colorTokens.primary,
width: task.progressPercentage > 0 ? `${Math.min(task.progressPercentage, 100)}%` : '2%',
backgroundColor: task.progressPercentage >= 100
? '#10B981'
: task.progressPercentage >= 50
? '#F59E0B'
: task.progressPercentage > 0
? colorTokens.primary
: '#E5E7EB',
},
]}
/>
{task.progressPercentage > 0 && task.progressPercentage < 100 && (
<View style={styles.progressGlow} />
)}
{/* 进度百分比文本 */}
<View style={styles.progressTextContainer}>
<Text style={styles.progressText}>{Math.round(task.progressPercentage)}%</Text>
</View>
</View>
{/* 底部信息 */}
@@ -314,15 +327,57 @@ const styles = StyleSheet.create({
color: '#FFFFFF',
},
progressBar: {
height: 2,
backgroundColor: '#E5E7EB',
borderRadius: 1,
height: 6,
backgroundColor: '#F3F4F6',
borderRadius: 3,
marginBottom: 16,
overflow: 'hidden',
overflow: 'visible',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2,
position: 'relative',
},
progressFill: {
height: '100%',
borderRadius: 1,
borderRadius: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.2,
shadowRadius: 2,
elevation: 3,
},
progressGlow: {
position: 'absolute',
right: 0,
top: 0,
width: 8,
height: '100%',
backgroundColor: 'rgba(255, 255, 255, 0.6)',
borderRadius: 3,
},
progressTextContainer: {
position: 'absolute',
right: 0,
top: -6,
backgroundColor: '#FFFFFF',
paddingHorizontal: 6,
paddingVertical: 2,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2,
borderWidth: 1,
borderColor: '#E5E7EB',
zIndex: 1,
},
progressText: {
fontSize: 10,
fontWeight: '600',
color: '#374151',
},
footer: {
flexDirection: 'row',

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
export type TaskFilterType = 'all' | 'pending' | 'completed';
export type TaskFilterType = 'all' | 'pending' | 'completed' | 'skipped';
interface TaskFilterTabsProps {
selectedFilter: TaskFilterType;
@@ -10,6 +10,7 @@ interface TaskFilterTabsProps {
all: number;
pending: number;
completed: number;
skipped: number;
};
}
@@ -101,6 +102,33 @@ export const TaskFilterTabs: React.FC<TaskFilterTabsProps> = ({
</Text>
</View>
</TouchableOpacity>
{/* 已跳过 Tab */}
<TouchableOpacity
style={[
styles.tab,
selectedFilter === 'skipped' && styles.activeTab
]}
onPress={() => onFilterChange('skipped')}
>
<Text style={[
styles.tabText,
selectedFilter === 'skipped' && styles.activeTabText
]}>
</Text>
<View style={[
styles.badge,
selectedFilter === 'skipped' ? styles.activeBadge : styles.inactiveBadge
]}>
<Text style={[
styles.badgeText,
selectedFilter === 'skipped' && styles.activeBadgeText
]}>
{taskCounts.skipped}
</Text>
</View>
</TouchableOpacity>
</View>
</View>
);

View File

@@ -55,7 +55,7 @@ export const TaskProgressCard: React.FC<TaskProgressCardProps> = ({
{/* 已跳过 卡片 */}
<View style={styles.statusCard}>
<View style={styles.cardHeader}>
<MaterialIcons name="skip-next" size={16} color="#6B7280" />
<MaterialIcons name="skip-next" size={16} color="#F59E0B" />
<Text style={styles.cardLabel} numberOfLines={1}></Text>
</View>
<Text style={styles.cardCount}>{skippedTasks.length}</Text>
@@ -106,7 +106,7 @@ const styles = StyleSheet.create({
statusCards: {
flexDirection: 'row',
justifyContent: 'space-between',
gap: 12,
gap: 8,
},
statusCard: {
flex: 1,

View File

@@ -146,20 +146,26 @@ export function ConfirmDialog({
{message && <Text style={styles.message}>{message}</Text>}
{/* 按钮组 */}
<View style={styles.buttonContainer}>
<TouchableOpacity
style={[styles.button, styles.cancelButton]}
onPress={handleCancel}
activeOpacity={0.7}
>
<Text style={styles.cancelButtonText}>{cancelText}</Text>
</TouchableOpacity>
<View style={[
styles.buttonContainer,
!cancelText && styles.singleButtonContainer
]}>
{cancelText && (
<TouchableOpacity
style={[styles.button, styles.cancelButton]}
onPress={handleCancel}
activeOpacity={0.7}
>
<Text style={styles.cancelButtonText}>{cancelText}</Text>
</TouchableOpacity>
)}
<TouchableOpacity
style={[
styles.button,
styles.confirmButton,
{ backgroundColor: confirmButtonColor },
!cancelText && styles.singleConfirmButton,
]}
onPress={handleConfirm}
activeOpacity={0.7}
@@ -226,12 +232,18 @@ const styles = StyleSheet.create({
gap: 12,
width: '100%',
},
singleButtonContainer: {
gap: 0,
},
button: {
flex: 1,
paddingVertical: 14,
borderRadius: 12,
alignItems: 'center',
},
singleConfirmButton: {
flex: 1,
},
cancelButton: {
backgroundColor: '#F3F4F6',
},