feat(client): 所有表格列头加筛选,图标更大靠右对齐

FilterableColumnHeader:
- mainAxisAlignment.spaceBetween 将图标推到列头右侧
- 漏斗图标从 13px 增大到 16px,清除图标 14px

各页面改动:
- batch_tracking:状态/仓库/供应商筛选移入列头,工具栏合并为一行
- finance:类型/往来单位筛选移入列头,工具栏保留月份选择和显示字段
- inventory:仓库筛选从 API 端单选改为客户端多选,移入"仓库"列头
- products:品牌列头加筛选,从加载数据中动态派生选项

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-04-18 19:00:03 +08:00
parent c5c74d59ab
commit 5a3907e3f9
5 changed files with 170 additions and 191 deletions
@@ -4,7 +4,7 @@ import '../../core/theme/app_theme.dart';
import '../../models/inventory.dart';
import '../../providers/inventory_provider.dart';
import '../../widgets/data_table_card.dart';
import '../../widgets/multi_select_dropdown.dart';
import '../../widgets/multi_select_dropdown.dart' show ColDef, ColumnToggleButton, FilterableColumnHeader;
class BatchTrackingScreen extends ConsumerStatefulWidget {
const BatchTrackingScreen({super.key});
@@ -135,12 +135,32 @@ class _BatchTrackingScreenState extends ConsumerState<BatchTrackingScreen> {
(c.minWidth == null || screenWidth >= c.minWidth!))
.toList();
final columns = visibleCols
.map((c) => DataColumn(
label: Text(c.label),
numeric: c.key == 'qty' || c.key == 'price',
))
.toList();
final columns = visibleCols.map((c) {
final label = switch (c.key) {
'status' => FilterableColumnHeader(
text: c.label,
options: const ['在售', '已卖出'],
selected: _filterStatus,
onChanged: (v) => setState(() => _filterStatus = v),
),
'warehouse' => FilterableColumnHeader(
text: c.label,
options: warehouseOptions,
selected: _filterWarehouse,
onChanged: (v) => setState(() => _filterWarehouse = v),
),
'supplier' => FilterableColumnHeader(
text: c.label,
options: supplierOptions,
selected: _filterSupplier,
onChanged: (v) => setState(() => _filterSupplier = v),
),
_ => Text(c.label),
};
return DataColumn(
label: label,
numeric: c.key == 'qty' || c.key == 'price');
}).toList();
final rows = records.isEmpty
? [
@@ -169,59 +189,24 @@ class _BatchTrackingScreenState extends ConsumerState<BatchTrackingScreen> {
_page = p;
_fetch();
}),
toolbar: Column(
crossAxisAlignment: CrossAxisAlignment.start,
toolbar: Row(
children: [
// Top row: description + refresh + column toggle
Row(
children: [
const Text('已审核入库商品(含库存与销售状态)',
style: TextStyle(
fontSize: 13, color: AppTheme.textSecondary)),
const Spacer(),
IconButton(
icon: const Icon(Icons.refresh, size: 18),
onPressed: () => setState(() {
_page = 1;
_fetch();
}),
tooltip: '刷新',
),
const SizedBox(width: 4),
ColumnToggleButton(
columns: _colDefs,
hidden: _hiddenCols,
onChanged: (v) => setState(() => _hiddenCols = v),
),
],
const Text('已审核入库商品(含库存与销售状态)',
style: TextStyle(fontSize: 13, color: AppTheme.textSecondary)),
const Spacer(),
IconButton(
icon: const Icon(Icons.refresh, size: 18),
onPressed: () => setState(() {
_page = 1;
_fetch();
}),
tooltip: '刷新',
),
// Filter bar row
const SizedBox(height: 6),
Row(
children: [
MultiSelectDropdown(
label: '状态',
options: const ['在售', '已卖出'],
selected: _filterStatus,
onChanged: (v) => setState(() => _filterStatus = v),
),
const SizedBox(width: 8),
if (warehouseOptions.length > 1)
MultiSelectDropdown(
label: '仓库',
options: warehouseOptions,
selected: _filterWarehouse,
onChanged: (v) => setState(() => _filterWarehouse = v),
),
if (warehouseOptions.length > 1) const SizedBox(width: 8),
if (supplierOptions.length > 1)
MultiSelectDropdown(
label: '供应商',
options: supplierOptions,
selected: _filterSupplier,
onChanged: (v) => setState(() => _filterSupplier = v),
),
],
const SizedBox(width: 4),
ColumnToggleButton(
columns: _colDefs,
hidden: _hiddenCols,
onChanged: (v) => setState(() => _hiddenCols = v),
),
],
),