feat: 移除目标管理演示页面并优化相关组件

- 删除目标管理演示页面的代码,简化项目结构
- 更新底部导航,移除目标管理演示页面的路由
- 调整相关组件的样式和逻辑,确保界面一致性
- 优化颜色常量的使用,提升视觉效果
This commit is contained in:
2025-08-22 21:24:31 +08:00
parent 9e719a9eda
commit c12329bc96
16 changed files with 1130 additions and 759 deletions

View File

@@ -9,7 +9,6 @@ import { DEFAULT_MEMBER_NAME, fetchActivityHistory, fetchMyProfile } from '@/sto
import { Ionicons } from '@expo/vector-icons';
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { useFocusEffect } from '@react-navigation/native';
import { LinearGradient } from 'expo-linear-gradient';
import React, { useMemo, useState } from 'react';
import { Image, Linking, SafeAreaView, ScrollView, StatusBar, StyleSheet, Switch, Text, TouchableOpacity, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -31,7 +30,6 @@ export default function PersonalScreen() {
// 颜色主题
const colors = Colors[colorScheme ?? 'light'];
const theme = (colorScheme ?? 'light') as 'light' | 'dark';
// 直接使用 Redux 中的用户信息,避免重复状态管理
const userProfile = useAppSelector((state) => state.user.profile);
@@ -66,129 +64,100 @@ export default function PersonalScreen() {
// 显示名称
const displayName = (userProfile.name?.trim()) ? userProfile.name : DEFAULT_MEMBER_NAME;
// 颜色令牌
const colorTokens = colors;
const UserInfoSection = () => (
<View style={[styles.userInfoCard, { backgroundColor: colorTokens.card }]}>
<View style={styles.userInfoContainer}>
{/* 头像 */}
<View style={styles.avatarContainer}>
<View style={[styles.avatar, { backgroundColor: colorTokens.ornamentAccent }]}>
<Image source={{ uri: userProfile.avatar || DEFAULT_AVATAR_URL }} style={{ width: '100%', height: '100%' }} />
// 用户信息头部
const UserHeader = () => (
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.cardContainer}>
<View style={styles.userInfoContainer}>
<View style={styles.avatarContainer}>
<Image
source={{ uri: userProfile.avatar || DEFAULT_AVATAR_URL }}
style={styles.avatar}
/>
</View>
<View style={styles.userDetails}>
<Text style={styles.userName}>{displayName}</Text>
</View>
<TouchableOpacity style={styles.editButton} onPress={() => pushIfAuthedElseLogin('/profile/edit')}>
<Text style={styles.editButtonText}></Text>
</TouchableOpacity>
</View>
{/* 用户信息 */}
<View style={styles.userDetails}>
<Text style={[styles.userName, { color: colorTokens.text }]}>{displayName}</Text>
</View>
{/* 编辑按钮 */}
<TouchableOpacity style={dynamicStyles.editButton} onPress={() => pushIfAuthedElseLogin('/profile/edit')}>
<Text style={dynamicStyles.editButtonText}></Text>
</TouchableOpacity>
</View>
</View>
);
// 数据统计部分
const StatsSection = () => (
<View style={[styles.statsContainer, { backgroundColor: colorTokens.card }]}>
<View style={styles.statItem}>
<Text style={dynamicStyles.statValue}>{formatHeight()}</Text>
<Text style={[styles.statLabel, { color: colorTokens.textMuted }]}></Text>
</View>
<View style={styles.statItem}>
<Text style={dynamicStyles.statValue}>{formatWeight()}</Text>
<Text style={[styles.statLabel, { color: colorTokens.textMuted }]}></Text>
</View>
<View style={styles.statItem}>
<Text style={dynamicStyles.statValue}>{formatAge()}</Text>
<Text style={[styles.statLabel, { color: colorTokens.textMuted }]}></Text>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.cardContainer}>
<View style={styles.statsContainer}>
<View style={styles.statItem}>
<Text style={styles.statValue}>{formatHeight()}</Text>
<Text style={styles.statLabel}></Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statValue}>{formatWeight()}</Text>
<Text style={styles.statLabel}></Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statValue}>{formatAge()}</Text>
<Text style={styles.statLabel}></Text>
</View>
</View>
</View>
</View>
);
// 菜单项组件
const MenuSection = ({ title, items }: { title: string; items: any[] }) => (
<View style={[styles.menuSection, { backgroundColor: colorTokens.card }]}>
<Text style={[styles.sectionTitle, { color: colorTokens.text }]}>{title}</Text>
{items.map((item, index) => (
<TouchableOpacity
key={index}
style={styles.menuItem}
onPress={item.type === 'switch' ? undefined : item.onPress}
disabled={item.type === 'switch'}
>
<View style={styles.menuItemLeft}>
<View style={[
styles.menuIcon,
{ backgroundColor: item.isDanger ? 'rgba(255,68,68,0.12)' : 'rgba(135,206,235,0.15)' }
]}>
<Ionicons
name={item.icon}
size={20}
color={item.isDanger ? colors.danger : '#4682B4'}
/>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>{title}</Text>
<View style={styles.cardContainer}>
{items.map((item, index) => (
<TouchableOpacity
key={index}
style={[styles.menuItem, index === items.length - 1 && { borderBottomWidth: 0 }]}
onPress={item.type === 'switch' ? undefined : item.onPress}
disabled={item.type === 'switch'}
>
<View style={styles.menuItemLeft}>
<View style={[
styles.iconContainer,
{ backgroundColor: item.isDanger ? 'rgba(255,68,68,0.1)' : 'rgba(147, 112, 219, 0.1)' }
]}>
<Ionicons
name={item.icon}
size={20}
color={item.isDanger ? '#FF4444' : '#9370DB'}
/>
</View>
<Text style={styles.menuItemText}>{item.title}</Text>
</View>
<Text style={[styles.menuItemText, { color: colorTokens.text }]}>{item.title}</Text>
</View>
{item.type === 'switch' ? (
<Switch
value={isLoggedIn ? notificationEnabled : false}
onValueChange={(value) => {
if (!isLoggedIn) {
pushIfAuthedElseLogin('/profile/notification-settings');
return;
}
setNotificationEnabled(value);
}}
trackColor={{ false: '#E5E5E5', true: colors.primary }}
thumbColor="#FFFFFF"
style={styles.switch}
/>
) : (
<Ionicons name="chevron-forward" size={20} color={colorTokens.icon} />
)}
</TouchableOpacity>
))}
{item.type === 'switch' ? (
<Switch
value={isLoggedIn ? notificationEnabled : false}
onValueChange={(value) => {
if (!isLoggedIn) {
pushIfAuthedElseLogin('/profile/notification-settings');
return;
}
setNotificationEnabled(value);
}}
trackColor={{ false: '#E5E5E5', true: '#9370DB' }}
thumbColor="#FFFFFF"
style={styles.switch}
/>
) : (
<Ionicons name="chevron-forward" size={20} color="#CCCCCC" />
)}
</TouchableOpacity>
))}
</View>
</View>
);
// 动态样式
const dynamicStyles = StyleSheet.create({
editButton: {
backgroundColor: colors.primary,
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 20,
},
editButtonText: {
color: colors.onPrimary,
fontSize: 14,
fontWeight: '600',
},
statValue: {
fontSize: 18,
fontWeight: 'bold',
color: colors.primary,
marginBottom: 4,
},
floatingButton: {
width: 56,
height: 56,
borderRadius: 28,
backgroundColor: colors.primary,
alignItems: 'center',
justifyContent: 'center',
shadowColor: colors.primary,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 8,
},
});
// 菜单项配置
const menuSections = [
{
@@ -197,25 +166,8 @@ export default function PersonalScreen() {
{
icon: 'flag-outline' as const,
title: '目标管理',
onPress: () => pushIfAuthedElseLogin('/goals'),
onPress: () => pushIfAuthedElseLogin('/profile/goals'),
},
{
icon: 'telescope-outline' as const,
title: '目标管理演示',
onPress: () => pushIfAuthedElseLogin('/goal-demo'),
},
// {
// icon: 'stats-chart-outline' as const,
// title: '训练进度',
// onPress: () => {
// // 训练进度页面暂未实现,先显示提示
// if (isLoggedIn) {
// Alert.alert('提示', '训练进度功能正在开发中');
// } else {
// pushIfAuthedElseLogin('/profile/training-progress');
// }
// },
// },
],
},
{
@@ -265,12 +217,6 @@ export default function PersonalScreen() {
return (
<View style={styles.container}>
<LinearGradient
colors={[colors.backgroundGradientStart, colors.backgroundGradientEnd]}
style={styles.gradientBackground}
start={{ x: 0, y: 0 }}
end={{ x: 0, y: 1 }}
/>
<StatusBar barStyle={'dark-content'} backgroundColor="transparent" translucent />
<SafeAreaView style={styles.safeArea}>
<ScrollView
@@ -278,13 +224,12 @@ export default function PersonalScreen() {
contentContainerStyle={{ paddingBottom: bottomPadding }}
showsVerticalScrollIndicator={false}
>
<UserInfoSection />
<UserHeader />
<StatsSection />
<ActivityHeatMap />
{menuSections.map((section, index) => (
<MenuSection key={index} title={section.title} items={section.items} />
))}
</ScrollView>
</SafeAreaView>
</View>
@@ -294,48 +239,53 @@ export default function PersonalScreen() {
const styles = StyleSheet.create({
container: {
flex: 1,
},
gradientBackground: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundColor: '#FAFAFA',
},
safeArea: {
flex: 1,
},
scrollView: {
flex: 1,
paddingHorizontal: 20,
paddingHorizontal: 16,
paddingTop: 16,
},
// 部分容器
sectionContainer: {
marginBottom: 20,
},
sectionTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#2C3E50',
marginBottom: 10,
paddingHorizontal: 4,
},
// 卡片容器
cardContainer: {
backgroundColor: '#FFFFFF',
borderRadius: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 4,
elevation: 2,
overflow: 'hidden',
},
// 用户信息区域
userInfoCard: {
borderRadius: 16,
marginBottom: 20,
shadowColor: 'rgba(135,206,235,0.3)',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.15,
shadowRadius: 8,
elevation: 3,
borderWidth: 1,
borderColor: 'rgba(135,206,235,0.1)',
},
userInfoContainer: {
flexDirection: 'row',
alignItems: 'center',
padding: 20,
padding: 16,
},
avatarContainer: {
marginRight: 15,
marginRight: 12,
},
avatar: {
width: 80,
height: 80,
borderRadius: 40,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
width: 60,
height: 60,
borderRadius: 30,
borderWidth: 2,
borderColor: '#9370DB',
},
userDetails: {
flex: 1,
@@ -343,86 +293,75 @@ const styles = StyleSheet.create({
userName: {
fontSize: 18,
fontWeight: 'bold',
color: '#2C3E50',
marginBottom: 4,
},
userRole: {
fontSize: 14,
color: '#9370DB',
fontWeight: '500',
},
editButton: {
backgroundColor: '#9370DB',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 16,
},
editButtonText: {
color: '#FFFFFF',
fontSize: 14,
fontWeight: '600',
},
// 数据统计
statsContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
borderRadius: 16,
padding: 20,
marginBottom: 20,
shadowColor: 'rgba(135,206,235,0.25)',
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.12,
shadowRadius: 6,
elevation: 3,
borderWidth: 1,
borderColor: 'rgba(135,206,235,0.08)',
padding: 16,
},
statItem: {
alignItems: 'center',
flex: 1,
},
statValue: {
fontSize: 18,
fontWeight: 'bold',
color: '#9370DB',
marginBottom: 4,
},
statLabel: {
fontSize: 12,
color: '#6C757D',
fontWeight: '500',
},
menuSection: {
marginBottom: 20,
padding: 16,
borderRadius: 16,
shadowColor: 'rgba(135,206,235,0.2)',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2,
borderWidth: 1,
borderColor: 'rgba(135,206,235,0.06)',
},
sectionTitle: {
fontSize: 20,
fontWeight: '800',
marginBottom: 12,
paddingHorizontal: 4,
},
// 菜单项
menuItem: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 16,
paddingVertical: 14,
paddingHorizontal: 16,
borderRadius: 12,
marginBottom: 8,
borderBottomWidth: 1,
borderBottomColor: '#F1F3F4',
},
menuItemLeft: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
},
menuIcon: {
width: 36,
height: 36,
borderRadius: 8,
iconContainer: {
width: 32,
height: 32,
borderRadius: 6,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
menuItemText: {
fontSize: 16,
flex: 1,
fontWeight: '600',
fontSize: 15,
color: '#2C3E50',
fontWeight: '500',
},
switch: {
transform: [{ scaleX: 0.8 }, { scaleY: 0.8 }],
},
// 浮动按钮
floatingButtonContainer: {
position: 'absolute',
bottom: 30,
left: 0,
right: 0,
alignItems: 'center',
pointerEvents: 'box-none',
},
});