ProfilePage.vue 8.76 KB
<template>
  <AppLayout title="我的" :right-content="rightContent">
    <div class="bg-gradient-to-br from-green-50 via-green-100/30 to-blue-50/30 min-h-screen">
      <!-- User Profile Header with Enhanced Design -->
      <div class="pt-6 pb-8 relative">
        <div class="absolute inset-0 bg-gradient-to-r from-green-500 to-blue-500 opacity-15"></div>
        <div class="relative z-10 flex flex-col items-center">
          <div class="w-24 h-24 rounded-full overflow-hidden border-4 border-white shadow-lg mb-4">
            <img
              :src="profile.avatar || '/assets/images/user-avatar-1.jpg'"
              :alt="profile.name"
              class="w-full h-full object-cover"
              @error="handleImageError"
            />
          </div>
          <h2 class="text-2xl font-bold mb-1">{{ profile.name }}</h2>
          <div class="flex items-center text-sm text-gray-600">
            <span>会员等级: 普通会员</span>
            <span class="mx-2">|</span>
            <span>ID: 88361425</span>
          </div>
        </div>
      </div>

      <!-- Check-in Statistics -->
      <div class="px-4 mb-5">
        <FrostedGlass class="p-4 rounded-xl">
          <div class="flex justify-between items-center mb-4">
            <h3 class="font-semibold text-base">打卡统计</h3>
            <span class="text-xs text-blue-500">查看更多</span>
          </div>

          <div class="flex justify-between">
            <div class="flex flex-col items-center">
              <div class="text-2xl font-bold text-gray-800">{{ profile.checkIns?.totalDays || 0 }}</div>
              <div class="text-xs text-gray-500 mt-1">累计打卡</div>
            </div>
            <div class="flex flex-col items-center">
              <div class="text-2xl font-bold text-green-600">{{ profile.checkIns?.currentStreak || 0 }}</div>
              <div class="text-xs text-gray-500 mt-1">连续打卡</div>
            </div>
            <div class="flex flex-col items-center">
              <div class="text-2xl font-bold text-blue-600">{{ profile.checkIns?.longestStreak || 0 }}</div>
              <div class="text-xs text-gray-500 mt-1">最长连续</div>
            </div>
            <div>
              <button class="bg-gradient-to-r from-green-500 to-green-600 text-white py-2 px-6 rounded-full text-sm shadow-sm">
                立即打卡
              </button>
            </div>
          </div>
        </FrostedGlass>
      </div>

      <!-- Check-in Types -->
      <div class="px-4 mb-5">
        <FrostedGlass class="p-4 rounded-xl">
          <h3 class="font-semibold text-base mb-4">打卡项目</h3>
          <div class="grid grid-cols-4 gap-2">
            <div v-for="type in checkInTypes" :key="type.id" class="flex flex-col items-center cursor-pointer" @click="handleCheckInClick(type.path)">
              <div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center mb-1">
                <svg v-if="type.icon === 'book'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
                </svg>
                <svg v-if="type.icon === 'running'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
                </svg>
                <svg v-if="type.icon === 'graduation-cap'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                  <path d="M12 14l9-5-9-5-9 5 9 5z" />
                  <path d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z" />
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222" />
                </svg>
                <svg v-if="type.icon === 'pencil-alt'" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
                </svg>
              </div>
              <span class="text-xs">{{ type.name }}</span>
            </div>
          </div>
        </FrostedGlass>
      </div>

      <!-- User Menu Options -->
      <div class="px-4 pb-16">
        <FrostedGlass class="rounded-xl overflow-hidden mb-5">
          <MenuItem
            v-for="item in menuItems1"
            :key="item.path"
            v-bind="item"
            @click="handleMenuClick(item.path)"
          />
        </FrostedGlass>

        <FrostedGlass class="rounded-xl overflow-hidden mb-5">
          <MenuItem
            v-for="item in menuItems2"
            :key="item.path"
            v-bind="item"
            @click="handleMenuClick(item.path)"
          />
        </FrostedGlass>

        <FrostedGlass class="rounded-xl overflow-hidden mb-5">
          <MenuItem
            v-for="item in menuItems3"
            :key="item.path"
            v-bind="item"
            @click="handleMenuClick(item.path)"
          />
        </FrostedGlass>

        <!-- Version Info -->
        <div class="text-center text-xs text-gray-400 mb-4">
          亲子教育 App v1.2.0
        </div>

        <!-- Logout Button -->
        <button
          @click="handleLogout"
          class="w-full bg-white/70 backdrop-blur-sm text-red-500 py-3 rounded-xl mb-6 font-medium shadow-sm"
        >
          退出登录
        </button>
      </div>
    </div>
  </AppLayout>
</template>

<script setup>
import { ref, h } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import AppLayout from '@/components/layout/AppLayout.vue'
import FrostedGlass from '@/components/ui/FrostedGlass.vue'
import MenuItem from '@/components/ui/MenuItem.vue'
import { useAuth } from '@/contexts/auth'
import { userProfile, checkInTypes } from '@/utils/mockData'
import { useTitle } from '@vueuse/core';
const router = useRouter()
const $route = useRoute();
const $router = useRouter();
useTitle($route.meta.title);
const { currentUser, logout } = useAuth()
const profile = ref(userProfile)

// Handle logout
const handleLogout = () => {
  logout()
  router.push('/login')
}

// Handle menu item click
const handleMenuClick = (path) => {
  router.push(path)
}

// Handle image error
const handleImageError = (e) => {
  e.target.onerror = null
  e.target.src = '/assets/images/user-avatar-1.jpg'
}

// Handle check-in type click
const handleCheckInClick = (path) => {
  router.push(path)
}

// Right content component
const rightContent = h('div', { class: 'flex items-center' }, [
  h('button', { class: 'p-2' }, [
    h('svg', {
      xmlns: 'http://www.w3.org/2000/svg',
      class: 'h-6 w-6 text-gray-700',
      fill: 'none',
      viewBox: '0 0 24 24',
      stroke: 'currentColor'
    }, [
      h('path', {
        'stroke-linecap': 'round',
        'stroke-linejoin': 'round',
        'stroke-width': '2',
        d: 'M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9'
      })
    ])
  ])
])

// Menu items
const menuItems1 = [
  {
    icon: 'clock',
    title: '学习记录',
    path: '/profile/learning-records',
    badge: 'NEW'
  },
  {
    icon: 'user',
    title: '我的活动',
    path: '/profile/activities'
  },
  {
    icon: 'book',
    title: '我的课程',
    path: '/profile/courses'
  },
  {
    icon: 'heart',
    title: '我的收藏',
    path: '/profile/favorites'
  }
]

const menuItems2 = [
  {
    icon: 'wallet',
    title: '我的钱包',
    path: '/profile/wallet'
  },
  {
    icon: 'document',
    title: '我的订单',
    path: '/profile/orders'
  },
  {
    icon: 'chat',
    title: '我的圈子',
    path: '/profile/community'
  }
]

const menuItems3 = [
  {
    icon: 'email',
    title: '消息中心',
    path: '/profile/messages',
    badge: '3'
  },
  {
    icon: 'question',
    title: '帮助中心',
    path: '/profile/help'
  },
  {
    icon: 'settings',
    title: '设置',
    path: '/profile/settings'
  }
]
</script>