From 93eb6b470773bc49ea6e1a9d4cbd914eb95d525b Mon Sep 17 00:00:00 2001
From: lrj <owen.stl@gmail.com>
Date: 星期二, 30 九月 2025 17:38:04 +0800
Subject: [PATCH] feat: 完善比赛晋级功能并清理测试文件

---
 web/src/views/ActivityForm.vue |  248 +++++++++++++++++++++++++++++++++----------------
 1 files changed, 166 insertions(+), 82 deletions(-)

diff --git a/web/src/views/ActivityForm.vue b/web/src/views/ActivityForm.vue
index 140c631..5cadcfc 100644
--- a/web/src/views/ActivityForm.vue
+++ b/web/src/views/ActivityForm.vue
@@ -65,19 +65,9 @@
           </el-col>
         </el-row>
 
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="姣旇禌鍦板潃" prop="address">
-              <el-input v-model="form.address" placeholder="璇疯緭鍏ユ瘮璧涘湴鍧�" />
-            </el-form-item>
-          </el-col>
-          
-          <el-col :span="12">
-            <el-form-item label="浜烘暟" prop="playerMax">
-              <el-input-number v-model="form.playerMax" :min="1" :max="9999" style="width: 100%" />
-            </el-form-item>
-          </el-col>
-        </el-row>
+        <el-form-item label="姣旇禌鍦板潃" prop="address">
+          <el-input v-model="form.address" placeholder="璇疯緭鍏ユ瘮璧涘湴鍧�" />
+        </el-form-item>
 
         <el-form-item label="姣旇禌鎻忚堪" prop="description">
           <el-input
@@ -146,35 +136,33 @@
             <el-tab-pane label="姣旇禌闃舵" name="stages">
               <div class="stages-header">
                 <span>姣旇禌闃舵</span>
-                <el-button size="small" type="primary" @click="addStage">娣诲姞闃舵</el-button>
+                <div class="stages-controls">
+                  <el-button size="small" type="primary" @click="addStage">娣诲姞闃舵</el-button>
+                </div>
               </div>
               
               <div v-if="form.stages && form.stages.length > 0" class="stages-list">
-                <div v-for="(stage, index) in form.stages" :key="index" class="stage-item">
+                <div v-for="(stage, index) in sortedFormStages" :key="index" class="stage-item">
                   <div class="stage-info">
-                    <div class="stage-name">{{ stage.name || '鏈懡鍚嶉樁娈�' }}</div>
+                    <div class="stage-header">
+                      <span class="stage-order">{{ stage.sortOrder || '-' }}</span>
+                      <span class="stage-name">{{ stage.name || '鏈懡鍚嶉樁娈�' }}</span>
+                    </div>
                     <div class="stage-details">
                       <span class="detail-item">
                         <el-icon><Clock /></el-icon>
                         {{ formatDateTime(stage.matchTime) }}
                       </span>
-                      <span class="detail-item">
-                        <el-icon><User /></el-icon>
-                        {{ stage.playerMax || 0 }} 浜�
-                      </span>
-                      <span class="detail-item">
-                        <el-icon><UserFilled /></el-icon>
-                        瀹為檯: {{ stage.actualPlayerCount || 0 }} 浜�
-                      </span>
+
                       <el-tag :type="stage.state === 1 ? 'success' : 'info'" size="small">
                         {{ stage.state === 1 ? '杩涜涓�' : '鏈紑濮�' }}
                       </el-tag>
                     </div>
                   </div>
                   <div class="stage-actions">
-                    <el-button size="small" @click="editStage(stage, index)">缂栬緫</el-button>
+                    <el-button size="small" @click="editStage(stage, getOriginalStageIndex(stage))">缂栬緫</el-button>
                     <el-button size="small" @click="closeStage(stage)" v-if="stage.state === 1">鍏抽棴</el-button>
-                    <el-button size="small" type="danger" @click="removeStage(index)">鍒犻櫎</el-button>
+                    <el-button size="small" type="danger" @click="removeStage(getOriginalStageIndex(stage))">鍒犻櫎</el-button>
                   </div>
                 </div>
               </div>
@@ -214,37 +202,7 @@
               <el-empty v-if="!form.judges || form.judges.length === 0" description="鏆傛棤璇勫" />
             </el-tab-pane>
 
