Files
jiu/docs/plans/2026-07-14-six-frontend-tweaks.md
T
2026-07-14 11:23:26 +08:00

163 lines
17 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.
# 六项前端体验优化 实现计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 一次性落地 6 个前端小需求:①库存抽屉加供应商 ②库存桌面列批次号/生产日期换位 ③出入库单详情抽屉加「生产日期+批次号」双行列(两端)④出入库列表状态筛选多选+默认草稿/待审核 ⑤新建单默认填充默认仓库 ⑥财务加往来单位筛选+自定义时间换统一组件。
**Architecture:** 全部为前端(Flutter client/+ 设计原型(design/prototype/)改动,**后端零改动**(调研已核实:供应商/批次/生产日期数据链路已通、status 已支持逗号多值、partner_id 过滤已存在、warehouses.is_default 全链路齐备)。按 design-first 铁律:每个任务先改原型、再改真实代码、**原型与代码同一提交**。
**Tech Stack:** Flutter/Riverpod + 原型 HTML/JStokens/atoms 单源);验收闸 check-ds / check_ds_code / check-l1-sync / fidelity / golden。
## Global Constraints
- **design-first 铁律(用户 2026-07-14 重申)**:涉及页面的改动必须先改 `design/prototype/` 原型再开发,原型与代码同一提交;改到的屏必须保持 CONTRACT「同步」态。
- 颜色/间距一律走 token`context.tokens`),禁硬编码 hex;新原子先登记原型 `index.html`
- 弹窗/抽屉宽度改动属 L1 层(`atoms.css`),须原型与 Flutter 常量同提交(过 `tools/check-l1-sync.mjs`)。
- 多租户/权限口径不变:出库成本/利润仅管理员可见的逻辑不受本次改动影响(新增列均为非成本字段)。
- DoD`flutter analyze --no-fatal-infos --no-fatal-warnings` 零 error、`flutter test` 全绿、`node client/tool/check_ds_code.mjs``node tools/check-l1-sync.mjs` 过闸、涉及屏 fidelity ≤ 8%;**不发版、不打 tag、不部署**,完成后停在本地提交态等用户验收。
- 提交规范:每个需求一个提交,`feat(client): ...`,原型改动包含在同一提交内。
## 已定决策(评审时可推翻)
- **D1 供应商行两端同显**:库存商品抽屉两端共用 `product_editor_drawer.dart``_infoBody`,加一行两端同时生效、成本为零;桌面端抽屉当前也没有供应商(表格有),一并补齐更一致。行位置:紧跟「生产日期」行之后。
- **D2 库存抽屉顺手补「批次号」行(推荐做)**:与需求③口径一致(出入库抽屉都有生产日期+批次号,库存抽屉只有生产日期不对称);`Inventory.batchNo` 数据已有,走与供应商相同的入参传递。若你只要最小改动(仅供应商),评审时说一声即去掉。
- **D3 状态筛选「重置」回默认**:重置按钮把状态恢复为默认 `draft,pending`(与进入页面初始态一致),不是回「全部」。
- **D4 退单伪状态与多选互斥**`ret:partial`/`ret:full` 保持与主状态互斥——选退单态清空状态多选,选任一主状态清退单态(沿用现语义,最小改动)。
- **D5 移动端 KPI 卡片点击 = 覆盖为单值**:点「待审核」卡即只看待审核(覆盖当前多选集合),保持现有心智。
- **D6 移动端财务往来单位筛选形态**:在时间 chips 行尾追加「往来单位」chip,点开 `showMSheet` 底部可搜索单选列表(与桌面 `ComboSearchField` 对应);再点 × 清除。
- **D7 出库抽屉加宽 600→660**:出库管理员形态 7→8 列太紧,`atoms.css` `.drawer.wide` 600→660px 与 Flutter `order_detail_drawer.dart:44` 常量同提交;入库 5→6 列余量够,只微调列宽不加宽。
## 执行分组(多 agent 调度)
| 波次 | 组 | 任务 | 关键文件域 | 并行性 |
|------|----|------|-----------|--------|
| 1 | A 库存域 | Task 1 + Task 2 | product_editor_drawer / inventory_list_screen / inventory.html / product-editor.js / m-inventory.html | 与 B、D 并行 |
| 1 | B 出入库抽屉 | Task 3 | order_detail_drawer / 两 list_screen(传参处)/ atoms.css / stock-*-list.html ×4 | 与 A、D 并行 |
| 1 | D 财务域 | Task 6 | finance_screen / finance_provider / finance.html / m-finance.html | 与 A、B 并行 |
| 2 | C 出入库筛选+表单 | Task 4 + Task 5 | 两 provider / 两 list_screen / 两 form_screen / stock-*-list.html ×4 / stock-in.js | **必须等 B 完成**(同文件) |
| 3 | 收尾 | Task 7 | CONTRACT.md / fidelity / golden / 手册核查 | 最后 |
---
### Task 1: 库存商品抽屉加「供应商」行(+D2 批次号行)【两端】
**Files:**
- Modify: `design/prototype/screens/product-editor.js:60-75``infoRows()`
- Modify: `design/prototype/screens/m-inventory.html:98`(详情 sheet 行序)
- Modify: `client/lib/widgets/product_editor_drawer.dart`(入参 + `_infoBody` 385-471
- Modify: `client/lib/screens/inventory/inventory_list_screen.dart:123``_openEditor` 传参)
**Interfaces:**
- Produces: `showProductEditorDrawer(..., String? supplier, String? batchNo)` 两个新可空入参;不传即不显示(条件行机制),其它调用方(products_screen.dart 等)零改动。
- [x] **Step 1(原型先行)**: `product-editor.js` `infoRows()` 在「生产日期」行后加「批次号」「供应商」两行(假数据示例 `B20260701` / `XX贸易`);`m-inventory.html` 详情 sheet 同步行序。本地 5180 预览确认。
- [x] **Step 2(代码)**: `product_editor_drawer.dart``showProductEditorDrawer``String? supplier, String? batchNo` 入参并透传;`_infoBody` 在生产日期行后按非空条件渲染「批次号」「供应商」两行(样式照抄现有 info 行)。
- [x] **Step 3(接线)**: `inventory_list_screen.dart` `_openEditor``supplier: item.supplierName, batchNo: item.batchNo`
- [x] **Step 4(验证)**: `cd client && flutter analyze --no-fatal-infos --no-fatal-warnings && flutter test`;若库存屏 golden 覆盖抽屉则 regen。
- [x] **Step 5(提交)**: `feat(client): 库存商品抽屉增加供应商与批次号信息行(两端,原型同步)`
### Task 2: 库存桌面表格「批次号」「生产日期」列换位【仅桌面】
**Files:**
- Modify: `design/prototype/screens/inventory.html:128``COLS` 数组对调)
- Modify: `client/lib/screens/inventory/inventory_list_screen.dart:58-73``_colDefs` 对调)
前提核实(调研已确认):移动端卡片无批次号字段,不涉及;列偏好/排序白名单均按 key 存,换位只动数组顺序不动 key,用户已存配置不受影响。
- [x] **Step 1(原型先行)**: `inventory.html` `COLS` 中 batch 与 prodDate 位置对调,5180 预览确认。
- [x] **Step 2(代码)**: `_colDefs` 同步对调两项顺序(连同对应 cell 构建顺序)。
- [x] **Step 3(验证)**: `flutter analyze && flutter test`;库存列表 golden 如有列序断言则 regen。
- [x] **Step 4(提交)**: `feat(client): 库存列表批次号与生产日期列位对调(原型同步)`
### Task 3: 出入库单详情抽屉明细加「生产日期+批次号」双行列【两端】
**Files:**
- Modify: `design/prototype/screens/stock-in-list.html:311-312`(明细 grid + 假数据 lines 补 `pdate`/`batch`
- Modify: `design/prototype/screens/stock-out-list.html:306-308``GRID` 常量 + 假数据)
- Modify: `design/prototype/screens/m-stock-in-list.html:145-149``m-stock-out-list.html``openOrder` lineRows 插入「生产日期/批次号」双行列)
- Modify: `design/prototype/atoms.css:218-220``.drawer.wide` 600→660D7
- Modify: `client/lib/widgets/order_detail_drawer.dart``OrderLine`:67-92 加字段;`_LinesTable`:418-618 加列;`_mLineRow`:331-367 插双行列;:44 宽度 660
- Modify: `client/lib/screens/stock_in/stock_in_list_screen.dart:988-999``client/lib/screens/stock_out/stock_out_list_screen.dart:1064-1080`(构造 OrderLine 传两字段)
- Modify: `client/lib/models/stock_in.dart:63-64``StockInItem` 两字段改 `lineOrProduct` 兜底,对齐 stock_out 写法)
- Test: `client/test/order_detail_drawer_columns_test.dart`(列结构断言同步更新)
**Interfaces:**
- Produces: `OrderLine(..., String? productionDate, String? batchNo)`;桌面新列单元格样式 = 上行生产日期(fsSm mono+ 下行批次号(fsXs faint mono),复用「商品·编码」列先例。
- [x] **Step 1(原型先行)**: 四个原型文件明细区插入「生产日期/批次号」双行列(桌面:入库 grid 插 ~72px 列并把 124→110、88→78;出库 GRID 插列且抽屉用 wide 660+ 移动 lineRows 在金额左侧同样插入双行列(上=生产日期 fsSm mono,下=批次号 fsXs faint,与桌面同构;用户 2026-07-14 审阅指定);`atoms.css` `.drawer.wide` 600→660。假数据补 pdate/batch。5180 预览两端四屏。
- [x] **Step 2(模型兜底)**: `stock_in.dart` `StockInItem.fromJson` 的 production_date/batch_no 改用 `lineOrProduct` 兜底(照 `stock_out.dart:68-69`)。
- [x] **Step 3(抽屉代码)**: `OrderLine` 加两字段;`_LinesTable` 入库 5→6 列 / 出库 7→8 列,新列插在「系列·规格」后,列宽按原型定稿值;`_mLineRow` 在金额区左侧插入双行列(上=生产日期 fsSm mono,下=批次号 fsXs faint;两值都空则不渲染该列,避免空占位);:44 桌面宽度 `min(660, 94%)`
- [x] **Step 4(接线)**: 两个 list_screen 构造 OrderLine 时传 `productionDate: it.productionDate, batchNo: it.batchNo`(显示截 `substring(0,10)`)。
- [x] **Step 5(测试)**: 更新 `order_detail_drawer_columns_test.dart` 列数/列头断言;`flutter analyze && flutter test`;涉及 goldenstock_{in,out}_list*)若开抽屉则 regen。
- [x] **Step 6L1 闸)**: `node tools/check-l1-sync.mjs` 过闸(atoms.css 与 Flutter 常量同源)。
- [x] **Step 7(提交)**: `feat(client): 出入库单详情抽屉明细增加生产日期+批次号列(两端,抽屉加宽 660,原型同步)`
### Task 4: 出入库列表状态筛选多选 + 默认「草稿+待审核」【两端】
**Files:**
- Modify: `design/prototype/screens/stock-in-list.html:67``stock-out-list.html`chipStatus 单选→多选,默认态 draft+pending,摘要「值/N 项」,参照 inventory.html:69,138-139 既有多选形态)
- Modify: `design/prototype/screens/m-stock-in-list.html:101-102``m-stock-out-list.html`(状态 sheet 单选→多选勾选)
- Modify: `client/lib/providers/stock_in_provider.dart:41``stock_out_provider.dart:41`(默认 `_status = 'draft,pending'`
- Modify: `client/lib/screens/stock_in/stock_in_list_screen.dart`:86-113 归一逻辑重写;:773-790 桌面 chip 改 `showDsMultiMenu`:678-724 移动 sheet 改多选;:621-630 KPI 点击、`_resetFilters`
- Modify: `client/lib/screens/stock_out/stock_out_list_screen.dart`:100-128 归一、`_MenuChip` 调用点 ~813、:1973-2020、KPI :548-560/:650-659 同构改法)
**Interfaces:**
- Consumes: 后端 `status IN (逗号分隔)` 已支持(stock_in.go:44-45 / stock_out.go:55-56),零改动。
- Produces: 屏内状态 = `Set<String>`,落 provider 时 `join(',')`;空集 = 全部。
关键陷阱(调研确认):两屏 `initState` 有「归零残留多值」逻辑,会把 `'pending,draft'` 重置为 `''`——必须重写为「按逗号拆分后逐值校验合法性,非法才归默认」,否则默认值刚设就被清掉。
- [x] **Step 1(原型先行)**: 四个原型文件状态筛选改多选(复用 inventory 屏 chip 摘要形态 / m 端 sheet 勾选形态),默认选中 草稿+待审核。5180 预览。
- [x] **Step 2provider**: 两个 provider 默认值改 `'draft,pending'`
- [x] **Step 3(归一重写)**: 两屏 initState 残留清洗逻辑改为逐值校验(合法集 = draft/pending/approved/rejected),并兼容退单伪状态互斥(D4)。
- [x] **Step 4(桌面 chip**: 单选 PopupMenu → `_multiChip` + `showDsMultiMenu`(照 `inventory_list_screen.dart:209-246``widgets/ds/ds_menu.dart:51`),摘要:1 项显示值 / 多项「N 项」/ × 清为默认(D3)。
- [x] **Step 5(移动 sheet + KPI**: `_openStatusSheet` 改多选勾选(确定时应用);KPI 卡点击 = 覆盖单值(D5);`_resetFilters` 状态回 `draft,pending`D3)。
- [x] **Step 6(验证)**: `flutter analyze && flutter test`;两屏列表 golden regen(默认筛选态变了,golden 基线会变)。
- [x] **Step 7(提交)**: `feat(client): 出入库列表状态筛选改多选并默认草稿+待审核(两端,原型同步)`
### Task 5: 新建入库/出库单默认填充默认仓库【两端,同一代码路径】
**Files:**
- Modify: `design/prototype/screens/stock-in.js``state.doc.warehouse` 新建时默认取默认仓,双模式驱动 in/out 两屏)
- Modify: `client/lib/screens/stock_in/stock_in_form_screen.dart``_warehouseId`:43,加载仓库后填充)
- Modify: `client/lib/screens/stock_out/stock_out_form_screen.dart`:158 同构)
**Interfaces:**
- Consumes: `Warehouse.isDefault`client/lib/models/warehouse.dart:5,18,已存在);设置页已可指定默认仓。
- [x] **Step 1(原型先行)**: `stock-in.js` 新建单时仓库字段预填默认仓(假数据标记一个默认仓)。5180 预览 in/out 两屏。
- [x] **Step 2(代码)**: 两张表单新建模式(非编辑回填)在仓库列表加载完成后:`_warehouseId ??= 列表中 isDefault 的仓 ?? (列表仅 1 个仓时取它 : 保持 null)`。编辑模式不受影响(仍从单据回填)。
- [x] **Step 3(验证)**: `flutter analyze && flutter test`(两表单已有测试如断言初始空值需同步)。
- [x] **Step 4(提交)**: `feat(client): 新建出入库单默认填充默认仓库(两端,原型同步)`
### Task 6: 财务加「往来单位」筛选 + 自定义时间换统一组件【两端】
**Files:**
- Modify: `design/prototype/screens/finance.html`:82-86 timerow「自定义」接 `datewheel.js openRange`(照 stock-in-list.html:228 写法);流水表 toolbar 加往来单位下拉)
- Modify: `design/prototype/screens/m-finance.html`(回填真实端已有的时间 chips 行【存量漂移】+ 时间行尾加「往来单位」chip→sheet,D6
- Modify: `client/lib/providers/finance_provider.dart:18-88`(加 `_partnerId` + `setPartner()`,照 `setType` 写法约 8 行)
- Modify: `client/lib/screens/finance/finance_screen.dart`:89-103 `_applyRange` 自定义分支 `showDateRangePicker``showDateRangeDropdown(anchorCtx, initial:)`anchor 用「自定义」chip 的 Builder context,照 stock_in `_dateMenu`:247-270:532-557 `_flowTable` toolbar 加 `ComboSearchField` 往来单位(照 stock_out_list_screen.dart:783-797);移动分支时间行尾加往来单位 chip→`showMSheet` 可搜单选)
**Interfaces:**
- Consumes: 后端 `/finance/records?partner_id=` 已支持(finance.go:34,51-52);`finance_repository.listRecords` 已有 `partnerId` 参数(:16,28);数据源 `allPartnersProvider`partner_provider.dart:31);`showDateRangeDropdown`widgets/wheel_date_picker.dart,组件内部自适应桌面下拉/窄屏 sheet)。
- [x] **Step 1(原型先行)**: `finance.html` 自定义时间接 datewheel + toolbar 加往来下拉;`m-finance.html` 回填时间行 + 往来单位 chip/sheet。5180 预览两端。
- [x] **Step 2provider**: `FinanceListNotifier``_partnerId`/`setPartner()` 并接入 `listRecords`
- [x] **Step 3(统一时间)**: `_applyRange` 自定义分支换 `showDateRangeDropdown`(一处代码两端生效,删除 `showDateRangePicker` 引用)。
- [x] **Step 4(桌面往来筛选)**: `_flowTable` toolbar 类型 chips 旁加 `ComboSearchField`(可搜单选,清除回全部)。
- [x] **Step 5(移动往来筛选)**: 时间 chips 行尾加「往来单位」chip → 底部 sheet 可搜单选(D6)。
- [x] **Step 6(验证)**: `flutter analyze && flutter test`;财务屏 golden regentoolbar 变了)。
- [x] **Step 7(提交)**: `feat(client): 财务筛选增加往来单位并统一自定义时间组件(两端,原型同步)`
### Task 7: 收尾——CONTRACT 台账回填 + fidelity 体检 + 手册核查
**Files:**
- Modify: `design/CONTRACT.md`(块 4:库存 :100-101、出入库列表 :106-107、财务 :108 各行备注回填本次改动,保持「同步」态)
- Check: `web/content/docs.md` / `docs/manual/user-manual.html`(若有「出入库列表默认显示」「财务筛选」相关描述则同步;无则不动)
- [x] **Step 1**: 跑受影响屏 fidelity`node tools/fidelity.mjs`(库存、入库列表、出库列表、财务,桌面+移动),超 8% 回改对齐。
- [x] **Step 2**: CONTRACT.md 对应行回填改动说明与 fidelity 结果。
- [x] **Step 3**: 手册核查:默认筛选行为变化(草稿+待审核)如手册有描述则两侧同步(web/content/docs.md + user-manual.html)。
- [x] **Step 4(全量 DoD**: `flutter analyze && flutter test``node client/tool/check_ds_code.mjs``node tools/check-l1-sync.mjs` 全绿。
- [x] **Step 5(提交)**: `docs(design): 六项前端优化 CONTRACT 台账回填与手册同步`