import { GoalCard } from '@/components/GoalCard';
import { Colors } from '@/constants/Colors';
import { TAB_BAR_BOTTOM_OFFSET, TAB_BAR_HEIGHT } from '@/constants/TabBar';
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
import { useColorScheme } from '@/hooks/useColorScheme';
import { deleteGoal, fetchGoals, loadMoreGoals } from '@/store/goalsSlice';
import { GoalListItem } from '@/types/goals';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { useFocusEffect } from '@react-navigation/native';
import { LinearGradient } from 'expo-linear-gradient';
import { useRouter } from 'expo-router';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Alert, FlatList, RefreshControl, SafeAreaView, StatusBar, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
export default function GoalsListScreen() {
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
const colorTokens = Colors[theme];
const dispatch = useAppDispatch();
const router = useRouter();
// Redux状态
const {
goals,
goalsLoading,
goalsError,
goalsPagination,
} = useAppSelector((state) => state.goals);
const [refreshing, setRefreshing] = useState(false);
// 页面聚焦时重新加载数据
useFocusEffect(
useCallback(() => {
console.log('useFocusEffect - loading goals');
loadGoals();
}, [dispatch])
);
// 加载目标列表
const loadGoals = async () => {
try {
await dispatch(fetchGoals({
page: 1,
pageSize: 20,
sortBy: 'createdAt',
sortOrder: 'desc',
})).unwrap();
} catch (error) {
console.error('Failed to load goals:', error);
// 在开发模式下,如果API调用失败,使用模拟数据
if (__DEV__) {
console.log('Using mock data for development');
// 添加模拟数据用于测试左滑删除功能
const mockGoals: GoalListItem[] = [
{
id: 'mock-1',
userId: 'test-user-1',
title: '每日运动30分钟',
repeatType: 'daily',
frequency: 1,
status: 'active',
completedCount: 5,
targetCount: 30,
hasReminder: true,
reminderTime: '09:00',
category: '运动',
priority: 5,
startDate: '2024-01-01',
startTime: 900,
endTime: 1800,
progressPercentage: 17,
},
{
id: 'mock-2',
userId: 'test-user-1',
title: '每天喝8杯水',
repeatType: 'daily',
frequency: 8,
status: 'active',
completedCount: 6,
targetCount: 8,
hasReminder: true,
reminderTime: '10:00',
category: '健康',
priority: 8,
startDate: '2024-01-01',
startTime: 600,
endTime: 2200,
progressPercentage: 75,
},
{
id: 'mock-3',
userId: 'test-user-1',
title: '每周读书2小时',
repeatType: 'weekly',
frequency: 2,
status: 'paused',
completedCount: 1,
targetCount: 2,
hasReminder: false,
category: '学习',
priority: 3,
startDate: '2024-01-01',
startTime: 800,
endTime: 2000,
progressPercentage: 50,
},
];
// 直接更新 Redux 状态(仅用于开发测试)
dispatch({
type: 'goals/fetchGoals/fulfilled',
payload: {
query: { page: 1, pageSize: 20, sortBy: 'createdAt', sortOrder: 'desc' },
response: {
list: mockGoals,
page: 1,
pageSize: 20,
total: mockGoals.length,
}
}
});
}
}
};
// 下拉刷新
const onRefresh = async () => {
setRefreshing(true);
try {
await loadGoals();
} finally {
setRefreshing(false);
}
};
// 加载更多目标
const handleLoadMoreGoals = async () => {
if (goalsPagination.hasMore && !goalsLoading) {
try {
await dispatch(loadMoreGoals()).unwrap();
} catch (error) {
console.error('Failed to load more goals:', error);
}
}
};
// 处理删除目标
const handleDeleteGoal = async (goalId: string) => {
try {
await dispatch(deleteGoal(goalId)).unwrap();
// 删除成功,Redux 会自动更新状态
} catch (error) {
console.error('Failed to delete goal:', error);
Alert.alert('错误', '删除目标失败,请重试');
}
};
// 处理错误提示
useEffect(() => {
if (goalsError) {
Alert.alert('错误', goalsError);
}
}, [goalsError]);
// 计算各状态的目标数量
const goalCounts = useMemo(() => ({
all: goals.length,
active: goals.filter(goal => goal.status === 'active').length,
paused: goals.filter(goal => goal.status === 'paused').length,
completed: goals.filter(goal => goal.status === 'completed').length,
cancelled: goals.filter(goal => goal.status === 'cancelled').length,
}), [goals]);
// 根据筛选条件过滤目标
const filteredGoals = useMemo(() => {
return goals;
}, [goals]);
// 处理目标点击
const handleGoalPress = (goal: GoalListItem) => {
};
// 渲染目标项
const renderGoalItem = ({ item }: { item: GoalListItem }) => (
);
// 渲染空状态
const renderEmptyState = () => {
let title = '暂无目标';
let subtitle = '创建您的第一个目标,开始您的健康之旅';
return (
{title}
{subtitle}
router.push('/(tabs)/goals')}
>
创建目标
);
};
// 渲染加载更多
const renderLoadMore = () => {
if (!goalsPagination.hasMore) return null;
return (
{goalsLoading ? '加载中...' : '上拉加载更多'}
);
};
return (
{/* 背景渐变 */}
{/* 标题区域 */}
router.back()}
>
目标列表
router.push('/(tabs)/goals')}
>
{/* 目标列表 */}
item.id}
contentContainerStyle={styles.goalsList}
showsVerticalScrollIndicator={false}
refreshControl={
}
onEndReached={handleLoadMoreGoals}
onEndReachedThreshold={0.1}
ListEmptyComponent={renderEmptyState}
ListFooterComponent={renderLoadMore}
/>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
gradientBackground: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
},
content: {
flex: 1,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 20,
paddingTop: 20,
paddingBottom: 16,
},
backButton: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: 'rgba(255, 255, 255, 0.8)',
alignItems: 'center',
justifyContent: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
pageTitle: {
fontSize: 24,
fontWeight: '700',
flex: 1,
textAlign: 'center',
},
addButton: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#7A5AF8',
alignItems: 'center',
justifyContent: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
goalsListContainer: {
flex: 1,
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
overflow: 'hidden',
},
goalsList: {
paddingHorizontal: 20,
paddingTop: 20,
paddingBottom: TAB_BAR_HEIGHT + TAB_BAR_BOTTOM_OFFSET + 20,
},
emptyState: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 80,
},
emptyStateTitle: {
fontSize: 18,
fontWeight: '600',
marginTop: 16,
marginBottom: 8,
},
emptyStateSubtitle: {
fontSize: 14,
textAlign: 'center',
lineHeight: 20,
marginBottom: 24,
paddingHorizontal: 40,
},
createButton: {
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 20,
},
createButtonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: '600',
},
loadMoreContainer: {
alignItems: 'center',
paddingVertical: 20,
},
loadMoreText: {
fontSize: 14,
fontWeight: '500',
},
});