feat: 看板每组显示考试成员榜(排名/姓名/成绩,成绩降序) + 防溢出

This commit is contained in:
2026-09-08 20:02:34 +08:00
parent 1b7683baab
commit 8e3ea32567
3 changed files with 91 additions and 4 deletions
+32
View File
@@ -171,11 +171,42 @@ try {
];
};
// 已提交成员榜:按成绩从高到低,缺成绩者排最后,并给出排名
$membersOf = static function (array $rs): array {
$ms = [];
foreach ($rs as $r) {
$ms[] = [
'sid' => (string)($r['sid'] ?? ''),
'name' => (string)($r['name'] ?? ''),
'score' => ($r['score'] ?? null),
];
}
usort($ms, static function (array $a, array $b): int {
$as = $a['score'];
$bs = $b['score'];
if ($as === null && $bs === null) {
return 0;
}
if ($as === null) {
return 1; // 无成绩排最后
}
if ($bs === null) {
return -1;
}
return $bs <=> $as; // 成绩降序
});
foreach ($ms as $i => $m) {
$ms[$i]['rank'] = $i + 1;
}
return $ms;
};
$classes = [];
foreach ($byCls as $k => $stu) {
$rs = $rowsByCls[$k] ?? [];
$row = $statOf($stu, $rs, $fs);
$row['class'] = $k;
$row['members'] = $membersOf($rs);
// 班级内按组别(A/B 等)再聚合,用于看板下钻对比
$stuByGrp = [];
@@ -192,6 +223,7 @@ try {
foreach ($stuByGrp as $g => $gstu) {
$gs = $statOf($gstu, $rowsByGrp[$g] ?? [], $fs);
$gs['group'] = $g;
$gs['members'] = $membersOf($rowsByGrp[$g] ?? []);
$groups[] = $gs;
}
usort($groups, static fn(array $a, array $b): int => strcmp((string)$a['group'], (string)$b['group']));
+28
View File
@@ -499,8 +499,36 @@ body.fs-on .topbar { padding-top: 8px; padding-bottom: 8px; }
.gpanel .ghead strong { font-size: 16px; }
.gpanel .ghead .rate-num { font-size: 15px; font-weight: 800; }
/* 成员榜:排名 / 姓名 / 成绩 */
.mem-title {
margin-top: 16px; margin-bottom: 8px;
font-size: 12px; font-weight: 700; color: var(--text-2);
}
.member-list {
max-height: 300px; overflow: auto;
border: 1px solid var(--border-2); border-radius: 10px; background: #fff;
}
.member-list table.grid { font-size: 12.5px; }
.member-list table.grid th { padding: 8px 10px; }
.member-list table.grid td { padding: 6px 10px; }
.rk {
display: inline-flex; align-items: center; justify-content: center;
min-width: 22px; height: 22px; padding: 0 6px; border-radius: 999px;
background: var(--border-2); color: var(--text-2);
font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.rk-1 { background: #fff2d6; color: #b7791f; }
.rk-2 { background: #eef1f6; color: #64748b; }
.rk-3 { background: #fbe9dd; color: #b45309; }
/* 防溢出:网格子项允许收缩,长内容不撑破页面 */
.class-grid, .group-compare, .cards, .gpanel, .class-card { min-width: 0; }
.gpanel, .class-card { overflow: hidden; }
.member-list td.name { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 看板全屏适配 */
body.fs-on .wrap { max-width: none; padding-top: 14px; }
body.fs-on .member-list { max-height: 46vh; }
body.fs-on .class-grid { grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); }
body.fs-on .group-compare { grid-template-columns: 1fr 1fr; }
body.fs-on .dash-hero { border-radius: var(--radius); }
+29 -2
View File
@@ -152,6 +152,27 @@ function renderClasses(d){
}).join('');
}
/* ---------- 成员榜:排名 / 姓名 / 成绩(默认成绩降序) ---------- */
function membersTable(members){
if (!members || !members.length) {
return '<div class="empty" style="padding:18px 0">暂无考试成员</div>';
}
return '<div class="member-list"><table class="grid compact">' +
'<thead><tr><th style="width:60px">排名</th><th>姓名</th><th style="width:80px">成绩</th></tr></thead><tbody>' +
members.map(m => {
const rk = m.rank;
const badge = rk === 1 ? 'rk rk-1' : (rk === 2 ? 'rk rk-2' : (rk === 3 ? 'rk rk-3' : 'rk'));
const sc = m.score;
const cls = sc == null ? '' : (sc >= 90 ? 'score-high' : (sc >= 60 ? 'score-mid' : 'score-low'));
return '<tr>' +
'<td class="num"><span class="' + badge + '">' + rk + '</span></td>' +
'<td class="name">' + esc(m.name) + '</td>' +
'<td class="score num ' + cls + '">' + (sc == null ? '-' : sc) + '</td>' +
'</tr>';
}).join('') +
'</tbody></table></div>';
}
/* ---------- 班级详情:整体 + A/B 分组对比 ---------- */
function showClassDetail(name){
if (!DASH) return;
@@ -180,8 +201,12 @@ function showClassDetail(name){
const groups = (c.groups || []).filter(g => g.group !== '(未分组)');
const box = $('#groupCompare');
if (!groups.length) {
$('#groupSecTitle').textContent = '分组对比';
box.innerHTML = '<div class="empty">该班级未设置分组(A/B),仅显示整体数据</div>';
$('#groupSecTitle').textContent = '考试成员';
box.innerHTML = '<div class="gpanel">' +
'<div class="ghead"><strong>本班考试成员</strong>' +
'<span class="rate-num">' + c.submitted + ' 人</span></div>' +
'<div class="sub">该班级未设置分组(A/B),以下按成绩从高到低排列</div>' +
membersTable(c.members) + '</div>';
return;
}
$('#groupSecTitle').textContent = '分组对比(' + groups.map(g => g.group).join(' vs ') + ')';
@@ -198,6 +223,8 @@ function showClassDetail(name){
'<div class="m metric-ok"><span>最高分</span><b>' + (g.max != null ? g.max : '-') + '</b></div>' +
'<div class="m metric-danger"><span>最低分</span><b>' + (g.min != null ? g.min : '-') + '</b></div>' +
'</div>' +
'<div class="mem-title">考试成员 · 按成绩降序(' + g.submitted + ' 人)</div>' +
membersTable(g.members) +
'</div>';
}).join('') + '</div>';
}