一款游戏化的英语单词默写练习 Web 应用:在「四线三格」中逐字母拼写单词,支持整词确认判题、发音朗读、错词本、限时挑战、生命制闯关,并通过连击播报、幸运红包、金币商店等机制提升练习动力。面向小学到大学(四六级 / 考研 / 雅思 / 托福)全学段词库。
- 四线三格拼写:字母逐格填写,整词确认后统一判题(忽略大小写与首尾空格),对错即时闪烁反馈
- 发音朗读:基于 Web Speech API 的英文朗读(en-US),可反复点击听音
- 两种练习模式
- 普通默写:可选打乱顺序、限时模式
- 闯关模式:一个单元为一关,默认每关三颗生命(答错 / 超时扣心);选择整个年级全部单元时整轮共享 10 颗生命且不清空,任一小关卡失败 AA23 即整轮结束、不可重试,过关弹层结算,全部通关累计通关轮数
- 连击播报:MOBA 风格的 FIRST BLOOD / DOUBLE KILL … LEGENDARY 连击横幅与专属音效
- 错词本:答错的单词自动收录(localStorage 持久化),可针对错词重新练习
- 幸运红包:每轮通关后抽金币(1
100 金币加权概率);23 个单元时金币 ×2,4 个以上单元时 3 个红包中有 2 个含金币并按阶梯倍率加成(46 个 ×1.5、79 个 ×2、10 个以上 ×2.4,单包封顶 100),整年级全部单元则发放 3 个必含金币的红包(其中 1 个至少 10 金币),服务端校验到账 - 金币商店
- 道具:如「查看单词」(冲锋枪扫过四线格逐个打出字母的特效),扣背包库存使用
- 实物礼物:原子扣库存,购买后生成
EWD-XXXXXXXX兑换券码 - 商品管理后台:
/admin可视化新增 / 编辑 / 上下架商品,商品目录存于 SQLite,学员端实时生效 - 用户管理后台:
/admin内按用户名 / ID 搜索用户,查看金币与通关轮数,增加 / 扣减金币并留备注,扣减不允许透支,所有调整写入金币流水可对账
- 账号体系:注册 / 登录 / 登出,scrypt 密码哈希 + 随机 token 会话(30 天有效),金币与通关轮数服务端权威存储,按用户隔离
- 词库自动探测:按「学段 / 年级(考试)/ 上下册 / 单元」组织 JSON 文件,启动时自动探测可用单元,也支持手动导入本地 JSON
| 层 | 技术 |
|---|---|
| 前端 | React 18、TypeScript 5(strict mode)、Vite 5 |
| 样式 / UI | Tailwind CSS 3、Radix UI 原语(shadcn/ui 风格组件)、lucide-react 图标 |
| 状态管理 | Zustand |
| 后端 | Node.js、Express 5 |
| 数据库 | SQLite(better-sqlite3,WAL 模式,启动自动建表 / 幂等迁移) |
| 其他 | Web Speech API(朗读)、localStorage(token / 错词本) |
ewd/
├── public/ # 静态资源与词库 JSON
│ ├── grade1.json # 旧版整年级词库(兼容)
│ ├── grade1a-u1.json # 一年级上册 1 单元词库
│ ├── sfx/ # 连击 / 一血等音效
│ └── peek-scope.mp4 # 「查看单词」冲锋枪特效
├── server/
│ ├── index.js # Express 服务:鉴权 / 奖励 / 商店接口 + 生产静态托管
│ ├── db.js # SQLite 表结构与商品目录种子
│ └── data/ewd.db # 运行后自动生成(已 gitignore)
├── src/
│ ├── components/ # 页面与组件(Home / Quiz / Result / 商店 / 闯关弹层 / AdminPage 等)
│ ├── hooks/ # 自定义 hooks(倒计时等)
│ ├── lib/ # api 客户端、词库加载、判题、奖励、音效、朗读、类型
│ ├── store/ # Zustand 全局状态
│ ├── App.tsx
│ └── main.tsx
├── vite.config.ts # 开发期 /api 代理到 http://localhost:3001
└── package.json
- Node.js 20 LTS 及以上(better-sqlite3 为原生模块,安装时需要匹配的 Node 版本)
- npm
前后端依赖统一在根目录安装:
npm install需要同时启动后端服务和 Vite 开发服务器(两个终端):
# 终端 1:后端接口服务,默认 http://localhost:3001
npm run server
# 终端 2:前端开发服务器,默认 http://localhost:5173
npm run dev打开 Vite 输出的地址(默认 http://localhost:5173)即可使用。开发环境下 /api 请求会自动代理到 http://localhost:3001(见 vite.config.ts)。
# 类型检查 + 构建,产物输出到 dist/
npm run build
# 启动单一服务:同时提供 API 与 dist/ 静态资源托管
npm start生产模式直接访问 http://localhost:3001 即可,无需额外的静态服务器。可用 PORT 环境变量修改端口:
PORT=8080 npm start首次启动服务时会自动创建
server/data/ewd.db并幂等播种内置商品目录,无需手动初始化数据库。
启动服务后访问 /admin(开发环境为 http://localhost:5173/admin,生产环境为 http://localhost:3001/admin),输入管理密码即可:
- 新增商品:名称、类型(道具 / 实物礼物)、描述、价格、库存、排序、是否上架
- 编辑商品:价格、库存、描述等即时生效;SKU 创建后不可修改
- 上下架:下架后学员端商店不再展示,已购买库存 / 兑换券不受影响
- 用户管理:搜索用户,查看金币 / 通关轮数,按 +N / -N 调整金币并填写备注;扣减不能使余额为负,调整记录写入金币流水,可展开查看最近 20 条
管理密码优先读取环境变量 ADMIN_PASSWORD;未配置时服务端会自动生成,并保存到 server/data/admin-password.txt(已 gitignore),同时打印在启动日志中:
ADMIN_PASSWORD=your-strong-password npm run server管理登录与学员账号完全隔离,管理会话 12 小时有效,服务重启后需重新登录。
| 命令 | 说明 |
|---|---|
npm run dev |
启动 Vite 开发服务器(5173) |
npm run server |
启动 Express 后端服务(3001) |
npm start |
同 npm run server(生产环境托管 dist/) |
npm run build |
TypeScript 类型检查 + 生产构建到 dist/ |
npm run preview |
本地预览构建产物 |
词库为放置在 public/ 下的 JSON 文件,格式为单词数组:
[
{ "name": "sport", "trans": ["体育运动"] },
{ "name": "player", "trans": ["运动员", "选手", "球员"] }
]文件命名约定(应用启动时自动探测,无需注册):
| 学段 | 单元文件示例 | 整年级 / 整册兜底 |
|---|---|---|
| 小学 | grade1a-u1.json(一年级上册 1 单元)、grade1b-u3.json(下册) |
grade1.json |
| 初中 | junior1a-u1.json(初一上册 1 单元) |
junior1.json |
| 高中 | senior2a-u1.json(高二上册 1 单元) |
senior2.json |
| 大学考试 | cet4-u1.json(四级 1 单元,不分上下册) |
cet4.json / cet6.json / kaoyan.json / ielts.json / toefl.json |
a= 上册,b= 下册;大学考试不分册,文件名不带a/b- 每个年级最多探测 12 个单元;缺失文件自动跳过
- 跨单元的同名单词(忽略大小写 / 首尾空格)只保留第一次出现
- 也可以在页面中手动导入本地 JSON 文件进行练习
所有接口以 /api 开头,除注册 / 登录外均需 Authorization: Bearer <token> 头:
POST /api/auth/register·POST /api/auth/login·POST /api/auth/logout·GET /api/auth/meGET /api/rewards·POST /api/rewards/grant(红包金币,单包或最多 3 包批量,每包 1~100 整数硬校验)·POST /api/runs(通关轮数 +1)GET /api/shop/catalog·POST /api/shop/purchase(事务保证扣款 / 扣库存原子性)·GET /api/shop/vouchersPOST /api/items/consume(消耗道具,记录使用场景)- 管理后台(使用
x-admin-token,与学员 Bearer 会话隔离):POST /api/admin/login·GET /api/admin/shop/items·POST /api/admin/shop/items·PATCH /api/admin/shop/items/:id·GET /api/admin/users·POST /api/admin/users/:id/coins(增加 / 扣减金币,事务内校验不透支并写流水)·GET /api/admin/users/:id/transactions
数据库表:users、sessions、shop_items、inventory、item_usages、vouchers、coin_transactions(金币收支流水,含余额快照可对账),建表语句见 server/db.js。