完成批次 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:
2026-09-13 10:25:48 +08:00
parent 804b04de60
commit 94ac381579
17 changed files with 856 additions and 301 deletions
+94
View File
@@ -483,6 +483,100 @@ class SurveyKingClient
return is_array($r['data']) ? $r['data'] : [];
}
/**
* 取问卷设置的「答题白名单」成员列表(待考名单来源)
*
* 卷王不同版本字段形状不一,这里尽量兼容:
* - survey.surveySetting.whiteLists:{ 题ID: [值,...] }(社区版常见)
* - survey.surveySetting.whiteList:{ 题ID: [值,...] }
* - 也可能直接是字符串或嵌套数组
* 返回去重后的纯值数组(顺序保持首次出现),值本身通常是学号 / 姓名 / 登陆账号。
* 未设置白名单返回空数组(上层据此「按现在的方案显示」)。
*
* @param string $projectId 问卷 ID
* @param array|null $survey 可传入已解析的 survey 对象,避免重复请求 API(DataService::build 已取过 project)
* @return array<int,string>
*/
public function getWhitelist(string $projectId, ?array $survey = null): array
{
if ($survey === null) {
$proj = $this->getProject($projectId);
if (!is_array($proj)) {
return [];
}
$survey = $proj['survey'] ?? null;
if (is_string($survey) && $survey !== '') {
$decoded = json_decode($survey, true);
if (is_array($decoded)) {
$survey = $decoded;
}
}
}
// 兼容 { surveySetting: ... } 与 { survey: { surveySetting: ... } } 两种结构,
// 也允许白名单直接挂在 survey 顶层(whiteLists / whiteList / whitelist)。
$setting = null;
if (is_array($survey)) {
if (isset($survey['surveySetting']) && is_array($survey['surveySetting'])) {
$setting = $survey['surveySetting'];
} elseif (isset($survey['survey']) && is_array($survey['survey']) && isset($survey['survey']['surveySetting'])) {
$setting = $survey['survey']['surveySetting'];
} else {
// 顶层直接是白名单结构
foreach (['whiteLists', 'whiteList', 'whitelist'] as $wk) {
if (isset($survey[$wk]) && (is_array($survey[$wk]) || is_string($survey[$wk]))) {
$setting = $survey;
break;
}
}
}
}
if (!is_array($setting)) {
return [];
}
return self::parseWhitelist($setting);
}
/**
* 从 surveySetting 中解析白名单成员(去重、保留首次出现顺序)。
* @return array<int,string>
*/
public static function parseWhitelist(array $setting): array
{
$raw = $setting['whiteLists'] ?? $setting['whiteList'] ?? $setting['whitelist'] ?? null;
if ($raw === null || $raw === [] || $raw === '') {
return [];
}
$out = [];
$walk = static function ($node) use (&$out, &$walk): void {
if (is_array($node)) {
// 列表或对象都逐值递归(兼容 { 题ID: [值] })
foreach ($node as $v) {
$walk($v);
}
return;
}
if (is_string($node) && trim($node) !== '') {
$out[] = trim($node);
} elseif (is_scalar($node) && $node !== '' && $node !== null) {
$out[] = (string)$node;
}
};
$walk($raw);
// 去重(大小写不敏感),保留首次出现顺序
$seen = [];
$res = [];
foreach ($out as $item) {
$key = strtolower($item);
if (!isset($seen[$key])) {
$seen[$key] = true;
$res[] = $item;
}
}
return $res;
}
/** 答卷列表(自动翻页取全量) */
public function listAnswers(string $projectId): array
{