① 设计真相源(design/prototype/)
tokens.css(三主题) + atoms.css / mobile-atoms.css + icons.js + index.html 登记
│ 新增颜色/组件/图标 → 必须先登记原型(L1 先行)
▼
② Token Codegen
token_source/tokens.css 快照 ──node client/tool/gen_tokens.mjs──▶ 3 个 .g.dart(禁手改)
▼
③ Flutter 实现(client/lib/)
widgets/ds/*(镜像 atoms 的组件层) → screens/*(页面层) → shell(外壳)
一切颜色走 context.tokens,禁硬编码 hex
▼
④ 像素验收
golden × 3 主题(回归自比) + fidelity 像素闸(同步态屏 vs 原型截图) + ds-compare 目检
▼
⑤ 静态闸(pre-commit + CI checks.yml)
原型 12 道 / L1 同源 4 道 / Flutter 颜色单源 / codegen 零 diff
核心思想:颜色、组件、图标只有一个出生地(原型),代码永远是镜像;镜像是否走样由两级验收兜底(golden 管回归、fidelity 管还原),闸负责把「绕过单源」的行为在提交/CI 时拦下。
Flutter 3.x 单代码库五端(Web→/app、macOS、Windows、Android、iOS),状态管理 Riverpod,网络 Dio(401 自动刷新 + 5xx 自动上报),路由 go_router。
| 目录 | 职责 |
|---|---|
| client/lib/core/ | 基建:api/(Dio 封装/重试) · auth/(auth_state,isAdminProvider/isReadonlyProvider) · config/(app_config 全部 URL 单源、store_compliance iOS 合规开关) · theme/(见第五章) · responsive/(context.isMobile/dialogWidth) · router/ · storage/(列偏好/登录历史) · update/(自更新) · utils/(打印/导出/日期等) |
| client/lib/models/ | 领域模型 15 个(inventory/product/stock_in/stock_out/finance/license/…) |
| client/lib/providers/ | Riverpod 状态层 18 个(列表 Notifier 持筛选/分页/排序状态,reload 保留旧数据不白屏) |
| client/lib/repositories/ | 数据访问层 15 个(组参数、DioException → AppException) |
| client/lib/screens/ | 21 屏:库存(列表/盘点)、出入库(列表/录单)、财务、往来、基础数据(列表/详情)、设备、设置(含授权面板/购买卡/用户)、我的(hub/授权)、关于、登录/注册、公开页(商品/店铺)、外壳 app_shell、共享 order_form_shell |
| client/lib/widgets/ds/ | 设计系统组件层(镜像原型 atoms),见下表 |
| client/lib/widgets/ | 业务复合组件:product_editor_drawer(商品抽屉)、order_detail_drawer、combo_search_field、theme_picker_pill(衬衫主题切换)、write_guard(只读拦截)、wheel_date_picker、privacy_consent_gate(iOS 隐私弹窗) 等 |
| 组件 | 用途(镜像的原型原子) |
|---|---|
| ds_atoms.dart | 原子合集:DsButton(primary/ghost/danger/…)、DsBadge(6 tone)、DsChip(筛选)、DsSeg、DsInput/DsField/DsSelect、DsCheck、DsSearchBox、DsLoadingScrim |
| ds_table.dart | 列表屏真相源表格:toolbar+表格+分页连成一卡;DsColumn 列定义;DsFilterHeader 漏斗列头、DsSortHeader 排序列头(2026-07-07) |
| ds_kpi.dart / ds_bar_chart.dart | KPI 卡 / 财务分组柱状图 |
| ds_menu.dart / ds_toast.dart / ds_switch.dart | 下拉菜单 / Toast / 开关(switch 2026-07-07 登记,库存挂售用) |
| grid_combo_cell.dart | 录单网格内的组合搜索单元格 |
| m_card / m_kpi_grid / m_sheet / m_search_row / m_tab_bar / m_hub | 移动范式组件族(见第八章) |
| status_icon_map.dart | 状态词→徽章图标映射(转录原型 BADGE_ICON,30 词,新增状态先登记原型 icons.js 再同步) |
| 层 | 范围 | 规则 |
|---|---|---|
| L1 | 设计系统层:tokens.css / atoms.css / mobile-atoms.css / icons.js + index.html 登记 | 原型是单一真源。新增颜色/组件/图标必须先登记原型,再同步 Flutter 与官网(icons.njk)。闸:pre-commit 12 道 + check_ds_code + check-l1-sync |
| L2 | 屏级三态(台账 = CONTRACT.md 块 4「真相源」列) | 同步=注册于 screens.mjs,fidelity 像素闸生效,整屏改版原型先行、小迭代改后跑闸;快照=原型退役,golden+文字规格为准,不回填原型;代码先行=无原型屏,golden 为唯一基准 |
| L3 | 新屏 / 整屏改版 | design-first:原型 → CONTRACT → 实现 → fidelity 验收,建成后入「同步」态 |
屏归属现状:同步态覆盖绝大多数(库存/往来/基础数据/入出库列表/财务/设备/设置/用户/关于/登录/移动壳/我的/授权/盘点);快照态仅注册页;代码先行 = 授权面板、购买卡及官网若干页;拍板不做 = 移动端建单表单。漂移体检:不逐提交强制同步,大版本前跑 node tools/fidelity.mjs 按报告逐屏决定重新对齐或降级快照。
:root 共享标量(间距 sp-*/圆角 r-*/字号 fs-*/字体/阴影/z 阶梯/品牌色/状态栏色)+ 三主题块 [data-theme=a/b/c](A 经典蓝浅 / B 琥珀深 / C 酒窖暖浅)。屏与组件只准引 var(--token)。m-*.html + 公开页(public-product/pay/checkout…)+ 共享 JS(shell.js 外壳、mobile-shell.js 移动壳、icons.js 图标 sprite 单源、datewheel.js 滚轮日期、product-editor.js 商品抽屉…)。serve.mjs(5180 端口)给 URL 用户自己看。design/prototype/tokens.css ← 原型真源 │ (check-l1-sync 闸①:逐字节一致) client/lib/core/theme/token_source/tokens.css ← 入库快照 │ node client/tool/gen_tokens.mjs ├─ app_tokens.g.dart kTokensA/B/C 三主题色(AppTokens 字段) ├─ app_dims.g.dart AppDims 标量(sp/r/fs,主题无关) └─ app_chrome.g.dart AppChrome 主题无关色(状态栏/品牌/遮罩/toast)
app_tokens.dart(ThemeExtension 定义)、context_tokens.dart(context.tokens 入口)、themes.dart(buildTheme 挂三套 ThemeData)、theme_controller.dart(切换持久化)、app_fonts.dart(NotoSansSC/JetBrainsMono)。Color(0x…)/Colors.x(check_ds_code 闸,行级 // ds-ignore: 理由 豁免,公开页两文件整文件豁免)。| 闸 | 挂载 | 查什么 |
|---|---|---|
| check-ds.mjs (原型 12 道) | pre-commit(动原型才跑) + CI | ① 硬编码色(白名单 #fff/#1677ff/#07c160,ds-allow 豁免)② 未定义 token ③ 未登记组件类 ④ 字号未走 --fs-* ⑤ z-index 未走阶梯 ⑥ fork 已登记原子 ⑦ 缺 tokens/atoms 引入 ⑧ 图标未走 sprite ⑨ 圆角未走 --r-* ⑩ 字体未走 --font* ⑪ 断点魔法数(仅 600/760/1080)⑫ 组件未登记 index.html |
| check-l1-sync.mjs (L1 同源 4 道) | CI | ① tokens 快照与原型逐字节一致 ② icons.js 与官网 icons.njk 同集同内容 ③ 官网 tokens.css 值对齐原型主题 A ④ web/ 无硬编码 hex |
| check_ds_code.mjs | CI + 本地 | Flutter 代码颜色单源:lib/**/*.dart 禁 Color(0x…)/Colors.x(排除 .g.dart;--strict 违规即败、--changed 只查改动) |
| codegen 新鲜度 | CI | regen → dart format → 与入库 .g.dart 零 diff(防手改/防快照漂移) |
| flutter analyze / test | DoD | 静态分析无 error(warning/info 允许)+ 全部测试过;测试未过禁提交禁发版 |
CI 入口:.gitea/workflows/checks.yml(PR + main push,mac runner,纯静态四步——golden/fidelity 重型像素闸不进 CI,本地手动跑)。本地钩子:新机器先 sh scripts/hooks/install.sh。
test/support/golden_harness.dart——ensureGoldenFonts() 装真字体消豆腐块;goldenAcrossThemes() 同一屏跑 a/b/c 三主题;shell_harness.dart 把屏挂进真 AppShell(顶栏+侧栏+状态栏,冻结时钟 2026-07-13 09:30:15、固定门店/用户 fixtures)出整框图。*_golden_test.dart,命名 <prefix>_<a|b|c>.png,基准入库 goldens/;改 UI 后 flutter test --update-goldens <file> 重录。golden 测试注意钉死动态值(日期用 initialDate 注入、角色用 isAdminProvider override——2026-07 两次踩坑教训)。tools/screens.mjs = 屏注册表单源(html/prefix/尺寸/阈值/zones 分区);tools/fidelity.mjs 截原型(注入同款字体)与 golden 做 pixelmatch,超逐屏阈值(多为 8%,实测残差 1.3–4.2%)即败。tools/ds-compare.mjs:原型与 golden 上下拼图人工目检(跨渲染器不做严格 pixelmatch 的兜底)。context.isMobile(<600,responsive.dart),禁散落 MediaQuery 魔法数;弹窗宽度一律 context.dialogWidth(X)。showMSheet 底部 sheet(详情/筛选/详搜/表单同一形态);列表 = MKpiGrid(2×2 可点 KPI) + MSearchRow + MCard 图标徽章卡片流。mobile_list_card.dart(label:value 竖排字段卡)与新 m_card.dart(镜像 mobile-atoms)并存,新屏用 MCard。| 规则 | 要点 |
|---|---|
| 前端 DoD | flutter analyze 无 error + flutter test 全过,未过禁提交禁发版 |
| URL 配置 | 一切后端地址走 AppConfig,禁硬编码 localhost/IP/域名 |
| 平台判断 | 用 Platform 前必查 kIsWeb(Web 无 dart:io) |
| 表格 | 列表屏统一 DsTable;筛选入口在工具栏 chip 或列头(2026-07-07 库存筛选整体上移工具栏、列头留排序);旧 FilterableColumnHeader 勿新用 |
| 成本可见性 | 成本/利润/货值仅管理员可见——服务端抹除 + 前端隐藏两侧都要做(stripStockOutCost/stripInventoryCost + isAdminProvider) |
| 拼音搜索 | 「页面能看到的就能搜到」;商品名支持汉字/全拼/首字母(后端 name_pinyin/name_initials) |
| 异常上报 | main.dart 与 Dio 拦截器已全局捕获;业务层只对技术性异常手动 reportError(e, st),AppException 不上报 |
client/;ui-designer 只出规范/设计文档;test-engineer 只写测试;linter 只做格式修复。前端小改流水线 = flutter-coder → linter。client-v*,patch+1 保持 1.1.x),mac runner 串行 Web→macOS→Android→iOS + Windows 并行;version.yaml 归 client,官网下载页/后端 /version 自动同步。golden 重录随功能 commit 一起入库。| 文档 | 内容 |
|---|---|
| design/CONTRACT.md | 设计契约执行真相源:token 映射 / 组件清单 / 页面像素规格 / 块 4 逐屏台账(三态归属) |
| docs/manual/dev-manual.html §3 | 开发手册前端章:目录分层 / 数据流 / ds 设计系统 / golden+fidelity / 响应式 |
| docs/context/project.md | Agent 必读项目全貌(前端结构/配置/表格规范段) |
| docs/plans/mobile-screens-implementation.html | 移动端全屏落地实现计划(2026-07-04 已执行,移动范式的出处) |
| docs/design/*.html | 功能级设计:库存三态 / 公开页提速 / 打印排版 / 退单原型 / 进价确认 等 |
| docs/review/flutter-layout-bugs.md 等 | 历史评审与 bug 清单 |
lib/core/theme/tool/,实际在 client/tool/gen_tokens.mjs;② CLAUDE.md「响应式」段仍写窄屏 Drawer 抽屉导航,实际 2026-07-04 起已是底部 5 tab + 我的 hub。