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, StatusBar, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; 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', }, });