import { useEffect, useMemo, useRef, useState } from 'react' import { Button, Card, Form, Input, Space, Tabs, } from 'antd' import { LockOutlined, MessageOutlined, MobileOutlined } from '@ant-design/icons' import { theme } from 'antd' import { toast } from 'sonner' import { useNavigate } from 'react-router-dom' import { apiGetSmsCode } from '@/apis/apisCommon' import { merchantProfileGet } from '@/apis/mallMerchant' import { apiMallMerchantLogin } from '@/apis/apisUser' import { useSessionStore } from '@/store/session' import { resolveMerchantSessionIdentity } from '@/utils/mall' interface LoginFormValues { account: string password?: string captcha?: string } type LoginMode = 'password' | 'sms' const PHONE_REGEX = /^1\d{10}$/ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ const resolveLoginType = (account: string): '0' | '1' => { return EMAIL_REGEX.test(account) ? '1' : '0' } export default function LoginPage() { const { token } = theme.useToken() const navigate = useNavigate() const setSession = useSessionStore(state => state.setSession) const setToken = useSessionStore(state => state.setToken) const setUserInfo = useSessionStore(state => state.setUserInfo) const existingToken = useSessionStore(state => state.token) const loginAccount = useSessionStore(state => state.loginAccount) const userInfo = useSessionStore(state => state.userInfo) const [mode, setMode] = useState('password') const [loading, setLoading] = useState(false) const [smsSending, setSmsSending] = useState(false) const [smsLeft, setSmsLeft] = useState(0) const smsTimerRef = useRef(null) const [form] = Form.useForm() useEffect(() => { if (!loading && existingToken?.accessToken) { navigate('/profile', { replace: true }) } }, [existingToken, loading, navigate]) useEffect(() => { return () => { if (smsTimerRef.current) { window.clearInterval(smsTimerRef.current) smsTimerRef.current = null } } }, []) const tabs = useMemo( () => [ { key: 'password', label: '密码登录' }, { key: 'sms', label: '短信登录' }, ], [] ) const startSmsCountdown = (seconds: number) => { setSmsLeft(seconds) if (smsTimerRef.current) { window.clearInterval(smsTimerRef.current) smsTimerRef.current = null } smsTimerRef.current = window.setInterval(() => { setSmsLeft(prev => { if (prev <= 1) { if (smsTimerRef.current) { window.clearInterval(smsTimerRef.current) smsTimerRef.current = null } return 0 } return prev - 1 }) }, 1000) } const onSendSms = async () => { try { const account = (form.getFieldValue('account') ?? '').toString().trim() if (!PHONE_REGEX.test(account)) { toast.error('请输入有效的手机号') return } setSmsSending(true) await apiGetSmsCode({ account, type: '1' }) toast.success('验证码已发送') startSmsCountdown(60) } catch (error) { const message = error instanceof Error ? error.message : '验证码发送失败' toast.error(message) } finally { setSmsSending(false) } } return (
{ try { const account = values.account.trim() const isPhone = PHONE_REGEX.test(account) const isEmail = EMAIL_REGEX.test(account) if (!isPhone && !isEmail) { toast.error('请输入有效的手机号或邮箱') return } if (mode === 'sms' && !isPhone) { toast.error('验证码登录仅支持手机号') return } setLoading(true) const payload = mode === 'password' ? { account, password: values.password?.trim(), loginType: resolveLoginType(account), type: '0' as const, loginIp: '', } : { account, captcha: values.captcha?.trim(), loginType: '0' as const, type: '1' as const, loginIp: '', } const loginResult = await apiMallMerchantLogin(payload) setToken(loginResult.data) setUserInfo(null) setSession({ loginAccount: account, merchantId: null, merchantName: '', }) try { const profileData = await merchantProfileGet() setSession({ loginAccount: account, ...resolveMerchantSessionIdentity(profileData.merchant), }) } catch (profileError) { const message = profileError instanceof Error ? profileError.message : '同步商家资料失败' toast.error(`登录成功,但同步商家资料失败:${message}`) } toast.success('登录成功') navigate('/profile', { replace: true }) } catch (error) { const message = error instanceof Error ? error.message : '登录失败' toast.error(message) } finally { setLoading(false) } }} > { setMode(key as LoginMode) form.resetFields(['password', 'captcha']) }} /> { const account = (value ?? '').toString().trim() if (!account) { return Promise.reject( new Error(mode === 'sms' ? '请输入手机号' : '请输入手机号或邮箱') ) } if (mode === 'sms') { return PHONE_REGEX.test(account) ? Promise.resolve() : Promise.reject(new Error('请输入有效的手机号')) } return PHONE_REGEX.test(account) || EMAIL_REGEX.test(account) ? Promise.resolve() : Promise.reject(new Error('请输入有效的手机号或邮箱')) }, }, ]} > } /> {mode === 'password' ? ( } /> ) : ( } /> )}
) }