Files
jiu/docs/frontend-overview.html
T
wangjia 05203a9b5b
Design Source Checks / design-source (push) Failing after 14m23s
refactor: 原型迁移 .superpowers/prototype → design/prototype(与 CONTRACT 同目录)
- git mv 全目录(历史保留);.gitignore 收敛为整个 .superpowers/ 忽略
- 全仓 16 处引用同步(CI checks.yml / l1-sync / screens.mjs / hooks /
  CLAUDE.md / CONTRACT / SSR 模板注释 / docs / web 注释)
- 修 pre-commit 路径正则残留;5180 评审服务已切新路径
- 验证:check-ds 12 道 / l1-sync 6 道 / fidelity 抽查全过

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
2026-07-07 18:47:31 +08:00

228 lines
20 KiB
HTML
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.
<!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; --danger-bg:#FDECEC;
--success:#2E8B57; --success-bg:#E6F3EC; --warn:#B45309; --warn-bg:#FFF4E5; --accent:#8B2331;
--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.65;max-width:1080px;margin:0 auto;}
h1{font-size:22px;margin:0 0 4px;}
h2{font-size:17px;margin:30px 0 12px;color:var(--primary-dark);border-bottom:2px solid var(--head);padding-bottom:6px;}
h3{font-size:14px;margin:18px 0 8px;color:var(--accent);}
.sub{color:var(--muted);font-size:13px;margin-bottom:18px;}
.card{background:#fff;border:1px solid var(--border);border-radius:10px;padding:16px 20px;margin:14px 0;}
.lead{font-size:13.5px;color:var(--ink);}
table{width:100%;border-collapse:collapse;font-size:13px;margin:8px 0;}
th{background:var(--head);color:var(--primary-dark);font-weight:600;font-size:12px;text-align:left;padding:9px 10px;border-bottom:1px solid var(--border);}
td{padding:8px 10px;border-bottom:1px solid #EEF1F5;vertical-align:top;}
.mono{font-family:ui-monospace,Menlo,monospace;font-size:12.5px;}
code{font-family:ui-monospace,Menlo,monospace;font-size:12px;background:#EEF2F8;padding:1px 5px;border-radius:4px;color:var(--primary-dark);}
pre{background:#1E2430;color:#D8E0EC;border-radius:8px;padding:12px 14px;overflow:auto;font-size:12px;line-height:1.6;}
pre b{color:#8CC7FF;}
.tag{display:inline-block;font-size:11px;padding:1px 8px;border-radius:10px;font-weight:600;}
.tag.ok{background:var(--success-bg);color:var(--success);}
.tag.info{background:var(--head);color:var(--primary);}
.tag.warn{background:var(--warn-bg);color:var(--warn);}
.tag.gray{background:#EEF1F5;color:var(--muted);}
.callout{border-left:4px solid var(--primary);background:#F0F6FF;padding:10px 14px;border-radius:4px;margin:12px 0;font-size:13px;}
.callout.warn{border-color:var(--warn);background:var(--warn-bg);}
ul,ol{margin:6px 0;padding-left:22px;}
li{margin:3px 0;font-size:13px;}
.toc{columns:2;font-size:13px;}
.toc a{color:var(--primary);text-decoration:none;}
</style>
</head>
<body>
<h1>jiu 前端开发全景</h1>
<div class="sub">2026-07-07 编制 · 覆盖:设计系统 / 工具链 / 各道闸 / 规则 / 移动范式 / 验收体系 · 单一入口速查</div>
<div class="card toc">
<b>目录</b>
<ol>
<li><a href="#s1">总览:一次 UI 改动的标准路径</a></li>
<li><a href="#s2">技术栈与目录地图</a></li>
<li><a href="#s3">设计真相源三层分治(L1/L2/L3</a></li>
<li><a href="#s4">原型 design/prototype</a></li>
<li><a href="#s5">Token Codegen(颜色单源)</a></li>
<li><a href="#s6">各道闸全景</a></li>
<li><a href="#s7">像素验收体系(golden / fidelity</a></li>
<li><a href="#s8">响应式与移动端范式</a></li>
<li><a href="#s9">规则与开发流程</a></li>
<li><a href="#s10">文档索引与已知滞后</a></li>
</ol>
</div>
<h2 id="s1">一、总览:一次 UI 改动的标准路径</h2>
<div class="card lead">
<pre>
<b>① 设计真相源</b>design/prototype/
tokens.css(三主题) + atoms.css / mobile-atoms.css + icons.js + index.html 登记
│ 新增颜色/组件/图标 → <b>必须先登记原型</b>L1 先行)
<b>② Token Codegen</b>
token_source/tokens.css 快照 ──node client/tool/gen_tokens.mjs──▶ 3 个 .g.dart(禁手改)
<b>③ Flutter 实现</b>client/lib/
widgets/ds/*(镜像 atoms 的组件层) → screens/*(页面层) → shell(外壳)
一切颜色走 context.tokens,禁硬编码 hex
<b>④ 像素验收</b>
golden × 3 主题(回归自比) + fidelity 像素闸(同步态屏 vs 原型截图) + ds-compare 目检
<b>⑤ 静态闸</b>pre-commit + CI checks.yml
原型 12 道 / L1 同源 4 道 / Flutter 颜色单源 / codegen 零 diff
</pre>
<p>核心思想:<b>颜色、组件、图标只有一个出生地(原型),代码永远是镜像</b>;镜像是否走样由两级验收兜底(golden 管回归、fidelity 管还原),闸负责把「绕过单源」的行为在提交/CI 时拦下。</p>
</div>
<h2 id="s2">二、技术栈与目录地图</h2>
<div class="card lead">
<p><b>Flutter 3.x 单代码库五端</b>Web→<code>/app</code>、macOS、Windows、Android、iOS),状态管理 Riverpod,网络 Dio401 自动刷新 + 5xx 自动上报),路由 go_router。</p>
<table>
<tr><th style="width:26%">目录</th><th>职责</th></tr>
<tr><td class="mono">client/lib/core/</td><td>基建:<code>api/</code>(Dio 封装/重试) · <code>auth/</code>(auth_stateisAdminProvider/isReadonlyProvider) · <code>config/</code>(app_config 全部 URL 单源、store_compliance iOS 合规开关) · <code>theme/</code>(见第五章) · <code>responsive/</code>(context.isMobile/dialogWidth) · <code>router/</code> · <code>storage/</code>(列偏好/登录历史) · <code>update/</code>(自更新) · <code>utils/</code>(打印/导出/日期等)</td></tr>
<tr><td class="mono">client/lib/models/</td><td>领域模型 15 个(inventory/product/stock_in/stock_out/finance/license/…)</td></tr>
<tr><td class="mono">client/lib/providers/</td><td>Riverpod 状态层 18 个(列表 Notifier 持筛选/分页/排序状态,reload 保留旧数据不白屏)</td></tr>
<tr><td class="mono">client/lib/repositories/</td><td>数据访问层 15 个(组参数、DioException → AppException</td></tr>
<tr><td class="mono">client/lib/screens/</td><td>21 屏:库存(列表/盘点)、出入库(列表/录单)、财务、往来、基础数据(列表/详情)、设备、设置(含授权面板/购买卡/用户)、我的(hub/授权)、关于、登录/注册、公开页(商品/店铺)、外壳 app_shell、共享 order_form_shell</td></tr>
<tr><td class="mono">client/lib/widgets/ds/</td><td>设计系统组件层(镜像原型 atoms),见下表</td></tr>
<tr><td class="mono">client/lib/widgets/</td><td>业务复合组件:product_editor_drawer(商品抽屉)、order_detail_drawer、combo_search_field、theme_picker_pill(衬衫主题切换)、write_guard(只读拦截)、wheel_date_picker、privacy_consent_gate(iOS 隐私弹窗) 等</td></tr>
</table>
<h3>ds 组件清单(widgets/ds/,全部镜像原型 atoms</h3>
<table>
<tr><th style="width:24%">组件</th><th>用途(镜像的原型原子)</th></tr>
<tr><td class="mono">ds_atoms.dart</td><td>原子合集:DsButton(primary/ghost/danger/…)、DsBadge(6 tone)、DsChip(筛选)、DsSeg、DsInput/DsField/DsSelect、DsCheck、DsSearchBox、DsLoadingScrim</td></tr>
<tr><td class="mono">ds_table.dart</td><td><b>列表屏真相源表格</b>:toolbar+表格+分页连成一卡;DsColumn 列定义;DsFilterHeader 漏斗列头、DsSortHeader 排序列头(2026-07-07)</td></tr>
<tr><td class="mono">ds_kpi.dart / ds_bar_chart.dart</td><td>KPI 卡 / 财务分组柱状图</td></tr>
<tr><td class="mono">ds_menu.dart / ds_toast.dart / ds_switch.dart</td><td>下拉菜单 / Toast / 开关(switch 2026-07-07 登记,库存挂售用)</td></tr>
<tr><td class="mono">grid_combo_cell.dart</td><td>录单网格内的组合搜索单元格</td></tr>
<tr><td class="mono">m_card / m_kpi_grid / m_sheet / m_search_row / m_tab_bar / m_hub</td><td>移动范式组件族(见第八章)</td></tr>
<tr><td class="mono">status_icon_map.dart</td><td>状态词→徽章图标映射(转录原型 BADGE_ICON,30 词,新增状态先登记原型 icons.js 再同步)</td></tr>
</table>
</div>
<h2 id="s3">三、设计真相源三层分治(2026-07-04 拍板)</h2>
<div class="card lead">
<table>
<tr><th style="width:12%"></th><th style="width:30%">范围</th><th>规则</th></tr>
<tr><td><span class="tag info">L1</span></td><td>设计系统层:tokens.css / atoms.css / mobile-atoms.css / icons.js + index.html 登记</td><td><b>原型是单一真源</b>。新增颜色/组件/图标必须先登记原型,再同步 Flutter 与官网(icons.njk)。闸:pre-commit 12 道 + check_ds_code + check-l1-sync</td></tr>
<tr><td><span class="tag ok">L2</span></td><td>屏级三态(台账 = CONTRACT.md 块 4「真相源」列)</td><td><b>同步</b>=注册于 screens.mjsfidelity 像素闸生效,整屏改版原型先行、小迭代改后跑闸;<b>快照</b>=原型退役,golden+文字规格为准,不回填原型;<b>代码先行</b>=无原型屏,golden 为唯一基准</td></tr>
<tr><td><span class="tag warn">L3</span></td><td>新屏 / 整屏改版</td><td>design-first:原型 → CONTRACT → 实现 → fidelity 验收,建成后入「同步」态</td></tr>
</table>
<p><b>屏归属现状</b>:同步态覆盖绝大多数(库存/往来/基础数据/入出库列表/财务/设备/设置/用户/关于/登录/移动壳/我的/授权/盘点);快照态仅注册页;代码先行 = 授权面板、购买卡及官网若干页;拍板不做 = 移动端建单表单。<b>漂移体检</b>:不逐提交强制同步,大版本前跑 <code>node tools/fidelity.mjs</code> 按报告逐屏决定重新对齐或降级快照。</p>
</div>
<h2 id="s4">四、原型(design/prototype/</h2>
<div class="card lead">
<ul>
<li><b>tokens.css</b>:令牌单源。<code>:root</code> 共享标量(间距 sp-*/圆角 r-*/字号 fs-*/字体/阴影/z 阶梯/品牌色/状态栏色)+ 三主题块 <code>[data-theme=a/b/c]</code>(A 经典蓝浅 / B 琥珀深 / C 酒窖暖浅)。屏与组件只准引 <code>var(--token)</code></li>
<li><b>atoms.css / mobile-atoms.css</b>:桌面/移动组件原子(.btn/.badge/.chip/.table/.switch/.m-card/.m-sheet…)。</li>
<li><b>index.html</b><b>组件活文档 + 登记簿</b>——每个组件原子必须在此有展示卡(闸 #12 强制),也是主题切换/品牌规范/字阶圆角标尺的展示页。</li>
<li><b>screens/</b>:桌面屏 html + 移动 <code>m-*.html</code> + 公开页(public-product/pay/checkout…)+ 共享 JSshell.js 外壳、mobile-shell.js 移动壳、icons.js 图标 sprite 单源、datewheel.js 滚轮日期、product-editor.js 商品抽屉…)。</li>
<li><b>评审流</b>:改完原型不截图,起 <code>serve.mjs</code>(5180 端口)给 URL 用户自己看。</li>
</ul>
</div>
<h2 id="s5">五、Token Codegen(颜色单源落地 Flutter</h2>
<div class="card lead">
<pre>
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)
</pre>
<ul>
<li>手写层:<code>app_tokens.dart</code>(ThemeExtension 定义)、<code>context_tokens.dart</code>(<code>context.tokens</code> 入口)、<code>themes.dart</code>(buildTheme 挂三套 ThemeData)、<code>theme_controller.dart</code>(切换持久化)、<code>app_fonts.dart</code>(NotoSansSC/JetBrainsMono)。</li>
<li>铁律:<b>.g.dart 禁手改</b>CI 有 regen 零 diff 闸);<b>业务代码禁 <code>Color(0x…)</code>/<code>Colors.x</code></b>check_ds_code 闸,行级 <code>// ds-ignore: 理由</code> 豁免,公开页两文件整文件豁免)。</li>
</ul>
</div>
<h2 id="s6">六、各道闸全景</h2>
<div class="card lead">
<table>
<tr><th style="width:22%"></th><th style="width:14%">挂载</th><th>查什么</th></tr>
<tr><td class="mono">check-ds.mjs<br>(原型 12 道)</td><td>pre-commit(动原型才跑)<br>+ CI</td><td>① 硬编码色(白名单 #fff/#1677ff/#07c160<code>ds-allow</code> 豁免)② 未定义 token ③ 未登记组件类 ④ 字号未走 --fs-* ⑤ z-index 未走阶梯 ⑥ fork 已登记原子 ⑦ 缺 tokens/atoms 引入 ⑧ 图标未走 sprite ⑨ 圆角未走 --r-* ⑩ 字体未走 --font* ⑪ 断点魔法数(仅 600/760/1080)⑫ 组件未登记 index.html</td></tr>
<tr><td class="mono">check-l1-sync.mjs<br>L1 同源 4 道)</td><td>CI</td><td>① tokens 快照与原型逐字节一致 ② icons.js 与官网 icons.njk 同集同内容 ③ 官网 tokens.css 值对齐原型主题 A ④ web/ 无硬编码 hex</td></tr>
<tr><td class="mono">check_ds_code.mjs</td><td>CI + 本地</td><td>Flutter 代码颜色单源:lib/**/*.dart 禁 Color(0x…)/Colors.x(排除 .g.dart--strict 违规即败、--changed 只查改动)</td></tr>
<tr><td class="mono">codegen 新鲜度</td><td>CI</td><td>regen → dart format → 与入库 .g.dart 零 diff(防手改/防快照漂移)</td></tr>
<tr><td class="mono">flutter analyze / test</td><td>DoD</td><td>静态分析无 errorwarning/info 允许)+ 全部测试过;<b>测试未过禁提交禁发版</b></td></tr>
</table>
<p>CI 入口:<code>.gitea/workflows/checks.yml</code>PR + main pushmac runner,纯静态四步——golden/fidelity 重型像素闸不进 CI,本地手动跑)。本地钩子:新机器先 <code>sh scripts/hooks/install.sh</code></p>
</div>
<h2 id="s7">七、像素验收体系</h2>
<div class="card lead">
<h3>golden × 3 主题(回归闸,flutter test 同渲染器自比)</h3>
<ul>
<li>harness<code>test/support/golden_harness.dart</code>——<code>ensureGoldenFonts()</code> 装真字体消豆腐块;<code>goldenAcrossThemes()</code> 同一屏跑 a/b/c 三主题;<code>shell_harness.dart</code> 把屏挂进真 AppShell(顶栏+侧栏+状态栏,冻结时钟 2026-07-13 09:30:15、固定门店/用户 fixtures)出整框图。</li>
<li>约 30 个 <code>*_golden_test.dart</code>,命名 <code>&lt;prefix&gt;_&lt;a|b|c&gt;.png</code>,基准入库 <code>goldens/</code>;改 UI 后 <code>flutter test --update-goldens &lt;file&gt;</code> 重录。golden 测试注意钉死动态值(日期用 initialDate 注入、角色用 isAdminProvider override——2026-07 两次踩坑教训)。</li>
</ul>
<h3>fidelity(还原保真闸,桌面同步态屏)</h3>
<ul>
<li><code>tools/screens.mjs</code> = 屏注册表单源(html/prefix/尺寸/阈值/zones 分区);<code>tools/fidelity.mjs</code> 截原型(注入同款字体)与 golden 做 pixelmatch,超逐屏阈值(多为 8%,实测残差 1.3–4.2%)即败。</li>
<li><code>tools/ds-compare.mjs</code>:原型与 golden 上下拼图人工目检(跨渲染器不做严格 pixelmatch 的兜底)。</li>
<li>分流原则:Web 同 Chromium 走严格 diff<b>Flutter 跨渲染器只做 montage 目检 + golden 自比</b>;移动 m-* 屏不入 fidelity(字体度量漂移大),golden ×3 即验收。</li>
</ul>
</div>
<h2 id="s8">八、响应式与移动端范式</h2>
<div class="card lead">
<ul>
<li>断点唯一入口 <code>context.isMobile</code>&lt;600responsive.dart),禁散落 MediaQuery 魔法数;弹窗宽度一律 <code>context.dialogWidth(X)</code></li>
<li><b>移动壳(2026-07-04 落地,Drawer 退役)</b>:底部 5 tab(库存/入库/出库/财务/我的)+「我的」hub 聚合二级屏(往来/基础数据/盘点/用户/授权/设备/设置/关于);二级屏隐藏 tabbar、顶栏返回;方向感知滑动过渡 + 横滑手势(tab 根左右滑切页、二级左滑退出)。</li>
<li>一切弹层窄屏统一 <code>showMSheet</code> 底部 sheet(详情/筛选/详搜/表单同一形态);列表 = <code>MKpiGrid</code>(2×2 可点 KPI) + <code>MSearchRow</code> + <code>MCard</code> 图标徽章卡片流。</li>
<li>移动端<b>不提供建单入口</b>(无 +/FAB)、<b>无打印动作</b>(拍板)。</li>
<li>桌面/移动同组件策略:抽屉(product_editor_drawer)等复合组件内部按 isMobile 分叉为 dialog/sheet,业务逻辑一份——库存挂售 switch 一处实现两端生效即此模式。</li>
<li>存量说明:<code>mobile_list_card.dart</code>label:value 竖排字段卡)与新 <code>m_card.dart</code>(镜像 mobile-atoms)并存,新屏用 MCard。</li>
</ul>
</div>
<h2 id="s9">九、规则与开发流程</h2>
<div class="card lead">
<h3>CLAUDE.md 前端硬规则(速查)</h3>
<table>
<tr><th style="width:26%">规则</th><th>要点</th></tr>
<tr><td>前端 DoD</td><td><code>flutter analyze</code> 无 error + <code>flutter test</code> 全过,未过禁提交禁发版</td></tr>
<tr><td>URL 配置</td><td>一切后端地址走 <code>AppConfig</code>,禁硬编码 localhost/IP/域名</td></tr>
<tr><td>平台判断</td><td><code>Platform</code> 前必查 <code>kIsWeb</code>Web 无 dart:io</td></tr>
<tr><td>表格</td><td>列表屏统一 <code>DsTable</code>;筛选入口在工具栏 chip 或列头(2026-07-07 库存筛选整体上移工具栏、列头留排序);旧 FilterableColumnHeader 勿新用</td></tr>
<tr><td>成本可见性</td><td>成本/利润/货值仅管理员可见——<b>服务端抹除 + 前端隐藏两侧都要做</b>stripStockOutCost/stripInventoryCost + isAdminProvider</td></tr>
<tr><td>拼音搜索</td><td>「页面能看到的就能搜到」;商品名支持汉字/全拼/首字母(后端 name_pinyin/name_initials</td></tr>
<tr><td>异常上报</td><td>main.dart 与 Dio 拦截器已全局捕获;业务层只对技术性异常手动 <code>reportError(e, st)</code>AppException 不上报</td></tr>
</table>
<h3>流程与分工</h3>
<ul>
<li><b>agent 边界</b>flutter-coder 只动 <code>client/</code>ui-designer 只出规范/设计文档;test-engineer 只写测试;linter 只做格式修复。前端小改流水线 = flutter-coder → linter。</li>
<li><b>design-distill skill</b>(全局,像素级还原任务必用)五阶段:识别归一化 → 蒸馏 CONTRACT → token codegen → 实现 → 截图 diff 验收;红线 = 不读铁律动手 / 颜色不走 token / 不跑验收就宣称还原。</li>
<li><b>先出原型再写代码</b>:整屏新设计先原型(5180 评审)获批准后才实现;小迭代可代码先行但同步态屏须回补原型或降级快照。</li>
<li><b>发版</b>client 独立流水线(tag <code>client-v*</code>patch+1 保持 1.1.x),mac runner 串行 Web→macOS→Android→iOS + Windows 并行;version.yaml 归 client,官网下载页/后端 /version 自动同步。golden 重录随功能 commit 一起入库。</li>
</ul>
</div>
<h2 id="s10">十、文档索引与已知滞后</h2>
<div class="card lead">
<table>
<tr><th style="width:34%">文档</th><th>内容</th></tr>
<tr><td class="mono">design/CONTRACT.md</td><td>设计契约执行真相源:token 映射 / 组件清单 / 页面像素规格 / 块 4 逐屏台账(三态归属)</td></tr>
<tr><td class="mono">docs/manual/dev-manual.html §3</td><td>开发手册前端章:目录分层 / 数据流 / ds 设计系统 / golden+fidelity / 响应式</td></tr>
<tr><td class="mono">docs/context/project.md</td><td>Agent 必读项目全貌(前端结构/配置/表格规范段)</td></tr>
<tr><td class="mono">docs/plans/mobile-screens-implementation.html</td><td>移动端全屏落地实现计划(2026-07-04 已执行,移动范式的出处)</td></tr>
<tr><td class="mono">docs/design/*.html</td><td>功能级设计:库存三态 / 公开页提速 / 打印排版 / 退单原型 / 进价确认 等</td></tr>
<tr><td class="mono">docs/review/flutter-layout-bugs.md 等</td><td>历史评审与 bug 清单</td></tr>
</table>
<div class="callout warn"><b>已知文字滞后(待修正,不影响执行)</b>:① CONTRACT.md 与 check-l1-sync.mjs 注释中 gen_tokens 写旧路径 <code>lib/core/theme/tool/</code>,实际在 <code>client/tool/gen_tokens.mjs</code>;② CLAUDE.md「响应式」段仍写窄屏 Drawer 抽屉导航,实际 2026-07-04 起已是底部 5 tab + 我的 hub。</div>
</div>
</body>
</html>