hookehuyr

feat(ActivityCard): 添加活动状态标签和动态样式

在ActivityCard组件中新增活动状态标签,根据活动时间动态显示不同状态(如“报名中”、“进行中”等),并为其设置相应的背景和文字颜色。同时优化了活动地点的显示逻辑,支持线上活动的显示。
1 <!-- 1 <!--
2 * @Date: 2025-04-17 13:16:20 2 * @Date: 2025-04-17 13:16:20
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-04-17 13:16:22 4 + * @LastEditTime: 2025-04-21 10:26:22
5 - * @FilePath: /reading-club-app/src/components/shared/ActivityCard.vue 5 + * @FilePath: /mlaj-reading-club/src/components/shared/ActivityCard.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
8 <template> 8 <template>
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
16 :alt="activity.title" 16 :alt="activity.title"
17 class="absolute inset-0 h-full w-full object-cover" 17 class="absolute inset-0 h-full w-full object-cover"
18 /> 18 />
19 - <div 19 + <!-- <div
20 v-if="activity.tags && activity.tags.length" 20 v-if="activity.tags && activity.tags.length"
21 class="absolute top-0 left-0 p-4 flex flex-wrap gap-2" 21 class="absolute top-0 left-0 p-4 flex flex-wrap gap-2"
22 > 22 >
...@@ -27,6 +27,12 @@ ...@@ -27,6 +27,12 @@
27 > 27 >
28 {{ tag }} 28 {{ tag }}
29 </span> 29 </span>
30 + </div> -->
31 + <div class="absolute top-2 left-2">
32 + <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium"
33 + :class="statusBadgeClass">
34 + {{ statusText }}
35 + </span>
30 </div> 36 </div>
31 </div> 37 </div>
32 38
...@@ -46,7 +52,7 @@ ...@@ -46,7 +52,7 @@
46 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" /> 52 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
47 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" /> 53 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
48 </svg> 54 </svg>
49 - <span>{{ activity.location }}</span> 55 + <span>{{activity.activity_type === 'online' ? '线上活动' : (activity.location ? (typeof activity.location === 'object' ? activity.location.name : activity.location) : '地点未设置')}}</span>
50 </div> 56 </div>
51 57
52 <div class="flex items-center justify-between"> 58 <div class="flex items-center justify-between">
...@@ -67,7 +73,7 @@ ...@@ -67,7 +73,7 @@
67 </template> 73 </template>
68 74
69 <script setup> 75 <script setup>
70 -import { defineProps } from 'vue' 76 +import { ref, computed, defineProps } from 'vue'
71 77
72 const props = defineProps({ 78 const props = defineProps({
73 activity: { 79 activity: {
...@@ -78,7 +84,7 @@ const props = defineProps({ ...@@ -78,7 +84,7 @@ const props = defineProps({
78 84
79 // 格式化日期时间 85 // 格式化日期时间
80 const formatDateTime = (dateTimeStr) => { 86 const formatDateTime = (dateTimeStr) => {
81 - const date = new Date(dateTimeStr.replace(' ', 'T')) 87 + const date = new Date(dateTimeStr)
82 return new Intl.DateTimeFormat('zh-CN', { 88 return new Intl.DateTimeFormat('zh-CN', {
83 year: 'numeric', 89 year: 'numeric',
84 month: 'long', 90 month: 'long',
...@@ -87,4 +93,45 @@ const formatDateTime = (dateTimeStr) => { ...@@ -87,4 +93,45 @@ const formatDateTime = (dateTimeStr) => {
87 minute: '2-digit' 93 minute: '2-digit'
88 }).format(date) 94 }).format(date)
89 } 95 }
96 +
97 + // Calculate if registration is still open
98 + const now = new Date();
99 + const registrationStart = new Date(props.activity.registration_start);
100 + const registrationEnd = new Date(props.activity.registration_end);
101 + const activityStart = new Date(props.activity.start_time);
102 +
103 + const isRegistrationOpen = ref(now >= registrationStart && now <= registrationEnd);
104 + const isUpcoming = ref(now < activityStart);
105 + const isPast = ref(now > new Date(props.activity.end_time));
106 + const isOngoing = ref(!isUpcoming && !isPast);
107 +
108 + // Calculate capacity percentage
109 + const capacityPercentage = Math.min((props.activity.participant_count / props.activity.max_participants) * 100, 100);
110 +
111 + // Dynamically set status badge colors
112 + const statusBadgeClass = computed(() => {
113 + if (isPast.value) {
114 + return 'bg-gray-100 text-gray-800';
115 + } else if (isOngoing.value) {
116 + return 'bg-green-100 text-green-800';
117 + } else if (isRegistrationOpen.value) {
118 + return 'bg-blue-100 text-blue-800';
119 + } else if (isUpcoming.value && !isRegistrationOpen.value) {
120 + return 'bg-yellow-100 text-yellow-800';
121 + }
122 + return '';
123 +});
124 +
125 +const statusText = computed(() => {
126 + if (isPast.value) {
127 + return '已结束';
128 + } else if (isOngoing.value) {
129 + return '进行中';
130 + } else if (isRegistrationOpen.value) {
131 + return '报名中';
132 + } else if (isUpcoming.value && !isRegistrationOpen.value) {
133 + return '即将开始';
134 + }
135 + return '';
136 +});
90 </script> 137 </script>
......