feat: 赛程编排表格按队伍分组显示选手
Some checks are pending
continuous-integration/drone/push Build is pending

- 将同一单位的选手合并为一个队伍行
- 多选手队伍可展开查看具体选手
- 队伍状态根据所有选手状态计算(已签到/未签到/部分签到/部分异常)
- 上移/下移操作移动整个队伍
- 异常标记:单人队伍在主行标记,多人队伍需展开后标记单个选手
- 修复语法错误(转义字符和字符串引号)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
DevOps
2025-12-23 17:17:34 +08:00
parent 226d92f725
commit 5e75688e13

View File

@@ -86,13 +86,44 @@
</div>
</div>
<el-table :data="group.items" border stripe size="small">
<el-table :data="groupItemsByTeam(group.items)" border stripe size="small" row-key="id">
<!-- 展开列 -->
<el-table-column type="expand" width="30">
<template #default="{ row }">
<div v-if="row.players.length > 1" class="player-expand-list">
<div v-for="player in row.players" :key="player.id" class="player-row">
<span class="player-name">{{ player.playerName }}</span>
<el-tag :type="player.status === '已签到' ? 'success' : player.status === '异常' ? 'danger' : 'info'" size="small">
{{ player.status || '未签到' }}
</el-tag>
<el-button
v-if="(player.status || '未签到') === '未签到'"
link
size="small"
@click="markPlayerAsException(group, player)"
:disabled="isScheduleCompleted"
style="color: #f56c6c;"
>
异常
</el-button>
</div>
</div>
<div v-else class="player-expand-list">
<span style="color: #909399;">单人队伍</span>
</div>
</template>
</el-table-column>
<el-table-column label="序号" type="index" width="60" align="center"></el-table-column>
<el-table-column prop="schoolUnit" label="学校/单位" min-width="200"></el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === '已签到' ? 'success' : scope.row.status === '异常' ? 'danger' : 'info'" size="small">
{{ scope.row.status || '未签到' }}
<el-table-column prop="schoolUnit" label="学校/单位" min-width="150"></el-table-column>
<el-table-column label="选手" min-width="120">
<template #default="{ row }">
{{ row.players.map(p => p.playerName).join('、') }}
</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag :type="getTeamStatusType(row)" size="small">
{{ getTeamStatus(row) }}
</el-tag>
</template>
</el-table-column>
@@ -101,7 +132,7 @@
<el-button
link
size="small"
@click="handleMoveUp(group, scope.$index)"
@click="handleTeamMoveUp(group, scope.$index)"
:disabled="scope.$index === 0 || isScheduleCompleted"
title="上移"
class="move-btn"
@@ -111,18 +142,18 @@
<el-button
link
size="small"
@click="handleMoveDown(group, scope.$index)"
:disabled="scope.$index === group.items.length - 1 || isScheduleCompleted"
@click="handleTeamMoveDown(group, scope.$index)"
:disabled="scope.$index === groupItemsByTeam(group.items).length - 1 || isScheduleCompleted"
title="下移"
class="move-btn"
>
<img src="/img/图标 4@3x.png" class="move-icon" alt="下移" />
</el-button>
<el-button
v-if="(scope.row.status || '未签到') === '未签到'"
v-if="scope.row.players.length === 1 && (scope.row.players[0].status || '未签到') === '未签到'"
link
size="small"
@click="markAsException(group, scope.$index)"
@click="markPlayerAsException(group, scope.row.players[0])"
:disabled="isScheduleCompleted"
style="color: #f56c6c;"
>
@@ -372,6 +403,146 @@ export default {
}
},
methods: {
// 将选手按学校/单位分组为队伍
groupItemsByTeam(items) {
if (!items || items.length === 0) return []
const teamMap = new Map()
items.forEach(item => {
const key = item.schoolUnit
if (!teamMap.has(key)) {
teamMap.set(key, {
id: `team_${item.id}`,
schoolUnit: key,
players: [],
playerIds: []
})
}
teamMap.get(key).players.push(item)
teamMap.get(key).playerIds.push(item.id)
})
return Array.from(teamMap.values())
},
// 获取队伍状态
getTeamStatus(team) {
if (!team || !team.players) return '未签到'
const statuses = team.players.map(p => p.status || '未签到')
if (statuses.every(s => s === '已签到')) return '已签到'
if (statuses.every(s => s === '未签到')) return '未签到'
if (statuses.some(s => s === '异常')) return '部分异常'
return '部分签到'
},
// 获取队伍状态类型
getTeamStatusType(team) {
const status = this.getTeamStatus(team)
switch(status) {
case '已签到': return 'success'
case '部分异常': return 'danger'
case '部分签到': return 'warning'
default: return 'info'
}
},
// 队伍上移
handleTeamMoveUp(group, teamIndex) {
const teams = this.groupItemsByTeam(group.items)
if (teamIndex === 0 || this.isScheduleCompleted) return
const currentTeam = teams[teamIndex]
const prevTeam = teams[teamIndex - 1]
// 找到两个队伍在原数组中的位置范围
const currentPlayerIds = currentTeam.playerIds
const prevPlayerIds = prevTeam.playerIds
// 重新排序:将当前队伍的所有选手移到前一个队伍之前
const newItems = []
let addedCurrent = false
for (const item of group.items) {
if (prevPlayerIds.includes(item.id) && !addedCurrent) {
// 先添加当前队伍的所有选手
for (const cItem of group.items) {
if (currentPlayerIds.includes(cItem.id)) {
newItems.push(cItem)
}
}
addedCurrent = true
}
if (!currentPlayerIds.includes(item.id)) {
newItems.push(item)
}
}
group.items = newItems
this.$message.success('上移成功')
},
// 队伍下移
handleTeamMoveDown(group, teamIndex) {
const teams = this.groupItemsByTeam(group.items)
if (teamIndex === teams.length - 1 || this.isScheduleCompleted) return
const currentTeam = teams[teamIndex]
const nextTeam = teams[teamIndex + 1]
const currentPlayerIds = currentTeam.playerIds
const nextPlayerIds = nextTeam.playerIds
// 重新排序:将当前队伍的所有选手移到下一个队伍之后
const newItems = []
let addedCurrent = false
for (const item of group.items) {
if (!currentPlayerIds.includes(item.id)) {
newItems.push(item)
}
if (nextPlayerIds.includes(item.id)) {
// 检查是否是下一个队伍的最后一个选手
const isLastOfNext = item.id === nextPlayerIds[nextPlayerIds.length - 1]
if (isLastOfNext && !addedCurrent) {
// 在下一个队伍最后一个选手之后添加当前队伍
for (const cItem of group.items) {
if (currentPlayerIds.includes(cItem.id)) {
newItems.push(cItem)
}
}
addedCurrent = true
}
}
}
group.items = newItems
this.$message.success('下移成功')
},
// 标记单个选手为异常
markPlayerAsException(group, player) {
if (this.isScheduleCompleted) {
this.$message.warning('编排已完成,无法标记异常')
return
}
// 在 group.items 中找到该选手并修改状态
const item = group.items.find(i => i.id === player.id)
if (item) {
item.status = '异常'
// 添加到异常列表
this.exceptionList.push({
groupId: group.id,
groupTitle: group.title,
participantId: player.id,
schoolUnit: player.schoolUnit,
playerName: player.playerName,
status: '异常'
})
this.$message.success(`已将 ${player.playerName} 标记为异常`)
}
},
goBack() {
this.$router.go(-1)
},
@@ -505,6 +676,7 @@ export default {
items: (group.participants || []).map(p => ({
id: p.id,
schoolUnit: p.schoolUnit,
playerName: p.playerName,
status: p.status || '未签到',
sortOrder: p.sortOrder
}))
@@ -1014,4 +1186,28 @@ export default {
vertical-align: middle;
}
}
// 展开行样式
.player-expand-list {
padding: 10px 20px 10px 60px;
background: #fafafa;
.player-row {
display: flex;
align-items: center;
gap: 15px;
padding: 8px 0;
border-bottom: 1px dashed #eee;
&:last-child {
border-bottom: none;
}
.player-name {
min-width: 80px;
color: #606266;
font-weight: 500;
}
}
}
</style>