完成批次 L-R:限流移除/白名单驱动/打印增强/全屏/表格对齐/主题变量/滚动闪烁修复
- 移除登录失败限流;fix-config.php 修正 e2e 串味的 127.0.0.1 连接 - 白名单驱动缺/待考名单(DataService + SurveyKingClient::getWhitelist,复用已取 survey) - 打印成绩明细增强(范围/合格列默认关/去页脚);看板全屏占满;表格 vertical-align:middle;隐藏登陆账号;导入默认追加 - 主题变量收口(:root 新增 brand/primary-2/ok-2 等,替换硬编码渐变);删死规则 - 表头/数据列对齐修复(index 未匹配表、dashboard 成员榜、draw 结果/历史/排除表);看板占位卡片配色一致 - 修复滚动闪烁:row-drop-in 动画经 _dropAnim 门控,仅强制重绘时播放 - 测试:新增 test-whitelist.js;run-all.sh 全绿 132
This commit is contained in:
+86
-22
@@ -30,9 +30,15 @@ class DataService
|
||||
return $out;
|
||||
}
|
||||
|
||||
// children 在根或子节点
|
||||
// 不同卷王版本题目可能挂在 children / questions / list 下,统一兼容
|
||||
$self = $node;
|
||||
$children = (isset($node['children']) && is_array($node['children'])) ? $node['children'] : null;
|
||||
$children = null;
|
||||
foreach (['children', 'questions', 'list', 'items'] as $ck) {
|
||||
if (isset($node[$ck]) && is_array($node[$ck])) {
|
||||
$children = $node[$ck];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$id = isset($self['id']) ? (string)$self['id'] : '';
|
||||
$title = isset($self['title']) ? self::cleanTitle((string)$self['title']) : '';
|
||||
@@ -138,26 +144,15 @@ class DataService
|
||||
}
|
||||
}
|
||||
|
||||
$students = Roster::all();
|
||||
// 免考:从名单中剔除,使其既不计入「总人数」也不计入「缺考」
|
||||
// 基础名单(全名单):用于答卷匹配,不受白名单影响
|
||||
$rosterAll = Roster::all();
|
||||
if ($exempt !== []) {
|
||||
$students = array_values(array_filter(
|
||||
$students,
|
||||
$rosterAll = array_values(array_filter(
|
||||
$rosterAll,
|
||||
static fn(array $s): bool => !isset($exempt[trim((string)($s['sid'] ?? ''))])
|
||||
));
|
||||
}
|
||||
// 姓名 -> 学号 / 班级(规范化)
|
||||
$studentInfo = [];
|
||||
foreach ($students as $s) {
|
||||
$k = ny_norm_name((string)$s['name']);
|
||||
if ($k !== '') {
|
||||
$studentInfo[$k] = [
|
||||
'sid' => (string)$s['sid'],
|
||||
'cls' => (string)($s['cls'] ?? ''),
|
||||
'grp' => (string)($s['grp'] ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
// 注:$students(应考名单)与 $studentInfo 在解析出 $survey(含白名单)后统一构建,见下方
|
||||
|
||||
$project = $client->getProject($projectId);
|
||||
$survey = $project['survey'] ?? null;
|
||||
@@ -167,6 +162,73 @@ class DataService
|
||||
$survey = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
// 卷内「答题白名单」:决定「应考名单」(缺/待考名单来源)。
|
||||
// 未设置白名单时「按现在的方案显示」(使用全名单)。
|
||||
$paperWhitelist = [];
|
||||
try {
|
||||
$paperWhitelist = $client->getWhitelist($projectId, $survey);
|
||||
} catch (Throwable $e) {
|
||||
$paperWhitelist = [];
|
||||
}
|
||||
$useWhitelist = ($paperWhitelist !== []);
|
||||
$whitelistTotal = 0;
|
||||
|
||||
if ($useWhitelist) {
|
||||
// 以学号 / 登陆账号 / 姓名(大小写不敏感)把白名单成员匹配回名单,
|
||||
// 命中的取名单里的姓名/班级/组别;未命中的以白名单值作占位,避免漏统计。
|
||||
$rosterMap = [];
|
||||
foreach ($rosterAll as $s) {
|
||||
$sid = strtolower(trim((string)($s['sid'] ?? '')));
|
||||
$login = strtolower(trim((string)($s['login'] ?? '')));
|
||||
$name = strtolower(trim((string)($s['name'] ?? '')));
|
||||
if ($sid) { $rosterMap['sid:' . $sid] = $s; }
|
||||
if ($login) { $rosterMap['login:' . $login] = $s; }
|
||||
if ($name) { $rosterMap['name:' . $name] = $s; }
|
||||
}
|
||||
$students = [];
|
||||
$seen = [];
|
||||
foreach ($paperWhitelist as $v) {
|
||||
$key = strtolower(trim((string)$v));
|
||||
$m = $rosterMap['sid:' . $key] ?? $rosterMap['login:' . $key] ?? $rosterMap['name:' . $key] ?? null;
|
||||
if ($m !== null) {
|
||||
$sid = trim((string)($m['sid'] ?? ''));
|
||||
if ($sid !== '' && isset($seen[$sid])) {
|
||||
continue;
|
||||
}
|
||||
if ($sid !== '') { $seen[$sid] = true; }
|
||||
$students[] = $m;
|
||||
} else {
|
||||
if (isset($seen['wk:' . $key])) {
|
||||
continue;
|
||||
}
|
||||
$seen['wk:' . $key] = true;
|
||||
$students[] = ['sid' => $v, 'name' => $v, 'cls' => '', 'grp' => '', 'login' => ''];
|
||||
}
|
||||
}
|
||||
$whitelistTotal = count($students);
|
||||
} else {
|
||||
$students = $rosterAll;
|
||||
}
|
||||
|
||||
// 姓名 -> 学号 / 班级(规范化):全名单 + 白名单独有成员都纳入,
|
||||
// 保证所有提交(含白名单外提交)都能匹配到姓名。
|
||||
$studentInfo = [];
|
||||
$addInfo = static function (array $s) use (&$studentInfo): void {
|
||||
$k = ny_norm_name((string)($s['name'] ?? ''));
|
||||
if ($k !== '') {
|
||||
$studentInfo[$k] = [
|
||||
'sid' => (string)$s['sid'],
|
||||
'cls' => (string)($s['cls'] ?? ''),
|
||||
'grp' => (string)($s['grp'] ?? ''),
|
||||
];
|
||||
}
|
||||
};
|
||||
foreach ($rosterAll as $s) { $addInfo($s); }
|
||||
if ($useWhitelist) {
|
||||
foreach ($students as $s) { $addInfo($s); }
|
||||
}
|
||||
|
||||
$questions = self::flattenQuestions($survey);
|
||||
|
||||
$keywords = (string)($cfg['name_keywords'] ?? '姓名');
|
||||
@@ -218,7 +280,7 @@ class DataService
|
||||
continue;
|
||||
}
|
||||
$v = ny_flatten_value($answerMap[$qid]);
|
||||
if ($v !== '' && isset($nameMap[ny_norm_name($v)])) {
|
||||
if ($v !== '' && isset($studentInfo[ny_norm_name($v)])) {
|
||||
$name = $v;
|
||||
break;
|
||||
}
|
||||
@@ -227,7 +289,7 @@ class DataService
|
||||
if ($name === '' && $questions === []) {
|
||||
foreach ($answerMap as $v) {
|
||||
$s = ny_flatten_value($v);
|
||||
if ($s !== '' && isset($nameMap[ny_norm_name($s)])) {
|
||||
if ($s !== '' && isset($studentInfo[ny_norm_name($s)])) {
|
||||
$name = $s;
|
||||
break;
|
||||
}
|
||||
@@ -252,8 +314,8 @@ class DataService
|
||||
$ts = ny_to_timestamp($a['updateAt'] ?? null);
|
||||
}
|
||||
|
||||
// 考试时长
|
||||
$ai = $a['metaInfo']['answerInfo'] ?? [];
|
||||
// 考试时长(metaInfo 可能缺失,先整体判空避免每答案两条 Undefined key 警告)
|
||||
$ai = ($a['metaInfo'] ?? [])['answerInfo'] ?? [];
|
||||
$start = is_array($ai) ? (int)($ai['startTime'] ?? 0) : 0;
|
||||
$end = is_array($ai) ? (int)($ai['endTime'] ?? 0) : 0;
|
||||
$dur = null;
|
||||
@@ -371,6 +433,8 @@ class DataService
|
||||
'stats' => $stats,
|
||||
'roster' => $students,
|
||||
'roster_count' => count($students),
|
||||
'whitelist_driven' => $useWhitelist,
|
||||
'whitelist_total' => $whitelistTotal,
|
||||
// 本卷独立配置(前端据此回显满分/合格率/免考);full_score 已按本卷解析
|
||||
'project_cfg' => [
|
||||
'full_score' => $pCfg['full_score'] ?? null,
|
||||
|
||||
Reference in New Issue
Block a user