update:赛事报名先选择团队

This commit is contained in:
ltx 2026-06-03 16:10:49 +08:00
parent 9f92b4da87
commit 1c36e46c4f

View File

@ -357,18 +357,59 @@
</template>
</el-dialog>
<!-- 赛事报名三步弹窗 -->
<!-- 赛事报名四步弹窗 -->
<el-dialog title="赛事报名" v-model="isShowRegisterDialog" width="900px" :close-on-click-modal="false"
@closed="clearRegisterState">
<el-steps :active="activeStep" finish-status="success" align-center>
<el-step title="选择团队" />
<el-step title="选择报名项目" />
<el-step title="分配队员" />
<el-step title="预览支付" />
</el-steps>
<div class="register-step-content">
<!-- Step 1 -->
<!-- Step 1: 选择团队 -->
<div v-if="activeStep === 0">
<el-skeleton v-if="teamListLoading" :rows="6" animated />
<template v-else>
<div class="team-select-section">
<div class="team-select-label">请选择您的团队:</div>
<el-select v-model="selectedTeamId" placeholder="请选择团队" style="width: 100%; max-width: 400px;"
@change="onTeamChange" filterable>
<el-option v-for="t in teamList" :key="t.id" :label="t.name" :value="t.id" />
</el-select>
</div>
<!-- 团队成员详情 -->
<template v-if="selectedTeamId">
<el-skeleton v-if="teamMemberLoading" :rows="4" animated style="margin-top: 20px;" />
<template v-else>
<div class="team-members-detail" v-if="teamMembers.length > 0">
<div class="team-members-detail-title">团队成员({{ teamMembers.length }} 人)</div>
<div class="team-members-detail-grid">
<div v-for="m in teamMembers" :key="m.userId ?? m.id" class="team-member-detail-card">
<div class="team-member-detail-name">{{ m.name }}</div>
<div class="team-member-detail-info">{{ formatGender(m.gender) }}</div>
<div class="team-member-detail-info" v-if="m.birthDate">{{ m.birthDate.slice(0, 10) }}</div>
<div class="team-member-detail-mobile" v-if="m.mobile">{{ m.mobile }}</div>
</div>
</div>
</div>
<el-empty v-else description="该团队暂无运动员" :image-size="60" style="margin-top: 20px;" />
</template>
</template>
<el-empty v-else description="请先选择团队,查看团队成员" :image-size="60" style="margin-top: 20px;" />
<!-- 底部操作区域 -->
<div class="step-footer" style="display: flex; justify-content: flex-end; margin-top: 20px;">
<el-button @click="isShowRegisterDialog = false" style="margin-right: 10px;">取消</el-button>
<el-button type="primary" @click="handleStep0Next" :disabled="!selectedTeamId || teamMemberLoading">
下一步
</el-button>
</div>
</template>
</div>
<!-- Step 2: 选择报名项目 -->
<div v-else-if="activeStep === 1">
<el-empty v-if="!registerLoading && !(projects?.length ?? 0)" description="暂无报名项目" />
<el-skeleton v-else-if="registerLoading" :rows="8" animated />
<template v-else>
@ -412,6 +453,7 @@
<!-- 底部操作区域 -->
<div class="step-footer" style="display: flex; justify-content: flex-end; margin-top: 20px;">
<el-button @click="isShowRegisterDialog = false" style="margin-right: 10px;">取消</el-button>
<el-button @click="handlePrevious" style="margin-right: 10px;">上一步</el-button>
<el-button type="primary" @click="handleStep1Next" :loading="registerLoading">
下一步
</el-button>
@ -419,8 +461,8 @@
</template>
</div>
<!-- Step 2 -->
<div v-else-if="activeStep === 1">
<!-- Step 3: 分配队员 -->
<div v-else-if="activeStep === 2">
<div class="step2-layout">
<!-- 左侧:已选组别,每个组别可分配队员 -->
<div class="step2-left">
@ -508,18 +550,12 @@
<el-empty v-if="!selectedGroupItems.length" description="请先在第一步选择报名组别" />
</div>
<!-- 右侧:团队选择器 + 成员列表 -->
<!-- 右侧:成员列表 -->
<div class="step2-right">
<div class="team-selector">
<el-select v-model="selectedTeamId" placeholder="请选择团队" style="width: 100%;"
@change="onTeamChange" :loading="teamListLoading" filterable>
<el-option v-for="t in teamList" :key="t.id" :label="t.name" :value="t.id" />
</el-select>
</div>
<div class="team-members-panel">
<el-skeleton v-if="teamMemberLoading" :rows="6" animated />
<el-empty v-else-if="!teamMembers.length"
:description="selectedTeamId ? '该团队暂无运动员' : '请先选择团队'" :image-size="60" />
:description="'该团队暂无运动员'" :image-size="60" />
<template v-else>
<div v-for="m in teamMembers" :key="m.userId ?? m.id" class="team-member-tag"
:class="{ 'team-member-tag-assigned': isMemberAssignedToActive(m.userId ?? m.id) }"
@ -562,8 +598,8 @@
</div>
</div>
<!-- Step 3 -->
<div v-else-if="activeStep === 2">
<!-- Step 4: 预览支付 -->
<div v-else-if="activeStep === 3">
<el-skeleton :rows="10" animated v-if="previewLoading" />
<template v-else>
<!-- 银行转账信息显示区域 -->
@ -1227,7 +1263,7 @@ const toggleGroupSelection = (id) => {
// 取消选中
selectedGroupIds.value.splice(idx, 1);
// 若在第二步删除了组别,清理该组别的验证缓存并重新触发验证
if (activeStep.value === 1) {
if (activeStep.value === 2) {
// 清理被删除组别的队员验证缓存
Object.keys(memberValidationMap.value).forEach(key => {
if (key.startsWith(`${id}_`)) {
@ -1239,14 +1275,14 @@ const toggleGroupSelection = (id) => {
}
} else {
// 选中新组别:先找出这个组别属于哪个项目,然后取消该项目的其他组别
const clickedGroup = allGroups.value.find(g => g.id === id);
if (clickedGroup && clickedGroup.projectId) {
// 移除同一项目下的其他组别
selectedGroupIds.value = selectedGroupIds.value.filter(selectedId => {
const selectedGroup = allGroups.value.find(g => g.id === selectedId);
return selectedGroup?.projectId !== clickedGroup.projectId;
});
}
// const clickedGroup = allGroups.value.find(g => g.id === id);
// if (clickedGroup && clickedGroup.projectId) {
// // 移除同一项目下的其他组别
// selectedGroupIds.value = selectedGroupIds.value.filter(selectedId => {
// const selectedGroup = allGroups.value.find(g => g.id === selectedId);
// return selectedGroup?.projectId !== clickedGroup.projectId;
// });
// }
// 添加新选中的组别
selectedGroupIds.value.push(id);
}
@ -1475,6 +1511,8 @@ const openRegistrationDialog = async (row) => {
paymentMethod.value = 1;
isShowRegisterDialog.value = true;
activeStep.value = 0;
// 加载团队列表(第一步需要展示)
fetchTeamList();
} catch (error) {
console.error('打开报名弹窗失败:', error);
ElMessage.error('项目加载失败');
@ -1531,7 +1569,8 @@ const onTeamChange = async (teamId) => {
mobile: m.mobile ?? '',
gender: m.gender ?? '',
role: m.role ?? '',
teamId: teamId, // 添加团队ID
birthDate: m.birthDate ?? m.birthday ?? '',
teamId: teamId,
}));
}
} catch {
@ -1590,7 +1629,7 @@ const validateMemberForGroup = async (member, group) => {
// 实时验证所有已选择的队员
const validateAllMembers = async () => {
if (!currentEventId.value || activeStep.value !== 1) {
if (!currentEventId.value || activeStep.value !== 2) {
return;
}
@ -2050,16 +2089,21 @@ const fetchMembers = async () => {
};
const handlePrevious = () => {
if (activeStep.value === 2) {
activeStep.value = 1;
if (activeStep.value === 3) {
activeStep.value = 2;
previewData.value = null;
previewLoading.value = false;
draftRegistrationId.value = null;
return;
}
if (activeStep.value === 2) {
activeStep.value = 1;
resetAssignMembersAndBeyond();
return;
}
if (activeStep.value === 1) {
activeStep.value = 0;
resetStep2AndBeyond();
selectedGroupIds.value = [];
return;
}
};
@ -2185,20 +2229,42 @@ const checkRegistrationLimit = async () => {
}
};
// 重置第三步(分配队员)及之后的状态,保留团队选择数据
const resetAssignMembersAndBeyond = () => {
activeAssignGroupId.value = null;
groupMemberMap.value = {};
usedTeamId.value = null;
groupTeamsMap.value = {};
activeTeamKey.value = null;
groupMaxTeamsMap.value = {};
previewData.value = null;
previewLoading.value = false;
draftRegistrationId.value = null;
memberValidationMap.value = {};
registrationLimitCheckResult.value = null;
};
const handleStep0Next = () => {
if (!selectedTeamId.value) {
ElMessage.warning('请先选择团队');
return;
}
activeStep.value = 1;
};
const handleStep1Next = async () => {
if (!(selectedGroupIds.value?.length ?? 0)) {
ElMessage.warning('请先选择报名组别');
return;
}
activeStep.value = 1;
resetStep2AndBeyond();
activeStep.value = 2;
resetAssignMembersAndBeyond();
// 默认激活第一个组别
if (selectedGroupItems.value.length > 0) {
activeAssignGroupId.value = selectedGroupItems.value[0].id;
}
// 加载团队列表
await fetchTeamList();
// 团队已在第一步选择,无需再次加载团队列表
// 加载队伍限制配置
await fetchGroupTeamLimit();
@ -2334,7 +2400,7 @@ const handleStep2Next = async () => {
}
draftRegistrationId.value = data;
activeStep.value = 2;
activeStep.value = 3;
previewLoading.value = true;
try {
const { succeeded: pOk, data: pData } = await usereventregistrationPreviewGet(data);
@ -4006,4 +4072,74 @@ const handleCreateOrderAndPay = async () => {
text-align: center;
}
}
/* ---- 第一步:选择团队 ---- */
.team-select-section {
display: flex;
align-items: center;
gap: 14px;
padding: 20px 0 8px;
}
.team-select-label {
font-size: 14px;
font-weight: 600;
color: #303133;
flex-shrink: 0;
}
.team-members-detail {
margin-top: 20px;
border: 1px solid #e0ecfd;
border-radius: 10px;
overflow: hidden;
}
.team-members-detail-title {
padding: 10px 16px;
background: linear-gradient(90deg, #eef5ff 0%, #f8fbff 100%);
border-bottom: 1px solid #e0ecfd;
font-size: 14px;
font-weight: 600;
color: #1a3c6e;
}
.team-members-detail-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 10px;
padding: 14px 16px;
background: #fff;
}
.team-member-detail-card {
display: flex;
flex-direction: column;
gap: 4px;
padding: 10px 12px;
border: 1px solid #e0ecfd;
border-radius: 8px;
background: #f8fbff;
transition: box-shadow 0.2s;
&:hover {
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.12);
}
}
.team-member-detail-name {
font-size: 14px;
font-weight: 600;
color: #303133;
}
.team-member-detail-info {
font-size: 12px;
color: #909399;
}
.team-member-detail-mobile {
font-size: 12px;
color: #909399;
}
</style>