import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { adminApi, clientApi, type CaptchaResponse } from '@/api/adminApi'; import { consumeSessionRecovery, markUserActivity, readSessionRecovery, writeSession, type Portal } from '@/api/session'; import { Button, Input, Modal } from '@/components/ui'; type LoginPageProps = { portal: Portal; }; function loginErrorMessage(err: unknown) { const message = err instanceof Error ? err.message : ''; if (message.includes('Invalid login or password')) return '用户名或密码错误'; if (message.includes('login and password are required')) return '请输入用户名和密码'; if (message.includes('Captcha expired')) return '验证码已过期,请刷新后重试'; if (message.includes('Captcha is incorrect')) return '验证码错误,请重新输入'; if (message.includes('User is locked')) return '账号已锁定,请 24 小时后再试'; if (message.includes('User is disabled or deleted')) return '账号已停用或已删除'; if (message.includes('Only platform admins can login to admin portal')) return '该账号不是运营端管理员'; if (message.includes('Only enterprise admins linked to a tenant can login to client portal')) return '该账号不是已绑定企业的客户端管理员'; return message || '登录失败,请检查账号信息后重试'; } export function LoginPage({ portal }: LoginPageProps) { const navigate = useNavigate(); const [login, setLogin] = useState(''); const [password, setPassword] = useState(''); const [captchaText, setCaptchaText] = useState(''); const [captcha, setCaptcha] = useState(null); const [error, setError] = useState(''); const [alertMessage, setAlertMessage] = useState(''); const [loading, setLoading] = useState(false); const isAdmin = portal === 'admin'; const recovery = readSessionRecovery(portal); async function refreshCaptcha(options: { clearError?: boolean } = {}) { if (options.clearError ?? true) { setError(''); } setCaptchaText(''); setCaptcha(await (isAdmin ? adminApi.getCaptcha() : clientApi.getCaptcha())); } useEffect(() => { void refreshCaptcha(); }, [portal]); async function submit() { if (!captcha) return; setLoading(true); setError(''); try { const session = await (isAdmin ? adminApi.login : clientApi.login)({ login, password, captchaId: captcha.captchaId, captchaText, }); writeSession(session); // A login can happen without a full page reload after the previous session // expired. Reset the in-memory activity clock so the new session is not // immediately locked using the previous session's stale idle duration. markUserActivity(); const target = consumeSessionRecovery(portal)?.returnUrl; navigate(target ?? (isAdmin ? '/admin' : '/client'), { replace: true }); } catch (err) { const message = loginErrorMessage(err); setError(message); setAlertMessage(message); await refreshCaptcha({ clearError: false }); } finally { setLoading(false); } } return (
{isAdmin

短信服务平台

{isAdmin ? '运营端登录' : '客户端登录'}

{recovery ? (

{recovery.message ?? '登录会话已失效,请重新登录。'} 登录成功后将返回之前访问的页面。

) : null} setLogin(event.target.value)} placeholder="请输入用户名、邮箱或手机号" value={login} /> setPassword(event.target.value)} placeholder="请输入密码" type="password" value={password} />
setCaptchaText(event.target.value)} placeholder="请输入计算结果" value={captchaText} />
{error ?

{error}

: null}
setAlertMessage('')}>知道了} onClose={() => setAlertMessage('')} open={Boolean(alertMessage)} title="登录失败" >

{alertMessage}

); }