feat: 更新 AI 教练聊天界面和个人信息页面
- 在 AI 教练聊天界面中添加训练记录分析功能,允许用户基于近期训练记录获取分析建议 - 更新 Redux 状态管理,集成每日步数和卡路里目标 - 在个人信息页面中优化用户头像显示,支持从库中选择头像 - 修改首页布局,添加可拖动的教练徽章,提升用户交互体验 - 更新样式以适应新功能的展示和交互
This commit is contained in:
@@ -2,6 +2,7 @@ import { Colors } from '@/constants/Colors';
|
||||
import { getTabBarBottomPadding } from '@/constants/TabBar';
|
||||
import { useAppSelector } from '@/hooks/redux';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
import { DEFAULT_MEMBER_NAME } from '@/store/userSlice';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
|
||||
@@ -9,7 +10,7 @@ import { useFocusEffect } from '@react-navigation/native';
|
||||
import type { Href } from 'expo-router';
|
||||
import { router } from 'expo-router';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Alert, SafeAreaView, ScrollView, StatusBar, StyleSheet, Switch, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { Alert, Image, SafeAreaView, ScrollView, StatusBar, StyleSheet, Switch, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
export default function PersonalScreen() {
|
||||
@@ -24,6 +25,7 @@ export default function PersonalScreen() {
|
||||
const colors = Colors[colorScheme ?? 'light'];
|
||||
const theme = (colorScheme ?? 'light') as 'light' | 'dark';
|
||||
const colorTokens = Colors[theme];
|
||||
const DEFAULT_AVATAR_URL = 'https://plates-1251306435.cos.ap-guangzhou.myqcloud.com/images/avatar/avatarGirl01.jpeg';
|
||||
|
||||
type UserProfile = {
|
||||
fullName?: string;
|
||||
@@ -117,25 +119,21 @@ export default function PersonalScreen() {
|
||||
};
|
||||
|
||||
|
||||
const displayName = (profile.fullName && profile.fullName.trim()) ? profile.fullName : DEFAULT_MEMBER_NAME;
|
||||
|
||||
const UserInfoSection = () => (
|
||||
<View style={styles.userInfoCard}>
|
||||
<View style={[styles.userInfoCard, { backgroundColor: colorTokens.card }]}>
|
||||
<View style={styles.userInfoContainer}>
|
||||
{/* 头像 */}
|
||||
<View style={styles.avatarContainer}>
|
||||
<View style={styles.avatar}>
|
||||
<View style={styles.avatarContent}>
|
||||
{/* 简单的头像图标,您可以替换为实际图片 */}
|
||||
<View style={styles.avatarIcon}>
|
||||
<View style={styles.avatarFace} />
|
||||
<View style={styles.avatarBody} />
|
||||
</View>
|
||||
</View>
|
||||
<View style={[styles.avatar, { backgroundColor: colorTokens.ornamentAccent }]}>
|
||||
<Image source={{ uri: profile.avatarUri || DEFAULT_AVATAR_URL }} style={{ width: '100%', height: '100%' }} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 用户信息 */}
|
||||
<View style={styles.userDetails}>
|
||||
<Text style={styles.userName}>{profile.fullName || '未设置姓名'}</Text>
|
||||
<Text style={[styles.userName, { color: colorTokens.text }]}>{displayName}</Text>
|
||||
</View>
|
||||
|
||||
{/* 编辑按钮 */}
|
||||
@@ -147,25 +145,25 @@ export default function PersonalScreen() {
|
||||
);
|
||||
|
||||
const StatsSection = () => (
|
||||
<View style={styles.statsContainer}>
|
||||
<View style={[styles.statsContainer, { backgroundColor: colorTokens.card }]}>
|
||||
<View style={styles.statItem}>
|
||||
<Text style={dynamicStyles.statValue}>{formatHeight()}</Text>
|
||||
<Text style={styles.statLabel}>身高</Text>
|
||||
<Text style={[styles.statLabel, { color: colorTokens.textMuted }]}>身高</Text>
|
||||
</View>
|
||||
<View style={styles.statItem}>
|
||||
<Text style={dynamicStyles.statValue}>{formatWeight()}</Text>
|
||||
<Text style={styles.statLabel}>体重</Text>
|
||||
<Text style={[styles.statLabel, { color: colorTokens.textMuted }]}>体重</Text>
|
||||
</View>
|
||||
<View style={styles.statItem}>
|
||||
<Text style={dynamicStyles.statValue}>{formatAge()}</Text>
|
||||
<Text style={styles.statLabel}>年龄</Text>
|
||||
<Text style={[styles.statLabel, { color: colorTokens.textMuted }]}>年龄</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
const MenuSection = ({ title, items }: { title: string; items: any[] }) => (
|
||||
<View style={styles.menuSection}>
|
||||
<Text style={styles.sectionTitle}>{title}</Text>
|
||||
<View style={[styles.menuSection, { backgroundColor: colorTokens.card }]}>
|
||||
<Text style={[styles.sectionTitle, { color: colorTokens.text }]}>{title}</Text>
|
||||
{items.map((item, index) => (
|
||||
<TouchableOpacity
|
||||
key={index}
|
||||
@@ -173,10 +171,10 @@ export default function PersonalScreen() {
|
||||
onPress={item.onPress}
|
||||
>
|
||||
<View style={styles.menuItemLeft}>
|
||||
<View style={[styles.menuIcon]}>
|
||||
<Ionicons name={item.icon} size={20} color={item.iconColor || colors.primary} />
|
||||
<View style={[styles.menuIcon, { backgroundColor: 'rgba(187,242,70,0.12)' }]}>
|
||||
<Ionicons name={item.icon} size={20} color={'#192126'} />
|
||||
</View>
|
||||
<Text style={styles.menuItemText}>{item.title}</Text>
|
||||
<Text style={[styles.menuItemText, { color: colorTokens.text }]}>{item.title}</Text>
|
||||
</View>
|
||||
{item.type === 'switch' ? (
|
||||
<Switch
|
||||
@@ -187,7 +185,7 @@ export default function PersonalScreen() {
|
||||
style={styles.switch}
|
||||
/>
|
||||
) : (
|
||||
<Ionicons name="chevron-forward" size={20} color="#C4C4C4" />
|
||||
<Ionicons name="chevron-forward" size={20} color={colorTokens.icon} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
@@ -290,7 +288,7 @@ export default function PersonalScreen() {
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: theme === 'light' ? colorTokens.pageBackgroundEmphasis : colorTokens.background }]}>
|
||||
<StatusBar barStyle="dark-content" backgroundColor="transparent" translucent />
|
||||
<StatusBar barStyle={theme === 'light' ? 'dark-content' : 'light-content'} backgroundColor="transparent" translucent />
|
||||
<SafeAreaView style={[styles.safeArea, { backgroundColor: theme === 'light' ? colorTokens.pageBackgroundEmphasis : colorTokens.background }]}>
|
||||
<ScrollView
|
||||
style={[styles.scrollView, { backgroundColor: theme === 'light' ? colorTokens.pageBackgroundEmphasis : colorTokens.background }]}
|
||||
@@ -333,6 +331,15 @@ const styles = StyleSheet.create({
|
||||
userInfoCard: {
|
||||
borderRadius: 16,
|
||||
marginBottom: 20,
|
||||
backgroundColor: '#FFFFFF',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.08,
|
||||
shadowRadius: 6,
|
||||
elevation: 3,
|
||||
},
|
||||
userInfoContainer: {
|
||||
flexDirection: 'row',
|
||||
@@ -380,7 +387,7 @@ const styles = StyleSheet.create({
|
||||
userName: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: '#000',
|
||||
color: '#192126',
|
||||
marginBottom: 4,
|
||||
},
|
||||
|
||||
@@ -408,18 +415,19 @@ const styles = StyleSheet.create({
|
||||
|
||||
statLabel: {
|
||||
fontSize: 12,
|
||||
color: '#888',
|
||||
color: '#687076',
|
||||
},
|
||||
// 菜单区域
|
||||
menuSection: {
|
||||
marginBottom: 20,
|
||||
backgroundColor: '#FFFFFF',
|
||||
padding: 16,
|
||||
borderRadius: 16,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: '#000',
|
||||
fontSize: 20,
|
||||
fontWeight: '800',
|
||||
color: '#192126',
|
||||
marginBottom: 12,
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
@@ -447,7 +455,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
menuItemText: {
|
||||
fontSize: 16,
|
||||
color: '#000',
|
||||
color: '#192126',
|
||||
flex: 1,
|
||||
},
|
||||
switch: {
|
||||
|
||||
Reference in New Issue
Block a user