|
|
|
|
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,
|
|
|
|
|
},
|
|
|
|
|
});
|