From e124ed7c6562e1f3fa57ce549c6734c620b62422 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Mon, 8 Jun 2026 01:31:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(todo):=20=E6=96=B0=E5=A2=9E=20/todo=20slas?= =?UTF-8?q?h=20command=EF=BC=8C=E6=94=AF=E6=8C=81=20add/done/list/reopen/r?= =?UTF-8?q?m?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - todo/todo.json:结构化 JSON 真相源(id/标题/重要度3级/平台标签/版本) - todo/todo.mjs:Node 零依赖单文件 CLI,写操作自动重渲染 HTML - todo/todo.html:重新设计布局(未完成置顶/已完成置底/sticky 筛选栏) - .claude/commands/todo.md:/todo slash command - CLAUDE.md:追加 TODO 管理规范(唯一真相源、完成即标记、禁止使用其它 todo 工具) 旧 todo.html 备份为 todo.legacy.html;存量 6 条待办已导入 todo.json Co-Authored-By: Claude Sonnet 4.6 --- .claude/commands/todo.md | 67 +++++ CLAUDE.md | 22 ++ todo/todo.html | 577 ++++++++++++++++++++------------------- todo/todo.json | 92 +++++++ todo/todo.legacy.html | 328 ++++++++++++++++++++++ todo/todo.mjs | 504 ++++++++++++++++++++++++++++++++++ 6 files changed, 1305 insertions(+), 285 deletions(-) create mode 100644 .claude/commands/todo.md create mode 100644 todo/todo.json create mode 100644 todo/todo.legacy.html create mode 100644 todo/todo.mjs diff --git a/.claude/commands/todo.md b/.claude/commands/todo.md new file mode 100644 index 0000000..8112023 --- /dev/null +++ b/.claude/commands/todo.md @@ -0,0 +1,67 @@ +--- +description: 项目 TODO 管理(list / add / done / reopen / rm) +argument-hint: "list | add <描述> | done [version] | reopen | rm " +model: claude-sonnet-4-6 +allowed-tools: Bash, Read +--- + +管理项目 `todo/todo.json` 待办清单,所有写操作会自动重渲染 `todo/todo.html`。 + +**重要规则**:所有待办项必须通过本命令管理,禁止使用其它 todo 工具(TaskCreate/TodoWrite 等)。 + +--- + +根据 `$ARGUMENTS` 分派,缺省等同 `list`: + +## list(或无参数) + +运行以下命令并将终端 summary 转述给用户,同时提示 HTML 路径便于浏览: + +```bash +node todo/todo.mjs list +``` + +## add <描述> + +将 `$ARGUMENTS` 去掉首词 `add` 后的内容解析为待办描述,推断: +- `--title`:核心一句话标题(简洁,去掉平台/类别信息) +- `--level`:`high`(阻断/紧急)/ `mid`(重要,默认)/ `low`(一般/优化) + - 关键词「紧急」「阻断」「必须」「严重」→ high;「重要」「需要」→ mid;其余 → low +- `--tags`:从描述推断平台/类别,可多个逗号分隔,常用值:`前端,后端,Web,mac,Windows,Android,iOS,数据库,CI/CD,文档` +- `--desc`:补充说明(可选,如有具体文件/路径/上下文则填入) + +然后运行: +```bash +node todo/todo.mjs add --title "..." --level mid --tags "前端,Web" --desc "..." +``` + +转述:「已添加 #id [级别] 标题 标签」,并显示 summary。 + +## done [version] + +从 `$ARGUMENTS` 提取 id(数字)和可选 version(格式 vX.Y.Z): + +- 有 version:`node todo/todo.mjs done --version ` +- 无 version:`node todo/todo.mjs done `(脚本自动用 `git describe --tags --abbrev=0`) + +转述:「#id 标记完成,记入版本 vX.Y.Z」,并显示 summary。 + +## reopen + +```bash +node todo/todo.mjs reopen +``` + +转述「#id 已重新开启」。 + +## rm + +```bash +node todo/todo.mjs rm +``` + +转述「#id 已删除」。 + +--- + +所有命令完成后,在终端显示来自脚本的完整 summary(共/未完成/已完成及分级列表)。 diff --git a/CLAUDE.md b/CLAUDE.md index 4c146d6..d32fb27 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -308,6 +308,28 @@ CI/CD(Forgejo,`.gitea/workflows/deploy.yml`)收到 tag 后自动:编译 } ``` +### 项目 TODO 管理 + +**唯一待办系统**:任务执行过程中如发现需要做的事项,**必须**用 `/todo add <描述>` 记入项目 todo 系统,**禁止**使用 TaskCreate/TodoWrite 等其它 todo 工具,保持单一真相源。 + +- **真相源**:`todo/todo.json`(结构化 JSON,含 id/标题/重要度/平台标签/创建时间/完成版本) +- **HTML**:`todo/todo.html` 为生成物,每次写操作自动重渲染 +- **重要度**:`high`(阻断/紧急)/ `mid`(重要)/ `low`(一般/优化)三级 +- **完成即标记**:完成对应 todo 后运行 `/todo done `,脚本自动记入当前 git tag 版本 + +常用命令: +``` +/todo — 查看 summary(含未完成分级列表) +/todo add 修复... — 新增待办(Claude 自动推断级别/标签) +/todo done — 标记完成,自动记入版本号 +/todo reopen — 撤销完成 +/todo rm — 删除 +``` + +底层脚本:`node todo/todo.mjs <子命令> [参数]` + +--- + ### 搜索实现(拼音) 商品名搜索同时支持汉字、全拼、首字母: diff --git a/todo/todo.html b/todo/todo.html index a0cd627..d291774 100644 --- a/todo/todo.html +++ b/todo/todo.html @@ -3,326 +3,333 @@ -Web 站点问题清单 / TODO · 岩美 +酒库管理系统 — 项目 TODO
-

