短信验证调试成功

This commit is contained in:
2026-07-06 13:18:56 +08:00
parent 5ba69eb935
commit 1c978b8adc
62 changed files with 2491 additions and 354 deletions
+35 -39
View File
@@ -2,22 +2,27 @@ import { useEffect, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import AppImage from '@dukang/shared-ui/AppImage';
import type { WechatLoginResult } from '@dukang/shared-types';
import { request, saveSession, type UserProfile } from '../lib/api';
import { SmsScene } from '@dukang/shared-types';
import { request, type SessionPayload } from '../lib/api';
import { normalizePhoneInput, validateMobilePhone } from '../lib/phone';
import { useSmsCode } from '../lib/use-sms-code';
import { isWechatEnv, weixinSdk } from '../lib/weixin';
import { useUserSession } from '../contexts/UserSessionContext';
export default function LoginPage() {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const returnTo = searchParams.get('return') || '/';
const [phone, setPhone] = useState('13800000001');
const [code, setCode] = useState('123456');
const { applySession } = useUserSession();
const [phone, setPhone] = useState('');
const [code, setCode] = useState('');
const [loading, setLoading] = useState(false);
const [agreed, setAgreed] = useState(true);
const [codeCooldown, setCodeCooldown] = useState(0);
const [msg, setMsg] = useState('');
const [wxSessionKey, setWxSessionKey] = useState<string | null>(null);
const [bindMode, setBindMode] = useState(false);
const { sendCode, sending, codeCooldown, sentHint, error: smsError, setError: setSmsError, clearMessages } =
useSmsCode();
useEffect(() => {
if (!isWechatEnv()) return;
@@ -38,12 +43,12 @@ export default function LoginPage() {
return;
}
if (result.accessToken) {
saveSession({
applySession({
accessToken: result.accessToken,
refreshToken: result.refreshToken ?? '',
deviceKey: result.deviceKey,
phoneVerified: !!result.phoneVerified,
user: result.user as never,
user: result.user as SessionPayload['user'],
});
navigate(returnTo.startsWith('/') ? returnTo : '/');
}
@@ -57,29 +62,11 @@ export default function LoginPage() {
return true;
}
async function sendCode() {
async function onSendCode() {
if (!ensureAgreed()) return;
const phoneCheck = validateMobilePhone(phone);
if (!phoneCheck.ok) {
setMsg(phoneCheck.message ?? '请输入正确的手机号码');
return;
}
clearMessages();
setMsg('');
await request('USER_H5', '/auth/sms/send', {
method: 'POST',
body: JSON.stringify({ phone, scene: bindMode ? 'BIND_PHONE' : 'USER_LOGIN' }),
});
setMsg('验证码已发送(Mock: 123456');
setCodeCooldown(60);
const timer = setInterval(() => {
setCodeCooldown((c) => {
if (c <= 1) {
clearInterval(timer);
return 0;
}
return c - 1;
});
}, 1000);
await sendCode(phone, bindMode ? SmsScene.BIND_PHONE : SmsScene.USER_LOGIN);
}
async function login() {
@@ -95,6 +82,7 @@ export default function LoginPage() {
}
setLoading(true);
setMsg('');
setSmsError('');
try {
if (bindMode && wxSessionKey) {
const data = await request<WechatLoginResult>('USER_H5', '/auth/wechat/bind-phone', {
@@ -104,15 +92,11 @@ export default function LoginPage() {
handleWechatLoginResult(data);
return;
}
const data = await request<{
accessToken: string;
refreshToken: string;
deviceKey?: string;
}>('USER_H5', '/auth/login/sms', {
const data = await request<SessionPayload>('USER_H5', '/auth/login/sms', {
method: 'POST',
body: JSON.stringify({ phone, code }),
});
saveSession(data);
applySession(data);
navigate(returnTo.startsWith('/') ? returnTo : '/');
} catch (e) {
setMsg(e instanceof Error ? e.message : '登录失败');
@@ -136,6 +120,8 @@ export default function LoginPage() {
}
}
const displayMsg = msg || smsError;
return (
<div className="login-page">
<header className="login-header">
@@ -164,6 +150,7 @@ export default function LoginPage() {
onChange={(e) => {
setPhone(normalizePhoneInput(e.target.value));
setMsg('');
clearMessages();
}}
/>
</div>
@@ -173,19 +160,28 @@ export default function LoginPage() {
inputMode="numeric"
className="login-field-input"
placeholder="请输入验证码"
maxLength={6}
value={code}
onChange={(e) => setCode(e.target.value)}
onChange={(e) => setCode(e.target.value.replace(/\D/g, '').slice(0, 6))}
/>
<button
type="button"
className={`login-get-code${codeCooldown > 0 ? ' disabled' : ''}`}
disabled={codeCooldown > 0}
onClick={sendCode}
className={`login-get-code${codeCooldown > 0 || sending ? ' disabled' : ''}`}
disabled={codeCooldown > 0 || sending}
onClick={onSendCode}
>
{codeCooldown > 0 ? `${codeCooldown}s 后重新获取` : '获取验证码'}
{sending
? '发送中...'
: codeCooldown > 0
? `${codeCooldown}s 后重新获取`
: '获取验证码'}
</button>
</div>
{msg && <p className="login-msg">{msg}</p>}
{(displayMsg || sentHint) && (
<p className={`login-msg${sentHint && !displayMsg ? ' login-msg--hint' : ''}`}>
{displayMsg || sentHint}
</p>
)}
<button
type="button"
className="login-sms-btn"