16 Commits
Author SHA1 Message Date
nxxy335 d05fd4a9b5 feat(export/dashboard): 导出增强与大屏公开化
导出-每次考试/多考试对比:
- 合格次数阈值等标记项抽为常显区块,三类成绩表共用
- 附缺考名单扩展到多考试导出(取所选考试全都没交的人)
- 块表每场固定2列(成绩+合并空位),修复漏推空串导致的整行左移/串列
- 列宽按实际内容估算,合并区空位只给最小宽度
- 合格次数始终显示实际次数(含0),仅 ≥ 阈值标绿底
- 打印标题改为「成绩明细」,缺考名单分页单独成页
- 打印样式加 print-color-adjust:exact,绿底/红字可正常印出

数据大屏:
- ?class= 班级大屏免登录公开访问,接口侧放行并按班级裁剪后回传
- 单班大屏移除退出按钮,仅首页保留
- 修复红绿判定真bug:membersTable 误用 window.DASH(DASH为let声明取不到)
  改为 scoreGrade() 直接读自由变量,按后台合格率正确判红/黄/绿

测试/样例/文档同步更新,全量回归通过。
2026-09-27 22:04:17 +08:00
nxxy335 f661862d46 fix(auth): 修复登录后反复报未配置账号并跳回登录页的死循环
根因:ny_save_config 只写不校验,Db::settingsSet 内部吞异常且返回 void。
data/ 目录不可写时登录页照常显示登录成功,但下一请求读到的账号仍是空的
→ 抛未配置卷王登录账号 → 前端 auth 打标 → 跳登录页 → 无限循环。

修复:
- Db::settingsSet 改返回 bool 并回读校验;新增 settingsDelete
- ny_save_config 双写后均做回读校验,失败写入 ny_config_save_error()
- 新增 ny_config_diag()/ny_session_usable(),暴露来源/可写性/是否已存账号
- ny_load_config 把 null 账号强制成空字符串(脏数据不再伪装成未配置)
- login.php 存凭据后回读确认,存不住/会话不可写直接报错而不是假成功
- login.php 账号为空时页面直接给出 data 目录写权限提示
- config_save 合并逻辑抽成 ny_apply_config_input:账号/密码不许被改空
- 鉴权失败响应带 extra.diag,前端 401 也算 auth
- 前端防死循环:刚登录 120s 内或已跳 2 次则停止自动跳转,弹窗给出真实原因+诊断

测试:新增 tests/test-config-persist.php(29) 与 tools/test-login-loop.js(19);
run-all.sh 纳入两者。全套 148+48=196 项通过。
2026-09-14 08:44:09 +08:00
nxxy335 eb5d06d392 修复考试白名单驱动缺/待考名单:改用卷王项目参与者(Project Partners)真实模型
旧代码误读 surveySetting.whiteLists(实为题目选项白名单),导致线上从未触发。现改为读取 /api/project/partner/list 的 RESPONDENT_SYS_USER/IMP_USER 参与者作为应考名单,并保留 whiteLists 作为极老版本兜底。

