diff --git a/MeAgent/navigation/Screens.js b/MeAgent/navigation/Screens.js index c1f86863..f2fc27bc 100644 --- a/MeAgent/navigation/Screens.js +++ b/MeAgent/navigation/Screens.js @@ -26,8 +26,8 @@ import Profile from "../screens/Profile"; import React from "react"; import Register from "../screens/Register"; import Search from "../screens/Search"; -// settings -import SettingsScreen from "../screens/Settings"; +// settings - 使用新的个人信息设置页面 +import SettingsScreen from "../src/screens/Profile/SettingsScreen"; import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; import { createDrawerNavigator } from "@react-navigation/drawer"; import { createStackNavigator } from "@react-navigation/stack"; @@ -114,10 +114,8 @@ function SettingsStack(props) { name="Settings" component={SettingsScreen} options={{ - header: ({ navigation, scene }) => ( -
- ), - cardStyle: { backgroundColor: "#F8F9FE" }, + headerShown: false, + cardStyle: { backgroundColor: "#0A0A0F" }, }} /> { } }, []); + // 更新用户信息(本地状态) + const updateUser = useCallback((updates) => { + setUser(prev => { + const newUser = { ...prev, ...updates }; + // 同步更新本地存储(使用 authService 内部已导入的 AsyncStorage) + import('@react-native-async-storage/async-storage').then((module) => { + module.default.setItem('@auth_user_info', JSON.stringify(newUser)); + }); + return newUser; + }); + }, []); + // 检查是否有指定订阅级别 const hasSubscriptionLevel = useCallback( (requiredLevel) => { @@ -153,6 +165,7 @@ export const AuthProvider = ({ children }) => { loginWithCode, logout, refreshUser, + updateUser, hasSubscriptionLevel, }; diff --git a/MeAgent/src/screens/Profile/SettingsScreen.js b/MeAgent/src/screens/Profile/SettingsScreen.js new file mode 100644 index 00000000..a55d343c --- /dev/null +++ b/MeAgent/src/screens/Profile/SettingsScreen.js @@ -0,0 +1,627 @@ +/** + * 设置页面 - 个人信息编辑 + * 参考 Web 版本设计,适配移动端 + */ + +import React, { useState, useCallback, useEffect } from 'react'; +import { + StyleSheet, + ScrollView, + KeyboardAvoidingView, + Platform, + TouchableOpacity, + Alert, +} from 'react-native'; +import { + Box, + VStack, + HStack, + Text, + Icon, + Pressable, + Input, + TextArea, + Spinner, + useToast, +} from 'native-base'; +import { Ionicons } from '@expo/vector-icons'; +import { LinearGradient } from 'expo-linear-gradient'; +import { useNavigation } from '@react-navigation/native'; +import { SafeAreaView } from 'react-native-safe-area-context'; + +import { useAuth } from '../../contexts/AuthContext'; +import { updateProfile } from '../../services/profileService'; + +// 性别选项 +const GENDER_OPTIONS = [ + { value: 'male', label: '男' }, + { value: 'female', label: '女' }, + { value: 'secret', label: '保密' }, +]; + +// 省份选项 +const LOCATION_OPTIONS = [ + '北京', '上海', '广东', '浙江', '江苏', '四川', '湖北', '湖南', + '山东', '河南', '福建', '陕西', '重庆', '天津', '其他', +]; + +/** + * 胶囊选择按钮 + */ +const TagButton = ({ label, isSelected, onPress }) => ( + + + + {label} + + + +); + +/** + * 下拉选择组件(简化版) + */ +const SelectButton = ({ value, placeholder, options, onSelect }) => { + const [showOptions, setShowOptions] = useState(false); + + return ( + + setShowOptions(!showOptions)}> + + + + {value || placeholder} + + + + + + + {showOptions && ( + + + {options.map((option, index) => ( + { + onSelect(option); + setShowOptions(false); + }} + > + + + {option} + + + + ))} + + + )} + + ); +}; + +/** + * 表单分组卡片 + */ +const FormCard = ({ title, children }) => ( + + {title && ( + + {title} + + )} + {children} + +); + +/** + * 表单项 + */ +const FormItem = ({ label, children, style }) => ( + + + {label} + + {children} + +); + +/** + * 账号信息展示行 + */ +const InfoRow = ({ icon, label, value, verified, color = 'white' }) => ( + + + + + + + {label} + + + + {value || '未设置'} + + {verified && ( + + + 已验证 + + + )} + + + +); + +/** + * 菜单项 + */ +const MenuItem = ({ icon, label, color = '#7C3AED', onPress }) => ( + + {({ pressed }) => ( + + + + + + {label} + + + + )} + +); + +/** + * 手机号脱敏 + */ +const maskPhone = (phone) => { + if (!phone) return '未绑定'; + if (phone.length < 7) return phone; + return phone.substring(0, 3) + '****' + phone.substring(phone.length - 4); +}; + +/** + * 邮箱脱敏 + */ +const maskEmail = (email) => { + if (!email) return '未绑定'; + const atIndex = email.indexOf('@'); + if (atIndex <= 0) return email; + const localPart = email.substring(0, atIndex); + const domain = email.substring(atIndex); + const visibleChars = Math.min(3, localPart.length); + return localPart.substring(0, visibleChars) + '***' + domain; +}; + +/** + * 设置页面主组件 + */ +const SettingsScreen = () => { + const navigation = useNavigation(); + const toast = useToast(); + const { user, updateUser, refreshUser } = useAuth(); + + // 表单状态 + const [form, setForm] = useState({ + nickname: '', + gender: 'secret', + location: '', + birthday: '', + bio: '', + }); + + const [isLoading, setIsLoading] = useState(false); + const [hasChanges, setHasChanges] = useState(false); + + // 初始化表单数据 + useEffect(() => { + if (user) { + setForm({ + nickname: user.nickname || '', + gender: user.gender || 'secret', + location: user.location || '', + birthday: user.birthday || '', + bio: user.bio || '', + }); + } + }, [user]); + + // 检测表单变化 + useEffect(() => { + if (!user) return; + const changed = + form.nickname !== (user.nickname || '') || + form.gender !== (user.gender || 'secret') || + form.location !== (user.location || '') || + form.birthday !== (user.birthday || '') || + form.bio !== (user.bio || ''); + setHasChanges(changed); + }, [form, user]); + + // 更新表单字段 + const updateField = useCallback((field, value) => { + setForm(prev => ({ ...prev, [field]: value })); + }, []); + + // 保存资料 + const handleSave = useCallback(async () => { + if (!hasChanges) return; + + setIsLoading(true); + try { + const result = await updateProfile(form); + if (result.success) { + updateUser(form); + toast.show({ + title: '保存成功', + status: 'success', + placement: 'top', + }); + setHasChanges(false); + } else { + throw new Error(result.error || '保存失败'); + } + } catch (error) { + toast.show({ + title: '保存失败', + description: error.message, + status: 'error', + placement: 'top', + }); + } finally { + setIsLoading(false); + } + }, [form, hasChanges, updateUser, toast]); + + // 重置表单 + const handleReset = useCallback(() => { + if (user) { + setForm({ + nickname: user.nickname || '', + gender: user.gender || 'secret', + location: user.location || '', + birthday: user.birthday || '', + bio: user.bio || '', + }); + } + }, [user]); + + return ( + + + {/* 背景渐变 */} + + + {/* 顶部导航 */} + + navigation.goBack()} hitSlop={10}> + + + + 设置 + + + {isLoading ? ( + + ) : ( + + 保存 + + )} + + + + + + {/* 头像区域 */} + + { + Alert.alert('提示', '头像上传功能即将上线'); + }}> + + + {(user?.nickname || user?.username || 'U').charAt(0).toUpperCase()} + + + + + + + + 点击更换头像 + + + + {/* 基本信息 */} + + + updateField('nickname', val)} + placeholder="请输入昵称" + placeholderTextColor="#6B7280" + color="white" + fontSize={14} + bg="rgba(255, 255, 255, 0.05)" + borderWidth={1} + borderColor="rgba(255, 255, 255, 0.1)" + borderRadius={12} + px={4} + py={3} + maxLength={20} + _focus={{ + borderColor: '#7C3AED', + bg: 'rgba(124, 58, 237, 0.1)', + }} + /> + + + + + {GENDER_OPTIONS.map((option) => ( + updateField('gender', option.value)} + /> + ))} + + + + + updateField('location', val)} + /> + + + + updateField('birthday', val)} + placeholder="YYYY-MM-DD" + placeholderTextColor="#6B7280" + color="white" + fontSize={14} + bg="rgba(255, 255, 255, 0.05)" + borderWidth={1} + borderColor="rgba(255, 255, 255, 0.1)" + borderRadius={12} + px={4} + py={3} + _focus={{ + borderColor: '#7C3AED', + bg: 'rgba(124, 58, 237, 0.1)', + }} + /> + + + +