lrj
2 天以前 7ad9c3c93f0cc103347ae2e2429e0122fb512e24
web/src/views/ActivityForm.vue
@@ -79,7 +79,10 @@
        </el-form-item>
        <!-- 图片/视频上传 -->
        <el-divider content-position="left">图片/视频</el-divider>
        <el-divider content-position="left">
          图片/视频
          <span class="media-description">支持jpg/png/mp4,最多3个文件</span>
        </el-divider>
        
        <el-form-item label="媒体文件">
          <div class="media-upload-section">
@@ -109,7 +112,7 @@
              <!-- 添加按钮 -->
              <el-upload
                v-if="form.mediaFiles.length < 3"
                class="media-uploader"
                class="media-uploader media-uploader-left"
                :show-file-list="false"
                :before-upload="beforeMediaUpload"
                action="#"
@@ -120,7 +123,6 @@
                <div class="upload-placeholder">
                  <el-icon class="upload-icon"><Plus /></el-icon>
                  <div class="upload-text">添加图片/视频</div>
                  <div class="upload-tip">支持jpg/png/mp4,最多3个文件</div>
                </div>
              </el-upload>
            </div>
@@ -141,7 +143,7 @@
                </div>
              </div>
              
              <div v-if="form.stages && form.stages.length > 0" class="stages-list">
              <div v-if="form.value && form.value.stages && form.value.stages.length > 0" class="stages-list">
                <div v-for="(stage, index) in sortedFormStages" :key="index" class="stage-item">
                  <div class="stage-info">
                    <div class="stage-header">
@@ -191,9 +193,8 @@
                    </el-tag>
                  </template>
                </el-table-column>
                <el-table-column label="操作" width="180" align="center">
                <el-table-column label="操作" width="100" align="center">
                  <template #default="{ row, $index }">
                    <el-button size="small" @click="editJudge(row, $index)">编辑</el-button>
                    <el-button size="small" type="danger" @click="removeJudge($index)">删除</el-button>
                  </template>
                </el-table-column>
@@ -321,14 +322,16 @@
        
        <!-- 阶段选择 -->
        <div style="margin-bottom: 16px;">
          <el-form-item label="添加到阶段:" label-width="100px">
            <el-select v-model="selectedStageOption" style="width: 100%;" @change="handleStageChange">
              <!-- 只显示比赛阶段 -->
          <el-form-item label="负责阶段:" label-width="100px">
            <!-- 调试信息 -->
            <el-select v-model="selectedStageOptions" multiple style="width: 100%;" placeholder="请选择负责的阶段">
              <!-- 使用计算属性 -->
              <el-option 
                v-for="stage in form.stages"
                :key="stage.id"
                :label="stage.name"
                :value="stage.id ? stage.id.toString() : ''"
                v-for="option in stageOptions"
                :key="option.value"
                :label="option.label"
                :value="option.value"
              />
            </el-select>
          </el-form-item>
@@ -393,7 +396,7 @@
          <el-select v-model="currentStudent.lastStageId" placeholder="请选择阶段" style="width: 100%">
            <el-option label="无" :value="null" />
            <el-option 
              v-for="stage in form.stages"
              v-for="stage in (form.value?.stages || [])"
              :key="stage.id" 
              :label="stage.name" 
              :value="stage.id"
