11<script setup lang="ts">
2+ import type { AllClubs } from ' @@/types/api/user/all_clubs'
3+ import type { MyRecords } from ' @@/types/api/cas/record/my'
24import { useUser } from ' vue-clerk'
5+ import { usePreferredDark } from ' @vueuse/core'
6+ import {
7+ User ,
8+ Mail ,
9+ Settings ,
10+ Clock ,
11+ } from ' lucide-vue-next'
312
413definePageMeta ({
514 middleware: [' auth' ],
@@ -10,10 +19,151 @@ useHead({
1019})
1120
1221const { user } = useUser ()
22+
23+ // Stats data and loading state
24+ const stats = ref ({
25+ totalActivities: 0 ,
26+ totalClubs: 0 ,
27+ lastActive: ' ' ,
28+ })
29+
30+ const isLoading = ref (true )
31+
32+ // Fetch clubs data
33+ const { data : clubs } = await useQuery <AllClubs >({
34+ queryKey: [' /api/user/all_clubs' ],
35+ })
36+
37+ // Calculate stats when clubs data is available
38+ if (clubs .value ) {
39+ const totalClubs = clubs .value .president .length + clubs .value .vice .length + clubs .value .member .length
40+ stats .value .totalClubs = totalClubs
41+ }
42+
43+ // Fetch activity records to calculate total activities and last active date
44+ const fetchActivityData = async () => {
45+ try {
46+ let latestDate = new Date (0 )
47+ let totalActivities = 0
48+
49+ // Only fetch if user has clubs
50+ if (clubs .value ?.president .length || clubs .value ?.vice .length ) {
51+ for (const club of [... clubs .value .president , ... clubs .value .vice ]) {
52+ const { data } = await useFetch <MyRecords >(` /api/cas/record/my?club=${club .id } ` )
53+ if (data .value ) {
54+ totalActivities += data .value .data .length
55+ // Find latest activity date
56+ data .value .data .forEach (record => {
57+ const recordDate = new Date (record .date )
58+ if (recordDate > latestDate ) {
59+ latestDate = recordDate
60+ }
61+ })
62+ }
63+ }
64+ }
65+
66+ stats .value .totalActivities = totalActivities
67+ stats .value .lastActive = latestDate .getTime () > 0
68+ ? latestDate .toLocaleDateString ()
69+ : ' 暂无活动'
70+ } catch (error ) {
71+ console .error (' Error fetching activity data:' , error )
72+ } finally {
73+ isLoading .value = false
74+ }
75+ }
76+
77+ onMounted (() => {
78+ fetchActivityData ()
79+ })
1380 </script >
1481
1582<template >
16- <h1 class =" text-3xl font-semibold" >
17- 👋 Hi, {{ user?.firstName }}!
18- </h1 >
19- </template >
83+ <div class =" mx-auto max-w-4xl space-y-8" >
84+ <Card >
85+ <CardHeader >
86+ <CardTitle class =" text-3xl" >
87+ <div class =" flex items-center gap-4" >
88+ <Avatar class =" h-16 w-16 rounded-lg" >
89+ <AvatarImage v-if =" user" :src =" user.imageUrl" :alt =" user.firstName" />
90+ <AvatarFallback class =" rounded-lg" >
91+ {{ user?.firstName?.slice(0, 2) }}
92+ </AvatarFallback >
93+ </Avatar >
94+ <div >
95+ 👋 Hi, {{ user?.firstName }}!
96+ <div class =" text-base font-normal text-muted-foreground" >
97+ {{ user?.primaryEmailAddress }}
98+ </div >
99+ </div >
100+ </div >
101+ </CardTitle >
102+ </CardHeader >
103+ </Card >
104+
105+ <div class =" grid gap-4 md:grid-cols-2" >
106+ <!-- Account Info Card -->
107+ <Card >
108+ <CardHeader >
109+ <CardTitle class =" flex items-center gap-2" >
110+ <User class =" h-5 w-5" />
111+ 账户信息
112+ </CardTitle >
113+ <CardDescription >账户基本信息</CardDescription >
114+ </CardHeader >
115+ <CardContent >
116+ <div class =" space-y-4" >
117+ <div class =" flex items-center gap-4" >
118+ <Mail class =" h-4 w-4 text-muted-foreground" />
119+ <div >
120+ <div class =" text-sm font-medium text-muted-foreground" >邮箱</div >
121+ <div >{{ user?.primaryEmailAddress }}</div >
122+ </div >
123+ </div >
124+ <div class =" flex items-center gap-4" >
125+ <Clock class =" h-4 w-4 text-muted-foreground" />
126+ <div >
127+ <div class =" text-sm font-medium text-muted-foreground" >创建时间</div >
128+ <div >{{ new Date(user?.createdAt || '').toLocaleDateString() }}</div >
129+ </div >
130+ </div >
131+ </div >
132+ </CardContent >
133+ </Card >
134+
135+ <!-- Statistics Card -->
136+ <Card >
137+ <CardHeader >
138+ <CardTitle class =" flex items-center gap-2" >
139+ <Icon name =" material-symbols:analytics-outline" class =" h-5 w-5" />
140+ 账户统计
141+ </CardTitle >
142+ <CardDescription >您的活动数据</CardDescription >
143+ </CardHeader >
144+ <CardContent >
145+ <div v-if =" isLoading" class =" grid grid-cols-2 gap-4" >
146+ <div v-for =" i in 3" :key =" i" class =" space-y-2" >
147+ <Skeleton class =" h-4 w-24" />
148+ <Skeleton class =" h-8 w-16" />
149+ </div >
150+ </div >
151+ <div v-else class =" grid grid-cols-2 gap-4" >
152+ <div class =" space-y-1" >
153+ <div class =" text-sm font-medium text-muted-foreground" >所属社团</div >
154+ <div class =" text-2xl font-bold" >{{ stats.totalClubs }}</div >
155+ </div >
156+ <div class =" space-y-1" >
157+ <div class =" text-sm font-medium text-muted-foreground" >活动记录</div >
158+ <div class =" text-2xl font-bold" >{{ stats.totalActivities }}</div >
159+ </div >
160+ <div class =" space-y-1" >
161+ <div class =" text-sm font-medium text-muted-foreground" >最近活动</div >
162+ <div class =" text-sm" >{{ stats.lastActive }}</div >
163+ </div >
164+ </div >
165+ </CardContent >
166+ </Card >
167+ </div >
168+ </div >
169+ </template >
0 commit comments