手机号从地址栏里获取
This commit is contained in:
@@ -4,11 +4,18 @@ import { normalizePhoneInput, validateMobilePhone } from '../lib/phone';
|
||||
|
||||
type PhoneVerifySheetProps = {
|
||||
open: boolean;
|
||||
/** 打开时预填手机号(如收货地址中的手机号) */
|
||||
defaultPhone?: string;
|
||||
onClose: () => void;
|
||||
onSuccess: () => void;
|
||||
};
|
||||
|
||||
export default function PhoneVerifySheet({ open, onClose, onSuccess }: PhoneVerifySheetProps) {
|
||||
export default function PhoneVerifySheet({
|
||||
open,
|
||||
defaultPhone,
|
||||
onClose,
|
||||
onSuccess,
|
||||
}: PhoneVerifySheetProps) {
|
||||
const [phone, setPhone] = useState('');
|
||||
const [code, setCode] = useState('');
|
||||
const [msg, setMsg] = useState('');
|
||||
@@ -21,8 +28,15 @@ export default function PhoneVerifySheet({ open, onClose, onSuccess }: PhoneVeri
|
||||
setCode('');
|
||||
setMsg('');
|
||||
setCodeCooldown(0);
|
||||
return;
|
||||
}
|
||||
}, [open]);
|
||||
if (defaultPhone) {
|
||||
const normalized = normalizePhoneInput(defaultPhone);
|
||||
if (validateMobilePhone(normalized).ok) {
|
||||
setPhone(normalized);
|
||||
}
|
||||
}
|
||||
}, [open, defaultPhone]);
|
||||
|
||||
async function sendCode() {
|
||||
const phoneCheck = validateMobilePhone(phone);
|
||||
|
||||
@@ -56,6 +56,15 @@ export function isLoggedIn() {
|
||||
return !!localStorage.getItem(ACCESS_TOKEN);
|
||||
}
|
||||
|
||||
const AUTH_RECOVERY_EXEMPT_PATHS = ['/auth/session/bootstrap', '/auth/token/refresh'];
|
||||
|
||||
async function recoverSession(): Promise<SessionPayload> {
|
||||
const refreshed = await refreshSession();
|
||||
if (refreshed) return refreshed;
|
||||
clearAuth();
|
||||
return bootstrapSession();
|
||||
}
|
||||
|
||||
async function rawRequest<T>(
|
||||
path: string,
|
||||
options: RequestInit = {},
|
||||
@@ -79,12 +88,34 @@ async function rawRequest<T>(
|
||||
return json.data as T;
|
||||
}
|
||||
|
||||
async function requestWithAuthRetry<T>(
|
||||
path: string,
|
||||
options: RequestInit = {},
|
||||
retried = false,
|
||||
): Promise<T> {
|
||||
try {
|
||||
return await rawRequest<T>(path, options);
|
||||
} catch (e) {
|
||||
const err = e as Error & { status?: number };
|
||||
const canRecover =
|
||||
err.status === 401 &&
|
||||
!retried &&
|
||||
!AUTH_RECOVERY_EXEMPT_PATHS.some((p) => path.startsWith(p));
|
||||
if (!canRecover) throw e;
|
||||
await recoverSession();
|
||||
return requestWithAuthRetry<T>(path, options, true);
|
||||
}
|
||||
}
|
||||
|
||||
export async function request<T>(
|
||||
_clientApp: string,
|
||||
path: string,
|
||||
options: RequestInit = {},
|
||||
): Promise<T> {
|
||||
return rawRequest<T>(path, options);
|
||||
if (!isLoggedIn() && !AUTH_RECOVERY_EXEMPT_PATHS.some((p) => path.startsWith(p))) {
|
||||
await bootstrapSession();
|
||||
}
|
||||
return requestWithAuthRetry<T>(path, options);
|
||||
}
|
||||
|
||||
export async function bootstrapSession(): Promise<SessionPayload> {
|
||||
@@ -145,7 +176,10 @@ export async function ensureSession(): Promise<SessionPayload> {
|
||||
}
|
||||
|
||||
export async function bindPhone(phone: string, code: string): Promise<SessionPayload> {
|
||||
const data = await rawRequest<SessionPayload>('/auth/phone/bind', {
|
||||
if (!isLoggedIn()) {
|
||||
await bootstrapSession();
|
||||
}
|
||||
const data = await requestWithAuthRetry<SessionPayload>('/auth/phone/bind', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ phone, code }),
|
||||
});
|
||||
|
||||
@@ -92,6 +92,8 @@ export default function AddressEditPage() {
|
||||
});
|
||||
}
|
||||
navigate(buildAddressListUrl(checkoutCtx));
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : '保存失败');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
|
||||
@@ -329,6 +329,7 @@ export default function OrderConfirmPage() {
|
||||
|
||||
<PhoneVerifySheet
|
||||
open={showPhoneVerify}
|
||||
defaultPhone={selectedAddress?.phone}
|
||||
onClose={() => {
|
||||
setShowPhoneVerify(false);
|
||||
setPendingSubmit(false);
|
||||
|
||||
Reference in New Issue
Block a user