手机号从地址栏里获取

This commit is contained in:
2026-07-01 22:13:25 +08:00
parent 8a67d72de4
commit b78ef8798c
4 changed files with 55 additions and 4 deletions
@@ -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);
+36 -2
View File
@@ -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);