Web 营销站点 — 问题清单 / TODO

-

检查范围:产品介绍(首页)· 功能详情页 · 价格 · 下载 · 文档 · 注册 · 页眉页脚

-
生成日期 2026-06-07 · 对照后端实现核对 · 代码版本 v1.0.20
+

酒库管理系统 — 项目 TODO

+
生成于 2026-06-08 · 真相源 todo/todo.json
+
+
6全部
+
6未完成
+
0已完成
+
-
- 阻断 必须改 - 重要 - 一般/优化 - 已知占位(暂留) - 已核实代码 - 已完成 +
+
+ 重要度 + + + + +
+
+
+ 状态 + + + +
+
+
+ 平台 / 标签 + +
- -
-

一、功能宣传与实际实现不符(已对照后端代码核实)

-
    -
  • -
    -
    「红冲入库 / 红冲出库」未实现 阻断已核实
    -
    后端 internal/ 全局无任何红冲 / reversal / 冲销逻辑,但首页 FAQ「审批通过后发现错误」与 features/approval.html 第 5 块(红冲 / 净额演示)大篇幅宣传。属虚假宣传,需删除或落地功能。
    +
    + +
    📋 未完成(6)
    +
      + +
    • +
      + 备案号为占位假值 + 高优 · 紧急 +
      +
      现为沪ICP备2026000000号(全0),上线前必须替换真实备案号。
      +
    • -
    • -
      -
      「调拨单」未实现 阻断已核实
      -
      后端无 transfer / 调拨 任何实现。features/approval.html 状态机把「调拨单」与入库/出库/盘点并列为四大单据。需移除调拨相关展示。
      + +
    • +
      + 两套 CSS / 设计令牌并存 + 重要 +
      +
      主站 njk 用 color.css+style.css,功能页用 colors_and_type.css,导航/页脚各写一份,维护分裂。建议合并到一套,功能页走 base.njk 模板。
      +
    • -
    • -
      -
      「库存预警 / 阈值推送」严重夸大 阻断已核实
      -
      后端仅有 min_stock 单一字段用于显示「偏低/告急」标记,没有任何告警、推送、按 SKU 多级阈值配置、按操作员分发的能力。但首页「库存预警实时提醒」与 features/inventory.html 整块「预警阈值配置 / 推送给张磊 / 短信通知李建国」均为虚构界面。需大幅收敛文案。
      + +
    • +
      + 注册接口同源依赖问题 + 一般 / 优化 +
      +
      用 window.location.origin+'/api/v1/public/register',要求同源部署;分离部署时指错。需确认部署形态。
      +
    • -
    • -
      -
      「短信通知 / 邮件提醒」未实现 重要已核实
      -
      后端无 SMS / 邮件发送能力。inventory/approval 页多处「短信通知」「邮件提醒」需删除。
      + +
    • +
      + scan.html / features 走 passthrough 不享模板 + 一般 / 优化 +
      +
      features/scan.html 直接复制,不经 njk 模板,页脚/备案/CSS 都得手动同步。建议长期改造为模板化。
      +
    • -
    • -
      -
      「过期预警 / 保质期推算」未实现 重要已核实
      -
      后端 expire 相关仅为「license 授权到期」,无商品保质期/过期预警。features/inventory.html 批次块的「过期预警」与阈值卡「临近过期 30 天邮件提醒」为虚构。
      + +
    • +
      + 页脚双份维护 + 一般 / 优化 +
      +
      njk 站点用 footer.njk 读变量,功能页把页脚硬编码各写一份,改一处不同步。根因同「两套模板」。
      +
    • -
    • -
      -
      「智能补货建议 v1.7 Beta」「SSO 单点登录」未实现 一般已核实
      -
      补货建议(inventory 页标 v1.7 Beta)、价格连锁版的「SSO 单点登录」后端均无。若作为路线图保留需明确标注「规划中」,否则移除。
      + +
    • +
      + 注册成功提示路径需核对 + 一般 / 优化
      -
    • -
    • -
      -
      「允许超量出库」可配置开关 待核实 一般
      -
      approval 页声称系统设置可开启「允许超量出库」,后端未检索到 oversell/超量 配置项,需确认是否真有该设置。
      -
      -
    • -
    • -
      -
      营销数据疑似杜撰 重要
      -
      首页 Trust 区「1,280+ 门店 / 8.4% 损耗下降 / 99.9% 可用性」无数据支撑;功能页「14 个审计字段」「审批延迟 ≤1s」等均需核实,否则改为中性表述。
      +
      提示「忘记门店编码可在「系统设置→关于」查看」,需与实际客户端菜单路径一致。
      +
    -
    ✅ 已核实确实存在的功能(文案无需改):FIFO 批次扣减出库(stock.go ApproveStockOut)、多仓库(warehouse)、库存盘点、入库/出库审批联动库存+应付应收、商品 min_stock 状态标记、拼音搜索、扫码验真(scan.html)。
    -
