微信小程序端登录和头像调整

This commit is contained in:
2026-07-21 08:59:44 +08:00
parent c5f526d51d
commit 350a086a73
10 changed files with 84 additions and 263 deletions
+29 -168
View File
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { View, Text, Image, Button, Input } from '@tarojs/components';
import { View, Text, Image } from '@tarojs/components';
import Taro, { useDidShow } from '@tarojs/taro';
import { isWxAuthorizeEnabled, type ClientRuntimeConfig } from '@dukang/shared-types';
import PageShell from '../../components/PageShell';
@@ -8,12 +8,10 @@ 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,
mergeWxDisplayProfile,
needsWxProfileFill,
uploadAvatarTempFile,
uploadMiniWechatProfile,
} from '../../lib/mini-wechat-profile';
import { isLoggedIn, logout, request, toast, type UserProfile } from '../../lib/api';
import { isWechatEnv } from '../../lib/weixin';
@@ -44,30 +42,15 @@ export default function MinePage() {
const [orderCounts, setOrderCounts] = useState<Record<string, number>>({});
const [wxAuthorize, setWxAuthorize] = useState(true);
const [bindingWx, setBindingWx] = useState(false);
const [savingProfile, setSavingProfile] = useState(false);
const [editingProfile, setEditingProfile] = useState(false);
const [draftNickname, setDraftNickname] = useState('');
function resetGuestState() {
setProfile(null);
setBenefitBalance(0);
setOrderCounts({});
setDraftNickname('');
setEditingProfile(false);
}
function applyProfile(me: UserProfile) {
const merged = mergeWxDisplayProfile(me);
setProfile(merged);
if (!needsWxProfileFill(merged)) {
setDraftNickname(merged.nickname || '');
setEditingProfile(false);
return;
}
// 缺头像但有真实昵称时预填
if (merged.nickname && !/^用户\d{4}$/.test(merged.nickname) && merged.nickname !== '微信用户') {
setDraftNickname(merged.nickname);
}
setProfile(mergeWxDisplayProfile(me));
}
function loadProfile() {
@@ -159,83 +142,13 @@ export default function MinePage() {
}
}
async function persistProfile(patch: { nickname?: string; avatarUrl?: string }, opts?: { silent?: boolean }) {
setSavingProfile(true);
try {
const bound = await ensureWechatBound();
if (!bound) return false;
const updated = await uploadMiniWechatProfile(patch);
if (updated) {
applyProfile(updated);
} else {
const me = await fetchUserProfile();
applyProfile(me);
}
if (!opts?.silent) toast('资料已更新', 'success');
return true;
} catch (e) {
toast(e instanceof Error ? e.message : '保存失败');
return false;
} finally {
setSavingProfile(false);
}
}
async function handleChooseAvatar(e: { detail?: { avatarUrl?: string } }) {
const tempPath = e.detail?.avatarUrl?.trim();
if (!tempPath) {
toast('未获取到头像');
return;
}
/** 未绑定微信时点击头像走授权(小程序 openId / H5 公众号 OAuth */
async function handleAvatarTap() {
if (!isLoggedIn()) {
goLogin('/pages/mine/index');
return;
}
try {
setSavingProfile(true);
const bound = await ensureWechatBound();
if (!bound) return;
const url = await uploadAvatarTempFile(tempPath);
setProfile((prev) =>
prev
? mergeWxDisplayProfile({ ...prev, hasWechat: true, avatarUrl: url })
: { id: '', hasWechat: true, avatarUrl: url, nickname: draftNickname || null },
);
setSavingProfile(false);
const ok = await persistProfile(
{
avatarUrl: url,
nickname: draftNickname.trim() || undefined,
},
{ silent: true },
);
if (ok) toast('头像已更新', 'success');
} catch (err) {
toast(err instanceof Error ? err.message : '头像更新失败');
setSavingProfile(false);
}
}
async function handleNicknameConfirm() {
const nickname = draftNickname.trim();
if (!nickname) {
toast('请填写微信昵称');
return;
}
if (!isLoggedIn()) {
goLogin('/pages/mine/index');
return;
}
await persistProfile({ nickname });
}
/** H5:点击头像走公众号 OAuth */
async function handleH5AvatarTap() {
if (!isLoggedIn()) {
goLogin('/pages/mine/index');
return;
}
if (profile?.hasWechat && !needsWxProfileFill(profile)) return;
if (profile?.hasWechat) return;
await ensureWechatBound();
loadProfile();
}
@@ -291,19 +204,17 @@ export default function MinePage() {
}
const hasWechat = !!profile?.hasWechat;
const needFill = needsWxProfileFill(profile) || editingProfile;
const canWxProfile = wxAuthorize && isWeapp;
const canWxAuth = wxAuthorize && (isWeapp || isWechatEnv());
const display = mergeWxDisplayProfile(
profile || { id: '', nickname: null, avatarUrl: null, hasWechat: false },
);
// 强制保留 common 导出,避免开发者工具「旧页 + 新 common」混用时报 is not a function
if (typeof needsWxProfileFill !== 'function' || typeof fetchMiniWechatUserInfo !== 'function') {
throw new Error('wx profile helpers missing');
}
const nickname = display.nickname || '用户';
const memberLabel = hasWechat
? needFill
? '完善头像昵称'
: '好客会员'
: canWxProfile
? '微信未授权'
: '未授权微信';
const memberLabel = hasWechat ? '好客会员' : canWxAuth ? '微信未授权' : '未授权微信';
void needsWxProfileFill(display);
return (
<PageShell variant="tab" className="mine-page no-tab-header">
@@ -311,77 +222,27 @@ export default function MinePage() {
<View className="mine-header">
<View className="mine-header-texture" />
<View className="mine-profile">
{canWxProfile ? (
<Button
className="mine-avatar-btn"
openType="chooseAvatar"
onChooseAvatar={(e) => void handleChooseAvatar(e)}
disabled={bindingWx || savingProfile}
>
<View
className={`mine-avatar${hasWechat && !needFill ? ' mine-avatar--wx-ok' : ' mine-avatar--wx-pending'}`}
>
{renderAvatarContent(display.avatarUrl)}
</View>
{needFill || !hasWechat ? (
<View className="mine-avatar-status mine-avatar-status--pending">
<Text>{bindingWx || savingProfile ? '处理中' : hasWechat ? '选头像' : '去授权'}</Text>
</View>
) : null}
</Button>
) : (
<View
className={`mine-avatar-wrap${!hasWechat && canWxAuth ? ' mine-avatar-wrap--action' : ''}`}
onClick={!hasWechat && canWxAuth ? () => void handleAvatarTap() : undefined}
>
<View
className={`mine-avatar-wrap${!hasWechat ? ' mine-avatar-wrap--action' : ''}`}
onClick={() => void handleH5AvatarTap()}
className={`mine-avatar${hasWechat ? ' mine-avatar--wx-ok' : ' mine-avatar--wx-pending'}`}
>
<View
className={`mine-avatar${hasWechat ? ' mine-avatar--wx-ok' : ' mine-avatar--wx-pending'}`}
>
{renderAvatarContent(display.avatarUrl)}
</View>
{!hasWechat ? (
<View className="mine-avatar-status mine-avatar-status--pending">
<Text>{bindingWx ? '授权中' : '去授权'}</Text>
</View>
) : null}
{renderAvatarContent(display.avatarUrl)}
</View>
)}
{!hasWechat && canWxAuth ? (
<View className="mine-avatar-status mine-avatar-status--pending">
<Text>{bindingWx ? '授权中' : '去授权'}</Text>
</View>
) : null}
</View>
<View className="mine-profile-meta">
{canWxProfile && needFill ? (
<>
<Input
className="mine-nickname-input"
type="nickname"
value={draftNickname}
placeholder="点击填写微信昵称"
maxlength={32}
onInput={(e) => setDraftNickname(e.detail.value)}
onBlur={() => void handleNicknameConfirm()}
onConfirm={() => void handleNicknameConfirm()}
/>
<Text className="mine-member-tag">{memberLabel}</Text>
<Text className="mine-wechat-hint"></Text>
</>
) : (
<>
<Text className="mine-profile-name">{nickname}</Text>
<Text className={`mine-member-tag${hasWechat && !needFill ? ' mine-member-tag--wechat' : ''}`}>
{memberLabel}
</Text>
{canWxProfile ? (
<Text
className="mine-wechat-hint mine-wechat-hint--link"
onClick={() => {
setDraftNickname(needsWxProfileFill(profile) ? '' : nickname);
setEditingProfile(true);
}}
>
</Text>
) : null}
</>
)}
<Text className="mine-profile-name">{nickname}</Text>
<Text className={`mine-member-tag${hasWechat ? ' mine-member-tag--wechat' : ''}`}>
{memberLabel}
</Text>
</View>
</View>
</View>