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