- -
-

二、版本号与品牌/域名一致性

-
    -
  • -
    -
    首页 Hero 版本号造假 阻断
    -
    首页徽标硬编码「v1.6 · 新增多仓库联动盘点」,inventory 页又写「v1.6.2」,而真实版本为 v1.0.20(CHANGELOG)。需统一为真实版本,最好像下载页一样从 changelog 数据动态取。index.njk:180-183
    -
    -
  • -
  • -
    -
    品牌域名三处不统一 重要
    -
    首页预览写 yanmei.app/inventory、客服邮箱 support@yanmei.app,而真实应用域名是 jiu.51yanmei.com。需统一域名口径。index.njk:201 · _data/site.json
    -
    -
  • -
  • -
    -
    下载页 API 域名硬编码 一般
    -
    https://jiu.51yanmei.com 直接写死在 download.njk 的 JS 里(释放接口、各端下载、Web 入口),应抽到 _data/site.json 统一管理,便于换域名/环境。
    -
    -
  • -
-
+
+
✅ 已完成(0)
+ + +
- -
-

三、页眉(导航)

-
    -
  • -
    -
    功能详情页死链:/download.html 阻断
    -
    两个功能页(手写 HTML)导航和页脚都链向 /download.html/docs.html,但 Eleventy 实际产出 pretty URL /download//docs/。dist 中无 download.html → 死链。统一改为 /download//docs/features/inventory.html:660-661 · approval.html 同
    -
    -
  • -
  • -
    -
    功能详情页未进主导航(孤岛页面) 重要
    -
    features/inventory.htmlapproval.html 制作精良,但主站导航「产品」只跳首页锚点 /#modules,这两页仅能从页脚或彼此跳转进入。建议在导航或首页模块卡加「了解更多」入口。
    -
    -
  • -
  • -
    -
    两套 CSS / 设计令牌并存 重要
    -
    主站 njk 用 color.css + style.css,功能页用 colors_and_type.css,导航/页脚样式各写一份。维护分裂、易不一致,建议合并到一套并让功能页也走 base.njk 模板。
    -
    -
  • -
  • -
    -
    注册入口不一致 一般
    -
    功能页 JS 未登录态指向 /app/login/app/register(Flutter 内路由),而 njk 站点有独立 web 注册页 /register/。两套注册入口口径不一,需统一。
    -
    -
  • -
-
+
- -
-

四、页脚

