Merge branch 'main' of http://8.153.108.90:3000/YD/YD_Event.Web
This commit is contained in:
commit
ef5116566e
@ -300,6 +300,18 @@
|
|||||||
|
|
||||||
<!-- 验证提示区域 -->
|
<!-- 验证提示区域 -->
|
||||||
<div class="validation-notes-section">
|
<div class="validation-notes-section">
|
||||||
|
<!-- 参赛人数错误提示 -->
|
||||||
|
<template v-if="showCompletionCountErrors && hasAnyCompletionCountError()">
|
||||||
|
<el-alert
|
||||||
|
v-for="error in getCompletionCountErrors()"
|
||||||
|
:key="error.groupId"
|
||||||
|
:title="error.message"
|
||||||
|
type="error"
|
||||||
|
:closable="false"
|
||||||
|
style="margin-bottom: 12px;"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 年龄性别不符合要求提示 -->
|
<!-- 年龄性别不符合要求提示 -->
|
||||||
<el-alert
|
<el-alert
|
||||||
v-if="hasAnyValidationErrors()"
|
v-if="hasAnyValidationErrors()"
|
||||||
@ -338,7 +350,7 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
@click="handleStep2Next"
|
@click="handleStep2Next"
|
||||||
:loading="submitDraftLoading"
|
:loading="submitDraftLoading"
|
||||||
:disabled="hasAnyValidationErrors() || hasAnyRegistrationLimit()"
|
:disabled="hasAnyValidationErrors() || hasAnyRegistrationLimit() || hasAnyCompletionCountError()"
|
||||||
>
|
>
|
||||||
下一步
|
下一步
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -931,6 +943,8 @@ const memberRegistrationLimitMap = ref({}); // { memberId: { isLimited: boolean,
|
|||||||
const registrationLimitCheckResult = ref(null); // 报名限制检查结果
|
const registrationLimitCheckResult = ref(null); // 报名限制检查结果
|
||||||
const validationLoading = ref(false); // 验证加载状态
|
const validationLoading = ref(false); // 验证加载状态
|
||||||
let validationDebounceTimer = null; // 防抖定时器
|
let validationDebounceTimer = null; // 防抖定时器
|
||||||
|
// 参赛人数错误提示显示控制
|
||||||
|
const showCompletionCountErrors = ref(false);
|
||||||
|
|
||||||
// Step2: 团队相关
|
// Step2: 团队相关
|
||||||
const teamList = ref([]);
|
const teamList = ref([]);
|
||||||
@ -1000,6 +1014,7 @@ const clearRegisterState = () => {
|
|||||||
memberRegistrationLimitMap.value = {};
|
memberRegistrationLimitMap.value = {};
|
||||||
registrationLimitCheckResult.value = null;
|
registrationLimitCheckResult.value = null;
|
||||||
validationLoading.value = false;
|
validationLoading.value = false;
|
||||||
|
showCompletionCountErrors.value = false;
|
||||||
if (validationDebounceTimer) {
|
if (validationDebounceTimer) {
|
||||||
clearTimeout(validationDebounceTimer);
|
clearTimeout(validationDebounceTimer);
|
||||||
validationDebounceTimer = null;
|
validationDebounceTimer = null;
|
||||||
@ -1044,6 +1059,10 @@ const resetStep2AndBeyond = () => {
|
|||||||
previewData.value = null;
|
previewData.value = null;
|
||||||
previewLoading.value = false;
|
previewLoading.value = false;
|
||||||
draftRegistrationId.value = null;
|
draftRegistrationId.value = null;
|
||||||
|
// 验证相关状态重置
|
||||||
|
memberValidationMap.value = {};
|
||||||
|
registrationLimitCheckResult.value = null;
|
||||||
|
showCompletionCountErrors.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const openRegistrationDialog = async (row) => {
|
const openRegistrationDialog = async (row) => {
|
||||||
@ -1190,6 +1209,7 @@ const validateAllMembers = async () => {
|
|||||||
try {
|
try {
|
||||||
// 收集所有需要验证的队员和组别组合
|
// 收集所有需要验证的队员和组别组合
|
||||||
const validationTasks = [];
|
const validationTasks = [];
|
||||||
|
let hasAnyMembers = false;
|
||||||
|
|
||||||
for (const group of selectedGroupItems.value) {
|
for (const group of selectedGroupItems.value) {
|
||||||
const gId = group.id;
|
const gId = group.id;
|
||||||
@ -1210,6 +1230,10 @@ const validateAllMembers = async () => {
|
|||||||
membersToValidate = groupMemberMap.value[gId] || [];
|
membersToValidate = groupMemberMap.value[gId] || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (membersToValidate.length > 0) {
|
||||||
|
hasAnyMembers = true;
|
||||||
|
}
|
||||||
|
|
||||||
// 为每个队员创建验证任务
|
// 为每个队员创建验证任务
|
||||||
for (const member of membersToValidate) {
|
for (const member of membersToValidate) {
|
||||||
validationTasks.push(validateMemberForGroup(member, group));
|
validationTasks.push(validateMemberForGroup(member, group));
|
||||||
@ -1219,8 +1243,11 @@ const validateAllMembers = async () => {
|
|||||||
// 并行执行所有验证任务
|
// 并行执行所有验证任务
|
||||||
await Promise.all(validationTasks);
|
await Promise.all(validationTasks);
|
||||||
|
|
||||||
|
// 只有在有队员的情况下才检查报名限制
|
||||||
|
if (hasAnyMembers) {
|
||||||
// 同时检查报名限制
|
// 同时检查报名限制
|
||||||
await checkRegistrationLimit();
|
await checkRegistrationLimit();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('批量验证队员失败:', error);
|
console.error('批量验证队员失败:', error);
|
||||||
} finally {
|
} finally {
|
||||||
@ -1266,6 +1293,69 @@ const hasAnyRegistrationLimit = () => {
|
|||||||
return registrationLimitCheckResult.value?.hasLimit ?? false;
|
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 });
|
groupMemberMap.value[gId].push({ ...member, teamId: member.teamId });
|
||||||
|
// 显示参赛人数错误提示
|
||||||
|
showCompletionCountErrors.value = true;
|
||||||
// 触发实时验证(包含报名限制检查)
|
// 触发实时验证(包含报名限制检查)
|
||||||
debounceValidateAllMembers();
|
debounceValidateAllMembers();
|
||||||
};
|
};
|
||||||
@ -1330,6 +1422,8 @@ const removeMemberFromGroup = (groupId, memberId) => {
|
|||||||
usedTeamId.value = null;
|
usedTeamId.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显示参赛人数错误提示
|
||||||
|
showCompletionCountErrors.value = true;
|
||||||
// 触发实时验证
|
// 触发实时验证
|
||||||
debounceValidateAllMembers();
|
debounceValidateAllMembers();
|
||||||
};
|
};
|
||||||
@ -1441,9 +1535,6 @@ const removeTeam = async (groupId, teamIndex) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ElMessage.success('团队删除成功');
|
ElMessage.success('团队删除成功');
|
||||||
|
|
||||||
// 触发实时验证
|
|
||||||
debounceValidateAllMembers();
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// 用户取消
|
// 用户取消
|
||||||
@ -1533,6 +1624,8 @@ const assignMemberToActiveTeam = async (member) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
team.members.push({ ...member, teamId: member.teamId });
|
team.members.push({ ...member, teamId: member.teamId });
|
||||||
|
// 显示参赛人数错误提示
|
||||||
|
showCompletionCountErrors.value = true;
|
||||||
// 触发实时验证(包含报名限制检查)
|
// 触发实时验证(包含报名限制检查)
|
||||||
debounceValidateAllMembers();
|
debounceValidateAllMembers();
|
||||||
};
|
};
|
||||||
@ -1556,6 +1649,8 @@ const removeMemberFromTeam = (groupId, teamIndex, memberId) => {
|
|||||||
usedTeamId.value = null;
|
usedTeamId.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显示参赛人数错误提示
|
||||||
|
showCompletionCountErrors.value = true;
|
||||||
// 触发实时验证
|
// 触发实时验证
|
||||||
debounceValidateAllMembers();
|
debounceValidateAllMembers();
|
||||||
};
|
};
|
||||||
@ -1666,12 +1761,58 @@ const fetchGroupTeamLimit = async () => {
|
|||||||
// 检查报名限制
|
// 检查报名限制
|
||||||
const checkRegistrationLimit = async () => {
|
const checkRegistrationLimit = async () => {
|
||||||
if (!currentEventId.value) return;
|
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 {
|
try {
|
||||||
const { succeeded, data } = await usereventregistrationRegistrationLimitCheckPost({
|
const { succeeded, data } = await usereventregistrationRegistrationLimitCheckPost({
|
||||||
eventId: currentEventId.value,
|
eventId: currentEventId.value,
|
||||||
|
groupMembers,
|
||||||
memberIds: allMemberIds,
|
memberIds: allMemberIds,
|
||||||
groupIds: selectedGroupIds.value
|
teamId: usedTeamId.value ?? selectedTeamId.value,
|
||||||
});
|
});
|
||||||
if (succeeded) {
|
if (succeeded) {
|
||||||
registrationLimitCheckResult.value = data;
|
registrationLimitCheckResult.value = data;
|
||||||
@ -1707,11 +1848,20 @@ const handleStep1Next = async () => {
|
|||||||
setActiveTeam(group.id, 1);
|
setActiveTeam(group.id, 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// 确保在初始化后不会立即显示参赛人数错误
|
||||||
|
showCompletionCountErrors.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStep2Next = async () => {
|
const handleStep2Next = async () => {
|
||||||
if (!currentEventId.value) return;
|
if (!currentEventId.value) return;
|
||||||
|
|
||||||
|
// 先检查参赛人数
|
||||||
|
if (hasAnyCompletionCountError()) {
|
||||||
|
const errors = getCompletionCountErrors();
|
||||||
|
ElMessage.warning(errors[0]?.message ?? '参赛人数错误,请检查后重试');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 先检查报名限制
|
// 先检查报名限制
|
||||||
await checkRegistrationLimit();
|
await checkRegistrationLimit();
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user