import { useEffect, useState } from 'react'; import { Ionicons } from '@expo/vector-icons'; import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'; import { StatusBar } from 'expo-status-bar'; import { useRouter } from 'expo-router'; import { LinearGradient } from 'expo-linear-gradient'; import { SafeAreaView } from 'react-native-safe-area-context'; import { colors } from '../../../src/theme'; import { useOnboarding, useWechatLogin } from '../../../src/hooks'; import { useRole } from '../../../src/context/RoleContext'; import { Illustration } from '../ui/Illustration'; import { AgreementCheckbox } from '../ui/AgreementCheckbox'; import { AgreementModal } from '../ui/AgreementModal'; import { SliderCaptcha } from '../ui/SliderCaptcha'; import { DeletedAccountModal } from '../ui/DeletedAccountModal'; /** P1 登录主页:对齐参考样式 —— 淡渐变顶部 + 居中 Logo + 微信绿实心/灰底一键登录 + 协议弹窗拦截 */ export function P1Login() { const { data, setData, navigate } = useOnboarding(); const router = useRouter(); const { setRole } = useRole(); const agreed = data.agreed; /** 未勾选协议时被拦截的动作,弹窗「同意」后继续执行 */ const [pending, setPending] = useState<(() => void) | null>(null); const [captchaVisible, setCaptchaVisible] = useState(false); /** 微信登录状态机:负责三分支判定与流转 */ const { state: wechatState, start: startWechatLogin, cancelLogin, reset: resetWechat, loading: wechatLoading, } = useWechatLogin(); /** 协议门卫:已勾选直接执行;未勾选弹协议弹窗 */ const gate = (action: () => void) => { if (agreed) { action(); } else { setPending(() => action); } }; const accept = () => { setData({ agreed: true }); const act = pending; setPending(null); act?.(); }; /** 微信登录:先滑块拼图验证,通过后进入微信授权与三分支判定 */ const wechatLogin = () => setCaptchaVisible(true); /* * 状态机的副作用集中在这里,页面只负责触发与渲染: * - needBindPhone:第一次登录,把临时凭证存入流程数据后跳 P2(手机号 + 验证码) * - success:第二次及以后登录,凭后端返回的身份直接落地,无身份则回注册流程选择 * - alreadyLoggedIn:本地 token 未过期,免授权直接落地 * 跳转后立即 reset,避免返回本页时重复触发。 */ useEffect(() => { if (wechatState.kind === 'needBindPhone') { setData({ wechatTicket: { token: wechatState.ticket.token, openid: wechatState.ticket.openid, }, }); navigate('P2'); resetWechat(); return; } if (wechatState.kind === 'success') { if (wechatState.role) { setRole(wechatState.role); router.replace( wechatState.role === 'enterprise' ? '/(enterprise)/job-management' : '/(tabs)/jobs', ); } else { router.replace('/register'); } resetWechat(); return; } if (wechatState.kind === 'alreadyLoggedIn') { router.replace('/(tabs)/jobs'); resetWechat(); } }, [wechatState, resetWechat, router, setRole, setData, navigate]); return ( {/* 登录方式 */} gate(wechatLogin)} disabled={wechatLoading} > {wechatLoading ? '登录中…' : '微信登录'} {wechatState.kind === 'error' ? ( {wechatState.message} ) : null} gate(() => navigate('P2Q'))}> 一键登录 gate(() => navigate('P2'))} hitSlop={8}> 其他方式登录 {/* 协议勾选 */} setData({ agreed: !agreed })} text={ 已阅读并同意 navigate('B2')}>《有职用户协议》 和 navigate('B2')}>《隐私政策》 ,允许有职统一登录本账号信息 } /> 服务热线 举报监督电话 资质证照 { setCaptchaVisible(false); void startWechatLogin(); }} onClose={() => setCaptchaVisible(false)} /> setPending(null)} onOpenAgreement={() => { setPending(null); navigate('B2'); }} /> ); } const styles = StyleSheet.create({ screen: { flex: 1, backgroundColor: colors.pageBg }, topGlow: { position: 'absolute', top: 0, left: 0, right: 0, height: 200 }, content: { flexGrow: 1, paddingHorizontal: 24, paddingBottom: 12 }, brandArea: { alignItems: 'center', marginTop: '22%' }, flexSpacer: { flex: 1, minHeight: 28 }, wechatBtn: { height: 50, borderRadius: 12, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, backgroundColor: colors.success, }, wechatText: { fontSize: 16, fontWeight: '700', color: colors.textInverse }, quickBtn: { height: 50, borderRadius: 12, alignItems: 'center', justifyContent: 'center', marginTop: 14, backgroundColor: colors.borderLight, }, quickText: { fontSize: 16, fontWeight: '700', color: colors.textPrimary }, otherLink: { textAlign: 'center', marginTop: 20, fontSize: 14, color: colors.textSecondary, }, loginError: { marginTop: 10, textAlign: 'center', fontSize: 12, color: colors.danger }, agree: { marginTop: 22 }, agreeText: { fontSize: 12, color: colors.textSecondary, lineHeight: 18 }, link: { color: colors.primary, fontWeight: '600' }, footer: { flexDirection: 'row', justifyContent: 'center', gap: 18, paddingTop: 24, paddingBottom: 8, }, footItem: { fontSize: 12, color: colors.textHint }, });