From 8e3ea32567476866ee201c7d39278265a5d5a9e3 Mon Sep 17 00:00:00 2001 From: nxxy335 Date: Tue, 8 Sep 2026 20:02:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=9C=8B=E6=9D=BF=E6=AF=8F=E7=BB=84?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E8=80=83=E8=AF=95=E6=88=90=E5=91=98=E6=A6=9C?= =?UTF-8?q?(=E6=8E=92=E5=90=8D/=E5=A7=93=E5=90=8D/=E6=88=90=E7=BB=A9,?= =?UTF-8?q?=E6=88=90=E7=BB=A9=E9=99=8D=E5=BA=8F)=20+=20=E9=98=B2=E6=BA=A2?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.php | 36 ++++++++++++++++++++++++++++++++++-- assets/style.css | 28 ++++++++++++++++++++++++++++ dashboard.php | 31 +++++++++++++++++++++++++++++-- 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/api.php b/api.php index 92c922e..1f7b13f 100644 --- a/api.php +++ b/api.php @@ -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['class'] = $k; + $row['members'] = $membersOf($rs); // 班级内按组别(A/B 等)再聚合,用于看板下钻对比 $stuByGrp = []; @@ -191,7 +222,8 @@ try { $groups = []; foreach ($stuByGrp as $g => $gstu) { $gs = $statOf($gstu, $rowsByGrp[$g] ?? [], $fs); - $gs['group'] = $g; + $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'])); diff --git a/assets/style.css b/assets/style.css index 31c151c..65d8cf9 100644 --- a/assets/style.css +++ b/assets/style.css @@ -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); } diff --git a/dashboard.php b/dashboard.php index 33e2c60..c56f0c4 100644 --- a/dashboard.php +++ b/dashboard.php @@ -152,6 +152,27 @@ function renderClasses(d){ }).join(''); } +/* ---------- 成员榜:排名 / 姓名 / 成绩(默认成绩降序) ---------- */ +function membersTable(members){ + if (!members || !members.length) { + return '
暂无考试成员
'; + } + return '
' + + '' + + 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 '' + + '' + + '' + + '' + + ''; + }).join('') + + '
排名姓名成绩
' + rk + '' + esc(m.name) + '' + (sc == null ? '-' : sc) + '
'; +} + /* ---------- 班级详情:整体 + 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 = '
该班级未设置分组(A/B),仅显示整体数据
'; + $('#groupSecTitle').textContent = '考试成员'; + box.innerHTML = '
' + + '
本班考试成员' + + '' + c.submitted + ' 人
' + + '
该班级未设置分组(A/B),以下按成绩从高到低排列
' + + membersTable(c.members) + '
'; return; } $('#groupSecTitle').textContent = '分组对比(' + groups.map(g => g.group).join(' vs ') + ')'; @@ -198,6 +223,8 @@ function showClassDetail(name){ '
最高分' + (g.max != null ? g.max : '-') + '
' + '
最低分' + (g.min != null ? g.min : '-') + '
' + '' + + '
考试成员 · 按成绩降序(' + g.submitted + ' 人)
' + + membersTable(g.members) + ''; }).join('') + ''; }