index.vue 8.49 KB
<template>
  <view class="min-h-screen bg-[#F5F7FA] relative flex flex-col items-center pb-[200rpx]">
    <!-- Professional Header Background -->
    <!-- Gradient background: Deep Blue to Tech Blue, fading to page background -->
    <view
      class="absolute top-0 left-0 w-full h-[500rpx] bg-gradient-to-b from-[#1E3A8A] via-[#2563EB] to-[#F5F7FA] z-0">
    </view>

    <!-- Header -->
    <NavHeader title="我的" background="transparent" textColor="#ffffff" />

    <!-- Content Container -->
    <view class="w-full px-[32rpx] z-10 pt-[20rpx]">

      <!-- User Info Card -->
      <view
        class="bg-white w-full rounded-[32rpx] shadow-sm flex items-center p-[40rpx] mb-[40rpx] relative overflow-hidden"
        @tap="go('/pages/avatar/index')">
        <!-- Subtle decorative circle -->
        <view
          class="absolute -top-[40rpx] -right-[40rpx] w-[200rpx] h-[200rpx] bg-blue-50 rounded-full opacity-60 pointer-events-none">
        </view>

        <!-- Avatar Area -->
        <view class="relative mr-[32rpx]">
          <view class="w-[136rpx] h-[136rpx] rounded-full p-[6rpx] bg-white shadow-sm border border-gray-100">
            <view class="w-full h-full rounded-full overflow-hidden bg-gray-50">
              <img class="w-full h-full object-cover" :src="userInfo?.avatar?.src || defaultAvatar" />
            </view>
          </view>
          <!-- Edit Badge -->
          <view
            class="absolute bottom-[4rpx] right-[4rpx] w-[40rpx] h-[40rpx] bg-white rounded-full flex items-center justify-center shadow-md border border-gray-50">
            <IconFont name="edit" size="12" color="#2563EB" />
          </view>
        </view>

        <!-- Info -->
        <view class="flex-1 flex flex-col justify-center z-10">
          <view class="flex items-center mb-[10rpx]">
            <text class="text-[38rpx] font-bold text-gray-900">{{ userInfo?.name || '加载中...' }}</text>
          </view>
          <view class="flex items-center">
            <text class="text-[26rpx] text-gray-500">工号: {{ userInfo?.employee_no || '--' }}</text>
          </view>
        </view>

        <!-- Arrow -->
        <IconFont name="rect-right" size="18" color="#9CA3AF" />
      </view>

      <!-- Menu List -->
      <!-- Added subtle styling to icons and softened the container -->
      <view class="bg-white w-full rounded-[32rpx] shadow-sm mb-[40rpx] p-[16rpx]">
        <view v-for="item in menuItems" :key="item.key"
          class="flex items-center justify-between p-[24rpx] rounded-[20rpx] active:bg-gray-50 transition-all duration-200"
          @tap="handleMenuClick(item)">
          <view class="flex items-center">
            <!-- Icon with soft background and badge -->
            <view class="w-[72rpx] h-[72rpx] rounded-[20rpx] flex items-center justify-center mr-[24rpx] relative"
              :class="item.bgClass">
              <IconFont :name="item.icon" size="22" :color="item.iconColor" />
              <!-- 消息红点(与 TabBar 样式保持一致)-->
              <view v-if="item.key === 'message' && showMessageBadge" class="menu-badge"></view>
            </view>
            <text class="text-[30rpx] text-gray-800 font-medium tracking-wide">{{ item.title }}</text>
          </view>
          <IconFont name="rectRight" size="14" color="#D1D5DB" />
        </view>
      </view>

      <!-- Logout Button -->
      <!-- Clean style: White background to match other cards, red text for action -->
      <view
        class="w-full py-[28rpx] rounded-[24rpx] bg-white shadow-sm active:bg-gray-50 flex items-center justify-center transition-colors duration-200"
        @tap="handleLogout"
      >
        <IconFont name="issue" size="16" color="#EF4444" class="mr-[12rpx]" />
        <text class="text-[28rpx] text-[#EF4444] font-medium">退出登录</text>
      </view>

    </view>

    <!-- Footer Copyright -->
    <view class="mt-[48rpx] flex flex-col items-center opacity-30">
      <text class="text-[20rpx] text-gray-400 font-medium tracking-wider uppercase">Manulife Professional</text>
    </view>

    <!-- TabBar -->
    <TabBar current="me" />
  </view>
</template>

