-
+ @click="activeAssignGroupId = group.id">
-
+
-
+
已添加 {{ getGroupTeams(group.id).length }} / {{ getGroupMaxTeams(group.id) }} 队
-
-
+
+
+
+
添加团队
-
+
-
+ :data-team-key="`${group.id}_${team.teamIndex}`"
+ @click.stop="setActiveTeam(group.id, team.teamIndex)">
-
+ style="margin: 3px;">
{{ m.name }}
选择此团队后添加队员
-
+
-
+
{{ m.name }}
- 点击此卡片后,从右侧选择队员
+ 点击此卡片后,从右侧选择队员
-
+
@@ -264,33 +230,19 @@
-
-
+
+
-
+
-
+ @click="assignMemberToActiveTeam(m)">
{{ m.name }}
@@ -301,48 +253,29 @@
-
+
-
+
-
+
@@ -378,13 +311,15 @@
-
请使用{{ paymentMethod === 2 ? '支付宝' : '微信'}}扫码支付
+
请使用{{ paymentMethod === 2 ? '支付宝' : '微信' }}扫码支付
@@ -393,7 +328,9 @@
¥ {{ finalPaymentAmount }}
-
+
+
+
{{ pollingStatus }}
@@ -408,7 +345,8 @@
-
+
{{ member.name }}
@@ -419,32 +357,23 @@
优惠券
-
-
+
+
使用优惠券
-
+
清除优惠券
-
- {{ validatedCoupon.couponName }},{{validatedCoupon.discountType == 0 ? `优惠价${validatedCoupon.discountValue / 10}折` : `优惠 ${validatedCoupon.discountAmount}元`}}
+
+
+
+ {{ validatedCoupon.couponName }},{{ validatedCoupon.discountType == 0 ?
+ `优惠价${validatedCoupon.discountValue / 10}折` : `优惠
+ ${validatedCoupon.discountAmount}元`}}
@@ -462,16 +391,19 @@
@@ -488,8 +420,10 @@
@@ -543,13 +477,15 @@ const columns = ref([
{ prop: 'type', label: '赛事类型', align: 'center', width: 120 },
{ prop: 'registrationType', label: '报名方式', align: 'center', width: 120 },
{ prop: 'chargeMode', label: '收费模式', align: 'center', width: 120 },
- { prop: 'status', label: '赛事状态', align: 'center', width: 120 , enum: [
+ {
+ prop: 'status', label: '赛事状态', align: 'center', width: 120, enum: [
{ label: '未开始', value: 0 },
{ label: '报名中', value: 1 },
{ label: '报名结束', value: 2 },
{ label: '比赛中', value: 3 },
{ label: '已结束', value: 4 },
- ],},
+ ],
+ },
{ prop: 'registrationStartTime', label: '报名起止时间', align: 'center', minWidth: 320 },
{ prop: 'matchStartTime', label: '比赛起止时间', align: 'center', minWidth: 320 },
{ prop: 'action', label: '操作', align: 'center', fixed: 'right', width: 210 },
@@ -905,8 +841,8 @@ const formatGender = (gender) => {
if (v === 0) return '男';
if (v === 1) return '女';
if (v === 2) return '保密';
- if (v === 3) return '不限';
- return '不限';
+ if (v === 3) return '性别不限';
+ return '性别不限';
};
// 格式化组别的年龄段(出生日期范围)
@@ -1066,7 +1002,7 @@ const openRegistrationDialog = async (row) => {
ElMessage.error(data ?? '项目加载失败');
return;
}
-
+
// 检查报名类型,如果是只允许个人报名,则显示提示并阻止继续
const registrationType = data?.registrationType;
if (registrationType === 'OnlyIndividual') {
@@ -1080,7 +1016,7 @@ const openRegistrationDialog = async (row) => {
);
return;
}
-
+
// 保存完整的详情数据,用于项目类型显示等
detailData.value = data;
projects.value = Array.isArray(data?.projects) ? data.projects : [];
@@ -1119,7 +1055,7 @@ const fetchTeamList = async () => {
const onTeamChange = async (teamId) => {
teamMembers.value = [];
if (!teamId) return;
-
+
// 检查是否已经分配了其他团队的队员
if (usedTeamId.value && usedTeamId.value !== teamId) {
ElMessage.warning('本次报名已选择了其他团队的队员,请先移除已分配的队员');
@@ -1127,7 +1063,7 @@ const onTeamChange = async (teamId) => {
selectedTeamId.value = usedTeamId.value;
return;
}
-
+
teamMemberLoading.value = true;
try {
const { succeeded, data } = await teamDetailGet(teamId);
@@ -1154,7 +1090,20 @@ const onTeamChange = async (teamId) => {
// 判断某成员是否已分配到当前激活的组别
const isMemberAssignedToActive = (memberId) => {
if (!activeAssignGroupId.value) return false;
- const list = groupMemberMap.value[activeAssignGroupId.value] ?? [];
+ const gId = activeAssignGroupId.value;
+ // 多团队模式:检查当前激活的 team-card 里是否有该成员
+ if (activeTeamKey.value) {
+ const [activeGroupId, activeTeamIdx] = activeTeamKey.value.split('_').map(Number);
+ if (activeGroupId === gId) {
+ const teams = groupTeamsMap.value[gId] ?? [];
+ const team = teams.find(t => t.teamIndex === activeTeamIdx);
+ if (team) {
+ return team.members.some(m => (m.userId ?? m.id) === memberId);
+ }
+ }
+ }
+ // 单团队模式降级
+ const list = groupMemberMap.value[gId] ?? [];
return list.some((m) => (m.userId ?? m.id) === memberId);
};
@@ -1200,13 +1149,13 @@ const validateAllMembers = async () => {
for (const group of selectedGroupItems.value) {
const gId = group.id;
-
+
// 检查是否为团队项目且启用了多团队模式
- const isMultiTeamMode = isTeamProject(group) && groupTeamsMap.value[gId] &&
+ const isMultiTeamMode = isTeamProject(group) && groupTeamsMap.value[gId] &&
(groupTeamsMap.value[gId].length > 1 || getGroupMaxTeams(gId) > 1);
let membersToValidate = [];
-
+
if (isMultiTeamMode) {
// 多团队模式:收集所有团队的队员
for (const team of groupTeamsMap.value[gId]) {
@@ -1330,7 +1279,7 @@ const assignMemberToActiveGroup = async (member) => {
const removeMemberFromGroup = (groupId, memberId) => {
if (!groupMemberMap.value[groupId]) return;
groupMemberMap.value[groupId] = groupMemberMap.value[groupId].filter((m) => (m.userId ?? m.id) !== memberId);
-
+
// 检查是否还有队员,如果没有则重置已使用的团队
let hasMembers = false;
Object.values(groupMemberMap.value).forEach(members => {
@@ -1341,7 +1290,7 @@ const removeMemberFromGroup = (groupId, memberId) => {
if (!hasMembers) {
usedTeamId.value = null;
}
-
+
// 触发实时验证
debounceValidateAllMembers();
};
@@ -1364,18 +1313,18 @@ const isTeamProject = (group) => {
// 1006: 团队花样
return group.projectType === 1005 || group.projectType === 1006;
}
-
+
// 备用判断:使用 projectTypeName 或 parentProjectTypeName
if (group?.projectTypeName || group?.parentProjectTypeName) {
const typeName = group.projectTypeName || group.parentProjectTypeName;
return typeName && typeName.includes('团队');
}
-
+
// 最后的备用判断:使用 projectTeamSize
if (group?.projectTeamSize) {
return group.projectTeamSize > 1;
}
-
+
return false;
};
@@ -1407,18 +1356,18 @@ const addTeam = (groupId) => {
initGroupTeams(groupId);
const teams = groupTeamsMap.value[groupId];
const maxTeams = getGroupMaxTeams(groupId);
-
+
if (teams.length >= maxTeams) {
ElMessage.warning(`已达到该项目组别限报${maxTeams}队的上限`);
return;
}
-
+
const newTeamIndex = teams.length + 1;
teams.push({
teamIndex: newTeamIndex,
members: []
});
-
+
ElMessage.success(`已添加团队${getTeamNumber(newTeamIndex)}`);
};
@@ -1426,12 +1375,12 @@ const addTeam = (groupId) => {
const removeTeam = async (groupId, teamIndex) => {
initGroupTeams(groupId);
const teams = groupTeamsMap.value[groupId];
-
+
if (teams.length <= 1) {
ElMessage.warning('至少需要保留一个团队');
return;
}
-
+
try {
await ElMessageBox.confirm(
`确定要删除团队${getTeamNumber(teamIndex)}吗?该团队的队员也会被移除。`,
@@ -1442,16 +1391,16 @@ const removeTeam = async (groupId, teamIndex) => {
type: 'warning'
}
);
-
+
const teamIdx = teams.findIndex(t => t.teamIndex === teamIndex);
if (teamIdx > -1) {
teams.splice(teamIdx, 1);
-
+
// 重新编号团队索引
teams.forEach((team, idx) => {
team.teamIndex = idx + 1;
});
-
+
ElMessage.success('团队删除成功');
}
} catch {
@@ -1465,24 +1414,29 @@ const getActiveTeamMembers = (groupId) => {
if (!groupTeamsMap.value[groupId] || groupTeamsMap.value[groupId].length === 0) {
return groupMemberMap.value[groupId] ?? [];
}
-
+
// 多团队模式:找到当前激活的团队
if (!activeTeamKey.value) {
return [];
}
-
+
const [activeGroupId, activeTeamIdx] = activeTeamKey.value.split('_').map(Number);
if (activeGroupId !== groupId) {
return [];
}
-
+
const team = groupTeamsMap.value[groupId].find(t => t.teamIndex === activeTeamIdx);
return team?.members ?? [];
};
// 设置当前激活的团队
const setActiveTeam = (groupId, teamIndex) => {
+ activeAssignGroupId.value = groupId;
activeTeamKey.value = `${groupId}_${teamIndex}`;
+ nextTick(() => {
+ const el = document.querySelector(`.team-card[data-team-key="${groupId}_${teamIndex}"]`);
+ if (el) el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
+ });
};
// 将成员分配到当前激活的团队
@@ -1492,55 +1446,55 @@ const assignMemberToActiveTeam = async (member) => {
ElMessage.warning('请先点击左侧的组别卡片');
return;
}
-
+
initGroupTeams(gId);
-
+
// 获取当前组别信息
const group = selectedGroupItems.value.find(g => g.id === gId);
// 检查是否为团队项目且启用了多团队模式
- const isMultiTeamMode = group && isTeamProject(group) &&
+ const isMultiTeamMode = group && isTeamProject(group) &&
(groupTeamsMap.value[gId].length > 1 || getGroupMaxTeams(gId) > 1);
-
+
if (!isMultiTeamMode) {
// 单团队模式,使用原来的逻辑
await assignMemberToActiveGroup(member);
return;
}
-
+
// 多团队模式
if (!activeTeamKey.value) {
ElMessage.warning('请先选择要分配队员的团队');
return;
}
-
+
const [activeGroupId, activeTeamIdx] = activeTeamKey.value.split('_').map(Number);
if (activeGroupId !== gId) {
ElMessage.warning('请先选择对应组别的团队');
return;
}
-
+
const team = groupTeamsMap.value[gId].find(t => t.teamIndex === activeTeamIdx);
if (!team) {
return;
}
-
+
const memberId = member.userId ?? member.id;
// 已存在则不重复添加
if (team.members.some((m) => (m.userId ?? m.id) === memberId)) {
return;
}
-
+
// 检查队员是否来自已选择的团队
if (usedTeamId.value && member.teamId !== usedTeamId.value) {
ElMessage.warning('本次报名只能选择同一个团队的队员');
return;
}
-
+
// 设置已使用的团队
if (!usedTeamId.value) {
usedTeamId.value = member.teamId;
}
-
+
team.members.push({ ...member, teamId: member.teamId });
// 触发实时验证(包含报名限制检查)
debounceValidateAllMembers();
@@ -1551,9 +1505,9 @@ const removeMemberFromTeam = (groupId, teamIndex, memberId) => {
initGroupTeams(groupId);
const team = groupTeamsMap.value[groupId].find(t => t.teamIndex === teamIndex);
if (!team) return;
-
+
team.members = team.members.filter((m) => (m.userId ?? m.id) !== memberId);
-
+
// 检查是否还有队员
let hasMembers = false;
groupTeamsMap.value[groupId].forEach(t => {
@@ -1564,7 +1518,7 @@ const removeMemberFromTeam = (groupId, teamIndex, memberId) => {
if (!hasMembers) {
usedTeamId.value = null;
}
-
+
// 触发实时验证
debounceValidateAllMembers();
};
@@ -1679,29 +1633,29 @@ const checkRegistrationLimit = async () => {
// 构造按组别分配的数据(与提交报名时保持一致)
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] &&
+ 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), // 保持向后兼容
@@ -1711,16 +1665,16 @@ const checkRegistrationLimit = async () => {
// 单团队模式(保持向后兼容)
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,
@@ -1783,7 +1737,7 @@ const handleStep1Next = async () => {
await fetchTeamList();
// 加载队伍限制配置
await fetchGroupTeamLimit();
-
+
// 初始化每个组别的团队数据
await nextTick();
selectedGroupItems.value.forEach(group => {
@@ -1813,7 +1767,7 @@ const handleStep2Next = async () => {
for (const g of selectedGroupItems.value) {
const gId = g.id;
let hasMembers = false;
-
+
// 检查是否为团队项目且启用了多团队模式
if (isTeamProject(g) && groupTeamsMap.value[gId] && groupTeamsMap.value[gId].length > 0) {
for (const team of groupTeamsMap.value[gId]) {
@@ -1826,12 +1780,12 @@ const handleStep2Next = async () => {
// 单团队模式
hasMembers = !!(groupMemberMap.value[gId]?.length);
}
-
+
if (!hasMembers) {
emptyGroups.push(g);
}
}
-
+
if (emptyGroups.length > 0) {
ElMessage.warning(`请为所有组别分配队员,"${emptyGroups[0].groupName ?? emptyGroups[0].name}"尚未分配`);
return;
@@ -1848,35 +1802,35 @@ const handleStep2Next = async () => {
const groupMembers = [];
const allTeams = []; // 用于收集所有团队数据
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] &&
+ 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
});
-
+
allTeams.push(...groupTeamsData.map(t => ({
groupId: gId,
...t
@@ -1885,14 +1839,14 @@ const handleStep2Next = async () => {
// 单团队模式(保持向后兼容)
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];
submitDraftLoading.value = true;
@@ -1936,21 +1890,21 @@ const handleStep2Next = async () => {
const generateQRCode = async (qrcodeUrl) => {
try {
await nextTick();
-
+
// 清理之前的二维码
if (qrcodeInstance) {
qrcodeInstance.clear();
qrcodeInstance = null;
}
-
+
if (!qrcodeRef.value) {
console.error('二维码容器未找到');
return;
}
-
+
// 清空容器
qrcodeRef.value.innerHTML = '';
-
+
// 创建新的二维码
qrcodeInstance = new QRCode(qrcodeRef.value, {
text: qrcodeUrl,
@@ -1983,7 +1937,7 @@ onUnmounted(() => {
const startPolling = (orderNo) => {
polling.value = true;
pollingStatus.value = '等待支付...';
-
+
pollingTimer = setInterval(async () => {
try {
const { succeeded, data } = await usereventregistrationOrderStatusGet(orderNo);
@@ -2103,15 +2057,15 @@ const handleCreateOrderAndPay = async () => {
// 微信/支付宝支付,显示二维码
paymentData.value = data;
-
+
// 检查二维码是否获取成功
if (!data.qrcodeUrl) {
ElMessage.error('支付链接获取失败,请稍后重试');
return;
}
-
+
await generateQRCode(data.qrcodeUrl);
-
+
// 开始轮询支付状态
if (data.orderNo) {
startPolling(data.orderNo);
@@ -2228,7 +2182,8 @@ const handleCreateOrderAndPay = async () => {
font-weight: 500;
}
- &-date, &-gender {
+ &-date,
+ &-gender {
color: #888;
font-size: 12px;
}
@@ -2299,7 +2254,9 @@ const handleCreateOrderAndPay = async () => {
&-active {
border-color: #409eff;
- box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
+ border-left: 8px solid #56b4f9;
+ box-shadow: 0 4px 8px rgba(64, 158, 255, 0.35);
+ // background: #f0f7ff;
}
&-header {
@@ -2317,6 +2274,7 @@ const handleCreateOrderAndPay = async () => {
cursor: pointer;
font-size: 16px;
opacity: 0.8;
+
&:hover {
opacity: 1;
}
@@ -2369,11 +2327,11 @@ const handleCreateOrderAndPay = async () => {
cursor: pointer;
transition: all 0.2s;
overflow: hidden;
-
+
&:hover {
border-color: #56b4f9;
}
-
+
&-active {
border-color: #409eff;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);