-            <!-- 瀛﹀憳鍒楄〃 -->
-            <el-tab-pane label="瀛﹀憳鍒楄〃" name="students">
-              <div class="students-header">
-                <span>瀛﹀憳鍒楄〃</span>
-              </div>
-              
-              <el-table :data="form.students" style="width: 100%" border>
-                <el-table-column label="瀛﹀憳鍚嶇О" prop="name" />
-                <el-table-column label="鏈�鍚庡弬涓庣殑姣旇禌闃舵" width="200">
-                  <template #default="{ row }">
-                    {{ getLastStage(row) }}
-                  </template>
-                </el-table-column>
-                <el-table-column label="鎿嶄綔" width="250" align="center">
-                  <template #default="{ row, $index }">
-                    <el-button size="small" @click="viewStudent(row, $index)">鏌ョ湅</el-button>
-                    <el-button size="small" type="primary" @click="rateStudent(row, $index)">璇勫垎</el-button>
-                    <el-button size="small" @click="commentStudent(row, $index)">鐐硅瘎</el-button>
-                    <el-button 
-                      size="small" 
-                      :type="row.isAdvanced ? 'success' : 'warning'"
-                      @click="toggleAdvancement(row, $index)"
-                    >
-                      {{ row.isAdvanced ? '宸叉檵绾�' : '鏅嬬骇' }}
-                    </el-button>
-                  </template>
-                </el-table-column>
-              </el-table>
-              
-              <el-empty v-if="!form.students || form.students.length === 0" description="鏆傛棤瀛﹀憳" />
-            </el-tab-pane>
+
           </el-tabs>
         </div>
 
@@ -274,6 +232,26 @@
           <el-input v-model="currentStage.name" placeholder="璇疯緭鍏ラ樁娈靛悕绉�" maxlength="30" />
         </el-form-item>
         
+        <el-form-item label="姣旇禌闃舵椤哄簭" prop="sortOrder">
+          <el-select v-model="currentStage.sortOrder" placeholder="璇烽�夋嫨闃舵椤哄簭" style="width: 100%">
+            <el-option label="1" :value="1" />
+            <el-option label="2" :value="2" />
+            <el-option label="3" :value="3" />
+            <el-option label="4" :value="4" />
+            <el-option label="5" :value="5" />
+          </el-select>
+        </el-form-item>
+        
+        <el-form-item label="瀛﹀憳浜烘暟" prop="playerMax">
+          <el-input-number 
+            v-model="currentStage.playerMax" 
+            :min="1" 
+            :max="1000"
+            placeholder="璇疯緭鍏ュ鍛樹汉鏁�"
+            style="width: 100%"
+          />
+        </el-form-item>
+        
         <el-form-item label="璇勫垎妯℃澘">
           <el-select v-model="currentStage.ratingSchemeId" placeholder="缁ф壙姣旇禌妯℃澘" style="width: 100%">
             <el-option label="缁ф壙姣旇禌妯℃澘" :value="null" />
@@ -299,10 +277,6 @@
         
         <el-form-item label="闃舵鍦板潃">
           <el-input v-model="currentStage.address" placeholder="璇疯緭鍏ラ樁娈靛湴鍧�" />
-        </el-form-item>
-        
-        <el-form-item label="浜烘暟">
-          <el-input-number v-model="currentStage.playerMax" :min="1" :max="9999" style="width: 100%" />
         </el-form-item>
         
         <el-form-item label="闃舵鎻忚堪">
@@ -349,7 +323,7 @@
         <div style="margin-bottom: 16px;">
           <el-form-item label="娣诲姞鍒伴樁娈碉細" label-width="100px">
             <el-select v-model="selectedStageOption" style="width: 100%;" @change="handleStageChange">
-              <el-option label="鎵�鏈夐樁娈�" value="all" />
+              <!-- 鍙樉绀烘瘮璧涢樁娈� -->
               <el-option 
                 v-for="stage in form.stages" 
                 :key="stage.id" 
@@ -462,6 +436,9 @@
 // Tab鐩稿叧
 const activeTab = ref('stages')
 
+// 闃舵鏁伴噺閫夋嫨
+const selectedStageCount = ref(1)
+
 // 闃舵缂栬緫寮圭獥鐩稿叧
 const stageDialogVisible = ref(false)
 const currentStageIndex = ref(-1)
@@ -472,7 +449,6 @@
   matchTime: '',
   address: '',
   ratingSchemeId: null,
-  playerMax: null,
   state: 1,
   actualPlayerCount: 0
 })
@@ -524,7 +500,7 @@
   matchTime: '',
   address: '',
   ratingSchemeId: null,
-  playerMax: 100,
+  playerMax: null,
   state: 1,
   stages: [],
   judges: [],
@@ -534,6 +510,16 @@
 
 // 璁$畻灞炴��
 const isEdit = computed(() => !!route.params.id)
