feat(admin): 选手列表新增小组编号/所属学校列,小组视图汇总双口径
R90 人工测试反馈修复(方案 1 + 方案 3-A):
- 选手列表新增「小组编号」「所属学校」两列,便于与小组视图横向对照
- 小组视图汇总行改为「全集 + 当前筛选后」三段式,并新增差额说明行
· 明确解释「选手列表激活人数」与「已激活小组成员数」的差额来源
(差额 = 部分激活小组内已激活的选手)
- 新增 teamFilteredSummary / teamPartialExtra computed
· 防御式读取:后端未同步时自动退回全量口径,不显示 undefined
- 新增 accountTeamLabel() 显示助手与 .team-summary-filtered / .team-summary-hint 样式
生产已部署并复验:线上产物与本地产物逐字节一致(sha256 3d09ea90…),
新增 UI 文案均已在线上 JS 中检出。
This commit is contained in:
@@ -170,6 +170,19 @@
|
|||||||
<el-table-column label="组别" min-width="100" align="center">
|
<el-table-column label="组别" min-width="100" align="center">
|
||||||
<template #default="{ row }">{{ groupMap[row.group] || row.group }}</template>
|
<template #default="{ row }">{{ groupMap[row.group] || row.group }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<!-- R90 新增:与「小组视图」对照用(人工测试反馈:选手列表缺小组编号,横向对比不便) -->
|
||||||
|
<el-table-column label="小组编号" min-width="200">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="accountTeamLabel(row)">{{ accountTeamLabel(row) }}</span>
|
||||||
|
<span v-else class="team-school-missing">— 无小组</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="所属学校" min-width="220">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row.schoolName">{{ row.schoolName }}</span>
|
||||||
|
<span v-else class="team-school-missing">— 未填写</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="状态" min-width="100" align="center">
|
<el-table-column label="状态" min-width="100" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="row.activated ? 'success' : 'info'" size="small">{{ row.activated ? '已激活' : '未激活' }}</el-tag>
|
<el-tag :type="row.activated ? 'success' : 'info'" size="small">{{ row.activated ? '已激活' : '未激活' }}</el-tag>
|
||||||
@@ -230,6 +243,13 @@
|
|||||||
<span class="team-summary" v-if="teamSummary">
|
<span class="team-summary" v-if="teamSummary">
|
||||||
共 {{ teamSummary.total }} 个小组 · 已激活 {{ teamSummary.activated }} · 部分激活 {{ teamSummary.partial }} · 未激活 {{ teamSummary.inactive }}
|
共 {{ teamSummary.total }} 个小组 · 已激活 {{ teamSummary.activated }} · 部分激活 {{ teamSummary.partial }} · 未激活 {{ teamSummary.inactive }}
|
||||||
|成员 {{ teamSummary.memberActivated }}/{{ teamSummary.memberTotal }} 已激活
|
|成员 {{ teamSummary.memberActivated }}/{{ teamSummary.memberTotal }} 已激活
|
||||||
|
<template v-if="teamActivatedFilter !== ''">
|
||||||
|
|<span class="team-summary-filtered">当前筛选后 {{ teamFilteredSummary.total }} 个小组 · 成员 {{ teamFilteredSummary.memberActivated }}/{{ teamFilteredSummary.memberTotal }} 已激活</span>
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
<!-- R90:解释「选手列表激活人数」与「已激活小组的成员数」不等的原因(差额=部分激活组内的已激活选手) -->
|
||||||
|
<span class="team-summary-hint" v-if="teamActivatedFilter !== '' && teamPartialExtra > 0">
|
||||||
|
说明:「已激活小组」只统计「全员已激活」的小组;另有 {{ teamPartialExtra }} 名已激活选手所在小组尚未全员激活(部分激活),故不计入上表,但会计入「选手列表」的已激活人数。
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<el-table :data="teams" stripe v-loading="teamsLoading" class="admin-table" style="width: 100%">
|
<el-table :data="teams" stripe v-loading="teamsLoading" class="admin-table" style="width: 100%">
|
||||||
@@ -1869,6 +1889,22 @@ const teamActivatedFilter = ref('')
|
|||||||
const teamIncludeTest = ref(false)
|
const teamIncludeTest = ref(false)
|
||||||
const teamSummary = ref(null)
|
const teamSummary = ref(null)
|
||||||
|
|
||||||
|
// R90:双口径汇总的读取辅助(防御式——后端未同步时自动退回全量口径,不显示 undefined)
|
||||||
|
const teamFilteredSummary = computed(() => {
|
||||||
|
const s = teamSummary.value
|
||||||
|
if (!s) return { total: 0, memberTotal: 0, memberActivated: 0 }
|
||||||
|
return {
|
||||||
|
total: s.filteredTotal === undefined ? s.total : s.filteredTotal,
|
||||||
|
memberTotal: s.filteredMemberTotal === undefined ? s.memberTotal : s.filteredMemberTotal,
|
||||||
|
memberActivated: s.filteredMemberActivated === undefined ? s.memberActivated : s.filteredMemberActivated,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 「部分激活」小组内已激活的选手数(= 选手列表激活人数 − 已激活小组成员数 的差额来源)
|
||||||
|
const teamPartialExtra = computed(() => {
|
||||||
|
const s = teamSummary.value
|
||||||
|
return s && typeof s.memberActivatedInPartial === 'number' ? s.memberActivatedInPartial : 0
|
||||||
|
})
|
||||||
|
|
||||||
async function fetchTeams() {
|
async function fetchTeams() {
|
||||||
teamsLoading.value = true
|
teamsLoading.value = true
|
||||||
try {
|
try {
|
||||||
@@ -1894,6 +1930,14 @@ function onAccountsViewChange(view) {
|
|||||||
if (view === 'teams') fetchTeams()
|
if (view === 'teams') fetchTeams()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// R90:选手列表「小组编号」显示文本(与小组视图同优先级:teamNumber → teamId → 队伍 #uid)
|
||||||
|
function accountTeamLabel(row) {
|
||||||
|
if (!row) return ''
|
||||||
|
if (row.teamNumber || row.teamId) return row.teamNumber || row.teamId
|
||||||
|
if (row.teamUid !== null && row.teamUid !== undefined) return '队伍 #' + row.teamUid
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchAccounts() {
|
async function fetchAccounts() {
|
||||||
accountsLoading.value = true
|
accountsLoading.value = true
|
||||||
try {
|
try {
|
||||||
@@ -3815,6 +3859,16 @@ onMounted(() => {
|
|||||||
color: #606266;
|
color: #606266;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
/* R90:当前筛选后口径(与全量口径同排显示,用主色区分) */
|
||||||
|
.team-summary-filtered { color: #409eff; font-weight: 600; }
|
||||||
|
/* R90:口径说明(解释「选手列表激活人数」与「已激活小组成员数」的差额来源) */
|
||||||
|
.team-summary-hint {
|
||||||
|
flex-basis: 100%;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
/* 小组视图:未填写学校时的占位样式(区别于有值文本) */
|
/* 小组视图:未填写学校时的占位样式(区别于有值文本) */
|
||||||
.team-school-missing { color: #c0c4cc; }
|
.team-school-missing { color: #c0c4cc; }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user