feat: 登录态持久化 + 禁用按钮灰色 + 登录提示精简 + 全局美化 + 独立数据看板页
This commit is contained in:
@@ -119,6 +119,76 @@ try {
|
||||
];
|
||||
ny_ok($result);
|
||||
|
||||
// ---------------- 数据看板(按班级聚合,面向班主任/学生整体情况) ----------------
|
||||
case 'dashboard':
|
||||
$projectId = (string)($_GET['projectId'] ?? '');
|
||||
if ($projectId === '') {
|
||||
ny_fail('请先选择问卷/考试');
|
||||
}
|
||||
$nameQid = (string)($_GET['nameQid'] ?? '');
|
||||
$c = cfg();
|
||||
$result = DataService::build($c, $projectId, $nameQid);
|
||||
$fsMap = is_array($c['full_scores'] ?? null) ? $c['full_scores'] : [];
|
||||
$fs = isset($fsMap[$projectId]) ? (int)$fsMap[$projectId] : (int)($c['full_score'] ?? 100);
|
||||
if ($fs < 1) {
|
||||
$fs = 100;
|
||||
}
|
||||
|
||||
$students = $result['roster'] ?: [];
|
||||
$rows = $result['rows'] ?: [];
|
||||
|
||||
$byCls = [];
|
||||
foreach ($students as $s) {
|
||||
$k = trim((string)($s['cls'] ?? '')) ?: '(未分班)';
|
||||
$byCls[$k][] = $s;
|
||||
}
|
||||
$rowsByCls = [];
|
||||
foreach ($rows as $r) {
|
||||
$k = trim((string)($r['cls'] ?? '')) ?: '(未分班)';
|
||||
$rowsByCls[$k][] = $r;
|
||||
}
|
||||
|
||||
$classes = [];
|
||||
foreach ($byCls as $k => $stu) {
|
||||
$rs = $rowsByCls[$k] ?? [];
|
||||
$total = count($stu);
|
||||
$submitted = count($rs);
|
||||
$scores = array_values(array_filter(array_map(static fn($r) => $r['score'] ?? null, $rs), static fn($v) => $v !== null));
|
||||
$avg = $scores !== [] ? round(array_sum($scores) / count($scores), 2) : null;
|
||||
$pass = count(array_filter($rs, static fn($r) => ($r['score'] ?? null) !== null && ($r['score'] >= 0.6 * $fs)));
|
||||
$passRate = $submitted > 0 ? round($pass / $submitted * 100, 1) : 0;
|
||||
$durs = array_values(array_filter(array_map(static fn($r) => $r['duration'] ?? null, $rs), static fn($v) => $v !== null));
|
||||
$avgDur = $durs !== [] ? (int)round(array_sum($durs) / count($durs)) : null;
|
||||
$classes[] = [
|
||||
'class' => $k,
|
||||
'total' => $total,
|
||||
'submitted' => $submitted,
|
||||
'absent' => max(0, $total - $submitted),
|
||||
'rate' => $total > 0 ? round($submitted / $total * 100, 1) : 0,
|
||||
'avg' => $avg,
|
||||
'pass_rate' => $passRate,
|
||||
'max' => $scores !== [] ? max($scores) : null,
|
||||
'min' => $scores !== [] ? min($scores) : null,
|
||||
'avg_duration' => $avgDur,
|
||||
];
|
||||
}
|
||||
usort($classes, static function (array $a, array $b): int {
|
||||
$an = is_numeric($a['class']) ? (int)$a['class'] : $a['class'];
|
||||
$bn = is_numeric($b['class']) ? (int)$b['class'] : $b['class'];
|
||||
if (is_int($an) && is_int($bn)) {
|
||||
return $an <=> $bn;
|
||||
}
|
||||
return strcmp((string)$a['class'], (string)$b['class']);
|
||||
});
|
||||
|
||||
ny_ok([
|
||||
'project' => $result['project'],
|
||||
'full_score' => $fs,
|
||||
'overall' => $result['stats'],
|
||||
'classes' => $classes,
|
||||
'updated_at' => $result['updated_at'],
|
||||
]);
|
||||
|
||||
// ---------------- 名单 ----------------
|
||||
case 'roster':
|
||||
ny_ok(['students' => Roster::all(), 'meta' => Roster::meta()]);
|
||||
|
||||
Reference in New Issue
Block a user