微信授权逻辑修改
This commit is contained in:
@@ -2,7 +2,7 @@ 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 { request } from '../lib/api';
|
||||
import { request, type UserProfile } from '../lib/api';
|
||||
import { useUserSession } from '../contexts/UserSessionContext';
|
||||
import ContactCustomerSheet from '../components/ContactCustomerSheet';
|
||||
|
||||
@@ -29,10 +29,8 @@ function formatMoney(amount: number) {
|
||||
|
||||
export default function MinePage() {
|
||||
const navigate = useNavigate();
|
||||
const { profile: sessionProfile, resetSession } = useUserSession();
|
||||
const [profile, setProfile] = useState<Record<string, unknown> | null>(
|
||||
sessionProfile as Record<string, unknown> | null,
|
||||
);
|
||||
const { profile: sessionProfile, refreshProfile, resetSession } = useUserSession();
|
||||
const [profile, setProfile] = useState<UserProfile | null>(sessionProfile);
|
||||
const [benefitBalance, setBenefitBalance] = useState(0);
|
||||
const [orderCounts, setOrderCounts] = useState<Record<string, number>>({});
|
||||
const [toast, setToast] = useState('');
|
||||
@@ -40,7 +38,7 @@ export default function MinePage() {
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
request<Record<string, unknown>>('USER_H5', '/auth/me'),
|
||||
request<UserProfile>('USER_H5', '/auth/me'),
|
||||
request<Array<Record<string, unknown>>>('USER_H5', '/benefit/coupons'),
|
||||
...ORDER_SHORTCUTS.map((s) =>
|
||||
request<{ total: number }>('USER_H5', `/trade/orders?tab=${s.tab}&pageSize=1`),
|
||||
@@ -48,6 +46,7 @@ export default function MinePage() {
|
||||
])
|
||||
.then(([me, coupons, ...totals]) => {
|
||||
setProfile(me);
|
||||
void refreshProfile();
|
||||
const balance = coupons.reduce((sum, c) => {
|
||||
if (String(c.status) === 'ACTIVE') return sum + Number(c.balance || 0);
|
||||
return sum;
|
||||
@@ -60,7 +59,7 @@ export default function MinePage() {
|
||||
setOrderCounts(counts);
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
}, [refreshProfile]);
|
||||
|
||||
function showToast(msg: string) {
|
||||
setToast(msg);
|
||||
@@ -77,9 +76,10 @@ export default function MinePage() {
|
||||
void resetSession().then(() => navigate('/'));
|
||||
}
|
||||
|
||||
const nickname = String(profile?.nickname || '用户');
|
||||
const userNo = String(profile?.userNo || '');
|
||||
const avatar = String(profile?.avatarUrl || DEFAULT_AVATAR);
|
||||
const nickname = profile?.nickname || '用户';
|
||||
const userNo = profile?.userNo || '';
|
||||
const avatar = profile?.avatarUrl || DEFAULT_AVATAR;
|
||||
const hasWechat = !!profile?.hasWechat;
|
||||
|
||||
return (
|
||||
<div className="mine-page">
|
||||
@@ -89,12 +89,17 @@ export default function MinePage() {
|
||||
<div className="mine-profile">
|
||||
<div className="mine-avatar-wrap">
|
||||
<AppImage src={avatar} alt="" wrapperClassName="mine-avatar app-image--fill" />
|
||||
{hasWechat && (
|
||||
<span className="mine-wechat-badge" title="已绑定微信">
|
||||
<span className="material-symbols-outlined">chat</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="mine-profile-info">
|
||||
<h1 className="mine-profile-name">{nickname}</h1>
|
||||
<div className="mine-profile-meta">
|
||||
{userNo && <span className="mine-profile-id">ID: {userNo}</span>}
|
||||
<span className="mine-member-tag">好客会员</span>
|
||||
<span className="mine-member-tag">{hasWechat ? '微信会员' : '好客会员'}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
|
||||
@@ -9,6 +9,12 @@ import { track } from '../lib/analytics';
|
||||
import PhoneVerifySheet from '../components/PhoneVerifySheet';
|
||||
import { useUserSession } from '../contexts/UserSessionContext';
|
||||
import { tryGetClientGpsLocation } from '../lib/client-location';
|
||||
import {
|
||||
applyWechatLoginResult,
|
||||
ensureWechatAuthForPay,
|
||||
handleWechatAuthCallback,
|
||||
} from '../lib/wechat-auth';
|
||||
import { isWechatEnv } from '../lib/weixin';
|
||||
|
||||
type Address = {
|
||||
id: string;
|
||||
@@ -55,6 +61,8 @@ export default function OrderConfirmPage() {
|
||||
const navigate = useNavigate();
|
||||
const { phoneVerified, refreshProfile } = useUserSession();
|
||||
const [showPhoneVerify, setShowPhoneVerify] = useState(false);
|
||||
const [showWechatBindPhone, setShowWechatBindPhone] = useState(false);
|
||||
const [wxSessionKey, setWxSessionKey] = useState<string | null>(null);
|
||||
const [pendingSubmit, setPendingSubmit] = useState(false);
|
||||
const productId = params.get('productId') || '';
|
||||
const forceCross = params.get('cross') === '1';
|
||||
@@ -84,6 +92,17 @@ export default function OrderConfirmPage() {
|
||||
}
|
||||
}, [productId, quantity]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isWechatEnv()) return;
|
||||
handleWechatAuthCallback()
|
||||
.then((result) => {
|
||||
if (result && applyWechatLoginResult(result)) {
|
||||
void refreshProfile();
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}, [refreshProfile]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!productId || !addressId) return;
|
||||
request<OrderPreview>('USER_H5', '/trade/orders/preview', {
|
||||
@@ -158,6 +177,33 @@ export default function OrderConfirmPage() {
|
||||
setShowPhoneVerify(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const authResult = await ensureWechatAuthForPay();
|
||||
if (!authResult.ok) {
|
||||
if ('needBindPhone' in authResult) {
|
||||
setWxSessionKey(authResult.wxSessionKey);
|
||||
setShowWechatBindPhone(true);
|
||||
setPendingSubmit(true);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setMsg('');
|
||||
try {
|
||||
await doSubmit();
|
||||
} catch (e) {
|
||||
setMsg(e instanceof Error ? e.message : '下单失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleWechatPhoneBound() {
|
||||
await refreshProfile();
|
||||
if (!pendingSubmit) return;
|
||||
setPendingSubmit(false);
|
||||
setLoading(true);
|
||||
setMsg('');
|
||||
try {
|
||||
@@ -347,6 +393,19 @@ export default function OrderConfirmPage() {
|
||||
}}
|
||||
onSuccess={handlePhoneVerified}
|
||||
/>
|
||||
|
||||
<PhoneVerifySheet
|
||||
open={showWechatBindPhone}
|
||||
mode="wechat_bind_phone"
|
||||
wxSessionKey={wxSessionKey ?? undefined}
|
||||
defaultPhone={selectedAddress?.phone}
|
||||
onClose={() => {
|
||||
setShowWechatBindPhone(false);
|
||||
setWxSessionKey(null);
|
||||
setPendingSubmit(false);
|
||||
}}
|
||||
onSuccess={handleWechatPhoneBound}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import type { WechatLoginResult, WechatPayOrderResult } from '@dukang/shared-types';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import type { WechatPayOrderResult } from '@dukang/shared-types';
|
||||
import SubPageHeader from '../components/SubPageHeader';
|
||||
import PhoneVerifySheet from '../components/PhoneVerifySheet';
|
||||
import { request } from '../lib/api';
|
||||
import { buildOrderConfirmUrl } from '../lib/navigation';
|
||||
import {
|
||||
authorizeWechatForPay,
|
||||
buildLoginReturnUrl,
|
||||
fetchClientConfig,
|
||||
fetchUserProfile,
|
||||
isWechatAuthRequiredError,
|
||||
needsWechatAuthForPay,
|
||||
saveWechatLoginResult,
|
||||
} from '../lib/pay-wechat';
|
||||
import { isWechatEnv, weixinSdk } from '../lib/weixin';
|
||||
import { applyWechatLoginResult, handleWechatAuthCallback } from '../lib/wechat-auth';
|
||||
import { isWechatEnv } from '../lib/weixin';
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
@@ -30,7 +31,6 @@ async function waitOrderPaid(orderId: string, maxAttempts = 15) {
|
||||
|
||||
export default function PayPage() {
|
||||
const [params] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const orderId = params.get('orderId') || '';
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -38,6 +38,8 @@ export default function PayPage() {
|
||||
const [mockMode, setMockMode] = useState(true);
|
||||
const [needsWechatAuth, setNeedsWechatAuth] = useState(false);
|
||||
const [msg, setMsg] = useState('');
|
||||
const [showBindPhone, setShowBindPhone] = useState(false);
|
||||
const [wxSessionKey, setWxSessionKey] = useState<string | null>(null);
|
||||
|
||||
const refreshPayReadiness = useCallback(async () => {
|
||||
try {
|
||||
@@ -51,10 +53,11 @@ export default function PayPage() {
|
||||
}, []);
|
||||
|
||||
const handleWechatLoginResult = useCallback(
|
||||
async (result: WechatLoginResult) => {
|
||||
async (result: Parameters<typeof applyWechatLoginResult>[0]) => {
|
||||
if (result.needBindPhone && result.wxSessionKey) {
|
||||
setMsg('微信授权成功,请先绑定手机号');
|
||||
navigate(buildLoginReturnUrl(location.pathname, location.search));
|
||||
setWxSessionKey(result.wxSessionKey);
|
||||
setShowBindPhone(true);
|
||||
setMsg('');
|
||||
return;
|
||||
}
|
||||
if (saveWechatLoginResult(result)) {
|
||||
@@ -62,7 +65,7 @@ export default function PayPage() {
|
||||
await refreshPayReadiness();
|
||||
}
|
||||
},
|
||||
[location.pathname, location.search, navigate, refreshPayReadiness],
|
||||
[refreshPayReadiness],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -71,8 +74,7 @@ export default function PayPage() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!isWechatEnv()) return;
|
||||
weixinSdk
|
||||
.handleOAuthCallback()
|
||||
handleWechatAuthCallback()
|
||||
.then((result) => {
|
||||
if (result) void handleWechatLoginResult(result);
|
||||
})
|
||||
@@ -121,6 +123,7 @@ export default function PayPage() {
|
||||
|
||||
if (result.mode === 'jsapi' && result.prepay) {
|
||||
setMockMode(false);
|
||||
const { weixinSdk } = await import('../lib/weixin');
|
||||
await weixinSdk.pay(result.prepay);
|
||||
const paid = await waitOrderPaid(orderId);
|
||||
if (!paid) {
|
||||
@@ -190,6 +193,19 @@ export default function PayPage() {
|
||||
{loading ? '支付中...' : needsWechatAuth ? '请先授权微信' : '确认支付'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<PhoneVerifySheet
|
||||
open={showBindPhone}
|
||||
mode="wechat_bind_phone"
|
||||
wxSessionKey={wxSessionKey ?? undefined}
|
||||
onClose={() => {
|
||||
setShowBindPhone(false);
|
||||
setWxSessionKey(null);
|
||||
}}
|
||||
onSuccess={() => {
|
||||
void refreshPayReadiness();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user