diff --git a/app/index.tsx b/app/index.tsx index f412813..9e2149f 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -7,5 +7,5 @@ export default function Index() { if (isAuthenticated) { return ; } - return ; + return ; } diff --git a/app/welcome.tsx b/app/welcome.tsx new file mode 100644 index 0000000..b582aae --- /dev/null +++ b/app/welcome.tsx @@ -0,0 +1,5 @@ +import { WelcomeScreen } from '../src/screens/auth'; + +export default function WelcomeRoute() { + return ; +} diff --git a/src/navigation/hrefs.ts b/src/navigation/hrefs.ts index a169d5c..00243b2 100644 --- a/src/navigation/hrefs.ts +++ b/src/navigation/hrefs.ts @@ -156,6 +156,10 @@ export function hrefAuthForgot(): string { return '/forgot-password'; } +export function hrefAuthWelcome(): string { + return '/welcome'; +} + // ==================== Materials (学习资料) ==================== export function hrefMaterials(): string { diff --git a/src/screens/auth/ForgotPasswordScreen.tsx b/src/screens/auth/ForgotPasswordScreen.tsx index fe092de..3049063 100644 --- a/src/screens/auth/ForgotPasswordScreen.tsx +++ b/src/screens/auth/ForgotPasswordScreen.tsx @@ -1,4 +1,15 @@ -import React, { useEffect, useMemo, useState } from 'react'; +/** + * 忘记密码页 ForgotPasswordScreen(简洁表单样式) + * 胡萝卜BBS - 找回密码 + * + * 设计风格: + * - 纯白背景,扁平化设计 + * - 增大间距 + * - 输入框带灰色背景填充 + * - 橙色圆角按钮 + */ + +import React, { useEffect, useMemo, useState, useRef } from 'react'; import { View, Text, @@ -9,123 +20,28 @@ import { 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 { LinearGradient } from 'expo-linear-gradient'; -import { spacing, borderRadius, shadows, fontSizes, useAppColors, type AppColors } from '../../theme'; +import { useAppColors, type AppColors } from '../../theme'; import { authService, resolveAuthApiError } from '../../services/authService'; import { showPrompt } from '../../services/promptService'; -function createForgotPasswordStyles(colors: AppColors) { - return StyleSheet.create({ - container: { flex: 1 }, - gradient: { flex: 1 }, - keyboardView: { flex: 1 }, - scrollContent: { - flexGrow: 1, - justifyContent: 'center', - paddingHorizontal: spacing.xl, - paddingVertical: spacing['2xl'], - }, - formCard: { - backgroundColor: colors.background.paper, - borderRadius: borderRadius['2xl'], - padding: spacing.xl, - ...shadows.md, - }, - title: { - fontSize: fontSizes['2xl'], - fontWeight: '700', - color: colors.text.primary, - textAlign: 'center', - marginBottom: spacing.xs, - }, - subtitle: { - fontSize: fontSizes.sm, - color: colors.text.secondary, - textAlign: 'center', - marginBottom: spacing.lg, - }, - inputWrapper: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: colors.background.default, - borderRadius: borderRadius.lg, - borderWidth: 1.5, - borderColor: colors.divider, - paddingHorizontal: spacing.md, - height: 52, - marginBottom: spacing.md, - }, - inputIcon: { - marginRight: spacing.sm, - }, - input: { - flex: 1, - fontSize: fontSizes.md, - color: colors.text.primary, - }, - codeRow: { - flexDirection: 'row', - alignItems: 'center', - gap: spacing.sm, - marginBottom: spacing.md, - }, - codeInput: { - flex: 1, - marginBottom: 0, - }, - sendCodeButton: { - height: 52, - minWidth: 110, - borderRadius: borderRadius.lg, - backgroundColor: colors.primary.main, - alignItems: 'center', - justifyContent: 'center', - paddingHorizontal: spacing.md, - }, - sendCodeButtonDisabled: { - opacity: 0.6, - }, - sendCodeButtonText: { - color: '#fff', - fontSize: fontSizes.sm, - fontWeight: '600', - }, - submitButton: { - height: 50, - borderRadius: borderRadius.lg, - backgroundColor: colors.primary.main, - alignItems: 'center', - justifyContent: 'center', - marginTop: spacing.sm, - }, - submitButtonDisabled: { - opacity: 0.6, - }, - submitButtonText: { - color: '#fff', - fontSize: fontSizes.lg, - fontWeight: '700', - }, - backButton: { - alignItems: 'center', - marginTop: spacing.md, - }, - backButtonText: { - color: colors.primary.main, - fontSize: fontSizes.md, - fontWeight: '500', - }, - }); -} +// 胡萝卜橙主题色 +const THEME_COLORS = { + primary: '#FF6B35', + primaryLight: '#FF8C5A', + primaryDark: '#E55A2B', +}; export const ForgotPasswordScreen: React.FC = () => { const colors = useAppColors(); const styles = useMemo(() => createForgotPasswordStyles(colors), [colors]); const router = useRouter(); + const [email, setEmail] = useState(''); const [verificationCode, setVerificationCode] = useState(''); const [newPassword, setNewPassword] = useState(''); @@ -136,6 +52,26 @@ export const ForgotPasswordScreen: React.FC = () => { const [loading, setLoading] = useState(false); const [countdown, setCountdown] = useState(0); + // 动画值 + 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; @@ -208,101 +144,308 @@ export const ForgotPasswordScreen: React.FC = () => { return ( - - - - + + + + + {/* 标题区域 */} + 找回密码 - 请输入邮箱并完成验证码验证 + + 请输入邮箱并完成验证 + - - - - - - - - + {/* 表单区域 */} + + {/* 邮箱输入框 */} + + 邮箱 + - - 0) && styles.sendCodeButtonDisabled]} - onPress={handleSendCode} - disabled={sendingCode || countdown > 0} - > - {sendingCode ? ( - - ) : ( - {countdown > 0 ? `${countdown}s` : '发送验证码'} + {email.length > 0 && validateEmail(email) && ( + )} - + - - - - setShowPassword((v) => !v)}> - - + {/* 验证码 */} + + 验证码 + + + + + 0) && styles.sendCodeButtonDisabled]} + onPress={handleSendCode} + disabled={sendingCode || countdown > 0} + activeOpacity={0.8} + > + {sendingCode ? ( + + ) : ( + {countdown > 0 ? `${countdown}s` : '获取验证码'} + )} + + - - - - setShowConfirmPassword((v) => !v)}> - - + {/* 新密码输入框 */} + + 新密码 + + + setShowPassword((v) => !v)} + style={styles.eyeButton} + > + + + + {/* 确认密码输入框 */} + + 确认密码 + + + setShowConfirmPassword((v) => !v)} + style={styles.eyeButton} + > + + + + + + {/* 重置密码按钮 */} - {loading ? : 重置密码} + {loading ? ( + + ) : ( + 重置密码 + )} - router.back()}> + {/* 返回登录 */} + router.back()} + activeOpacity={0.8} + > + 返回登录 - - - + + + ); }; +function createForgotPasswordStyles(colors: AppColors) { + return StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#fff', + }, + keyboardView: { + flex: 1, + }, + scrollContent: { + flexGrow: 1, + paddingHorizontal: 28, + paddingTop: 50, + paddingBottom: 40, + }, + content: { + flex: 1, + maxWidth: 400, + width: '100%', + alignSelf: 'center', + }, + + // 标题区域 + titleSection: { + marginBottom: 40, + marginTop: 20, + }, + 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, + }, + + // 提交按钮 + submitButton: { + height: 56, + borderRadius: 14, + backgroundColor: THEME_COLORS.primary, + alignItems: 'center', + justifyContent: 'center', + marginTop: 8, + }, + submitButtonDisabled: { + opacity: 0.7, + }, + submitButtonText: { + color: '#fff', + fontSize: 17, + fontWeight: '600', + }, + + // 返回按钮 + backButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + marginTop: 24, + }, + backButtonText: { + color: THEME_COLORS.primary, + fontSize: 15, + fontWeight: '500', + }, + }); +} + export default ForgotPasswordScreen; diff --git a/src/screens/auth/LoginScreen.tsx b/src/screens/auth/LoginScreen.tsx index 9443b65..b7c7a05 100644 --- a/src/screens/auth/LoginScreen.tsx +++ b/src/screens/auth/LoginScreen.tsx @@ -1,10 +1,12 @@ /** - * 登录页 LoginScreen(响应式适配 - 分栏布局) + * 登录页 LoginScreen(简洁表单样式) * 胡萝卜BBS - 用户登录 * - * 布局规则: - * - 屏幕宽度 < 768px:单栏布局,橙色渐变背景 - * - 屏幕宽度 >= 768px:分栏布局,左侧橙色右侧中性灰 + * 设计风格: + * - 纯白背景,扁平化设计 + * - 增大间距,避免页面太空 + * - 输入框带灰色背景填充 + * - 橙色圆角按钮 */ import React, { useState, useEffect, useRef, useMemo } from 'react'; @@ -24,15 +26,18 @@ import { import { SafeAreaView } from 'react-native-safe-area-context'; import { useRouter } from 'expo-router'; import { MaterialCommunityIcons } from '@expo/vector-icons'; -import { LinearGradient } from 'expo-linear-gradient'; -import { spacing, borderRadius, shadows, fontSizes, useAppColors, type AppColors } from '../../theme'; +import { useAppColors, type AppColors } from '../../theme'; import { useAuthStore } from '../../stores'; import * as hrefs from '../../navigation/hrefs'; -import { useResponsive, useResponsiveValue } from '../../hooks'; +import { useResponsive } from '../../hooks'; import { showPrompt } from '../../services/promptService'; -// 分栏布局断点 -const SPLIT_BREAKPOINT = 768; +// 胡萝卜橙主题色 +const THEME_COLORS = { + primary: '#FF6B35', + primaryLight: '#FF8C5A', + primaryDark: '#E55A2B', +}; export const LoginScreen: React.FC = () => { const colors = useAppColors(); @@ -40,67 +45,29 @@ export const LoginScreen: React.FC = () => { const router = useRouter(); const login = useAuthStore((state) => state.login); const isAuthenticated = useAuthStore((state) => state.isAuthenticated); - const storeError = useAuthStore((state) => state.error); const setStoreError = useAuthStore((state) => state.setError); - // 响应式布局 - const { isLandscape, width } = useResponsive(); - - // 是否使用分栏布局 - const isSplitLayout = width >= SPLIT_BREAKPOINT; + const { isLandscape } = useResponsive(); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [loading, setLoading] = useState(false); const [showPassword, setShowPassword] = useState(false); - const [errorMsg, setErrorMsg] = useState(null); // 动画值 const fadeAnim = useRef(new Animated.Value(0)).current; - const slideAnim = useRef(new Animated.Value(50)).current; - const scaleAnim = useRef(new Animated.Value(0.9)).current; - const inputFadeAnim = useRef(new Animated.Value(0)).current; - - // 响应式值 - 大屏模式单独放大Logo尺寸,更显大气 - const logoSize = useResponsiveValue({ - xs: 64, sm: 72, md: 80, - lg: isSplitLayout ? 140 : 96, // 大屏模式Logo图标进一步放大 - xl: isSplitLayout ? 160 : 110 - }); - const logoContainerSize = useResponsiveValue({ - xs: 110, sm: 120, md: 130, - lg: isSplitLayout ? 200 : 150, // 大屏模式Logo容器进一步放大 - xl: isSplitLayout ? 220 : 170 - }); - const appNameSize = useResponsiveValue({ - xs: 28, sm: 30, md: 32, - lg: isSplitLayout ? 60 : 36, // 大屏模式标题文字大幅放大 - xl: isSplitLayout ? 72 : 40 - }); - const formMaxWidth = isSplitLayout ? 420 : undefined; + const slideAnim = useRef(new Animated.Value(20)).current; // 启动入场动画 useEffect(() => { - Animated.sequence([ - Animated.parallel([ - Animated.timing(fadeAnim, { - toValue: 1, - duration: 600, - useNativeDriver: true, - }), - Animated.timing(scaleAnim, { - toValue: 1, - duration: 600, - useNativeDriver: true, - }), - ]), - Animated.timing(slideAnim, { - toValue: 0, - duration: 500, + Animated.parallel([ + Animated.timing(fadeAnim, { + toValue: 1, + duration: 400, useNativeDriver: true, }), - Animated.timing(inputFadeAnim, { - toValue: 1, + Animated.timing(slideAnim, { + toValue: 0, duration: 400, useNativeDriver: true, }), @@ -124,13 +91,6 @@ export const LoginScreen: React.FC = () => { return true; }; - // store error 同步到本地 - useEffect(() => { - if (storeError) { - setErrorMsg(storeError); - } - }, [storeError]); - useEffect(() => { if (isAuthenticated) { router.replace(hrefs.hrefHome()); @@ -138,7 +98,6 @@ export const LoginScreen: React.FC = () => { }, [isAuthenticated, router]); const clearError = () => { - setErrorMsg(null); setStoreError(null); }; @@ -152,11 +111,11 @@ export const LoginScreen: React.FC = () => { const success = await login({ username, password }); if (!success) { if (!useAuthStore.getState().error) { - setErrorMsg('登录失败,请稍后重试'); + showPrompt({ title: '登录失败', message: '请检查用户名和密码', type: 'error' }); } } } catch (error: any) { - setErrorMsg(error.message || '登录失败,请检查用户名和密码'); + showPrompt({ title: '登录失败', message: error.message || '请检查用户名和密码', type: 'error' }); } finally { setLoading(false); } @@ -167,605 +126,257 @@ export const LoginScreen: React.FC = () => { router.push(hrefs.hrefAuthRegister()); }; - // 渲染左侧面板(Logo和品牌信息)- 优化大屏布局 - const renderLeftPanel = () => ( - - {/* 胡萝卜图标 - 靠上显示 */} - - - - - {/* 品牌名称 - 优化大屏排版 */} - 萝卜社区 - 发现有趣的内容,结识志同道合的朋友 - - {/* 装饰元素 - 大屏模式改为水平排列 + 大幅放大 */} - - {isSplitLayout ? ( - // 大屏模式:水平排列 + 大幅放大图标 - - - - - - ) : ( - // 小屏模式:保持原有横向排列 - - - - - - )} - - - ); - - // 渲染表单内容 - const renderFormContent = () => ( - - - 欢迎回来 - - {/* 用户名输入框 */} - - - - { setUsername(v); clearError(); }} - autoCapitalize="none" - autoCorrect={false} - returnKeyType="next" - /> - {username.length > 0 && ( - setUsername('')} - style={styles.clearButton} - > - - - )} - - - - {/* 密码输入框 */} - - - - { setPassword(v); clearError(); }} - secureTextEntry={!showPassword} - autoCapitalize="none" - returnKeyType="done" - onSubmitEditing={handleLogin} - /> - setShowPassword(!showPassword)} - style={styles.clearButton} - > - - - - - - {/* 忘记密码 */} - router.push(hrefs.hrefAuthForgot())} - > - 忘记密码? - - - {/* 内联错误提示 */} - {errorMsg && ( - - - {errorMsg} - - )} - - {/* 登录按钮 */} - - - {loading ? ( - - ) : ( - 登 录 - )} - - - - - {/* 底部注册提示 */} - - 还没有账号? - - 立即注册 - - - - ); - - // 渲染分栏布局 - const renderSplitLayout = () => ( - - {/* 左侧橙色面板 */} - - {/* 装饰性背景元素 */} - - - - - - {renderLeftPanel()} - - - - {/* 右侧中性灰面板 */} - - - - {/* 核心修复:用View包裹,确保垂直+水平居中 */} - - {renderFormContent()} - - - - - ); - - // 渲染单栏布局 - const renderSingleLayout = () => ( + return ( - - + - - - {/* 装饰性背景元素 */} - - - - {/* 头部 */} - - - - - 萝卜社区 - 发现有趣的内容,结识志同道合的朋友 - + {/* 标题区域 - 增大间距 */} + + 欢迎回来 + + 请登录您的账号 + - {/* 表单 */} - {renderFormContent()} - - - + {/* 表单区域 */} + + {/* 用户名输入框 */} + + 用户名 + + { setUsername(v); clearError(); }} + autoCapitalize="none" + autoCorrect={false} + returnKeyType="next" + /> + {username.length > 0 && ( + + )} + + + + {/* 密码输入框 */} + + 密码 + + { setPassword(v); clearError(); }} + secureTextEntry={!showPassword} + autoCapitalize="none" + returnKeyType="done" + onSubmitEditing={handleLogin} + /> + setShowPassword(!showPassword)} + style={styles.eyeButton} + > + + + + + + {/* 忘记密码 */} + router.push(hrefs.hrefAuthForgot())} + > + 忘记密码? + + + {/* 登录按钮 */} + + {loading ? ( + + ) : ( + 登 录 + )} + + + + {/* 底部注册提示 */} + + 还没有账号? + + 立即注册 + + + + + ); - - return isSplitLayout ? renderSplitLayout() : renderSingleLayout(); }; function createLoginStyles(colors: AppColors) { return StyleSheet.create({ - container: { - flex: 1, - }, - gradient: { - flex: 1, - }, - keyboardView: { - flex: 1, - }, - // 单栏布局内容 - scrollContent: { - flexGrow: 1, - paddingHorizontal: spacing.xl, - paddingVertical: spacing['2xl'], - }, - scrollContentCentered: { - alignItems: 'center', - justifyContent: 'center', - flex: 1, - }, - scrollContentLandscape: { - paddingVertical: spacing.lg, - }, - // 装饰性背景元素 - decorCircle1: { - position: 'absolute', - top: -100, - right: -100, - width: 300, - height: 300, - borderRadius: 150, - backgroundColor: 'rgba(255,255,255,0.1)', - }, - decorCircle2: { - position: 'absolute', - bottom: 100, - left: -150, - width: 250, - height: 250, - borderRadius: 125, - backgroundColor: 'rgba(255,255,255,0.08)', - }, - // 头部区域 - headerSection: { - alignItems: 'center', - marginTop: spacing['3xl'], - marginBottom: spacing['3xl'], - }, - headerSectionLandscape: { - marginTop: spacing.lg, - marginBottom: spacing.lg, - }, - logoContainer: { - backgroundColor: 'rgba(255,255,255,0.25)', - alignItems: 'center', - justifyContent: 'center', - marginBottom: spacing.lg, - borderWidth: 2, - borderColor: 'rgba(255,255,255,0.4)', - ...shadows.md, - }, - // 大屏模式Logo容器样式 - 增加阴影和内边距,更立体 - splitLogoContainer: { - borderWidth: 3, - borderColor: 'rgba(255,255,255,0.5)', - ...shadows.lg, - }, - logoContainerTop: { - marginTop: spacing['3xl'], - }, - appName: { - fontWeight: '800', - color: '#FFFFFF', - marginBottom: spacing.sm, - textShadowColor: 'rgba(0,0,0,0.1)', - textShadowOffset: { width: 0, height: 2 }, - textShadowRadius: 4, - }, - // 大屏模式标题样式 - 大幅放大,增加间距和文字效果 - splitAppName: { - fontSize: 72, // 大幅放大主标题 - marginBottom: spacing['2xl'], - textShadowOffset: { width: 0, height: 4 }, - textShadowRadius: 8, - letterSpacing: 4, // 增加字间距,更显大气 - fontWeight: '900', - }, - subtitle: { - fontSize: fontSizes.md, - color: 'rgba(255,255,255,0.9)', - textAlign: 'center', - paddingHorizontal: spacing.xl, - }, - // 大屏模式副标题样式 - 大幅放大 - splitSubtitle: { - fontSize: 24, // 放大副标题 - marginBottom: spacing['6xl'], // 增加底部间距 - lineHeight: 36, - paddingHorizontal: spacing['4xl'], - letterSpacing: 1.5, - fontWeight: '500', - }, - // 表单卡片(基础样式,无阴影) - formCard: { - backgroundColor: colors.background.paper, - borderRadius: borderRadius['2xl'], - padding: spacing['2xl'], - marginBottom: spacing.xl, - }, - // 小屏模式:原有默认阴影 - normalFormCardShadow: { - ...shadows.md, - }, - // 大屏模式:专用全向阴影 - splitFormCardShadow: { - // iOS 全向柔和阴影 - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.12, - shadowRadius: 12, - // Android 高程 + 轻微边框(模拟顶部阴影) - elevation: 8, - // 新增:极细微的顶部边框,强化区分度 - borderTopWidth: 1, - borderTopColor: 'rgba(0,0,0,0.05)', - }, - formTitle: { - fontSize: fontSizes['2xl'], - fontWeight: '700', - color: colors.text.primary, - marginBottom: spacing.xl, - textAlign: 'center', - }, - inputContainer: { - marginBottom: spacing.lg, - }, - inputWrapper: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: colors.background.default, - borderRadius: borderRadius.lg, - borderWidth: 1.5, - borderColor: colors.divider, - paddingHorizontal: spacing.md, - height: 56, - }, - inputIcon: { - marginRight: spacing.sm, - }, - input: { - flex: 1, - fontSize: fontSizes.md, - color: colors.text.primary, - }, - clearButton: { - padding: spacing.xs, - }, - forgotPassword: { - alignSelf: 'flex-end', - marginBottom: spacing.md, - }, - forgotPasswordText: { - fontSize: fontSizes.sm, - color: colors.primary.main, - fontWeight: '600', - }, - errorBox: { - flexDirection: 'row', - alignItems: 'flex-start', - backgroundColor: colors.accent.light, - borderRadius: borderRadius.md, - paddingHorizontal: spacing.md, - paddingVertical: spacing.sm, - marginBottom: spacing.md, - borderLeftWidth: 3, - borderLeftColor: colors.accent.main, - }, - errorText: { - flex: 1, - fontSize: fontSizes.sm, - color: colors.accent.dark, - lineHeight: 20, - }, - loginButton: { - borderRadius: borderRadius.lg, - overflow: 'hidden', - ...shadows.md, - }, - loginButtonGradient: { - height: 52, - alignItems: 'center', - justifyContent: 'center', - }, - loginButtonDisabled: { - opacity: 0.7, - }, - loginButtonText: { - fontSize: fontSizes.lg, - fontWeight: '700', - color: '#FFFFFF', - }, - // 底部区域 - footerSection: { - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - paddingTop: spacing.xl, - }, - footerText: { - fontSize: fontSizes.md, - color: colors.text.primary, - }, - registerLink: { - fontSize: fontSizes.md, - color: colors.primary.main, - fontWeight: '700', - marginLeft: spacing.xs, - textDecorationLine: 'underline', - }, - // ========== 分栏布局样式 ========== - splitContainer: { - flex: 1, - flexDirection: 'row', - }, - leftPanelContainer: { - flex: 1, // 50% - justifyContent: 'center', - alignItems: 'center', - overflow: 'hidden', - }, - leftPanelSafeArea: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - paddingHorizontal: spacing['4xl'], // 增加内边距,不贴边 - }, - leftPanel: { - alignItems: 'center', - paddingHorizontal: spacing['2xl'], - }, - // 大屏模式左侧面板布局 - splitLeftPanel: { - width: '100%', - justifyContent: 'center', - paddingVertical: spacing['6xl'], - }, - leftPanelDecor: { - position: 'absolute', - bottom: spacing['4xl'], - left: 0, - right: 0, - alignItems: 'center', - }, - leftPanelDecorBottom: { - marginTop: spacing['2xl'], - }, - // 大屏模式装饰元素布局 - splitLeftPanelDecorBottom: { - marginTop: spacing['6xl'], - width: '100%', - alignItems: 'center', - }, - // 大屏模式装饰元素水平排列 - splitDecorRow: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-around', - width: '100%', - gap: spacing['2xl'], // 增加图标间距 - }, - // 大屏模式装饰图标样式 - splitDecorIcon: { - transform: [{ scale: 1.1 }], - opacity: 0.9, - }, - decorRow: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-around', - width: '80%', - }, - rightPanelContainer: { - flex: 1, // 50% - backgroundColor: colors.background.paper, - }, - rightPanelSafeArea: { - flex: 1, - // 移除左右内边距,交给子容器控制 - }, - // 分栏布局右侧内容包裹器(核心修复) - splitRightContentWrapper: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - // 新增:左右内边距,保证表单和边缘有间距 - paddingHorizontal: spacing['2xl'], - }, - // 表单内容包裹器(区分大屏/小屏) - splitFormWrapper: { - // 关键修改:设置最大宽度 + 自适应宽度,实现水平居中 - maxWidth: 420, - width: '100%', - }, - singleFormWrapper: { - flexGrow: 1, - width: '100%', - // 移除这里的阴影,避免重复 - }, + container: { + flex: 1, + backgroundColor: '#fff', + }, + keyboardView: { + flex: 1, + }, + scrollContent: { + flexGrow: 1, + paddingHorizontal: 28, + paddingTop: 60, + paddingBottom: 40, + }, + scrollContentLandscape: { + paddingTop: 30, + }, + content: { + flex: 1, + maxWidth: 400, + width: '100%', + alignSelf: 'center', + }, + + // 标题区域 - 增大间距 + titleSection: { + marginBottom: 48, + marginTop: 20, + }, + 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: 32, + }, + + // 输入框 + inputContainer: { + marginBottom: 24, + }, + 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, + }, + + // 忘记密码 + forgotPassword: { + alignSelf: 'flex-end', + marginBottom: 28, + marginTop: -8, + }, + forgotPasswordText: { + fontSize: 14, + color: THEME_COLORS.primary, + fontWeight: '500', + }, + + // 登录按钮 + loginButton: { + height: 56, + borderRadius: 14, + backgroundColor: THEME_COLORS.primary, + alignItems: 'center', + justifyContent: 'center', + }, + loginButtonDisabled: { + opacity: 0.7, + }, + loginButtonText: { + fontSize: 17, + fontWeight: '600', + color: '#FFFFFF', + }, + + // 底部 + footer: { + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + marginTop: 40, + }, + footerText: { + fontSize: 15, + color: colors.text.secondary, + }, + registerLink: { + fontSize: 15, + color: THEME_COLORS.primary, + fontWeight: '600', + marginLeft: 6, + }, }); } -export default LoginScreen; \ No newline at end of file +export default LoginScreen; diff --git a/src/screens/auth/RegisterScreen.tsx b/src/screens/auth/RegisterScreen.tsx index 19c0b90..bf77133 100644 --- a/src/screens/auth/RegisterScreen.tsx +++ b/src/screens/auth/RegisterScreen.tsx @@ -1,10 +1,12 @@ /** - * 注册页 RegisterScreen(响应式适配 - 分栏布局) + * 注册页 RegisterScreen(简洁表单样式) * 胡萝卜BBS - 用户注册 - * - * 布局规则: - * - 屏幕宽度 < 768px:单栏布局,橙色渐变背景 - * - 屏幕宽度 >= 768px:分栏布局,左侧橙色右侧中性灰 + * + * 设计风格: + * - 纯白背景,扁平化设计 + * - 增大间距,避免页面太空 + * - 输入框带灰色背景填充 + * - 橙色圆角按钮 */ import React, { useState, useEffect, useRef, useMemo } from 'react'; @@ -24,16 +26,18 @@ import { import { SafeAreaView } from 'react-native-safe-area-context'; import { useRouter } from 'expo-router'; import { MaterialCommunityIcons } from '@expo/vector-icons'; -import { LinearGradient } from 'expo-linear-gradient'; -import { spacing, borderRadius, shadows, fontSizes, useAppColors, type AppColors } from '../../theme'; +import { useAppColors, type AppColors } from '../../theme'; import { authService, resolveAuthApiError } from '../../services/authService'; import { useAuthStore } from '../../stores'; import * as hrefs from '../../navigation/hrefs'; -import { useResponsive, useResponsiveValue } from '../../hooks'; import { showPrompt } from '../../services/promptService'; -// 分栏布局断点 -const SPLIT_BREAKPOINT = 768; +// 胡萝卜橙主题色 +const THEME_COLORS = { + primary: '#FF6B35', + primaryLight: '#FF8C5A', + primaryDark: '#E55A2B', +}; export const RegisterScreen: React.FC = () => { const colors = useAppColors(); @@ -41,12 +45,6 @@ export const RegisterScreen: React.FC = () => { const router = useRouter(); const register = useAuthStore((state) => state.register); const isAuthenticated = useAuthStore((state) => state.isAuthenticated); - - // 响应式布局 - const { isLandscape, width } = useResponsive(); - - // 是否使用分栏布局 - const isSplitLayout = width >= SPLIT_BREAKPOINT; useEffect(() => { if (isAuthenticated) { @@ -70,51 +68,19 @@ export const RegisterScreen: React.FC = () => { // 动画值 const fadeAnim = useRef(new Animated.Value(0)).current; - const slideAnim = useRef(new Animated.Value(50)).current; - const scaleAnim = useRef(new Animated.Value(0.95)).current; - const inputFadeAnim = useRef(new Animated.Value(0)).current; - - // 响应式值 - 大屏模式单独放大图标尺寸,更显大气 - const iconSize = useResponsiveValue({ - xs: 48, sm: 52, md: 56, - lg: isSplitLayout ? 120 : 64, // 大屏模式图标进一步放大 - xl: isSplitLayout ? 140 : 72 - }); - const iconContainerSize = useResponsiveValue({ - xs: 80, sm: 88, md: 96, - lg: isSplitLayout ? 180 : 108, // 大屏模式图标容器进一步放大 - xl: isSplitLayout ? 200 : 120 - }); - const titleSize = useResponsiveValue({ - xs: 24, sm: 26, md: 28, - lg: isSplitLayout ? 60 : 30, // 大屏模式标题文字大幅放大 - xl: isSplitLayout ? 72 : 32 - }); - const formMaxWidth = isSplitLayout ? 480 : undefined; + const slideAnim = useRef(new Animated.Value(20)).current; // 启动入场动画 useEffect(() => { - Animated.sequence([ - Animated.parallel([ - Animated.timing(fadeAnim, { - toValue: 1, - duration: 500, - useNativeDriver: true, - }), - Animated.timing(scaleAnim, { - toValue: 1, - duration: 500, - useNativeDriver: true, - }), - ]), - Animated.timing(slideAnim, { - toValue: 0, + Animated.parallel([ + Animated.timing(fadeAnim, { + toValue: 1, duration: 400, useNativeDriver: true, }), - Animated.timing(inputFadeAnim, { - toValue: 1, - duration: 300, + Animated.timing(slideAnim, { + toValue: 0, + duration: 400, useNativeDriver: true, }), ]).start(); @@ -164,7 +130,6 @@ export const RegisterScreen: React.FC = () => { showPrompt({ title: '提示', message: '请输入邮箱验证码', type: 'warning' }); return false; } - // 手机号验证(如果填写了) if (phone && !/^1[3-9]\d{9}$/.test(phone)) { showPrompt({ title: '提示', message: '请输入正确的手机号', type: 'warning' }); return false; @@ -247,838 +212,405 @@ export const RegisterScreen: React.FC = () => { router.push(hrefs.hrefAuthLogin()); }; - // 渲染左侧面板(Logo和品牌信息)- 优化大屏布局 - const renderLeftPanel = () => ( - - {/* 用户图标 - 靠上显示 */} - - - - - {/* 品牌名称 - 优化大屏排版 */} - 加入萝卜社区 - 加入萝卜社区,发现有趣的内容 - - {/* 装饰元素 - 大屏模式改为水平排列 + 大幅放大 */} - - {isSplitLayout ? ( - // 大屏模式:水平排列 + 大幅放大图标 - - - - - - ) : ( - // 小屏模式:保持原有横向排列 - - - - - - )} - - - ); - - // 渲染表单内容 - const renderFormContent = () => ( - - - 注册账号 - - {/* 用户名输入框 */} - - - - - {username.length > 0 && ( - setUsername('')} - style={styles.clearButton} - > - - - )} - - - - {/* 昵称输入框 */} - - - - - {nickname.length > 0 && ( - setNickname('')} - style={styles.clearButton} - > - - - )} - - - - {/* 邮箱输入框 */} - - - - - {email.length > 0 && ( - setEmail('')} - style={styles.clearButton} - > - - - )} - - - - {/* 邮箱验证码 */} - - - - - - - 0) && styles.sendCodeButtonDisabled, - ]} - onPress={handleSendCode} - disabled={sendingCode || countdown > 0} - activeOpacity={0.8} - > - - {sendingCode ? '发送中...' : countdown > 0 ? `${countdown}s` : '发送验证码'} - - - - - - {/* 手机号输入框 */} - - - - - {phone.length > 0 && ( - setPhone('')} - style={styles.clearButton} - > - - - )} - - - - {/* 密码输入框 */} - - - - - setShowPassword(!showPassword)} - style={styles.clearButton} - > - - - - - - {/* 确认密码输入框 */} - - - - - setShowConfirmPassword(!showConfirmPassword)} - style={styles.clearButton} - > - - - - - - {/* 服务条款 */} - setAgreedToTerms(!agreedToTerms)} - activeOpacity={0.8} - > - - - - - 我已阅读并同意 - 《用户协议》 - 和 - 《隐私政策》 - - - - {/* 注册按钮 */} - - - {loading ? ( - - ) : ( - 注 册 - )} - - - - - {/* 底部登录提示 */} - - 已有账号? - - 立即登录 - - - - ); - - // 渲染分栏布局 - const renderSplitLayout = () => ( - - {/* 左侧橙色面板 */} - - {/* 装饰性背景元素 */} - - - - - - {renderLeftPanel()} - - - - {/* 右侧中性灰面板 */} - - - - {/* 核心修复:用View包裹,确保垂直+水平居中 */} - - {renderFormContent()} - - - - - ); - - // 渲染单栏布局 - const renderSingleLayout = () => ( + return ( - - + - - - {/* 装饰性背景元素 */} - - - - {/* 头部 */} - - - - - 加入萝卜社区 - 加入萝卜社区,发现有趣的内容 - + {/* 标题区域 - 增大间距 */} + + 创建新账号 + + 填写以下信息完成注册 + - {/* 表单 */} - {renderFormContent()} - - - + {/* 表单区域 */} + + {/* 用户名输入框 */} + + 用户名 + + + {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 ? ( + + ) : ( + 注 册 + )} + + + + {/* 底部登录提示 */} + + 已有账号? + + 立即登录 + + + + + ); - - return isSplitLayout ? renderSplitLayout() : renderSingleLayout(); }; function createRegisterStyles(colors: AppColors) { return StyleSheet.create({ - container: { - flex: 1, - }, - gradient: { - flex: 1, - }, - keyboardView: { - flex: 1, - }, - scrollContent: { - flexGrow: 1, - paddingHorizontal: spacing.xl, - paddingVertical: spacing.xl, - }, - scrollContentLandscape: { - paddingVertical: spacing.md, - }, - // 装饰性背景元素 - decorCircle1: { - position: 'absolute', - top: -80, - right: -80, - width: 250, - height: 250, - borderRadius: 125, - backgroundColor: 'rgba(255,255,255,0.1)', - }, - decorCircle2: { - position: 'absolute', - bottom: 150, - left: -120, - width: 200, - height: 200, - borderRadius: 100, - backgroundColor: 'rgba(255,255,255,0.08)', - }, - // 头部区域 - headerSection: { - alignItems: 'center', - marginTop: spacing.xl, - marginBottom: spacing.xl, - }, - headerSectionLandscape: { - marginTop: spacing.md, - marginBottom: spacing.md, - }, - iconContainer: { - backgroundColor: 'rgba(255,255,255,0.25)', - alignItems: 'center', - justifyContent: 'center', - marginBottom: spacing.md, - borderWidth: 2, - borderColor: 'rgba(255,255,255,0.4)', - ...shadows.md, - }, - title: { - fontWeight: '800', - color: '#FFFFFF', - marginBottom: spacing.xs, - textShadowColor: 'rgba(0,0,0,0.1)', - textShadowOffset: { width: 0, height: 2 }, - textShadowRadius: 4, - }, - subtitle: { - fontSize: fontSizes.md, - color: 'rgba(255,255,255,0.9)', - }, - // 表单卡片 - formCard: { - backgroundColor: colors.background.paper, - borderRadius: borderRadius['2xl'], - padding: spacing.xl, - marginBottom: spacing.lg, - ...shadows.md, - }, - inputContainer: { - marginBottom: spacing.md, - }, - codeRow: { - flexDirection: 'row', - alignItems: 'center', - gap: spacing.sm, - }, - inputWrapper: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: colors.background.default, - borderRadius: borderRadius.lg, - borderWidth: 1.5, - borderColor: colors.divider, - paddingHorizontal: spacing.md, - height: 52, - }, - inputWrapperWide: { - height: 56, - }, - codeInputWrapper: { - flex: 1, - }, - inputIcon: { - marginRight: spacing.sm, - }, - input: { - flex: 1, - fontSize: fontSizes.md, - color: colors.text.primary, - }, - inputWide: { - fontSize: fontSizes.lg, - }, - clearButton: { - padding: spacing.xs, - }, - sendCodeButton: { - height: 52, - minWidth: 110, - paddingHorizontal: spacing.md, - borderRadius: borderRadius.lg, - backgroundColor: colors.primary.main, - alignItems: 'center', - justifyContent: 'center', - }, - sendCodeButtonDisabled: { - opacity: 0.6, - }, - sendCodeButtonText: { - color: '#fff', - fontSize: fontSizes.sm, - fontWeight: '600', - }, - // 服务条款 - termsContainer: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: spacing.lg, - marginTop: spacing.sm, - }, - checkbox: { - marginRight: spacing.sm, - }, - checkboxChecked: { - // 选中状态的额外样式 - }, - termsText: { - fontSize: fontSizes.sm, - color: colors.text.secondary, - flex: 1, - lineHeight: 20, - }, - termsLink: { - color: colors.primary.main, - fontWeight: '500', - }, - // 注册按钮 - registerButton: { - borderRadius: borderRadius.lg, - overflow: 'hidden', - ...shadows.md, - }, - registerButtonGradient: { - height: 50, - alignItems: 'center', - justifyContent: 'center', - }, - registerButtonGradientWide: { - height: 54, - }, - registerButtonDisabled: { - opacity: 0.7, - }, - registerButtonText: { - fontSize: fontSizes.lg, - fontWeight: '700', - color: '#FFFFFF', - }, - // 底部区域 - footerSection: { - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - marginTop: 'auto', - paddingTop: spacing.md, - paddingBottom: spacing.md, - }, - splitFooterSection: { - paddingTop: spacing.xl, - }, - footerText: { - fontSize: fontSizes.md, - color: 'rgba(255,255,255,0.9)', - }, - splitFooterText: { - color: colors.text.primary, - }, - loginLink: { - fontSize: fontSizes.md, - color: '#FFFFFF', - fontWeight: '700', - marginLeft: spacing.xs, - textDecorationLine: 'underline', - }, - splitLoginLink: { - color: colors.primary.main, - }, - // ========== 分栏布局样式 ========== - splitContainer: { - flex: 1, - flexDirection: 'row', - }, - leftPanelContainer: { - flex: 1, // 50% - justifyContent: 'center', - alignItems: 'center', - overflow: 'hidden', - }, - leftPanelSafeArea: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - paddingHorizontal: spacing['4xl'], // 增加内边距,不贴边 - }, - leftPanel: { - alignItems: 'center', - paddingHorizontal: spacing['2xl'], - }, - // 大屏模式左侧面板布局 - splitLeftPanel: { - width: '100%', - justifyContent: 'center', - paddingVertical: 120, - }, - // 大屏模式图标容器样式 - 增加阴影和内边距,更立体 - splitIconContainer: { - borderWidth: 3, - borderColor: 'rgba(255,255,255,0.5)', - ...shadows.lg, - }, - iconContainerTop: { - marginTop: spacing['3xl'], - }, - // 大屏模式标题样式 - 大幅放大,增加间距和文字效果 - splitTitle: { - fontSize: 72, // 大幅放大主标题 - marginBottom: 48, - textShadowOffset: { width: 0, height: 4 }, - textShadowRadius: 8, - letterSpacing: 4, // 增加字间距,更显大气 - fontWeight: '900', - }, - // 大屏模式副标题样式 - 大幅放大 - splitSubtitle: { - fontSize: 24, // 放大副标题 - marginBottom: 120, // 增加底部间距 - lineHeight: 36, - paddingHorizontal: spacing['4xl'], - letterSpacing: 1.5, - fontWeight: '500', - }, - leftPanelDecorBottom: { - marginTop: spacing['2xl'], - }, - // 大屏模式装饰元素布局 - splitLeftPanelDecorBottom: { - marginTop: 120, - width: '100%', - alignItems: 'center', - }, - // 大屏模式装饰元素水平排列 - splitDecorRow: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-around', - width: '100%', - gap: spacing['2xl'], // 增加图标间距 - }, - // 大屏模式装饰图标样式 - splitDecorIcon: { - transform: [{ scale: 1.1 }], - opacity: 0.9, - }, - decorRow: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-around', - width: '80%', - }, - rightPanelContainer: { - flex: 1, // 50% - backgroundColor: colors.background.paper, - }, - rightPanelSafeArea: { - flex: 1, - // 移除左右内边距,交给子容器控制 - }, - // 分栏布局右侧内容包裹器(核心修复) - splitRightContentWrapper: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - // 新增:左右内边距,保证表单和边缘有间距 - paddingHorizontal: spacing['2xl'], - }, - // 表单内容包裹器(区分大屏/小屏) - splitFormWrapper: { - // 关键修改:设置最大宽度 + 自适应宽度,实现水平居中 - maxWidth: 480, - width: '100%', - }, - singleFormWrapper: { - flexGrow: 1, - width: '100%', - }, - // 小屏模式:原有默认阴影 - normalFormCardShadow: { - ...shadows.md, - }, - // 大屏模式:专用全向阴影 - splitFormCardShadow: { - // iOS 全向柔和阴影 - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.12, - shadowRadius: 12, - // Android 高程 + 轻微边框(模拟顶部阴影) - elevation: 8, - // 新增:极细微的顶部边框,强化区分度 - borderTopWidth: 1, - borderTopColor: 'rgba(0,0,0,0.05)', - }, - formTitle: { - fontSize: fontSizes['2xl'], - fontWeight: '700', - color: colors.text.primary, - marginBottom: spacing.xl, - textAlign: 'center', - }, + 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, + }, }); } diff --git a/src/screens/auth/WelcomeScreen.tsx b/src/screens/auth/WelcomeScreen.tsx new file mode 100644 index 0000000..efa0de5 --- /dev/null +++ b/src/screens/auth/WelcomeScreen.tsx @@ -0,0 +1,265 @@ +/** + * 欢迎页 WelcomeScreen + * 胡萝卜BBS - 用户欢迎页 + * + * 亮色模式:白色主色调 + 胡萝卜橙辅色 + * 暗色模式:深色背景 + 胡萝卜橙辅色 + */ + +import React, { useEffect, useRef, useMemo } from 'react'; +import { + View, + Text, + StyleSheet, + TouchableOpacity, + Animated, + StatusBar, + Dimensions, +} from 'react-native'; +import { SafeAreaView } from 'react-native-safe-area-context'; +import { useRouter } from 'expo-router'; +import { useAppColors, useResolvedColorScheme, type AppColors } from '../../theme'; +import * as hrefs from '../../navigation/hrefs'; + +const { width, height } = Dimensions.get('window'); + +export const WelcomeScreen: React.FC = () => { + const router = useRouter(); + const colors = useAppColors(); + const resolved = useResolvedColorScheme(); + const styles = useMemo(() => createWelcomeStyles(colors), [colors]); + const isDark = resolved === 'dark'; + + // 动画值 + const fadeAnim = useRef(new Animated.Value(0)).current; + const slideUpAnim = useRef(new Animated.Value(50)).current; + const scaleAnim = useRef(new Animated.Value(0.9)).current; + + useEffect(() => { + // 启动入场动画 + Animated.parallel([ + Animated.timing(fadeAnim, { + toValue: 1, + duration: 800, + useNativeDriver: true, + }), + Animated.timing(slideUpAnim, { + toValue: 0, + duration: 800, + useNativeDriver: true, + }), + Animated.timing(scaleAnim, { + toValue: 1, + duration: 800, + useNativeDriver: true, + }), + ]).start(); + }, []); + + const handleSignIn = () => { + router.push(hrefs.hrefAuthLogin()); + }; + + const handleCreateAccount = () => { + router.push(hrefs.hrefAuthRegister()); + }; + + return ( + + + + {/* 装饰性背景元素 */} + + + + + + + {/* 品牌区域 */} + + Welcome To + 萝卜社区 + + + + 发现有趣的内容{'\n'} + 结识志同道合的朋友 + + + + {/* 按钮区域 */} + + + 登录 + + + + 创建账号 + + + + + + ); +}; + +function createWelcomeStyles(colors: AppColors) { + return StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.background.default, + }, + safeArea: { + flex: 1, + }, + content: { + flex: 1, + justifyContent: 'space-between', + paddingHorizontal: 32, + paddingTop: 60, + paddingBottom: 40, + }, + + // 装饰性背景元素 - 使用胡萝卜橙的淡色 + decorCircle1: { + position: 'absolute', + top: -100, + right: -100, + width: 400, + height: 400, + borderRadius: 200, + backgroundColor: colors.primary.main + '08', // 非常淡的胡萝卜橙 + }, + decorCircle2: { + position: 'absolute', + top: height * 0.3, + left: -80, + width: 200, + height: 200, + borderRadius: 100, + backgroundColor: colors.primary.main + '10', + }, + decorCircle3: { + position: 'absolute', + bottom: -50, + right: -50, + width: 250, + height: 250, + borderRadius: 125, + backgroundColor: colors.primary.main + '08', + }, + + // 品牌区域 + brandSection: { + alignItems: 'center', + }, + badge: { + backgroundColor: colors.primary.main + '15', + paddingHorizontal: 20, + paddingVertical: 8, + borderRadius: 20, + marginBottom: 24, + }, + badgeText: { + color: colors.primary.main, + fontSize: 14, + fontWeight: '600', + letterSpacing: 1, + }, + welcomeEn: { + fontSize: 24, + fontWeight: '400', + color: colors.text.secondary, + marginBottom: 4, + letterSpacing: 1, + }, + brandName: { + fontSize: 42, + fontWeight: '800', + color: colors.text.primary, + letterSpacing: 4, + marginBottom: 8, + }, + brandUnderline: { + width: 80, + height: 4, + backgroundColor: colors.primary.main, + borderRadius: 2, + marginBottom: 24, + }, + title: { + fontSize: 48, + fontWeight: '800', + color: colors.text.primary, + marginBottom: 16, + letterSpacing: 2, + }, + subtitle: { + fontSize: 16, + color: colors.text.secondary, + textAlign: 'center', + lineHeight: 24, + letterSpacing: 0.5, + }, + + // 按钮区域 + buttonSection: { + width: '100%', + gap: 16, + }, + signInButton: { + backgroundColor: colors.primary.main, + borderRadius: 16, + height: 56, + alignItems: 'center', + justifyContent: 'center', + shadowColor: colors.primary.main, + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 8, + elevation: 5, + }, + signInButtonText: { + color: colors.primary.contrast, + fontSize: 16, + fontWeight: '700', + letterSpacing: 1, + }, + createAccountButton: { + backgroundColor: 'transparent', + borderRadius: 16, + height: 56, + alignItems: 'center', + justifyContent: 'center', + borderWidth: 2, + borderColor: colors.primary.main, + }, + createAccountButtonText: { + color: colors.primary.main, + fontSize: 16, + fontWeight: '700', + letterSpacing: 1, + }, + }); +} + +export default WelcomeScreen; diff --git a/src/screens/auth/index.ts b/src/screens/auth/index.ts index f74a9f0..620897d 100644 --- a/src/screens/auth/index.ts +++ b/src/screens/auth/index.ts @@ -6,3 +6,4 @@ export { LoginScreen } from './LoginScreen'; export { RegisterScreen } from './RegisterScreen'; export { ForgotPasswordScreen } from './ForgotPasswordScreen'; +export { WelcomeScreen } from './WelcomeScreen';