feat: 看板每组显示考试成员榜(排名/姓名/成绩,成绩降序) + 防溢出
This commit is contained in:
@@ -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 = [];
|
$classes = [];
|
||||||
foreach ($byCls as $k => $stu) {
|
foreach ($byCls as $k => $stu) {
|
||||||
$rs = $rowsByCls[$k] ?? [];
|
$rs = $rowsByCls[$k] ?? [];
|
||||||
$row = $statOf($stu, $rs, $fs);
|
$row = $statOf($stu, $rs, $fs);
|
||||||
$row['class'] = $k;
|
$row['class'] = $k;
|
||||||
|
$row['members'] = $membersOf($rs);
|
||||||
|
|
||||||
// 班级内按组别(A/B 等)再聚合,用于看板下钻对比
|
// 班级内按组别(A/B 等)再聚合,用于看板下钻对比
|
||||||
$stuByGrp = [];
|
$stuByGrp = [];
|
||||||
@@ -191,7 +222,8 @@ try {
|
|||||||
$groups = [];
|
$groups = [];
|
||||||
foreach ($stuByGrp as $g => $gstu) {
|
foreach ($stuByGrp as $g => $gstu) {
|
||||||
$gs = $statOf($gstu, $rowsByGrp[$g] ?? [], $fs);
|
$gs = $statOf($gstu, $rowsByGrp[$g] ?? [], $fs);
|
||||||
$gs['group'] = $g;
|
$gs['group'] = $g;
|
||||||
|
$gs['members'] = $membersOf($rowsByGrp[$g] ?? []);
|
||||||
$groups[] = $gs;
|
$groups[] = $gs;
|
||||||
}
|
}
|
||||||
usort($groups, static fn(array $a, array $b): int => strcmp((string)$a['group'], (string)$b['group']));
|
usort($groups, static fn(array $a, array $b): int => strcmp((string)$a['group'], (string)$b['group']));
|
||||||
|
|||||||
@@ -499,8 +499,36 @@ body.fs-on .topbar { padding-top: 8px; padding-bottom: 8px; }
|
|||||||
.gpanel .ghead strong { font-size: 16px; }
|
.gpanel .ghead strong { font-size: 16px; }
|
||||||
.gpanel .ghead .rate-num { font-size: 15px; font-weight: 800; }
|
.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 .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 .class-grid { grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); }
|
||||||
body.fs-on .group-compare { grid-template-columns: 1fr 1fr; }
|
body.fs-on .group-compare { grid-template-columns: 1fr 1fr; }
|
||||||
body.fs-on .dash-hero { border-radius: var(--radius); }
|
body.fs-on .dash-hero { border-radius: var(--radius); }
|
||||||
|
|||||||
+29
-2
@@ -152,6 +152,27 @@ function renderClasses(d){
|
|||||||
}).join('');
|
}).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 分组对比 ---------- */
|
/* ---------- 班级详情:整体 + A/B 分组对比 ---------- */
|
||||||
function showClassDetail(name){
|
function showClassDetail(name){
|
||||||
if (!DASH) return;
|
if (!DASH) return;
|
||||||
@@ -180,8 +201,12 @@ function showClassDetail(name){
|
|||||||
const groups = (c.groups || []).filter(g => g.group !== '(未分组)');
|
const groups = (c.groups || []).filter(g => g.group !== '(未分组)');
|
||||||
const box = $('#groupCompare');
|
const box = $('#groupCompare');
|
||||||
if (!groups.length) {
|
if (!groups.length) {
|
||||||
$('#groupSecTitle').textContent = '分组对比';
|
$('#groupSecTitle').textContent = '考试成员';
|
||||||
box.innerHTML = '<div class="empty">该班级未设置分组(A/B),仅显示整体数据</div>';
|
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;
|
return;
|
||||||
}
|
}
|
||||||
$('#groupSecTitle').textContent = '分组对比(' + groups.map(g => g.group).join(' vs ') + ')';
|
$('#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-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 class="m metric-danger"><span>最低分</span><b>' + (g.min != null ? g.min : '-') + '</b></div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
|
'<div class="mem-title">考试成员 · 按成绩降序(' + g.submitted + ' 人)</div>' +
|
||||||
|
membersTable(g.members) +
|
||||||
'</div>';
|
'</div>';
|
||||||
}).join('') + '</div>';
|
}).join('') + '</div>';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user