Quellcode durchsuchen

活动管理bug

caojp vor 15 Stunden
Ursprung
Commit
e7e641dcdc

+ 1 - 0
marriageAdmin-vue/src/config/api.js

@@ -38,6 +38,7 @@ export const API_ENDPOINTS = {
   ACTIVITY_LIST: '/api/activity/list',
   ACTIVITY_DETAIL: '/api/activity/detail',
   ACTIVITY_ADMIN_LIST: '/admin/activity/list',
+  ACTIVITY_ADMIN_DETAIL: '/admin/activity/detail',
   ACTIVITY_CREATE: '/admin/activity/create',
   ACTIVITY_UPDATE: '/admin/activity/update',
   ACTIVITY_DELETE: '/admin/activity/delete',

+ 3 - 3
marriageAdmin-vue/src/views/activity/ActivityForm.vue

@@ -77,7 +77,7 @@
             :before-upload="handleBeforeUpload"
             :http-request="handleUpload"
           >
-            <img v-if="activityForm.coverImage" :src="activityForm.coverImage" class="cover-preview" />
+            <img v-if="activityForm.coverImage" :src="resolveImageUrl(activityForm.coverImage)" class="cover-preview" />
             <el-icon v-else class="cover-uploader-icon"><Plus /></el-icon>
           </el-upload>
           <div class="upload-tip">建议尺寸:750x500px,支持jpg、png格式</div>
@@ -360,7 +360,7 @@ const resolveImageUrl = (url) => {
 // 加载活动详情
 const loadActivityDetail = async (id) => {
   try {
-    const response = await request.get(`${API_ENDPOINTS.ACTIVITY_DETAIL}/${id}`)
+    const response = await request.get(`${API_ENDPOINTS.ACTIVITY_ADMIN_DETAIL}/${id}`)
     
     if (response.code === 200) {
       const data = response.data
@@ -374,7 +374,7 @@ const loadActivityDetail = async (id) => {
         location: data.location || '',
         startTime: data.startTime || data.start_time || null,
         endTime: data.endTime || data.end_time || null,
-        coverImage: resolveImageUrl(data.coverImage || data.cover_image || data.cover || ''),
+        coverImage: data.coverImage || data.cover_image || data.cover || '',
         price: data.price || 0,
         status: data.status || 1,
         registrationEndTime: data.registrationEndTime || data.registration_end_time || null,

+ 46 - 4
marriageAdmin-vue/src/views/activity/ActivityList.vue

@@ -126,11 +126,11 @@
         <el-table-column prop="status" label="状态" width="100" align="center">
           <template #default="{ row }">
             <el-tag 
-              :type="row.status === 1 ? 'info' : row.status === 2 ? 'success' : 'danger'"
+              :type="getStatusType(getComputedStatus(row))"
               size="small"
               effect="light"
             >
-              {{ getStatusText(row.status) }}
+              {{ getStatusText(getComputedStatus(row)) }}
             </el-tag>
           </template>
         </el-table-column>
@@ -230,7 +230,7 @@
 </template>
 
 <script setup>
-import { ref, reactive, onMounted } from 'vue'
+import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
 import { useRouter } from 'vue-router'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { Calendar, Picture } from '@element-plus/icons-vue'
@@ -250,6 +250,38 @@ const filters = reactive({
   keyword: ''
 })
 
+const nowTs = ref(Date.now())
+let nowTimer = null
+
+const parseToMs = (val) => {
+  if (!val) return null
+  if (val instanceof Date) return val.getTime()
+  if (typeof val === 'number') return val > 1e12 ? val : val * 1000
+  if (typeof val === 'string') {
+    const trimmed = val.trim()
+    if (!trimmed) return null
+    const normalized = trimmed.replace('T', ' ').replace(/\//g, '-')
+    const ms = Date.parse(normalized)
+    if (!Number.isNaN(ms)) return ms
+  }
+  return null
+}
+
+const getComputedStatus = (row) => {
+  const startMs = parseToMs(row?.startTime)
+  const endMs = parseToMs(row?.endTime)
+  const now = nowTs.value
+
+  if (!startMs || !endMs) {
+    return row?.status
+  }
+
+  if (now < startMs) return 1
+  if (now >= endMs) return 3
+  if (now >= startMs && now < endMs) return 2
+  return row?.status
+}
+
 // 规范化图片地址,兼容相对路径和缺少协议的情况
 const resolveImageUrl = (url) => {
   if (!url || typeof url !== 'string') return ''
@@ -351,7 +383,7 @@ const getStatusText = (status) => {
 
 // 获取状态颜色
 const getStatusType = (status) => {
-  const types = { 1: 'info', 2: 'success', 3: '' }
+  const types = { 1: 'info', 2: 'success', 3: 'danger' }
   return types[status] || ''
 }
 
@@ -448,6 +480,16 @@ const handleDelete = async (row) => {
 
 onMounted(() => {
   loadActivityList()
+  nowTimer = window.setInterval(() => {
+    nowTs.value = Date.now()
+  }, 1000)
+})
+
+onBeforeUnmount(() => {
+  if (nowTimer) {
+    window.clearInterval(nowTimer)
+    nowTimer = null
+  }
 })
 </script>
 

+ 23 - 0
service/admin/src/main/java/com/zhentao/controller/ActivityController.java

@@ -29,6 +29,29 @@ public class ActivityController {
     private ActivityRegistrationService activityRegistrationService;
     
     private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    @GetMapping("/detail/{id}")
+    public Result<Activity> detail(@PathVariable Integer id) {
+        try {
+            Activity activity = activityMapper.selectById(id);
+            if (activity == null) {
+                return Result.error("活动不存在");
+            }
+
+            // 计算实际报名人数(排除已取消 status=2)
+            LambdaQueryWrapper<ActivityRegistration> regQuery = new LambdaQueryWrapper<ActivityRegistration>()
+                    .eq(ActivityRegistration::getActivityId, activity.getId())
+                    .ne(ActivityRegistration::getStatus, 2);
+            long cnt = activityRegistrationService.count(regQuery);
+            activity.setActualParticipants((int) cnt);
+
+            return Result.success(activity);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("获取活动详情失败:" + e.getMessage());
+        }
+    }
+
     @GetMapping("/list")
     public Result<?> list(
             @RequestParam(value = "page", defaultValue = "1") Integer page,