chore: 补提测试修复、新增测试工具和设计文档

- fix(test): 同步 listInventory 签名(补 series/spec 参数)
- test(backend): 新增 import_parse_test.go
- chore(backend): 新增 cmd/test_xls 调试工具
- docs: UI 筛选组件设计规范、Gemini 项目配置

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-15 11:17:35 +08:00
parent eac3e8825c
commit 36564571fa
7 changed files with 531 additions and 17 deletions
+149
View File
@@ -0,0 +1,149 @@
# 库存查询「规格」「系列」列头筛选 — UI 变更规范
**变更类型**:简单变更,flutter-coder 可直接实现
**对应文件**`client/lib/screens/inventory/inventory_list_screen.dart`
---
## 复杂度判断
复用现有 `FilterableColumnHeader` 组件,实现方式与已有「仓库」列筛选完全一致,无新组件、无新页面、无复杂交互,属于简单变更。
---
## 变更详情
### [UI-001] 新增 State 变量
**位置**`_InventoryListScreenState` 类顶部,紧跟 `_filterWarehouse` 声明之后
```dart
Set<String> _filterWarehouse = {};
Set<String> _filterSpec = {}; // 新增:规格筛选
Set<String> _filterSeries = {}; // 新增:系列筛选
```
---
### [UI-002] 派生筛选选项
**位置**`_buildInventoryTab()` 方法内,`data:` 回调中,紧跟 `warehouseOptions``filteredItems` 的现有代码之后替换
**现有代码段(第 368-379 行):**
```dart
final warehouseOptions = items
.map((i) => i.warehouseName)
.where((s) => s.isNotEmpty)
.toSet()
.toList()
..sort();
final filteredItems = _filterWarehouse.isEmpty
? items
: items
.where((i) => _filterWarehouse.contains(i.warehouseName))
.toList();
```
**替换为:**
```dart
final warehouseOptions = items
.map((i) => i.warehouseName)
.where((s) => s.isNotEmpty)
.toSet()
.toList()
..sort();
final specOptions = items // 新增:规格选项
.map((i) => i.spec)
.where((s) => s.isNotEmpty)
.toSet()
.toList()
..sort();
final seriesOptions = items // 新增:系列选项
.map((i) => i.series)
.where((s) => s.isNotEmpty)
.toSet()
.toList()
..sort();
// 三个筛选叠加(AND 逻辑)
final filteredItems = items.where((i) {
if (_filterWarehouse.isNotEmpty && !_filterWarehouse.contains(i.warehouseName)) {
return false;
}
if (_filterSpec.isNotEmpty && !_filterSpec.contains(i.spec)) {
return false;
}
if (_filterSeries.isNotEmpty && !_filterSeries.contains(i.series)) {
return false;
}
return true;
}).toList();
```
**说明:**
- 选项数据从当前页已加载的 `items` 派生,客户端筛选,与「仓库」逻辑一致
- 三个筛选条件为 AND 叠加(同时满足才显示)
- 空集合(`isEmpty`)视为「不筛选该维度」,等同于全选
---
### [UI-003] 替换「规格」和「系列」列头
**位置**`columns:` 列表中第 491-494 行(当前为 `const DataColumn`
**现有代码:**
```dart
const DataColumn(label: Text('规格')),
const DataColumn(label: Text('系列')),
```
**替换为:**
```dart
DataColumn(
label: FilterableColumnHeader(
text: '规格',
options: specOptions,
selected: _filterSpec,
onChanged: (v) => setState(() => _filterSpec = v),
),
),
DataColumn(
label: FilterableColumnHeader(
text: '系列',
options: seriesOptions,
selected: _filterSeries,
onChanged: (v) => setState(() => _filterSeries = v),
),
),
```
**注意**:移除原来的 `const` 关键字,因为构造函数参数不再是编译期常量。
---
## 视觉规范(无需改动)
`FilterableColumnHeader` 的所有视觉行为保持原样:
| 状态 | 表现 |
|------|------|
| 默认(无筛选) | 纯文字列头,无图标 |
| Hover | 显示半透明 `filter_alt_outlined` 图标(灰色)|
| 已激活筛选 | 图标变为实心 `filter_alt`(主色 #1565C0+ 右侧出现 X 清除按钮 |
| X 按钮点击 | 清空该维度筛选(`onChanged({})` ),无需打开弹窗 |
| 列头点击图标 | 弹出 `_MultiSelectDialog`(全选/清空 + checkbox 列表 + 取消/应用) |
---
## 受影响范围
- 仅修改 `_InventoryListScreenState` 内部,不涉及 provider、model、API
- `_buildWarningTab()``_buildLogTab()` 不受影响
- 导出 Excel 的 `filteredItems` 自动受益于新筛选逻辑,无需额外改动
- 窄屏卡片流(`mobileCards`)同样使用 `filteredItems`,筛选效果自动生效