From 73dbf6effb28b0219632d9f290b130770df75f74 Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Tue, 21 Jul 2026 10:31:07 +0800 Subject: [PATCH] chore(deploy): stop tracking auto-release.env; feat(mini-user): avatar profile sheet Remove secrets file from the index (keep local). Mine page can choose avatar and nickname via WeChat components. Co-authored-by: Cursor --- apps/mini-user/src/pages/mine/index.tsx | 174 +++++++++++++++++++++--- apps/mini-user/src/styles/mine.css | 123 +++++++++++++++++ deploy/auto-release.env | 15 -- 3 files changed, 281 insertions(+), 31 deletions(-) delete mode 100644 deploy/auto-release.env diff --git a/apps/mini-user/src/pages/mine/index.tsx b/apps/mini-user/src/pages/mine/index.tsx index 47dbf86..c9b96a1 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 } from '@tarojs/components'; +import { View, Text, Image, Button, Input } from '@tarojs/components'; import Taro, { useDidShow } from '@tarojs/taro'; import { isWxAuthorizeEnabled, type ClientRuntimeConfig } from '@dukang/shared-types'; import PageShell from '../../components/PageShell'; @@ -10,8 +10,11 @@ import { goLogin } from '../../lib/auth-nav'; import { bindWechatForUser } from '../../lib/wechat-auth'; import { fetchMiniWechatUserInfo, + isDefaultMiniNickname, mergeWxDisplayProfile, needsWxProfileFill, + uploadAvatarTempFile, + uploadMiniWechatProfile, } from '../../lib/mini-wechat-profile'; import { isLoggedIn, logout, request, toast, type UserProfile } from '../../lib/api'; import { isWechatEnv } from '../../lib/weixin'; @@ -42,6 +45,11 @@ export default function MinePage() { const [orderCounts, setOrderCounts] = useState>({}); const [wxAuthorize, setWxAuthorize] = useState(true); const [bindingWx, setBindingWx] = useState(false); + const [profileSheetOpen, setProfileSheetOpen] = useState(false); + const [draftAvatarTemp, setDraftAvatarTemp] = useState(''); + const [draftAvatarUrl, setDraftAvatarUrl] = useState(''); + const [draftNickname, setDraftNickname] = useState(''); + const [savingProfile, setSavingProfile] = useState(false); function resetGuestState() { setProfile(null); @@ -135,22 +143,83 @@ export default function MinePage() { } return false; } catch (e) { - toast(e instanceof Error ? e.message : '微信授权失败'); + toast(e instanceof Error ? e.message : '授权失败'); return false; } finally { setBindingWx(false); } } - /** 未绑定微信时点击头像走授权(小程序 openId / H5 公众号 OAuth) */ + function openProfileSheet(me?: UserProfile | null) { + const base = mergeWxDisplayProfile( + me || profile || { id: '', nickname: null, avatarUrl: null, hasWechat: false }, + ); + setDraftAvatarTemp(''); + setDraftAvatarUrl(base.avatarUrl || ''); + setDraftNickname(isDefaultMiniNickname(base.nickname) ? '' : base.nickname || ''); + setProfileSheetOpen(true); + } + + /** 点击头像:绑定账号后用 chooseAvatar + nickname 获取头像昵称 */ async function handleAvatarTap() { if (!isLoggedIn()) { goLogin('/pages/mine/index'); return; } - if (profile?.hasWechat) return; - await ensureWechatBound(); - loadProfile(); + if (!isWeapp) { + if (!profile?.hasWechat) { + const ok = await ensureWechatBound(); + if (ok) loadProfile(); + } + return; + } + if (!profile?.hasWechat) { + const ok = await ensureWechatBound(); + if (!ok) return; + } + openProfileSheet(); + } + + async function onChooseAvatar(e: { detail?: { avatarUrl?: string } }) { + const tempPath = e.detail?.avatarUrl?.trim(); + if (!tempPath) { + toast('未获取到头像,请重试'); + return; + } + setDraftAvatarTemp(tempPath); + setDraftAvatarUrl(tempPath); + } + + async function saveWxProfile() { + const nickname = draftNickname.trim(); + if (!nickname) { + toast('请填写昵称'); + return; + } + if (!draftAvatarTemp && !draftAvatarUrl) { + toast('请选择头像'); + return; + } + setSavingProfile(true); + try { + if (!profile?.hasWechat) { + const ok = await ensureWechatBound(); + if (!ok) return; + } + let avatarUrl = draftAvatarUrl; + if (draftAvatarTemp) { + avatarUrl = await uploadAvatarTempFile(draftAvatarTemp); + } + const updated = await uploadMiniWechatProfile({ nickname, avatarUrl }); + if (updated) applyProfile({ ...updated, hasWechat: true }); + setProfileSheetOpen(false); + toast('头像昵称已更新', 'success'); + loadProfile(); + } catch (err) { + toast(err instanceof Error ? err.message : '保存失败'); + } finally { + setSavingProfile(false); + } } function handleService(item: (typeof SERVICES)[number]) { @@ -213,8 +282,16 @@ export default function MinePage() { throw new Error('wx profile helpers missing'); } const nickname = display.nickname || '用户'; - const memberLabel = hasWechat ? '好客会员' : canWxAuth ? '未完成授权' : '未完成授权'; - void needsWxProfileFill(display); + const needProfileFill = isWeapp && needsWxProfileFill(display); + const memberLabel = needProfileFill + ? '点击头像完善资料' + : hasWechat + ? '好客会员' + : canWxAuth + ? '点击头像授权' + : '好客会员'; + const avatarClickable = isWeapp || (!hasWechat && canWxAuth); + const previewAvatar = draftAvatarUrl || display.avatarUrl; return ( @@ -223,24 +300,37 @@ export default function MinePage() { void handleAvatarTap() : undefined} + className={`mine-avatar-wrap${avatarClickable ? ' mine-avatar-wrap--action' : ''}`} + onClick={avatarClickable ? () => void handleAvatarTap() : undefined} > {renderAvatarContent(display.avatarUrl)} - {!hasWechat && canWxAuth ? ( - - {bindingWx ? '授权中' : '去授权'} + {avatarClickable ? ( + + + {bindingWx ? '授权中' : needProfileFill || !hasWechat ? '去完善' : '更换'} + ) : null} - + void handleAvatarTap() : undefined} + > {nickname} - + {memberLabel} @@ -338,6 +428,58 @@ export default function MinePage() { {shouldRenderPageTabBar() ? : null} + + {profileSheetOpen ? ( + !savingProfile && setProfileSheetOpen(false)}> + e.stopPropagation()}> + 完善头像与昵称 + + 点击头像选择,并填写昵称后保存(用于个人中心展示) + + + + 昵称 + setDraftNickname(e.detail.value)} + onBlur={(e) => setDraftNickname(e.detail.value.trim())} + /> + + + !savingProfile && setProfileSheetOpen(false)} + > + 取消 + + void saveWxProfile()} + > + {savingProfile ? '保存中...' : '保存'} + + + + + ) : null} ); } diff --git a/apps/mini-user/src/styles/mine.css b/apps/mini-user/src/styles/mine.css index 4b19af3..f564d77 100644 --- a/apps/mini-user/src/styles/mine.css +++ b/apps/mini-user/src/styles/mine.css @@ -367,3 +367,126 @@ font-size: 15px; font-weight: 600; } + +.mine-profile-sheet-mask { + position: fixed; + inset: 0; + z-index: 1000; + background: rgba(20, 16, 14, 0.45); + display: flex; + align-items: flex-end; + justify-content: center; +} + +.mine-profile-sheet { + width: 100%; + max-width: 480px; + box-sizing: border-box; + background: #fff; + border-radius: 20px 20px 0 0; + padding: 20px 20px calc(20px + env(safe-area-inset-bottom, 0px)); +} + +.mine-profile-sheet-title { + display: block; + font-size: 17px; + font-weight: 700; + color: #1f1a17; + text-align: center; +} + +.mine-profile-sheet-hint { + display: block; + margin-top: 8px; + font-size: 12px; + line-height: 1.5; + color: #8d706e; + text-align: center; +} + +.mine-profile-avatar-btn { + margin: 20px auto 0; + padding: 0; + width: auto; + background: transparent; + border: none; + line-height: 1.2; + display: flex; + flex-direction: column; + align-items: center; +} + +.mine-profile-avatar-btn::after { + border: none; +} + +.mine-profile-avatar-preview { + width: 88px; + height: 88px; + border-radius: 50%; + overflow: hidden; + border: 2px solid rgba(166, 29, 36, 0.25); + background: #f7f4ef; +} + +.mine-profile-avatar-tip { + margin-top: 8px; + font-size: 12px; + color: var(--color-heritage-red); + font-weight: 600; +} + +.mine-profile-nickname-wrap { + margin-top: 18px; +} + +.mine-profile-nickname-label { + display: block; + font-size: 13px; + color: #5c504c; + margin-bottom: 8px; +} + +.mine-profile-nickname-input { + width: 100%; + box-sizing: border-box; + height: 44px; + padding: 0 14px; + border-radius: 12px; + background: #f7f4ef; + font-size: 15px; + color: #1f1a17; +} + +.mine-profile-sheet-actions { + margin-top: 22px; + display: flex; + gap: 12px; +} + +.mine-profile-sheet-cancel, +.mine-profile-sheet-save { + flex: 1; + height: 44px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 15px; + font-weight: 600; +} + +.mine-profile-sheet-cancel { + background: #f0ebe4; + color: #5c504c; +} + +.mine-profile-sheet-save { + background: var(--color-heritage-red); + color: #fff; +} + +.mine-profile-sheet-save.is-disabled { + opacity: 0.65; + pointer-events: none; +} diff --git a/deploy/auto-release.env b/deploy/auto-release.env deleted file mode 100644 index 1ddaa5d..0000000 --- a/deploy/auto-release.env +++ /dev/null @@ -1,15 +0,0 @@ -# 复制为 auto-release.env(勿提交 Git) -# 服务器路径: /opt/dukang-haoke/deploy/auto-release.env - -APP_ROOT=/opt/dukang-haoke -GIT_REMOTE=origin -GIT_BRANCH=dev -DEPLOY_GIT_REF=refs/heads/dev - -# Webhook 密钥(CodeUp 配置「Secret Token」时使用同一值) -DEPLOY_WEBHOOK_SECRET=f85777004ea4435bf8b4d5813de8255d286a85d34ce2b30a - -DEPLOY_WEBHOOK_HOST=127.0.0.1 -DEPLOY_WEBHOOK_PORT=8095 -DEPLOY_LOG_FILE=/var/log/dukang/deploy.log -DEPLOY_LOCK_FILE=/var/run/dukang-deploy.lock