import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import React, { useEffect } from 'react'; import { Pressable, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native'; import Animated, { FadeInUp, FadeOut, interpolate, Layout, useAnimatedStyle, useSharedValue, withRepeat, withSpring, withTiming } from 'react-native-reanimated'; import { ThemedText } from '@/components/ThemedText'; import { HeaderBar } from '@/components/ui/HeaderBar'; import { palette } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { clearError, deletePlan, loadPlans, setCurrentPlan, type TrainingPlan } from '@/store/trainingPlanSlice'; const GOAL_TEXT: Record = { postpartum_recovery: { title: '产后恢复', color: '#9BE370', description: '温和激活,核心重建' }, fat_loss: { title: '减脂塑形', color: '#FFB86B', description: '全身燃脂,线条雕刻' }, posture_correction: { title: '体态矫正', color: '#95CCE3', description: '打开胸肩,改善圆肩驼背' }, core_strength: { title: '核心力量', color: '#A48AED', description: '核心稳定,提升运动表现' }, flexibility: { title: '柔韧灵活', color: '#B0F2A7', description: '拉伸延展,释放紧张' }, rehab: { title: '康复保健', color: '#FF8E9E', description: '循序渐进,科学修复' }, stress_relief: { title: '释压放松', color: '#9BD1FF', description: '舒缓身心,改善睡眠' }, }; // 动态背景组件 function DynamicBackground() { const rotate = useSharedValue(0); const scale = useSharedValue(1); React.useEffect(() => { rotate.value = withRepeat(withTiming(360, { duration: 20000 }), -1); scale.value = withRepeat(withTiming(1.2, { duration: 8000 }), -1, true); }, []); const backgroundStyle = useAnimatedStyle(() => ({ transform: [ { rotate: `${rotate.value}deg` }, { scale: scale.value } ], })); return ( ); } // 简洁的训练计划卡片 function PlanCard({ plan, onPress, onDelete, isActive, index }: { plan: TrainingPlan; onPress: () => void; onDelete: () => void; isActive?: boolean; index: number }) { const scale = useSharedValue(1); const glow = useSharedValue(0); React.useEffect(() => { glow.value = withRepeat(withTiming(1, { duration: 2000 + index * 100 }), -1, true); }, [index]); const goalConfig = GOAL_TEXT[plan.goal] || { title: '训练计划', color: palette.primary, description: '开始你的训练之旅' }; const cardStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], })); const glowStyle = useAnimatedStyle(() => { const opacity = isActive ? interpolate(glow.value, [0, 1], [0.3, 0.7]) : interpolate(glow.value, [0, 1], [0.15, 0.4]); return { shadowOpacity: opacity, shadowColor: goalConfig.color, shadowRadius: isActive ? 24 : 16, elevation: isActive ? 16 : 12, borderColor: `${goalConfig.color}${isActive ? '50' : '30'}`, }; }); const formatDate = (dateStr: string) => { const date = new Date(dateStr); return `${date.getMonth() + 1}月${date.getDate()}日`; }; const getFrequencyText = () => { if (plan.mode === 'daysOfWeek') { return `每周${plan.daysOfWeek.length}天`; } return `每周${plan.sessionsPerWeek}次`; }; return ( { scale.value = withSpring(0.98); }} onPressOut={() => { scale.value = withSpring(1); }} style={styles.cardContent} > {/* 左侧色彩指示器 */} {/* 主要内容 */} {goalConfig.title} {goalConfig.description} {isActive && ( 当前 )} 开始时间 {formatDate(plan.startDate)} 训练频率 {getFrequencyText()} {plan.preferredTimeOfDay && ( 时间偏好 {plan.preferredTimeOfDay === 'morning' ? '晨练' : plan.preferredTimeOfDay === 'noon' ? '午间' : '晚间'} )} ); } export default function TrainingPlanListScreen() { const router = useRouter(); const dispatch = useAppDispatch(); const { plans, currentId, loading, error } = useAppSelector((s) => s.trainingPlan); useEffect(() => { dispatch(loadPlans()); }, [dispatch]); useEffect(() => { if (error) { // 可以在这里显示错误提示,比如使用 Alert 或 Toast console.error('训练计划错误:', error); // 3秒后自动清除错误 const timer = setTimeout(() => { dispatch(clearError()); }, 3000); return () => clearTimeout(timer); } }, [error, dispatch]); return ( {/* 动态背景 */} router.back()} withSafeTop={false} tone='light' transparent={true} right={( router.push('/training-plan/create' as any)} style={styles.createBtn}> + 新建 )} /> 我的训练计划 点击激活计划,长按删除 {error && ( ⚠️ {error} )} {loading && plans.length === 0 ? ( 加载中... ) : plans.length === 0 ? ( 📋 还没有训练计划 创建你的第一个计划开始训练吧 router.push('/training-plan/create' as any)} style={styles.primaryBtn}> 创建计划 ) : ( {plans.map((p, index) => ( { dispatch(setCurrentPlan(p.id)); }} onDelete={() => dispatch(deletePlan(p.id))} /> ))} {loading && ( 处理中... )} )} ); } const styles = StyleSheet.create({ safeArea: { flex: 1, }, contentWrapper: { flex: 1, }, content: { paddingHorizontal: 20, paddingTop: 8, }, // 动态背景 backgroundOrb: { position: 'absolute', width: 300, height: 300, borderRadius: 150, backgroundColor: 'rgba(187,242,70,0.15)', top: -150, right: -100, }, backgroundOrb2: { position: 'absolute', width: 400, height: 400, borderRadius: 200, backgroundColor: 'rgba(164,138,237,0.12)', bottom: -200, left: -150, }, // 页面标题区域 headerSection: { marginBottom: 20, }, title: { fontSize: 28, fontWeight: '800', color: '#192126', marginBottom: 4, }, subtitle: { fontSize: 14, color: '#5E6468', opacity: 0.8, }, // 训练计划列表 plansList: { gap: 12, }, // 训练计划卡片 planCard: { borderRadius: 16, overflow: 'hidden', shadowOffset: { width: 0, height: 6 }, shadowRadius: 16, elevation: 12, borderWidth: 1.5, borderColor: 'rgba(187,242,70,0.3)', backgroundColor: '#FFFFFF', shadowColor: '#BBF246', }, cardContent: { position: 'relative', }, cardGradient: { ...StyleSheet.absoluteFillObject, borderRadius: 16, }, cardGlow: { position: 'absolute', top: -2, left: -2, right: -2, bottom: -2, borderRadius: 18, backgroundColor: 'transparent', }, colorIndicator: { position: 'absolute', left: 0, top: 0, bottom: 0, width: 4, borderTopLeftRadius: 16, borderBottomLeftRadius: 16, }, cardMain: { padding: 20, paddingLeft: 24, }, cardHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16, }, titleSection: { flex: 1, }, planTitle: { fontSize: 18, fontWeight: '800', color: '#192126', marginBottom: 4, }, planDescription: { fontSize: 13, color: '#5E6468', opacity: 0.8, }, activeBadge: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12, marginLeft: 12, }, activeText: { fontSize: 11, fontWeight: '800', color: palette.ink, }, cardInfo: { flexDirection: 'row', gap: 20, }, infoItem: { flex: 1, }, infoLabel: { fontSize: 11, color: '#888F92', marginBottom: 2, fontWeight: '600', }, infoValue: { fontSize: 14, color: '#384046', fontWeight: '600', }, // 按钮样式 primaryBtn: { marginTop: 20, backgroundColor: palette.primary, paddingVertical: 14, paddingHorizontal: 28, borderRadius: 24, alignItems: 'center', shadowColor: palette.primary, shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.3, shadowRadius: 8, elevation: 6, }, primaryBtnText: { color: palette.ink, fontSize: 15, fontWeight: '800', }, createBtn: { backgroundColor: palette.primary, paddingHorizontal: 16, paddingVertical: 10, borderRadius: 22, shadowColor: palette.primary, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.3, shadowRadius: 4, elevation: 4, minWidth: 44, minHeight: 44, alignItems: 'center', justifyContent: 'center', }, createBtnText: { color: palette.ink, fontWeight: '800', fontSize: 14, }, // 空状态 emptyWrap: { alignItems: 'center', justifyContent: 'center', paddingVertical: 60, }, emptyIcon: { width: 80, height: 80, borderRadius: 40, backgroundColor: 'rgba(187,242,70,0.1)', alignItems: 'center', justifyContent: 'center', marginBottom: 16, }, emptyIconText: { fontSize: 32, }, emptyText: { fontSize: 18, color: '#192126', fontWeight: '600', marginBottom: 4, }, emptySubtext: { fontSize: 14, color: '#5E6468', textAlign: 'center', marginBottom: 20, }, // 加载状态 loadingWrap: { alignItems: 'center', justifyContent: 'center', paddingVertical: 60, }, loadingIcon: { width: 80, height: 80, borderRadius: 40, backgroundColor: 'rgba(187,242,70,0.1)', alignItems: 'center', justifyContent: 'center', marginBottom: 16, }, loadingIconText: { fontSize: 32, }, loadingText: { fontSize: 18, color: '#192126', fontWeight: '600', marginBottom: 4, }, loadingIndicator: { alignItems: 'center', paddingVertical: 20, }, loadingIndicatorText: { fontSize: 14, color: '#5E6468', fontWeight: '600', }, // 错误状态 errorContainer: { backgroundColor: 'rgba(237,71,71,0.1)', borderRadius: 12, padding: 16, marginBottom: 16, borderWidth: 1, borderColor: 'rgba(237,71,71,0.2)', }, errorText: { fontSize: 14, color: '#ED4747', fontWeight: '600', textAlign: 'center', }, });