Files
jiu/design/CONTRACT.md
T

187 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 设计契约 — 岩美酒库 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`(设计期返回球) | — | 仅原型,不还原 | 不实现 |
| `.btn.lg`(登录/注册主按钮 h44/pad22/fs14 | `DsButton(large:true)` | 全宽居中 | 已有 |
| `.check` / `.agree`(自绘复选 18px+勾) | `DsCheck`ds_atoms | value/onChanged/Widget label/alignTop | 已有 |
| `.theme-switch`auth 页右上 A/B/C 圆钮) | `ThemePickerPill(onSurface:true)`(表单面板右上) | 2026-07-03 用户拍板:不用原型 A/B/C,复用主界面小衣服 pill 浅色变体 | 已有 |
| `.auth` + `.brand`(两栏卡片+品牌渐变面板) | `AuthPageScaffold`/`AuthBrandPanel` | 栏宽/窄屏折叠 | 已有 |
---
## 块 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` | ✅ KPI/徽章/列/副标/toolbar 已还原 | ✅ golden 自比 | ✅ golden 自比 |
| 库存列表(移动) | `m-inventory.html` | 同上窄屏(卡片流) | ✅ golden 自比 | ✅ golden 自比 | ✅ golden 自比 |
| 往来单位(桌面) | `partners.html` | `partners_screen.dart` | ✅ **Phase2 重建**:单表+类型 chips/应收应付列(finance summary)/详情抽屉/frow 弹窗;fidelity 2.53.5%≤8% | ✅ fidelity | ✅ fidelity |
| 往来单位(移动) | `m-partners.html` | 同上窄屏(卡片流) | ✅ golden 自比 | ✅ golden 自比 | ✅ golden 自比 |
| 基础数据(桌面) | `products.html` | `products_screen.dart` | ✅ **Phase2 重建**.seg 5 tab(档案/介绍/系列/规格/仓库)+三套视图+编辑抽屉复用;fidelity 2.43.5%≤8%(已知差异见下) | ✅ fidelity | ✅ fidelity |
| 基础数据(移动) | `m-products.html` | 同上窄屏(卡片流) | ✅ golden 自比 | ✅ golden 自比 | ✅ golden 自比 |
| 入库列表(桌/移) | `stock-in-list.html` | `stock_in_list_screen.dart` | ✅ 副标 + 共享 StatusPill(草稿/待审/已审/已拒) | ✅ golden | ✅ golden |
| 出库列表(桌/移) | `stock-out-list.html` | `stock_out_list_screen.dart` | ✅ 副标 + 共享 StatusPill | ✅ golden | ✅ golden |
| 财务(桌/移) | `finance.html` | `finance_screen.dart` | ✅ **Phase2 重建**:时间范围 chips(真实过滤)+KPI 4 卡(stock summary 环比)+收支趋势柱状图(DsBarChart, /finance/trend)+应收应付汇总(/finance/summary)+流水表+往来抽屉+登记收支(补往来单位)fidelity 3.04.2%≤8% | ✅ fidelity | ✅ fidelity |
| 设备管理(桌/移) | `devices.html` | `device_management_screen.dart` | ✅ **Phase2 重建**:会话表(DsTable)+外设卡网格(custom_fields.peripherals 本地存档)+打印模板;fidelity 2.43.0%≤8% | ✅ fidelity | ✅ fidelity |
| 系统设置(桌/移) | `settings.html` | `settings_screen.dart` | ✅ **Phase2 重建**:subnav 5 面板(门店/用户预览/编号规则/授权兑换券/偏好);假「系统参数」已删;fidelity 1.72.3%≤8% | ✅ fidelity | ✅ fidelity |
| 用户管理(桌/移) | `users.html` | `users_screen.dart``/settings/users` | ✅ **Phase2 新独立页**KPI 4 卡+搜索/角色筛选+rcard 弹窗,角色四级拉平;fidelity 1.32.1%≤8% | ✅ fidelity | ✅ fidelity |
| 关于我们(桌/移) | `about.html` | `about_screen.dart` | ✅ **Phase2 重建**:Hero/产品信息/授权信息/更新日志 timeline(/public/release 同源)fidelity 2.22.7%≤8% | ✅ fidelity | ✅ fidelity |
| 登录 | `login.html` | `login_screen.dart` | ✅ **Phase2 重建(2026-07-03**:两栏卡片(品牌渐变面板+表单)+右上主题切换器+记住我/忘记密码+lg 主按钮;fidelity 1.42.1%≤8%(已知差异见下) | ✅ fidelity | ✅ fidelity |
| 注册新门店 | `register.html` | `register_screen.dart` | ✅ **Phase2 重建(2026-07-03**:品牌面板三步时间线+grid2 表单+协议勾选;**暂不入 fidelity 闸**(少 门店编号/兑换券 两字段致结构错位,用户拍板先不加,见下) | ✅ golden ×3 | ✅ golden ×3 |
> **共享组件杠杆**`StatusBadge`(入/出库单据状态)一处升级两屏受益;`StatusPill`/`KpiCard` 现已被库存/往来/入出库/财务/设备/设置/关于复用——新屏直接套,无需重做徽章/卡片。
### 全量清扫收尾(其余屏状态)
| 屏 | 状态 |
|---|---|
| 登录/注册 | ✅ **Phase2 已照原型重建**login.html/register.html,见上验收表) |
| 盘点 | ✅ 斑马行 token 化(表单屏,0 残留,无 golden:静态态不含明细) |
| 商品列表(基础数据) | ✅ **Phase2 已照原型重建**(.seg 5 tab,见上验收表);default 金星 FFB300 为有意 accent |
| 商品详情 | ✅ 主数据/价格/描述区 token 净;原型 3 KPI/仓库分布/流水 **待后端聚合** |
| 公开商品/门店页 | ✅ **有意独立设计**warm-paper/酒红 bespoke 调色板 `_kPaper/_kBurgundy…`),顾客扫码固定品牌页,不参与 A/B/C 主题——保持现状 |
| Web 营销站 | ✅ **已对齐品牌**`web/assets/color.css``--brand-500:#2563AC` / `--accent` 酒红 / 灰阶 与原型令牌一致,roadmap「Web 镜像品牌」已满足 |
### 仍属「新建功能」(Phase 6,非重建既有屏)
应用内注册 / 购买·套餐·下单(设计+前端壳,**支付不接**)——这是 net-new 功能开发,需各自 spec→设计→实现,不在「既有屏设计语言升级」范围内。
> **Phase 2 已解决(2026-07-02**
> - **往来单位**:应收/应付列改由 `GET /finance/summary`(按 partner 聚合未结清)供给,两 Tab 改回原型的类型 chips 单表;详情抽屉近期单据走 `finance/records?partner_id=`。
> - **基础数据**:tab 集合按用户口径收敛为 5 个 **档案/介绍/系列/规格/仓库**(商品名称并入档案;产地/保质期/储存/香型撤管理入口,后端字典保留)。
> - **商品详情**`getDetail` 聚合已就绪(3 KPI/仓库分布/流水,见 product_detail golden)。
>
> **基础数据屏已知差异(数据/模型驱动,fidelity 残差包含它们)**
> - 档案无「分类」列、无香型筛选 chips(香型弃用);无「状态(在售/停用)」列(products 无 status 字段,用户拍板不加)。
> - 档案无「新增商品」按钮(product=序列号铁律:入库每行自动建档)。
> - 字典视图无「关联商品」计数列(无后端计数源);seg 第 5 tab 香型→仓库。
> - 档案 pager 用带控件分页(真实数据量大);介绍/字典/仓库 pager 仅文案(对齐原型)。
>
> **库存屏已知差异(2026-07-02 用户口径调整)**
> - 搜索合一:原型 名称/拼音(240)+编码(180) 两框 → 一框「商品名 / 拼音 / 编码」(后端 keyword 本就四列 LIKE),回车触发(原型 oninput 即搜)。
> - 工具栏「状态」筛选 chip 删除(用户裁定无用);状态列 + 列头漏斗 + KPI 缺货点击筛选保留。
> - reload 不再白屏:skipLoadingOnReload + DsLoadingScrim 半透明遮罩(与入库/出库屏一致)。
>
> **商品编辑抽屉(2026-07-03 用户增强)**
> - 「从介绍库选择」由原型 .pe-pick 普通菜单升级为**搜索下拉**GridComboCell 表单形态,供应商选择器同款:弹层搜索 + ✓ 选中 + 计数);收起态外观与原型一致。
>
> **出入库列表补闸 + 单元格对齐(2026-07-03**
> - stock-in/stock-out 此前**漏注册 fidelity**(偏差长期未被闸住)→ 桌面 golden 改外壳挂载并入册(阈值 9%,实测 3.0–5.2%)。
> - 单元格对齐原型:单号 = .pcode(faint+mono,弃蓝色链接样式);金额 = ¥+千分位无小数(td.num mono);审核员空值 = faint「—」。
> - 全局 InputDecorationTheme 渗漏修复:内嵌裸 TextField 的组合控件显式屏蔽主题边框(搜索框双层框根因)。旧 DataTableCard 组件已无引用,删除。
>
> **登录/注册屏重建(2026-07-03**
> - **主题切换(用户拍板)**:原型右上角 A/B/C 圆钮不用,改放主界面同款小衣服 `ThemePickerPill(onSurface:true)` 于表单面板右上角(宽窄屏都落白底)。
> - **登录已知差异(功能位保留)**:门店编号/账号历史下拉(后缀箭头)+ 密码可见切换(后缀眼睛)为原型没有的功能件;密码空值显示 hint(原型演示值 8 dots);服务器错误条/离线条/授权锁定引导卡在按钮上方按需出现。「记住我」= 记录并预填最近登录账号(关闭则不记录),「忘记密码」= toast 提示联系管理员。
> - **注册已知差异(用户拍板 2026-07-03:先不加)**:无「门店编号(留空自动分配)」「授权兑换券」两字段——后端 RegisterInput 暂不支持;门店地址必填(后端 binding:required,原型选填);密码下限 6 位(后端 min=6,原型文案 8 位)。表单因此少两行 → **fidelity 暂撤册**screens.mjs 有注释存根),golden ×3 锁回归;后端补 shop_code/voucher 后恢复入闸。
> - 校验口径 = 原型:提交时逐项 toast(DsToast),无内联错误文案,38 高盒式字段不抖动。
>
> **出入库定价重设计(2026-07-03 用户拍板,plan 批准)**
> - 出库详情明细:单价/金额 两列 → **成本价/售价/利润** 三列(管理员);operator 见 售价/小计。合计区双行:合计金额(总售价)+ 合计利润(管理员)。
> - 出库建单:金额列(=售价×数量,qty=1 与售价重复)→ **利润**列(实时,负数 danger);商品名称下带编码(字体同系列列);底栏加合计利润;进价/利润列仅管理员。
> - 入库建单列头:进价 → **进价(单瓶)**、售价 → **参考售价**、金额 → **总进价**。
> - 原型已同步(stock-in.js 双模式 + stock-out-list.html 详情抽屉),与真实页一致。
> - 底层字段消歧:unit_price/total_price/total_amount → cost_price/cost_amount/cost_total(入)/sale_amount/sale_total/profit_total(出),见 CLAUDE.md「定价字段口径」。
>
> **全局对话框统一(2026-07-03 用户要求遍历)**
> - 主题层钉真相源:`DialogTheme`surface/r-xl/边框/标题 fs-h2·700)、全局 `InputDecorationTheme`.input 盒式,禁下划线兜底)、Elevated/FilledButton 主题(.btn.primary 规格)。
> - 全部 AlertDialog 动作行换 `DsButton`(取消=ghost / 确认=primary / 删除·拒绝=danger / 通过=success / 撤回·结清=accent`DsBtnVariant` 扩 success/accent);表单字段改 `DsField`(label 在上)+ 盒式输入(添加设备等)。
> - 例外保留:`public_product_screen`(对外扫码页,自有品牌配色非 ds 体系);表格行内链接式 TextButton(备注/打标签/测试打印)非对话框不在此列。
>
> **出入库列表 KPI 口径(2026-07-03 用户拍板)**
> - 原型「本月入库/出库笔数·金额 + 较上月」自然月口径月初必然全 0(7-03 实测 0/¥0/▼100% 观感如坏数据)→ 卡片与头部小字改「近30天」滚动窗(`summary?window=rolling30`,对照前一个 30 天);财务屏仍用自然月口径(原型「本月销售/采购额」文案不变),同一接口两种 window。
>
> **财务屏已知差异(2026-07-02 Phase2 第 9 屏)**
> - KPI3/4 delta:原型「逾期 ¥9.2万 / 本周到期 ¥4.8万」无到期日数据源 → 改「未结清 N 笔 / 按到期及时结清」。
> - KPI 应收/应付值 = `/finance/summary` 实算求和(原型硬编码 ¥48.6万 与其汇总表行自身不一致)。
> - 流水类型 chips 原型 3 个扩到 5(含应收/应付),并追加 状态/操作 两列(结清能力不回退);关联单号列显示 `单据类型 #id`(原型 SK/FK 单号字符串无对应模型)。
> - 时间范围 chips 真实过滤流水(原型只改文案);趋势柱最高 140px(原型 ~157,标签悬浮方式不同)。
>
> **设备/设置/用户/关于 已知差异(2026-07-02 Phase2 第 6-8 屏)**
> - 设备:外设设备=登记式本地存档(店级 `custom_fields.peripherals`,无真实连接协议),测试打印/配置/模板预览为占位 toast;打印模板静态两卡。
> - 设置:子导航比原型多「编号规则」(真实功能保留);门店卡多 Logo 行;授权卡保留降级说明;偏好即时生效无「保存」按钮;原型「系统参数」假设置删除。
> - 用户:独立页挂 `/settings/users`(原型导航同样高亮系统设置);角色四级(后端为准),原型 settings 预览三级口径弃用;授权数据 settings/about 同源 licenseProvider(原型两处数字不一致)。
> - 关于:旧版 帮助文档/扫码防伪/系统信息 卡删除,构建号并入版本 cell;更新日志走 `/public/release`(与官网时间线同源),接口不可达回退只显当前版本。
**验收法(HTML→Flutter 特例)**HTML(Chromium) 与 Flutter 渲染器不同,**严格 0.1% pixelmatch 不适用跨渲染器**(见 design-distill `references/flutter-golden.md`)。本项目双闸:
1. **保真闸(人工目检)**`node tools/ds-compare.mjs --html .superpowers/prototype/screens/<screen>.html --prefix <prefix>` → 截原型基准 + 与 Flutter golden 用 montage 并排 → `design/_compare/`
2. **回归闸(自动)**`test/golden/<screen>_golden_test.dart``pumpGolden`/`goldenAcrossThemes``test/support/golden_harness.dart`)出 A/B/C 三主题 golden,入库随 `flutter test` 同渲染器自比,抓串色/漏 token。
3. token 残留闸:`grep 0xFF…` 该屏为 0(颜色全走 `context.tokens`)。
> **基建就位**(本轮):golden 骨架 + 整屏还原闸(库存列表已接)+ ds-compare 目检编排 + design-distill skill 补全(montage/flutter-golden/跨渲染器分流/多主题 codegen)。
> 后续屏(商品/单据/往来/财务/系统域)逐个追加块 3 规格 + 块 4 行 + 一份 `*_golden_test.dart`,一屏一闭环,禁积压。