| | |
| | | </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="description"> |
| | | <el-input |
| | |
| | | <el-tab-pane label="比赛阶段" name="stages"> |
| | | <div class="stages-header"> |
| | | <span>比赛阶段</span> |
| | | <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> |
| | |
| | | <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> |
| | | |
| | |
| | | <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" /> |
| | |
| | | |
| | | <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="阶段描述"> |
| | |
| | | <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" |
| | |
| | | // Tab相关 |
| | | const activeTab = ref('stages') |
| | | |
| | | // 阶段数量选择 |
| | | const selectedStageCount = ref(1) |
| | | |
| | | // 阶段编辑弹窗相关 |
| | | const stageDialogVisible = ref(false) |
| | | const currentStageIndex = ref(-1) |
| | |
| | | matchTime: '', |
| | | address: '', |
| | | ratingSchemeId: null, |
| | | playerMax: null, |
| | | state: 1, |
| | | actualPlayerCount: 0 |
| | | }) |
| | |
| | | matchTime: '', |
| | | address: '', |
| | | ratingSchemeId: null, |
| | | playerMax: 100, |
| | | playerMax: null, |
| | | state: 1, |
| | | stages: [], |
| | | judges: [], |
| | |
| | | |
| | | // 计算属性 |
| | | const isEdit = computed(() => !!route.params.id) |
| | | |
| | | // 按sortOrder排序的阶段列表 |
| | | 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 = { |
| | |
| | | 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' } |
| | | ] |
| | | } |
| | | |
| | |
| | | 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 || [], |
| | |
| | | return mediaItem |
| | | }) |
| | | console.log('最终的mediaFiles:', form.value.mediaFiles) |
| | | |
| | | // 设置阶段数量选择器的值 |
| | | selectedStageCount.value = form.value.stages.length || 1 |
| | | } catch (e) { |
| | | console.error('加载活动媒体失败:', e) |
| | | } |
| | |
| | | } |
| | | |
| | | // 阶段管理 |
| | | // 阶段数量变化处理 |
| | | 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() |
| | |
| | | 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 { |
| | | // 用户取消删除 |
| | |
| | | await stageFormRef.value.validate() |
| | | |
| | | if (currentStageIndex.value === -1) { |
| | | // 新增阶段 |
| | | form.value.stages.push({ ...currentStage.value }) |
| | | // 新增阶段 - 设置正确的sortOrder |
| | | const newStage = { ...currentStage.value } |
| | | newStage.sortOrder = form.value.stages.length + 1 |
| | | form.value.stages.push(newStage) |
| | | } else { |
| | | // 编辑阶段 |
| | | form.value.stages[currentStageIndex.value] = { ...currentStage.value } |
| | |
| | | address: '', |
| | | ratingSchemeId: null, |
| | | playerMax: null, |
| | | sortOrder: null, // 将在saveStage中设置正确的值 |
| | | state: 1, |
| | | actualPlayerCount: 0 |
| | | } |
| | |
| | | } |
| | | |
| | | 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 = [] |
| | | } |
| | | |
| | |
| | | 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) |
| | | } |
| | | } |
| | | } 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++ |
| | |
| | | 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 => ({ |
| | |
| | | await loadRatingSchemes() |
| | | await loadAllJudges() |
| | | await loadActivity() |
| | | |
| | | // 如果是新建模式且没有阶段,自动创建一个阶段 |
| | | if (!isEdit.value && form.value.stages.length === 0) { |
| | | onStageCountChange(1) |
| | | } |
| | | }) |
| | | </script> |
| | | |
| | |
| | | |
| | | .stage-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | justify-content: flex-start; |
| | | align-items: center; |
| | | } |
| | | |
| | |
| | | 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 { |