docs(design): 库存列表设计契约 CONTRACT(design-distill 阶段1)

token映射(prototype var→AppTokens,已1:1)/组件清单/库存列表像素规格/
逐屏验收清单(HTML→Flutter 跨渲染器特例:基准并排目检+三主题自比)。
标注缺口:AppTokens 目前仅颜色,字阶/圆角/间距需补进 token 单源。
登记进 docs/index.html。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSKEiHsvauyxYUW2itzUXX
This commit is contained in:
wangjia
2026-06-25 06:27:20 +08:00
parent 5964fd0eba
commit f9c9c5dcac
2 changed files with 164 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
# 设计契约 — 岩美酒库 Flutter 还原(Phase 1 起:库存列表)
> design-distill 阶段 1 产物。像素以 `.superpowers/prototype/` 原型为**唯一基准**
> token 走 codegen 单源(已在 P0 落地);还原由截图 diff 验收,不靠目测。
> 本文件是**执行真相源**(与 plan `.md` 同性质,机器/diff 引用),随逐屏推进增补。
## 阶段 0 · 归一化产物(已就位)
| 项 | 位置 | 说明 |
|---|---|---|
| token 单源 | `.superpowers/prototype/tokens.css` | 三主题 `[data-theme=a/b/c]`,已 snapshot 到 `client/lib/core/theme/token_source/tokens.css` |
| token codegen | `client/lib/core/theme/tool/gen_tokens.mjs``app_tokens.g.dart` | 单源生成,**禁手改**`kTokensA/B/C` + `appTokensOf(key)` |
| 实现层 | `themes.dart`buildTheme+ `context_tokens.dart``context.tokens` | AppTokens ThemeExtension 挂三 ThemeData |
| 原型清单 | `.superpowers/prototype/screens/*.html` | 桌面 + `m-*` 移动;viewport 桌面 1280×860 / 移动 390×844 |
| 设计铁律 | `.superpowers/prototype/index.html`atoms 注册)+ `tools/check-ds.mjs` 闸 | 颜色只走 token;组件出自已登记 atoms |
| 验收工具 | `~/.claude/skills/design-distill/tools/{shoot-prototype,diff}.mjs` | prototype shoot 已验证三主题可出图 |
### 主题
- **a 经典蓝**(浅,**diff 基准**) · **b 琥珀**(深) · **c 酒窖**(暖浅)。结构/密度/字阶三套统一,只换颜色。
---
## 块 1 · token 映射表(prototype CSS var → Flutter AppTokens
颜色字段 P0 已 1:1 落地(同名)。下表为 a 主题取值(来源 `tokens.css`b/c 见 `app_tokens.g.dart`):
| 原型 var | a 值 | AppTokens 字段 | 用途 |
|---|---|---|---|
| `--page` | `#E7EBF1` | `page` | 最外层底 |
| `--bg` | `#F5F7FA` | `bg` | 内容区底 |
| `--surface` | `#FFFFFF` | `surface` | 卡片/工具条/表格面 |
| `--border` / `--border-subtle` | `#DCE2EB` / `#ECEFF4` | `border` / `borderSubtle` | 描边 |
| `--text` / `--heading` / `--title` | `#232934` / `#1B2430` / `#1B2430` | `text` / `heading` / `title` | 正文/小标题/大标题 |
| `--muted` / `--faint` | `#6E7888` / `#99A3B3` | `muted` / `faint` | 次要/极弱文字 |
| `--primary` / `--primary-dark` / `--on-primary` | `#2563AC` / `#154072` / `#FFFFFF` | `primary` / `primaryDark` / `onPrimary` | 主操作 |
| `--brand400` / `--brand50` / `--accent` | `#4F86C6` / `#EEF4FB` / `#8B2331` | `brand400` / `brand50` / `accent` | 品牌/强调 |
| `--th-bg` / `--row-hover` / `--shadow` | `#F7F9FC` / `#EEF4FB` | `thBg` / `rowHover` / `shadow` | 表头/悬停/投影 |
| 顶栏 `--top-*` / 侧栏 `--side-*` / 用户菜单 `--menu-user-*` | — | `topBg…/sideBg…/menuUser*` | 外壳 chromeP0 已迁移) |
| 状态徽章 `.badge.b-在售/预警/缺货` | success/warn/danger 系 | `success`+`okSoft` / `warn`+`warnBg` / `danger`+`dangerBg` | 状态色 |
### ⚠️ 缺口(鲁棒还原前需补)
AppTokens 目前**仅颜色**。原型还驱动:
- **字阶** `--fs-display 28 / h1 21 / h2 17 / title 15 / body 13 / sm 12 / xs 11`
- **圆角** `--r-sm 4 / md 6 / lg 10 / xl 14 / pill 999`
- **间距** kpis gap 14、toolbar pad 12/14、head mb 18、searchbox 240/180 等
→ 还原前需把字阶/圆角(必要时间距)补进 token 单源 + codegenP1 第一步,**禁页面里写死 px**)。
---
## 块 2 · 组件清单(原型 atom → Flutter widget
| 原型 atom | Flutter | props / 状态 | 现状 |
|---|---|---|---|
| `.top`(顶栏,品牌+门店+主题+通知+用户) | `shell/app_shell.dart` 顶栏 | active 项、主题选择器、通知红点、用户菜单 | 已有,需对齐新视觉 |
| `.side`(分组导航 单据/经营/系统 + 用户页脚) | `shell/app_shell.dart` 侧栏/Drawer | 分组、active、窄屏 Drawer | 已有,需分组化 |
| `.kpi` / `.kpi.alert.click`(KPI 卡 + 图标 + 同比) | **新** `widgets/kpi_card.dart` | icon/title/value/delta(up/down)/onTap/alert | **缺**,需新建 |
| `.toolbar` + `.searchbox` + `.chip`(筛选条) | `widgets/` 复用 + 列头 chip | 搜索/编码/状态 chip/重置 | 部分有 |
| `.table` + `.fh.filtered`(列头漏斗筛选) | `widgets/data_table_card.dart` + `FilterableColumnHeader` | 列头内嵌筛选、可隐藏列、`thBg` 表头 | 已有 |
| `.badge.b-*`(状态徽章 圆点+色) | `widgets/status_badge.dart` | 在售/预警/缺货 → ok/warn/danger | 已有,校色 |
| `.qty.low`(低库存染色 qty≤10 | data row 单元 | qty≤min_stock 染 danger | 需补 |
| `.pager` + `.pgsize-btn`(分页 + 每页条数) | `widgets/` 分页 | 页码/每页 10/20/50/100 | 需对齐 |
| `.ds-fab`(设计期返回球) | — | 仅原型,不还原 | 不实现 |
---
## 块 3 · 页面像素规格 — 库存列表(`screens/inventory.html`,桌面 1280×860
数值取自原型源码(`inventory.html` 内联 + `atoms.css`/`tokens.css`),非目测。
- **head**`align-items:flex-end; gap:14; margin-bottom:18``h1` `fs-h1(21)/700/title/letter .3px``.sub` `fs-sm(12)/muted``.actions` 右对齐 gap 10`列设置`(ghost) `导出`(ghost) `新增入库`(primary)。
- **kpis**`grid 4×1fr gap:14 margin-bottom:20`。每卡:左 `.ic`(圆角 `r-xl` 色块 info/ok/blue/alert+ `.t`(fs-sm muted) + `.v`(大数值) + `.d`(同比 up=success▲ / down=danger▼)。第 4 卡 `alert.click` → 点击筛状态=缺货;第 1 卡 click → 清筛选。
- **toolbar**`surface` 面、`border` 无下边、上圆角 `r-lg``pad 12/14 gap 10``searchbox` 240(商品名/拼音);`searchbox.code` 180(商品编码);`chip` 状态(`.on` 高亮 + 选中值);右侧 `重置`(ghost sm)。
- **table**`flush-top` 接 toolbar):表头 `thBg`;列见块 4。行 `click` 进商品抽屉;`qty≤10``low`(danger)`生产日期` 等宽 `text``入库时间` 等宽 `muted``status``.badge.b-{在售|预警|缺货}``act` 列 眼睛图标查看。空态 `.empty「没有匹配的商品 · 试试调整筛选或搜索」`
- **pager**`每页 [10▾] 条`10/20/50/100 菜单) + `显示 ab,共 N` + 页码 ` 1 2 … `,当前页 `.cur`
- **移动**`m-inventory.html`390×844):表格 → `MobileListCard` 卡片流(复用 `mobile_list_card.dart`);筛选 pill 换行不横滚(原型已修)。
### 列定义(顺序 = 还原顺序)
`商品`(name+code 两行,fixed) · `规格` · `系列` · `批次号` · `库存`(num,≤10染色) · `成本价`(num) · `单价`(num) · `生产日期`(mono) · `入库时间`(mono,muted) · `供应商` · `状态`(filter) · `操作`(fixed)
> 真实后端字段对齐:name=品牌 / series=型号 / spec=版本 / code=商品编号 / batch_no / production_date;状态派生 qty vs `products.min_stock`(在售 / 预警 / 缺货)。快照列回退 `COALESCE(p.*, 快照)`。
---
## 块 4 · 逐屏验收清单(每屏 × 主题;diff 闸)
| 屏 | 原型 | Flutter | a(基准) | b | c |
|---|---|---|---|---|---|
| 库存列表(桌面) | `inventory.html` | `inventory_list_screen.dart` | ☐ 像素 diff ≤阈 | ☐ 结构 | ☐ 结构 |
| 库存列表(移动) | `m-inventory.html` | 同上窄屏 | ☐ | ☐ | ☐ |
**验收法(HTML→Flutter 特例)**HTML(Chromium) 与 Flutter 渲染器不同,**严格 0.1% pixelmatch 不适用跨渲染器**。本项目采用:
1. **原型基准**`shoot-prototype.mjs <screen>.html out.png --width 1280 --height 860 --theme a|b|c`(已验证)。
2. **Flutter 实现图**golden / integration_test 截目标屏 → png。
3. **a 主题**:原型 vs Flutter **并排人工目检** + 关键区域结构 diff(容差放宽);**b/c**Flutter 三主题自比,确认 chrome/表头/徽章随主题换色、无残留硬编码。
4. token 残留闸:`grep 0xFF…` 该屏为 0(颜色全走 `context.tokens`)。
> 后续屏(商品/单据/往来/财务/系统域)逐个追加块 3 规格 + 块 4 行,一屏一闭环,禁积压。
+67
View File
@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文档索引 — 岩美酒库</title>
<style>
:root{
--primary:#2563AC; --primary-dark:#154072; --danger:#D14343; --accent:#8B2331;
--success:#2E8B57; --warn:#B45309;
--ink:#232934; --muted:#6E7888; --border:#DCE2EB; --paper:#F5F7FA; --head:#F0F4FF;
}
*{box-sizing:border-box;font-family:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;}
body{margin:0;background:var(--paper);color:var(--ink);padding:28px;line-height:1.6;}
h1{font-size:21px;margin:0 0 4px;}
.sub{color:var(--muted);font-size:13px;margin-bottom:22px;}
h2{font-size:15px;margin:24px 0 8px;color:var(--primary-dark);border-left:4px solid var(--primary);padding-left:10px;}
.card{background:#fff;border:1px solid var(--border);border-radius:10px;padding:6px 4px;max-width:920px;margin-bottom:14px;}
a.doc{display:flex;gap:10px;align-items:baseline;text-decoration:none;color:var(--ink);padding:9px 14px;border-bottom:1px solid #EEF1F5;}
a.doc:last-child{border-bottom:none;}
a.doc:hover{background:#F7FAFF;}
a.doc b{color:var(--primary-dark);font-size:13.5px;}
a.doc .d{color:var(--muted);font-size:12.5px;}
a.doc .tg{margin-left:auto;font-size:11px;padding:1px 7px;border-radius:9px;flex:none;}
.tg.html{background:#EAF1FB;color:var(--primary);}
.tg.md{background:#EEF1F5;color:var(--muted);}
.tg.new{background:#E6F3EC;color:var(--success);}
</style>
</head>
<body>
<h1>岩美酒库 · 文档索引</h1>
<div class="sub">单一入口 · 新增文档须登记到本页 · 2026-06-24</div>
<h2>设计方案</h2>
<div class="card">
<a class="doc" href="../design/CONTRACT.md"><b>设计契约 CONTRACT</b><span class="d">design-distill 阶段1 · token映射/组件清单/库存列表像素规格/逐屏验收(执行真相源,逐屏增补)</span><span class="tg new">NEW · MD</span></a>
<a class="doc" href="design/flutter-integration-plan.html"><b>原型 → Flutter 集成方案</b><span class="d">设计稿转化 / 目录结构 / 接口适配 / 执行计划</span><span class="tg new">NEW · HTML</span></a>
<a class="doc" href="design/stock-in-cost-confirm-design.html"><b>暂估价 → 确认进价</b><span class="d">调货「先卖后定价」前向补偿模型</span><span class="tg html">HTML</span></a>
<a class="doc" href="design/order-return-prototype.html"><b>单据退单原型</b><span class="d">已审核单退货 · 库存/财务冲销</span><span class="tg html">HTML</span></a>
</div>
<h2>实现计划</h2>
<div class="card">
<a class="doc" href="superpowers/plans/2026-06-24-token-theme-foundation.md"><b>P0 · 令牌层 + 三主题地基</b><span class="d">codegen 单源 → AppTokens → 三 ThemeData → main 接线 → 持久化(✅ 已执行完成)</span><span class="tg new">DONE · MD</span></a>
<a class="doc" href="superpowers/plans/2026-06-24-apptheme-to-tokens-migration.md"><b>P0 · AppTheme → context.tokens 迁移</b><span class="d">28 文件/12 字段映射迁移 + 删除 app_theme.dart(✅ 已执行完成,AppTheme 退役)</span><span class="tg new">DONE · MD</span></a>
<a class="doc" href="TODO.md"><b>TODO</b><span class="d">待办(以全局 todo skill 为准)</span><span class="tg md">MD</span></a>
<span class="d" style="display:block;padding:9px 14px">后续各 Phase 子 plan 定稿后在此登记(<code>.md</code> 执行真相源 + 可选 HTML 阅读版)。</span>
</div>
<h2>知识库 · 调研</h2>
<div class="card">
<a class="doc" href="context/project.md"><b>项目全貌 project.md</b><span class="d">开工前必读</span><span class="tg md">MD</span></a>
<a class="doc" href="dev-setup.md"><b>开发环境搭建</b><span class="d">本地开发 / Docker / 种子数据</span><span class="tg md">MD</span></a>
<a class="doc" href="user-manual.md"><b>用户手册</b><span class="d">功能说明</span><span class="tg md">MD</span></a>
<a class="doc" href="testing/license-test-flow.html"><b>授权测试流程</b><span class="d">兑换券激活/校验</span><span class="tg html">HTML</span></a>
</div>
<h2>排障 · Runbook · 部署</h2>
<div class="card">
<a class="doc" href="deployment-nas-gitea.md"><b>NAS / Gitea 部署</b><span class="d">CI/CD runner</span><span class="tg md">MD</span></a>
<a class="doc" href="android-signing.md"><b>Android 签名</b><span class="d">发版配置</span><span class="tg md">MD</span></a>
<a class="doc" href="ios-signing.md"><b>iOS 签名</b><span class="d">TestFlight / 证书</span><span class="tg md">MD</span></a>
<a class="doc" href="macos-signing.md"><b>macOS 签名</b><span class="d">公证 / 分发</span><span class="tg md">MD</span></a>
</div>
</body>
</html>