refactor(client): 库存屏移除预警/流水死 tab 及关联死代码
build() 只渲染库存视图(预警并入 KPI 缺货卡点击筛选、流水移到商品详情), _buildWarningTab/_buildLogTab/_warningCard/_logCard/_DirectionBadge 已成死代码, 连同未用的 data_table_card/tab_state_provider/page_scaffold import 一并移除(约 -290 行)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,8 +13,7 @@ import '../../models/inventory.dart';
|
||||
import '../../core/auth/auth_state.dart';
|
||||
import '../../widgets/write_guard.dart';
|
||||
import '../../providers/inventory_provider.dart';
|
||||
import '../../widgets/data_table_card.dart';
|
||||
import '../../widgets/kpi_card.dart' show StatusPill; // 旧 tab/移动卡片用,待统一 DsBadge
|
||||
import '../../widgets/kpi_card.dart' show StatusPill; // 移动卡片状态药丸
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import '../../widgets/ds/ds_atoms.dart';
|
||||
import '../../widgets/ds/ds_kpi.dart';
|
||||
@@ -23,14 +22,12 @@ import '../../widgets/mobile_list_card.dart';
|
||||
import '../../core/theme/app_dims.g.dart';
|
||||
import '../../widgets/multi_select_dropdown.dart' show ColDef, ColumnToggleButton, FilterableColumnHeader;
|
||||
import '../../core/storage/column_prefs.dart';
|
||||
import '../../widgets/page_scaffold.dart';
|
||||
import '../../core/utils/export_util.dart';
|
||||
import '../../core/utils/print_util.dart';
|
||||
import '../../core/utils/dialog_util.dart';
|
||||
import '../../widgets/label_preview_dialog.dart';
|
||||
import '../../providers/product_provider.dart';
|
||||
import '../../providers/product_option_provider.dart';
|
||||
import '../../providers/tab_state_provider.dart';
|
||||
import '../../providers/shop_provider.dart' show shopInfoProvider;
|
||||
|
||||
class InventoryListScreen extends ConsumerStatefulWidget {
|
||||
@@ -469,63 +466,6 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 库存预警:窄屏卡片
|
||||
Widget _warningCard(Inventory item) {
|
||||
return MobileListCard(
|
||||
onTap: item.productId != null
|
||||
? () => context.push('/products/${item.productId}')
|
||||
: null,
|
||||
title: Text(item.productName.isEmpty ? '-' : item.productName),
|
||||
subtitle: item.productCode.isEmpty ? null : Text(item.productCode),
|
||||
trailing: _InventoryStatusBadge(item),
|
||||
fields: [
|
||||
MobileCardField('仓库', item.warehouseName.isEmpty ? '-' : item.warehouseName),
|
||||
MobileCardField('当前库存', null,
|
||||
valueWidget: Text(
|
||||
item.quantity.toStringAsFixed(0),
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: item.quantity == 0 ? context.tokens.danger : context.tokens.accent),
|
||||
)),
|
||||
MobileCardField('安全库存', '${item.minStock}'),
|
||||
MobileCardField('缺口', null,
|
||||
valueWidget: Text(
|
||||
'${item.minStock! - item.quantity.toInt()}',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: context.tokens.danger,
|
||||
fontWeight: FontWeight.w600),
|
||||
)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 流水记录:窄屏卡片
|
||||
Widget _logCard(dynamic log) {
|
||||
return MobileListCard(
|
||||
title: Text(log.productName ?? '-'),
|
||||
subtitle: Text(log.createdAt?.substring(0, 19) ?? '-'),
|
||||
trailing: _DirectionBadge(log.direction),
|
||||
fields: [
|
||||
MobileCardField('仓库', log.warehouseName ?? '-'),
|
||||
MobileCardField('数量', null,
|
||||
valueWidget: Text(
|
||||
log.quantity.toStringAsFixed(0),
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: log.direction == 'in'
|
||||
? context.tokens.success
|
||||
: context.tokens.danger),
|
||||
)),
|
||||
MobileCardField('变前', log.qtyBefore?.toStringAsFixed(0) ?? '-'),
|
||||
MobileCardField('变后', log.qtyAfter?.toStringAsFixed(0) ?? '-'),
|
||||
if (log.refType != null) MobileCardField('来源', log.refType),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 照原型 inventory.html:单视图(无 tab)。库存预警并入 KPI 缺货卡点击筛选,
|
||||
@@ -931,267 +871,6 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWarningTab() {
|
||||
final asyncInventory = ref.watch(inventoryListProvider);
|
||||
return asyncInventory.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 40, color: context.tokens.muted),
|
||||
const SizedBox(height: 12),
|
||||
Text('加载失败:$e',
|
||||
style: TextStyle(color: context.tokens.muted),
|
||||
textAlign: TextAlign.center),
|
||||
const SizedBox(height: 12),
|
||||
ElevatedButton(
|
||||
onPressed: () =>
|
||||
ref.read(inventoryListProvider.notifier).reload(),
|
||||
child: const Text('重试'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
data: (result) {
|
||||
final warnings = result.data
|
||||
.where((item) =>
|
||||
item.minStock != null && item.quantity < item.minStock!)
|
||||
.toList();
|
||||
return DataTableCard(
|
||||
totalCount: warnings.length,
|
||||
page: 1,
|
||||
toolbar: Row(
|
||||
children: [
|
||||
Icon(Icons.warning_amber,
|
||||
color: context.tokens.accent, size: 18),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'共 ${warnings.length} 个商品库存低于安全库存',
|
||||
style: TextStyle(
|
||||
fontSize: 13, color: context.tokens.accent),
|
||||
),
|
||||
const Spacer(),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () => exportExcel(
|
||||
filename: '库存预警',
|
||||
headers: ['商品编码', '商品名称', '仓库', '当前库存', '安全库存', '缺口', '状态'],
|
||||
rows: warnings.map((item) => [
|
||||
item.productCode.isEmpty ? '' : item.productCode,
|
||||
item.productName.isEmpty ? '' : item.productName,
|
||||
item.warehouseName.isEmpty ? '' : item.warehouseName,
|
||||
item.quantity.toInt(),
|
||||
item.minStock ?? 0,
|
||||
item.minStock! - item.quantity.toInt(),
|
||||
item.quantity == 0 ? '缺货' : '库存不足',
|
||||
]).toList(),
|
||||
),
|
||||
icon: const Icon(Icons.download, size: 16),
|
||||
label: const Text('导出'),
|
||||
),
|
||||
],
|
||||
),
|
||||
mobileCards: warnings.map(_warningCard).toList(),
|
||||
columns: const [
|
||||
DataColumn(label: Text('商品编码')),
|
||||
DataColumn(label: Text('商品名称')),
|
||||
DataColumn(label: Text('仓库')),
|
||||
DataColumn(label: Text('当前库存'), numeric: true),
|
||||
DataColumn(label: Text('安全库存'), numeric: true),
|
||||
DataColumn(label: Text('缺口'), numeric: true),
|
||||
DataColumn(label: Text('状态')),
|
||||
],
|
||||
rows: warnings.isEmpty
|
||||
? [
|
||||
DataRow(cells: [
|
||||
const DataCell(SizedBox()),
|
||||
DataCell(Text('暂无预警商品',
|
||||
style: TextStyle(
|
||||
color: context.tokens.muted))),
|
||||
const DataCell(SizedBox()),
|
||||
const DataCell(SizedBox()),
|
||||
const DataCell(SizedBox()),
|
||||
const DataCell(SizedBox()),
|
||||
const DataCell(SizedBox()),
|
||||
])
|
||||
]
|
||||
: warnings
|
||||
.map((item) => DataRow(
|
||||
color: WidgetStateProperty.all(
|
||||
item.quantity == 0
|
||||
? context.tokens.danger.withOpacity(0.05)
|
||||
: context.tokens.accent.withOpacity(0.04)),
|
||||
cells: [
|
||||
DataCell(Text(
|
||||
item.productCode.isEmpty ? '-' : item.productCode,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12))),
|
||||
DataCell(item.productId != null
|
||||
? GestureDetector(
|
||||
onTap: () => context.push('/products/${item.productId}'),
|
||||
child: SizedBox(
|
||||
width: 180,
|
||||
child: Text(
|
||||
item.productName.isEmpty ? '-' : item.productName,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: context.tokens.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: context.tokens.primary,
|
||||
)),
|
||||
),
|
||||
)
|
||||
: Text(item.productName.isEmpty ? '-' : item.productName,
|
||||
overflow: TextOverflow.ellipsis)),
|
||||
DataCell(Text(item.warehouseName.isEmpty ? '-' : item.warehouseName)),
|
||||
DataCell(Text(
|
||||
item.quantity.toStringAsFixed(0),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: item.quantity == 0
|
||||
? context.tokens.danger
|
||||
: context.tokens.accent),
|
||||
)),
|
||||
DataCell(Text('${item.minStock}')),
|
||||
DataCell(Text(
|
||||
'${item.minStock! - item.quantity.toInt()}',
|
||||
style: TextStyle(
|
||||
color: context.tokens.danger,
|
||||
fontWeight: FontWeight.w600),
|
||||
)),
|
||||
DataCell(_InventoryStatusBadge(item)),
|
||||
],
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLogTab() {
|
||||
final asyncLogs = ref.watch(inventoryLogProvider);
|
||||
return asyncLogs.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 40, color: context.tokens.muted),
|
||||
const SizedBox(height: 12),
|
||||
Text('加载失败:$e',
|
||||
style: TextStyle(color: context.tokens.muted),
|
||||
textAlign: TextAlign.center),
|
||||
const SizedBox(height: 12),
|
||||
ElevatedButton(
|
||||
onPressed: () =>
|
||||
ref.read(inventoryLogProvider.notifier).reload(),
|
||||
child: const Text('重试'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
data: (result) {
|
||||
final logs = result.data;
|
||||
return DataTableCard(
|
||||
totalCount: result.total,
|
||||
page: result.page,
|
||||
pageSize: result.pageSize,
|
||||
onPageChanged: (p) =>
|
||||
ref.read(inventoryLogProvider.notifier).setPage(p),
|
||||
onPageSizeChanged: (s) =>
|
||||
ref.read(inventoryLogProvider.notifier).setPageSize(s),
|
||||
toolbar: Row(
|
||||
children: [
|
||||
Icon(Icons.history, color: context.tokens.primary, size: 18),
|
||||
const SizedBox(width: 8),
|
||||
const Text('库存流水记录',
|
||||
style: TextStyle(
|
||||
fontSize: 14, fontWeight: FontWeight.w500)),
|
||||
const Spacer(),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () => exportExcel(
|
||||
filename: '库存流水',
|
||||
headers: ['商品名称', '仓库', '方向', '数量', '变前', '变后', '来源', '时间'],
|
||||
rows: logs.map((log) => [
|
||||
log.productName ?? '',
|
||||
log.warehouseName ?? '',
|
||||
log.direction == 'in' ? '入库' : '出库',
|
||||
log.quantity.toInt(),
|
||||
log.qtyBefore?.toInt() ?? 0,
|
||||
log.qtyAfter?.toInt() ?? 0,
|
||||
log.refType ?? '',
|
||||
log.createdAt?.substring(0, 19) ?? '',
|
||||
]).toList(),
|
||||
),
|
||||
icon: const Icon(Icons.download, size: 16),
|
||||
label: const Text('导出'),
|
||||
),
|
||||
],
|
||||
),
|
||||
mobileCards: logs.map(_logCard).toList(),
|
||||
columns: const [
|
||||
DataColumn(label: Text('商品名称')),
|
||||
DataColumn(label: Text('仓库')),
|
||||
DataColumn(label: Text('方向')),
|
||||
DataColumn(label: Text('数量'), numeric: true),
|
||||
DataColumn(label: Text('变前'), numeric: true),
|
||||
DataColumn(label: Text('变后'), numeric: true),
|
||||
DataColumn(label: Text('来源')),
|
||||
DataColumn(label: Text('时间')),
|
||||
],
|
||||
rows: logs.isEmpty
|
||||
? [
|
||||
DataRow(cells: [
|
||||
const DataCell(SizedBox()),
|
||||
DataCell(Text('暂无流水记录',
|
||||
style: TextStyle(
|
||||
color: context.tokens.muted))),
|
||||
const DataCell(SizedBox()),
|
||||
const DataCell(SizedBox()),
|
||||
const DataCell(SizedBox()),
|
||||
const DataCell(SizedBox()),
|
||||
const DataCell(SizedBox()),
|
||||
const DataCell(SizedBox()),
|
||||
])
|
||||
]
|
||||
: logs
|
||||
.map((log) => DataRow(cells: [
|
||||
DataCell(SizedBox(
|
||||
width: 160,
|
||||
child: Text(log.productName ?? '-',
|
||||
overflow: TextOverflow.ellipsis),
|
||||
)),
|
||||
DataCell(Text(log.warehouseName ?? '-')),
|
||||
DataCell(_DirectionBadge(log.direction)),
|
||||
DataCell(Text(
|
||||
log.quantity.toStringAsFixed(0),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: log.direction == 'in'
|
||||
? context.tokens.success
|
||||
: context.tokens.danger),
|
||||
)),
|
||||
DataCell(Text(
|
||||
log.qtyBefore?.toStringAsFixed(0) ?? '-')),
|
||||
DataCell(Text(
|
||||
log.qtyAfter?.toStringAsFixed(0) ?? '-')),
|
||||
DataCell(Text(log.refType ?? '-',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: context.tokens.muted))),
|
||||
DataCell(Text(
|
||||
log.createdAt?.substring(0, 19) ?? '-',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: context.tokens.muted),
|
||||
)),
|
||||
]))
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InventoryStatusBadge extends StatelessWidget {
|
||||
@@ -1212,22 +891,6 @@ class _InventoryStatusBadge extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _DirectionBadge extends StatelessWidget {
|
||||
final String direction;
|
||||
const _DirectionBadge(this.direction);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = context.tokens;
|
||||
final isIn = direction == 'in';
|
||||
return StatusPill(
|
||||
label: isIn ? '入库' : '出库',
|
||||
color: isIn ? t.success : t.danger,
|
||||
background: isIn ? t.okSoft : t.dangerBg,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── 导入进度对话框 ──────────────────────────────────────────
|
||||
|
||||
enum _ImportStage { uploading, processing, done, error }
|
||||
|
||||
Reference in New Issue
Block a user