feat: 增强任务管理功能,新增任务筛选和状态统计组件
- 在目标页面中集成任务筛选标签,支持按状态(全部、待完成、已完成)过滤任务 - 更新任务卡片,展示任务状态和优先级信息 - 新增任务进度卡片,统计各状态任务数量 - 优化空状态展示,根据筛选条件动态显示提示信息 - 引入新图标和图片资源,提升界面视觉效果
This commit is contained in:
@@ -1,140 +1,139 @@
|
||||
import { TaskListItem } from '@/types/goals';
|
||||
import React from 'react';
|
||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
interface TaskProgressCardProps {
|
||||
tasks: TaskListItem[];
|
||||
headerButtons?: ReactNode;
|
||||
}
|
||||
|
||||
export const TaskProgressCard: React.FC<TaskProgressCardProps> = ({
|
||||
tasks,
|
||||
headerButtons,
|
||||
}) => {
|
||||
// 计算今日任务完成进度
|
||||
const todayTasks = tasks.filter(task => task.isToday);
|
||||
const completedTodayTasks = todayTasks.filter(task => task.status === 'completed');
|
||||
const progressPercentage = todayTasks.length > 0
|
||||
? Math.round((completedTodayTasks.length / todayTasks.length) * 100)
|
||||
: 0;
|
||||
|
||||
// 计算进度角度
|
||||
const progressAngle = (progressPercentage / 100) * 360;
|
||||
// 计算各状态的任务数量
|
||||
const pendingTasks = tasks.filter(task => task.status === 'pending');
|
||||
const completedTasks = tasks.filter(task => task.status === 'completed');
|
||||
const skippedTasks = tasks.filter(task => task.status === 'skipped');
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{/* 左侧内容 */}
|
||||
<View style={styles.leftContent}>
|
||||
<View style={styles.textContainer}>
|
||||
<Text style={styles.title}>今日目标</Text>
|
||||
<Text style={styles.subtitle}>加油,快完成啦!</Text>
|
||||
{/* 标题区域 */}
|
||||
<View style={styles.header}>
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={styles.title}>任务状态统计</Text>
|
||||
<Text style={styles.subtitle}>各状态任务数量分布</Text>
|
||||
</View>
|
||||
|
||||
{headerButtons && (
|
||||
<View style={styles.headerButtons}>
|
||||
{headerButtons}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 右侧进度圆环 */}
|
||||
<View style={styles.progressContainer}>
|
||||
{/* 背景圆环 */}
|
||||
<View style={[styles.progressCircle, styles.progressBackground]} />
|
||||
|
||||
{/* 进度圆环 */}
|
||||
<View style={[styles.progressCircle, styles.progressFill]}>
|
||||
<View
|
||||
style={[
|
||||
styles.progressArc,
|
||||
{
|
||||
width: 68,
|
||||
height: 68,
|
||||
borderRadius: 34,
|
||||
borderWidth: 6,
|
||||
borderColor: '#8B5CF6',
|
||||
borderTopColor: progressAngle > 0 ? '#8B5CF6' : 'transparent',
|
||||
borderRightColor: progressAngle > 90 ? '#8B5CF6' : 'transparent',
|
||||
borderBottomColor: progressAngle > 180 ? '#8B5CF6' : 'transparent',
|
||||
borderLeftColor: progressAngle > 270 ? '#8B5CF6' : 'transparent',
|
||||
transform: [{ rotate: '-90deg' }],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{/* 状态卡片区域 */}
|
||||
<View style={styles.statusCards}>
|
||||
{/* 待完成 卡片 */}
|
||||
<View style={styles.statusCard}>
|
||||
<View style={styles.cardHeader}>
|
||||
<MaterialIcons name="pending" size={16} color="#7A5AF8" />
|
||||
<Text style={styles.cardLabel} numberOfLines={1}>待完成</Text>
|
||||
</View>
|
||||
<Text style={styles.cardCount}>{pendingTasks.length}</Text>
|
||||
</View>
|
||||
|
||||
{/* 进度文字 */}
|
||||
<View style={styles.progressTextContainer}>
|
||||
<Text style={styles.progressText}>{progressPercentage}%</Text>
|
||||
{/* 已完成 卡片 */}
|
||||
<View style={styles.statusCard}>
|
||||
<View style={styles.cardHeader}>
|
||||
<MaterialIcons name="check-circle" size={16} color="#10B981" />
|
||||
<Text style={styles.cardLabel} numberOfLines={1}>已完成</Text>
|
||||
</View>
|
||||
<Text style={styles.cardCount}>{completedTasks.length}</Text>
|
||||
</View>
|
||||
|
||||
{/* 已跳过 卡片 */}
|
||||
<View style={styles.statusCard}>
|
||||
<View style={styles.cardHeader}>
|
||||
<MaterialIcons name="skip-next" size={16} color="#6B7280" />
|
||||
<Text style={styles.cardLabel} numberOfLines={1}>已跳过</Text>
|
||||
</View>
|
||||
<Text style={styles.cardCount}>{skippedTasks.length}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#8B5CF6',
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderRadius: 16,
|
||||
padding: 20,
|
||||
marginHorizontal: 20,
|
||||
marginBottom: 20,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 8,
|
||||
elevation: 4,
|
||||
},
|
||||
header: {
|
||||
marginBottom: 20,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
titleContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
headerButtons: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
shadowColor: '#8B5CF6',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 8,
|
||||
elevation: 8,
|
||||
},
|
||||
leftContent: {
|
||||
flex: 1,
|
||||
marginRight: 20,
|
||||
},
|
||||
textContainer: {
|
||||
marginBottom: 16,
|
||||
gap: 8,
|
||||
},
|
||||
title: {
|
||||
color: '#FFFFFF',
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
marginBottom: 2,
|
||||
fontSize: 20,
|
||||
fontWeight: '700',
|
||||
color: '#1F2937',
|
||||
marginBottom: 4,
|
||||
},
|
||||
subtitle: {
|
||||
color: '#FFFFFF',
|
||||
fontSize: 14,
|
||||
color: '#6B7280',
|
||||
fontWeight: '400',
|
||||
opacity: 0.9,
|
||||
},
|
||||
progressContainer: {
|
||||
width: 80,
|
||||
height: 80,
|
||||
justifyContent: 'center',
|
||||
statusCards: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
gap: 12,
|
||||
},
|
||||
statusCard: {
|
||||
flex: 1,
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
padding: 12,
|
||||
borderWidth: 1,
|
||||
borderColor: '#E5E7EB',
|
||||
alignItems: 'flex-start',
|
||||
minHeight: 80,
|
||||
},
|
||||
cardHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
marginBottom: 8,
|
||||
gap: 6,
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
progressCircle: {
|
||||
position: 'absolute',
|
||||
width: 80,
|
||||
height: 80,
|
||||
borderRadius: 40,
|
||||
cardLabel: {
|
||||
fontSize: 11,
|
||||
fontWeight: '500',
|
||||
color: '#1F2937',
|
||||
lineHeight: 14,
|
||||
},
|
||||
progressBackground: {
|
||||
borderWidth: 6,
|
||||
borderColor: 'rgba(255, 255, 255, 0.3)',
|
||||
},
|
||||
progressFill: {
|
||||
borderWidth: 6,
|
||||
borderColor: 'transparent',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
progressArc: {
|
||||
position: 'absolute',
|
||||
},
|
||||
progressTextContainer: {
|
||||
position: 'absolute',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
progressText: {
|
||||
color: '#FFFFFF',
|
||||
fontSize: 16,
|
||||
cardCount: {
|
||||
fontSize: 24,
|
||||
fontWeight: '700',
|
||||
color: '#1F2937',
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user