抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

当AI生成的课堂工具,被学生重写了

故事是这样的。

我们班主任最近,迷上了用 AI 生成课堂工具。

就是那种把需求打在对话框里,AI 啪一下吐出一个 HTML 文件,她保存下来就能直接用。有一天她捣鼓出一个英语单词配对 PK 游戏,英文卡片对中文卡片,两个人对战配对,看谁先配完。

她发到群里,说,你们试试。

我打开试了一下。

嗯。。。

怎么说呢,作为一个 AI 一句话生成的东西,它能用。但也就真的,只是「能用」的那个程度。

就那种感觉,你懂吧,能用,但是浑身不舒服。

排行榜存在浏览器的 LocalStorage 里,没法迁移。换个设备打开,白板,数据全没了。单词列表也是固定的,没法换。想换一批词?不行。词表写死在 HTML 里,动不了。

我当时看着那个页面沉默了很久。

然后我想,与其等班主任再去找 AI 优化,不如我自己来写一个。

行吧,那就干。

先说说需求。我要做一个 HTML 文件,双击打开直接玩,零安装零依赖。技术栈选了 Vue 3,从 CDN 加载,纯 CSS。首次加载后浏览器会缓存,离线也能用。

整个项目的核心思路就一条,所有东西塞进一个 HTML 文件里。词库、界面、游戏逻辑、排行榜、复习数据,全在一份文件里搞定。没有后端,没有数据库,没有 API。连构建过程都是一个 Python 脚本把词库注入到 HTML 模板里而已。

原理其实很简单。模板 HTML 里有一个占位符,长这样

const ALL_WORDS_DATA = null; // 🏗️ Running

build.py 做的事情就是,读 words 目录下的所有 JSON → 按教材分组 → 构造成 {books: [{name, units: [{name, words}]}]} 的树形结构 → JSON.stringify → 替换掉那个占位符。跑完之后 index.html 里就嵌着全部 2373 个词的完整数据了。

Vue 3 在运行时直接读这个全局变量,渲染出选词界面。整个流程没有任何网络请求,所以断网也能用。

第一版其实很快就跑起来了。核心配对逻辑,一个 Vue 组件,16 张卡片,点击配对,匹配就淡出。前后可能也就写了两三个小时。但真正花时间的,是那些你以为「应该很简单」的东西。

选词界面是第一关。我得把词库按教材分组,每个教材可以展开收起,每个单元前面有个 checkbox,教材级别的 checkbox 要有全选、半选、不选三种状态。纯前端逻辑,没有任何后端帮忙。那个半选状态的 CSS 我调了五六个版本才满意。就为了一个小方块。

然后是词库。

从必修一到选必四,总共 7 本教材 36 个单元的单词,百词斩里全都有,直接拷出来的。先把手机 app 数据目录里的 .baicizhan 和 lookup.db 拷出来打包,一个是词库数据文件,一个是对照数据库,记录了每个词属于哪本教材哪个单元。写了一个 Python 转换脚本,读取百词斩的索引结构,查出每个词对应的单元,然后把 2000 多个词按教材和单元分好,去掉了一些纯专有名词(比如人名地名没法配对的),补了部分缺失的中文释义,最后输出成一个个标准的 JSON 文件。

每个文件长这样

{
“name”: “Welcome Unit”,
“words”: [
{“en”: “exchange”, “zh”: “v. 交流;交换”},
{“en”: “lecture”, “zh”: “n. 演讲;讲座”},
]
}

然后写了一个 build.py 脚本,构建的时候把所有词库注入到 HTML 里。

跑出来的结果是这样

📚 读取 7 册课本
人教版高中英语必修一: 6 单元, 333 词
人教版高中英语必修二: 5 单元, 357 词
人教版高中英语必修三: 5 单元, 403 词
人教版高中英语选必一: 5 单元, 340 词
人教版高中英语选必二: 5 单元, 348 词
人教版高中英语选必三: 5 单元, 309 词
人教版高中英语选必四: 5 单元, 283 词
📝 共 2373 词
✅ 已生成 index.html (396 KB)

396 KB,一个文件,全部 2373 个词。每次跑完看到这个输出,我都觉得还挺有成就感的。

