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

@@ -57,7 +57,7 @@ export default function TabLayout() {
};
const { icon, title } = getIconAndTitle();
const activeContentColor = colorTokens.onPrimary;
const activeContentColor = colorTokens.tabIconSelected; // 使用专门为Tab定义的选中颜色
const inactiveContentColor = colorTokens.tabIconDefault;
return (

View File

@@ -27,6 +27,7 @@ import { Colors } from '@/constants/Colors';
import { getTabBarBottomPadding } from '@/constants/TabBar';
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
import { useAuthGuard } from '@/hooks/useAuthGuard';
import { useColorScheme } from '@/hooks/useColorScheme';
import { useCosUpload } from '@/hooks/useCosUpload';
import { deleteConversation, getConversationDetail, listConversations, type AiConversationListItem } from '@/services/aiCoach';
import { loadAiCoachSessionCache, saveAiCoachSessionCache } from '@/services/aiCoachSession';
@@ -124,7 +125,8 @@ export default function CoachScreen() {
const { isLoggedIn, pushIfAuthedElseLogin } = useAuthGuard();
// 为了让页面更贴近品牌主题与更亮的观感,这里使用亮色系配色
const theme = Colors.light;
const colorScheme = useColorScheme();
const theme = Colors[colorScheme ?? 'light'];
const botName = (params?.name || 'Seal').toString();
const [input, setInput] = useState('');
const [isSending, setIsSending] = useState(false);
@@ -282,6 +284,16 @@ export default function CoachScreen() {
{ key: 'weight', label: '#记体重', action: () => insertWeightInputCard() },
{ key: 'diet', label: '#记饮食', action: () => insertDietInputCard() },
{ key: 'dietPlan', label: '#饮食方案', action: () => insertDietPlanCard() },
{
key: 'mood',
label: '#记心情',
action: () => {
if (Platform.OS === 'ios') {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
}
router.push('/mood/calendar');
}
},
], [router, planDraft, checkin]);
const scrollToEnd = useCallback(() => {
@@ -1333,7 +1345,7 @@ export default function CoachScreen() {
{/* 标题部分 */}
<View style={styles.dietPlanHeader}>
<View style={styles.dietPlanTitleContainer}>
<Ionicons name="restaurant-outline" size={20} color={Colors.light.accentGreenDark} />
<Ionicons name="restaurant-outline" size={20} color={theme.success} />
<Text style={styles.dietPlanTitle}></Text>
</View>
<Text style={styles.dietPlanSubtitle}>MY DIET PLAN</Text>
@@ -1522,7 +1534,7 @@ export default function CoachScreen() {
</View>
)}
{isSelected && isPending && (
<ActivityIndicator size="small" color={Colors.light.accentGreenDark} />
<ActivityIndicator size="small" color={theme.success} />
)}
{isSelected && !isPending && (
<View style={styles.selectedBadge}>
@@ -1829,7 +1841,7 @@ export default function CoachScreen() {
<View style={styles.screen}>
{/* 背景渐变 */}
<LinearGradient
colors={['#F0F9FF', '#E0F2FE']}
colors={['#fafaff', '#f4f3ff']} // 使用紫色主题的浅色渐变
style={styles.gradientBackground}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
@@ -1866,7 +1878,7 @@ export default function CoachScreen() {
source={require('@/assets/images/icons/iconFlash.png')}
style={styles.usageIcon}
/>
<Text style={[styles.usageText, { color: theme.text }]}>
<Text style={styles.usageText}>
{userProfile?.isVip ? '不限' : `${userProfile?.freeUsageCount || 0}/${userProfile?.maxUsageCount || 0}`}
</Text>
</TouchableOpacity>
@@ -1876,16 +1888,16 @@ export default function CoachScreen() {
<TouchableOpacity
accessibilityRole="button"
onPress={startNewConversation}
style={[styles.headerActionButton, { backgroundColor: `${Colors.light.accentGreen}33` }]} // 20% opacity
style={[styles.headerActionButton, { backgroundColor: `${theme.primary}20` }]} // 20% opacity
>
<Ionicons name="add-outline" size={18} color={theme.onPrimary} />
<Ionicons name="add-outline" size={18} color={theme.primary} />
</TouchableOpacity>
<TouchableOpacity
accessibilityRole="button"
onPress={openHistory}
style={[styles.headerActionButton, { backgroundColor: `${Colors.light.accentGreen}33` }]} // 20% opacity
style={[styles.headerActionButton, { backgroundColor: `${theme.primary}20` }]} // 20% opacity
>
<Ionicons name="time-outline" size={18} color={theme.onPrimary} />
<Ionicons name="time-outline" size={18} color={theme.primary} />
</TouchableOpacity>
</View>
</View>
@@ -1948,8 +1960,18 @@ export default function CoachScreen() {
contentContainerStyle={{ paddingHorizontal: 6, gap: 8 }}
>
{chips.map((c) => (
<TouchableOpacity key={c.key} style={[styles.chip, { borderColor: `${Colors.light.accentGreen}59`, backgroundColor: `${Colors.light.accentGreen}1F` }]} onPress={c.action}>
<Text style={[styles.chipText, { color: '#192126' }]}>{c.label}</Text>
<TouchableOpacity
key={c.key}
style={[
styles.chip,
{
borderColor: c.key === 'mood' ? `${theme.success}40` : `${theme.primary}40`,
backgroundColor: c.key === 'mood' ? `${theme.success}15` : `${theme.primary}15`
}
]}
onPress={c.action}
>
<Text style={[styles.chipText, { color: c.key === 'mood' ? theme.success : theme.text }]}>{c.label}</Text>
</TouchableOpacity>
))}
</ScrollView>
@@ -1990,18 +2012,18 @@ export default function CoachScreen() {
</ScrollView>
)}
<View style={[styles.inputRow, { borderColor: `${Colors.light.accentGreen}59`, backgroundColor: `${Colors.light.accentGreen}14` }]}>
<View style={[styles.inputRow, { borderColor: `${theme.primary}30`, backgroundColor: `${theme.primary}08` }]}>
<TouchableOpacity
accessibilityRole="button"
onPress={pickImages}
style={[styles.mediaBtn, { backgroundColor: `${Colors.light.accentGreen}28` }]}
style={[styles.mediaBtn, { backgroundColor: `${theme.primary}20` }]}
>
<Ionicons name="image-outline" size={18} color={'#192126'} />
<Ionicons name="image-outline" size={18} color={theme.text} />
</TouchableOpacity>
<TextInput
placeholder="问我任何健康相关的问题,如营养、健身、生活管理等..."
placeholderTextColor={theme.textMuted}
style={[styles.input, { color: '#192126' }]}
style={[styles.input, { color: theme.text }]}
value={input}
onChangeText={setInput}
multiline
@@ -2021,7 +2043,7 @@ export default function CoachScreen() {
style={[
styles.sendBtn,
{
backgroundColor: (isSending || isStreaming) ? '#FF4444' : theme.primary,
backgroundColor: (isSending || isStreaming) ? theme.danger : theme.primary,
opacity: ((input.trim() || selectedImages.length > 0) || (isSending || isStreaming)) ? 1 : 0.5
}
]}
@@ -2144,8 +2166,8 @@ const styles = StyleSheet.create({
width: 60,
height: 60,
borderRadius: 30,
backgroundColor: '#0EA5E9',
opacity: 0.1,
backgroundColor: '#7a5af8', // 紫色主题
opacity: 0.08,
},
decorativeCircle2: {
position: 'absolute',
@@ -2154,8 +2176,8 @@ const styles = StyleSheet.create({
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#0EA5E9',
opacity: 0.05,
backgroundColor: '#7a5af8', // 紫色主题
opacity: 0.04,
},
headerLeft: {
flexDirection: 'row',
@@ -2177,6 +2199,11 @@ const styles = StyleSheet.create({
borderRadius: 16,
alignItems: 'center',
justifyContent: 'center',
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2,
},
historyButton: {
width: 32,
@@ -2237,7 +2264,7 @@ const styles = StyleSheet.create({
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: `${Colors.light.accentGreen}99` // 60% opacity
backgroundColor: '#7a5af899' // 紫色主题 60% opacity
},
dietOptionsContainer: {
gap: 8,
@@ -2249,13 +2276,13 @@ const styles = StyleSheet.create({
borderRadius: 12,
backgroundColor: 'rgba(255,255,255,0.9)',
borderWidth: 1,
borderColor: `${Colors.light.accentGreen}4D`, // 30% opacity
borderColor: '#7a5af84d', // 紫色主题 30% opacity
},
dietOptionIconContainer: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: `${Colors.light.accentGreen}33`, // 20% opacity
backgroundColor: '#7a5af833', // 紫色主题 20% opacity
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
@@ -2304,7 +2331,7 @@ const styles = StyleSheet.create({
borderRadius: 10,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: `${Colors.light.accentGreen}99`, // 60% opacity
backgroundColor: '#7a5af899', // 紫色主题 60% opacity
alignSelf: 'flex-end',
},
// markdown 基础样式承载容器的字体尺寸保持与气泡一致
@@ -2349,7 +2376,7 @@ const styles = StyleSheet.create({
borderRadius: 12,
overflow: 'hidden',
position: 'relative',
backgroundColor: 'rgba(0,0,0,0.06)'
backgroundColor: 'rgba(122,90,248,0.08)' // 使用紫色主题的浅色背景
},
imageThumb: {
width: '100%',
@@ -2404,7 +2431,7 @@ const styles = StyleSheet.create({
padding: 8,
borderWidth: 1,
borderRadius: 16,
backgroundColor: 'rgba(0,0,0,0.04)'
backgroundColor: 'rgba(122,90,248,0.04)' // 使用紫色主题的极浅色背景
},
mediaBtn: {
width: 40,
@@ -2617,17 +2644,17 @@ const styles = StyleSheet.create({
choiceButton: {
backgroundColor: 'rgba(255,255,255,0.9)',
borderWidth: 1,
borderColor: `${Colors.light.accentGreen}4D`, // 30% opacity
borderColor: '#7a5af84d', // 紫色主题 30% opacity
borderRadius: 12,
padding: 12,
},
choiceButtonRecommended: {
borderColor: `${Colors.light.accentGreen}99`, // 60% opacity
backgroundColor: `${Colors.light.accentGreen}1A`, // 10% opacity
borderColor: '#7a5af899', // 紫色主题 60% opacity
backgroundColor: '#7a5af81a', // 紫色主题 10% opacity
},
choiceButtonSelected: {
borderColor: Colors.light.accentGreenDark,
backgroundColor: `${Colors.light.accentGreen}33`, // 20% opacity
borderColor: '#19b36e', // success[500]
backgroundColor: '#19b36e33', // 20% opacity
borderWidth: 2,
},
choiceButtonDisabled: {
@@ -2647,10 +2674,10 @@ const styles = StyleSheet.create({
flex: 1,
},
choiceLabelRecommended: {
color: Colors.light.accentGreenDark,
color: '#19b36e', // success[500]
},
choiceLabelSelected: {
color: Colors.light.accentGreenDark,
color: '#19b36e', // success[500]
fontWeight: '700',
},
choiceLabelDisabled: {
@@ -2662,7 +2689,7 @@ const styles = StyleSheet.create({
gap: 8,
},
recommendedBadge: {
backgroundColor: `${Colors.light.accentGreen}CC`, // 80% opacity
backgroundColor: '#7a5af8cc', // 紫色主题 80% opacity
borderRadius: 6,
paddingHorizontal: 8,
paddingVertical: 2,
@@ -2670,10 +2697,10 @@ const styles = StyleSheet.create({
recommendedText: {
fontSize: 12,
fontWeight: '700',
color: Colors.light.accentGreenDark,
color: '#19b36e', // success[500]
},
selectedBadge: {
backgroundColor: Colors.light.accentGreenDark,
backgroundColor: '#19b36e', // success[500]
borderRadius: 6,
paddingHorizontal: 8,
paddingVertical: 2,
@@ -2714,7 +2741,7 @@ const styles = StyleSheet.create({
padding: 16,
gap: 16,
borderWidth: 1,
borderColor: `${Colors.light.accentGreen}33`, // 20% opacity
borderColor: '#7a5af833', // 紫色主题 20% opacity
},
dietPlanHeader: {
gap: 4,
@@ -2834,7 +2861,7 @@ const styles = StyleSheet.create({
caloriesValue: {
fontSize: 18,
fontWeight: '800',
color: Colors.light.accentGreenDark,
color: '#19b36e', // success[500]
},
nutritionGrid: {
flexDirection: 'row',
@@ -2871,7 +2898,7 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
gap: 8,
backgroundColor: Colors.light.accentGreenDark,
backgroundColor: '#19b36e', // success[500]
paddingVertical: 12,
paddingHorizontal: 16,
borderRadius: 12,
@@ -2889,7 +2916,7 @@ const styles = StyleSheet.create({
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
backgroundColor: 'rgba(0,0,0,0.06)',
backgroundColor: 'rgba(122,90,248,0.08)', // 紫色主题浅色背景
},
usageIcon: {
width: 16,
@@ -2898,7 +2925,7 @@ const styles = StyleSheet.create({
usageText: {
fontSize: 12,
fontWeight: '600',
color: '#687076',
color: '#7a5af8', // 紫色主题文字颜色
},
});

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',
},
});

View File

@@ -22,7 +22,6 @@ import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { useFocusEffect } from '@react-navigation/native';
import dayjs from 'dayjs';
import { LinearGradient } from 'expo-linear-gradient';
import { router } from 'expo-router';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
Animated,
@@ -120,6 +119,9 @@ export default function ExploreScreen() {
standHours: 0,
standHoursGoal: 12
});
// 血氧饱和度和心率数据
const [oxygenSaturation, setOxygenSaturation] = useState<number | null>(null);
const [heartRate, setHeartRate] = useState<number | null>(null);
const [isLoading, setIsLoading] = useState(false);
// 用于触发动画重置的 token当日期或数据变化时更新
@@ -225,6 +227,13 @@ export default function ExploreScreen() {
// 更新HRV数据时间
setHrvUpdateTime(new Date());
// 设置血氧饱和度和心率数据
setOxygenSaturation(data.oxygenSaturation ?? null);
setHeartRate(data.heartRate ?? null);
console.log('血氧饱和度数据:', data.oxygenSaturation);
console.log('心率数据:', data.heartRate);
setAnimToken((t) => t + 1);
} else {
console.log('忽略过期健康数据请求结果key=', requestKey, '最新key=', latestRequestKeyRef.current);
@@ -233,6 +242,9 @@ export default function ExploreScreen() {
} catch (error) {
console.error('HealthKit流程出现异常:', error);
// 重置血氧饱和度和心率数据
setOxygenSaturation(null);
setHeartRate(null);
} finally {
setIsLoading(false);
}
@@ -389,7 +401,7 @@ export default function ExploreScreen() {
<FloatingCard style={[styles.masonryCard, styles.moodCard]} delay={1500}>
<MoodCard
moodCheckin={currentMoodCheckin}
onPress={() => router.push('/mood/calendar')}
onPress={() => pushIfAuthedElseLogin('/mood/calendar')}
isLoading={isMoodLoading}
/>
</FloatingCard>
@@ -436,6 +448,7 @@ export default function ExploreScreen() {
<OxygenSaturationCard
resetToken={animToken}
style={styles.basalMetabolismCardOverride}
oxygenSaturation={oxygenSaturation}
/>
</FloatingCard>
@@ -444,6 +457,7 @@ export default function ExploreScreen() {
<HeartRateCard
resetToken={animToken}
style={styles.basalMetabolismCardOverride}
heartRate={heartRate}
/>
</FloatingCard>

View File

@@ -1,202 +0,0 @@
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import { useRouter } from 'expo-router';
import React from 'react';
import { SafeAreaView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
export default function GoalDemoScreen() {
const router = useRouter();
const theme = useColorScheme() ?? 'light';
const colorTokens = Colors[theme];
return (
<SafeAreaView style={styles.container}>
<LinearGradient
colors={[
colorTokens.backgroundGradientStart,
colorTokens.backgroundGradientEnd,
]}
style={styles.backgroundGradient}
/>
<View style={styles.content}>
<View style={styles.header}>
<TouchableOpacity
style={[styles.backButton, { backgroundColor: colorTokens.card }]}
onPress={() => router.back()}
>
<Ionicons name="arrow-back" size={24} color={colorTokens.text} />
</TouchableOpacity>
<Text style={[styles.title, { color: colorTokens.text }]}>
</Text>
</View>
<View style={styles.demoContainer}>
<View style={[styles.demoCard, { backgroundColor: colorTokens.card }]}>
<View style={styles.iconContainer}>
<Ionicons name="flag" size={48} color={colorTokens.primary} />
</View>
<Text style={[styles.demoTitle, { color: colorTokens.text }]}>
</Text>
<Text style={[styles.demoDescription, { color: colorTokens.textSecondary }]}>
</Text>
<View style={styles.featureList}>
<View style={styles.featureItem}>
<Ionicons name="checkmark-circle" size={16} color={colorTokens.primary} />
<Text style={[styles.featureText, { color: colorTokens.textSecondary }]}>
1.5
</Text>
</View>
<View style={styles.featureItem}>
<Ionicons name="checkmark-circle" size={16} color={colorTokens.primary} />
<Text style={[styles.featureText, { color: colorTokens.textSecondary }]}>
//
</Text>
</View>
<View style={styles.featureItem}>
<Ionicons name="checkmark-circle" size={16} color={colorTokens.primary} />
<Text style={[styles.featureText, { color: colorTokens.textSecondary }]}>
</Text>
</View>
<View style={styles.featureItem}>
<Ionicons name="checkmark-circle" size={16} color={colorTokens.primary} />
<Text style={[styles.featureText, { color: colorTokens.textSecondary }]}>
</Text>
</View>
</View>
<TouchableOpacity
style={[styles.enterButton, { backgroundColor: colorTokens.primary }]}
onPress={() => router.push('/goals')}
>
<Text style={[styles.enterButtonText, { color: colorTokens.onPrimary }]}>
</Text>
<Ionicons name="arrow-forward" size={20} color={colorTokens.onPrimary} />
</TouchableOpacity>
</View>
</View>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
backgroundGradient: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
},
content: {
flex: 1,
padding: 20,
},
header: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 30,
marginTop: 20,
},
backButton: {
width: 40,
height: 40,
borderRadius: 20,
justifyContent: 'center',
alignItems: 'center',
marginRight: 16,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
title: {
fontSize: 24,
fontWeight: '800',
},
demoContainer: {
flex: 1,
justifyContent: 'center',
},
demoCard: {
borderRadius: 24,
padding: 32,
shadowColor: '#000',
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.15,
shadowRadius: 20,
elevation: 10,
alignItems: 'center',
},
iconContainer: {
width: 80,
height: 80,
borderRadius: 40,
backgroundColor: '#E6F3FF',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 24,
},
demoTitle: {
fontSize: 24,
fontWeight: '700',
marginBottom: 16,
textAlign: 'center',
},
demoDescription: {
fontSize: 16,
lineHeight: 24,
textAlign: 'center',
marginBottom: 24,
},
featureList: {
alignSelf: 'stretch',
marginBottom: 32,
},
featureItem: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
featureText: {
fontSize: 14,
marginLeft: 8,
flex: 1,
},
enterButton: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 32,
paddingVertical: 16,
borderRadius: 28,
shadowColor: '#87CEEB',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 6,
},
enterButtonText: {
fontSize: 16,
fontWeight: '700',
marginRight: 8,
},
});

View File

@@ -195,11 +195,15 @@ export default function MoodCalendarScreen() {
return (
<View style={styles.container}>
<LinearGradient
colors={backgroundGradientColors}
colors={['#fafaff', '#f4f3ff']} // 使用紫色主题的浅色渐变
style={styles.gradientBackground}
start={{ x: 0, y: 0 }}
end={{ x: 0, y: 1 }}
/>
{/* 装饰性圆圈 */}
<View style={styles.decorativeCircle1} />
<View style={styles.decorativeCircle2} />
<SafeAreaView style={styles.safeArea}>
<HeaderBar
title="心情日历"
@@ -345,6 +349,26 @@ const styles = StyleSheet.create({
top: 0,
bottom: 0,
},
decorativeCircle1: {
position: 'absolute',
top: 40,
right: 20,
width: 60,
height: 60,
borderRadius: 30,
backgroundColor: '#7a5af8',
opacity: 0.08,
},
decorativeCircle2: {
position: 'absolute',
bottom: -15,
left: -15,
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#7a5af8',
opacity: 0.04,
},
safeArea: {
flex: 1,
},
@@ -353,45 +377,56 @@ const styles = StyleSheet.create({
flex: 1,
},
calendar: {
backgroundColor: '#fff',
backgroundColor: 'rgba(255,255,255,0.95)',
margin: 16,
borderRadius: 16,
padding: 16,
borderRadius: 20,
padding: 20,
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.1,
shadowRadius: 12,
elevation: 6,
},
monthNavigation: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 20,
marginBottom: 24,
},
navButton: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#f8f9fa',
width: 44,
height: 44,
borderRadius: 22,
backgroundColor: 'rgba(122,90,248,0.1)',
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2,
},
navButtonText: {
fontSize: 20,
color: '#333',
fontWeight: '600',
fontSize: 24,
color: '#7a5af8',
fontWeight: '700',
},
monthTitle: {
fontSize: 18,
fontWeight: '700',
fontSize: 20,
fontWeight: '800',
color: '#192126',
},
weekHeader: {
flexDirection: 'row',
justifyContent: 'space-around',
marginBottom: 16,
marginBottom: 20,
},
weekDay: {
fontSize: 14,
color: '#666',
fontSize: 13,
color: '#5d6676',
textAlign: 'center',
width: (width - 96) / 7,
fontWeight: '600',
},
weekRow: {
flexDirection: 'row',
@@ -403,19 +438,25 @@ const styles = StyleSheet.create({
alignItems: 'center',
},
dayButton: {
width: 40,
height: 40,
borderRadius: 20,
width: 44,
height: 44,
borderRadius: 22,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 8,
backgroundColor: 'transparent',
},
dayButtonSelected: {
backgroundColor: Colors.light.accentGreen,
backgroundColor: '#FFFFFF',
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 6,
elevation: 4,
},
dayButtonToday: {
borderWidth: 2,
borderColor: Colors.light.accentGreen,
borderColor: '#7a5af8',
},
dayContent: {
position: 'relative',
@@ -426,142 +467,174 @@ const styles = StyleSheet.create({
},
dayNumber: {
fontSize: 14,
color: '#999',
fontWeight: '500',
color: '#777f8c',
fontWeight: '600',
position: 'absolute',
top: 2,
zIndex: 1,
},
dayNumberSelected: {
color: '#FFFFFF',
fontWeight: '600',
color: '#192126',
fontWeight: '700',
},
dayNumberToday: {
color: Colors.light.accentGreen,
fontWeight: '600',
color: '#7a5af8',
fontWeight: '700',
},
dayNumberDisabled: {
color: '#ccc',
color: '#c0c4ca',
},
moodIconContainer: {
position: 'absolute',
bottom: 2,
width: 20,
height: 20,
borderRadius: 10,
width: 22,
height: 22,
borderRadius: 11,
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 1,
},
moodIcon: {
width: 16,
height: 16,
borderRadius: 8,
backgroundColor: 'rgba(255,255,255,0.9)',
width: 18,
height: 18,
borderRadius: 9,
backgroundColor: 'rgba(255,255,255,0.95)',
justifyContent: 'center',
alignItems: 'center',
},
moodEmoji: {
fontSize: 12,
fontSize: 11,
},
defaultMoodIcon: {
position: 'absolute',
bottom: 2,
width: 20,
height: 20,
borderRadius: 10,
borderWidth: 1,
borderColor: '#ddd',
width: 22,
height: 22,
borderRadius: 11,
borderWidth: 1.5,
borderColor: 'rgba(122,90,248,0.3)',
borderStyle: 'dashed',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(122,90,248,0.05)',
},
defaultMoodEmoji: {
fontSize: 10,
opacity: 0.3,
opacity: 0.4,
color: '#7a5af8',
},
selectedDateSection: {
backgroundColor: '#fff',
backgroundColor: 'rgba(255,255,255,0.95)',
margin: 16,
marginTop: 0,
borderRadius: 16,
padding: 16,
borderRadius: 20,
padding: 20,
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.1,
shadowRadius: 12,
elevation: 6,
},
selectedDateHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
marginBottom: 20,
},
selectedDateTitle: {
fontSize: 20,
fontWeight: '700',
fontSize: 22,
fontWeight: '800',
color: '#192126',
},
addMoodButton: {
paddingHorizontal: 16,
height: 32,
borderRadius: 16,
backgroundColor: Colors.light.accentGreen,
paddingHorizontal: 20,
height: 36,
borderRadius: 18,
backgroundColor: '#7a5af8',
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 4,
elevation: 3,
},
addMoodButtonText: {
color: '#fff',
fontSize: 14,
fontWeight: '600',
fontWeight: '700',
},
moodRecord: {
flexDirection: 'row',
alignItems: 'flex-start',
paddingVertical: 12,
paddingVertical: 16,
backgroundColor: 'rgba(122,90,248,0.05)',
borderRadius: 16,
paddingHorizontal: 16,
},
recordIcon: {
width: 48,
height: 48,
borderRadius: 24,
backgroundColor: Colors.light.accentGreen,
width: 52,
height: 52,
borderRadius: 26,
backgroundColor: '#7a5af8',
justifyContent: 'center',
alignItems: 'center',
marginRight: 12,
marginRight: 16,
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 4,
elevation: 2,
},
recordContent: {
flex: 1,
},
recordMood: {
fontSize: 16,
color: '#333',
fontWeight: '500',
fontSize: 18,
color: '#192126',
fontWeight: '700',
marginBottom: 4,
},
recordIntensity: {
fontSize: 14,
color: '#666',
color: '#5d6676',
marginTop: 2,
fontWeight: '500',
},
recordDescription: {
fontSize: 14,
color: '#666',
marginTop: 4,
color: '#5d6676',
marginTop: 6,
fontStyle: 'italic',
lineHeight: 20,
},
spacer: {
flex: 1,
},
recordTime: {
fontSize: 14,
color: '#999',
color: '#777f8c',
fontWeight: '500',
},
emptyRecord: {
alignItems: 'center',
paddingVertical: 20,
paddingVertical: 32,
},
emptyRecordText: {
fontSize: 16,
color: '#666',
color: '#5d6676',
marginBottom: 8,
fontWeight: '600',
},
emptyRecordSubtext: {
fontSize: 12,
color: '#999',
fontSize: 13,
color: '#777f8c',
textAlign: 'center',
lineHeight: 18,
},
});

View File

@@ -4,25 +4,25 @@ import { useAppDispatch, useAppSelector } from '@/hooks/redux';
import { useColorScheme } from '@/hooks/useColorScheme';
import { getMoodOptions, MoodType } from '@/services/moodCheckins';
import {
createMoodRecord,
deleteMoodRecord,
fetchDailyMoodCheckins,
selectMoodRecordsByDate,
updateMoodRecord
createMoodRecord,
deleteMoodRecord,
fetchDailyMoodCheckins,
selectMoodRecordsByDate,
updateMoodRecord
} from '@/store/moodSlice';
import dayjs from 'dayjs';
import { LinearGradient } from 'expo-linear-gradient';
import { router, useLocalSearchParams } from 'expo-router';
import React, { useEffect, useState } from 'react';
import {
Alert,
SafeAreaView,
ScrollView,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
Alert,
SafeAreaView,
ScrollView,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
export default function MoodEditScreen() {
@@ -163,11 +163,15 @@ export default function MoodEditScreen() {
return (
<View style={styles.container}>
<LinearGradient
colors={backgroundGradientColors}
colors={['#fafaff', '#f4f3ff']} // 使用紫色主题的浅色渐变
style={styles.gradientBackground}
start={{ x: 0, y: 0 }}
end={{ x: 0, y: 1 }}
/>
{/* 装饰性圆圈 */}
<View style={styles.decorativeCircle1} />
<View style={styles.decorativeCircle2} />
<SafeAreaView style={styles.safeArea}>
<HeaderBar
title={existingMood ? '编辑心情' : '记录心情'}
@@ -220,6 +224,7 @@ export default function MoodEditScreen() {
<TextInput
style={styles.descriptionInput}
placeholder="描述一下你的心情..."
placeholderTextColor="#777f8c"
value={description}
onChangeText={setDescription}
multiline
@@ -270,6 +275,26 @@ const styles = StyleSheet.create({
top: 0,
bottom: 0,
},
decorativeCircle1: {
position: 'absolute',
top: 40,
right: 20,
width: 60,
height: 60,
borderRadius: 30,
backgroundColor: '#7a5af8',
opacity: 0.08,
},
decorativeCircle2: {
position: 'absolute',
bottom: -15,
left: -15,
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#7a5af8',
opacity: 0.04,
},
safeArea: {
flex: 1,
},
@@ -278,29 +303,39 @@ const styles = StyleSheet.create({
flex: 1,
},
dateSection: {
backgroundColor: '#fff',
backgroundColor: 'rgba(255,255,255,0.95)',
margin: 16,
borderRadius: 16,
padding: 16,
borderRadius: 20,
padding: 20,
alignItems: 'center',
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.1,
shadowRadius: 12,
elevation: 6,
},
dateTitle: {
fontSize: 24,
fontWeight: '700',
fontSize: 26,
fontWeight: '800',
color: '#192126',
},
moodSection: {
backgroundColor: '#fff',
backgroundColor: 'rgba(255,255,255,0.95)',
margin: 16,
marginTop: 0,
borderRadius: 16,
padding: 16,
borderRadius: 20,
padding: 20,
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.1,
shadowRadius: 12,
elevation: 6,
},
sectionTitle: {
fontSize: 18,
fontWeight: '600',
color: '#333',
marginBottom: 16,
fontSize: 20,
fontWeight: '700',
color: '#192126',
marginBottom: 20,
},
moodOptions: {
flexDirection: 'row',
@@ -310,114 +345,163 @@ const styles = StyleSheet.create({
moodOption: {
width: '30%',
alignItems: 'center',
paddingVertical: 16,
paddingVertical: 20,
marginBottom: 16,
borderRadius: 12,
backgroundColor: '#f8f8f8',
borderRadius: 16,
backgroundColor: 'rgba(122,90,248,0.05)',
borderWidth: 1,
borderColor: 'rgba(122,90,248,0.1)',
},
selectedMoodOption: {
backgroundColor: '#e8f5e8',
backgroundColor: 'rgba(122,90,248,0.15)',
borderWidth: 2,
borderColor: Colors.light.accentGreen,
borderColor: '#7a5af8',
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 4,
elevation: 2,
},
moodEmoji: {
fontSize: 24,
marginBottom: 8,
fontSize: 28,
marginBottom: 10,
},
moodLabel: {
fontSize: 14,
color: '#333',
color: '#192126',
fontWeight: '600',
},
intensitySection: {
backgroundColor: '#fff',
backgroundColor: 'rgba(255,255,255,0.95)',
margin: 16,
marginTop: 0,
borderRadius: 16,
padding: 16,
borderRadius: 20,
padding: 20,
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.1,
shadowRadius: 12,
elevation: 6,
},
intensityContainer: {
alignItems: 'center',
},
intensityLabel: {
fontSize: 16,
fontWeight: '600',
color: '#333',
marginBottom: 12,
fontSize: 18,
fontWeight: '700',
color: '#192126',
marginBottom: 16,
},
intensitySlider: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
marginBottom: 8,
marginBottom: 12,
paddingHorizontal: 10,
},
intensityDot: {
width: 20,
height: 20,
borderRadius: 10,
backgroundColor: '#ddd',
width: 24,
height: 24,
borderRadius: 12,
backgroundColor: 'rgba(122,90,248,0.1)',
borderWidth: 2,
borderColor: 'rgba(122,90,248,0.2)',
},
intensityDotActive: {
backgroundColor: Colors.light.accentGreen,
backgroundColor: '#7a5af8',
borderColor: '#7a5af8',
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 4,
elevation: 2,
},
intensityLabels: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
paddingHorizontal: 10,
},
intensityLabelText: {
fontSize: 12,
color: '#666',
fontSize: 13,
color: '#5d6676',
fontWeight: '500',
},
descriptionSection: {
backgroundColor: '#fff',
backgroundColor: 'rgba(255,255,255,0.95)',
margin: 16,
marginTop: 0,
borderRadius: 16,
padding: 16,
borderRadius: 20,
padding: 20,
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.1,
shadowRadius: 12,
elevation: 6,
},
descriptionInput: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
padding: 12,
borderWidth: 1.5,
borderColor: 'rgba(122,90,248,0.2)',
borderRadius: 12,
padding: 16,
fontSize: 16,
minHeight: 80,
minHeight: 100,
textAlignVertical: 'top',
backgroundColor: 'rgba(122,90,248,0.02)',
color: '#192126',
},
characterCount: {
fontSize: 12,
color: '#999',
color: '#777f8c',
textAlign: 'right',
marginTop: 4,
marginTop: 8,
fontWeight: '500',
},
footer: {
padding: 16,
backgroundColor: '#fff',
padding: 20,
backgroundColor: 'rgba(255,255,255,0.95)',
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: -4 },
shadowOpacity: 0.1,
shadowRadius: 12,
elevation: 6,
},
saveButton: {
backgroundColor: Colors.light.accentGreen,
borderRadius: 12,
paddingVertical: 16,
backgroundColor: '#7a5af8',
borderRadius: 16,
paddingVertical: 18,
alignItems: 'center',
marginTop: 8,
marginTop: 12,
shadowColor: '#7a5af8',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 4,
elevation: 3,
},
deleteButton: {
backgroundColor: '#F44336',
borderRadius: 12,
paddingVertical: 16,
backgroundColor: '#f95555',
borderRadius: 16,
paddingVertical: 18,
alignItems: 'center',
shadowColor: '#f95555',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 4,
elevation: 3,
},
disabledButton: {
backgroundColor: '#ccc',
backgroundColor: '#c0c4ca',
shadowOpacity: 0,
elevation: 0,
},
saveButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
fontWeight: '700',
},
deleteButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
fontWeight: '700',
},
});

View File

@@ -1,5 +1,5 @@
import { HeaderBar } from '@/components/ui/HeaderBar';
import { Colors, palette } from '@/constants/Colors';
import { Colors } from '@/constants/Colors';
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
import { useColorScheme } from '@/hooks/useColorScheme';
import { useCosUpload } from '@/hooks/useCosUpload';
@@ -270,9 +270,18 @@ export default function EditProfileScreen() {
return (
<SafeAreaView style={[styles.container, { backgroundColor: '#F5F5F5' }]}>
<StatusBar barStyle={'dark-content'} />
{/* HeaderBar 放在 ScrollView 外部,确保全宽显示 */}
<HeaderBar
title="编辑资料"
onBack={() => router.back()}
withSafeTop={false}
transparent={true}
variant="elevated"
/>
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : undefined} style={{ flex: 1 }}>
<ScrollView contentContainerStyle={{ paddingBottom: 40 }} style={{ paddingHorizontal: 20 }} showsVerticalScrollIndicator={false}>
<HeaderBar title="编辑资料" onBack={() => router.back()} withSafeTop={false} transparent />
{/* 头像(带相机蒙层,点击从相册选择) */}
<View style={{ alignItems: 'center', marginTop: 4, marginBottom: 16 }}>
@@ -561,7 +570,7 @@ const styles = StyleSheet.create({
backgroundColor: '#F1F5F9',
},
modalBtnPrimary: {
backgroundColor: palette.primary,
backgroundColor: '#7a5af8',
},
modalBtnText: {
color: '#334155',
@@ -571,15 +580,6 @@ const styles = StyleSheet.create({
color: '#0F172A',
fontWeight: '700',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 0,
marginBottom: 8,
},
backButton: { padding: 4, width: 32 },
headerTitle: { fontSize: 18, fontWeight: '700', color: '#192126' },
});