用户端小程序修改

This commit is contained in:
2026-07-12 20:24:03 +08:00
parent e2dfb08de3
commit b267e885be
16 changed files with 551 additions and 214 deletions
+105 -34
View File
@@ -8,6 +8,12 @@ import UserTabBar, { shouldRenderPageTabBar, syncTabBarSelected } from '../../co
import { BRAND_LOGO_MARK_URL } from '@dukang/shared-types';
import { goLogin } from '../../lib/auth-nav';
import { bindWechatForUser } from '../../lib/wechat-auth';
import { fetchUserProfile } from '../../lib/pay-wechat';
import {
fetchMiniWechatUserInfo,
getCachedWxProfile,
mergeWxDisplayProfile,
} from '../../lib/mini-wechat-profile';
import { isLoggedIn, logout, request, toast, type UserProfile } from '../../lib/api';
const ORDER_SHORTCUTS = [
@@ -28,22 +34,21 @@ function formatMoney(amount: number) {
}
export default function MinePage() {
const loggedIn = isLoggedIn();
const [authed, setAuthed] = useState(() => isLoggedIn());
const [profile, setProfile] = useState<UserProfile | null>(null);
const [benefitBalance, setBenefitBalance] = useState(0);
const [orderCounts, setOrderCounts] = useState<Record<string, number>>({});
const [wxAuthorize, setWxAuthorize] = useState(true);
const [bindingWx, setBindingWx] = useState(false);
useDidShow(() => {
syncTabBarSelected(3);
if (isLoggedIn()) {
loadProfile();
}
});
function resetGuestState() {
setProfile(null);
setBenefitBalance(0);
setOrderCounts({});
}
function loadProfile() {
if (!loggedIn) return;
if (!isLoggedIn()) return;
Promise.all([
request<UserProfile>('/auth/me'),
request<Array<Record<string, unknown>>>('/benefit/coupons').catch(() => []),
@@ -52,7 +57,7 @@ export default function MinePage() {
),
])
.then(([me, coupons, ...totals]) => {
setProfile(me);
setProfile(mergeWxDisplayProfile(me));
const balance = (coupons as Array<Record<string, unknown>>).reduce((sum, c) => {
if (String(c.status) === 'ACTIVE') return sum + Number(c.balance || 0);
return sum;
@@ -67,30 +72,74 @@ export default function MinePage() {
.catch(() => {});
}
useDidShow(() => {
syncTabBarSelected(3);
const loggedInNow = isLoggedIn();
setAuthed(loggedInNow);
if (loggedInNow) {
loadProfile();
} else {
resetGuestState();
}
});
useEffect(() => {
request<ClientRuntimeConfig>('/common/client-config')
.then((config) => setWxAuthorize(isWxAuthorizeEnabled(config)))
.catch(() => setWxAuthorize(true));
}, []);
useEffect(() => {
loadProfile();
}, [loggedIn]);
async function handleAvatarTap() {
if (!loggedIn) {
if (!isLoggedIn()) {
goLogin('/pages/mine/index');
return;
}
if (profile?.hasWechat || !wxAuthorize || process.env.TARO_ENV !== 'weapp') return;
if (bindingWx) return;
let current = profile;
if (!current) {
try {
current = await fetchUserProfile();
setProfile(current);
} catch {
/* ignore */
}
}
if (current?.hasWechat) return;
if (!wxAuthorize) {
toast('当前环境未开启微信授权');
return;
}
if (process.env.TARO_ENV !== 'weapp') {
toast('请在微信小程序中完成微信授权');
return;
}
setBindingWx(true);
try {
const result = await bindWechatForUser();
let wxInfo = null;
try {
wxInfo = await fetchMiniWechatUserInfo();
} catch (e) {
toast(e instanceof Error ? e.message : '需要授权微信头像和昵称');
return;
}
const result = await bindWechatForUser(wxInfo);
if (!result.ok && result.needBindPhone) {
goLogin('/pages/mine/index', { bindMode: '1', wxSessionKey: result.wxSessionKey });
return;
}
if (result.ok) {
const merged = mergeWxDisplayProfile({
...(result.profile ?? {}),
id: result.profile?.id ?? profile?.id ?? '',
hasWechat: true,
nickname: result.profile?.nickname || wxInfo.nickname || profile?.nickname,
avatarUrl: result.profile?.avatarUrl || wxInfo.avatarUrl || profile?.avatarUrl,
});
setProfile(merged);
loadProfile();
toast('微信授权成功', 'success');
}
@@ -115,33 +164,46 @@ export default function MinePage() {
}
}
function renderAvatarContent(profile: UserProfile | null) {
if (profile?.avatarUrl) {
return <Image className="mine-avatar-img" src={profile.avatarUrl} mode="aspectFill" />;
function resolveDisplayProfile(profile: UserProfile | null, hasWechat: boolean) {
if (!profile) {
return { nickname: '用户', avatarUrl: null as string | null };
}
const merged = hasWechat ? mergeWxDisplayProfile(profile) : profile;
return {
nickname: merged.nickname || '用户',
avatarUrl: merged.avatarUrl || null,
};
}
function renderAvatarContent(displayAvatarUrl: string | null) {
if (displayAvatarUrl) {
return <Image className="mine-avatar-img" src={displayAvatarUrl} mode="aspectFill" />;
}
return <Image className="mine-avatar-img" src={BRAND_LOGO_MARK_URL} mode="aspectFit" />;
}
if (!loggedIn) {
if (!authed) {
return (
<PageShell variant="tab" className="mine-page">
<TabMainHeader title="我的" />
<View className="mine-header">
<View className="mine-header-texture" />
<View className="mine-profile">
<View className="mine-avatar">
<Image className="mine-avatar-img" src={BRAND_LOGO_MARK_URL} mode="aspectFit" />
<View className="mine-avatar-wrap mine-avatar-wrap--action" onClick={() => goLogin('/pages/mine/index')}>
<View className="mine-avatar mine-avatar--wx-pending">
<Image className="mine-avatar-img" src={BRAND_LOGO_MARK_URL} mode="aspectFit" />
</View>
</View>
<View>
<Text className="mine-profile-name"></Text>
<Text className="mine-member-tag"></Text>
<Text className="mine-member-tag"></Text>
</View>
</View>
</View>
<View className="mine-login-gate u-card">
<View className="u-empty"></View>
<View className="mine-login-gate">
<View className="mine-login-gate-hint"></View>
<View
className="u-btn u-btn--block"
className="mine-login-btn"
onClick={() => goLogin('/pages/mine/index')}
>
<Text></Text>
@@ -152,9 +214,11 @@ export default function MinePage() {
);
}
const nickname = profile?.nickname || '用户';
const hasWechat = !!profile?.hasWechat;
const memberLabel = hasWechat ? '微信会员' : '未授权微信';
const canWxBind = wxAuthorize && process.env.TARO_ENV === 'weapp';
const display = resolveDisplayProfile(profile, hasWechat);
const nickname = display.nickname;
const memberLabel = hasWechat ? '好客会员' : canWxBind ? '微信未授权' : '未授权微信';
return (
<PageShell variant="tab" className="mine-page">
@@ -162,12 +226,19 @@ export default function MinePage() {
<View className="mine-header">
<View className="mine-header-texture" />
<View className="mine-profile">
<View className="mine-avatar-wrap" onClick={() => void handleAvatarTap()}>
<View className="mine-avatar">
{renderAvatarContent(profile)}
<View
className={`mine-avatar-wrap${!hasWechat && canWxBind ? ' mine-avatar-wrap--action' : ''}`}
onClick={() => void handleAvatarTap()}
>
<View
className={`mine-avatar${hasWechat ? ' mine-avatar--wx-ok' : canWxBind ? ' mine-avatar--wx-pending' : ''}`}
>
{renderAvatarContent(display.avatarUrl)}
</View>
{!hasWechat && wxAuthorize && process.env.TARO_ENV === 'weapp' ? (
<Text className="mine-avatar-badge">{bindingWx ? '授权中' : '授权'}</Text>
{!hasWechat && canWxBind ? (
<View className="mine-avatar-status mine-avatar-status--pending">
<Text>{bindingWx ? '授权中' : '去授权'}</Text>
</View>
) : null}
</View>
<View>
@@ -175,7 +246,7 @@ export default function MinePage() {
<Text className={`mine-member-tag${hasWechat ? ' mine-member-tag--wechat' : ''}`}>
{memberLabel}
</Text>
{!hasWechat && wxAuthorize && process.env.TARO_ENV === 'weapp' ? (
{!hasWechat && canWxBind ? (
<Text className="mine-wechat-hint"></Text>
) : null}
</View>