diff --git a/apps/mini-user/project.config.json b/apps/mini-user/project.config.json index 983c63d..c34e3d6 100644 --- a/apps/mini-user/project.config.json +++ b/apps/mini-user/project.config.json @@ -10,5 +10,6 @@ "postcss": false, "minified": false }, - "compileType": "miniprogram" + "compileType": "miniprogram", + "preloadBackgroundData": false } diff --git a/apps/mini-user/src/lib/mini-wechat-profile.ts b/apps/mini-user/src/lib/mini-wechat-profile.ts index a401a97..4a92353 100644 --- a/apps/mini-user/src/lib/mini-wechat-profile.ts +++ b/apps/mini-user/src/lib/mini-wechat-profile.ts @@ -1,5 +1,5 @@ import Taro from '@tarojs/taro'; -import { API_BASE, CLIENT_APP, getToken, request, type UserProfile } from './api'; +import type { UserProfile } from './api'; export type MiniWechatProfile = { nickname?: string; @@ -57,6 +57,7 @@ export function mergeWxDisplayProfile(profile: UserProfile): UserProfile { /** 上传 chooseAvatar 临时文件到 OSS,返回永久 URL */ export async function uploadAvatarTempFile(tempFilePath: string): Promise { + const { API_BASE, CLIENT_APP, getToken } = await import('./api'); const token = getToken(); if (!token) throw new Error('请先登录'); @@ -91,6 +92,7 @@ export async function uploadAvatarTempFile(tempFilePath: string): Promise { if (!info.nickname && !info.avatarUrl) return null; + const { request } = await import('./api'); const updated = await request('/auth/wechat/mini-profile', { method: 'POST', data: info, @@ -103,26 +105,16 @@ export async function uploadMiniWechatProfile(info: MiniWechatProfile): Promise< } /** - * @deprecated getUserProfile 已收回真实头像昵称,仅作兼容;小程序请用 chooseAvatar + nickname + * 兼容旧调用:getUserProfile 已无法拿到真实头像昵称。 + * 始终导出为函数,避免循环依赖/旧包出现 “is not a function”。 */ export async function fetchMiniWechatUserInfo(): Promise { - if (process.env.TARO_ENV !== 'weapp') { - throw new Error('请在微信小程序中授权'); + const cached = getCachedWxProfile(); + if (cached?.nickname || cached?.avatarUrl) { + return cached; } - const res = await Taro.getUserProfile({ desc: '用于完善会员资料' }); - const info: MiniWechatProfile = { - nickname: res.userInfo?.nickName?.trim(), - avatarUrl: res.userInfo?.avatarUrl?.trim(), - }; - if (!info.nickname && !info.avatarUrl) { - throw new Error('未获取到微信头像或昵称'); - } - // 灰色默认头像 / 「微信用户」视为无效,需走填写能力 - if (info.nickname === '微信用户' || !info.avatarUrl) { - throw new Error('请使用头像昵称填写能力完善资料'); - } - cacheWxProfile(info); - return info; + // 不再弹 getUserProfile;引导走「我的」页 chooseAvatar / nickname + throw new Error('请在「我的」页点击头像完善微信头像和昵称'); } /** 绑定后上报微信资料(优先使用已拉取的信息) */ @@ -130,9 +122,8 @@ export async function syncMiniWechatProfile( prefetched?: MiniWechatProfile | null, ): Promise { if (process.env.TARO_ENV !== 'weapp') return null; - if (!prefetched?.nickname && !prefetched?.avatarUrl) { - return getCachedWxProfile(); - } - await uploadMiniWechatProfile(prefetched); - return prefetched; + const info = prefetched?.nickname || prefetched?.avatarUrl ? prefetched : getCachedWxProfile(); + if (!info?.nickname && !info?.avatarUrl) return null; + await uploadMiniWechatProfile(info); + return info; } diff --git a/apps/mini-user/src/lib/wechat-auth.ts b/apps/mini-user/src/lib/wechat-auth.ts index 5e3fb73..d4eb6fd 100644 --- a/apps/mini-user/src/lib/wechat-auth.ts +++ b/apps/mini-user/src/lib/wechat-auth.ts @@ -3,7 +3,32 @@ import { isWxAuthorizeEnabled } from '@dukang/shared-types'; import { stripOAuthParamsFromLocation } from '@dukang/weixin-sdk'; import Taro from '@tarojs/taro'; import { saveAuth } from './api'; -import { syncMiniWechatProfile } from './mini-wechat-profile'; +import { + fetchMiniWechatUserInfo, + mergeWxDisplayProfile, + needsWxProfileFill, + syncMiniWechatProfile, +} from './mini-wechat-profile'; + +/** + * 兼容旧分包对资料 helper 的引用,避免 tree-shake 后出现 is not a function + *(开发者工具热更新时常见旧页 + 新 common 混用) + */ +export { fetchMiniWechatUserInfo, mergeWxDisplayProfile, needsWxProfileFill }; + +/** 强制保留导出绑定,防止打包器删掉未引用的 re-export */ +const _wxProfileCompat = { + fetchMiniWechatUserInfo, + mergeWxDisplayProfile, + needsWxProfileFill, +}; +if ( + typeof _wxProfileCompat.needsWxProfileFill !== 'function' || + typeof _wxProfileCompat.fetchMiniWechatUserInfo !== 'function' || + typeof _wxProfileCompat.mergeWxDisplayProfile !== 'function' +) { + throw new Error('mini-wechat-profile helpers missing'); +} import { authorizeWechatForPay, fetchClientConfig, diff --git a/apps/mini-user/src/pages/benefit/index.tsx b/apps/mini-user/src/pages/benefit/index.tsx index 3b9e2e5..993f9d4 100644 --- a/apps/mini-user/src/pages/benefit/index.tsx +++ b/apps/mini-user/src/pages/benefit/index.tsx @@ -106,14 +106,6 @@ export default function BenefitPage() { - - Taro.navigateTo({ url: '/pages/benefit-detail/index' })} - > - 查看权益明细 › - - Taro.navigateTo({ url: '/pages/redeem/index' })} diff --git a/apps/mini-user/src/pages/login/index.tsx b/apps/mini-user/src/pages/login/index.tsx index 8940aaa..705622c 100644 --- a/apps/mini-user/src/pages/login/index.tsx +++ b/apps/mini-user/src/pages/login/index.tsx @@ -234,7 +234,7 @@ export default function LoginPage() { setSentHint(''); setWxLoading(true); try { - // 头像昵称改由「我的」页 chooseAvatar / nickname 填写;登录仅换 openId + // 登录仅绑定 openId;资料展示走账号昵称/头像 const wxInfo = getCachedWxProfile(); if (completeMode === 'wechat' && isLoggedIn()) { diff --git a/apps/mini-user/src/pages/mine/index.tsx b/apps/mini-user/src/pages/mine/index.tsx index 31e60a5..8c5ba6d 100644 --- a/apps/mini-user/src/pages/mine/index.tsx +++ b/apps/mini-user/src/pages/mine/index.tsx @@ -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>({}); 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 ( @@ -311,77 +222,27 @@ export default function MinePage() { - {canWxProfile ? ( - - ) : ( + void handleAvatarTap() : undefined} + > void handleH5AvatarTap()} + className={`mine-avatar${hasWechat ? ' mine-avatar--wx-ok' : ' mine-avatar--wx-pending'}`} > - - {renderAvatarContent(display.avatarUrl)} - - {!hasWechat ? ( - - {bindingWx ? '授权中' : '去授权'} - - ) : null} + {renderAvatarContent(display.avatarUrl)} - )} + {!hasWechat && canWxAuth ? ( + + {bindingWx ? '授权中' : '去授权'} + + ) : null} + - {canWxProfile && needFill ? ( - <> - setDraftNickname(e.detail.value)} - onBlur={() => void handleNicknameConfirm()} - onConfirm={() => void handleNicknameConfirm()} - /> - {memberLabel} - 点击头像选微信头像,并填写昵称 - - ) : ( - <> - {nickname} - - {memberLabel} - - {canWxProfile ? ( - { - setDraftNickname(needsWxProfileFill(profile) ? '' : nickname); - setEditingProfile(true); - }} - > - 更新头像昵称 - - ) : null} - - )} + {nickname} + + {memberLabel} + diff --git a/apps/mini-user/src/pages/redeem/index.tsx b/apps/mini-user/src/pages/redeem/index.tsx index e17cfee..ac7bb21 100644 --- a/apps/mini-user/src/pages/redeem/index.tsx +++ b/apps/mini-user/src/pages/redeem/index.tsx @@ -101,8 +101,10 @@ export default function RedeemPage() { className="redeem-input" type="digit" placeholder="输入核销金额" + placeholderClass="redeem-input-placeholder" value={amount} onInput={(e) => setAmount(e.detail.value)} + style={{ textAlign: 'center' }} /> diff --git a/apps/mini-user/src/styles/benefit.css b/apps/mini-user/src/styles/benefit.css index 2f1775c..9f79285 100644 --- a/apps/mini-user/src/styles/benefit.css +++ b/apps/mini-user/src/styles/benefit.css @@ -120,19 +120,7 @@ font-size: 18px; } -.benefit-hero-actions { - display: flex; - margin-top: 8px; -} - -.benefit-hero-link { - font-size: 13px; - color: var(--color-heritage-red); - margin-right: 16px; -} - .benefit-hero-cta { - flex: 1; height: 44px; border-radius: var(--radius-full); background: var(--color-heritage-red); diff --git a/apps/mini-user/src/styles/mine.css b/apps/mini-user/src/styles/mine.css index a9809c1..4b19af3 100644 --- a/apps/mini-user/src/styles/mine.css +++ b/apps/mini-user/src/styles/mine.css @@ -46,42 +46,11 @@ cursor: pointer; } -/* 小程序 chooseAvatar:去掉 Button 默认样式,保留圆形头像 */ -.mine-avatar-btn { - position: relative; - flex-shrink: 0; - margin: 0 12px 0 0 !important; - padding: 0 !important; - background: transparent !important; - border: none !important; - border-radius: 0 !important; - line-height: 1 !important; - overflow: visible !important; -} - -.mine-avatar-btn::after { - border: none !important; -} - .mine-profile-meta { min-width: 0; flex: 1; } -.mine-nickname-input { - width: 100%; - max-width: 200px; - height: 32px; - padding: 0 10px; - margin-bottom: 4px; - border-radius: 8px; - background: rgba(255, 255, 255, 0.95); - color: var(--color-ink-black); - font-size: 15px; - font-weight: 600; - box-sizing: border-box; -} - .mine-avatar-status { position: absolute; left: 50%; @@ -122,18 +91,6 @@ background: rgba(255, 255, 255, 0.25); } -.mine-wechat-hint { - display: block; - margin-top: 4px; - font-size: 10px; - color: rgba(255, 255, 255, 0.75); -} - -.mine-wechat-hint--link { - text-decoration: underline; - text-underline-offset: 2px; -} - .mine-avatar { width: 64px; height: 64px; diff --git a/apps/mini-user/src/styles/redeem.css b/apps/mini-user/src/styles/redeem.css index 3bae355..bd2fe36 100644 --- a/apps/mini-user/src/styles/redeem.css +++ b/apps/mini-user/src/styles/redeem.css @@ -47,16 +47,20 @@ color: var(--color-heritage-red); text-align: center; box-sizing: border-box; - display: flex; - align-items: center; - justify-content: center; } +.redeem-input-placeholder { + color: var(--color-subtle-gray); + text-align: center; +} + +/* 小程序原生 input:text-align 需落到组件自身与内部节点 */ +.redeem-input, .redeem-input input, .redeem-input .taro-input, .redeem-input .weui-input { width: 100% !important; - height: 100% !important; + height: 52px !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; @@ -65,8 +69,8 @@ box-sizing: border-box !important; font-size: 24px !important; font-weight: 700 !important; - line-height: normal !important; - color: inherit; + line-height: 52px !important; + color: var(--color-heritage-red); text-align: center !important; }