zjk
昨天 f8c73a38709f7120d11b04bd70c6af3ccc53014d
hugray-user-miniapp/pages/profile/index.vue
@@ -7,11 +7,13 @@
        <view class="header">
            <view class="user-main" @tap="handleAccountTap">
                <view class="avatar">
                    <view class="icon" :class="isLoggedIn ? 'icon-camera' : 'icon-user'"></view>
                    <!-- 有头像则渲染图片,否则回退图标(已登录相机 / 未登录用户) -->
                    <image v-if="avatar" class="avatar-img" :src="avatar" mode="aspectFill" />
                    <view v-else class="icon" :class="isLoggedIn ? 'icon-camera' : 'icon-user'"></view>
                </view>
                <view class="user-text">
                    <!-- 已登录:昵称 + 会员等级;未登录:登录入口 + 引导文案 -->
                    <text class="user-name">{{ isLoggedIn ? nickname : '点击登录 / 注册' }}</text>
                    <text class="user-name">{{ isLoggedIn ? displayName : '点击登录 / 注册' }}</text>
                    <view v-if="isLoggedIn" class="level-pill">
                        <view class="level-medal">
                            <text class="level-medal-text">V</text>
@@ -26,7 +28,7 @@
            </view>
        </view>
        <!-- 收益数据展示区(343×74pt):未登录或 L0 普通用户不展示 -->
        <!-- 收益数据展示区(343×74pt):未登录或 C 普通用户不展示 -->
        <view v-if="!isRestrictedView" class="earnings-card">
            <view v-for="item in earnings" :key="item.key" class="earning-item">
                <text class="earning-label">{{ item.label }}</text>
@@ -71,6 +73,8 @@
import { computed, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { getToken } from '@/utils/request.js'
import { getMemberInfo } from '@/api/index.js'
import { formatYuan } from '@/utils/format.js'
import config from '@/config/index.js'
import StockOrderToast from '@/components/stock-order-toast/stock-order-toast.vue'
@@ -80,22 +84,34 @@
/** 登录态:本页需同时兼容「已登录 / 未登录」两种展示,故以本地 token 判定 */
const isLoggedIn = ref(false)
/** 用户昵称:用户信息接口未就绪,先本地 mock(联调后替换为 getUserInfo) */
const nickname = ref('辉煌G891')
/** 用户姓名/昵称:登录后由 getMemberInfo 下发;注册未采集,通常为空 */
const nickname = ref('')
/** 用户等级:取值 L0~L4,其中 L0 为普通用户;等级接口未联调,先本地 mock(联调后取 getUserInfo 下发等级) */
const userLevel = ref('L4')
/** 用户手机号:登录后由 getMemberInfo 下发(已脱敏,形如 133****5555) */
const phone = ref('')
/** 是否受限展示:未登录或 L0 普通用户,个人中心仅展示「收货地址」「联系我们」 */
const isRestrictedView = computed(() => !isLoggedIn.value || userLevel.value === 'L0')
/** 头部展示名:姓名优先,为空时回退展示脱敏手机号 */
const displayName = computed(() => nickname.value || phone.value)
/** 收益三栏:未登录态统一展示 0.00 */
/** 用户头像:登录后由 getMemberInfo 下发,为空时回退默认图标 */
const avatar = ref('')
/** 用户等级:取值 C/L1~L4,登录后由 getMemberInfo 下发;C 为普通用户 */
const userLevel = ref('C')
/** 是否受限展示:未登录或 C 普通用户,个人中心仅展示「收货地址」「联系我们」 */
const isRestrictedView = computed(() => !isLoggedIn.value || userLevel.value === 'C')
/** 收益汇总原始值(单位:分),登录后由 getMemberInfo 下发,未登录保持全零 */
const earningsRaw = ref({ today: 0, month: 0, total: 0 })
/** 收益三栏:后端下发单位为分,统一格式化为元展示;未登录统一展示 0.00 */
const earnings = computed(() => {
    const zeroValue = '0.00'
    const source = isLoggedIn.value ? earningsRaw.value : { today: 0, month: 0, total: 0 }
    return [
        { key: 'today', label: '今日收益', value: isLoggedIn.value ? '10.00' : zeroValue },
        { key: 'month', label: '本月收益', value: isLoggedIn.value ? '400.00' : zeroValue },
        { key: 'total', label: '累计收益', value: isLoggedIn.value ? '128000.00' : zeroValue }
        { key: 'today', label: '今日收益', value: formatYuan(source.today) },
        { key: 'month', label: '本月收益', value: formatYuan(source.month) },
        { key: 'total', label: '累计收益', value: formatYuan(source.total) }
    ]
})
@@ -109,18 +125,18 @@
    { key: 'contact', icon: 'contact', title: '联系我们' }
]
/** 当前可见菜单:受限展示(未登录 / L0)时仅保留「收货地址」「联系我们」 */
/** 当前可见菜单:受限展示(未登录 / C)时仅保留「收货地址」「联系我们」 */
const menuList = computed(() =>
    isRestrictedView.value ? MENU_LIST.filter((menu) => !menu.restricted) : MENU_LIST
)
/** 头像 / 名称 / 按钮点击:未登录跳登录页,已登录进入资料编辑 */
/** 头像 / 名称 / 编辑资料按钮点击:未登录跳登录页,已登录进入「我的资料」页 */
const handleAccountTap = () => {
    if (!isLoggedIn.value) {
        uni.reLaunch({ url: config.loginPage })
        return
    }
    uni.showToast({ title: '敬请期待', icon: 'none' })
    uni.navigateTo({ url: '/pages/profile/edit' })
}
/** 菜单项点击:未登录先提示登录;已登录进入二级功能页(未接入的暂为占位反馈) */
@@ -151,9 +167,37 @@
    uni.showToast({ title: '敬请期待', icon: 'none' })
}
// 每次页面显示均刷新登录态,保证登录 / 退出后展示态同步
/**
 * 拉取当前会员信息(昵称 / 头像 / 等级 / 收益汇总)
 * 失败时静默降级为普通用户态,不阻塞页面展示
 */
const loadMemberInfo = async () => {
    try {
        const res = await getMemberInfo()
        const data = res.data || {}
        nickname.value = data.nickname || ''
        // 手机号后端已脱敏(133****5555),可直接用于展示兜底
        phone.value = data.phone || ''
        avatar.value = data.avatar || ''
        // 等级缺省兜底 C,与后端 MemberLevelEnum.normalize 保持一致
        userLevel.value = data.memberLevel || 'C'
        earningsRaw.value = data.earnings || { today: 0, month: 0, total: 0 }
    } catch (error) {
        console.error('[PROFILE] 拉取会员信息失败', error)
        nickname.value = ''
        phone.value = ''
        avatar.value = ''
        userLevel.value = 'C'
        earningsRaw.value = { today: 0, month: 0, total: 0 }
    }
}
// 每次页面显示均刷新登录态,保证登录 / 退出后展示态同步;已登录则拉取会员信息
onShow(() => {
    isLoggedIn.value = Boolean(getToken())
    if (isLoggedIn.value) {
        loadMemberInfo()
    }
})
</script>
@@ -208,6 +252,13 @@
    border: 5rpx solid #ffffff;
}
/* 用户头像图片:铺满头像容器并裁为圆形 */
.avatar-img {
    width: 100%;
    height: 100%;
    border-radius: 50%;
}
/* 已登录:相机图标;未登录:用户图标 */
.icon-camera {
    width: 64rpx;