WMU 综测计算器技术栈总结与开发复盘:零依赖纯前端的完整形态
温医大综测计算器的技术栈全解:零依赖纯前端如何实现双方案计算、教务成绩粘贴导入、13 学院转专业规则数据化,以及 81 个零安装单测与 GitHub Actions CI 的工程化闭环。
WMU 综测计算器技术栈总结与开发复盘:零依赖纯前端的完整形态
给温州医科大学同学做了一个「学生素质综合测评计算器」(含独立的转专业考核计算器):按照《学生素质综合测评办法》把 C1 思想品德 / C2 课程成绩 / C3 发展素质的算法、加分细则、体测降档判定全部做成网页,另配套本部 13 个学院的转专业考核办法计算。部署在 GitHub Pages,上线后同学直接打开链接就能用。
这个项目最特别的地方不是功能多,而是整个仓库没有任何依赖:没有 package.json、没有框架、没有构建步骤,双击 index.html 就能跑,测试用 Node 内置模块就能全绿。这篇把技术栈和架构取舍完整讲一遍。
文中截图均为项目真实页面,数据为虚构演示数据。计算仅供学生自评参考,测评结果以班级测评小组、系及学院认定为准。
一句话总览
一个零依赖、零构建的纯前端静态网站 + Node 内置单测 + GitHub Actions CI + GitHub Pages 部署。
| 层 | 技术 | 职责 |
|---|---|---|
| 页面载体 | HTML5 + 原生 JavaScript | index.html(综测)+ transfer.html(转专业)互链 |
| 样式 | 手写 CSS 单文件(22.9 KB) | 卡片式表单布局、明暗两套观感 |
| 计算核心 | 纯函数模块 calc.js / transfer.js |
C1/C2/C3、加权平均、排名、体测判定,不碰 DOM |
| 评分数据 | data.js 方案注册表 |
本部 / 仁济两套方案、表 1~表 7 加分细则 |
| 持久化 | localStorage + JSON 导入导出 | 主数据、档案卡、转专业输入三个 key |
| 测试 | Node 内置 assert + 自写微型 runner |
3 个测试文件共 81 个用例,零安装 |
| CI | GitHub Actions(ubuntu + Node 20) | push / PR 自动跑三个测试 |
| 部署 | GitHub Pages 分支部署 | 手动推送即上线,无自动部署 |
| 可选验证 | Playwright 渲染冒烟脚本 | 本机人工跑,不入 CI |
仓库结构一眼看完:
index.html 综测页面骨架(个人测评 / 班级排名 / 数据管理 三个 tab)
transfer.html 转专业考核计算页面(与综测互链)
css/style.css 全部样式
js/data.js 评分表数据(表1~表7、两套方案、五级换算)
js/calc.js 综测纯计算逻辑(C1/C2/C3/总分/排名/教务文本解析)
js/storage.js localStorage 持久化 + JSON 导入导出
js/app.js 综测交互层(渲染、快捷添加、学年管理、导入 UI)
js/transfer.js 转专业数据 + 纯计算(本部 13 学院 + 仁济)
js/transfer-app.js 转专业页面交互逻辑
test/*.test.js 计算层 / 存储层单测(Node 直接跑)
scripts/verify-transfer.js Playwright 渲染冒烟验证(可选)
六个 JS 文件约 3200 行:data.js(728 行)和 transfer.js(547 行)几乎全是规则数据,app.js(1022 行)是交互层,真正的计算函数加起来不到 300 行——规则是数据,逻辑是函数,这是整个项目最核心的架构决定。
先看成品
综测页首屏(1440×900 视口)——顶部是方案切换与个人信息,往下依次是学年概览、C1 思想品德、C2 课程成绩、C3 发展素质四块:

整页全貌——可以看到课程表支持百分制 / 五级制混排、课程性质标记、快捷添加按钮和底部的学年综合成绩概览:

转专业页首屏——左侧选学院与专业、录入成绩与排名,右侧实时输出综合成绩与分项明细:

转专业页整页——下方还有该学院考核办法原文卡片:

架构:三条设计主线
1. 计算层与 DOM 严格分离,一份代码双环境复用
calc.js / transfer.js 是纯函数模块:不引用 document、不碰 localStorage,通过 window.ZCCalc 导出给浏览器,同时 module.exports 导出给 Node——测试直接 require("../js/calc.js") 就能跑,不需要浏览器、不需要 mock DOM:
// calc.js 尾部:双环境导出
if (typeof module !== "undefined" && module.exports) {
module.exports = { ... };
} else if (typeof window !== "undefined") {
window.ZCCalc = { ... };
}
这是「零依赖也能有像样测试」的关键:把计算从 UI 里抠出来,测试就只需要喂数据、断言结果。教务文本解析、五级制换算、加权平均、排名这些最容易算错的逻辑全部落在纯函数里,交互层只负责把 DOM 值喂进去、把结果显示出来。
2. 方案注册表:切换校区 = 换一套数据
温医大本部与仁济学院的评分表、基准分、体测文案都不一样(本部 C3 基准 65 分,仁济 70 分)。data.js 用 SCHEMES 注册表收拢两套方案,setScheme() 一次性把 FIVE_GRADE / WEIGHTS / BASE_C1 / BASE_C3 / CAP / C1_PASS / C2_FAIL_CREDITS 等常量挂到全局:
const SCHEMES = {
benbu: { label: "温医大本部", ... }, // 本部:C3 基准 65
renji: { label: "仁济学院", ... }, // 仁济:C3 基准 70
};
function setScheme(key) { /* 把该方案的全部常量挂到全局 */ }
切换方案时,评分表、快捷添加面板、公式速览、体测提示全部跟随变化,UI 代码零改动——新增一套方案 = 在注册表里加一份数据,而不是改逻辑。
3. 脚本加载顺序契约
index.html 按 data.js → calc.js → storage.js → app.js 顺序加载,依赖全局变量链传递数据。这是零依赖方案的代价:顺序敏感、全局命名空间共享,所以交接文档里专门有一条「新增脚本别打乱顺序」。小项目里这是可接受的简单性,但它是后续接手的第一个坑位。
数据模型与持久化
三个 localStorage key 各管一件事:
| key | 内容 |
|---|---|
wmu-zongce-v1 |
主数据:{profile, scheme, years[], classMembers[]} |
wmu-zongce-archives-v1 |
个人档案卡列表(命名快照,可载入 / 同步 / 导出 / 分享) |
wmu-transfer-v1 |
转专业页的方案、学院、专业、输入值 |
主数据带 version 字段,load() 时做结构合并 + scheme 白名单校验——旧数据缺字段自动补默认值,非法方案回退本部,为将来迁移留了口子。跨设备数据同步没有后端,靠 JSON 导出 / 导入文件兜底;「个人档案卡」则让多位同学共用一台设备时能互不干扰地建档。
功能细节里的工程味道
- 教务成绩粘贴导入:教务系统复制成绩表 → 粘贴 →
parseJwText解析。表头定位 + 制表符 / 空格分隔 + 作废行跳过,补考、重修、等级制自动识别并提示计分规则(补考按 60 分、重修按最高分计入)。 - 豆包 OCR 批量导入:内置引导卡片——截图 → 豆包 OCR(一键复制的提示词)→ 把 OCR 输出的表格粘回本站。OCR 结果走同一条解析器:入口可以有多个,解析逻辑只有一份。
- 五级制与百分制混排:每门课可独立切换,优 = 90 / 良 = 80 / 中 = 70 / 及格 = 60 / 不及格 = 50,切换自动换算清空;「仅统计必修 + 限选」开关与教务口径对齐,排除任选课。
- 边界钳制:C1 加分封顶 100、减分下限钳到 0 不出现负分;空学年不计算误导性总分(显示「—」);不及格学分 ≥ 20 自动提示不合格。
- 体测降档判定:总分 ≥ 80(良好)不降档、低于 80 降一等级评定,保健班 / 保健科证明豁免,红绿徽章实时提示——依据《奖学金实施办法》写成了纯函数
peVerdict。 - 快捷添加矩阵:表 1~表 7 全部做成快选,学科竞赛、文体竞赛用「级别 × 等级」矩阵编码(省级一等奖 2 分这类),已按 20-21、23-24 学年测评会议纪要维护,也支持自由录入。
转专业计算器:把规章文件变成数据
本部按接收学院切换 13 个学院的《选拔类转专业考核实施办法》(2024-12-30 印发),每份办法被拆解成结构化数据:专业成绩排名公式(含各专业不同系数,如第一临床 0.15/0.3/0.5、护理分档制)、面试满分与合格线(精神医学面试 < 18 分不予录取)、大学英语计分表(10 种规则,覆盖四六级、眼视光雅思 / 托福、口腔 / 药学特殊分档)、专长档次、高考成绩计分(口腔公式 10 − [(最低录取分 − 高考) × 0.25])、跨专业计分(眼视光非医 → 医 0 分)。
仁济学院则是 2025 年统一方案:成绩 65% / 面试 25% / 专长 5% / 英语 5%,并判定第一轮(前 15%)、第二轮(前 50%)申请资格。
transfer.js 的 547 行里大部分是 BENBU_COLLEGES 的数据声明,计算逻辑与数据完全分离——改学院办法 = 改数据,不动计算代码。选好学院和专业,页面自动匹配该学院的公式与计分规则,实时输出综合成绩、分项明细和资格判定。
测试与 CI:零依赖也能有 81 个用例
三个测试文件直接 node test/calc.test.js 运行,全部使用 Node 内置的 assert + 十几行自写的微型 runner(t(name, fn) 收集断言计数),不需要安装任何东西:
node test/calc.test.js # 33 用例:C1/C2/C3、五级换算、教务文本解析、体测判定
node test/transfer.test.js # 25 用例:13 学院公式、英语计分、资格判定
node test/storage.test.js # 23 用例:持久化结构合并、scheme 白名单、导入校验
计算层单测的关键技巧是上面说的「双环境复用」:测试里 require("../js/calc.js") 直接调纯函数,配合 setScheme("renji") 把全局常量切到仁济方案验证——每个测试文件都覆盖了两套方案的边界(比如 C3 基准 65 / 70)。
GitHub Actions 的 CI 只有 20 行:ubuntu + Node 20,push / PR 时依次跑三个测试。没有 lint、没有构建、没有部署步骤——因为项目根本不需要构建。
复盘:零依赖的得与失
得到了什么
- 双击即用:
index.html本地打开就能跑,数据存本机浏览器;部署到 GitHub Pages 后链接即开箱。 - 零供应链风险:没有 npm 依赖就没有
node_modules泄漏、没有构建故障、没有依赖升级破坏;仓库 clone 下来就 2 MB。 - 测试零门槛:任何有 Node 的机器直接跑测试,CI 配置也极简。
- 零后端成本:数据全在 localStorage,个人使用场景完全够用。
付出了什么
- 跨设备数据靠手动:没有账号体系,只能 JSON 导出 / 导入,这是最明显的短板。
- UI 全靠手写 DOM:
app.js1022 行里大量模板字符串拼 HTML,交互复杂度再翻一倍就会吃力。 - 全局命名空间共享:脚本加载顺序敏感,新增模块要遵守既有契约。
- 规则维护靠人:加分细则、学院方案更新时要手动改
data.js/transfer.js并同步更新测试用例(README 里有「想改什么改哪里」对照表)。
什么时候该上框架?我的判断是:交互复杂度再翻一倍、需要组件复用或多端复用时,会认真考虑 Vue / React + 构建工具链。但「表单 + 计算器 + 规则数据」这个规模,零依赖仍然是最优解——它把「部署」这个环节压缩到接近零,把「出 bug」的概率压到供应链之外。
结尾
技术栈本身没有秘密:HTML、CSS、原生 JS、localStorage、Node 内置测试、GitHub Actions、GitHub Pages。真正值得抄的是三条工程习惯——计算与 UI 分离、规则数据化、测试零依赖。这套组合让一个没有构建工具链的静态网页,也能做到有测试、有 CI、有清晰的数据模型,算是在「最小规模」和「工程化」之间找到了一个很舒服的平衡点。
项目托管在 GitHub Pages(wmu-zongce),计算规则依据学校公开的测评办法维护,仅供学生自评参考。