单文件 HTML 计分排行应用生成器
> 一句话:用户说「做个 XX 计分榜」,你产出一个单文件 HTML——双击或发链接就能用,数据存浏览器本地,不连任何服务器。
本 skill 提供一份通用骨架(assets/template.html)+ 一套生成方法论。你拿它照着改,几分钟就能交付一个带排行榜、明细、导出合并、K 线、玩法切换的计分 App。
首个生产实例:KK Club 台球英雄榜(references/kk-club-example.md),已上线运行。
适用场景 / 触发词
| 用户说 | 怎么做 |
|---|---|
| 「做个台球/麻将/棋牌计分榜」 | 拷 assets/template.html,改文案+玩法即交付 |
| 「想记录和朋友的健身/跑步对练」 | 同上,玩法用「比分制」或「胜负制」 |
| 「要个打卡榜/积分榜」 | 同上,累计类用「比分制」记分即可 |
| 「把线下计分搬到网页」 | 同上,强调离线+本地存储 |
| 「做个不连服务器的战绩本」 | 同上,明确走纯 localStorage 路线 |
不适合:需要多人实时同步(同链接互看即时更新)的场景——那必须后端,纯前端单文件做不到,如实告知用户。
核心能力(模板已内置,开箱即用)
- ✅ 零依赖单文件:HTML/CSS/JS 全内联,无 CDN、无后端、无接口,丢哪都能开、可离线
- ✅ localStorage 持久化 + 本地账号 ID(
localId生成,改昵称不丢战绩) - ✅ 排行榜聚合:按胜场/净胜/场次排序,自动算胜率、当前连胜、最高连胜、段位
- ✅ 对局明细:倒序列表,单条可删除(删除按钮
data-del必须等于m.id,这是真实踩过的坑) - ✅ 导出/合并:导出 JSON 发好友 → 好友「合并好友数据」导入,以对局 ID 去重汇总成总榜
- ✅ K 线走势:Canvas 按天汇总选中玩家净胜,0 基准线 + 红绿点
- ✅ 多玩法切换:比分制(填双方分数)/ 胜负制(选胜负+净胜),UI 与数据层解耦
- ✅ 移动端优先 + 深色主题
5 步工作流
# 1. 拷模板到你的项目
cp <本skill>/assets/template.html <项目>/scoreboard.html
# 2. 和用户厘清需求(关键!别上来就写)
# - 什么场景?对抗还是累计?
# - 计分规则:比分制 / 胜负+净胜 / 其他?
# - 排行口径:按胜场?按净胜?按积分?
# - 要不要玩法切换、K线、导出合并?
# 3. 改 template.html(全局搜「CUSTOMIZE」注释,按需改)
# - 标题/昵称提示等文案
# - 玩法 seg 按钮(data-mode 值会写进 match.mode)
# - 段位阈值 RANKS、排行榜排序权重
# - 颜色变量(--teal / --gold)
# 4. 验证(务必跑,见 assets/test_e2e.js)
# node assets/test_e2e.js # 需要 jsdom
# 5. 可选部署成固定链接
# workbuddy_cloudstudio_deploy(directory="<项目>", port=3000)
技术骨架(关键函数,全在 template.html 全局作用域)
| 类别 | 函数 | 用途 |
|---|---|---|
| 存储 | lget(k) / lset(k,v) |
localStorage 读写(自带 try/catch) |
| 存储 | localId(p) |
生成本地 ID:p_<时间戳36>_<随机6>(玩家 p_ / 对局 m_),替代云端 _id |
| 存储 | loadAll() |
启动读取 players / matches |
| 写操作 | getOrCreatePlayer(name) |
按名查/建玩家,返回 id |
| 写操作 | addMatch(m) |
m.id=localId("m") 后入栈保存,返回 Promise |
| 写操作 | removeMatch(id) |
按 m.id 过滤删除 |
| 写操作 | importMatches(obj) |
合并好友数据:players 补齐 + matches 按 id 去重 |
| 写操作 | buildExport() |
导出对象 {version, exportedAt, me, players, matches} |
| 聚合 | computeBoard() |
遍历 matches 聚合每个玩家 胜/负/平/净胜/连胜/段位,按胜场→净胜→场次排序 |
| 聚合 | matchWinner(m) / matchNetFor(m,side) |
判胜负、算某方本场净胜(比分制/胜负制通用) |
| 可视化 | klineSeries(pid) / drawKLine(canvas,series) |
按天汇总净胜 + Canvas 折线 |
> 业务逻辑(计分规则、段位阈值)每次按需求改;存储层 / 聚合 / 导出合并这套骨架直接复用。
铁律(血泪换来的,通用适用)
- 单文件、零外部依赖——所有 CSS/JS 内联,不准引 CDN、不准引外部脚本。否则"离线可用"和"双击即开"全废。
- 数据只用 localStorage——不要为了"好看"偷偷接后端。用户要的是不连服务器的本地小工具;多人实时同步是另一回事,提前说清。
- ID 用
localId生成,不要用云端的_id——纯本地没有云端自增 ID。 - 删除按钮
data-del必须等于记录真实 ID(m.id)——KK Club 真实踩过的坑:曾写成m._id(云版字段),本地版对局字段已改名m.id,导致data-del全空、点击被if(!id) return拦截、连确认框都不弹。改字段名时务必全局搜一遍._id。 - 导出用 JSON + 合并按
id去重——共享模型是"各记各的→导出→合并",不是云端实时同步。 - 玩法切换 UI 与数据层解耦——
bindEvents和render不能在"初始化云端"之后才绑定,否则云端没连上整页就死(KK Club 另一个踩过的坑)。本模板已是解耦写法。 - 验证必须跑 jsdom——见
assets/test_e2e.js。JSDOM 三个 gotcha:① 必须给url选项(如"https://x.local/"),否则localStorage访问抛DOMException;② 用w.getComputedStyle(不是全局);③canvas.getContext需 stub(环境常缺canvas包)。
部署(可选,变成固定链接)
workbuddy_cloudstudio_deploy(directory="<项目目录>", port=3000)
首次部署建 workspace,之后复用同一 workspace,链接固定不变,适合发群里。注意:纯本地版部署到链接后,数据仍按访问者各自的浏览器本地存储(不是云端共享)。若用户要"多人共享同一份战绩",那必须后端,单文件方案做不到——如实告知。
参考实例
references/kk-club-example.md —— KK Club 台球英雄榜,本模板的首个生产实例。演示了:台球夜场视觉、段位系统、K 线按天汇总净胜、导出/合并共享模型、玩法切换(斯诺克/中式8球)解耦。拿不准怎么定制时翻它。
变更记录
- v1.0.0:初始版。通用骨架
template.html(两种玩法 + 排行榜 + 明细删除 + 导出合并 + K 线 + 深色移动端主题)+ jsdom 验证脚本 + KK Club 实例参考。