feat: 新用户默认昵称及个人中心加载状态修复

- 新用户注册时随机生成普拉提主题默认昵称(16 个可选)
- 修复 App 启动后个人中心首次进入不展示用户信息的 bug
  - loggedIn 改为仅依赖 token 是否存在
  - 新增 hasProfile 判断用户数据是否已加载
  - 未加载时显示骨架屏而非空白
- 抽离随机函数为可注入依赖,消除 Math.random 测试耦合
This commit is contained in:
richarjiang
2026-04-05 10:21:58 +08:00
parent 982e569fa3
commit 640cfbf467
5 changed files with 89 additions and 11 deletions

View File

@@ -16,8 +16,8 @@
</button>
</view>
<!-- Logged in state -->
<view v-else class="user-card__user">
<!-- Logged in + profile loaded -->
<view v-else-if="loggedIn && hasProfile" class="user-card__user">
<view class="user-card__avatar-wrap">
<image
class="user-card__avatar-img"
@@ -36,10 +36,23 @@
<text v-if="maskedPhone" class="user-card__phone">{{ maskedPhone }}</text>
</view>
</view>
<!-- Logged in but profile still loading -->
<view v-else class="user-card__loading">
<view class="user-card__avatar-wrap">
<view class="user-card__avatar-skeleton" />
</view>
<view class="user-card__info">
<view class="user-card__name-row">
<view class="user-card__nickname-skeleton" />
</view>
<view class="user-card__phone-skeleton" />
</view>
</view>
</view>
<!-- Stats row: shown only when logged in -->
<view v-if="loggedIn" class="user-card__stats">
<!-- Stats row: shown only when profile is loaded -->
<view v-if="loggedIn && hasProfile" class="user-card__stats">
<view class="user-card__stat-item">
<text class="user-card__stat-value">{{ stats?.totalBookings ?? 0 }}</text>
<text class="user-card__stat-label">总训练()</text>
@@ -65,6 +78,7 @@ import { MembershipStatus } from '@mp-pilates/shared'
const props = defineProps<{
loggedIn: boolean
hasProfile: boolean
user: UserProfileResponse | null
stats: UserStatsResponse | null
memberships?: readonly MembershipWithCardType[]
@@ -235,6 +249,35 @@ function handleLogin() {
color: rgba(255, 255, 255, 0.75);
}
// ── Loading state ──
&__loading {
display: flex;
align-items: center;
gap: $spacing-md;
}
&__avatar-skeleton {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
}
&__nickname-skeleton {
width: 160rpx;
height: 36rpx;
border-radius: $radius-sm;
background: rgba(255, 255, 255, 0.3);
}
&__phone-skeleton {
width: 120rpx;
height: 26rpx;
border-radius: $radius-sm;
background: rgba(255, 255, 255, 0.2);
margin-top: 8rpx;
}
// ── Stats row ──
&__stats {
display: flex;