dashboard.php 移除未实现的提交率卡片;全_score 默认 100 已在 data 层修正。e2e 测试 mock 与断言同步改为真实模型,全量 132 测试通过。
2026-09-13 11:02:34 +08:00
nxxy335 94ac381579 完成批次 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
2026-09-13 10:25:48 +08:00
nxxy335 25a2e8a42c 文档部署到 /ny-exam-platform 子路径 + 移除快照/趋势/异常 + 打印与 UI 全面完善
文档站(#88)
- base 默认改为 /ny-exam-platform/,可用 DOCS_BASE=/ 覆盖本地预览
- 新增 DOCS_OUT 环境变量,避免构建时清空输出目录失败导致产物半截
- 新增 tools/docs-preview.js:按子路径重写路由的本地预览服务
- 删除 docs/guide/snapshot.md,同步清理全部相关文档

移除存快照 / 趋势 / 异常(#89)
- 后端:删除 api.php 的 snapshot_save/list/delete/trend 四个 action 与
  $WRITE_ACTIONS 中的同名条目;删除 lib/Snapshot.php 与自动加载注册;
  DataService 移除 detectAnomalies() 及其调用
- 前端:移除三个按钮与三个弹窗、相关 JS 处理与 state 字段
- 测试:移除对应用例,补充 5 条名单导入列识别断言(17+9+18+32+29+11 全过)

打印完善(#90)
- 改用隐藏 iframe 输出,规避弹窗拦截;@page A4 支持纵向/横向
- 表头每页重复、行内不跨页断开、全表连续名次、可选「是否合格」列
- 分组小计(人数/平均/最高/最低/合格人数与合格率)、可附缺考名单另起一页
- 末尾签字栏;缺考名单打印改为同一套输出

UI / 交互 / Bug 修复(#91)
- dashboard.php:补「← 主系统 / 退出」入口(此前全屏后无法离开);
  修复成员下钻 onclick 里 JSON.stringify 双引号提前闭合属性导致点击报错,
  改为 data-sid 事件委托;补 Esc 关弹窗;后台标签页暂停轮询、切回自动补拉;
  刷新中断原因写入 title;数值字段统一兜底,避免 NaN / undefined%
- draw.php / draw.js:结果表新增「登陆账号」列(导出前可核对,缺失显示 学号 ⚠);
  弹窗只关最上层且支持 Esc;「全部」改为跟随候选池,手工改人数即解除;
  候选池不足时给出明确提示;自定义比例框隐藏时清空,避免旧权重被误读
- app.js / dashboard.php:成绩着色改为按「占本卷满分的百分比」判断,
  修复满分不是 100 时整列误标红
- style.css:数值列右对齐;新增 subbar、draw-stage-sub;
  看板顶栏窄屏下拉与按钮换行,消除横向滚动
2026-09-11 16:23:08 +08:00
nxxy335 157f257728 feat: 成绩明细打印 + SQLite + 每卷独立配置(满分/合格率/免考) + 导航精简
- 成绩明细打印:默认成绩降序,可选按班级/组别分组对比(段首小计)
- 修复看板退出全屏无反应(exitFullscreen 挂在 document 而非 documentElement)
- 导航栏精简:三页统一为单一主标题,移除 Logo 与副标题
- 优化三端布局:重排断点、竖排隐藏 spacer、触摸设备热区
- 全面启用 SQLite(data/ny.db):配置/名单/每卷配置入库,无 pdo_sqlite 自动降级文件
- 新增独立合格率配置(全局 + 每卷),合格率不再写死 60%
- 新增免考配置,仅对当前试卷生效,试卷间隔离
- 试卷独立满分从设置移出,改到主页面「试卷设置」
- 测试:新增 4 条每卷配置隔离断言,全套 113 项通过
2026-09-10 20:22:17 +08:00
nxxy335 9ee2aec2f0 feat: 全面打磨平台交互
- 名单导入容错:rowsToStudents 加去重/跳过统计、表头正则扩充、.0 尾巴清理;importFile 返回精确 count
- 看板:修复 .repeat(0) 空状态坏代码;新增实时点 stale 指示(手动/自动刷新失败变红 + 提示刷新中断);
        手动刷新按钮加刷新中…反馈;记住自动刷新开关与间隔(localStorage + ?interval= 深链)
- 主统计页:后台静默刷新连续失败时连接指示转实时刷新中断告警;记住用户手动调整的刷新间隔
- 设置页:新增 config_test 端点 + 独立测试连接按钮(用弹窗内未保存凭据测连接,不落盘);
        保存按钮加保存中…防重提交;登录失败不清旧 token
- 抽取页:重置结果加二次确认;新增复制名单按钮(两列制表符分隔,可粘到微信/Excel)
- 修复看板预先存在 JS 语法错误(少一个右括号导致整页 JS 失效)
2026-09-09 18:53:00 +08:00
nxxy335 c4c3bf7da1 修复"未配置卷王登录账号"导致后台刷新陷入死状态
根因:login.php 仅持久化 username、不持久化 password;token 缓存失效后
自动重登因缺少密码而抛 RuntimeException('未配置卷王登录账号'),前端仅提示
"后台刷新失败,已显示本地缓存"却无法自愈,刷新循环永不恢复。

修复:
- login.php:登录成功时同时持久化 username 与 password,token 失效时平台
  可凭凭据自动重新登录卷王,避免死状态。
- api.php:全局 catch 对鉴权类错误(未配置账号/登录失败)打标 extra.auth=true。
- assets/app.js、dashboard.php:api() 透传 auth 标记;刷新失败若为鉴权错误,
  自动跳转 login.php 重新登录,不再停留在本地缓存死状态。

验证:本地 mock 卷王端到端复现 CASE1(空账号精确复现报错)与 CASE2
(凭据齐备自动重登成功),HTTP 接口确认返回 extra.auth:true 与 ok:true。
2026-09-09 17:55:58 +08:00
nxxy335 d2f0908385 feat: 支持单套试卷同一提交人多次刷卷并展示
- DataService::build 新增 aggregateByStudent 按学号聚合:
  dedupe=true 仅保留最新一份;false 则每学号一行(代表成绩取最高分)
  附 attempts(次数) 与 attempts_list(每次提交明细),修正提交人数被多次提交inflate的问题
- api.php dashboard 透传 attempts/attempts_list;statOf 增加 attempts 求和;export 增加刷题次数列
- dashboard.php 成员榜加"刷N次"徽标,点击下钻弹窗看每次提交(时间/成绩/时长/暂存);hero 显示总提交次数
- 主统计页成绩明细同样加"刷N次"徽标与下钻弹窗
- stats 新增 attempts_total
2026-09-09 15:55:06 +08:00
nxxy335 3952c77afe refactor: 取消 install.php 安装向导 + 下线 pgsql/mysql 支持 + 班级看板独立
- 删除 install.php:所有入口(index/dashboard/draw/login/api)不再跳安装,
  未登录统一跳 login.php;连接信息变更在【设置】里直接修改 data/config.json
- 下线 pgsql/mysql:
  * bootstrap.php 简化为单文件存储路径(ny_load_config/ny_save_config 直接读写 data/config.json),
    删除 ny_db_config、配置迁移、配置双写分支
  * lib/Db.php 改为纯 stub:pdo() 恒返回 null,避免上层误连;migrate/ensureRosterLogin 等为 no-op
  * Roster.php 经 useDb 永远 false,自动走 roster.json
  * api.php 不再返回 db_driver/db_connected/db_error 诊断
- 班级看板独立(dashboard.php 重写):
  * 删除"← 返回总览"按钮 + 整体概况 #overview 区
  * 进入即展示单班看板:6 张汇总卡 + 分组对比/成员榜
  * 顶部 hero 区动态显示当前班级名 + 问卷名
  * 工具栏新增 #classSel 班级选择器(切换班级不离开本页)
  * URL 支持 ?pid=&class=&auto=1|0 分享与刷新恢复
  * 下拉班级选项随数据动态刷新;保留自动刷新 + 全屏 + 缓存
- e2e 测试(test-config-save)加固 cleanup 钩子:
  * 统一 cleanup() 函数接管所有退出路径
  * 解决之前 e2e 留 residue 把 data/config.json 改成 mock 地址的问题(导致本机登录失败)
- php -l 全文件通过;node --check 内嵌 JS 通过;e2e test-config-save 27/27 + test-draw-alloc 29/29 全过
2026-09-09 14:04:59 +08:00
nxxy335 8686eb99c7 feat: 接入PostgreSQL/MariaDB + 首次安装向导 + 登录页独立 + 设置重构 + 文件夹识别兜底
- 数据库层 lib/Db.php:统一 PDO 支持 pgsql/mysql/sqlite,连接参数存 data/db.json
  (git-ignored),migrate 建 ny_settings/ny_roster 表(三方言兼容 upsert)
- 配置/名单读写:bootstrap ny_load_config/ny_save_config 与 Roster 均改为
  '数据库可用则走 DB、否则回退 data/config.json/roster.json' 的文件降级模式
- 首次安装向导 install.php:选择存储后端(文件/PostgreSQL/MariaDB),测试连接+
  建表+写入卷王连接信息与默认满分;支持 ?force=1 重新配置
- 登录门分离:新增 login.php 独立登录页(仅需密码,账号预填)+logout.php+
  会话(ny_session_start/ny_is_auth/ny_login/ny_logout);index/dashboard 未安装
  跳 install、未登录跳 login;api.php 加会话保护与 auth_check
- 设置重构:删除卷王系统地址/接口前缀/登录账号/登录密码(连接信息由安装向导
  配置);新增布局配置(默认/成绩明细与缺考名单同行)与各试卷独立满分列表(默认
  分数作用于全部);工具栏布局下拉移除
- 文件夹识别兜底:app.js/dashboard.php 双策略 — mode 含 folder 优先,缺省时用
  parent_id 引用反推(应对部分版本无 mode 字段);空文件夹自动隐藏

验证:php -l 全部通过、node --check app.js 通过;SQLite 等价实测 Db 层
(migrate/config upsert/Roster 读写/ny_load_config)与会话辅助函数全部PASS;
e2e(内置服务器)验证路由:未装→install、装完→login、index/dashboard 未登录
跳转、api 401 保护、?force=1 重配、logout 302 均正确。本机无真实 pgsql/mysql
服务器,DB 连接按部署环境实测。
2026-09-09 07:15:56 +08:00
nxxy335 64d0116c0e feat: 文件夹双策略识别 + 布局选项 + 移除提交率卡片 + localStorage缓存层
- 文件夹识别:mode 含 'folder' 优先,缺省时用 parentId 引用反推;
  即使卷王版本无 mode 字段也能正确分组,避免'直接没有文件夹选项'
- 布局选项:工具栏新增 #layoutSel(默认 / 成绩明细与缺考名单同行),
  用 CSS Grid 双模式渲染,窄屏回落单列;偏好写 localStorage
- 移除统计卡片中的提交率卡片(卡片数 9 → 8)
- localStorage 缓存层:问卷列表 / 每个项目数据 / 看板数据 / 班级组别
  筛选 / 布局 / 上次项目 id 全部持久化;进入页面先渲染缓存,
  后台再异步拉新数据并写回缓存;网络失败时仍能用本地缓存查看
- dashboard.php 同步:文件夹识别 / 数据缓存一致更新
2026-09-09 00:08:08 +08:00
nxxy335 7345f9c29b fix: 文件夹试卷可见(用optgroup) + 班级后组别筛选 + 看板全屏锁屏不溢出 2026-09-08 23:31:47 +08:00
nxxy335 8e3ea32567 feat: 看板每组显示考试成员榜(排名/姓名/成绩,成绩降序) + 防溢出 2026-09-08 20:02:34 +08:00
nxxy335 1b7683baab feat: 看板支持A/B组对比下钻 + 全屏 + 删除返回按钮 + 全系统组别(A/B)支持 2026-09-08 19:44:45 +08:00
nxxy335 e111151504 feat: 登录态持久化 + 禁用按钮灰色 + 登录提示精简 + 全局美化 + 独立数据看板页 2026-09-08 16:30:19 +08:00