refactor(client): 前端体系治理——漏斗退役/老卡片标 legacy/fidelity 收阈/文档口径修正

- DsFilterHeader 删除(筛选已全部上移工具栏 chips,屏内零引用)
- mobile_list_card 标 LEGACY 禁新用(6 屏迁移 MCard 登记 todo #13)
- fidelity 阈值按 2026-07-07 实测收紧:一刀切 8% → 各屏实测最大残差+2pp
  (4%~7%),新阈下 36 屏×主题全过;proto 基准同步重摄(含库存改版)
- CLAUDE.md 修正:表格筛选口径(工具栏 chips+列头仅排序)、响应式导航
  (Drawer 退役→底部 5 tab,修文字滞后)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
wangjia
2026-07-07 18:24:48 +08:00
parent 21a4a8cab7
commit d1b7e8e2e9
37 changed files with 23 additions and 51 deletions
+5 -5
View File
@@ -268,16 +268,16 @@ cd client && flutter test
- **漂移体检**:不逐提交强制同步;大版本/整屏改版前跑 `node tools/fidelity.mjs`,按报告逐屏决定「重新对齐原型」或「降级快照」,结果回填 CONTRACT。
- **CI 闸**`.gitea/workflows/checks.yml`PR + main push 自动跑):① `check-ds.mjs` 原型 12 道;② `tools/check-l1-sync.mjs` L1 同源四道(tokens 快照逐字节 / icons 两端同集同内容 / 官网 token 值对齐原型主题 A / web 硬编码 hex,白名单 #fff#1677ff`ds-allow` 可豁免);③ `client/tool/check_ds_code.mjs` Flutter 颜色单源;④ codegen 新鲜度(regen→`dart format`→与入库产物零 diff)。本地可直接跑同名命令。
### 表格与列头筛选(ds 真相源)
- 列表屏统一 `DsTable``widgets/ds/ds_table.dart`toolbar+表格+pager 连成一卡),列定义 `DsColumn`,可筛选列走其列头漏斗(`filtered` 态高亮),可隐藏列走列设置菜单
- 不要在 toolbar 放独立筛选按钮,筛选入口内嵌列头或工具栏 `DsChip`
### 表格与筛选/排序ds 真相源)
- 列表屏统一 `DsTable``widgets/ds/ds_table.dart`toolbar+表格+pager 连成一卡),列定义 `DsColumn`,可隐藏列走列设置菜单
- **筛选统一放工具栏 `DsChip`**2026-07-07 用户口径,形式参照出库管理:单选/多选 chip + 选中值/N 项摘要 + × 单清);列头**只放排序** `DsSortHeader`(点击循环 无→升→降→无,服务端全局排序)。列头漏斗 `DsFilterHeader` 已退役删除
-`FilterableColumnHeader`/`multi_select_dropdown.dart` 仅存量引用,勿在新屏使用
### 响应式 / 移动端适配
- 断点判定统一用 `context.isMobile``client/lib/core/responsive/responsive.dart`,宽度 < 600 为窄屏/手机),**禁止**在各处散落 `MediaQuery.width < 600` 这类魔法数
- **列表屏**:统一用 `DsTable``widgets/ds/ds_table.dart`,真相源表格:toolbar+列+pager);窄屏卡片流`MobileListCard` / `MobileCardField``widgets/mobile_list_card.dart`)。`DataTableCard` 已删除(2026-07-03),勿再引用
- **列表屏**:统一用 `DsTable``widgets/ds/ds_table.dart`,真相源表格:toolbar+列+pager);窄屏卡片流用 `MCard``widgets/ds/m_card.dart`,镜像原型 mobile-atoms)。旧 `MobileListCard` 为 legacy 仅存量引用(迁移在 todo),`DataTableCard` 已删除(2026-07-03),勿再引用
- **弹窗固定宽度**:一律用 `context.dialogWidth(X)`(≤ 屏宽 92%),**禁止**裸写 `width: <固定值>` 导致窄屏溢出
- **导航**:窄屏(手机)用 Drawer 抽屉(`app_shell.dart`),宽屏保持侧边栏;新增页面无需单独处理,挂在 shell 下即可
- **导航**:窄屏(手机)= 底部 5 tab(库存/入库/出库/财务/我的)+「我的」hub 聚合二级屏(`app_shell.dart` + `m_tab_bar.dart`2026-07-04 起,Drawer 已退役);宽屏保持侧边栏;一切弹层窄屏统一走 `showMSheet` 底部 sheet
- 平台判断仍遵守上面「前端平台判断」规则(`kIsWeb` 先于 `dart:io`
### 权限中间件(已全局挂载)
+2 -35
View File
@@ -523,38 +523,5 @@ class DsSortHeader extends StatelessWidget {
}
}
/// 原型 thead th .fh 漏斗列头:label + funnel(12, sw1.6),有筛选值时整体 primary。
/// 点击回调携带列头自身 BuildContext(供 showDsMenu 锚定)
class DsFilterHeader extends StatelessWidget {
final String label;
final bool filtered;
final void Function(BuildContext anchorContext) onOpen;
const DsFilterHeader(
{super.key,
required this.label,
required this.filtered,
required this.onOpen});
@override
Widget build(BuildContext context) {
final t = context.tokens;
final color = filtered ? t.primary : t.muted;
return Builder(
builder: (bctx) => InkWell(
onTap: () => onOpen(bctx),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(label,
style: TextStyle(
fontSize: AppDims.fsSm,
fontWeight: FontWeight.w600,
color: color)),
const SizedBox(width: 5),
Icon(LucideIcons.funnel, size: 12, color: color),
],
),
),
);
}
}
// DsFilterHeader(列头漏斗筛选)已于 2026-07-07 退役删除:筛选统一上移工具栏
// chips(用户口径,形式参照出库管理),列头仅保留 DsSortHeader 排序
+5
View File
@@ -1,6 +1,11 @@
import 'package:flutter/material.dart';
import '../core/theme/context_tokens.dart';
// ⚠️ LEGACY2026-07-07 标注):本组件为旧移动卡片范式(label:value 竖排字段),
// 新范式 = widgets/ds/m_card.dart 的 MCard(镜像原型 mobile-atoms .m-card
// 图标徽章 + 双栏紧凑)。**新屏禁用本组件,一律用 MCard**;存量 6 屏
// (出入库录单/往来/盘点/基础数据/财务)迁移已登记 todo,迁完删除本文件。
/// 移动端列表卡片的单个字段(label: value)。
class MobileCardField {
final String label;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 KiB

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 KiB

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 445 KiB

After

Width:  |  Height:  |  Size: 443 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 KiB

After

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 423 KiB

After

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 KiB

After

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 KiB

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 KiB

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 KiB

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 KiB

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 KiB

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 KiB

After

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 426 KiB

After

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 KiB

After

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 KiB

After

Width:  |  Height:  |  Size: 388 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 KiB

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 KiB

After

Width:  |  Height:  |  Size: 388 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 KiB

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

After

Width:  |  Height:  |  Size: 363 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 KiB

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 KiB

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 KiB

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

After

Width:  |  Height:  |  Size: 404 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

After

Width:  |  Height:  |  Size: 218 KiB

+11 -11
View File
@@ -39,7 +39,7 @@ export const SCREENS = {
waitFor: '.app',
// Phase 2 重建后校准:实测 a 3.8% / b 2.9% / c 4.2%(残差=跨渲染器行高漂移
// + 状态徽章已知语义差),收紧到 8% 抓结构性回归。
threshold: 0.08,
threshold: 0.07, // 实测 4.7% + 2pp2026-07-07 收紧,原 0.08
},
partners: {
html: '.superpowers/prototype/screens/partners.html',
@@ -47,7 +47,7 @@ export const SCREENS = {
width: 1280, height: 900, dpr: 2,
waitFor: '.app',
// 重建后校准:实测 a 3.2 / b 2.5 / c 3.5%(残差=列宽分布+文字度量+保留的导出按钮)
threshold: 0.08,
threshold: 0.06, // 实测 3.6% + 2pp2026-07-07 收紧,原 0.08
},
products: {
html: '.superpowers/prototype/screens/products.html',
@@ -57,7 +57,7 @@ export const SCREENS = {
// 重建后校准:实测 a 3.2 / b 2.4 / c 3.5%。残差含已知数据驱动差
// (见 design/CONTRACT.md):档案无 分类/状态 列、无香型 chips、
// seg 第 5 tab 香型→仓库、无「新增商品」、pager 带翻页控件。
threshold: 0.08,
threshold: 0.06, // 实测 3.7% + 2pp2026-07-07 收紧,原 0.08
},
devices: {
html: '.superpowers/prototype/screens/devices.html',
@@ -65,7 +65,7 @@ export const SCREENS = {
width: 1280, height: 900, dpr: 2,
waitFor: '.app',
// 重建后校准:实测 2.4–3.0%(残差=会话表列宽分布+外设卡按钮文字省略)
threshold: 0.08,
threshold: 0.06, // 实测 3.5% + 2pp2026-07-07 收紧,原 0.08
},
settings: {
html: '.superpowers/prototype/screens/settings.html',
@@ -73,7 +73,7 @@ export const SCREENS = {
width: 1280, height: 900, dpr: 2,
waitFor: '.app',
// 已知差异:子导航多「编号规则」项、门店卡多 Logo 行。实测 1.72.3%
threshold: 0.08,
threshold: 0.04, // 实测 2.4% + 2pp2026-07-07 收紧,原 0.08
},
users: {
html: '.superpowers/prototype/screens/users.html',
@@ -81,14 +81,14 @@ export const SCREENS = {
width: 1280, height: 900, dpr: 2,
waitFor: '.app',
// 已知差异:角色口径拉平为后端四级(原型 KPI 初值/角色混用三级)。实测 1.3–2.1%
threshold: 0.08,
threshold: 0.04, // 实测 1.9% + 2pp2026-07-07 收紧,原 0.08
},
about: {
html: '.superpowers/prototype/screens/about.html',
prefix: 'about',
width: 1280, height: 900, dpr: 2,
waitFor: '.app',
threshold: 0.08, // 实测 2.22.7%
threshold: 0.05, // 实测 2.7% + 2pp2026-07-07 收紧,原 0.08
},
'stock-in': {
html: '.superpowers/prototype/screens/stock-in-list.html',
@@ -98,7 +98,7 @@ export const SCREENS = {
// 2026-07-03 补注册(此前漏注册导致单元格样式偏差长期未被闸住)。
// 页面留白/单元格对齐后校准:实测 a 3.6 / b 2.6 / c 3.9%
//(残差=真实数据 vs 原型演示数据 + 状态列口径/placeholder 文案已知差异)。
threshold: 0.08,
threshold: 0.06, // 实测 4.0% + 2pp2026-07-07 收紧,原 0.08
},
'stock-out': {
html: '.superpowers/prototype/screens/stock-out-list.html',
@@ -106,7 +106,7 @@ export const SCREENS = {
width: 1280, height: 900, dpr: 2,
waitFor: '.app',
// 页面留白/单元格对齐后校准:实测 a 3.6 / b 2.6 / c 3.9%
threshold: 0.08,
threshold: 0.06, // 实测 4.0% + 2pp2026-07-07 收紧,原 0.08
},
login: {
html: '.superpowers/prototype/screens/login.html',
@@ -116,7 +116,7 @@ export const SCREENS = {
// 2026-07-03 重建后校准:实测 a 1.9 / b 1.4 / c 2.1%。已知差异
// design/CONTRACT.md):密码框空值 hint vs 原型 8 dots、
// 历史下拉/密码可见后缀箭头(功能位)、ds-fab 仅原型有。
threshold: 0.08,
threshold: 0.04, // 实测 2.4% + 2pp2026-07-07 收紧,原 0.08
},
// register:暂不入闸(2026-07-03 用户拍板)。原型多「门店编号/授权兑换券」
// 两字段,后端 RegisterInput 暂不支持 → 表单少两行,整卡结构性错位
@@ -136,6 +136,6 @@ export const SCREENS = {
waitFor: '.app',
// 重建后校准:实测 a 3.9 / b 3.0 / c 4.2%(残差=柱高比例+文字度量+
// KPI 应收为实算汇总 vs 原型硬编码,见 CONTRACT 已知差异)
threshold: 0.08,
threshold: 0.06, // 实测 4.3% + 2pp2026-07-07 收紧,原 0.08
},
};