From 0553ac4eee40e4af7ffac625ca5517b530e48410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E6=A2=93=E5=BD=AD?= Date: Thu, 16 Apr 2026 11:08:47 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B5=9B=E4=BA=8B=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/user/registration/index.vue | 166 ++++++++++++++++++++++++-- 1 file changed, 158 insertions(+), 8 deletions(-) diff --git a/src/views/user/registration/index.vue b/src/views/user/registration/index.vue index 2ac0493..0d4f85e 100644 --- a/src/views/user/registration/index.vue +++ b/src/views/user/registration/index.vue @@ -300,6 +300,18 @@
+ + + 下一步 @@ -931,6 +943,8 @@ const memberRegistrationLimitMap = ref({}); // { memberId: { isLimited: boolean, const registrationLimitCheckResult = ref(null); // 报名限制检查结果 const validationLoading = ref(false); // 验证加载状态 let validationDebounceTimer = null; // 防抖定时器 +// 参赛人数错误提示显示控制 +const showCompletionCountErrors = ref(false); // Step2: 团队相关 const teamList = ref([]); @@ -1000,6 +1014,7 @@ const clearRegisterState = () => { memberRegistrationLimitMap.value = {}; registrationLimitCheckResult.value = null; validationLoading.value = false; + showCompletionCountErrors.value = false; if (validationDebounceTimer) { clearTimeout(validationDebounceTimer); validationDebounceTimer = null; @@ -1044,6 +1059,10 @@ const resetStep2AndBeyond = () => { previewData.value = null; previewLoading.value = false; draftRegistrationId.value = null; + // 验证相关状态重置 + memberValidationMap.value = {}; + registrationLimitCheckResult.value = null; + showCompletionCountErrors.value = false; }; const openRegistrationDialog = async (row) => { @@ -1190,6 +1209,7 @@ const validateAllMembers = async () => { try { // 收集所有需要验证的队员和组别组合 const validationTasks = []; + let hasAnyMembers = false; for (const group of selectedGroupItems.value) { const gId = group.id; @@ -1210,6 +1230,10 @@ const validateAllMembers = async () => { membersToValidate = groupMemberMap.value[gId] || []; } + if (membersToValidate.length > 0) { + hasAnyMembers = true; + } + // 为每个队员创建验证任务 for (const member of membersToValidate) { validationTasks.push(validateMemberForGroup(member, group)); @@ -1219,8 +1243,11 @@ const validateAllMembers = async () => { // 并行执行所有验证任务 await Promise.all(validationTasks); - // 同时检查报名限制 - await checkRegistrationLimit(); + // 只有在有队员的情况下才检查报名限制 + if (hasAnyMembers) { + // 同时检查报名限制 + await checkRegistrationLimit(); + } } catch (error) { console.error('批量验证队员失败:', error); } finally { @@ -1266,6 +1293,69 @@ const hasAnyRegistrationLimit = () => { return registrationLimitCheckResult.value?.hasLimit ?? false; }; +// ==================== 参赛人数验证相关 ==================== +// 获取某个组别的当前分配人数(考虑多团队模式) +const getGroupAssignedCount = (groupId) => { + const group = selectedGroupItems.value.find(g => g.id === groupId); + if (!group) return 0; + + // 检查是否为团队项目且启用了多团队模式 + const isMultiTeamMode = isTeamProject(group) && groupTeamsMap.value[groupId] && + (groupTeamsMap.value[groupId].length > 1 || getGroupMaxTeams(groupId) > 1); + + if (isMultiTeamMode) { + // 多团队模式:统计所有团队的队员数 + return groupTeamsMap.value[groupId].reduce((total, team) => { + return total + (team.members?.length ?? 0); + }, 0); + } else { + // 单团队模式 + return groupMemberMap.value[groupId]?.length ?? 0; + } +}; + +// 检查某个组别的参赛人数是否符合要求 +const checkGroupCompletionCount = (group) => { + const requiredCount = group.projectCompletionCount ?? 0; + if (requiredCount <= 0) return { isValid: true, message: '' }; + + const currentCount = getGroupAssignedCount(group.id); + + if (currentCount !== requiredCount) { + let message; + if (currentCount < requiredCount) { + message = `${group.projectName}参赛人数错误(少${requiredCount - currentCount}人)`; + } else { + message = `${group.projectName}参赛人数错误(多${currentCount - requiredCount}人)`; + } + return { isValid: false, message }; + } + + return { isValid: true, message: '' }; +}; + +// 获取所有参赛人数错误信息 +const getCompletionCountErrors = () => { + const errors = []; + for (const group of selectedGroupItems.value) { + const result = checkGroupCompletionCount(group); + if (!result.isValid) { + errors.push({ + groupId: group.id, + projectName: group.projectName, + groupName: group.groupName ?? group.name, + message: result.message + }); + } + } + return errors; +}; + +// 检查是否有任何参赛人数错误 +const hasAnyCompletionCountError = () => { + return getCompletionCountErrors().length > 0; +}; + // 判断组别是否已达到队伍数量限制 @@ -1310,6 +1400,8 @@ const assignMemberToActiveGroup = async (member) => { } groupMemberMap.value[gId].push({ ...member, teamId: member.teamId }); + // 显示参赛人数错误提示 + showCompletionCountErrors.value = true; // 触发实时验证(包含报名限制检查) debounceValidateAllMembers(); }; @@ -1330,6 +1422,8 @@ const removeMemberFromGroup = (groupId, memberId) => { usedTeamId.value = null; } + // 显示参赛人数错误提示 + showCompletionCountErrors.value = true; // 触发实时验证 debounceValidateAllMembers(); }; @@ -1441,9 +1535,6 @@ const removeTeam = async (groupId, teamIndex) => { }); ElMessage.success('团队删除成功'); - - // 触发实时验证 - debounceValidateAllMembers(); } } catch { // 用户取消 @@ -1533,6 +1624,8 @@ const assignMemberToActiveTeam = async (member) => { } team.members.push({ ...member, teamId: member.teamId }); + // 显示参赛人数错误提示 + showCompletionCountErrors.value = true; // 触发实时验证(包含报名限制检查) debounceValidateAllMembers(); }; @@ -1556,6 +1649,8 @@ const removeMemberFromTeam = (groupId, teamIndex, memberId) => { usedTeamId.value = null; } + // 显示参赛人数错误提示 + showCompletionCountErrors.value = true; // 触发实时验证 debounceValidateAllMembers(); }; @@ -1666,12 +1761,58 @@ const fetchGroupTeamLimit = async () => { // 检查报名限制 const checkRegistrationLimit = async () => { if (!currentEventId.value) return; - const allMemberIds = [...new Set(Object.values(groupMemberMap.value).flat().map(m => m.userId ?? m.id))]; + + // 构造按组别分配的数据(与提交报名时保持一致) + const groupMembers = []; + const allMemberIdsSet = new Set(); + + for (const gId of selectedGroupIds.value) { + // 找到当前组别信息 + const group = selectedGroupItems.value.find(g => g.id === gId); + // 检查是否为团队项目且启用了多团队模式 + const isMultiTeamMode = group && isTeamProject(group) && groupTeamsMap.value[gId] && + (groupTeamsMap.value[gId].length > 1 || getGroupMaxTeams(gId) > 1); + + if (isMultiTeamMode) { + // 多团队模式 + const teams = groupTeamsMap.value[gId]; + const groupTeamsData = []; + + for (const team of teams) { + const memberIds = team.members.map(m => m.userId ?? m.id); + memberIds.forEach(id => allMemberIdsSet.add(id)); + + groupTeamsData.push({ + teamIndex: team.teamIndex, + memberIds: memberIds + }); + } + + groupMembers.push({ + groupId: gId, + memberIds: groupTeamsData.flatMap(t => t.memberIds), // 保持向后兼容 + teams: groupTeamsData + }); + } else { + // 单团队模式(保持向后兼容) + const memberIds = (groupMemberMap.value[gId] ?? []).map((m) => m.userId ?? m.id); + memberIds.forEach(id => allMemberIdsSet.add(id)); + + groupMembers.push({ + groupId: gId, + memberIds: memberIds + }); + } + } + + const allMemberIds = [...allMemberIdsSet]; + try { const { succeeded, data } = await usereventregistrationRegistrationLimitCheckPost({ eventId: currentEventId.value, + groupMembers, memberIds: allMemberIds, - groupIds: selectedGroupIds.value + teamId: usedTeamId.value ?? selectedTeamId.value, }); if (succeeded) { registrationLimitCheckResult.value = data; @@ -1707,11 +1848,20 @@ const handleStep1Next = async () => { setActiveTeam(group.id, 1); } }); + // 确保在初始化后不会立即显示参赛人数错误 + showCompletionCountErrors.value = false; }; const handleStep2Next = async () => { if (!currentEventId.value) return; + // 先检查参赛人数 + if (hasAnyCompletionCountError()) { + const errors = getCompletionCountErrors(); + ElMessage.warning(errors[0]?.message ?? '参赛人数错误,请检查后重试'); + return; + } + // 先检查报名限制 await checkRegistrationLimit();