import { Ionicons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import { useEffect, useRef, useState } from 'react'; import { Pressable, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { colors, headerGradientShort } from '../src/theme'; import { useAuth } from '../src/context/AuthContext'; import { useLogin } from '../src/hooks'; const PHONE_RE = /^1\d{10}$/; export default function LoginScreen() { const router = useRouter(); const { login: authLogin } = useAuth(); const { login, loading, error } = useLogin(); const [phone, setPhone] = useState(''); const [code, setCode] = useState(''); const [agreed, setAgreed] = useState(false); const [countdown, setCountdown] = useState(0); const timerRef = useRef | null>(null); useEffect(() => { return () => { if (timerRef.current) clearInterval(timerRef.current); }; }, []); const phoneValid = PHONE_RE.test(phone); const canSubmit = phoneValid && code.length >= 4 && agreed && !loading; const startCountdown = () => { if (!phoneValid || countdown > 0) return; setCountdown(60); timerRef.current = setInterval(() => { setCountdown((c) => { if (c <= 1) { if (timerRef.current) clearInterval(timerRef.current); return 0; } return c - 1; }); }, 1000); }; const handleLogin = async () => { if (!canSubmit) return; try { const res = await login(phone, code); authLogin(res.token); router.replace('/identity-select'); } catch { // 错误已由 error 向上展示 } }; const handleGuest = () => { router.replace('/identity-select'); }; return ( 职 登录职来 智能求职 · 企业招聘一站式服务平台 手机号 验证码 0 || !phoneValid) && styles.codeBtnDisabled]} onPress={startCountdown} disabled={countdown > 0 || !phoneValid} > {countdown > 0 ? `${countdown}s 后重发` : '获取验证码'} {error && {error.message}} {loading ? '登录中…' : '登录 / 注册'} setAgreed((v) => !v)} hitSlop={6} > {agreed && } 已阅读并同意 router.push('/settings/privacy-policy')}> 《隐私政策》 和 router.push('/settings/user-agreement')}> 《用户协议》 其他登录方式 {}} hitSlop={8}> {}} hitSlop={8}> 暂不登录,先逛逛 ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: colors.pageBg }, header: { alignItems: 'center', paddingTop: 44, paddingBottom: 36, }, logoCircle: { width: 64, height: 64, borderRadius: 20, backgroundColor: colors.white, alignItems: 'center', justifyContent: 'center', marginBottom: 14, }, logoText: { fontSize: 30, fontWeight: '800', color: colors.primary }, headerTitle: { fontSize: 22, fontWeight: '800', color: colors.textInverse }, headerSub: { fontSize: 13, color: colors.textInverse, opacity: 0.9, marginTop: 6 }, body: { flexGrow: 1, padding: 20, paddingTop: 24 }, field: { marginBottom: 18 }, label: { fontSize: 14, fontWeight: '600', color: colors.textPrimary, marginBottom: 8 }, input: { height: 50, borderRadius: 12, borderWidth: 1, borderColor: colors.border, backgroundColor: colors.white, paddingHorizontal: 14, fontSize: 16, color: colors.textPrimary, }, codeRow: { flexDirection: 'row', alignItems: 'center', gap: 12 }, codeInput: { flex: 1 }, codeBtn: { height: 50, paddingHorizontal: 16, borderRadius: 12, backgroundColor: colors.primaryLight, alignItems: 'center', justifyContent: 'center', }, codeBtnDisabled: { backgroundColor: colors.borderLight, opacity: 0.7 }, codeBtnText: { fontSize: 14, fontWeight: '600', color: colors.primaryText }, errorText: { fontSize: 13, color: colors.danger, marginBottom: 12, }, submitBtn: { height: 50, borderRadius: 25, backgroundColor: colors.primaryBtn, alignItems: 'center', justifyContent: 'center', marginTop: 8, }, submitDisabled: { backgroundColor: colors.border, opacity: 0.7 }, submitText: { fontSize: 16, fontWeight: '700', color: colors.textInverse }, agreement: { flexDirection: 'row', alignItems: 'flex-start', marginTop: 16, }, checkbox: { width: 18, height: 18, borderRadius: 5, borderWidth: 1.5, borderColor: colors.borderStrong, alignItems: 'center', justifyContent: 'center', marginTop: 2, marginRight: 8, }, checkboxChecked: { backgroundColor: colors.primary, borderColor: colors.primary }, agreementText: { flex: 1, fontSize: 12, color: colors.textSecondary, lineHeight: 18 }, link: { color: colors.primaryText, fontWeight: '600' }, others: { alignItems: 'center', marginTop: 32 }, othersTitle: { fontSize: 12, color: colors.textHint, marginBottom: 16 }, othersRow: { flexDirection: 'row', gap: 36 }, otherItem: { width: 52, height: 52, borderRadius: 26, backgroundColor: colors.white, borderWidth: 1, borderColor: colors.borderLight, alignItems: 'center', justifyContent: 'center', }, guest: { alignItems: 'center', paddingVertical: 18, backgroundColor: colors.pageBg, }, guestText: { fontSize: 14, color: colors.textHint }, });