You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1313 lines
34 KiB

import React, { useEffect, useState, useRef } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity,
StatusBar,
Platform,
BackHandler,
TextInput,
Animated,
Easing,
FlatList,
Keyboard,
Modal,
InteractionManager,
Image,
Alert
} from "react-native";
import { useTranslation } from "react-i18next";
import { useNavigation } from "@react-navigation/native";
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { Country, countries } from "../constants/countries";
import { useAuth } from "../contexts/AuthContext";
import { settingApi } from "../services/api/setting";
import { userApi } from "../services/api/userApi";
import AsyncStorage from "@react-native-async-storage/async-storage";
import useUserStore from "../store/user";
type RootStackParamList = {
Login: undefined;
EmailLogin: undefined;
MainTabs: { screen: string };
Google: undefined;
Home: { screen: string };
};
// 常见邮箱后缀列表
const EMAIL_DOMAINS = [
"gmail.com",
"yahoo.com",
"hotmail.com",
"outlook.com",
"icloud.com",
"mail.com",
"protonmail.com",
"qq.com",
"163.com",
"126.com",
];
type LoginScreenProps = {
onClose?: () => void;
isModal?: boolean;
};
export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
const { t } = useTranslation();
const navigation =
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
const { login } = useAuth();
const { setSettings, settings, user, setUser } = useUserStore();
// 邮箱登录状态
const [showEmailLogin, setShowEmailLogin] = useState(false);
const [email, setEmail] = useState("");
const [emailPassword, setEmailPassword] = useState("");
const [emailPasswordError, setEmailPasswordError] = useState(false);
const [showSuggestions, setShowSuggestions] = useState(false);
const [suggestions, setSuggestions] = useState<string[]>([]);
// 手机号登录状态
const [showPhoneLogin, setShowPhoneLogin] = useState(false);
const [phoneNumber, setPhoneNumber] = useState("");
const [password, setPassword] = useState("");
const [passwordError, setPasswordError] = useState(false);
const [showCountryModal, setShowCountryModal] = useState(false);
const [selectedCountry, setSelectedCountry] = useState<Country>({
name: "Republic of Congo",
code: "CG",
flag: "🇨🇬",
userCount: 300000,
phoneCode: "+242",
});
// 动画值
const emailSlideAnim = useRef(new Animated.Value(400)).current;
const phoneSlideAnim = useRef(new Animated.Value(400)).current;
const [emailLoginMounted, setEmailLoginMounted] = useState(false);
const [phoneLoginMounted, setPhoneLoginMounted] = useState(false);
// 处理邮箱输入变化,生成邮箱建议
const handleEmailChange = (text: string) => {
setEmail(text);
// 检查是否包含@符号
if (text.includes("@")) {
const [username, domain] = text.split("@");
if (domain) {
// 如果已经输入了部分域名,过滤匹配的域名
const filteredDomains = EMAIL_DOMAINS.filter((item) =>
item.toLowerCase().startsWith(domain.toLowerCase())
);
// 生成完整的邮箱建议列表
const emailSuggestions = filteredDomains.map((d) => `${username}@${d}`);
setSuggestions(emailSuggestions);
setShowSuggestions(emailSuggestions.length > 0);
} else {
// 如果只输入了@,显示所有域名建议
const emailSuggestions = EMAIL_DOMAINS.map((d) => `${username}@${d}`);
setSuggestions(emailSuggestions);
setShowSuggestions(true);
}
} else if (text.length > 0) {
// 没有@符号但有输入内容,显示常见邮箱后缀建议
const emailSuggestions = EMAIL_DOMAINS.map((d) => `${text}@${d}`);
setSuggestions(emailSuggestions);
setShowSuggestions(true);
} else {
// 输入为空,不显示建议
setShowSuggestions(false);
}
};
// 选择一个邮箱建议
const handleSelectSuggestion = (suggestion: string) => {
setEmail(suggestion);
setShowSuggestions(false);
};
// 验证邮箱格式
const isValidEmail = (email: string): boolean => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
// 渲染单个邮箱建议项
const renderSuggestionItem = ({ item }: { item: string }) => (
<TouchableOpacity
style={styles.suggestionItem}
onPress={() => handleSelectSuggestion(item)}
>
<Text style={styles.suggestionText}>{item}</Text>
</TouchableOpacity>
);
// 选择国家
const handleCountrySelect = (country: Country) => {
setSelectedCountry(country);
setShowCountryModal(false);
};
// 渲染国家列表项 - 添加性能优化
const renderCountryItem = React.useCallback(
({ item }: { item: Country }) => (
<TouchableOpacity
style={styles.countryItem}
onPress={() => handleCountrySelect(item)}
activeOpacity={0.7}
>
<Text style={styles.countryItemFlag}>{item.flag}</Text>
<View style={styles.countryItemContent}>
<Text style={styles.countryItemName}>{item.name}</Text>
<Text style={styles.countryItemCode}>{item.phoneCode}</Text>
</View>
</TouchableOpacity>
),
[]
);
// 添加Android返回按钮处理
useEffect(() => {
const backAction = () => {
if (showEmailLogin) {
closeEmailLogin();
return true;
}
if (showPhoneLogin) {
closePhoneLogin();
return true;
}
if (showCountryModal) {
setShowCountryModal(false);
return true;
}
handleClose();
return true;
};
const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
backAction
);
return () => backHandler.remove();
}, [showEmailLogin, showPhoneLogin, showCountryModal]);
// 初始化动画配置
useEffect(() => {
// React Native Animated API不支持直接设置useNativeDriver
// 这些配置会在动画函数中使用
}, []);
// 打开邮箱登录面板
const openEmailLogin = () => {
emailSlideAnim.setValue(400);
setEmailLoginMounted(true);
// 等待组件挂载后启动动画
requestAnimationFrame(() => {
setShowEmailLogin(true);
Animated.spring(emailSlideAnim, {
toValue: 0,
useNativeDriver: true,
friction: 8,
tension: 40,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 0.01,
}).start();
});
};
// 关闭邮箱登录面板
const closeEmailLogin = () => {
Keyboard.dismiss();
// 使用更简单的配置减少计算复杂度
Animated.timing(emailSlideAnim, {
toValue: 400,
duration: 200,
useNativeDriver: true,
easing: Easing.cubic,
}).start();
// 先隐藏建议列表,减少布局计算
setShowSuggestions(false);
// 使用InteractionManager确保动画完成后再执行耗时操作
InteractionManager.runAfterInteractions(() => {
setShowEmailLogin(false);
setEmail("");
setEmailPassword("");
setEmailPasswordError(false);
// 延迟卸载组件
setTimeout(() => {
setEmailLoginMounted(false);
}, 50);
});
};
// 打开手机号登录面板
const openPhoneLogin = () => {
phoneSlideAnim.setValue(400);
setPhoneLoginMounted(true);
// 等待组件挂载后启动动画
requestAnimationFrame(() => {
setShowPhoneLogin(true);
Animated.spring(phoneSlideAnim, {
toValue: 0,
useNativeDriver: true,
friction: 8,
tension: 40,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 0.01,
}).start();
});
};
// 关闭手机号登录面板
const closePhoneLogin = () => {
Keyboard.dismiss();
// 使用更简单的配置减少计算复杂度
Animated.timing(phoneSlideAnim, {
toValue: 400,
duration: 200,
useNativeDriver: true,
easing: Easing.cubic,
}).start();
// 使用InteractionManager确保动画完成后再执行耗时操作
InteractionManager.runAfterInteractions(() => {
setShowPhoneLogin(false);
setPhoneNumber("");
// 延迟卸载组件
setTimeout(() => {
setPhoneLoginMounted(false);
}, 50);
});
};
// 修改关闭按钮处理函数
const handleClose = () => {
if (isModal && onClose) {
onClose();
} else {
navigation.goBack();
}
};
// 处理谷歌登录
const handleGoogleLogin = async () => {
navigation.navigate("Google");
};
const handleFacebookLogin = () => {
// 处理Facebook登录
};
const handleAppleLogin = () => {
// 处理Apple登录
};
const handleInstagramLogin = () => {
// 处理Instagram登录
};
const handleEmailLogin = () => {
// 处理邮箱登录 - 显示邮箱登录面板
openEmailLogin();
};
//
const handleEmailContinue = async () => {
const params = {
grant_type: "password",
username: "lifei",
password: "123456",
client_id: "2",
client_secret: "",
scope: "",
};
try {
const res = await userApi.login(params);
if (res.access_token) {
const token = res.token_type + " " + res.access_token;
await AsyncStorage.setItem("token", token);
// const data = await settingApi.postFirstLogin(221);
// setSettings(data);
const user = await userApi.getProfile()
setUser(user);
navigation.navigate("MainTabs", { screen: "Home" });
}
} catch (error) {
Alert.alert(t("loginFailed"));
}
};
const handlePhoneLogin = () => {
// 处理手机号登录 - 显示手机号登录面板
openPhoneLogin();
};
const handlePhoneVerificationSuccess = async () => {
await login();
closePhoneLogin();
navigation.replace("MainTabs", { screen: "Home" });
};
const handlePhoneContinue = async () => {
if (phoneNumber.trim() && password) {
if (password === "123") {
await login();
closePhoneLogin();
navigation.replace("MainTabs", { screen: "Home" });
} else {
setPasswordError(true);
}
}
};
const handleForgotPassword = () => {
// 处理忘记密码
};
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" backgroundColor="#0066FF" />
{/* 顶部蓝色背景区域 */}
<View style={styles.blueHeader}>
<Text style={styles.logo}>brainnel</Text>
<View style={styles.features}>
<View style={styles.featureItem}>
<View style={styles.featureIconContainer}>
<Text style={styles.featureIcon}>💰</Text>
</View>
<Text style={styles.featureText}>{t("wholesalePrice")}</Text>
</View>
<View style={styles.featureItem}>
<View style={styles.featureIconContainer}>
<Text style={styles.featureIcon}>🚚</Text>
</View>
<Text style={styles.featureText}>{t("fastShipping")}</Text>
</View>
</View>
</View>
{/* 登录区域 */}
<View style={styles.loginContainer}>
<TouchableOpacity style={styles.closeButton} onPress={handleClose}>
<Text style={styles.closeButtonText}>×</Text>
</TouchableOpacity>
<View style={styles.titleContainer}>
{/* <Text style={styles.title}>Login For brainnel</Text> */}
<Text style={styles.subtitle}>{t("loginSubtitle")}</Text>
</View>
{/* 登录按钮 */}
<TouchableOpacity
style={styles.loginButton}
onPress={handleGoogleLogin}
>
<View style={styles.loginButtonIcon}>
<Image
source={require("../../assets/img/google.png")}
style={{ width: 20, height: 20 }}
/>
</View>
<Text style={styles.loginButtonText}>{t("continueWithGoogle")}</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.loginButton}
onPress={handleFacebookLogin}
>
<View style={[styles.loginButtonIcon, styles.facebookIcon]}>
<Text style={{ color: "#fff" }}>f</Text>
</View>
<Text style={styles.loginButtonText}>
{t("continueWithFacebook")}
</Text>
</TouchableOpacity>
{Platform.OS === "ios" && (
<TouchableOpacity
style={styles.loginButton}
onPress={handleAppleLogin}
>
<View style={[styles.loginButtonIcon, styles.appleIconBg]}>
<Text>🍎</Text>
</View>
<Text style={styles.loginButtonText}>{t("continueWithApple")}</Text>
</TouchableOpacity>
)}
<TouchableOpacity
style={styles.loginButton}
onPress={handleInstagramLogin}
>
<View style={[styles.loginButtonIcon, styles.instagramIcon]}>
<Text>📷</Text>
</View>
<Text style={styles.loginButtonText}>
{t("continueWithInstagram")}
</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.loginButton} onPress={handleEmailLogin}>
<View style={styles.loginButtonIcon}>
<Text></Text>
</View>
<Text style={styles.loginButtonText}>{t("continueWithEmail")}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.loginButton} onPress={handlePhoneLogin}>
<View style={styles.loginButtonIcon}>
<Text>📱</Text>
</View>
<Text style={styles.loginButtonText}>{t("continueWithPhone")}</Text>
</TouchableOpacity>
{/* 忘记密码 */}
<TouchableOpacity
style={styles.forgotPassword}
onPress={handleForgotPassword}
>
<Text style={styles.forgotPasswordText}>{t("forgotPassword")}</Text>
</TouchableOpacity>
{/* 服务条款 */}
<View style={styles.termsContainer}>
<Text style={styles.terms}>
{t("termsText")} <Text style={styles.link}>{t("termsOfUse")}</Text>
</Text>
<Text style={styles.terms}>
{t("and")} <Text style={styles.link}>{t("privacyPolicy")}</Text>
</Text>
</View>
</View>
{/* 邮箱登录面板 - 从底部滑出 */}
{emailLoginMounted && (
<Animated.View
style={[
styles.emailLoginContainer,
{
transform: [{ translateY: emailSlideAnim }],
opacity: emailSlideAnim.interpolate({
inputRange: [0, 400],
outputRange: [1, 0.7],
}),
},
]}
removeClippedSubviews={true}
collapsable={false}
>
<View style={styles.emailLoginHeader}>
<TouchableOpacity
style={styles.emailLoginCloseButton}
onPress={closeEmailLogin}
activeOpacity={0.7}
>
<Text style={styles.emailLoginCloseButtonText}></Text>
</TouchableOpacity>
<Text style={styles.emailLoginTitle}>{t("logInOrSignUp")}</Text>
</View>
<View style={styles.emailLoginContent}>
<View style={styles.emailInputContainer}>
<TextInput
style={styles.emailInput}
placeholder={t("pleaseEnterEmail")}
value={email}
onChangeText={(text) => {
handleEmailChange(text);
setEmailPasswordError(false);
}}
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
autoFocus
maxLength={50}
/>
{email.length > 0 && (
<TouchableOpacity
style={styles.emailClearButton}
onPress={() => {
setEmail("");
setShowSuggestions(false);
setEmailPasswordError(false);
}}
activeOpacity={0.7}
>
<Text style={styles.emailClearButtonText}></Text>
</TouchableOpacity>
)}
</View>
{/* 邮箱后缀建议列表 */}
{showSuggestions && (
<View style={styles.suggestionsContainer}>
<FlatList
data={suggestions.slice(0, 5)} // 限制最多显示5个建议
renderItem={renderSuggestionItem}
keyExtractor={(item) => item}
style={styles.suggestionsList}
keyboardShouldPersistTaps="handled"
removeClippedSubviews={true}
initialNumToRender={5}
maxToRenderPerBatch={5}
windowSize={5}
getItemLayout={(data, index) => ({
length: 44,
offset: 44 * index,
index,
})}
/>
</View>
)}
{/* 密码输入框 */}
<View
style={[
styles.phoneInputContainer,
emailPasswordError && styles.passwordErrorContainer,
]}
>
<TextInput
style={styles.passwordInput}
placeholder={t("enterPassword")}
value={emailPassword}
onChangeText={(text) => {
setEmailPassword(text);
setEmailPasswordError(false);
}}
secureTextEntry={true}
autoCapitalize="none"
/>
{emailPasswordError && (
<View style={styles.passwordErrorIcon}>
<Text style={styles.passwordErrorIconText}>!</Text>
</View>
)}
</View>
{/* 密码错误提示 */}
{emailPasswordError && (
<>
<Text style={styles.passwordErrorText}>
{t("passwordIncorrect")}
</Text>
<TouchableOpacity
style={styles.forgotPasswordLink}
onPress={handleForgotPassword}
activeOpacity={0.7}
>
<Text style={styles.forgotPasswordLinkText}>
{t("forgotPassword")}
</Text>
</TouchableOpacity>
</>
)}
<TouchableOpacity
style={[
styles.emailContinueButton,
(!isValidEmail(email) || !emailPassword) &&
styles.emailDisabledButton,
]}
onPress={handleEmailContinue}
disabled={!isValidEmail(email) || !emailPassword}
activeOpacity={0.7}
>
<Text style={styles.emailContinueButtonText}>
{t("continue")}
</Text>
</TouchableOpacity>
</View>
</Animated.View>
)}
{/* 手机号登录面板 - 从底部滑出 */}
{phoneLoginMounted && (
<Animated.View
style={[
styles.phoneLoginContainer,
{
transform: [{ translateY: phoneSlideAnim }],
opacity: phoneSlideAnim.interpolate({
inputRange: [0, 400],
outputRange: [1, 0.7],
}),
},
]}
removeClippedSubviews={true}
collapsable={false}
>
<View style={styles.phoneLoginHeader}>
<TouchableOpacity
style={styles.phoneLoginCloseButton}
onPress={closePhoneLogin}
activeOpacity={0.7}
>
<Text style={styles.phoneLoginCloseButtonText}></Text>
</TouchableOpacity>
<Text style={styles.phoneLoginTitle}>{t("logInOrSignUp")}</Text>
</View>
<View style={styles.phoneLoginContent}>
<View style={styles.phoneInputContainer}>
<TouchableOpacity
style={styles.countrySelector}
onPress={() => setShowCountryModal(true)}
activeOpacity={0.7}
>
<Text style={styles.countryFlag}>{selectedCountry.flag}</Text>
<Text style={styles.countryCode}>
{selectedCountry.phoneCode}
</Text>
<Text style={styles.downArrow}></Text>
</TouchableOpacity>
<TextInput
style={styles.phoneInput}
placeholder={t("phoneNumber")}
value={phoneNumber}
onChangeText={(text) => {
setPhoneNumber(text);
setPasswordError(false);
}}
keyboardType="phone-pad"
autoFocus
maxLength={15}
/>
{phoneNumber.length > 0 && (
<TouchableOpacity
style={styles.phoneClearButton}
onPress={() => {
setPhoneNumber("");
setPasswordError(false);
}}
activeOpacity={0.7}
>
<Text style={styles.phoneClearButtonText}></Text>
</TouchableOpacity>
)}
</View>
{/* 密码输入框 */}
<View
style={[
styles.phoneInputContainer,
passwordError && styles.passwordErrorContainer,
]}
>
<TextInput
style={styles.passwordInput}
placeholder={t("enterPassword")}
value={password}
onChangeText={(text) => {
setPassword(text);
setPasswordError(false);
}}
secureTextEntry={true}
autoCapitalize="none"
/>
{passwordError && (
<View style={styles.passwordErrorIcon}>
<Text style={styles.passwordErrorIconText}>!</Text>
</View>
)}
</View>
{/* 密码错误提示 */}
{passwordError && (
<>
<Text style={styles.passwordErrorText}>
{t("passwordIncorrect")}
</Text>
<TouchableOpacity
style={styles.forgotPasswordLink}
onPress={handleForgotPassword}
activeOpacity={0.7}
>
<Text style={styles.forgotPasswordLinkText}>
{t("forgotPassword")}
</Text>
</TouchableOpacity>
</>
)}
<Text
style={[styles.phoneInfoText, passwordError && { marginTop: 5 }]}
></Text>
<TouchableOpacity
style={[
styles.phoneContinueButton,
(!phoneNumber.trim() || !password) &&
styles.phoneDisabledButton,
]}
onPress={handlePhoneContinue}
disabled={!phoneNumber.trim() || !password}
activeOpacity={0.7}
>
<Text style={styles.phoneContinueButtonText}>
{t("continue")}
</Text>
</TouchableOpacity>
</View>
</Animated.View>
)}
{/* 国家选择模态框 */}
<Modal
visible={showCountryModal}
animationType="slide"
transparent={true}
onRequestClose={() => setShowCountryModal(false)}
hardwareAccelerated={true}
statusBarTranslucent={true}
presentationStyle="overFullScreen"
>
<View style={styles.countryModalContainer}>
<View style={styles.countryModalContent}>
<View style={styles.countryModalHeader}>
<TouchableOpacity
style={styles.countryModalCloseButton}
onPress={() => setShowCountryModal(false)}
activeOpacity={0.7}
>
<Text style={styles.countryModalCloseButtonText}></Text>
</TouchableOpacity>
<Text style={styles.countryModalTitle}>{t("selectCountry")}</Text>
</View>
<FlatList
data={countries}
renderItem={renderCountryItem}
keyExtractor={(item) => item.code}
style={styles.countryList}
showsVerticalScrollIndicator={false}
removeClippedSubviews={true}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={10}
getItemLayout={(data, index) => ({
length: 69,
offset: 69 * index,
index,
})}
/>
</View>
</View>
</Modal>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
},
closeButton: {
position: "absolute",
top: 15,
left: 10,
width: 24,
height: 24,
justifyContent: "center",
alignItems: "center",
zIndex: 1,
},
closeButtonText: {
color: "#000",
fontSize: 24,
fontWeight: "300",
},
blueHeader: {
backgroundColor: "#0066FF",
paddingHorizontal: 20,
paddingBottom: 20,
paddingTop: Platform.OS === "ios" ? 60 : 40,
borderBottomLeftRadius: 24,
borderBottomRightRadius: 24,
},
logo: {
fontSize: 28,
fontWeight: "bold",
color: "#fff",
marginBottom: 15,
},
features: {
flexDirection: "row",
gap: 16,
},
featureItem: {
flexDirection: "row",
alignItems: "center",
gap: 8,
},
featureIconContainer: {
backgroundColor: "rgba(255, 255, 255, 0.2)",
borderRadius: 8,
width: 24,
height: 24,
justifyContent: "center",
alignItems: "center",
},
featureIcon: {
fontSize: 12,
},
featureText: {
fontSize: 14,
color: "#fff",
},
loginContainer: {
flex: 1,
paddingHorizontal: 20,
paddingTop: Platform.OS === "ios" ? 40 : 20,
},
titleContainer: {
alignItems: "center",
marginBottom: 30,
paddingTop: 20,
position: "relative",
},
title: {
fontSize: 24,
fontWeight: "bold",
color: "#000",
marginBottom: 8,
textAlign: "center",
},
subtitle: {
fontSize: 14,
color: "#666",
textAlign: "center",
},
loginButton: {
flexDirection: "row",
height: 50,
borderRadius: 25,
borderWidth: 1,
borderColor: "#E1E1E1",
alignItems: "center",
marginBottom: 12,
paddingHorizontal: 16,
backgroundColor: "#fff",
},
loginButtonIcon: {
width: 24,
height: 24,
borderRadius: 12,
justifyContent: "center",
alignItems: "center",
marginRight: 16,
},
facebookIcon: {
backgroundColor: "#3b5998",
},
appleIconBg: {
backgroundColor: "#000",
},
instagramIcon: {
backgroundColor: "#E1306C",
},
loginButtonText: {
flex: 1,
fontSize: 16,
color: "#000",
textAlign: "center",
marginRight: 16,
},
forgotPassword: {
alignItems: "center",
marginVertical: 20,
},
forgotPasswordText: {
color: "#0066FF",
fontSize: 14,
},
termsContainer: {
alignItems: "center",
marginTop: 10,
},
terms: {
fontSize: 12,
color: "#666",
textAlign: "center",
lineHeight: 18,
},
link: {
color: "#0066FF",
},
// 邮箱登录样式
emailLoginContainer: {
position: "absolute",
top: Platform.OS === "ios" ? 60 : 40,
height: Platform.OS === "ios" ? "auto" : "100%",
bottom: 0,
left: 0,
right: 0,
backgroundColor: "#fff",
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
shadowColor: "#000",
shadowOffset: { width: 0, height: -2 },
shadowOpacity: 0.1,
shadowRadius: 5,
elevation: 5,
zIndex: 10,
},
emailLoginHeader: {
flexDirection: "row",
alignItems: "center",
paddingTop: 20,
paddingHorizontal: 16,
paddingBottom: 15,
borderBottomWidth: 1,
borderBottomColor: "#f0f0f0",
},
emailLoginCloseButton: {
padding: 8,
width: 36,
height: 36,
justifyContent: "center",
alignItems: "center",
},
emailLoginCloseButtonText: {
fontSize: 18,
color: "#000",
},
emailLoginTitle: {
flex: 1,
fontSize: 18,
fontWeight: "600",
color: "#000",
textAlign: "center",
marginRight: 36,
},
emailLoginContent: {
padding: 20,
paddingBottom: Platform.OS === "ios" ? 50 : 30,
flex: 1,
},
emailInputContainer: {
flexDirection: "row",
alignItems: "center",
borderWidth: 1,
borderColor: "#E1E1E1",
borderRadius: 25,
height: 50,
marginBottom: 20,
},
emailInput: {
flex: 1,
height: "100%",
paddingHorizontal: 16,
fontSize: 16,
paddingRight: 36,
},
emailClearButton: {
position: "absolute",
right: 12,
top: "50%",
transform: [{ translateY: -12 }],
height: 24,
width: 24,
justifyContent: "center",
alignItems: "center",
},
emailClearButtonText: {
fontSize: 16,
color: "#999",
fontWeight: "500",
textAlign: "center",
},
suggestionsContainer: {
borderWidth: 1,
borderColor: "#E1E1E1",
borderRadius: 10,
marginTop: -10,
marginBottom: 20,
maxHeight: 200,
backgroundColor: "#fff",
},
suggestionsList: {
padding: 8,
},
suggestionItem: {
paddingVertical: 12,
paddingHorizontal: 16,
borderBottomWidth: 1,
borderBottomColor: "#F0F0F0",
},
suggestionText: {
fontSize: 16,
color: "#333",
},
emailContinueButton: {
height: 50,
backgroundColor: "#0039CB",
borderRadius: 25,
justifyContent: "center",
alignItems: "center",
marginTop: 20,
},
emailDisabledButton: {
backgroundColor: "#CCCCCC",
},
emailContinueButtonText: {
color: "#fff",
fontSize: 16,
fontWeight: "600",
},
// 手机号登录样式
phoneLoginContainer: {
position: "absolute",
top: Platform.OS === "ios" ? 60 : 40,
height: Platform.OS === "ios" ? "auto" : "100%",
bottom: 0,
left: 0,
right: 0,
backgroundColor: "#fff",
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
shadowColor: "#000",
shadowOffset: { width: 0, height: -2 },
shadowOpacity: 0.1,
shadowRadius: 5,
elevation: 5,
zIndex: 10,
},
phoneLoginHeader: {
flexDirection: "row",
alignItems: "center",
paddingTop: 20,
paddingHorizontal: 16,
paddingBottom: 15,
borderBottomWidth: 1,
borderBottomColor: "#f0f0f0",
},
phoneLoginCloseButton: {
padding: 8,
width: 36,
height: 36,
justifyContent: "center",
alignItems: "center",
},
phoneLoginCloseButtonText: {
fontSize: 18,
color: "#000",
},
phoneLoginTitle: {
flex: 1,
fontSize: 18,
fontWeight: "600",
color: "#000",
textAlign: "center",
marginRight: 36,
},
phoneLoginContent: {
padding: 20,
paddingBottom: Platform.OS === "ios" ? 50 : 30,
flex: 1,
},
phoneInputContainer: {
flexDirection: "row",
alignItems: "center",
borderWidth: 1,
borderColor: "#E1E1E1",
borderRadius: 25,
height: 50,
marginBottom: 20,
position: "relative",
},
countrySelector: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 12,
borderRightWidth: 1,
borderRightColor: "#E1E1E1",
backgroundColor: "#F7F7F7",
height: "100%",
minWidth: 90,
width: 90,
justifyContent: "center",
},
countryFlag: {
fontSize: 18,
marginRight: 4,
},
countryCode: {
fontSize: 12,
color: "#333",
marginRight: 4,
},
downArrow: {
fontSize: 8,
color: "#666",
},
phoneInput: {
flex: 1,
height: "100%",
paddingHorizontal: 16,
fontSize: 16,
paddingRight: 36,
},
phoneClearButton: {
position: "absolute",
right: 12,
top: "50%",
transform: [{ translateY: -12 }],
height: 24,
width: 24,
justifyContent: "center",
alignItems: "center",
},
phoneClearButtonText: {
fontSize: 16,
color: "#999",
fontWeight: "500",
textAlign: "center",
},
phoneInfoText: {
fontSize: 14,
color: "#666",
marginBottom: 32,
lineHeight: 20,
},
phoneContinueButton: {
height: 50,
backgroundColor: "#0039CB",
borderRadius: 25,
justifyContent: "center",
alignItems: "center",
},
phoneDisabledButton: {
backgroundColor: "#CCCCCC",
},
phoneContinueButtonText: {
color: "#fff",
fontSize: 16,
fontWeight: "600",
},
// 国家选择模态框样式
countryModalContainer: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.5)",
justifyContent: "flex-end",
zIndex: 999,
},
countryModalContent: {
backgroundColor: "#fff",
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
maxHeight: "80%",
zIndex: 1000,
},
countryModalHeader: {
flexDirection: "row",
alignItems: "center",
padding: 16,
borderBottomWidth: 1,
borderBottomColor: "#E5E5E5",
},
countryModalCloseButton: {
padding: 4,
},
countryModalCloseButtonText: {
fontSize: 18,
color: "#999",
},
countryModalTitle: {
flex: 1,
fontSize: 18,
fontWeight: "600",
textAlign: "center",
marginRight: 24,
},
countryList: {
padding: 8,
},
countryItem: {
flexDirection: "row",
alignItems: "center",
padding: 16,
borderBottomWidth: 1,
borderBottomColor: "#F0F0F0",
},
countryItemFlag: {
fontSize: 24,
marginRight: 16,
},
countryItemContent: {
flex: 1,
},
countryItemName: {
fontSize: 16,
color: "#333",
},
countryItemCode: {
fontSize: 14,
color: "#666",
marginTop: 4,
},
// 密码输入框样式
passwordInput: {
flex: 1,
height: "100%",
paddingHorizontal: 16,
fontSize: 16,
},
passwordErrorContainer: {
borderColor: "#FF3B30",
},
passwordErrorIcon: {
position: "absolute",
right: 12,
top: "50%",
transform: [{ translateY: -12 }],
width: 24,
height: 24,
backgroundColor: "#FF3B30",
borderRadius: 12,
justifyContent: "center",
alignItems: "center",
},
passwordErrorIconText: {
color: "white",
fontWeight: "bold",
fontSize: 16,
},
passwordErrorText: {
color: "#FF3B30",
fontSize: 14,
marginTop: -12,
marginBottom: 16,
paddingHorizontal: 5,
},
forgotPasswordLink: {
alignItems: "center",
marginTop: 5,
},
forgotPasswordLinkText: {
color: "#0066FF",
fontSize: 14,
},
});