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();