feat: 优化目标创建成功提示及任务筛选功能
- 将目标创建成功后的提示从系统默认的 Alert.alert 改为使用自定义确认弹窗,提升用户体验和视觉一致性 - 在任务筛选中新增“已跳过”选项,支持用户更好地管理任务状态 - 更新任务卡片和进度卡片,展示跳过任务的数量和状态 - 调整相关组件样式,确保界面一致性和美观性 - 编写相关文档,详细描述新功能和使用方法
This commit is contained in:
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
// 时间选择器
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user