feat(ActivityCard): 添加活动状态标签和动态样式
在ActivityCard组件中新增活动状态标签,根据活动时间动态显示不同状态(如“报名中”、“进行中”等),并为其设置相应的背景和文字颜色。同时优化了活动地点的显示逻辑,支持线上活动的显示。
Showing
1 changed file
with
53 additions
and
6 deletions
| 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> | ... | ... |
-
Please register or login to post a comment