+
+// 鎸塻ortOrder鎺掑簭鐨勯樁娈靛垪琛�
+const sortedFormStages = computed(() => {
+  if (!form.value.stages) return []
+  return [...form.value.stages].sort((a, b) => {
+    const orderA = a.sortOrder || 999
+    const orderB = b.sortOrder || 999
+    return orderA - orderB
+  })
+})
 
 // 琛ㄥ崟楠岃瘉瑙勫垯
 const rules = {
@@ -553,6 +539,10 @@
   name: [
     { required: true, message: '璇疯緭鍏ラ樁娈靛悕绉�', trigger: 'blur' },
     { max: 30, message: '闃舵鍚嶇О涓嶈兘瓒呰繃30涓瓧绗�', trigger: 'blur' }
+  ],
+  playerMax: [
+    { required: true, message: '璇疯緭鍏ュ鍛樹汉鏁�', trigger: 'blur' },
+    { type: 'number', min: 1, max: 1000, message: '瀛﹀憳浜烘暟蹇呴』鍦�1-1000涔嬮棿', trigger: 'blur' }
   ]
 }
 
@@ -599,7 +589,7 @@
         matchTime: activity.matchTime || '',
         address: activity.address || '',
         ratingSchemeId: activity.ratingSchemeId,
-        playerMax: activity.playerMax || 100,
+        playerMax: activity.playerMax,
         state: activity.state,
         stages: activity.stages || [],
         judges: activity.judges || [],
@@ -629,6 +619,9 @@
           return mediaItem
         })
         console.log('鏈�缁堢殑mediaFiles:', form.value.mediaFiles)
+      
+      // 璁剧疆闃舵鏁伴噺閫夋嫨鍣ㄧ殑鍊�
+      selectedStageCount.value = form.value.stages.length || 1
       } catch (e) {
         console.error('鍔犺浇娲诲姩濯掍綋澶辫触:', e)
       }
@@ -642,6 +635,45 @@
 }
 
 // 闃舵绠$悊
