diff --git a/src/views/match/manage/work.vue b/src/views/match/manage/work.vue index 4883612..5ddbb34 100644 --- a/src/views/match/manage/work.vue +++ b/src/views/match/manage/work.vue @@ -1363,9 +1363,8 @@ const calculateProjectDuration = (item) => { let error = null; if (isTeamProject(item.projectType)) { - // 团队项目:计算队伍数(这里暂时用人数模拟,实际应该统计团队数) - // 由于团队数需要从后端获取,这里先使用人数模拟 - const teamCount = totalParticipants > 0 ? Math.ceil(totalParticipants / 5) : 1; // 假设每个团队5人 + // 团队项目:使用后端返回的团队总数 + const teamCount = item.totalTeams > 0 ? item.totalTeams : 1; totalRounds = Math.ceil(teamCount / venueCount); } else { // 个人项目 diff --git a/src/views/user/record/index.vue b/src/views/user/record/index.vue index 6f60067..b6d3712 100644 --- a/src/views/user/record/index.vue +++ b/src/views/user/record/index.vue @@ -433,53 +433,113 @@ - +
- -
-
- ({{ project.projectType }}/{{ project.personCount }}) {{ project.projectName }} - - - -
-
-
- {{ member.name }} - - 队长 - - - - + + + + +
@@ -807,22 +867,41 @@ const formatRefundRules = (rules) => { // 格式化项目标题 const formatItemTitle = (item) => { - const projectType = item.projectTypeName || ''; - // 使用报名项目人数作为完成人数显示 - const completionCount = item.personCount ?? 0; - const projectName = item.projectName || ''; - const groupName = item.groupName || ''; - const dateRange = formatGroupDate(item); - const genderReq = formatGenderRequirement(item.genderRequirement); - - let title = `(${projectType}/${completionCount}) ${projectName}-${groupName}`; - if (dateRange) { - title += `/${dateRange}`; - } - if (genderReq) { - title += `/${genderReq}`; - } - return title; + const projectType = item.projectTypeName || ''; + // 使用报名项目人数作为完成人数显示 + const completionCount = item.personCount ?? 0; + const projectName = item.projectName || ''; + const groupName = item.groupName || ''; + const dateRange = formatGroupDate(item); + const genderReq = formatGenderRequirement(item.genderRequirement); + + let title = `(${projectType}/${completionCount}) ${projectName}-${groupName}`; + if (dateRange) { + title += `/${dateRange}`; + } + if (genderReq) { + title += `/${genderReq}`; + } + return title; +}; + +// 格式化取消报名预览中的项目组别标题 +const formatCancelPreviewItemTitle = (project, group) => { + const projectType = group.projectTypeName || ''; + const completionCount = group.personCount ?? 0; + const projectName = project.projectName || ''; + const groupName = group.groupName || ''; + const dateRange = formatGroupDate(group); + const genderReq = formatGenderRequirement(group.genderRequirement); + + let title = `(${projectType}/${completionCount}) ${projectName}-${groupName}`; + if (dateRange) { + title += `/${dateRange}`; + } + if (genderReq) { + title += `/${genderReq}`; + } + return title; }; const onOpenMenu = async (row) => { @@ -1153,8 +1232,8 @@ const mockMembers3 = ref([ ]); // 新的响应式变量 - const selectedMembers = ref([]); - const teamRemoved = ref(false); + const selectedMembers = ref([]); // 选择的队员ID + const removedTeams = ref(new Set()); // 存储被移除的团队(格式:registrationItemId_teamIndex const cancelAll = ref(false); const hasValidationError = ref(false); const hasLimitError = ref(false); @@ -1169,12 +1248,107 @@ const mockMembers3 = ref([ const totalRefundAmount = computed(() => { // 免费项目退款金额为0 - if (isFreeProject.value) { + if (isFreeProject.value || cancelPreviewData.value?.chargeMode === 0) { return 0; } - // 使用后端返回的可退款金额 - return cancelPreviewData.value?.refundableAmount ?? 0; + // 如果取消全部,直接返回可退款金额 + if (cancelAll.value) { + return cancelPreviewData.value?.refundableAmount ?? 0; + } + + // 根据收费模式动态计算退款金额 + const chargeMode = cancelPreviewData.value?.chargeMode; + const refundableAmount = cancelPreviewData.value?.refundableAmount ?? 0; + const actualPaidAmount = cancelPreviewData.value?.actualPaidAmount ?? 0; + + // 计算退款比例 + let refundRatio = 1; + if (actualPaidAmount > 0 && refundableAmount >= 0) { + refundRatio = refundableAmount / actualPaidAmount; + } + + let calculatedAmount = 0; + + switch (chargeMode) { + case 1: // 定价(赛事) + // 定价赛事:个人取消不退款,团队取消按比例退款 + // 检查是否有团队被取消 + const hasRemovedTeam = removedTeams.value.size > 0; + if (hasRemovedTeam) { + calculatedAmount = refundableAmount; + } + break; + + case 2: // 单价(赛事) + // 单价赛事:个人取消按比例退款,团队取消按比例退款 + // 统计取消的人数 + let cancelCount = 0; + + // 统计团队被取消的团队中的队员数量 + for (const project of cancelPreviewData.value?.projectDetails || []) { + for (const group of project.groups || []) { + for (const team of group.teams || []) { + const teamKey = `${team.registrationItemId}_${team.teamIndex}`; + if (removedTeams.value.has(teamKey)) { + cancelCount += (team.members?.length || 0); + } else { + // 统计该团队中被单独取消的队员 + for (const member of team.members || []) { + if (selectedMembers.value.includes(member.memberId)) { + cancelCount++; + } + } + } + } + } + } + + // 统计总人数 + let totalCount = 0; + for (const member of cancelPreviewData.value?.members || []) { + totalCount++; + } + + // 计算单价 + const unitPriceEvent = totalCount > 0 ? actualPaidAmount / totalCount : 0; + calculatedAmount = unitPriceEvent * cancelCount * refundRatio; + break; + + case 3: // 单价(项目) + // 单价项目:个人取消按比例退款,团队取消按比例退款 + let projectCancelAmount = 0; + + for (const project of cancelPreviewData.value?.projectDetails || []) { + for (const group of project.groups || []) { + const unitPrice = group.unitPrice || 0; + + for (const team of group.teams || []) { + const teamKey = `${team.registrationItemId}_${team.teamIndex}`; + + if (removedTeams.value.has(teamKey)) { + // 团队取消:整个团队都取消 + projectCancelAmount += unitPrice * (team.members?.length || 0); + } else { + // 统计该团队中被单独取消的队员 + for (const member of team.members || []) { + if (selectedMembers.value.includes(member.memberId)) { + projectCancelAmount += unitPrice; + } + } + } + } + } + } + + calculatedAmount = projectCancelAmount * refundRatio; + break; + + default: + calculatedAmount = 0; + } + + return Math.max(0, calculatedAmount); }); const toggleMember = (memberId) => { @@ -1183,12 +1357,40 @@ const toggleMember = (memberId) => { return; } - // 团队项目队员不允许手动选择 - const teamMemberIds = (cancelPreviewData.value?.projects || []) - .filter(p => p.projectType === '团队') - .flatMap(p => (cancelPreviewData.value?.members || []).map(m => m.memberId)); + // 检查该队员是否属于团队项目 + let isTeamMember = false; - if (teamMemberIds.includes(memberId)) { + // 检查新的数据结构 + if (cancelPreviewData.value?.projectDetails && cancelPreviewData.value.projectDetails.length > 0) { + for (const project of cancelPreviewData.value.projectDetails) { + if (project.projectType === '团队') { + for (const group of project.groups || []) { + for (const team of group.teams || []) { + for (const member of team.members || []) { + if (member.memberId === memberId) { + isTeamMember = true; + break; + } + } + if (isTeamMember) break; + } + if (isTeamMember) break; + } + if (isTeamMember) break; + } + } + } else { + // 检查旧的数据结构 + const teamMemberIds = (cancelPreviewData.value?.projects || []) + .filter(p => p.projectType === '团队') + .flatMap(p => (cancelPreviewData.value?.members || []).map(m => m.memberId)); + + if (teamMemberIds.includes(memberId)) { + isTeamMember = true; + } + } + + if (isTeamMember) { return; } @@ -1212,27 +1414,135 @@ const removeMember = (memberId) => { } }; -const toggleTeam = () => { - teamRemoved.value = !teamRemoved.value; - if (teamRemoved.value) { - // 团队被移除时,清空该团队的队员选择 - const teamMemberIds = (cancelPreviewData.value?.members || []) - .filter(m => !m.isLeader) - .map(m => m.memberId); - selectedMembers.value = selectedMembers.value.filter(id => !teamMemberIds.includes(id)); +// 团队级别的移除功能 +const isTeamRemoved = (registrationItemId, teamIndex) => { + const teamKey = `${registrationItemId}_${teamIndex}`; + return removedTeams.value.has(teamKey); +}; + +const toggleTeam = (registrationItemId, teamIndex) => { + const teamKey = `${registrationItemId}_${teamIndex}`; + + if (removedTeams.value.has(teamKey)) { + removedTeams.value.delete(teamKey); + } else { + removedTeams.value.add(teamKey); + + // 移除该团队下所有队员的选择 + for (const project of cancelPreviewData.value?.projectDetails || []) { + for (const group of project.groups || []) { + for (const team of group.teams || []) { + if (team.registrationItemId === registrationItemId && team.teamIndex === teamIndex) { + const teamMemberIds = (team.members || []) + .filter(m => !m.isLeader) + .map(m => m.memberId); + selectedMembers.value = selectedMembers.value.filter(id => !teamMemberIds.includes(id)); + } + } + } + } + } +}; + +// 项目级别的团队移除功能(兼容旧逻辑) +const isTeamRemovedForProject = (projectId) => { + // 检查该项目下是否有任何团队被移除 + for (const project of cancelPreviewData.value?.projectDetails || []) { + if (project.projectId === projectId) { + for (const group of project.groups || []) { + for (const team of group.teams || []) { + const teamKey = `${team.registrationItemId}_${team.teamIndex}`; + if (removedTeams.value.has(teamKey)) { + return true; + } + } + } + } + } + return false; +}; + +const toggleTeamForProject = (projectId) => { + // 切换该项目下所有团队的移除状态 + let hasAnyRemoved = false; + for (const project of cancelPreviewData.value?.projectDetails || []) { + if (project.projectId === projectId) { + for (const group of project.groups || []) { + for (const team of group.teams || []) { + const teamKey = `${team.registrationItemId}_${team.teamIndex}`; + if (removedTeams.value.has(teamKey)) { + hasAnyRemoved = true; + break; + } + } + } + } + } + + if (hasAnyRemoved) { + // 如果有任何团队被移除,则移除所有团队 + for (const project of cancelPreviewData.value?.projectDetails || []) { + if (project.projectId === projectId) { + for (const group of project.groups || []) { + for (const team of group.teams || []) { + const teamKey = `${team.registrationItemId}_${team.teamIndex}`; + removedTeams.value.delete(teamKey); + } + } + } + } + } else { + // 如果没有团队被移除,则添加所有团队 + for (const project of cancelPreviewData.value?.projectDetails || []) { + if (project.projectId === projectId) { + for (const group of project.groups || []) { + for (const team of group.teams || []) { + const teamKey = `${team.registrationItemId}_${team.teamIndex}`; + removedTeams.value.add(teamKey); + } + } + } + } + + // 移除该项目下所有队员的选择 + for (const project of cancelPreviewData.value?.projectDetails || []) { + if (project.projectId === projectId) { + const teamMemberIds = []; + for (const group of project.groups || []) { + for (const team of group.teams || []) { + for (const member of team.members || []) { + if (!member.isLeader) { + teamMemberIds.push(member.memberId); + } + } + } + } + selectedMembers.value = selectedMembers.value.filter(id => !teamMemberIds.includes(id)); + } + } } }; // 监听取消全部的变化 watch(cancelAll, (newVal) => { if (newVal) { - // 取消全部:选中所有队员,并取消团队 + // 取消全部:选中所有队员,并取消所有团队 selectedMembers.value = (cancelPreviewData.value?.members || []).map(m => m.memberId); - teamRemoved.value = true; + + // 处理新数据结构中的所有团队 + removedTeams.value.clear(); + for (const project of cancelPreviewData.value?.projectDetails || []) { + for (const group of project.groups || []) { + for (const team of group.teams || []) { + const teamKey = `${team.registrationItemId}_${team.teamIndex}`; + removedTeams.value.add(teamKey); + } + } + } } else { // 取消取消全部:清空选择 selectedMembers.value = []; - teamRemoved.value = false; + removedTeams.value.clear(); } }); @@ -1244,7 +1554,7 @@ const openCancelDialog = async (row) => { cancelPreviewData.value = null; cancelResult.value = null; selectedMembers.value = []; - teamRemoved.value = false; + removedTeams.value.clear(); cancelAll.value = false; hasValidationError.value = false; hasLimitError.value = false; @@ -1294,16 +1604,39 @@ const submitCancel = async () => { } } + // 收集所有要取消的人员ID + const allCancelMemberIds = new Set(selectedMembers.value); + + // 添加被取消团队中的所有队员 + for (const project of cancelPreviewData.value?.projectDetails || []) { + for (const group of project.groups || []) { + for (const team of group.teams || []) { + const teamKey = `${team.registrationItemId}_${team.teamIndex}`; + if (removedTeams.value.has(teamKey)) { + for (const member of team.members || []) { + allCancelMemberIds.add(member.memberId); + } + } + } + } + } + + const finalPersonIds = Array.from(allCancelMemberIds); + cancelSubmitting.value = true; try { const payload = { registrationId: cancelPreviewData.value.registrationId, cancelType: cancelAll.value ? 0 : 1, - cancelReason: '个人原因' + cancelReason: '个人原因', + // 添加取消的团队 + cancelTeams: Array.from(removedTeams.value), + // 添加计算后的退款金额 + calculatedRefundAmount: totalRefundAmount.value }; if (!cancelAll.value) { - payload.personIds = selectedMembers.value; + payload.personIds = finalPersonIds; } // 如果是线下支付且已支付,添加收款人信息 @@ -1361,7 +1694,7 @@ const onCancelDialogClosed = () => { cancelResult.value = null; // 按顺序重置,避免 watch 触发问题 selectedMembers.value = []; - teamRemoved.value = false; + removedTeams.value.clear(); cancelAll.value = false; hasValidationError.value = false; hasLimitError.value = false; @@ -1926,6 +2259,62 @@ const submitChangeMembers = async () => { font-weight: 500; } +// 组别容器 +.group-container { + margin-bottom: 12px; +} + +.group-header { + display: inline-block; + padding: 4px 10px; + background: #f0f7ff; + color: #409eff; + border-radius: 4px; + font-size: 13px; + font-weight: 500; + margin-bottom: 8px; +} + +// 团队容器 +.team-container { + margin-left: 16px; + margin-bottom: 8px; +} + +.team-name { + font-size: 13px; + color: var(--el-text-color-secondary); + margin-bottom: 6px; + padding-left: 4px; +} + +.team-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; + padding-bottom: 8px; + border-bottom: 1px solid #e4e7ed; +} + +.team-label { + font-size: 13px; + font-weight: 500; + color: #606266; +} + +.team-container { + margin-bottom: 12px; + padding: 12px; + background: #f5f7fa; + border-radius: 8px; + border: 1px dashed #dcdfe6; + + &:last-child { + margin-bottom: 0; + } +} + .remove-team-icon { cursor: pointer; color: var(--el-text-color-secondary);