ProfilePage.vue 10.7 KB
<template>
  <AppLayout :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>
              <div @click="showCheckInDialog = true" class="cursor-pointer">
                <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>
          </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>

  <CheckInDialog
    v-model:show="showCheckInDialog"
    @check-in-success="handleCheckInSuccess"
  />
</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 CheckInDialog from "@/components/ui/CheckInDialog.vue";

import { useTitle } from "@vueuse/core";
const router = useRouter();
const $route = useRoute();
useTitle($route.meta.title);

const { currentUser, logout } = useAuth();
const profile = ref(userProfile);
const showCheckInDialog = ref(false);

// 处理打卡成功
const handleCheckInSuccess = () => {
  profile.value.checkIns.totalDays++;
  profile.value.checkIns.currentStreak++;
  profile.value.checkIns.longestStreak = Math.max(
    profile.value.checkIns.longestStreak,
    profile.value.checkIns.currentStreak
  );
};

// Handle logout
const handleLogout = () => {
  logout();
  router.push("/");
};

// Handle menu item click
const handleMenuClick = (path) => {
  if (path === '/profile/community') {
    window.location.href = 'https://community.mlaj.com';
  } else {
    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>