Files
ny-exam-platform/docs/guide/export-print.md
T
nxxy335 e87986fcc6 多考试块表:每场考试改为单列并修正第几次列合并
- ny_assemble_block_sheet() 中每场考试由 2 列改为 1 列,删除历史遗留的
  「是否最佳」空白列,消除每场右侧多余空白窄列
- 删除「第几次」列的纵向合并,改为逐行显示 1/2/3…,不再被吞成 1
- 合格次数行同步改为每场 1 列;未考合并加 $end > $start 跨行守卫,
  规避单行学生产生的非法 1x1 合并(Excel 修复提示)
- 列宽按考试名估算(CJK 记 2 半角宽),clamp 8~18
- 同步更新 test-multi-export.php(95 项含 4 条防回归断言)、
  make-samples.php(李四数学改为真正整场未考)、导出/接口/测试文档
  及样例预览文件;全量回归 404 项 0 失败
2026-10-06 12:22:16 +08:00

198 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 导出与打印
工具栏下方只有一个 **导出** 按钮,所有导出与打印的选项都收在这个弹窗里,不再散落多个小按钮。
选项在你打开弹窗前就配好了会**自动记住**(存本地),下次打开就是上次的设置。
> 生成的是 `.xlsx`:零依赖,仓库内自行实现 XLSX 写入,没有 `zip` 扩展也能用。
## 导出弹窗
| 分区 | 内容 |
| --- | --- |
| **导出内容** | 成绩明细 / 每次考试 / 缺考名单 / 多考试对比(四选一,选中后下方分区跟着变) |
| **统计范围** | 班级 / 组别多选(不选 = 全部,按住 `Ctrl` / `⌘` 多选) |
| **成绩口径与列** | 每人成绩(最新 / 最高)、排序、出表方式、按班级分工作表、附缺考名单 |
| **多考试对比** | 表格形态、对比成绩、勾选要对比的考试 |
| **附加内容** | 附缺考名单(成绩明细 / 每次考试 / 多考试对比 都出工作表) |
| **标记与合格次数** | 合格次数阈值、最高分标红、输出「合格次数」行 |
| **打印设置** | 纸张方向、打印标题 |
- 分区随「导出内容」显隐:选 **缺考名单** 时只保留范围与打印设置;
选 **多考试对比** 时才出现考试勾选表;
- **附加内容** 与 **标记与合格次数** 是三类成绩表共用的,不随导出内容消失;
「按班级分工作表」只有成绩明细支持(置灰不可勾选);
- 弹窗底部有**一句话汇总**(导什么、哪些考试、多大范围),导错了一眼能看出来;
- 打印走隐藏 iframe 调起,**不会被浏览器弹窗拦截挡掉**,也不会多开标签页。
### 成绩明细:每人最新成绩 / 最高成绩
**默认按每人最新成绩显示**(最近一次真实提交),可选 **最高成绩**(该生在这些考试里的最高分)。
| 选项 | 效果 |
| --- | --- |
| 每人成绩 | `最新成绩`(默认)/ `最高成绩` —— 每人一行时的代表成绩 |
| 排序 | `按提交时间` / `按学号` / `按成绩降序` / `按成绩升序` |
| 出表方式 | `每人一行(汇总)`(默认)/ `每次考试一行(明细)`:明细模式保留每位学生的历史提交 |
| 附缺考名单 | 默认开;只导成绩时取消勾选(接口 `noAbsent=1`)。**每次考试 / 多考试对比 也支持**,口径为「名单里有、所选考试全都没交」 |
| 按班级分工作表 | 一个班级一个工作表(xlsx 多工作表) |
默认列:`学号 / 姓名 / 班级 / 组别 / 成绩 / 最高分 / 考试次数 / 最近提交时间 / 考试时长 / 考试时长(秒)`。
## 「每次考试」块表(多场对比,主推格式)
勾选多场考试,选 **两行表头** 形态。**一列「第几次」被所有考试共用**(逐行写 1、2、3…,不合并),
每场考试占 **1 列**,学生块末尾多一行 **合格次数**。
| 学号 | 姓名 | 第几次 | 第一次月考·数学 | 第一次月考·英语 |
| --- | --- | --- | --- | --- |
| | | | 成绩 | 成绩 |
| (合并) | (合并) | 1 | 112 | 130 |
| (合并) | (合并) | 2 | 100 | 129 |
| (合并) | (合并) | 3 | **136** | **133** |
| (合并) | (合并) | **合格次数** | 3 | 3 |
| 356002 | 李四 | 1 | (合并)**未考** | 128 |
| (合并) | (合并) | **合格次数** | | 1 |
规则:
- **学生块高度** = 该生在所有勾选考试里最多的那次次数 + 1(末行是合格次数);
- **学号 / 姓名**按学生块纵向合并;**第几次**逐行写各自的次序号(1、2、3…),**不合并**
——合并会把 2、3 吞掉只剩 1;
- **每场考试占 1 列**:第 1 行是考试名、第 2 行是 `成绩` 小标题;
- **没考的考试**(整场没有该生记录)→ 该列 × 学生块高合并成一个 **`未考`** 格子;
- **排序**:学生按学号升序(出现班级 / 组别列时按 班级 → 组别 → 学号);
- **暂存草稿**:默认不导出、也不占「第几次」编号。
### 单场考试的宽表版式
只导出 **一场** 考试时,改用更清楚的宽表:每人一行,第几次直接做列名。
| 学号 | 姓名 | 第1次 | 第2次 | 第3次 | 合格次数 |
| --- | --- | --- | --- | --- | --- |
| 356001 | 张三 | 112 | 100 | **136** | 3 |
| 356002 | 李四 | **128** | | | 1 |
导出与打印用的是同一套判定,所以单场和「多场块表」的标记完全一样。
## 红 / 绿标记
| 标记 | 条件 | 效果 |
| --- | --- | --- |
| **最高分标红** | 该场该生成绩 = 该场最高分 | 红字加粗(同分只标最早达成的那次) |
| **合格次数标绿** | 该场该生达到合格线的次数 ≥ 阈值 | 绿底加粗 |
| 合格次数未达标 | 未达阈值 | **仍显示实际次数**(含 0 次),只不标绿 |
- 合格线 = 该场**本卷满分 × 合格率**(每个试卷各自的配置,不是全场统一一条线);
- 阈值在弹窗里可改(默认 1 次);关掉「最高分标红」或「合格次数行」就只出表不标记;
- 整场没考的不参与合格次数判定,直接是 `未考`;
- 学号等长数字列统一按**文本格式**写入,不会被 Excel 转成科学计数法,也不会被当公式执行;
- 打印与导出的 Excel 用同一份服务端装配结果(红字 / 绿底也来自同一份 `cell_styles`),
不存在「导出一个样、打印另一个样」。
## 班级 / 组别列什么时候出现
| 选择情况 | 班级列 | 组别列 |
| --- | --- | --- |
| 只选 1 个班级、未选组别 | 不出 | 不出 |
| 选 ≥2 个班级 | **出** | 未选组别时按数据判断(数据跨多组才出) |
| 只选 1 个班级但选 ≥2 个组别 | 不出 | **出** |
| 什么都不选(= 全部) | 数据跨 ≥2 个班级才出 | 数据跨 ≥2 个组别才出 |
- 出列时**连续相同班级 / 组别的单元格会纵向合并**(顺序按 班级 → 组别 → 学号),一个班只占一格;
- 也可以用接口参数 `withCls=1` / `withGrp=1` 强制出列(`=0` 强制不出)。
### 其它形态
| 表格形态 | 产出 |
| --- | --- |
| 两行表头(每次考试 + 合格次数) | 上面那套块表(**默认**) |
| 成绩对比(每人一行) | 一行一个学生、一列一场考试,成绩取「最新」或「最高」 |
| 两者都出 | 第一张块表 + 第二张成绩对比表 |
### 文件名
```
成绩明细_<试卷名>[_<班级>][_<组别>][_最新成绩|_最高成绩]_<YYYYMMDD_HHMMSS>.xlsx
每次考试_<试卷名>[_<班级>][_<组别>]_<YYYYMMDD_HHMMSS>.xlsx
缺考名单_<试卷名>[_<班级>][_<组别>]_<YYYYMMDD_HHMMSS>.xlsx
多考试成绩对比_<N>场[_<班级…>][_<组别…>]_<最新成绩|最高分>_<YYYYMMDD_HHMMSS>.xlsx
每次考试与成绩对比_<N>场[_<班级…>][_<组别…>]_<YYYYMMDD_HHMMSS>.xlsx # 两者都出
```
### 跟随筛选
导出**完全跟随页面当前的班级 / 组别筛选**,缺考口径也与页面一致
(筛选后名单 − 筛选后已提交,按学号匹配),不会出现「页面上 30 人、导出 50 人」的情况。
## 打印
| 选项 | 说明 |
| --- | --- |
| 打印标题 | 默认按考试名自动生成,可改成学校 / 考试正式名称 |
| 纸张方向 | 多考试打印默认 **横向**(列多不折行),可改纵向 |
| 成绩明细的分组对比 | 按班级或组别分段,段首给出小计(人数 / 平均 / 最高 / 最低 / 合格人数与合格率) |
| 成绩明细的显示选项 | 显示名次、显示「是否合格」列、显示考试次数、附缺考名单 |
- 表头在**每一页自动重复**,行不会被切断到两页;两行表头都放进 `<thead>`;
- 页眉含范围(班级 / 组别)、打印时间、人数、平均分、最高 / 最低、合格人数与合格线;
- 页脚含**教师 / 教研组长签字栏与日期**,可直接签字归档。
成绩明细面板右上角的**打印**与导出弹窗里的打印设置是同一套(打印标题、方向、分组对比都在弹窗里改)。
::: tip 绿底 / 红字要能印出来
打印样式里写了 `print-color-adjust: exact`,**不需要**在打印对话框里手动勾选「背景图形」,
Chrome / Edge 会照常输出绿底与红字。若仍看不到底纹,检查是否换用了不支持该属性的浏览器。
:::
::: tip 缺考名单单独成页
勾选「附缺考名单」后,缺考名单工作表会在打印时**另起一页**(不会挤在成绩表底部),便于分开归档。
:::
## 直接调用导出接口
需要对接其它系统时,可以带参数直接 GET(需已登录):
```
# 成绩明细(每人一行最新 / 最高)
api.php?action=export&projectId=xxx&cls=1班,2班&grp=A组&mode=latest&scoreMode=best&sort=score_desc
# 「每次考试」块表(多场)
api.php?action=export_multi&projectIds=a,b&mode=block&passTimes=1
# 单场宽表版式(每人一行,第几次做列名)
api.php?action=export_multi&projectIds=a&mode=block&wide=1
# 多考试:成绩对比 / 两者都出
api.php?action=export_multi&projectIds=a,b&mode=summary&scoreMode=highest
api.php?action=export_multi&projectIds=a,b&mode=both
# 「多考试打印」用的数据(不下载文件,返回 rows / merges / header_rows / cell_styles)
api.php?action=export_multi&projectIds=a,b&mode=block&format=json
# 缺考名单
api.php?action=export_absent&projectId=xxx
```
| 参数 | 取值 | 说明 |
| --- | --- | --- |
| `projectId` | 试卷 ID | 必填(`export_multi` 用 `projectIds`,逗号分隔,可只传一场) |
| `cls` / `grp` | 班级 / 组别 | 与页面筛选一致;`export_multi` 支持**多选**(逗号分隔),空 = 全部 |
| `mode` | `latest` / `all` | `export` 的成绩明细:每人一行(默认,取最新)/ 每次考试一行 |
| `scoreMode` | `latest` / `best` | 每人一行的代表成绩:最新(默认)/ 最高 |
| `mode`(`export_multi`) | `block` / `summary` / `both` | 每次考试块表(默认)/ 成绩对比 / 两者都出;旧值仍兼容:`matrix`→`summary`、`merged`·`attempts`→`block` |
| `wide` | `1` | 单场 `block` 时改用宽表版式(每人一行、第几次做列名) |
| `passTimes` | 正整数 | 合格次数达标阈值(默认 1) |
| `markBest` | `0` | `=0` 取消「最高分标红」 |
| `passRow` | `0` | `=0` 不输出「合格次数」行(也就没有绿底) |
| `format` | `json` | 不下载文件,返回 `sheets`(`rows` / `merges` / `header_rows` / `cell_styles`)供打印渲染 |
| `withCls` / `withGrp` | `1` / `0` | 强制出 / 不出班级、组别列;缺省按上表规则自动判断 |
| `noAbsent` | `1` | 成绩明细不附缺考名单 |
| `skipTemp` | `1` | `mode=all` 时排除暂存草稿 |
| `includeTemp` | `1` | 每次考试块表保留暂存草稿(标注为 `第N次(草稿)`) |
| `sort` | `time` / `sid` / `score_desc` / `score_asc` | 成绩明细排序(块表恒按 班级 → 组别 → 学号 / 学号) |
| `split` | `cls` | 按班级拆分多工作表 |
| `cols` | 逗号分隔列名 | 从默认列中挑选(仅 `export`) |
`format=json` 返回的 `cell_styles` 是 `行,列 → red | green`,行与列都是 **1-based**;
前端据此加红字 / 绿底,合并区间内的非首格继承左上角首格的标记。
## 相关
- [成绩明细与统计口径](./scores.md)
- [接口一览](../dev/api.md)