feat: 商品追踪页面 + 出库提交库存校验

后端:
- feat(backend): 重命名 Batches→Products 接口,新增库存状态(在售/已卖出)和买家信息
- feat(backend): 出库单创建/提交时校验仓库库存,不足则返回明确错误信息
- fix(backend): 出库创建 status 判断逻辑修复(空值默认 draft)

前端:
- feat(client): 批次追踪改为商品追踪,新增状态列(在售/已卖出)和买家/时间列
- fix(client): 无批次号时显示"无批次"而非空
- refactor(client): BatchRecord → ProductTrackingRecord,repository 接口更新

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-04-10 22:26:42 +08:00
parent ce1cbf404c
commit 66e54af8c6
12 changed files with 513 additions and 68 deletions
+68
View File
@@ -0,0 +1,68 @@
# Flutter 布局 Bug 报告
## BUG-001
**严重程度**:高
**问题描述**
`DataTableCard` 将 toolbar 包裹在 `SingleChildScrollView(scrollDirection: Axis.horizontal)` 中,
而 toolbar 内使用了 `Spacer``Flexible` 组件)。`Spacer` 在无界宽度约束下无法布局,导致运行时
抛出 Flutter assertion
```
RenderFlex children have non-zero flex but incoming width constraints are unbounded.
```
受影响页面:
- `ProductsScreen``products_screen.dart:139`
- `PartnersScreen``partners_screen.dart`
- `StockInListScreen``stock_in_list_screen.dart`
**复现步骤**
1. 打开包含 `DataTableCard` 的任意页面(商品、往来单位、入库单等)
2. 由于 `SingleChildScrollView` 提供无界宽度,toolbar 中的 `Spacer` 无法展开
3. Flutter 抛出断言错误,页面渲染失败(测试中表现为所有 Widget 测试失败)
**失败的测试用例**
所有 `products_screen_test.dart``partners_screen_test.dart``stock_in_screen_test.dart` 中的
测试均失败,错误来自 `data_table_card.dart:35` 的布局计算。
**根因分析**
`client/lib/widgets/data_table_card.dart` 第 33-46 行:
```dart
SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
ConstrainedBox(
constraints: BoxConstraints(
minWidth: (MediaQuery.of(context).size.width - 212 - 24).clamp(0.0, double.infinity),
),
child: toolbar!, // toolbar 中含有 Spacer,而父容器宽度无界
),
],
),
),
```
`ConstrainedBox` 设置了 `minWidth`,但 `SingleChildScrollView` 为子组件提供的是
`BoxConstraints(minWidth, Infinity)` — 最大宽度无界。`Spacer` 尝试填充剩余空间
(即无限空间),触发 Flutter 的约束断言。
**修复建议**
方案 A:将 `ConstrainedBox` 改为固定宽度的 `SizedBox`,让 toolbar 占满 `minWidth`
并把 toolbar 的 `Spacer` 替换为 `MainAxisAlignment.spaceBetween`
`SizedBox(width: X)` 固定间距:
```dart
// data_table_card.dart - 去掉 SingleChildScrollView 横向滚动,
// 改用固定宽度容器,或让 toolbar Row 使用 mainAxisSize: MainAxisSize.min
SizedBox(
width: (MediaQuery.of(context).size.width - 212 - 24).clamp(0.0, double.infinity),
child: toolbar!,
)
```
方案 B:在各 Screen 的 toolbar Row 中将 `Spacer()` 替换为 `SizedBox(width: 8)`
并在 `DataTableCard` 中改用非横向滚动的固定宽度容器。