<script setup>
import { computed } from 'vue'
import { useGo } from '@/hooks/useGo'
import { useUserStore } from '@/stores/user'
import IconFont from '@/components/icons/IconFont.vue'
import TabBar from '@/components/navigation/TabBar.vue'
import NavHeader from '@/components/navigation/NavHeader.vue'
import { features } from '@/config/features.js'
import Taro, { useLoad, useDidShow } from '@tarojs/taro'
import defaultAvatar from '@/assets/images/icon/avatar.svg'

const go = useGo()
const userStore = useUserStore()

/**
 * @description 用户信息(从 userStore 读取,自动响应变化)
 */
const userInfo = computed(() => userStore.userInfo)

/**
 * @description 是否显示消息红点(与 TabBar 逻辑一致)
 */
const showMessageBadge = computed(() => userStore.unreadMsgCount > 0)

/**
 * @description 页面加载时获取用户信息(首次进入)
 */
useLoad(() => {
  // 只在未登录时请求,避免与首页的 useDidShow 重复请求
  if (!userStore.isLoggedIn) {
    userStore.fetchUserInfo()
  }
})

/**
 * @description 页面显示时刷新用户信息(从其他页面返回时)
 * @description 例如:从头像设置页面保存后返回,需要刷新显示新头像
 * @description 带防抖机制(5秒内不重复请求)
 */
useDidShow(() => {
  // 从头像设置等页面返回时,强制刷新以显示最新数据
  // 使用 force=true 跳过防抖检查
  userStore.fetchUserInfo(true)
})

// Modern Professional Palette
// Using subtle background colors for icons to add vitality without being "playful"
const rawMenuItems = [
  {
    key: 'plan',
    title: '我的计划书',
    icon: 'order',
    path: '/pages/plan/index',
    iconColor: '#2563EB', // Blue
    bgClass: 'bg-blue-50'
  },
  {
    key: 'message',
    title: '我的消息',
    icon: 'message',
    path: '/pages/message/index',
    iconColor: '#059669', // Emerald (Trust)
    bgClass: 'bg-emerald-50'
  },
  {
    key: 'favorites',
    title: '我的收藏',
    icon: 'star',
    path: '/pages/favorites/index',
    iconColor: '#D97706', // Amber (Value)
    bgClass: 'bg-amber-50'
  },
  {
    key: 'help',
    title: '帮助中心',
    icon: 'service',
    path: '/pages/help-center/index',
    iconColor: '#4F46E5', // Indigo (Service)
    bgClass: 'bg-indigo-50'
  },
  {
    key: 'feedback',
    title: '意见反馈',
    icon: 'edit',
    path: '/pages/feedback-list/index',
    iconColor: '#DB2777', // Pink/Rose (Feedback)
    bgClass: 'bg-pink-50'
  }
]

// 根据功能配置过滤菜单项
const menuItems = computed(() => {
  return rawMenuItems.filter(item => {
    // 如果配置了 feedback 关闭,过滤掉意见反馈菜单
    if (item.key === 'feedback' && !features.feedback) {
      return false
    }
    return true
  })
})

const handleMenuClick = (item) => {
  if (item.path) {
    go(item.path)
  } else if (item.action === 'toast') {
    Taro.showToast({
      title: '功能开发中',
      icon: 'none'
    })
  }
}

/**
 * 退出登录
 * @description 调用 logoutAPI 解绑 openid,清除本地状态
 */
const handleLogout = async () => {
  Taro.showModal({
    title: '提示',
    content: '确定要退出登录吗?',
    confirmText: '确定',
    cancelText: '取消',
    confirmColor: '#EF4444',
    success: async (res) => {
      if (res.confirm) {
        // 显示加载提示
        Taro.showLoading({
          title: '退出中...',
          mask: true
        })

        try {
          // 调用 userStore 的 logout 方法(会调用 logoutAPI)
          await userStore.logout()

          Taro.hideLoading()

          // 跳转到首页
          Taro.reLaunch({
            url: '/pages/index/index'
          })

          Taro.showToast({
            title: '已退出登录',
            icon: 'success'
          })
        } catch (error) {
          Taro.hideLoading()
          console.error('退出登录失败:', error)
          Taro.showToast({
            title: error.message || '退出失败,请重试',
            icon: 'none'
          })
        }
      }
    }
  })
}
</script>

<style lang="less">
/**
 * 菜单红点样式(与 TabBar 保持一致)
 * @description 使用行业标准的小红点设计:右上角、16rpx、红色背景
 */
.menu-badge {
  position: absolute;
  top: -4rpx;
  right: -4rpx;
  width: 16rpx;
  height: 16rpx;
  background-color: #ff4d4f;
  border-radius: 50%;
  border: 2rpx solid #fff;
  box-shadow: 0 2rpx 8rpx rgba(255, 77, 79, 0.3);
  z-index: 1;
}
</style>