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:
@@ -4,7 +4,7 @@ import '../../core/theme/app_theme.dart';
|
|||||||
import '../../models/finance.dart';
|
import '../../models/finance.dart';
|
||||||
import '../../providers/finance_provider.dart';
|
import '../../providers/finance_provider.dart';
|
||||||
import '../../widgets/data_table_card.dart';
|
import '../../widgets/data_table_card.dart';
|
||||||
import '../../widgets/multi_select_dropdown.dart';
|
import '../../widgets/multi_select_dropdown.dart' show ColDef, ColumnToggleButton, FilterableColumnHeader;
|
||||||
import '../../widgets/page_scaffold.dart';
|
import '../../widgets/page_scaffold.dart';
|
||||||
|
|
||||||
class FinanceScreen extends ConsumerWidget {
|
class FinanceScreen extends ConsumerWidget {
|
||||||
@@ -161,12 +161,26 @@ class _FinanceTabState extends ConsumerState<_FinanceTab> {
|
|||||||
(c.minWidth == null || screenWidth >= c.minWidth!))
|
(c.minWidth == null || screenWidth >= c.minWidth!))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
final columns = visibleCols
|
final columns = visibleCols.map((c) {
|
||||||
.map((c) => DataColumn(
|
final label = switch (c.key) {
|
||||||
label: Text(c.label),
|
'type' => FilterableColumnHeader(
|
||||||
numeric: c.key == 'amount' || c.key == 'balance',
|
text: c.label,
|
||||||
))
|
options: typeOptions,
|
||||||
.toList();
|
selected: _filterType,
|
||||||
|
onChanged: (v) => setState(() => _filterType = v),
|
||||||
|
),
|
||||||
|
'partner' => FilterableColumnHeader(
|
||||||
|
text: c.label,
|
||||||
|
options: partnerOptions,
|
||||||
|
selected: _filterPartner,
|
||||||
|
onChanged: (v) => setState(() => _filterPartner = v),
|
||||||
|
),
|
||||||
|
_ => Text(c.label),
|
||||||
|
};
|
||||||
|
return DataColumn(
|
||||||
|
label: label,
|
||||||
|
numeric: c.key == 'amount' || c.key == 'balance');
|
||||||
|
}).toList();
|
||||||
|
|
||||||
DataCell buildFinanceCell(String key, FinanceRecord r) {
|
DataCell buildFinanceCell(String key, FinanceRecord r) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@@ -284,44 +298,22 @@ class _FinanceTabState extends ConsumerState<_FinanceTab> {
|
|||||||
_fetch();
|
_fetch();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
toolbar: Column(
|
toolbar: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
// Filter bar + month selector + column toggle
|
const Spacer(),
|
||||||
Row(
|
_MonthSelector(
|
||||||
children: [
|
value: _month,
|
||||||
if (typeOptions.length > 1)
|
onChanged: (v) {
|
||||||
MultiSelectDropdown(
|
_month = v;
|
||||||
label: '类型',
|
_page = 1;
|
||||||
options: typeOptions,
|
_refetch();
|
||||||
selected: _filterType,
|
},
|
||||||
onChanged: (v) => setState(() => _filterType = v),
|
),
|
||||||
),
|
const SizedBox(width: 8),
|
||||||
if (typeOptions.length > 1) const SizedBox(width: 8),
|
ColumnToggleButton(
|
||||||
if (partnerOptions.length > 1)
|
columns: _colDefs,
|
||||||
MultiSelectDropdown(
|
hidden: _hiddenCols,
|
||||||
label: '往来单位',
|
onChanged: (v) => setState(() => _hiddenCols = v),
|
||||||
options: partnerOptions,
|
|
||||||
selected: _filterPartner,
|
|
||||||
onChanged: (v) =>
|
|
||||||
setState(() => _filterPartner = v),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
_MonthSelector(
|
|
||||||
value: _month,
|
|
||||||
onChanged: (v) {
|
|
||||||
_month = v;
|
|
||||||
_page = 1;
|
|
||||||
_refetch();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
ColumnToggleButton(
|
|
||||||
columns: _colDefs,
|
|
||||||
hidden: _hiddenCols,
|
|
||||||
onChanged: (v) => setState(() => _hiddenCols = v),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import '../../core/theme/app_theme.dart';
|
|||||||
import '../../models/inventory.dart';
|
import '../../models/inventory.dart';
|
||||||
import '../../providers/inventory_provider.dart';
|
import '../../providers/inventory_provider.dart';
|
||||||
import '../../widgets/data_table_card.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 {
|
class BatchTrackingScreen extends ConsumerStatefulWidget {
|
||||||
const BatchTrackingScreen({super.key});
|
const BatchTrackingScreen({super.key});
|
||||||
@@ -135,12 +135,32 @@ class _BatchTrackingScreenState extends ConsumerState<BatchTrackingScreen> {
|
|||||||
(c.minWidth == null || screenWidth >= c.minWidth!))
|
(c.minWidth == null || screenWidth >= c.minWidth!))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
final columns = visibleCols
|
final columns = visibleCols.map((c) {
|
||||||
.map((c) => DataColumn(
|
final label = switch (c.key) {
|
||||||
label: Text(c.label),
|
'status' => FilterableColumnHeader(
|
||||||
numeric: c.key == 'qty' || c.key == 'price',
|
text: c.label,
|
||||||
))
|
options: const ['在售', '已卖出'],
|
||||||
.toList();
|
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
|
final rows = records.isEmpty
|
||||||
? [
|
? [
|
||||||
@@ -169,59 +189,24 @@ class _BatchTrackingScreenState extends ConsumerState<BatchTrackingScreen> {
|
|||||||
_page = p;
|
_page = p;
|
||||||
_fetch();
|
_fetch();
|
||||||
}),
|
}),
|
||||||
toolbar: Column(
|
toolbar: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
// Top row: description + refresh + column toggle
|
const Text('已审核入库商品(含库存与销售状态)',
|
||||||
Row(
|
style: TextStyle(fontSize: 13, color: AppTheme.textSecondary)),
|
||||||
children: [
|
const Spacer(),
|
||||||
const Text('已审核入库商品(含库存与销售状态)',
|
IconButton(
|
||||||
style: TextStyle(
|
icon: const Icon(Icons.refresh, size: 18),
|
||||||
fontSize: 13, color: AppTheme.textSecondary)),
|
onPressed: () => setState(() {
|
||||||
const Spacer(),
|
_page = 1;
|
||||||
IconButton(
|
_fetch();
|
||||||
icon: const Icon(Icons.refresh, size: 18),
|
}),
|
||||||
onPressed: () => setState(() {
|
tooltip: '刷新',
|
||||||
_page = 1;
|
|
||||||
_fetch();
|
|
||||||
}),
|
|
||||||
tooltip: '刷新',
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
ColumnToggleButton(
|
|
||||||
columns: _colDefs,
|
|
||||||
hidden: _hiddenCols,
|
|
||||||
onChanged: (v) => setState(() => _hiddenCols = v),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
// Filter bar row
|
const SizedBox(width: 4),
|
||||||
const SizedBox(height: 6),
|
ColumnToggleButton(
|
||||||
Row(
|
columns: _colDefs,
|
||||||
children: [
|
hidden: _hiddenCols,
|
||||||
MultiSelectDropdown(
|
onChanged: (v) => setState(() => _hiddenCols = v),
|
||||||
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),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import 'package:go_router/go_router.dart';
|
|||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../models/inventory.dart';
|
import '../../models/inventory.dart';
|
||||||
import '../../providers/inventory_provider.dart';
|
import '../../providers/inventory_provider.dart';
|
||||||
import '../../providers/warehouse_provider.dart';
|
|
||||||
import '../../widgets/data_table_card.dart';
|
import '../../widgets/data_table_card.dart';
|
||||||
|
import '../../widgets/multi_select_dropdown.dart' show FilterableColumnHeader;
|
||||||
import '../../widgets/page_scaffold.dart';
|
import '../../widgets/page_scaffold.dart';
|
||||||
|
|
||||||
class InventoryListScreen extends ConsumerStatefulWidget {
|
class InventoryListScreen extends ConsumerStatefulWidget {
|
||||||
@@ -20,7 +20,7 @@ class InventoryListScreen extends ConsumerStatefulWidget {
|
|||||||
class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
||||||
final _searchCtrl = TextEditingController();
|
final _searchCtrl = TextEditingController();
|
||||||
Timer? _debounce;
|
Timer? _debounce;
|
||||||
int? _warehouseFilter;
|
Set<String> _filterWarehouse = {};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -55,7 +55,6 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
|||||||
|
|
||||||
Widget _buildInventoryTab() {
|
Widget _buildInventoryTab() {
|
||||||
final asyncInventory = ref.watch(inventoryListProvider);
|
final asyncInventory = ref.watch(inventoryListProvider);
|
||||||
final asyncWarehouses = ref.watch(warehouseListProvider);
|
|
||||||
|
|
||||||
return asyncInventory.when(
|
return asyncInventory.when(
|
||||||
loading: () => const Center(child: CircularProgressIndicator()),
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
@@ -82,6 +81,19 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
|||||||
items.where((i) => i.minStock != null && i.quantity < i.minStock!).length;
|
items.where((i) => i.minStock != null && i.quantity < i.minStock!).length;
|
||||||
final emptyCount = items.where((i) => i.quantity == 0).length;
|
final emptyCount = items.where((i) => i.quantity == 0).length;
|
||||||
|
|
||||||
|
// 仓库选项从数据中派生,客户端筛选
|
||||||
|
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();
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
// Summary cards
|
// Summary cards
|
||||||
@@ -116,7 +128,7 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
|||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DataTableCard(
|
child: DataTableCard(
|
||||||
totalCount: result.total,
|
totalCount: filteredItems.length,
|
||||||
page: result.page,
|
page: result.page,
|
||||||
onPageChanged: (p) =>
|
onPageChanged: (p) =>
|
||||||
ref.read(inventoryListProvider.notifier).setPage(p),
|
ref.read(inventoryListProvider.notifier).setPage(p),
|
||||||
@@ -128,49 +140,6 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
|||||||
label: const Text('发起盘点'),
|
label: const Text('发起盘点'),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
// Warehouse filter
|
|
||||||
asyncWarehouses.when(
|
|
||||||
loading: () => const SizedBox(),
|
|
||||||
error: (_, __) => const SizedBox(),
|
|
||||||
data: (warehouses) => Container(
|
|
||||||
height: 36,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(color: AppTheme.border),
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
color: AppTheme.surface,
|
|
||||||
),
|
|
||||||
child: DropdownButtonHideUnderline(
|
|
||||||
child: DropdownButton<int?>(
|
|
||||||
value: _warehouseFilter,
|
|
||||||
hint: const Text('全部仓库',
|
|
||||||
style: TextStyle(fontSize: 13)),
|
|
||||||
items: [
|
|
||||||
const DropdownMenuItem<int?>(
|
|
||||||
value: null,
|
|
||||||
child: Text('全部仓库',
|
|
||||||
style: TextStyle(fontSize: 13))),
|
|
||||||
...warehouses.map((w) =>
|
|
||||||
DropdownMenuItem<int?>(
|
|
||||||
value: w.id,
|
|
||||||
child: Text(w.name,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 13)))),
|
|
||||||
],
|
|
||||||
onChanged: (v) {
|
|
||||||
setState(() => _warehouseFilter = v);
|
|
||||||
ref
|
|
||||||
.read(inventoryListProvider.notifier)
|
|
||||||
.setWarehouseId(v);
|
|
||||||
},
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
color: AppTheme.textPrimary),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 200,
|
width: 200,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
@@ -185,15 +154,22 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
columns: const [
|
columns: [
|
||||||
DataColumn(label: Text('商品编码')),
|
const DataColumn(label: Text('商品编码')),
|
||||||
DataColumn(label: Text('商品名称')),
|
const DataColumn(label: Text('商品名称')),
|
||||||
DataColumn(label: Text('品牌')),
|
const DataColumn(label: Text('品牌')),
|
||||||
DataColumn(label: Text('规格')),
|
const DataColumn(label: Text('规格')),
|
||||||
DataColumn(label: Text('仓库')),
|
DataColumn(
|
||||||
DataColumn(label: Text('库存'), numeric: true),
|
label: FilterableColumnHeader(
|
||||||
DataColumn(label: Text('安全库存'), numeric: true),
|
text: '仓库',
|
||||||
DataColumn(label: Text('状态')),
|
options: warehouseOptions,
|
||||||
|
selected: _filterWarehouse,
|
||||||
|
onChanged: (v) => setState(() => _filterWarehouse = v),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const DataColumn(label: Text('库存'), numeric: true),
|
||||||
|
const DataColumn(label: Text('安全库存'), numeric: true),
|
||||||
|
const DataColumn(label: Text('状态')),
|
||||||
],
|
],
|
||||||
rows: items.isEmpty
|
rows: items.isEmpty
|
||||||
? [
|
? [
|
||||||
@@ -210,7 +186,7 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
|||||||
DataCell(SizedBox()),
|
DataCell(SizedBox()),
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
: items
|
: filteredItems
|
||||||
.map((item) => DataRow(
|
.map((item) => DataRow(
|
||||||
color: WidgetStateProperty.resolveWith(
|
color: WidgetStateProperty.resolveWith(
|
||||||
(states) {
|
(states) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import '../../core/theme/app_theme.dart';
|
|||||||
import '../../models/product.dart';
|
import '../../models/product.dart';
|
||||||
import '../../providers/product_provider.dart';
|
import '../../providers/product_provider.dart';
|
||||||
import '../../widgets/data_table_card.dart';
|
import '../../widgets/data_table_card.dart';
|
||||||
|
import '../../widgets/multi_select_dropdown.dart' show FilterableColumnHeader;
|
||||||
import '../../widgets/page_scaffold.dart';
|
import '../../widgets/page_scaffold.dart';
|
||||||
|
|
||||||
class ProductsScreen extends ConsumerStatefulWidget {
|
class ProductsScreen extends ConsumerStatefulWidget {
|
||||||
@@ -17,6 +18,7 @@ class ProductsScreen extends ConsumerStatefulWidget {
|
|||||||
class _ProductsScreenState extends ConsumerState<ProductsScreen> {
|
class _ProductsScreenState extends ConsumerState<ProductsScreen> {
|
||||||
final _searchCtrl = TextEditingController();
|
final _searchCtrl = TextEditingController();
|
||||||
Timer? _debounce;
|
Timer? _debounce;
|
||||||
|
Set<String> _filterBrand = {};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -133,6 +135,17 @@ class _ProductsScreenState extends ConsumerState<ProductsScreen> {
|
|||||||
|
|
||||||
Widget _buildProductList(
|
Widget _buildProductList(
|
||||||
List<Product> products, int totalCount, int page) {
|
List<Product> products, int totalCount, int page) {
|
||||||
|
// 品牌选项从当前数据中派生,客户端筛选
|
||||||
|
final brandOptions = products
|
||||||
|
.map((p) => p.brand ?? '')
|
||||||
|
.where((s) => s.isNotEmpty)
|
||||||
|
.toSet()
|
||||||
|
.toList()
|
||||||
|
..sort();
|
||||||
|
final filteredProducts = _filterBrand.isEmpty
|
||||||
|
? products
|
||||||
|
: products.where((p) => _filterBrand.contains(p.brand ?? '')).toList();
|
||||||
|
|
||||||
return DataTableCard(
|
return DataTableCard(
|
||||||
totalCount: totalCount,
|
totalCount: totalCount,
|
||||||
page: page,
|
page: page,
|
||||||
@@ -160,18 +173,25 @@ class _ProductsScreenState extends ConsumerState<ProductsScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
columns: const [
|
columns: [
|
||||||
DataColumn(label: Text('商品编码')),
|
const DataColumn(label: Text('商品编码')),
|
||||||
DataColumn(label: Text('商品名称')),
|
const DataColumn(label: Text('商品名称')),
|
||||||
DataColumn(label: Text('品牌')),
|
DataColumn(
|
||||||
DataColumn(label: Text('规格')),
|
label: FilterableColumnHeader(
|
||||||
DataColumn(label: Text('单位')),
|
text: '品牌',
|
||||||
DataColumn(label: Text('进价'), numeric: true),
|
options: brandOptions,
|
||||||
DataColumn(label: Text('售价'), numeric: true),
|
selected: _filterBrand,
|
||||||
DataColumn(label: Text('安全库存'), numeric: true),
|
onChanged: (v) => setState(() => _filterBrand = v),
|
||||||
DataColumn(label: Text('操作')),
|
),
|
||||||
|
),
|
||||||
|
const DataColumn(label: Text('规格')),
|
||||||
|
const DataColumn(label: Text('单位')),
|
||||||
|
const DataColumn(label: Text('进价'), numeric: true),
|
||||||
|
const DataColumn(label: Text('售价'), numeric: true),
|
||||||
|
const DataColumn(label: Text('安全库存'), numeric: true),
|
||||||
|
const DataColumn(label: Text('操作')),
|
||||||
],
|
],
|
||||||
rows: products.isEmpty
|
rows: filteredProducts.isEmpty
|
||||||
? [
|
? [
|
||||||
const DataRow(cells: [
|
const DataRow(cells: [
|
||||||
DataCell(SizedBox()),
|
DataCell(SizedBox()),
|
||||||
@@ -186,7 +206,7 @@ class _ProductsScreenState extends ConsumerState<ProductsScreen> {
|
|||||||
DataCell(SizedBox()),
|
DataCell(SizedBox()),
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
: products
|
: filteredProducts
|
||||||
.map((p) => DataRow(
|
.map((p) => DataRow(
|
||||||
cells: [
|
cells: [
|
||||||
DataCell(Text(p.code,
|
DataCell(Text(p.code,
|
||||||
|
|||||||
@@ -176,31 +176,37 @@ class _FilterableColumnHeaderState extends State<FilterableColumnHeader> {
|
|||||||
onEnter: (_) => setState(() => _hovered = true),
|
onEnter: (_) => setState(() => _hovered = true),
|
||||||
onExit: (_) => setState(() => _hovered = false),
|
onExit: (_) => setState(() => _hovered = false),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(widget.text),
|
Text(widget.text),
|
||||||
AnimatedOpacity(
|
Row(
|
||||||
opacity: showIcon ? 1.0 : 0.0,
|
mainAxisSize: MainAxisSize.min,
|
||||||
duration: const Duration(milliseconds: 120),
|
children: [
|
||||||
child: InkWell(
|
AnimatedOpacity(
|
||||||
onTap: widget.options.isEmpty ? null : () => _show(context),
|
opacity: showIcon ? 1.0 : 0.0,
|
||||||
borderRadius: BorderRadius.circular(4),
|
duration: const Duration(milliseconds: 120),
|
||||||
child: Padding(
|
child: InkWell(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 2),
|
onTap: widget.options.isEmpty ? null : () => _show(context),
|
||||||
child: Icon(
|
borderRadius: BorderRadius.circular(4),
|
||||||
active ? Icons.filter_alt : Icons.filter_alt_outlined,
|
child: Padding(
|
||||||
size: 13,
|
padding: const EdgeInsets.symmetric(horizontal: 3),
|
||||||
color: active ? AppTheme.primary : AppTheme.textSecondary,
|
child: Icon(
|
||||||
|
active ? Icons.filter_alt : Icons.filter_alt_outlined,
|
||||||
|
size: 16,
|
||||||
|
color: active ? AppTheme.primary : AppTheme.textSecondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (active)
|
||||||
|
InkWell(
|
||||||
|
onTap: () => widget.onChanged({}),
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
child: const Icon(Icons.cancel, size: 14,
|
||||||
|
color: AppTheme.primary),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
if (active)
|
|
||||||
InkWell(
|
|
||||||
onTap: () => widget.onChanged({}),
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
child: const Icon(Icons.cancel, size: 12, color: AppTheme.primary),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user