用户静默注册

This commit is contained in:
2026-07-01 02:48:59 +08:00
parent d3dd3a0ad1
commit 9f4577d3d8
18 changed files with 912 additions and 93 deletions
+7 -3
View File
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import AppImage from '@dukang/shared-ui/AppImage';
import { request, saveAuth } from '../lib/api';
import { request, saveSession } from '../lib/api';
import { normalizePhoneInput, validateMobilePhone } from '../lib/phone';
export default function LoginPage() {
@@ -60,11 +60,15 @@ export default function LoginPage() {
setLoading(true);
setMsg('');
try {
const data = await request<{ accessToken: string; refreshToken: string }>('USER_H5', '/auth/login/sms', {
const data = await request<{
accessToken: string;
refreshToken: string;
deviceKey?: string;
}>('USER_H5', '/auth/login/sms', {
method: 'POST',
body: JSON.stringify({ phone, code }),
});
saveAuth(data);
saveSession(data);
navigate('/');
} catch (e) {
setMsg(e instanceof Error ? e.message : '登录失败');
+7 -4
View File
@@ -2,7 +2,8 @@ import { useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import TabMainHeader from '../components/TabMainHeader';
import AppImage from '@dukang/shared-ui/AppImage';
import { clearAuth, request } from '../lib/api';
import { request } from '../lib/api';
import { useUserSession } from '../contexts/UserSessionContext';
const DEFAULT_AVATAR =
'https://lh3.googleusercontent.com/aida-public/AB6AXuAz_9Pnpk_Md4sEU6PXkeybus8oLZO9e-3pOpLuSwBX0jm_Z0JCfX1w2oZxz1VZayTh0PKUPjwjSuxJVX410fjtWFGR_f55f-nWppXWUweHRnEC7WyIWEqx4AyVHt-k02OhyaSGQfvY5cHG5IuRe9EqdcHy47gBQ82_cxGgX-DrKV4oYcwLoNRynAV0_xv2p1GOhisnQVulHwZcQClUJcP8q4nTY0Y3DR1w4ioa0DYTHePE43mLDJptjZcQqS7V8LihJdn4ze6fvQA';
@@ -27,7 +28,10 @@ function formatMoney(amount: number) {
export default function MinePage() {
const navigate = useNavigate();
const [profile, setProfile] = useState<Record<string, unknown> | null>(null);
const { profile: sessionProfile, resetSession } = useUserSession();
const [profile, setProfile] = useState<Record<string, unknown> | null>(
sessionProfile as Record<string, unknown> | null,
);
const [benefitBalance, setBenefitBalance] = useState(0);
const [orderCounts, setOrderCounts] = useState<Record<string, number>>({});
const [toast, setToast] = useState('');
@@ -68,8 +72,7 @@ export default function MinePage() {
}
function logout() {
clearAuth();
navigate('/login');
void resetSession().then(() => navigate('/'));
}
const nickname = String(profile?.nickname || '用户');
+56 -17
View File
@@ -6,6 +6,9 @@ import { request } from '../lib/api';
import { buildProductDetailUrl } from '../lib/navigation';
import { STITCH_ORDER_PRODUCT_IMAGE } from '../lib/order-images';
import { tryGetClientGpsLocation } from '../lib/client-location';
import { getProductMainImage } from '../lib/product-images';
import PhoneVerifySheet from '../components/PhoneVerifySheet';
import { useUserSession } from '../contexts/UserSessionContext';
type Address = {
id: string;
@@ -48,6 +51,9 @@ function formatAddress(a: Address) {
export default function OrderConfirmPage() {
const [params] = useSearchParams();
const navigate = useNavigate();
const { phoneVerified, refreshProfile } = useUserSession();
const [showPhoneVerify, setShowPhoneVerify] = useState(false);
const [pendingSubmit, setPendingSubmit] = useState(false);
const productId = params.get('productId') || '';
const forceCross = params.get('cross') === '1';
const [quantity, setQuantity] = useState(Number(params.get('qty') || 2));
@@ -111,31 +117,55 @@ export default function OrderConfirmPage() {
const productImage =
productIndex === 0 ? STITCH_ORDER_PRODUCT_IMAGE : getProductMainImage(productIndex);
async function doSubmit() {
const clientLocation = await tryGetClientGpsLocation();
const order = await request<{ id: string }>('USER_H5', '/trade/orders', {
method: 'POST',
body: JSON.stringify({
productId,
quantity,
addressId,
...(clientLocation ? { clientLocation } : {}),
}),
});
const qs = new URLSearchParams();
qs.set('orderId', order.id);
qs.set('productId', productId);
qs.set('qty', String(quantity));
qs.set('addressId', addressId);
if (forceCross) qs.set('cross', '1');
navigate(`/pay?${qs.toString()}`);
}
async function submit() {
if (!addressId) {
setMsg('请选择收货地址');
return;
}
if (!phoneVerified) {
setPendingSubmit(true);
setShowPhoneVerify(true);
return;
}
setLoading(true);
setMsg('');
try {
const clientLocation = await tryGetClientGpsLocation();
const order = await request<{ id: string }>('USER_H5', '/trade/orders', {
method: 'POST',
body: JSON.stringify({
productId,
quantity,
addressId,
...(clientLocation ? { clientLocation } : {}),
}),
});
const qs = new URLSearchParams();
qs.set('orderId', order.id);
qs.set('productId', productId);
qs.set('qty', String(quantity));
qs.set('addressId', addressId);
if (forceCross) qs.set('cross', '1');
navigate(`/pay?${qs.toString()}`);
await doSubmit();
} catch (e) {
setMsg(e instanceof Error ? e.message : '下单失败');
} finally {
setLoading(false);
}
}
async function handlePhoneVerified() {
await refreshProfile();
if (!pendingSubmit) return;
setPendingSubmit(false);
setLoading(true);
setMsg('');
try {
await doSubmit();
} catch (e) {
setMsg(e instanceof Error ? e.message : '下单失败');
} finally {
@@ -296,6 +326,15 @@ export default function OrderConfirmPage() {
</button>
</div>
</footer>
<PhoneVerifySheet
open={showPhoneVerify}
onClose={() => {
setShowPhoneVerify(false);
setPendingSubmit(false);
}}
onSuccess={handlePhoneVerified}
/>
</div>
);
}