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.
139 lines
5.6 KiB
139 lines
5.6 KiB
import { useState } from 'react';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { useRouter } from 'expo-router';
|
|
import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
|
|
import { colors } from '../../../src/theme';
|
|
import { useAgreementGate, useOnboarding, usePasswordLogin } from '../../../src/hooks';
|
|
import { isValidPhone } from '../../../src/utils';
|
|
import { useAuth } from '../../../src/context/AuthContext';
|
|
import { useRole } from '../../../src/context/RoleContext';
|
|
import { PasswordField } from '../../../src/components/PasswordField';
|
|
import { ScreenShell } from '../ui/ScreenShell';
|
|
import { AgreementCheckbox } from '../ui/AgreementCheckbox';
|
|
import { PrimaryButton } from '../ui/PrimaryButton';
|
|
import { AgreementModal } from '../ui/AgreementModal';
|
|
|
|
/**
|
|
* P2P 手机号 + 密码登录。
|
|
*
|
|
* 与 P2(验证码)、P2R(设置密码)互相可达:本页底部可切回验证码登录,忘记密码进 P2R。
|
|
* 登录成功后按是否已选过身份决定落地页,与 P1 微信分支的原逻辑一致。
|
|
*/
|
|
export function P2PasswordLogin() {
|
|
const { data, setData, navigate, back } = useOnboarding();
|
|
const { gate, modalProps } = useAgreementGate();
|
|
const { login, loading, error } = usePasswordLogin();
|
|
/** 登录态写入:换取令牌后必须落库,否则冷启动会被打回登录页 */
|
|
const { login: writeAuth } = useAuth();
|
|
const { role } = useRole();
|
|
const router = useRouter();
|
|
|
|
const [password, setPassword] = useState('');
|
|
|
|
const agreed = data.agreed;
|
|
const phone = data.phone;
|
|
const phoneValid = isValidPhone(phone);
|
|
const canSubmit = phoneValid && password.length > 0;
|
|
|
|
const handleLogin = async () => {
|
|
if (!canSubmit || loading) return;
|
|
try {
|
|
const result = await login(phone, password);
|
|
writeAuth(result.token);
|
|
} catch {
|
|
// 失败原因由 usePasswordLogin 记录,页面下方展示
|
|
return;
|
|
}
|
|
|
|
// 已有身份的老用户直接进对应主界面,新用户才去选身份
|
|
if (role) {
|
|
router.replace(role === 'enterprise' ? '/(enterprise)/job-management' : '/(tabs)/jobs');
|
|
return;
|
|
}
|
|
navigate('P3');
|
|
};
|
|
|
|
return (
|
|
<ScreenShell showBack onBack={back}>
|
|
<Text style={styles.h1}>密码登录</Text>
|
|
<Text style={styles.subtitle}>使用手机号与登录密码进入轻松聘</Text>
|
|
|
|
{/* 手机号 */}
|
|
<View style={styles.fieldRow}>
|
|
<Text style={styles.prefix}>+86</Text>
|
|
<Ionicons name="caret-down" size={14} color={colors.textSecondary} />
|
|
<View style={styles.divider} />
|
|
<TextInput
|
|
style={styles.input}
|
|
value={phone}
|
|
onChangeText={(t) => setData({ phone: t.replace(/[^\d]/g, '').slice(0, 11) })}
|
|
placeholder="请输入手机号"
|
|
placeholderTextColor={colors.textHint}
|
|
keyboardType="phone-pad"
|
|
maxLength={11}
|
|
/>
|
|
{phone.length > 0 ? (
|
|
<Pressable onPress={() => setData({ phone: '' })} hitSlop={8}>
|
|
<Ionicons name="close-circle" size={18} color={colors.textDisabled} />
|
|
</Pressable>
|
|
) : null}
|
|
</View>
|
|
<View style={styles.underline} />
|
|
|
|
{/* 密码 */}
|
|
<View style={styles.passwordArea}>
|
|
<PasswordField value={password} onChangeText={setPassword} placeholder="请输入登录密码" />
|
|
</View>
|
|
|
|
<Pressable onPress={() => gate(() => navigate('P2R'))} hitSlop={8}>
|
|
<Text style={styles.forgotLink}>忘记密码?</Text>
|
|
</Pressable>
|
|
|
|
<View style={styles.btnArea}>
|
|
<PrimaryButton label="登录" active={canSubmit} loading={loading} onPress={() => gate(handleLogin)} />
|
|
</View>
|
|
|
|
{error ? <Text style={styles.formError}>{error.message}</Text> : null}
|
|
|
|
<View style={styles.agree}>
|
|
<AgreementCheckbox
|
|
checked={agreed}
|
|
onToggle={() => setData({ agreed: !agreed })}
|
|
text={
|
|
<Text style={styles.agreeText}>
|
|
已阅读并同意
|
|
<Text style={styles.link} onPress={() => navigate('B2')}>《轻松聘用户协议》</Text>
|
|
和
|
|
<Text style={styles.link} onPress={() => navigate('B2')}>《隐私政策》</Text>
|
|
,允许轻松聘统一管理本人账号信息
|
|
</Text>
|
|
}
|
|
/>
|
|
</View>
|
|
|
|
<Pressable onPress={() => gate(() => navigate('P2'))} hitSlop={8}>
|
|
<Text style={styles.switchLink}>验证码登录</Text>
|
|
</Pressable>
|
|
|
|
<AgreementModal {...modalProps} />
|
|
</ScreenShell>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
h1: { fontSize: 26, fontWeight: '800', color: colors.textPrimary, marginTop: 12 },
|
|
subtitle: { fontSize: 14, color: colors.textSecondary, marginTop: 10 },
|
|
fieldRow: { flexDirection: 'row', alignItems: 'center', height: 56, marginTop: 28, gap: 6 },
|
|
prefix: { fontSize: 17, fontWeight: '700', color: colors.textPrimary },
|
|
divider: { width: 1, height: 20, backgroundColor: colors.border, marginHorizontal: 10 },
|
|
input: { flex: 1, fontSize: 17, color: colors.textPrimary, padding: 0 },
|
|
underline: { height: 1, backgroundColor: colors.borderLight },
|
|
passwordArea: { marginTop: 22 },
|
|
forgotLink: { marginTop: 14, alignSelf: 'flex-end', fontSize: 13, color: colors.primaryText, fontWeight: '600' },
|
|
btnArea: { marginTop: 26 },
|
|
formError: { marginTop: 10, textAlign: 'center', fontSize: 12, color: colors.danger },
|
|
agree: { marginTop: 18 },
|
|
agreeText: { fontSize: 12, color: colors.textSecondary, lineHeight: 18 },
|
|
link: { color: colors.primary, fontWeight: '600' },
|
|
switchLink: { marginTop: 26, textAlign: 'center', fontSize: 14, color: colors.textSecondary },
|
|
});
|
|
|