双人 PK 模式是核心玩法。两人分左右两侧同时配对,计时计分。每局固定 8 对单词,16 张卡片,洗牌后随机排列。每张卡有个 pairId,英文卡和中文卡共享同一个 pairId。点第一张选中它,点第二张如果 pairId 相同就配对成功,淡出动画消失;不同就闪烁提示错误,重置选择状态。逻辑不复杂,但手感调了很久。

匹配正确有淡出动画,那个瞬间还蛮爽的。

然后我还有了个单人模式。说实话一开始我也觉得一个人玩这个挺傻的,后来我干脆把它跟复习系统绑在了一起。

对,我还做了个复习系统。

经典的 Leitner 五盒模型。原理很简单,每个单词分五个复习盒,盒 1 每天复习,盒 2 每 2 天,盒 3 每 4 天,盒 4 每 8 天,盒 5 每 16 天。全对升到下一个盒,答错直接扔回盒 1。间隔越来越长,刚好踩在遗忘曲线的节点上。

数据存在浏览器的 LocalStorage 里,结构就是 {wordKey: {box: 1-5, lastReviewed: 时间戳, errors: 错误次数}}。每次打开游戏会检查哪些盒今天该复习了,自动拿出来。

单人模式里配错的单词,自动进入盒 1。这样你就不是在「背单词」,你是在「打游戏顺便把错词复习了」。这个闭环我觉得还挺舒服的。

说到这个复习系统,其实最开始我根本没打算做它。单人模式初版就是个单纯的配对游戏,玩了两把我自己都觉得没意思。后来有天晚上躺在床上想,如果错词能自动进复习,第二天打开还能看到昨天的错词,这不就有连续性了吗。第二天爬起来就加了。

排行榜也做了导入导出。导出就是一个 JSON.stringify,把 LocalStorage 里的排行榜数据序列化,下载成文件。换设备再导入,反过来 JSON.parse 写回 LocalStorage。没有后端没有云,靠的就是浏览器那点存储空间加一个文件传输。虽然原始,但在「不能联网」的前提下,这是最可靠的方案。

项目托管在 GitHub 上,push 到 main 分支自动触发 Vercel 部署。Vercel 的构建环境跑一遍 build.py,输出 index.html,直接部署到全球 CDN。还绑了自定义域名走国内加速节点,不用翻墙也能访问。

说到域名还有个小插曲。我一开始用的 Vercel 默认域名,打开那个 .vercel.app 的地址,在国内加载慢得要命。后来研究了一下,发现 Vercel 的域名被国内 DNS 污染是常态,得绑自己的域名才能走国内节点。我又去配了 Cloudflare 的 DNS,折腾了半天 CNAME 记录,最后才跑通。第一次用自定义域名打开的时候,页面几乎秒开,那个瞬间还是很爽的。

地址在这儿

https://word-pair-pk.hdilp.top

写到这里我突然想到一件事。

班主任用 AI 生成那个初版的时候,前后花了大概五分钟。就是打了一段话,AI 吐出来,她保存,发群里。完事。

我重写这个,前前后后折腾了十几个版本。从最基础的配对逻辑,到选词界面,到复习系统,到导入导出,到发布部署。说实话挺累的。

但你说 AI 那五分钟生成的东西没用吗。也不是。

它是个起点。

AI 帮我省掉了「从零开始想这个需求是什么」的时间。但「怎么做得更好」这件事,还是得自己想。初版能跑,但不好用。我把它变成了一个好用的东西。

我觉得这件事本身就是现在这个时代做东西的方式。AI 负责「快速搞一个能用版本」,人负责「把它变成真正好用的东西」。不需要谁取代谁,互相搭把手就行。

回头想想,如果班主任没有先用 AI 搞出那个初版,我可能根本不会想到要去做这个东西。因为它看起来已经「有了」,没必要再动。但恰恰是因为它「有了但不够好」,才让人觉得「我来」。

这大概就是最好的那种推动力吧。

你说是不是。


项目开源在 GitHub
https://github.com/HDILP/word-pair-pk

在线体验
https://word-pair-pk.hdilp.top

如果想用自己的词表也很简单,按 words 目录里的格式准备 JSON,跑一下 build.py 就行。

以上,既然看到这里了,如果觉得不错,随手点个赞、在看、转发三连吧~

评论区