+// 闃舵鏁伴噺鍙樺寲澶勭悊
+const onStageCountChange = (count) => {
+  if (!count) return
+  
+  // 濡傛灉褰撳墠闃舵鏁伴噺灏戜簬閫夋嫨鐨勬暟閲忥紝鑷姩娣诲姞闃舵
+  while (form.value.stages.length < count) {
+    const stageIndex = form.value.stages.length + 1
+    form.value.stages.push({
+      id: null,
+      name: getDefaultStageName(stageIndex),
+      description: '',
+      matchTime: '',
+      address: form.value.address || '',
+      ratingSchemeId: form.value.ratingSchemeId,
+      sortOrder: stageIndex,
+      state: 1,
+      actualPlayerCount: 0
+    })
+  }
+  
+  // 濡傛灉褰撳墠闃舵鏁伴噺澶氫簬閫夋嫨鐨勬暟閲忥紝鍒犻櫎澶氫綑鐨勯樁娈�
+  if (form.value.stages.length > count) {
+    form.value.stages = form.value.stages.slice(0, count)
+  }
+  
+  ElMessage.success(`宸茶缃负${count}涓樁娈礰)
+}
+
+// 鑾峰彇榛樿闃舵鍚嶇О
+const getDefaultStageName = (index) => {
+  const stageNames = ['', '娴烽��', '澶嶈禌', '鍗婂喅璧�', '鍐宠禌', '鎬诲喅璧�']
+  return stageNames[index] || `绗�${index}闃舵`
+}
+
+// 鑾峰彇闃舵鍦ㄥ師濮嬫暟缁勪腑鐨勭储寮�
+const getOriginalStageIndex = (stage) => {
+  return form.value.stages.findIndex(s => s === stage)
+}
+
 const addStage = () => {
   currentStageIndex.value = -1
   resetStageForm()
@@ -662,6 +694,15 @@
       type: 'warning'
     })
     form.value.stages.splice(index, 1)
+    
+    // 閲嶆柊鎺掑簭sortOrder
+    form.value.stages.forEach((stage, idx) => {
+      stage.sortOrder = idx + 1
+    })
+    
+    // 鏇存柊閫夋嫨鐨勯樁娈垫暟閲�
+    selectedStageCount.value = form.value.stages.length
+    
     ElMessage.success('鍒犻櫎鎴愬姛')
   } catch {
     // 鐢ㄦ埛鍙栨秷鍒犻櫎
@@ -687,8 +728,10 @@
     await stageFormRef.value.validate()
     
     if (currentStageIndex.value === -1) {
-      // 鏂板闃舵
-      form.value.stages.push({ ...currentStage.value })
+      // 鏂板闃舵 - 璁剧疆姝g‘鐨剆ortOrder
+      const newStage = { ...currentStage.value }
+      newStage.sortOrder = form.value.stages.length + 1
+      form.value.stages.push(newStage)
     } else {
       // 缂栬緫闃舵
       form.value.stages[currentStageIndex.value] = { ...currentStage.value }
@@ -710,6 +753,7 @@
     address: '',
     ratingSchemeId: null,
     playerMax: null,
+    sortOrder: null, // 灏嗗湪saveStage涓缃纭殑鍊�
     state: 1,
     actualPlayerCount: 0
   }
@@ -756,13 +800,30 @@
 }
 
 const getJudgeStages = (judge) => {
-  if (!judge.stageIds || !form.value.stages) return []
-  return form.value.stages.filter(stage => judge.stageIds.includes(stage.id))
+  if (!judge.stageIds) return []
+  
+  const stages = []
+  
+  // 鍙鏌ユ瘮璧涢樁娈�
+  if (form.value.stages) {
+    const matchedStages = form.value.stages.filter(stage => judge.stageIds.includes(stage.id))
+    matchedStages.forEach(stage => {
+      stages.push({
+        id: stage.id,
+        name: stage.name
+      })
+    })
+  }
+  
+  return stages
 }
 
 const resetJudgeDialog = () => {
   judgeSearchText.value = ''
-  selectedStageOption.value = 'all'
+  // 榛樿閫夋嫨绗竴涓樁娈�
+  selectedStageOption.value = form.value.stages && form.value.stages.length > 0 
+    ? form.value.stages[0].id?.toString() || '' 
+    : ''
   selectedJudges.value = []
 }
 
@@ -806,22 +867,18 @@
       const existingJudge = form.value.judges.find(j => j.id === judgeId)
       if (existingJudge) {
         // 鏇存柊鐜版湁璇勫鐨勯樁娈�
-        if (selectedStageOption.value === 'all') {
-          existingJudge.stageIds = form.value.stages.map(s => s.id).filter(id => id != null)
-        } else {
-          const stageId = parseInt(selectedStageOption.value)
-          if (!existingJudge.stageIds.includes(stageId)) {
-            existingJudge.stageIds.push(stageId)
-          }
+        const stageId = parseInt(selectedStageOption.value)
+        if (!existingJudge.stageIds.includes(stageId)) {
+          existingJudge.stageIds.push(stageId)
         }
       } else {
         // 娣诲姞鏂拌瘎濮�
+        const stageIds = [parseInt(selectedStageOption.value)]
+        
         const newJudge = {
           id: judge.id,
           name: judge.name,
-          stageIds: selectedStageOption.value === 'all' 
-            ? form.value.stages.map(s => s.id).filter(id => id != null)
-            : [parseInt(selectedStageOption.value)]
+          stageIds: stageIds
         }
         form.value.judges.push(newJudge)
         addedCount++
@@ -1084,6 +1141,7 @@
         address: stage.address,
         ratingSchemeId: stage.ratingSchemeId,
         playerMax: stage.playerMax,
+        sortOrder: stage.sortOrder,
         state: stage.state || 1
       })) : [],
       judges: form.value.judges ? form.value.judges.map(judge => ({
@@ -1134,6 +1192,11 @@
   await loadRatingSchemes()
   await loadAllJudges()
   await loadActivity()
+  
+  // 濡傛灉鏄柊寤烘ā寮忎笖娌℃湁闃舵锛岃嚜鍔ㄥ垱寤轰竴涓樁娈�
+  if (!isEdit.value && form.value.stages.length === 0) {
+    onStageCountChange(1)
+  }
 })
 </script>
 
@@ -1169,7 +1232,7 @@
 
 .stage-header {
   display: flex;
-  justify-content: space-between;
+  justify-content: flex-start;
   align-items: center;
 }
 
@@ -1271,11 +1334,32 @@
   flex: 1;
 }
 
+.stage-header {
+  display: flex;
+  align-items: center;
+  margin-bottom: 8px;
+}
+
+.stage-order {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 24px;
+  height: 24px;
+  background-color: #409eff;
+  color: white;
+  border-radius: 50%;
+  font-size: 12px;
+  font-weight: 600;
+  flex-shrink: 0;
+}
+
 .stage-name {
   font-size: 16px;
   font-weight: 500;
   color: #303133;
-  margin-bottom: 8px;
+  margin: 0;
+  margin-left: 4px;
 }
 
 .stage-details {

--
Gitblit v1.8.0