- 在训练计划中新增训练项目的添加、更新和删除功能,支持用户灵活管理训练内容 - 优化训练计划排课界面,提升用户体验 - 更新打卡功能,支持按日期加载和展示打卡记录 - 删除不再使用的打卡相关页面,简化代码结构 - 新增今日训练页面,集成今日训练计划和动作展示 - 更新样式以适应新功能的展示和交互
223 lines
6.2 KiB
TypeScript
223 lines
6.2 KiB
TypeScript
import { type TrainingPlan } from '@/store/trainingPlanSlice';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import React from 'react';
|
|
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
|
|
interface MyPlanCardProps {
|
|
plan: TrainingPlan;
|
|
onPress: () => void;
|
|
}
|
|
|
|
const GOAL_TEXT: Record<string, { title: string; color: string; description: string }> = {
|
|
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: '舒缓身心,改善睡眠' },
|
|
};
|
|
|
|
export function MyPlanCard({ plan, onPress }: MyPlanCardProps) {
|
|
const goalConfig = GOAL_TEXT[plan.goal] || { title: '训练计划', color: '#FF6B47', description: '开始你的训练之旅' };
|
|
|
|
// 格式化日期
|
|
const formatDate = (dateStr: string) => {
|
|
const date = new Date(dateStr);
|
|
const months = ['January', 'February', 'March', 'April', 'May', 'June',
|
|
'July', 'August', 'September', 'October', 'November', 'December'];
|
|
return `${months[date.getMonth()]}, ${date.getFullYear()}`;
|
|
};
|
|
|
|
// 获取训练类型显示文本
|
|
const getWorkoutTypeText = () => {
|
|
if (plan.goal === 'core_strength') return 'Body Weight';
|
|
if (plan.goal === 'flexibility') return 'Flexibility';
|
|
if (plan.goal === 'posture_correction') return 'Posture';
|
|
return 'Body Weight';
|
|
};
|
|
|
|
// 获取周数和训练次数
|
|
const getWeekInfo = () => {
|
|
const startDate = new Date(plan.startDate);
|
|
const now = new Date();
|
|
const diffTime = Math.abs(now.getTime() - startDate.getTime());
|
|
const diffWeeks = Math.ceil(diffTime / (1000 * 60 * 60 * 24 * 7));
|
|
const currentWeek = Math.min(diffWeeks, 12); // 假设最多12周
|
|
|
|
const totalSessions = plan.mode === 'daysOfWeek'
|
|
? plan.daysOfWeek.length * currentWeek
|
|
: plan.sessionsPerWeek * currentWeek;
|
|
|
|
return {
|
|
week: currentWeek,
|
|
session: Math.min(totalSessions, 60), // 假设最多60次训练
|
|
totalSessions: 60
|
|
};
|
|
};
|
|
|
|
const weekInfo = getWeekInfo();
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
{/* Header */}
|
|
<View style={styles.header}>
|
|
<Text style={styles.title}>我的计划</Text>
|
|
<Pressable style={styles.menuButton}>
|
|
<View style={styles.menuDot} />
|
|
<View style={styles.menuDot} />
|
|
<View style={styles.menuDot} />
|
|
</Pressable>
|
|
</View>
|
|
|
|
{/* Date */}
|
|
<Text style={styles.date}>{formatDate(plan.startDate)}</Text>
|
|
|
|
{/* Main Card */}
|
|
<Pressable style={styles.card} onPress={onPress}>
|
|
{/* Icon */}
|
|
<View style={styles.iconContainer}>
|
|
<View style={[styles.iconCircle, { backgroundColor: goalConfig.color }]}>
|
|
<Ionicons name="flash" size={24} color="#FFFFFF" />
|
|
</View>
|
|
</View>
|
|
|
|
{/* Content */}
|
|
<View style={styles.content}>
|
|
{/* Week indicator */}
|
|
<Text style={styles.weekText}>周 {weekInfo.week}</Text>
|
|
|
|
{/* Workout type */}
|
|
<Text style={styles.workoutType}>{getWorkoutTypeText()}</Text>
|
|
|
|
{/* Session info */}
|
|
<Text style={styles.sessionInfo}>训练 {weekInfo.session} 次</Text>
|
|
|
|
{/* Next exercise section */}
|
|
<View style={styles.nextExerciseContainer}>
|
|
<View style={styles.nextExerciseIcon}>
|
|
<Ionicons name="play" size={16} color="#666666" />
|
|
<Ionicons name="play" size={16} color="#666666" style={{ marginLeft: -4 }} />
|
|
</View>
|
|
<View style={styles.nextExerciseText}>
|
|
<Text style={styles.nextExerciseLabel}>下一个动作</Text>
|
|
<Text style={styles.nextExerciseName}>下蹲</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</Pressable>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
marginHorizontal: 24,
|
|
marginBottom: 24,
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
marginBottom: 8,
|
|
},
|
|
title: {
|
|
fontSize: 24,
|
|
fontWeight: 'bold',
|
|
color: '#1A1A1A',
|
|
paddingHorizontal: 24,
|
|
marginBottom: 18,
|
|
},
|
|
menuButton: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 4,
|
|
},
|
|
menuDot: {
|
|
width: 4,
|
|
height: 4,
|
|
borderRadius: 2,
|
|
backgroundColor: '#999999',
|
|
},
|
|
date: {
|
|
fontSize: 16,
|
|
color: '#999999',
|
|
marginBottom: 20,
|
|
},
|
|
card: {
|
|
backgroundColor: '#F5E6E0',
|
|
borderRadius: 24,
|
|
padding: 24,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
shadowColor: '#000',
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 4,
|
|
},
|
|
shadowOpacity: 0.1,
|
|
shadowRadius: 12,
|
|
elevation: 5,
|
|
},
|
|
iconContainer: {
|
|
marginRight: 20,
|
|
},
|
|
iconCircle: {
|
|
width: 64,
|
|
height: 64,
|
|
borderRadius: 32,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
shadowColor: '#000',
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 4,
|
|
},
|
|
shadowOpacity: 0.2,
|
|
shadowRadius: 8,
|
|
elevation: 4,
|
|
},
|
|
content: {
|
|
flex: 1,
|
|
},
|
|
weekText: {
|
|
fontSize: 12,
|
|
color: '#999999',
|
|
fontWeight: '600',
|
|
letterSpacing: 1,
|
|
marginBottom: 4,
|
|
},
|
|
workoutType: {
|
|
fontSize: 24,
|
|
fontWeight: 'bold',
|
|
color: '#1A1A1A',
|
|
marginBottom: 4,
|
|
},
|
|
sessionInfo: {
|
|
fontSize: 14,
|
|
color: '#666666',
|
|
marginBottom: 20,
|
|
},
|
|
nextExerciseContainer: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
nextExerciseIcon: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
marginRight: 12,
|
|
},
|
|
nextExerciseText: {
|
|
flex: 1,
|
|
},
|
|
nextExerciseLabel: {
|
|
fontSize: 12,
|
|
color: '#999999',
|
|
marginBottom: 2,
|
|
},
|
|
nextExerciseName: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
color: '#1A1A1A',
|
|
},
|
|
}); |