-
    -
  • -
    -
    大量占位死链 href="#" 重要
    -
    页脚以下链接全部为空锚点,需逐一落地或移除:API 文档、视频教程、数据导入模板、提交工单、技术支持、服务等级、关于岩美、客户案例、招贤纳士、联系我们、版本更新。footer.njk · features/*.html
    -
    -
  • -
  • -
    -
    备案号为占位假值 已知占位
    -
    现为 沪 ICP 备 2026000000 号 · 沪公网安备 31010000000000 号(全 0)。按要求暂填,上线前必须替换为真实备案号(且 ICP 主体地需与实际服务器/主体一致,岩美若在沪外需核对)。_data/site.json:5
    -
    -
  • -
  • -
    -
    客服邮箱/电话为占位 重要
    -
    邮箱 support@yanmei.app(与真实域名不符)、电话 400-880-8888(明显假号)。需换成真实联系方式。_data/site.json:7-8
    -
    -
  • -
  • -
    -
    页脚双份维护 一般
    -
    njk 站点用 footer.njk(读 site.json 变量),功能页则把页脚、备案、联系方式硬编码各写一份,改一处不同步。根因同「两套模板」,建议功能页接入 base.njk。
    -
    -
  • -
-
+ - + \ No newline at end of file diff --git a/todo/todo.json b/todo/todo.json new file mode 100644 index 0000000..157d5f5 --- /dev/null +++ b/todo/todo.json @@ -0,0 +1,92 @@ +{ + "meta": { + "title": "酒库管理系统 — 项目 TODO", + "updated_at": "2026-06-07T17:31:30.449Z" + }, + "seq": 7, + "items": [ + { + "id": 1, + "title": "两套 CSS / 设计令牌并存", + "desc": "主站 njk 用 color.css+style.css,功能页用 colors_and_type.css,导航/页脚各写一份,维护分裂。建议合并到一套,功能页走 base.njk 模板。", + "level": "mid", + "tags": [ + "前端", + "Web" + ], + "created_at": "2026-06-07T17:30:32.138Z", + "done": false, + "completed_at": null, + "version": null + }, + { + "id": 2, + "title": "备案号为占位假值", + "desc": "现为沪ICP备2026000000号(全0),上线前必须替换真实备案号。", + "level": "high", + "tags": [ + "Web" + ], + "created_at": "2026-06-07T17:30:32.188Z", + "done": false, + "completed_at": null, + "version": null + }, + { + "id": 3, + "title": "注册接口同源依赖问题", + "desc": "用 window.location.origin+'/api/v1/public/register',要求同源部署;分离部署时指错。需确认部署形态。", + "level": "low", + "tags": [ + "前端", + "Web" + ], + "created_at": "2026-06-07T17:30:32.237Z", + "done": false, + "completed_at": null, + "version": null + }, + { + "id": 4, + "title": "scan.html / features 走 passthrough 不享模板", + "desc": "features/scan.html 直接复制,不经 njk 模板,页脚/备案/CSS 都得手动同步。建议长期改造为模板化。", + "level": "low", + "tags": [ + "前端", + "Web" + ], + "created_at": "2026-06-07T17:30:32.287Z", + "done": false, + "completed_at": null, + "version": null + }, + { + "id": 5, + "title": "页脚双份维护", + "desc": "njk 站点用 footer.njk 读变量,功能页把页脚硬编码各写一份,改一处不同步。根因同「两套模板」。", + "level": "low", + "tags": [ + "前端", + "Web" + ], + "created_at": "2026-06-07T17:30:32.334Z", + "done": false, + "completed_at": null, + "version": null + }, + { + "id": 6, + "title": "注册成功提示路径需核对", + "desc": "提示「忘记门店编码可在「系统设置→关于」查看」,需与实际客户端菜单路径一致。", + "level": "low", + "tags": [ + "前端", + "Web" + ], + "created_at": "2026-06-07T17:30:32.381Z", + "done": false, + "completed_at": null, + "version": null + } + ] +} diff --git a/todo/todo.legacy.html b/todo/todo.legacy.html new file mode 100644 index 0000000..a0cd627 --- /dev/null +++ b/todo/todo.legacy.html @@ -0,0 +1,328 @@ + + + + + +Web 站点问题清单 / TODO · 岩美 + + + + +
+
+

Web 营销站点 — 问题清单 / TODO

+

检查范围:产品介绍(首页)· 功能详情页 · 价格 · 下载 · 文档 · 注册 · 页眉页脚

+
生成日期 2026-06-07 · 对照后端实现核对 · 代码版本 v1.0.20
+
+
+ +
+ 阻断 必须改 + 重要 + 一般/优化 + 已知占位(暂留) + 已核实代码 + 已完成 +
+ + +
+

一、功能宣传与实际实现不符(已对照后端代码核实)

+
    +
  • +
    +
    「红冲入库 / 红冲出库」未实现 阻断已核实
    +
    后端 internal/ 全局无任何红冲 / reversal / 冲销逻辑,但首页 FAQ「审批通过后发现错误」与 features/approval.html 第 5 块(红冲 / 净额演示)大篇幅宣传。属虚假宣传,需删除或落地功能。
    +
    +
  • +
  • +
    +
    「调拨单」未实现 阻断已核实
    +
    后端无 transfer / 调拨 任何实现。features/approval.html 状态机把「调拨单」与入库/出库/盘点并列为四大单据。需移除调拨相关展示。
    +
    +
  • +
  • +
    +
    「库存预警 / 阈值推送」严重夸大 阻断已核实
    +
    后端仅有 min_stock 单一字段用于显示「偏低/告急」标记,没有任何告警、推送、按 SKU 多级阈值配置、按操作员分发的能力。但首页「库存预警实时提醒」与 features/inventory.html 整块「预警阈值配置 / 推送给张磊 / 短信通知李建国」均为虚构界面。需大幅收敛文案。
    +
    +
  • +
  • +
    +
    「短信通知 / 邮件提醒」未实现 重要已核实
    +
    后端无 SMS / 邮件发送能力。inventory/approval 页多处「短信通知」「邮件提醒」需删除。
    +
    +
  • +
  • +
    +
    「过期预警 / 保质期推算」未实现 重要已核实
    +
    后端 expire 相关仅为「license 授权到期」,无商品保质期/过期预警。features/inventory.html 批次块的「过期预警」与阈值卡「临近过期 30 天邮件提醒」为虚构。
    +
    +
  • +
  • +
    +
    「智能补货建议 v1.7 Beta」「SSO 单点登录」未实现 一般已核实
    +
    补货建议(inventory 页标 v1.7 Beta)、价格连锁版的「SSO 单点登录」后端均无。若作为路线图保留需明确标注「规划中」,否则移除。
    +
    +
  • +
  • +
    +
    「允许超量出库」可配置开关 待核实 一般
    +
    approval 页声称系统设置可开启「允许超量出库」,后端未检索到 oversell/超量 配置项,需确认是否真有该设置。
    +
    +
  • +
  • +
    +
    营销数据疑似杜撰 重要
    +
    首页 Trust 区「1,280+ 门店 / 8.4% 损耗下降 / 99.9% 可用性」无数据支撑;功能页「14 个审计字段」「审批延迟 ≤1s」等均需核实,否则改为中性表述。
    +
    +
  • +
+
✅ 已核实确实存在的功能(文案无需改):FIFO 批次扣减出库(stock.go ApproveStockOut)、多仓库(warehouse)、库存盘点、入库/出库审批联动库存+应付应收、商品 min_stock 状态标记、拼音搜索、扫码验真(scan.html)。
+
+ + +
+

二、版本号与品牌/域名一致性

+
    +
  • +
    +
    首页 Hero 版本号造假 阻断
    +
    首页徽标硬编码「v1.6 · 新增多仓库联动盘点」,inventory 页又写「v1.6.2」,而真实版本为 v1.0.20(CHANGELOG)。需统一为真实版本,最好像下载页一样从 changelog 数据动态取。index.njk:180-183
    +
    +
  • +
  • +
    +
    品牌域名三处不统一 重要
    +
    首页预览写 yanmei.app/inventory、客服邮箱 support@yanmei.app,而真实应用域名是 jiu.51yanmei.com。需统一域名口径。index.njk:201 · _data/site.json
    +
    +
  • +
  • +
    +
    下载页 API 域名硬编码 一般
    +
    https://jiu.51yanmei.com 直接写死在 download.njk 的 JS 里(释放接口、各端下载、Web 入口),应抽到 _data/site.json 统一管理,便于换域名/环境。
    +
    +
  • +
+
+ + +
+

三、页眉(导航)

+
    +
  • +
    +
    功能详情页死链:/download.html 阻断
    +
    两个功能页(手写 HTML)导航和页脚都链向 /download.html/docs.html,但 Eleventy 实际产出 pretty URL /download//docs/。dist 中无 download.html → 死链。统一改为 /download//docs/features/inventory.html:660-661 · approval.html 同
    +
    +
  • +
  • +
    +
    功能详情页未进主导航(孤岛页面) 重要
    +
    features/inventory.htmlapproval.html 制作精良,但主站导航「产品」只跳首页锚点 /#modules,这两页仅能从页脚或彼此跳转进入。建议在导航或首页模块卡加「了解更多」入口。
    +
    +
  • +
  • +
    +
    两套 CSS / 设计令牌并存 重要
    +
    主站 njk 用 color.css + style.css,功能页用 colors_and_type.css,导航/页脚样式各写一份。维护分裂、易不一致,建议合并到一套并让功能页也走 base.njk 模板。
    +
    +
  • +
  • +
    +
    注册入口不一致 一般
    +
    功能页 JS 未登录态指向 /app/login/app/register(Flutter 内路由),而 njk 站点有独立 web 注册页 /register/。两套注册入口口径不一,需统一。
    +
    +
  • +
+
+ + +
+

四、页脚

+
    +
  • +
    +
    大量占位死链 href="#" 重要
    +
    页脚以下链接全部为空锚点,需逐一落地或移除:API 文档、视频教程、数据导入模板、提交工单、技术支持、服务等级、关于岩美、客户案例、招贤纳士、联系我们、版本更新。footer.njk · features/*.html
    +
    +
  • +
  • +
    +
    备案号为占位假值 已知占位
    +
    现为 沪 ICP 备 2026000000 号 · 沪公网安备 31010000000000 号(全 0)。按要求暂填,上线前必须替换为真实备案号(且 ICP 主体地需与实际服务器/主体一致,岩美若在沪外需核对)。_data/site.json:5
    +
    +
  • +
  • +
    +
    客服邮箱/电话为占位 重要
    +
    邮箱 support@yanmei.app(与真实域名不符)、电话 400-880-8888(明显假号)。需换成真实联系方式。_data/site.json:7-8
    +
    +
  • +
  • +
    +
    页脚双份维护 一般
    +
    njk 站点用 footer.njk(读 site.json 变量),功能页则把页脚、备案、联系方式硬编码各写一份,改一处不同步。根因同「两套模板」,建议功能页接入 base.njk。
    +
    +
  • +
+
+ + +
+

五、价格

+
    +
  • +
    +
    价格数字需确认是否真实对外 重要
    +
    单店版 ¥299/月、年付 ¥2,988/年、连锁版「定制」疑为占位。上线前需确认真实报价。
    +
    +
  • +
  • +
    +
    「购买 / 联系销售 / 预约演示」按钮无对应流程 重要
    +
    价格卡「购买」「联系销售」、Hero「观看演示」、CTA「预约 1 对 1 演示」全部指向 /app/,但并无支付、销售联系、演示预约流程。按钮文案与去向不符。index.njk:554,568,188,622
    +
    +
  • +
  • +
    +
    「试用版」与「单店版」文案口径混乱 一般
    +
    价格区把「试用版(¥0/30天)」与「单店版(¥299)」分列两卡,而 FAQ 又称「标准版提供 30 天免费试用」。试用 vs 标准 的口径需统一。
    +
    +
  • +
+
+ + +
+

六、下载页

+
    +
  • +
    +
    下载按钮失败无降级 重要
    +
    Windows/macOS/Android 按钮初始 href="#",依赖运行时 fetch /api/v1/public/release 回填真实下载地址;接口失败 / 无数据时点击无任何反应、也无提示。需加失败兜底(如提示「暂未就绪」或禁用态)。download.njk:500-543
    +
    +
  • +
  • +
    +
    iOS 状态依赖接口字段 一般
    +
    iOS 卡片仅当接口返回 download_urls.ios 才从「敬请期待」切换为「TestFlight 安装」。需确认 TestFlight 公开链接是否已配到 release 接口,否则 iOS 永远显示敬请期待(与首页「五端就绪」矛盾)。
    +
    +
  • +
  • +
    +
    首页「五端就绪」与下载页 iOS 状态矛盾 重要
    +
    首页平台区把 Web/Win/macOS/iOS/Android 全列为可用,下载页 iOS 仍是「敬请期待」。需对齐口径。
    +
    +
  • +
+
+ + +
+

七、文档页

+
    +
  • +
    +
    ⌘K 搜索快捷键是装饰 一般
    +
    文档顶栏显示 ⌘K 徽标但未绑定键盘事件聚焦搜索框;搜索本身仅按整章显隐,无关键词高亮/定位。要么实现快捷键,要么去掉徽标。docs.njk
    +
    +
  • +
  • +
    +
    反馈接口路径不一致 / 待确认 一般
    +
    文档反馈用相对路径 /api/v1/public/feedback,下载页却用绝对域名。需确认该端点真实存在且同源可达。docs.njk:182
    +
    +
  • +
+
+ + +
+

八、注册页

+
    +
  • +
    +
    注册接口同源依赖 一般
    +
    window.location.origin + '/api/v1/public/register',要求静态站与后端同源部署;本地预览或分离部署时会指错。需确认部署形态(与下载页写死域名的做法也不统一)。register.njk:175
    +
    +
  • +
  • +
    +
    成功提示路径需核对 一般
    +
    提示「忘记门店编码可在『系统设置 → 关于』查看」,需与实际客户端菜单路径一致。
    +
    +
  • +
+
+ + +
+

九、构建 / 工程结构

+
    +
  • +
    +
    dist 残留旧产物 docs.html 一般
    +
    dist 同时存在 docs.html(疑似旧构建残留)与 docs/index.html,造成同内容双 URL。建议清理 dist 后全量重建,避免历史残留。
    +
    +
  • +
  • +
    +
    scan.html / features 走 passthrough,不享模板 一般
    +
    .eleventy.js 把 features、scan.html 直接复制,不经 njk 模板,导致页脚、备案、域名、CSS 都得手动同步——是上面多个「双份维护」问题的工程根因。建议长期改造为模板化。
    +
    +
  • +
+
+ + + diff --git a/todo/todo.mjs b/todo/todo.mjs new file mode 100644 index 0000000..bc8d985 --- /dev/null +++ b/todo/todo.mjs @@ -0,0 +1,504 @@ +#!/usr/bin/env node +/** + * todo/todo.mjs — 项目 TODO 管理 CLI(零依赖,Node.js >= 18) + * + * 子命令: + * add --title "..." [--level high|mid|low] [--tags a,b] [--desc "..."] + * done [--version vX.Y.Z] + * reopen + * rm + * list + * render + */ + +import { readFileSync, writeFileSync, existsSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { execSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; + +const __dir = dirname(fileURLToPath(import.meta.url)); +const DB = resolve(__dir, 'todo.json'); +const HTML = resolve(__dir, 'todo.html'); + +// ─── 数据读写 ──────────────────────────────────────────────────────────────── + +function load() { + if (!existsSync(DB)) { + return { meta: { title: '酒库管理系统 — 项目 TODO', updated_at: now() }, seq: 0, items: [] }; + } + return JSON.parse(readFileSync(DB, 'utf8')); +} + +function save(db) { + db.meta.updated_at = now(); + writeFileSync(DB, JSON.stringify(db, null, 2) + '\n', 'utf8'); +} + +function now() { + return new Date().toISOString(); +} + +function fmtDate(iso) { + if (!iso) return ''; + const d = new Date(iso); + return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; +} + +function gitTag() { + try { + return execSync('git describe --tags --abbrev=0', { encoding: 'utf8', stdio: ['ignore','pipe','ignore'] }).trim(); + } catch { + return null; + } +} + +// ─── 终端 summary ──────────────────────────────────────────────────────────── + +const LEVEL_LABEL = { high: '高', mid: '中', low: '低' }; +const LEVEL_ORDER = { high: 0, mid: 1, low: 2 }; + +function printSummary(db) { + const pending = db.items.filter(i => !i.done) + .sort((a,b) => (LEVEL_ORDER[a.level]??9) - (LEVEL_ORDER[b.level]??9) || a.id - b.id); + const done = db.items.filter(i => i.done) + .sort((a,b) => (b.completed_at||'') > (a.completed_at||'') ? 1 : -1); + + console.log(`\nTODO 共 ${db.items.length} · 未完成 ${pending.length} · 已完成 ${done.length}`); + + if (pending.length) { + console.log('未完成(按重要度):'); + for (const i of pending) { + const tags = i.tags && i.tags.length ? i.tags.join('/') : ''; + const lvl = LEVEL_LABEL[i.level] || i.level; + console.log(` [${lvl}] #${i.id} ${i.title}${tags ? ' ' + tags : ''}`); + } + } + + if (done.length) { + console.log('已完成(最近 5 条):'); + for (const i of done.slice(0, 5)) { + const ver = i.version ? `(${i.version})` : ''; + console.log(` ✅ #${i.id} ${i.title}${ver}`); + } + } + console.log(''); +} + +// ─── HTML 渲染 ─────────────────────────────────────────────────────────────── + +function esc(s) { + return String(s ?? '').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); +} + +/** 把描述里的 `code` 渲染成 */ +function renderDesc(s) { + if (!s) return ''; + return esc(s).replace(/`([^`]+)`/g, '$1'); +} + +function levelClass(level) { + return { high: 't-block', mid: 't-high', low: 't-low' }[level] || 't-low'; +} +function levelText(level) { + return { high: '高优 · 紧急', mid: '重要', low: '一般 / 优化' }[level] || level; +} + +function renderItems(items) { + if (!items.length) return '

暂无条目

'; + return items.map(i => { + const tags = (i.tags||[]).map(t => `${esc(t)}`).join(' '); + const desc = i.desc ? `
${renderDesc(i.desc)}
` : ''; + const meta = `
+ 🕐 ${fmtDate(i.created_at)} + ${i.done ? `✅ 完成 ${fmtDate(i.completed_at)}${i.version ? ' · '+esc(i.version)+'' : ''}` : ''} +
`; + const lvlCls = levelClass(i.level); + return ` +
  • +
    + ${esc(i.title)} + ${levelText(i.level)} +
    + ${desc} + +
  • `; + }).join('\n'); +} + +function buildHtml(db) { + const allTags = [...new Set(db.items.flatMap(i => i.tags||[]))].sort(); + const pending = db.items.filter(i => !i.done) + .sort((a,b) => (LEVEL_ORDER[a.level]??9) - (LEVEL_ORDER[b.level]??9) || a.id - b.id); + const done = db.items.filter(i => i.done) + .sort((a,b) => (b.completed_at||'') > (a.completed_at||'') ? 1 : -1); + + const tagChips = allTags.map(t => + `` + ).join(''); + + return ` + + + + +${esc(db.meta.title)} + + + + +
    +
    +

    ${esc(db.meta.title)}

    +
    生成于 ${fmtDate(db.meta.updated_at)} · 真相源 todo/todo.json
    +
    +
    ${db.items.length}全部
    +
    ${pending.length}未完成
    +
    ${done.length}已完成
    +
    +
    +
    + +
    +
    + 重要度 + + + + +
    +
    +
    + 状态 + + + +
    + ${allTags.length ? `
    +
    + 平台 / 标签 + ${tagChips} +
    ` : ''} +
    + +
    + +
    📋 未完成(${pending.length})
    +
      + ${renderItems(pending)} +
    + +
    +
    ✅ 已完成(${done.length})
    + + +
    + +
    + + + +`; +} + +// ─── CLI 解析 ──────────────────────────────────────────────────────────────── + +function parseArgs(argv) { + const args = {}; + let pos = []; + for (let i = 0; i < argv.length; i++) { + if (argv[i].startsWith('--')) { + const key = argv[i].slice(2); + const val = argv[i+1] && !argv[i+1].startsWith('--') ? argv[++i] : true; + args[key] = val; + } else { + pos.push(argv[i]); + } + } + args._ = pos; + return args; +} + +function findItem(db, id) { + const n = parseInt(id, 10); + const item = db.items.find(i => i.id === n); + if (!item) { console.error(`错误:找不到 id=${n} 的条目`); process.exit(1); } + return item; +} + +// ─── 子命令 ───────────────────────────────────────────────────────────────── + +function cmdAdd(db, args) { + const title = args.title || (args._.join(' ') || '').trim(); + if (!title) { console.error('错误:--title 不能为空'); process.exit(1); } + + const level = ['high','mid','low'].includes(args.level) ? args.level : 'mid'; + const tags = args.tags ? args.tags.split(',').map(t => t.trim()).filter(Boolean) : []; + const desc = args.desc || null; + + db.seq++; + const item = { + id: db.seq, + title, + desc, + level, + tags, + created_at: now(), + done: false, + completed_at: null, + version: null, + }; + db.items.push(item); + save(db); + writeFileSync(HTML, buildHtml(db), 'utf8'); + + console.log(`✅ 已添加 #${item.id} [${levelText(level)}] ${title}${tags.length ? ' ' + tags.join('/') : ''}`); + printSummary(db); +} + +function cmdDone(db, args) { + const id = args._[0]; + const item = findItem(db, id); + + let version = args.version || null; + if (!version) { + version = gitTag(); + if (!version) console.warn('⚠️ 无法从 git 获取版本号,version 将为空'); + } + + item.done = true; + item.completed_at = now(); + item.version = version; + + save(db); + writeFileSync(HTML, buildHtml(db), 'utf8'); + + console.log(`✅ #${item.id}「${item.title}」已标记完成${version ? ',记入版本 ' + version : ''}`); + printSummary(db); +} + +function cmdReopen(db, args) { + const id = args._[0]; + const item = findItem(db, id); + + item.done = false; + item.completed_at = null; + item.version = null; + + save(db); + writeFileSync(HTML, buildHtml(db), 'utf8'); + + console.log(`🔄 #${item.id}「${item.title}」已重新开启`); + printSummary(db); +} + +function cmdRm(db, args) { + const id = parseInt(args._[0], 10); + const idx = db.items.findIndex(i => i.id === id); + if (idx < 0) { console.error(`错误:找不到 id=${id}`); process.exit(1); } + + const [removed] = db.items.splice(idx, 1); + save(db); + writeFileSync(HTML, buildHtml(db), 'utf8'); + + console.log(`🗑️ #${removed.id}「${removed.title}」已删除`); + printSummary(db); +} + +function cmdList(db) { + // 确保 HTML 最新 + writeFileSync(HTML, buildHtml(db), 'utf8'); + printSummary(db); + console.log(`📄 HTML:todo/todo.html`); +} + +function cmdRender(db) { + writeFileSync(HTML, buildHtml(db), 'utf8'); + console.log('🖨️ todo/todo.html 已重新渲染'); +} + +// ─── 入口 ──────────────────────────────────────────────────────────────────── + +const [,, subcmd, ...rest] = process.argv; +const db = load(); +const args = parseArgs(rest); + +switch (subcmd) { + case 'add': cmdAdd(db, args); break; + case 'done': cmdDone(db, args); break; + case 'reopen': cmdReopen(db, args); break; + case 'rm': cmdRm(db, args); break; + case 'list': + case undefined: + case '': cmdList(db); break; + case 'render': cmdRender(db); break; + default: + console.error(`未知子命令:${subcmd}\n用法:node todo.mjs add|done|reopen|rm|list|render`); + process.exit(1); +}