import { useEffect, useRef, useState } from 'react'; import { useNavigate, useSearchParams, Link } from 'react-router-dom'; import AppImage from '@dukang/shared-ui/AppImage'; import type { WechatLoginResult } from '@dukang/shared-types'; import { SmsScene } from '@dukang/shared-types'; import { request, type SessionPayload } from '../lib/api'; import { fetchClientConfig } from '../lib/pay-wechat'; import { isWxAuthorizeEnabled } from '@dukang/shared-types'; import { normalizePhoneInput, validateMobilePhone } from '../lib/phone'; import { useSmsCode } from '../lib/use-sms-code'; import { isWechatEnv } from '../lib/weixin'; import { loginWithWechatSdk, handleWechatAuthCallback as handleWechatOAuthCallback } from '../lib/wechat-auth'; import { useUserSession } from '../contexts/UserSessionContext'; import { touchPromoIfNeeded } from '../lib/promo'; async function finishLogin(navigate: (path: string) => void, returnTo: string) { await touchPromoIfNeeded(); navigate(returnTo.startsWith('/') ? returnTo : '/'); } export default function LoginPage() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const returnTo = searchParams.get('return') || '/'; const { applySession } = useUserSession(); const [phone, setPhone] = useState(''); const [code, setCode] = useState(''); const [loading, setLoading] = useState(false); const [agreed, setAgreed] = useState(false); const [msg, setMsg] = useState(''); const [wxSessionKey, setWxSessionKey] = useState(null); const [bindMode, setBindMode] = useState(false); const [wxAuthorize, setWxAuthorize] = useState(false); const agreementRef = useRef(null); const { sendCode, sending, codeCooldown, sentHint, error: smsError, setError: setSmsError, clearMessages } = useSmsCode(); useEffect(() => { fetchClientConfig() .then((config) => setWxAuthorize(isWxAuthorizeEnabled(config))) .catch(() => setWxAuthorize(false)); }, []); useEffect(() => { if (!isWechatEnv() || !wxAuthorize) return; handleWechatOAuthCallback() .then((result) => { if (!result) return; handleWechatLoginResult(result); }) .catch((e) => setMsg(e instanceof Error ? e.message : '微信登录失败')); }, [wxAuthorize]); function handleWechatLoginResult(result: WechatLoginResult) { if (result.accessToken) { applySession({ accessToken: result.accessToken, refreshToken: result.refreshToken ?? '', deviceKey: result.deviceKey, phoneVerified: !!result.phoneVerified, user: result.user as SessionPayload['user'], }); void finishLogin(navigate, returnTo); return; } if (result.needBindPhone && result.wxSessionKey) { setBindMode(true); setWxSessionKey(result.wxSessionKey); setMsg('微信授权成功,可绑定手机号(也可跳过,稍后在下单时再绑定)'); return; } } function ensureAgreed() { if (!agreed) { setMsg('请先勾选并同意用户协议'); agreementRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' }); return false; } return true; } async function onSendCode() { if (!ensureAgreed()) return; clearMessages(); setMsg(''); await sendCode(phone, bindMode ? SmsScene.BIND_PHONE : SmsScene.USER_LOGIN); } async function login() { if (!ensureAgreed()) return; const phoneCheck = validateMobilePhone(phone); if (!phoneCheck.ok) { setMsg(phoneCheck.message ?? '请输入正确的手机号码'); return; } if (!code.trim()) { setMsg('请输入验证码'); return; } setLoading(true); setMsg(''); setSmsError(''); try { if (bindMode && wxSessionKey) { const data = await request('USER_H5', '/auth/wechat/bind-phone', { method: 'POST', body: JSON.stringify({ wxSessionKey, phone, code }), }); handleWechatLoginResult(data); return; } const data = await request('USER_H5', '/auth/login/sms', { method: 'POST', body: JSON.stringify({ phone, code }), }); applySession(data); await finishLogin(navigate, returnTo); } catch (e) { setMsg(e instanceof Error ? e.message : '登录失败'); } finally { setLoading(false); } } async function wechatLogin() { if (!ensureAgreed()) return; setMsg(''); try { const result = await loginWithWechatSdk(); if (result) handleWechatLoginResult(result); } catch (e) { setMsg(e instanceof Error ? e.message : '微信登录失败'); } } const displayMsg = msg || smsError; return (
官方

欢迎来到杜康好客

买美酒,享好礼

{bindMode ? '绑定手机号' : '手机验证码登录'}

+86 { setPhone(normalizePhoneInput(e.target.value)); setMsg(''); clearMessages(); }} />
setCode(e.target.value.replace(/\D/g, '').slice(0, 6))} />
{(displayMsg || sentHint) && (

{displayMsg || sentHint}

)}
{!bindMode && wxAuthorize && ( <>
或者
)}
); }