/** * 注册页 RegisterScreen(简洁表单样式) * 胡萝卜BBS - 用户注册 * * 设计风格: * - 纯白背景,扁平化设计 * - 增大间距,避免页面太空 * - 输入框带灰色背景填充 * - 橙色圆角按钮 */ import React, { useState, useEffect, useRef, useMemo } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, TextInput, KeyboardAvoidingView, Platform, ScrollView, ActivityIndicator, Animated, StatusBar, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useRouter } from 'expo-router'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { useAppColors, type AppColors } from '../../theme'; import { authService, resolveAuthApiError } from '../../services/authService'; import { useAuthStore } from '../../stores'; import * as hrefs from '../../navigation/hrefs'; import { showPrompt } from '../../services/promptService'; // 胡萝卜橙主题色 const THEME_COLORS = { primary: '#FF6B35', primaryLight: '#FF8C5A', primaryDark: '#E55A2B', }; export const RegisterScreen: React.FC = () => { const colors = useAppColors(); const styles = useMemo(() => createRegisterStyles(colors), [colors]); const router = useRouter(); const register = useAuthStore((state) => state.register); const isAuthenticated = useAuthStore((state) => state.isAuthenticated); useEffect(() => { if (isAuthenticated) { router.replace(hrefs.hrefHome()); } }, [isAuthenticated, router]); const [username, setUsername] = useState(''); const [nickname, setNickname] = useState(''); const [email, setEmail] = useState(''); const [phone, setPhone] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [verificationCode, setVerificationCode] = useState(''); const [loading, setLoading] = useState(false); const [sendingCode, setSendingCode] = useState(false); const [countdown, setCountdown] = useState(0); const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [agreedToTerms, setAgreedToTerms] = useState(true); // 动画值 const fadeAnim = useRef(new Animated.Value(0)).current; const slideAnim = useRef(new Animated.Value(20)).current; // 启动入场动画 useEffect(() => { Animated.parallel([ Animated.timing(fadeAnim, { toValue: 1, duration: 400, useNativeDriver: true, }), Animated.timing(slideAnim, { toValue: 0, duration: 400, useNativeDriver: true, }), ]).start(); }, []); useEffect(() => { if (countdown <= 0) { return; } const timer = setInterval(() => { setCountdown((prev) => (prev <= 1 ? 0 : prev - 1)); }, 1000); return () => clearInterval(timer); }, [countdown]); // 表单验证 const validateForm = (): boolean => { if (!username.trim()) { showPrompt({ title: '提示', message: '请输入用户名', type: 'warning' }); return false; } if (username.length < 3 || username.length > 20) { showPrompt({ title: '提示', message: '用户名长度需在3-20个字符之间', type: 'warning' }); return false; } if (!/^[a-zA-Z0-9_]+$/.test(username)) { showPrompt({ title: '提示', message: '用户名只能包含字母、数字和下划线', type: 'warning' }); return false; } if (!nickname.trim()) { showPrompt({ title: '提示', message: '请输入昵称', type: 'warning' }); return false; } if (nickname.length < 2 || nickname.length > 20) { showPrompt({ title: '提示', message: '昵称长度需在2-20个字符之间', type: 'warning' }); return false; } if (!email.trim()) { showPrompt({ title: '提示', message: '请输入邮箱', type: 'warning' }); return false; } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { showPrompt({ title: '提示', message: '请输入正确的邮箱地址', type: 'warning' }); return false; } if (!verificationCode.trim()) { showPrompt({ title: '提示', message: '请输入邮箱验证码', type: 'warning' }); return false; } if (phone && !/^1[3-9]\d{9}$/.test(phone)) { showPrompt({ title: '提示', message: '请输入正确的手机号', type: 'warning' }); return false; } if (!password) { showPrompt({ title: '提示', message: '请输入密码', type: 'warning' }); return false; } if (password.length < 6) { showPrompt({ title: '提示', message: '密码长度不能少于6位', type: 'warning' }); return false; } if (password !== confirmPassword) { showPrompt({ title: '提示', message: '两次输入的密码不一致', type: 'warning' }); return false; } if (!agreedToTerms) { showPrompt({ title: '提示', message: '请同意用户协议和隐私政策', type: 'warning' }); return false; } return true; }; const handleSendCode = async () => { if (!email.trim()) { showPrompt({ title: '提示', message: '请先输入邮箱', type: 'warning' }); return; } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { showPrompt({ title: '提示', message: '请输入正确的邮箱地址', type: 'warning' }); return; } if (countdown > 0) { return; } setSendingCode(true); try { const ok = await authService.sendRegisterCode(email.trim()); if (ok) { setCountdown(60); showPrompt({ title: '发送成功', message: '验证码已发送,请查收邮箱', type: 'success' }); } } catch (error: any) { showPrompt({ title: '发送失败', message: resolveAuthApiError(error, '验证码发送失败,请稍后重试'), type: 'error' }); } finally { setSendingCode(false); } }; // 处理注册 const handleRegister = async () => { if (!validateForm()) return; setLoading(true); try { const success = await register({ username, password, nickname, email: email.trim(), verification_code: verificationCode.trim(), phone: phone || undefined, }); if (success) { // 注册成功,导航会自动切换到主页 } else { const msg = useAuthStore.getState().error || '注册失败,请稍后重试'; showPrompt({ title: '注册失败', message: msg, type: 'error' }); } } catch (error: any) { showPrompt({ title: '注册失败', message: resolveAuthApiError(error, '请稍后重试'), type: 'error' }); } finally { setLoading(false); } }; // 跳转到登录页 const handleGoToLogin = () => { router.push(hrefs.hrefAuthLogin()); }; return ( {/* 标题区域 - 增大间距 */} 创建新账号 填写以下信息完成注册 {/* 表单区域 */} {/* 用户名输入框 */} 用户名 {username.length > 0 && /^[a-zA-Z0-9_]{3,20}$/.test(username) && ( )} {/* 昵称输入框 */} 昵称 {nickname.length > 0 && nickname.length >= 2 && ( )} {/* 邮箱输入框 */} 邮箱 {email.length > 0 && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && ( )} {/* 邮箱验证码 */} 验证码 0) && styles.sendCodeButtonDisabled, ]} onPress={handleSendCode} disabled={sendingCode || countdown > 0} activeOpacity={0.8} > {sendingCode ? '发送中...' : countdown > 0 ? `${countdown}s` : '获取验证码'} {/* 手机号输入框 */} 手机号(选填) {/* 密码输入框 */} 密码 setShowPassword(!showPassword)} style={styles.eyeButton} > {/* 确认密码输入框 */} 确认密码 setShowConfirmPassword(!showConfirmPassword)} style={styles.eyeButton} > {/* 服务条款 */} setAgreedToTerms(!agreedToTerms)} activeOpacity={0.8} > 我已阅读并同意 《用户协议》《隐私政策》 {/* 注册按钮 */} {loading ? ( ) : ( 注 册 )} {/* 底部登录提示 */} 已有账号? 立即登录 ); }; function createRegisterStyles(colors: AppColors) { return StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', }, keyboardView: { flex: 1, }, scrollContent: { flexGrow: 1, paddingHorizontal: 28, paddingTop: 50, paddingBottom: 50, }, content: { flex: 1, maxWidth: 400, width: '100%', alignSelf: 'center', }, // 标题区域 - 增大间距 titleSection: { marginBottom: 40, marginTop: 10, }, title: { fontSize: 32, fontWeight: '700', color: colors.text.primary, lineHeight: 40, }, underline: { width: 40, height: 4, backgroundColor: THEME_COLORS.primary, borderRadius: 2, marginTop: 12, marginBottom: 16, }, subtitle: { fontSize: 16, color: colors.text.secondary, }, // 表单区域 formSection: { marginBottom: 24, }, // 输入框 inputContainer: { marginBottom: 20, }, inputLabel: { fontSize: 14, fontWeight: '500', color: colors.text.secondary, marginBottom: 10, }, inputWrapper: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#F5F5F7', borderRadius: 14, paddingHorizontal: 18, height: 56, }, input: { flex: 1, fontSize: 16, color: colors.text.primary, height: 56, }, checkIcon: { marginLeft: 8, }, eyeButton: { padding: 4, marginLeft: 4, }, // 验证码行 codeRow: { flexDirection: 'row', gap: 12, }, codeInputWrapper: { flex: 1, }, sendCodeButton: { height: 56, paddingHorizontal: 16, backgroundColor: 'rgba(255, 107, 53, 0.1)', borderRadius: 14, alignItems: 'center', justifyContent: 'center', minWidth: 100, }, sendCodeButtonDisabled: { opacity: 0.5, }, sendCodeButtonText: { fontSize: 14, fontWeight: '600', color: THEME_COLORS.primary, }, // 服务条款 termsContainer: { flexDirection: 'row', alignItems: 'center', marginBottom: 28, marginTop: 4, }, termsText: { fontSize: 14, color: colors.text.secondary, marginLeft: 10, flex: 1, lineHeight: 22, }, termsLink: { color: THEME_COLORS.primary, fontWeight: '500', }, // 注册按钮 registerButton: { height: 56, borderRadius: 14, backgroundColor: THEME_COLORS.primary, alignItems: 'center', justifyContent: 'center', }, registerButtonDisabled: { opacity: 0.7, }, registerButtonText: { fontSize: 17, fontWeight: '600', color: '#FFFFFF', }, // 底部 footer: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', marginTop: 32, }, footerText: { fontSize: 15, color: colors.text.secondary, }, loginLink: { fontSize: 15, color: THEME_COLORS.primary, fontWeight: '600', marginLeft: 6, }, }); } export default RegisterScreen;