像搭积木一样,制作考试答题卡。
Build exam answer sheets, block by block.
答题卡制作器是一个纯前端的可视化工具,用来快速制作考试答题卡并打印 / 导出 PDF。 无需后端、无需注册、数据不出本机;所有逻辑都在浏览器里完成,打包成 Docker 镜像即可在内网部署。
在线试用:打开
app.html(或在 Docker 部署后访问站点根路径)即可开始制作。
| 能力 | 说明 |
|---|---|
| 🧩 模块化题型 | 选择题 / 填空题 / 解答题 / 考生信息栏 / 自定义编辑区,均为独立模块,可添加、复制、删除、拖拽排序。 |
| 📄 固定纸张分页 | 纸张尺寸固定不随内容变化;内容按「第一面 → 第二面 → 第二页第一面……」顺序填充。A3 每面双栏(左右各一张 A4 规格)。 |
| 🔤 填涂 / 手写 | 选择题支持中括号 [A] 填涂与横线手写;选项数量任意(超过 26 个用 AA/AB… 续排)。列数在渲染时实测单题真实宽度后决定,每题完整排在一行内、排不下的整题换行,绝不溢出。行间距(mm)可调,且标题到第一行、行与行的间距严格一致。 |
| 🎯 四角定位点 | 支持方块 / 三角两种样式,边长可调(2–10mm)。每一面只要有题目内容就自动补四角定位点,空白面不加;页边距会按定位点尺寸自动预留,绝不覆盖任何内容;打印时强制输出底色,供阅卷机定位。 |
| ✎ 多级填空 | 支持 11(1)、11(2)①/② 这类多级小题,编号按层级自动区分(小题 (1)(2) → 小小题 ①② → 更深 a) b));一行没用完就继续放下一题,排满才换行;每个空长度与行间距可调,大题之间与折行后的行距严格一致。 |
| 📐 解答题横线 / 插图 | 作答区可选「空白」或「横线」样式,横线间距与作答区高度可调 —— 高度也可以直接在预览区拖动作答区下边缘实时调整(40–400mm,松手即记一个撤销点);每题可插一张图片:选中模块后把鼠标停在预览区某作答区上按 Ctrl+V 即贴进该题,图片叠加在作答区内(不占高度、不挤压横线),九宫格定位 + 宽度可调,超出部分自动裁切(本地压缩,仅存浏览器)。 |
| 📝 主观题人工阅卷 | 选择题 / 填空题 / 解答题都能勾「人工阅卷」:填满分,可选再拆小问(各自给分)。导出模板时带上整题一块的作答区坐标;扫描端据此裁出作答区图,阅卷工作台里对着作答区给分,成绩单自动多出「客观分 / 主观分 / 总分」三列。两种给分模式(整题给一个分 / 按小问逐项给)由模板决定,分值夹到各自满分。纯选择题卷子的行为一个字没变。 |
| 🪪 考生信息栏 | 可增删「班级 / 姓名 / 考号」等手写栏;考号填涂区为中括号方块、框内直接显示数字,位数上限 20。填涂区宽度默认不超过页面的 1/4(可调 10–60%),上限内把方框撑到 3–5mm(可填涂、可读),余量交给右侧手写栏拉满;只有连 3mm 都放不下时才自动放宽,永不超出纸张。 |
| ⌨️ 快捷键 | Ctrl+C / Ctrl+X / Ctrl+V 复制、剪切、粘贴模块(选中「图片」模块时可直接粘贴剪贴板图片);Ctrl+Z / Ctrl+Y 撤销、重做(快照式,最多 100 步)。 |
| 🖼 图片插图 | 可插入图片并调整宽度(%)与对齐(左/中/右);上传时在本机自动压缩,仅存浏览器缓存、不上传服务器,打印 / 导出 PDF 时正常显示。 |
| ⬛ 统一黑色边框 | 每个题型区块都带黑色边框,结构清晰、打印友好。 |
| 🎛 通用样式 | 每个模块可单独设置字号与对齐(左/中/右)。 |
| 💾 模板复用 | 本地保存(localStorage)、载入;支持导出 / 导入 JSON 模板。 |
| 🖨 打印 / 导出 PDF | 浏览器直接打印或另存 PDF;支持双面长边翻转。 |
┌───────────────┬─────────────────────────────┬───────────────┐
│ ① 添加题型 │ 预览(固定分页) │ ③ 属性设置 │
│ 选择题/填空/… │ ┌───────────────────┐ │ 题数 / 样式 / │
├───────────────┤ │ A3 第一面(双栏) │ │ 空格 / 字号 / │
│ ② 答题卡结构 │ ├───────────────────┤ │ 对齐 / 间距 … │
│ (拖拽排序) │ │ A3 第二面 │ │ │
└───────────────┴─────────────────────────────┴───────────────┘
到 Releases 下载两个 zip,解压、双击 exe 即可, 不需要装 Python、不需要 Docker、不需要联网:
| 下载 | 双击这个 | 说明 |
|---|---|---|
...-cardmaker-windows-x64.zip(约 9 MB) |
答题卡制作器.exe |
制卡端。会自动起一个只监听 127.0.0.1 的本地服务并打开浏览器(页面是 ES Module,file:// 下打不开) |
...-scanner-windows-x64.zip(约 20 MB) |
答题卡扫描服务.exe |
扫描端。默认 http://127.0.0.1:8081,首次打开会让你创建管理员账号 |
扫描端这个包已经瘦身:手写 A-D 的 CNN 改走 ONNX Runtime 推理(不再带 290 MB 的 CPU 版 PyTorch,
torch_cpu.dll没了),zip 从 ~210 MB 降到 ~20 MB。真实手写净准确率 75.5% → 99.2%, 精度零退化(ONNX 与 torch 在 249 个真实字形上逐一比对、最大偏差 < 1e-5)。只做填涂识别、不需要 手写的话,加--no-cnn启动能省一点内存(识别自动退回纯 OpenCV)。
答题卡制作器.exe --port 8899 :: 指定端口(默认自动挑一个空闲端口)
答题卡扫描服务.exe --port 8081 --data D:\asb :: 数据和校对图放哪儿
答题卡扫描服务.exe --no-cnn :: 关掉手写 CNN,省内存(纯 OpenCV 照常跑)默认数据目录 %LOCALAPPDATA%\asb-scanner\data(拷走整个文件夹就是完整备份)。
每个 zip 里都有一份 使用说明.txt。杀毒软件/首次运行若拦截,放行即可 —— PyInstaller 单文件包被误报是常态。
# 1) 构建镜像
docker build -t answer-sheet-builder .
# 2) 运行
docker run -d --name asb -p 8080:80 answer-sheet-builder
# 3) 浏览器打开
# http://<服务器IP>:8080或使用 compose:
docker compose up -d项目使用 ES Module,必须通过 HTTP 访问(不能直接双击打开 HTML):
cd answer-sheet-builder
python3 -m http.server 8080
# 浏览器打开 http://localhost:8080/app.htmlCI 会在每次推送 main 或打 v* 标签时自动构建并推送镜像到 GitHub Container Registry:
docker pull ghcr.io/dc1024/answer-sheet-builder:latest
docker run -d --name asb -p 8080:80 ghcr.io/dc1024/answer-sheet-builder:latest
# 打开 http://<服务器IP>:8080镜像名全小写:
ghcr.io/dc1024/answer-sheet-builder。标签包含latest、分支名、v1.2.3/1.2、以及sha-xxxxxxx。 若拉取提示无权限,请在仓库 Packages 里把该包可见性设为 Public(首次推送后生效)。
- 在左侧「① 添加题型」点击需要的题型,会自动加入结构列表。
- 在「② 答题卡结构」拖动
⠿手柄排序,或点 ↑ / ↓ 调整;点 ⧉ 复制、✕ 删除。 - 选中模块,在右侧「③ 属性设置」配置题数、样式、空格、高度、字号、对齐等。
- 顶栏选择纸张(A3 / A4)与方向(竖版 / 横版),中间预览按固定尺寸逐面显示。
- 点「🖨 打印 / 导出 PDF」,在打印对话框选择对应纸张、勾选「双面(长边翻转)」,另存为 PDF 或直接打印。
排好卷子只是上半场 —— 学生作答、收卷之后,🎯 阅卷模板 能把这份卷子变成机器可读的坐标文件,
交给本仓库自带的 scanner/ 服务自动识读选择题并出班级统计。
制卡端排卷 ──▶ 点「🎯 阅卷模板」 ──▶ asb-omr-template-*.json ──▶ scanner 服务
│
扫描 / 拍照 ────────────────────────┘
│
每题答案 + 存疑标注 + 班级统计 + CSV
导出的模板里含每个填涂圈的毫米坐标、四角定位点、纸张尺寸与题号选项;考生信息栏开启了
考号填涂区时,连同考号每一位的格子坐标一起导出(模板格式 asb-omr/2)。扫描服务据此做
透视矫正后逐圈采样,纯 OpenCV 实现、不下载模型、可离线运行。实测(6 份合成卷 × 20 题):
| 场景 | 答案 | 卷面考号 |
|---|---|---|
| 干净扫描(300dpi 级) | 100% | 100% |
| 手机拍照模拟(透视 + 明暗 + 模糊 + 噪声 + JPEG) | 100% | 100% |
未涂题自动标 blank、浅涂题标 faint、一题涂两个标 multi。
批量模式:一次丢一个班 —— 拖入 zip / 整个文件夹 / 一堆散图,自动归组、多页自动合并;
再传一份 考号,姓名,班级 的名单,姓名班级自动贴上,考号对不上、页序异常、名单里没交卷的
都会列进待人工确认队列。成绩 CSV 带考号/姓名/班级列。
卷子归谁名下,优先看卷面:学生涂的考号就在答题卡上,识别出来直接用来归组 ——
不需要给每个学生发二维码,也不需要把 50 个文件改名成考号。文件名里写了考号(2026010234_1.png
或每个学生一个目录)会用来互相印证;两者不一致时不会擅自改动,而是把两边都报出来让人确认。
名单还能把只差一位的考号认出来(疑似涂错一位)、把卷面漏涂的那一位补全。
前提:卷子里要有填涂圈模式的选择题,或开启考号填涂区(两者至少其一); 全是手写作答模式时会提示「没有可识别的填涂圈」。
主观题(解答题 / 填空题)人工阅卷:这套工具不只判选择题。在制卡端给任意一道题勾上
「人工阅卷」、填好满分(可再拆小问,每个小问各自给分),导出模板时这道题就会带上
整题一块的作答区坐标。扫描识别时按这个坐标从校正后的干净图上裁出作答区,阅卷工作台里
右边默认显示的就是这道题的作答区(够大、看得清字),一键可切回整页校对;每道主观题一张
打分卡片,填完分数弹窗底部立刻重算 客观 19 + 主观 13 = 32 / 35。
给分方式由模板决定:设了小问就按小问逐项给分再求和,没设就是整题给一个分;每一项都会
夹到自己的满分,手滑把 8 打成 80 不会把总分炸掉。有主观题的考试,成绩单和导出的 CSV 自动从
两列变成「客观分 / 主观分 / 总分」三列(满分写在 X-Score-Max 头里);纯选择题的考试仍然
是原来的两列,老师的手感和已有脚本都不受影响。
设置与检查更新:制卡端工具栏的 ⚙ 设置、扫描端「设置 → ① 设置」卡片里,可以开关自动检查更新、
手动点一下「检查更新」、看当前版本号。学校内网连不上 GitHub 是常态,所以这种时候只会显示
「暂时查不到」并说明原因 —— 不算错误、不弹红。换源 / 内网镜像 / fork 出去自己发版,
用 ASB_UPDATE_API 指一下即可。
answer-sheet-builder/
├── index.html # 宣传页(GitHub Pages 首页)
├── app.html # 制作器入口
├── Dockerfile # nginx 托管静态资源
├── docker-compose.yml # 一键部署
├── assets/
│ ├── css/style.css # 全部样式(含打印 @media print)
│ └── js/
│ ├── app.js # 主程序:面板 / 结构 / 属性 / 预览 / 打印装配
│ └── core/
│ ├── util.js # esc / uid / 深拷贝 / 通用样式
│ ├── store.js # 全局状态(blocks 数组、增删改移、持久化)
│ ├── registry.js # 题型注册表(聚合模块,便于扩展)
│ ├── preview.js # 固定纸张分页引擎
│ ├── uistate.js # 纯运行期 UI 状态(当前作答区等,不写入模板)
│ └── omr.js # 导出阅卷模板(每面定位点 + 每个填涂圈的 mm 坐标)
│ └── blocks/ # 各题型模块(每个含 defaults / configUI / render)
│ ├── info.js
│ ├── singleChoice.js
│ ├── fillBlank.js
│ ├── answer.js
│ ├── image.js
│ └── custom.js
├── scanner/ # 配套:扫描识别服务(Python + OpenCV + Flask,见 scanner/README.md)
├── LICENSE
├── CHANGELOG.md
└── README.md / README_EN.md
扩展新题型:在 assets/js/blocks/ 新建一个模块(导出 { type, name, icon, defaults, configUI, render }),再在 core/registry.js 的数组里 import 并加入即可,无需改动其他代码。
- 使用 Chrome / Edge 打印效果最佳;页边距请选「无 / None」,每页内部留白由页面卡片自身控制。
- 预览区每张卡片 = 一个「面」。A3 双面打印时勾选「双面打印 / 长边翻转」,即可按「第一面 → 第二面 → 第二页第一面……」顺序输出。
- 每个题型区块都与预览一致地带上黑色边框。
- 原生 ES Module(无框架、无构建步骤)
- CSS 多栏 +
repeating-linear-gradient(横线)+@media print - 原生 Docker + nginx:alpine 托管
MIT © 2026 DC · 完全免费开源,可自由使用、修改与分发。