feat: 成绩明细打印 + SQLite + 每卷独立配置(满分/合格率/免考) + 导航精简
- 成绩明细打印:默认成绩降序,可选按班级/组别分组对比(段首小计) - 修复看板退出全屏无反应(exitFullscreen 挂在 document 而非 documentElement) - 导航栏精简:三页统一为单一主标题,移除 Logo 与副标题 - 优化三端布局:重排断点、竖排隐藏 spacer、触摸设备热区 - 全面启用 SQLite(data/ny.db):配置/名单/每卷配置入库,无 pdo_sqlite 自动降级文件 - 新增独立合格率配置(全局 + 每卷),合格率不再写死 60% - 新增免考配置,仅对当前试卷生效,试卷间隔离 - 试卷独立满分从设置移出,改到主页面「试卷设置」 - 测试:新增 4 条每卷配置隔离断言,全套 113 项通过
This commit is contained in:
@@ -79,11 +79,73 @@ class DataService
|
||||
/**
|
||||
* 组装结果
|
||||
*/
|
||||
/**
|
||||
* 读取某份试卷的独立配置(满分 / 合格率 / 免考名单)。
|
||||
* 优先 SQLite(ny_project_config 表);不可用时退回 config.json 里的 project_config 字段,
|
||||
* 保证没有 pdo_sqlite 的环境也能用(只是隔离粒度仍在,只是落到文件)。
|
||||
*
|
||||
* @return array{full_score:?int,pass_rate:?float,exempt:array<int,string>}
|
||||
*/
|
||||
public static function projectConfig(string $projectId): array
|
||||
{
|
||||
$def = ['full_score' => null, 'pass_rate' => null, 'exempt' => []];
|
||||
if ($projectId === '') {
|
||||
return $def;
|
||||
}
|
||||
if (class_exists('Db') && Db::pdo() !== null) {
|
||||
return Db::projectConfigGet($projectId);
|
||||
}
|
||||
// 文件回退
|
||||
$all = [];
|
||||
$saved = ny_read_json(NY_DATA . '/project_config.json', null);
|
||||
if (is_array($saved)) {
|
||||
$all = $saved;
|
||||
}
|
||||
$one = $all[$projectId] ?? null;
|
||||
return is_array($one) ? array_merge($def, $one) : $def;
|
||||
}
|
||||
|
||||
/** 写入某份试卷的独立配置 */
|
||||
public static function projectConfigSave(string $projectId, array $cfg): void
|
||||
{
|
||||
if ($projectId === '') {
|
||||
return;
|
||||
}
|
||||
if (class_exists('Db') && Db::pdo() !== null) {
|
||||
Db::projectConfigSet($projectId, $cfg);
|
||||
return;
|
||||
}
|
||||
$all = [];
|
||||
$saved = ny_read_json(NY_DATA . '/project_config.json', null);
|
||||
if (is_array($saved)) {
|
||||
$all = $saved;
|
||||
}
|
||||
$all[$projectId] = $cfg;
|
||||
ny_write_json(NY_DATA . '/project_config.json', $all);
|
||||
}
|
||||
|
||||
public static function build(array $cfg, string $projectId, string $nameQid = ''): array
|
||||
{
|
||||
$client = new SurveyKingClient($cfg);
|
||||
|
||||
// 每卷独立配置(满分 / 合格率 / 免考名单):试卷之间互相隔离
|
||||
$pCfg = self::projectConfig($projectId);
|
||||
$exempt = [];
|
||||
foreach ((array)($pCfg['exempt'] ?? []) as $sid) {
|
||||
$sid = trim((string)$sid);
|
||||
if ($sid !== '') {
|
||||
$exempt[$sid] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$students = Roster::all();
|
||||
// 免考:从名单中剔除,使其既不计入「总人数」也不计入「缺考」
|
||||
if ($exempt !== []) {
|
||||
$students = array_values(array_filter(
|
||||
$students,
|
||||
static fn(array $s): bool => !isset($exempt[trim((string)($s['sid'] ?? ''))])
|
||||
));
|
||||
}
|
||||
// 姓名 -> 学号 / 班级(规范化)
|
||||
$studentInfo = [];
|
||||
foreach ($students as $s) {
|
||||
@@ -313,6 +375,12 @@ class DataService
|
||||
'anomalies' => $anomalies,
|
||||
'roster' => $students,
|
||||
'roster_count' => count($students),
|
||||
// 本卷独立配置(前端据此回显满分/合格率/免考);full_score 已按本卷解析
|
||||
'project_cfg' => [
|
||||
'full_score' => $pCfg['full_score'] ?? null,
|
||||
'pass_rate' => $pCfg['pass_rate'] ?? null,
|
||||
'exempt' => array_values(array_map('strval', array_keys($exempt))),
|
||||
],
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user