导出-每次考试/多考试对比: - 合格次数阈值等标记项抽为常显区块,三类成绩表共用 - 附缺考名单扩展到多考试导出(取所选考试全都没交的人) - 块表每场固定2列(成绩+合并空位),修复漏推空串导致的整行左移/串列 - 列宽按实际内容估算,合并区空位只给最小宽度 - 合格次数始终显示实际次数(含0),仅 ≥ 阈值标绿底 - 打印标题改为「成绩明细」,缺考名单分页单独成页 - 打印样式加 print-color-adjust:exact,绿底/红字可正常印出 数据大屏: - ?class= 班级大屏免登录公开访问,接口侧放行并按班级裁剪后回传 - 单班大屏移除退出按钮,仅首页保留 - 修复红绿判定真bug:membersTable 误用 window.DASH(DASH为let声明取不到) 改为 scoreGrade() 直接读自由变量,按后台合格率正确判红/黄/绿 测试/样例/文档同步更新,全量回归通过。
7.9 KiB
接口一览
所有接口统一走 api.php?action=<name>,响应为 JSON:
// 成功
{ "ok": true, "data": { ... } }
// 失败
{ "ok": false, "error": "错误信息" }
- 未登录时返回
{ "ok": false, "error": "...", "auth": false },前端会清会话并跳登录页; - 写操作必须在 JSON 体里带
_csrf字段(或X-CSRF-Token头), 令牌从config_get接口获取。
写操作白名单
config_save、roster_save、roster_import、roster_clear、
unmatched_map、project_config_save
接口清单
认证与配置
| Action | 方法 | 参数 | 说明 |
|---|---|---|---|
auth_check |
GET | — | 是否已登录 |
config_get |
GET | — | 返回配置(密码不回显)+ csrf_token + storage / db_driver / db_connected |
config_save |
POST | JSON 配置对象 | 保存;返回 conn_changed 与 ping 结果 |
config_test |
POST | 连接信息 | 测试连接,不落库 |
ping |
GET | — | 探测卷王是否可达 |
diag |
GET | — | 诊断信息(存储后端、DB 状态等) |
只有连接信息(地址 / 前缀 / 账号 / 密码 / 证书校验)变化时,
config_save才会清除令牌并重新登录。
数据
| Action | 方法 | 参数 | 说明 |
|---|---|---|---|
projects |
GET | — | 卷王项目(问卷 / 考试)列表 |
data |
GET | projectId、nameQid、force |
主系统数据:统计、成绩行、名单、缺考、未匹配 |
dashboard |
GET | projectId、nameQid、force、class |
按班级 / 组别聚合的看板数据。class 非空且未登录时,服务端只回这一个班(并标记 public) |
data 响应关键字段:
stats { total, submitted, absent, rate, avg, pass_rate, max, min, avg_duration }
rows[] { sid, name, cls, grp, score, best_score, attempts, attempts_list[],
submit_ts, submit_at, last_submit_ts, duration, duration_txt }
roster[] { sid, name, cls, grp, login }
absent[] 缺考名单
unmatched[] 未匹配答卷
project_cfg 本卷配置
_cache { cached, at }
名单
| Action | 方法 | 参数 | 说明 |
|---|---|---|---|
roster |
GET | — | 名单与元信息 |
roster_import |
POST | multipart:file、mode=replace|append |
导入(扩展名白名单 + 随机名 + 解析后即删) |
roster_save |
POST | JSON { students: [...] } |
全量替换(DELETE + INSERT) |
roster_clear |
POST | — | 清空 |
unmatched_map |
POST | JSON { name, sid, cls, grp, login } |
未匹配答卷补入名单(学号已存在则更新姓名) |
每卷配置
| Action | 方法 | 参数 | 说明 |
|---|---|---|---|
project_config |
GET | projectId |
返回 cfg、effective(生效值)、global(全局默认) |
project_config_save |
POST | JSON { full_score, pass_rate, exempt[] } |
留空/0 = 清除该项,回落全局 |
导出
| Action | 方法 | 参数 | 说明 |
|---|---|---|---|
export |
GET | projectId、nameQid、cls、grp、mode=latest|all、scoreMode=latest|best、split=cls、sort、cols、skipTemp=1、noAbsent=1 |
成绩明细 .xlsx(all 时每人每次考试一行) |
export_absent |
GET | projectId、nameQid、cls、grp |
缺考名单 .xlsx |
export_multi |
GET | projectIds(逗号分隔)、nameQid、mode=block|summary|both、wide=1、passTimes、markBest=0、passRow=0、scoreMode=latest|highest、cls / grp(可多值)、withCls / withGrp、includeTemp、format=json |
「每次考试」块表 / 宽表 / 成绩对比 .xlsx(format=json 时返回装配结果供前端打印) |
draw_export |
GET | type=import|list |
白名单导入模板 / 白名单列表 |
sort 取值:time / sid / score_desc / score_asc。
导出新参数
| 参数 | 适用接口 | 取值 | 说明 |
|---|---|---|---|
mode=all |
export |
缺省 | 每次考试一行(同一学号考 N 次就有 N 行);mode=latest 为每人一行最新成绩 |
scoreMode=best |
export |
缺省 | 每人一行的代表成绩改取最高成绩(默认 latest = 最近一次真实提交);mode=all 不生效 |
noAbsent=1 |
export、export_multi |
缺省 | 不附缺考名单工作表(export_multi 口径:所选考试全都没交的人) |
skipTemp=1 |
export |
缺省 | mode=all 时排除暂存草稿;includeTemp=1 可强制保留 |
mode=block |
export_multi |
默认 | 两行表头的「每次考试」块表:一列「第几次」被所有考试共用,每场考试占 2 列(考试名横跨 2 列 + 第二行「成绩」小标题),学生块末尾一行「合格次数」,学号·姓名纵向合并,没考的考试合并成「未考」(旧值 merged / attempts 等价) |
wide=1 |
export_multi |
— | 只传 1 个 projectId 时改用宽表:每人一行、第几次直接做列名(学号 / 姓名 / 第1次…第N次 / [合格次数]),由 ny_assemble_attempt_wide_sheet() 生成 |
passTimes |
export_multi |
1 | 「合格次数」达标阈值:该场达到合格线的次数 ≥ 此值才标绿(默认 1) |
markBest=0 |
export_multi |
开 | =0 取消「最高分标红」 |
passRow=0 |
export_multi |
开 | =0 不输出「合格次数」行(也就没有绿底) |
mode=summary |
export_multi |
— | 成绩对比:一行一个学生、一列一场考试(旧值 matrix 等价) |
mode=both |
export_multi |
— | 第一张块表 + 第二张成绩对比表 |
format=json |
export_multi |
— | 不下载文件,ny_ok 返回 sheets(rows / merges / widths / header_rows / cell_styles)+ meta(考试名、场次、班级 / 组别、列开关、pass_line / pass_times / mark_best / pass_row / wide),供「多考试打印」渲染 |
cls / grp |
export_multi |
新增 | 支持多选(逗号分隔),空 = 全部;与单考试 export 的筛选口径一致 |
withCls / withGrp |
export_multi |
新增 | 强制出 / 不出班级、组别列;缺省由 ny_multi_col_flags() 判定:选 ≥2 个班级(组别)才出,只选 1 个班级且未选组别两列都不出 |
includeTemp=1 |
export_multi |
缺省 | 不传 = 块表默认跳过暂存草稿(保留时标注 第N次(草稿)) |
块表由 ny_assemble_block_sheet() 生成,返回 rows(两行表头 + 学生块)/ merges / widths / header_rows=2
/cell_styles;宽表由 ny_assemble_attempt_wide_sheet() 生成,参数同上。
合格线按每场各自的 ny_resolve_score_cfg($pid, $c) 解析(本卷满分 × 合格率),不是全场统一一条线。
cell_styles 是 行,列 → red | green(行与列均 1-based),只有两类标记:
red—— 该场该生成绩 = 该场最高分(同分只标最早达成的那次);green—— 该场该生成绩达到合格线的次数 ≥passTimes(未达标不标记)。
XlsxWriter::renderMulti() 支持 merges(1-based 闭区间 [r1,c1,r2,c2])、header_rows(多行表头同样式)
与 cell_styles(单元格级红字 / 绿底);被合并的非首格写成「空值 + 同样式」以保证合并区域四周边框完整,
并跟随左上角首格的标记,避免绿底只覆盖半个格子。
前端 sheetToHtml() 把同一份 merges 换算成 colspan / rowspan、把 cell_styles 换算成红字 / 绿底,
所以打印与导出的 Excel 用同一套判定。
列显示规则 ny_multi_col_flags() 与排序比较函数同样放在 lib/multi_export.php(纯函数,可单测)。
前端调用约定
// 读
const d = await api('data', { params: { projectId, force: 1 } })
// 写:api() 会自动注入 _csrf
await api('roster_save', { json: { students } })