@@ -413,7 +416,7 @@
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'
import { ref, onMounted, computed, nextTick } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, VideoPlay, Clock, User, UserFilled, Search } from '@element-plus/icons-vue'
@@ -466,7 +469,7 @@
// 评委选择相关
const allJudges = ref([])
const judgeSearchText = ref('')
const selectedStageOption = ref('all')
const selectedStageOptions = ref([])
const selectedJudges = ref([])
const judgeLoading = ref(false)
@@ -521,6 +524,21 @@
  })
})
// 用于下拉框的阶段选项
const stageOptions = computed(() => {
  if (!form.value?.stages) {
    return []
  }
  return form.value.stages
    .filter(stage => stage && stage.id != null)
    .map(stage => ({
      label: stage.name,
      value: stage.id.toString(),
      stage: stage
    }))
})
// 表单验证规则
const rules = {
  name: [
@@ -563,7 +581,6 @@
    judgeLoading.value = true
    const judges = await getAllJudges()
    allJudges.value = judges || []
    console.log('加载评委列表成功:', allJudges.value.length, '个评委')
  } catch (error) {
    console.error('加载评委列表失败:', error)
    ElMessage.error('加载评委列表失败: ' + error.message)
@@ -580,6 +597,7 @@
  try {
    loading.value = true
    const activity = await getActivity(route.params.id)
    if (activity) {
      form.value = {
        id: activity.id,
@@ -599,12 +617,8 @@
      // 加载并回填已上传媒体:targetType=2 假设为“活动”,如不同请调整
      try {
        const medias = await getMediasByTarget(MediaTargetType.ACTIVITY, parseInt(activity.id))
        console.log('=== 加载活动媒体调试信息 ===')
        console.log('活动ID:', activity.id)
        console.log('获取到的媒体数据:', medias)
        
        form.value.mediaFiles = (medias || []).map(m => {
          console.log('处理媒体文件:', m)
          const isImage = (m.mediaType === 1) || (m.fileExt && ['jpg','jpeg','png','gif','webp'].includes(m.fileExt.toLowerCase()))
          const isVideo = (m.mediaType === 2) || (m.fileExt && ['mp4','mov','m4v','avi','mkv'].includes(m.fileExt.toLowerCase()))
          const mediaItem = {
@@ -615,16 +629,14 @@
            uploaded: true, // 标记为已上传,不需要重新上传
            file: null // 已保存的文件没有file对象
          }
          console.log('转换后的媒体项:', mediaItem)
          return mediaItem
        })
        console.log('最终的mediaFiles:', form.value.mediaFiles)
      // 设置阶段数量选择器的值
      selectedStageCount.value = form.value.stages.length || 1
      } catch (e) {
        console.error('加载活动媒体失败:', e)
      }
      // 设置阶段数量选择器的值
      selectedStageCount.value = (form.value && form.value.stages) ? form.value.stages.length || 1 : 1
    }
  } catch (error) {
    console.error('加载比赛数据失败:', error)
@@ -637,7 +649,7 @@
// 阶段管理
// 阶段数量变化处理
const onStageCountChange = (count) => {
  if (!count) return
  if (!count || !form.value || !form.value.stages) return
  
  // 如果当前阶段数量少于选择的数量,自动添加阶段
  while (form.value.stages.length < count) {
@@ -672,6 +684,7 @@
// 获取阶段在原始数组中的索引
const getOriginalStageIndex = (stage) => {
  if (!form.value || !form.value.stages) return -1
  return form.value.stages.findIndex(s => s === stage)
}
@@ -688,6 +701,8 @@
}
const removeStage = async (index) => {
  if (!form.value || !form.value.stages) return
  try {
    await ElMessageBox.confirm('确定要删除这个阶段吗?', '提示', {
      confirmButtonText: '确定',
@@ -725,6 +740,8 @@
}
const saveStage = async () => {
  if (!form.value || !form.value.stages) return
  try {
    await stageFormRef.value.validate()
    
@@ -787,6 +804,11 @@
}
const removeJudge = async (index) => {
  if (!form.value || !form.value.judges) {
    ElMessage.error('表单数据未初始化')
    return
  }
  try {
    await ElMessageBox.confirm('确定要删除这个评委吗?', '提示', {
      confirmButtonText: '确定',
@@ -801,43 +823,40 @@
}
const getJudgeStages = (judge) => {
  if (!judge.stageIds) return []
  if (!judge.stageIds || !form.value || !form.value.stages) return []
  
  const stages = []
  
  // 只检查比赛阶段
  if (form.value.stages) {
    const matchedStages = form.value.stages.filter(stage => judge.stageIds.includes(stage.id))
    matchedStages.forEach(stage => {
  // 检查比赛阶段
  judge.stageIds.forEach(stageId => {
    // 处理实际阶段ID
    const stage = form.value.stages.find(s => s.id === stageId)
    if (stage) {
      stages.push({
        id: stage.id,
        name: stage.name
      })
    })
  }
  })
  
  return stages
}
const resetJudgeDialog = () => {
  judgeSearchText.value = ''
  // 默认选择第一个阶段
  selectedStageOption.value = form.value.stages && form.value.stages.length > 0
    ? form.value.stages[0].id?.toString() || ''
    : ''
  // 清空阶段选择
  selectedStageOptions.value = []
  selectedJudges.value = []
}
const handleJudgeSearch = (value) => {
  console.log('搜索评委:', value)
  // 搜索评委
}
const handleStageChange = (value) => {
  console.log('选择阶段:', value)
}
const handleJudgeSelectionChange = (value) => {
  console.log('选择评委:', value)
  // 选择评委
}
const toggleSelectAll = () => {
@@ -859,6 +878,17 @@
    return
  }
  
  if (!form.value || !form.value.judges) {
    ElMessage.error('表单数据未初始化')
    return
  }
  // 如果有阶段但没有选择阶段,则提示
  if (form.value && form.value.stages && form.value.stages.length > 0 && selectedStageOptions.value.length === 0) {
    ElMessage.warning('请选择至少一个负责阶段')
    return
  }
  let addedCount = 0
  
  selectedJudges.value.forEach(judgeId => {
@@ -867,14 +897,20 @@
      // 检查是否已经存在
      const existingJudge = form.value.judges.find(j => j.id === judgeId)
      if (existingJudge) {
        // 更新现有评委的阶段
        const stageId = parseInt(selectedStageOption.value)
        if (!existingJudge.stageIds.includes(stageId)) {
          existingJudge.stageIds.push(stageId)
        // 更新现有评委的阶段,合并新选择的阶段
        if (selectedStageOptions.value.length > 0) {
          selectedStageOptions.value.forEach(stageId => {
            const stageIdInt = parseInt(stageId)
            if (!existingJudge.stageIds.includes(stageIdInt)) {
              existingJudge.stageIds.push(stageIdInt)
            }
          })
        }
      } else {
        // 添加新评委
        const stageIds = [parseInt(selectedStageOption.value)]
        // 添加新评委,包含所有选择的阶段
        const stageIds = selectedStageOptions.value.length > 0
          ? selectedStageOptions.value.map(id => parseInt(id))
          : []
        
        const newJudge = {
          id: judge.id,
@@ -886,6 +922,10 @@
      }
    }
  })
  // 清空选择
  selectedJudges.value = []
  selectedStageOptions.value = []
  
  judgeDialogVisible.value = false
  ElMessage.success(`成功添加 ${addedCount} 个评委`)
@@ -940,7 +980,7 @@
}
const getLastStage = (student) => {
  if (!student.lastStageId || !form.value.stages) return '无'
  if (!student.lastStageId || !form.value || !form.value.stages) return '无'
  const stage = form.value.stages.find(s => s.id === student.lastStageId)
  return stage ? stage.name : '无'
}
@@ -985,13 +1025,17 @@
    uploaded: false // 标记为未上传
  }
  
  if (form.value && form.value.mediaFiles) {
  form.value.mediaFiles.push(mediaFile)
  ElMessage.success('文件已选择,点击更新按钮时将上传')
  }
  
  return false // 阻止el-upload的默认上传
}
const beforeMediaUpload = (file) => {
  if (!form.value || !form.value.mediaFiles) return false
  if (form.value.mediaFiles.length >= 3) {
    ElMessage.error('最多只能上传3个文件!')
    return false
@@ -1056,6 +1100,8 @@
// 处理媒体文件上传
const handleMediaUpload = async (activityId) => {
  if (!form.value || !form.value.mediaFiles) return
  try {
    for (const mediaFile of form.value.mediaFiles) {
      // 跳过已经有 id 的媒体文件(已保存的)
@@ -1074,10 +1120,8 @@
      }
      
      try {
        console.log('开始上传文件:', mediaFile.name)
        // 1. 上传文件到服务器
        const uploadResult = await uploadFile(mediaFile.file)
        console.log('文件上传成功:', uploadResult)
        
        // 2. 保存媒体信息到数据库
        const mediaInput = {
@@ -1089,11 +1133,7 @@
          targetType: MediaTargetType.ACTIVITY, // 活动
          targetId: parseInt(activityId) // 转换为数字类型
        }
        console.log('准备保存媒体信息:', mediaInput)
        console.log('活动ID:', activityId)
        const savedMedia = await saveMedia(mediaInput)
        console.log(`媒体文件 ${mediaFile.name} 上传并保存成功:`, savedMedia)
        
        // 更新媒体文件信息
        mediaFile.id = savedMedia.id
@@ -1117,6 +1157,8 @@
// 提交表单
const handleSubmit = async () => {
  if (submitting.value) return
  if (!form.value) return
  try {
    await formRef.value.validate()
    
@@ -1124,8 +1166,6 @@
    
    // 准备保存数据,只包含后端支持的字段
    const saveData = {
      id: form.value.id,
      pid: form.value.pid || 0,
      name: form.value.name,
      description: form.value.description,
      signupDeadline: form.value.signupDeadline,
@@ -1134,22 +1174,41 @@
      ratingSchemeId: form.value.ratingSchemeId,
      playerMax: form.value.playerMax,
      state: form.value.state || 1,
      stages: form.value.stages ? form.value.stages.map(stage => ({
        id: stage.id,
      stages: form.value.stages ? form.value.stages.map(stage => {
        const stageData = {
        name: stage.name,
        description: stage.description,
        matchTime: stage.matchTime,
        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 => ({
        }
        // 只在有有效ID时才添加id字段
        if (stage.id) {
          stageData.id = stage.id
        }
        // 只在有有效ratingSchemeId时才添加该字段
        if (stage.ratingSchemeId) {
          stageData.ratingSchemeId = stage.ratingSchemeId
        }
        return stageData
      }) : [],
      judges: form.value.judges ? form.value.judges.filter(judge => judge.id && judge.name).map(judge => ({
        judgeId: judge.id,
        judgeName: judge.name,
        stageIds: judge.stageIds || []
      })) : []
    }
    // 如果是编辑模式,添加id字段
    if (isEdit.value && form.value.id) {
      saveData.id = form.value.id
    }
    // 如果有pid,添加pid字段
    if (form.value.pid) {
      saveData.pid = form.value.pid
    }
    
    const result = await saveActivity(saveData)
@@ -1195,7 +1254,7 @@
  await loadActivity()
  
  // 如果是新建模式且没有阶段,自动创建一个阶段
  if (!isEdit.value && form.value.stages.length === 0) {
  if (!isEdit.value && form.value && form.value.stages && form.value.stages.length === 0) {
    onStageCountChange(1)
  }
})
@@ -1572,4 +1631,24 @@
  flex: 1;
  padding-left: 8px;
}
/* 媒体描述文本样式 */
.media-description {
  font-size: 12px;
  color: #909399;
  font-weight: normal;
  margin-left: 8px;
}
/* 媒体上传按钮 */
.media-uploader-left {
  margin-left: 16px;
}
.media-container {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap;
}
</style>