PC端的部分适配
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* 登录页 LoginScreen(响应式适配)
|
||||
* 登录页 LoginScreen(响应式适配 - 分栏布局)
|
||||
* 胡萝卜BBS - 用户登录
|
||||
* 现代化设计 - 渐变背景 + 动画效果
|
||||
* 登录表单在桌面端居中显示,限制最大宽度
|
||||
* 支持横屏模式下的布局调整
|
||||
*
|
||||
* 布局规则:
|
||||
* - 屏幕宽度 < 768px:单栏布局,橙色渐变背景
|
||||
* - 屏幕宽度 >= 768px:分栏布局,左侧橙色右侧中性灰
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
ScrollView,
|
||||
ActivityIndicator,
|
||||
Animated,
|
||||
StatusBar,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
@@ -28,11 +30,13 @@ import { colors, spacing, borderRadius, shadows, fontSizes } from '../../theme';
|
||||
import { useAuthStore } from '../../stores';
|
||||
import { RootStackParamList } from '../../navigation/types';
|
||||
import { useResponsive, useResponsiveValue } from '../../hooks';
|
||||
import { ResponsiveContainer } from '../../components/common';
|
||||
import { showPrompt } from '../../services/promptService';
|
||||
|
||||
type LoginNavigationProp = NativeStackNavigationProp<RootStackParamList, 'Auth'>;
|
||||
|
||||
// 分栏布局断点
|
||||
const SPLIT_BREAKPOINT = 768;
|
||||
|
||||
export const LoginScreen: React.FC = () => {
|
||||
const navigation = useNavigation<LoginNavigationProp>();
|
||||
const login = useAuthStore((state) => state.login);
|
||||
@@ -40,7 +44,10 @@ export const LoginScreen: React.FC = () => {
|
||||
const setStoreError = useAuthStore((state) => state.setError);
|
||||
|
||||
// 响应式布局
|
||||
const { isWideScreen, isLandscape, isDesktop, width } = useResponsive();
|
||||
const { isLandscape, width } = useResponsive();
|
||||
|
||||
// 是否使用分栏布局
|
||||
const isSplitLayout = width >= SPLIT_BREAKPOINT;
|
||||
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
@@ -54,11 +61,23 @@ export const LoginScreen: React.FC = () => {
|
||||
const scaleAnim = useRef(new Animated.Value(0.9)).current;
|
||||
const inputFadeAnim = useRef(new Animated.Value(0)).current;
|
||||
|
||||
// 响应式值
|
||||
const logoSize = useResponsiveValue({ xs: 64, sm: 72, md: 80, lg: 96, xl: 110 });
|
||||
const logoContainerSize = useResponsiveValue({ xs: 110, sm: 120, md: 130, lg: 150, xl: 170 });
|
||||
const appNameSize = useResponsiveValue({ xs: 28, sm: 30, md: 32, lg: 36, xl: 40 });
|
||||
const formMaxWidth = isDesktop ? 480 : isWideScreen ? 420 : undefined;
|
||||
// 响应式值 - 大屏模式单独放大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;
|
||||
|
||||
// 启动入场动画
|
||||
useEffect(() => {
|
||||
@@ -105,7 +124,7 @@ export const LoginScreen: React.FC = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
// store error 同步到本地,方便在输入时清除
|
||||
// store error 同步到本地
|
||||
useEffect(() => {
|
||||
if (storeError) {
|
||||
setErrorMsg(storeError);
|
||||
@@ -126,8 +145,6 @@ export const LoginScreen: React.FC = () => {
|
||||
try {
|
||||
const success = await login({ username, password });
|
||||
if (!success) {
|
||||
// authStore 已将错误写入 storeError,useEffect 会同步到 errorMsg
|
||||
// 若 storeError 为空则显示通用提示
|
||||
if (!useAuthStore.getState().error) {
|
||||
setErrorMsg('登录失败,请稍后重试');
|
||||
}
|
||||
@@ -144,39 +161,66 @@ export const LoginScreen: React.FC = () => {
|
||||
navigation.navigate('Register' as any);
|
||||
};
|
||||
|
||||
// 渲染左侧面板(Logo和品牌信息)- 优化大屏布局
|
||||
const renderLeftPanel = () => (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.leftPanel,
|
||||
isSplitLayout && styles.splitLeftPanel, // 大屏模式专用布局
|
||||
{
|
||||
opacity: fadeAnim,
|
||||
transform: [{ scale: scaleAnim }],
|
||||
},
|
||||
]}
|
||||
>
|
||||
{/* 胡萝卜图标 - 靠上显示 */}
|
||||
<View style={[
|
||||
styles.logoContainer,
|
||||
styles.logoContainerTop,
|
||||
isSplitLayout && styles.splitLogoContainer, // 大屏模式Logo容器样式
|
||||
{ width: logoContainerSize, height: logoContainerSize, borderRadius: logoContainerSize / 2 }
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="carrot"
|
||||
size={logoSize}
|
||||
color="#FFF"
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 品牌名称 - 优化大屏排版 */}
|
||||
<Text style={[styles.appName, isSplitLayout && styles.splitAppName]}>胡萝卜</Text>
|
||||
<Text style={[styles.subtitle, isSplitLayout && styles.splitSubtitle]}>发现有趣的内容,结识志同道合的朋友</Text>
|
||||
|
||||
{/* 装饰元素 - 大屏模式改为水平排列 + 大幅放大 */}
|
||||
<View style={[styles.leftPanelDecorBottom, isSplitLayout && styles.splitLeftPanelDecorBottom]}>
|
||||
{isSplitLayout ? (
|
||||
// 大屏模式:水平排列 + 大幅放大图标
|
||||
<View style={styles.splitDecorRow}>
|
||||
<MaterialCommunityIcons name="chat-outline" size={80} color="rgba(255,255,255,0.4)" style={styles.splitDecorIcon} />
|
||||
<MaterialCommunityIcons name="account-group-outline" size={90} color="rgba(255,255,255,0.45)" style={styles.splitDecorIcon} />
|
||||
<MaterialCommunityIcons name="bell-outline" size={85} color="rgba(255,255,255,0.35)" style={styles.splitDecorIcon} />
|
||||
</View>
|
||||
) : (
|
||||
// 小屏模式:保持原有横向排列
|
||||
<View style={styles.decorRow}>
|
||||
<MaterialCommunityIcons name="chat-outline" size={48} color="rgba(255,255,255,0.35)" />
|
||||
<MaterialCommunityIcons name="account-group-outline" size={44} color="rgba(255,255,255,0.3)" />
|
||||
<MaterialCommunityIcons name="bell-outline" size={40} color="rgba(255,255,255,0.25)" />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
|
||||
// 渲染表单内容
|
||||
const renderFormContent = () => (
|
||||
<>
|
||||
{/* Logo和标题区域 */}
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.headerSection,
|
||||
isLandscape && styles.headerSectionLandscape,
|
||||
{
|
||||
opacity: fadeAnim,
|
||||
transform: [{ scale: scaleAnim }],
|
||||
},
|
||||
]}
|
||||
>
|
||||
<View style={[
|
||||
styles.logoContainer,
|
||||
{ width: logoContainerSize, height: logoContainerSize, borderRadius: logoContainerSize / 2 }
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="carrot"
|
||||
size={logoSize}
|
||||
color="#FFF"
|
||||
/>
|
||||
</View>
|
||||
<Text style={[styles.appName, { fontSize: appNameSize }]}>胡萝卜</Text>
|
||||
<Text style={styles.subtitle}>发现有趣的内容,结识志同道合的朋友</Text>
|
||||
</Animated.View>
|
||||
|
||||
{/* 表单卡片 */}
|
||||
<View style={isSplitLayout ? styles.splitFormWrapper : styles.singleFormWrapper}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.formCard,
|
||||
formMaxWidth && { maxWidth: formMaxWidth, alignSelf: 'center', width: '100%' },
|
||||
// 核心修改:大屏模式使用专用的阴影样式,小屏用原有阴影
|
||||
isSplitLayout ? styles.splitFormCardShadow : styles.normalFormCardShadow,
|
||||
formMaxWidth && { maxWidth: formMaxWidth, width: '100%' },
|
||||
{
|
||||
opacity: inputFadeAnim,
|
||||
transform: [{ translateY: slideAnim }],
|
||||
@@ -187,21 +231,15 @@ export const LoginScreen: React.FC = () => {
|
||||
|
||||
{/* 用户名输入框 */}
|
||||
<View style={styles.inputContainer}>
|
||||
<View style={[
|
||||
styles.inputWrapper,
|
||||
isWideScreen && styles.inputWrapperWide,
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="account-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
<View style={styles.inputWrapper}>
|
||||
<MaterialCommunityIcons
|
||||
name="account-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
style={styles.inputIcon}
|
||||
/>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.input,
|
||||
isWideScreen && styles.inputWide,
|
||||
]}
|
||||
style={styles.input}
|
||||
placeholder="用户名 / 邮箱 / 手机号"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={username}
|
||||
@@ -227,21 +265,15 @@ export const LoginScreen: React.FC = () => {
|
||||
|
||||
{/* 密码输入框 */}
|
||||
<View style={styles.inputContainer}>
|
||||
<View style={[
|
||||
styles.inputWrapper,
|
||||
isWideScreen && styles.inputWrapperWide,
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="lock-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
<View style={styles.inputWrapper}>
|
||||
<MaterialCommunityIcons
|
||||
name="lock-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
style={styles.inputIcon}
|
||||
/>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.input,
|
||||
isWideScreen && styles.inputWide,
|
||||
]}
|
||||
style={styles.input}
|
||||
placeholder="请输入密码"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={password}
|
||||
@@ -275,7 +307,7 @@ export const LoginScreen: React.FC = () => {
|
||||
<MaterialCommunityIcons
|
||||
name="alert-circle-outline"
|
||||
size={16}
|
||||
color={colors.error?.main ?? '#D32F2F'}
|
||||
color={colors.error.dark}
|
||||
style={{ marginRight: 6, marginTop: 1 }}
|
||||
/>
|
||||
<Text style={styles.errorText}>{errorMsg}</Text>
|
||||
@@ -290,13 +322,10 @@ export const LoginScreen: React.FC = () => {
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={['#FF6B35', '#FF8F66']}
|
||||
colors={[colors.primary.main, colors.primary.light]}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={[
|
||||
styles.loginButtonGradient,
|
||||
isWideScreen && styles.loginButtonGradientWide,
|
||||
]}
|
||||
style={styles.loginButtonGradient}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator size="small" color="#FFF" />
|
||||
@@ -319,13 +348,48 @@ export const LoginScreen: React.FC = () => {
|
||||
<Text style={styles.registerLink}>立即注册</Text>
|
||||
</TouchableOpacity>
|
||||
</Animated.View>
|
||||
</>
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
// 渲染分栏布局
|
||||
const renderSplitLayout = () => (
|
||||
<View style={styles.splitContainer}>
|
||||
{/* 左侧橙色面板 */}
|
||||
<LinearGradient
|
||||
colors={['#FF6B35', '#FF8F66', '#FFB088']}
|
||||
colors={[colors.primary.main, colors.primary.light, '#FFB088']}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
style={styles.leftPanelContainer}
|
||||
>
|
||||
{/* 装饰性背景元素 */}
|
||||
<View style={styles.decorCircle1} />
|
||||
<View style={styles.decorCircle2} />
|
||||
|
||||
<StatusBar barStyle="light-content" />
|
||||
<SafeAreaView style={styles.leftPanelSafeArea}>
|
||||
{renderLeftPanel()}
|
||||
</SafeAreaView>
|
||||
</LinearGradient>
|
||||
|
||||
{/* 右侧中性灰面板 */}
|
||||
<View style={styles.rightPanelContainer}>
|
||||
<StatusBar barStyle="dark-content" />
|
||||
<SafeAreaView style={styles.rightPanelSafeArea}>
|
||||
{/* 核心修复:用View包裹,确保垂直+水平居中 */}
|
||||
<View style={styles.splitRightContentWrapper}>
|
||||
{renderFormContent()}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
// 渲染单栏布局
|
||||
const renderSingleLayout = () => (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar barStyle="light-content" />
|
||||
<LinearGradient
|
||||
colors={[colors.primary.main, colors.primary.light, '#FFB088']}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
style={styles.gradient}
|
||||
@@ -334,43 +398,52 @@ export const LoginScreen: React.FC = () => {
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
style={styles.keyboardView}
|
||||
>
|
||||
{isWideScreen ? (
|
||||
<ResponsiveContainer maxWidth={600}>
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
styles.scrollContent,
|
||||
isLandscape && styles.scrollContentLandscape,
|
||||
]}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
{/* 装饰性背景元素 */}
|
||||
<View style={styles.decorCircle1} />
|
||||
<View style={styles.decorCircle2} />
|
||||
|
||||
{renderFormContent()}
|
||||
</ScrollView>
|
||||
</ResponsiveContainer>
|
||||
) : (
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
styles.scrollContent,
|
||||
isLandscape && styles.scrollContentLandscape,
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
styles.scrollContent,
|
||||
isLandscape && styles.scrollContentLandscape,
|
||||
]}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
{/* 装饰性背景元素 */}
|
||||
<View style={styles.decorCircle1} />
|
||||
<View style={styles.decorCircle2} />
|
||||
|
||||
{/* 头部 */}
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.headerSection,
|
||||
isLandscape && styles.headerSectionLandscape,
|
||||
{
|
||||
opacity: fadeAnim,
|
||||
transform: [{ scale: scaleAnim }],
|
||||
},
|
||||
]}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
{/* 装饰性背景元素 */}
|
||||
<View style={styles.decorCircle1} />
|
||||
<View style={styles.decorCircle2} />
|
||||
|
||||
{renderFormContent()}
|
||||
</ScrollView>
|
||||
)}
|
||||
<View style={[
|
||||
styles.logoContainer,
|
||||
{ width: logoContainerSize, height: logoContainerSize, borderRadius: logoContainerSize / 2 }
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="carrot"
|
||||
size={logoSize}
|
||||
color="#FFF"
|
||||
/>
|
||||
</View>
|
||||
<Text style={[styles.appName, { fontSize: appNameSize }]}>胡萝卜</Text>
|
||||
<Text style={styles.subtitle}>发现有趣的内容,结识志同道合的朋友</Text>
|
||||
</Animated.View>
|
||||
|
||||
{/* 表单 */}
|
||||
{renderFormContent()}
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
</LinearGradient>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
||||
return isSplitLayout ? renderSplitLayout() : renderSingleLayout();
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -383,11 +456,17 @@ const styles = StyleSheet.create({
|
||||
keyboardView: {
|
||||
flex: 1,
|
||||
},
|
||||
// 单栏布局内容
|
||||
scrollContent: {
|
||||
flexGrow: 1,
|
||||
paddingHorizontal: spacing.xl,
|
||||
paddingVertical: spacing['2xl'],
|
||||
},
|
||||
scrollContentCentered: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flex: 1,
|
||||
},
|
||||
scrollContentLandscape: {
|
||||
paddingVertical: spacing.lg,
|
||||
},
|
||||
@@ -429,6 +508,15 @@ const styles = StyleSheet.create({
|
||||
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',
|
||||
@@ -437,20 +525,54 @@ const styles = StyleSheet.create({
|
||||
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',
|
||||
@@ -471,9 +593,6 @@ const styles = StyleSheet.create({
|
||||
paddingHorizontal: spacing.md,
|
||||
height: 56,
|
||||
},
|
||||
inputWrapperWide: {
|
||||
height: 60,
|
||||
},
|
||||
inputIcon: {
|
||||
marginRight: spacing.sm,
|
||||
},
|
||||
@@ -482,9 +601,6 @@ const styles = StyleSheet.create({
|
||||
fontSize: fontSizes.md,
|
||||
color: colors.text.primary,
|
||||
},
|
||||
inputWide: {
|
||||
fontSize: fontSizes.lg,
|
||||
},
|
||||
clearButton: {
|
||||
padding: spacing.xs,
|
||||
},
|
||||
@@ -495,23 +611,23 @@ const styles = StyleSheet.create({
|
||||
forgotPasswordText: {
|
||||
fontSize: fontSizes.sm,
|
||||
color: colors.primary.main,
|
||||
fontWeight: '500',
|
||||
fontWeight: '600',
|
||||
},
|
||||
errorBox: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
backgroundColor: '#FFEBEE',
|
||||
backgroundColor: colors.accent.light,
|
||||
borderRadius: borderRadius.md,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
marginBottom: spacing.md,
|
||||
borderLeftWidth: 3,
|
||||
borderLeftColor: '#D32F2F',
|
||||
borderLeftColor: colors.accent.main,
|
||||
},
|
||||
errorText: {
|
||||
flex: 1,
|
||||
fontSize: fontSizes.sm,
|
||||
color: '#D32F2F',
|
||||
color: colors.accent.dark,
|
||||
lineHeight: 20,
|
||||
},
|
||||
loginButton: {
|
||||
@@ -524,9 +640,6 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
loginButtonGradientWide: {
|
||||
height: 56,
|
||||
},
|
||||
loginButtonDisabled: {
|
||||
opacity: 0.7,
|
||||
},
|
||||
@@ -540,20 +653,108 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: 'auto',
|
||||
paddingTop: spacing.xl,
|
||||
},
|
||||
footerText: {
|
||||
fontSize: fontSizes.md,
|
||||
color: 'rgba(255,255,255,0.9)',
|
||||
color: colors.text.primary,
|
||||
},
|
||||
registerLink: {
|
||||
fontSize: fontSizes.md,
|
||||
color: '#FFFFFF',
|
||||
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%',
|
||||
// 移除这里的阴影,避免重复
|
||||
},
|
||||
});
|
||||
|
||||
export default LoginScreen;
|
||||
export default LoginScreen;
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* 注册页 RegisterScreen(响应式适配)
|
||||
* 注册页 RegisterScreen(响应式适配 - 分栏布局)
|
||||
* 胡萝卜BBS - 用户注册
|
||||
* 现代化设计 - 渐变背景 + 动画效果
|
||||
* 注册表单在桌面端居中显示,限制最大宽度
|
||||
* 支持横屏模式下的布局调整
|
||||
*
|
||||
* 布局规则:
|
||||
* - 屏幕宽度 < 768px:单栏布局,橙色渐变背景
|
||||
* - 屏幕宽度 >= 768px:分栏布局,左侧橙色右侧中性灰
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
ScrollView,
|
||||
ActivityIndicator,
|
||||
Animated,
|
||||
StatusBar,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
@@ -29,17 +31,22 @@ import { authService, resolveAuthApiError } from '../../services/authService';
|
||||
import { useAuthStore } from '../../stores';
|
||||
import { RootStackParamList } from '../../navigation/types';
|
||||
import { useResponsive, useResponsiveValue } from '../../hooks';
|
||||
import { ResponsiveContainer } from '../../components/common';
|
||||
import { showPrompt } from '../../services/promptService';
|
||||
|
||||
type RegisterNavigationProp = NativeStackNavigationProp<RootStackParamList, 'Auth'>;
|
||||
|
||||
// 分栏布局断点
|
||||
const SPLIT_BREAKPOINT = 768;
|
||||
|
||||
export const RegisterScreen: React.FC = () => {
|
||||
const navigation = useNavigation<RegisterNavigationProp>();
|
||||
const register = useAuthStore((state) => state.register);
|
||||
|
||||
// 响应式布局
|
||||
const { isWideScreen, isLandscape, isDesktop } = useResponsive();
|
||||
const { isLandscape, width } = useResponsive();
|
||||
|
||||
// 是否使用分栏布局
|
||||
const isSplitLayout = width >= SPLIT_BREAKPOINT;
|
||||
|
||||
const [username, setUsername] = useState('');
|
||||
const [nickname, setNickname] = useState('');
|
||||
@@ -61,11 +68,23 @@ export const RegisterScreen: React.FC = () => {
|
||||
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: 64, xl: 72 });
|
||||
const iconContainerSize = useResponsiveValue({ xs: 80, sm: 88, md: 96, lg: 108, xl: 120 });
|
||||
const titleSize = useResponsiveValue({ xs: 24, sm: 26, md: 28, lg: 30, xl: 32 });
|
||||
const formMaxWidth = isDesktop ? 520 : isWideScreen ? 480 : undefined;
|
||||
// 响应式值 - 大屏模式单独放大图标尺寸,更显大气
|
||||
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;
|
||||
|
||||
// 启动入场动画
|
||||
useEffect(() => {
|
||||
@@ -219,64 +238,93 @@ export const RegisterScreen: React.FC = () => {
|
||||
|
||||
// 跳转到登录页
|
||||
const handleGoToLogin = () => {
|
||||
navigation.goBack();
|
||||
navigation.navigate('Login' as any);
|
||||
};
|
||||
|
||||
// 渲染左侧面板(Logo和品牌信息)- 优化大屏布局
|
||||
const renderLeftPanel = () => (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.leftPanel,
|
||||
isSplitLayout && styles.splitLeftPanel, // 大屏模式专用布局
|
||||
{
|
||||
opacity: fadeAnim,
|
||||
transform: [{ scale: scaleAnim }],
|
||||
},
|
||||
]}
|
||||
>
|
||||
{/* 用户图标 - 靠上显示 */}
|
||||
<View style={[
|
||||
styles.iconContainer,
|
||||
styles.iconContainerTop,
|
||||
isSplitLayout && styles.splitIconContainer, // 大屏模式图标容器样式
|
||||
{ width: iconContainerSize, height: iconContainerSize, borderRadius: iconContainerSize / 2 }
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="account-plus"
|
||||
size={iconSize}
|
||||
color="#FFF"
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 品牌名称 - 优化大屏排版 */}
|
||||
<Text style={[styles.title, isSplitLayout && styles.splitTitle]}>创建账号</Text>
|
||||
<Text style={[styles.subtitle, isSplitLayout && styles.splitSubtitle]}>加入胡萝卜,开始你的旅程</Text>
|
||||
|
||||
{/* 装饰元素 - 大屏模式改为水平排列 + 大幅放大 */}
|
||||
<View style={[styles.leftPanelDecorBottom, isSplitLayout && styles.splitLeftPanelDecorBottom]}>
|
||||
{isSplitLayout ? (
|
||||
// 大屏模式:水平排列 + 大幅放大图标
|
||||
<View style={styles.splitDecorRow}>
|
||||
<MaterialCommunityIcons name="account-plus-outline" size={80} color="rgba(255,255,255,0.4)" style={styles.splitDecorIcon} />
|
||||
<MaterialCommunityIcons name="email-check-outline" size={90} color="rgba(255,255,255,0.45)" style={styles.splitDecorIcon} />
|
||||
<MaterialCommunityIcons name="shield-check-outline" size={85} color="rgba(255,255,255,0.35)" style={styles.splitDecorIcon} />
|
||||
</View>
|
||||
) : (
|
||||
// 小屏模式:保持原有横向排列
|
||||
<View style={styles.decorRow}>
|
||||
<MaterialCommunityIcons name="account-plus-outline" size={48} color="rgba(255,255,255,0.35)" />
|
||||
<MaterialCommunityIcons name="email-check-outline" size={44} color="rgba(255,255,255,0.3)" />
|
||||
<MaterialCommunityIcons name="shield-check-outline" size={40} color="rgba(255,255,255,0.25)" />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
|
||||
// 渲染表单内容
|
||||
const renderFormContent = () => (
|
||||
<>
|
||||
{/* 标题区域 */}
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.headerSection,
|
||||
isLandscape && styles.headerSectionLandscape,
|
||||
{
|
||||
opacity: fadeAnim,
|
||||
transform: [{ scale: scaleAnim }],
|
||||
},
|
||||
]}
|
||||
>
|
||||
<View style={[
|
||||
styles.iconContainer,
|
||||
{ width: iconContainerSize, height: iconContainerSize, borderRadius: iconContainerSize / 2 }
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="account-plus"
|
||||
size={iconSize}
|
||||
color="#FFF"
|
||||
/>
|
||||
</View>
|
||||
<Text style={[styles.title, { fontSize: titleSize }]}>创建账号</Text>
|
||||
<Text style={styles.subtitle}>加入胡萝卜,开始你的旅程</Text>
|
||||
</Animated.View>
|
||||
|
||||
{/* 表单卡片 */}
|
||||
<Animated.View
|
||||
<View style={isSplitLayout ? styles.splitFormWrapper : styles.singleFormWrapper}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.formCard,
|
||||
formMaxWidth && { maxWidth: formMaxWidth, alignSelf: 'center', width: '100%' },
|
||||
// 核心修改:大屏模式使用专用的阴影样式,小屏用原有阴影
|
||||
isSplitLayout ? styles.splitFormCardShadow : styles.normalFormCardShadow,
|
||||
formMaxWidth && { maxWidth: formMaxWidth, width: '100%' },
|
||||
{
|
||||
opacity: inputFadeAnim,
|
||||
transform: [{ translateY: slideAnim }],
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text style={styles.formTitle}>注册账号</Text>
|
||||
|
||||
{/* 用户名输入框 */}
|
||||
<View style={styles.inputContainer}>
|
||||
<View style={[
|
||||
styles.inputWrapper,
|
||||
isWideScreen && styles.inputWrapperWide,
|
||||
isSplitLayout && styles.inputWrapperWide,
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="account-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
<MaterialCommunityIcons
|
||||
name="account-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
style={styles.inputIcon}
|
||||
/>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.input,
|
||||
isWideScreen && styles.inputWide,
|
||||
isSplitLayout && styles.inputWide,
|
||||
]}
|
||||
placeholder="用户名(3-20个字符)"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
@@ -305,18 +353,18 @@ export const RegisterScreen: React.FC = () => {
|
||||
<View style={styles.inputContainer}>
|
||||
<View style={[
|
||||
styles.inputWrapper,
|
||||
isWideScreen && styles.inputWrapperWide,
|
||||
isSplitLayout && styles.inputWrapperWide,
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="emoticon-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
<MaterialCommunityIcons
|
||||
name="emoticon-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
style={styles.inputIcon}
|
||||
/>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.input,
|
||||
isWideScreen && styles.inputWide,
|
||||
isSplitLayout && styles.inputWide,
|
||||
]}
|
||||
placeholder="昵称(2-20个字符)"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
@@ -343,18 +391,18 @@ export const RegisterScreen: React.FC = () => {
|
||||
<View style={styles.inputContainer}>
|
||||
<View style={[
|
||||
styles.inputWrapper,
|
||||
isWideScreen && styles.inputWrapperWide,
|
||||
isSplitLayout && styles.inputWrapperWide,
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="email-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
<MaterialCommunityIcons
|
||||
name="email-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
style={styles.inputIcon}
|
||||
/>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.input,
|
||||
isWideScreen && styles.inputWide,
|
||||
isSplitLayout && styles.inputWide,
|
||||
]}
|
||||
placeholder="邮箱(必填)"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
@@ -382,7 +430,7 @@ export const RegisterScreen: React.FC = () => {
|
||||
{/* 邮箱验证码 */}
|
||||
<View style={styles.inputContainer}>
|
||||
<View style={styles.codeRow}>
|
||||
<View style={[styles.inputWrapper, styles.codeInputWrapper, isWideScreen && styles.inputWrapperWide]}>
|
||||
<View style={[styles.inputWrapper, styles.codeInputWrapper, isSplitLayout && styles.inputWrapperWide]}>
|
||||
<MaterialCommunityIcons
|
||||
name="shield-key-outline"
|
||||
size={22}
|
||||
@@ -390,7 +438,7 @@ export const RegisterScreen: React.FC = () => {
|
||||
style={styles.inputIcon}
|
||||
/>
|
||||
<TextInput
|
||||
style={[styles.input, isWideScreen && styles.inputWide]}
|
||||
style={[styles.input, isSplitLayout && styles.inputWide]}
|
||||
placeholder="邮箱验证码"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={verificationCode}
|
||||
@@ -419,18 +467,18 @@ export const RegisterScreen: React.FC = () => {
|
||||
<View style={styles.inputContainer}>
|
||||
<View style={[
|
||||
styles.inputWrapper,
|
||||
isWideScreen && styles.inputWrapperWide,
|
||||
isSplitLayout && styles.inputWrapperWide,
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="phone-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
<MaterialCommunityIcons
|
||||
name="phone-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
style={styles.inputIcon}
|
||||
/>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.input,
|
||||
isWideScreen && styles.inputWide,
|
||||
isSplitLayout && styles.inputWide,
|
||||
]}
|
||||
placeholder="手机号(选填)"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
@@ -458,18 +506,18 @@ export const RegisterScreen: React.FC = () => {
|
||||
<View style={styles.inputContainer}>
|
||||
<View style={[
|
||||
styles.inputWrapper,
|
||||
isWideScreen && styles.inputWrapperWide,
|
||||
isSplitLayout && styles.inputWrapperWide,
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="lock-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
<MaterialCommunityIcons
|
||||
name="lock-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
style={styles.inputIcon}
|
||||
/>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.input,
|
||||
isWideScreen && styles.inputWide,
|
||||
isSplitLayout && styles.inputWide,
|
||||
]}
|
||||
placeholder="密码(至少6位)"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
@@ -495,18 +543,18 @@ export const RegisterScreen: React.FC = () => {
|
||||
<View style={styles.inputContainer}>
|
||||
<View style={[
|
||||
styles.inputWrapper,
|
||||
isWideScreen && styles.inputWrapperWide,
|
||||
isSplitLayout && styles.inputWrapperWide,
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="lock-check-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
<MaterialCommunityIcons
|
||||
name="lock-check-outline"
|
||||
size={22}
|
||||
color={colors.primary.main}
|
||||
style={styles.inputIcon}
|
||||
/>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.input,
|
||||
isWideScreen && styles.inputWide,
|
||||
isSplitLayout && styles.inputWide,
|
||||
]}
|
||||
placeholder="确认密码"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
@@ -559,12 +607,12 @@ export const RegisterScreen: React.FC = () => {
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={['#FF6B35', '#FF8F66']}
|
||||
colors={[colors.primary.main, colors.primary.light]}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={[
|
||||
styles.registerButtonGradient,
|
||||
isWideScreen && styles.registerButtonGradientWide,
|
||||
isSplitLayout && styles.registerButtonGradientWide,
|
||||
]}
|
||||
>
|
||||
{loading ? (
|
||||
@@ -577,24 +625,60 @@ export const RegisterScreen: React.FC = () => {
|
||||
</Animated.View>
|
||||
|
||||
{/* 底部登录提示 */}
|
||||
<Animated.View
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.footerSection,
|
||||
isSplitLayout && styles.splitFooterSection,
|
||||
{ opacity: inputFadeAnim },
|
||||
]}
|
||||
>
|
||||
<Text style={styles.footerText}>已有账号?</Text>
|
||||
<Text style={[styles.footerText, isSplitLayout && styles.splitFooterText]}>已有账号?</Text>
|
||||
<TouchableOpacity onPress={handleGoToLogin}>
|
||||
<Text style={styles.loginLink}>立即登录</Text>
|
||||
<Text style={[styles.loginLink, isSplitLayout && styles.splitLoginLink]}>立即登录</Text>
|
||||
</TouchableOpacity>
|
||||
</Animated.View>
|
||||
</>
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
// 渲染分栏布局
|
||||
const renderSplitLayout = () => (
|
||||
<View style={styles.splitContainer}>
|
||||
{/* 左侧橙色面板 */}
|
||||
<LinearGradient
|
||||
colors={['#FF6B35', '#FF8F66', '#FFB088']}
|
||||
colors={[colors.primary.main, colors.primary.light, '#FFB088']}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
style={styles.leftPanelContainer}
|
||||
>
|
||||
{/* 装饰性背景元素 */}
|
||||
<View style={styles.decorCircle1} />
|
||||
<View style={styles.decorCircle2} />
|
||||
|
||||
<StatusBar barStyle="light-content" />
|
||||
<SafeAreaView style={styles.leftPanelSafeArea}>
|
||||
{renderLeftPanel()}
|
||||
</SafeAreaView>
|
||||
</LinearGradient>
|
||||
|
||||
{/* 右侧中性灰面板 */}
|
||||
<View style={styles.rightPanelContainer}>
|
||||
<StatusBar barStyle="dark-content" />
|
||||
<SafeAreaView style={styles.rightPanelSafeArea}>
|
||||
{/* 核心修复:用View包裹,确保垂直+水平居中 */}
|
||||
<View style={styles.splitRightContentWrapper}>
|
||||
{renderFormContent()}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
// 渲染单栏布局
|
||||
const renderSingleLayout = () => (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar barStyle="light-content" />
|
||||
<LinearGradient
|
||||
colors={[colors.primary.main, colors.primary.light, '#FFB088']}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
style={styles.gradient}
|
||||
@@ -603,43 +687,52 @@ export const RegisterScreen: React.FC = () => {
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
style={styles.keyboardView}
|
||||
>
|
||||
{isWideScreen ? (
|
||||
<ResponsiveContainer maxWidth={600}>
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
styles.scrollContent,
|
||||
isLandscape && styles.scrollContentLandscape,
|
||||
]}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
{/* 装饰性背景元素 */}
|
||||
<View style={styles.decorCircle1} />
|
||||
<View style={styles.decorCircle2} />
|
||||
|
||||
{renderFormContent()}
|
||||
</ScrollView>
|
||||
</ResponsiveContainer>
|
||||
) : (
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
styles.scrollContent,
|
||||
isLandscape && styles.scrollContentLandscape,
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
styles.scrollContent,
|
||||
isLandscape && styles.scrollContentLandscape,
|
||||
]}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
{/* 装饰性背景元素 */}
|
||||
<View style={styles.decorCircle1} />
|
||||
<View style={styles.decorCircle2} />
|
||||
|
||||
{/* 头部 */}
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.headerSection,
|
||||
isLandscape && styles.headerSectionLandscape,
|
||||
{
|
||||
opacity: fadeAnim,
|
||||
transform: [{ scale: scaleAnim }],
|
||||
},
|
||||
]}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
{/* 装饰性背景元素 */}
|
||||
<View style={styles.decorCircle1} />
|
||||
<View style={styles.decorCircle2} />
|
||||
|
||||
{renderFormContent()}
|
||||
</ScrollView>
|
||||
)}
|
||||
<View style={[
|
||||
styles.iconContainer,
|
||||
{ width: iconContainerSize, height: iconContainerSize, borderRadius: iconContainerSize / 2 }
|
||||
]}>
|
||||
<MaterialCommunityIcons
|
||||
name="account-plus"
|
||||
size={iconSize}
|
||||
color="#FFF"
|
||||
/>
|
||||
</View>
|
||||
<Text style={[styles.title, { fontSize: titleSize }]}>创建账号</Text>
|
||||
<Text style={styles.subtitle}>加入胡萝卜,开始你的旅程</Text>
|
||||
</Animated.View>
|
||||
|
||||
{/* 表单 */}
|
||||
{renderFormContent()}
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
</LinearGradient>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
||||
return isSplitLayout ? renderSplitLayout() : renderSingleLayout();
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -827,10 +920,16 @@ const styles = StyleSheet.create({
|
||||
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',
|
||||
@@ -838,6 +937,141 @@ const styles = StyleSheet.create({
|
||||
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',
|
||||
},
|
||||
});
|
||||
|
||||
export default RegisterScreen;
|
||||
|
||||
Reference in New Issue
Block a user