feat(client): 全模块 API 对接 + 修复登陆跳转 + 菜单 UI 优化
API 对接: - 入库/出库/库存/财务/往来单位/基础数据全部对接后端 REST API - 新增 repositories、providers、models 层,统一分层架构 - auth 从 flutter_secure_storage 迁移到 shared_preferences 登陆跳转修复: - 将 _RouterNotifier 提取为独立 Riverpod provider,appRouterProvider 使用 ref.read 避免依赖链导致 router 重建后跳回 /login - redirect 函数新增 initialized 守卫,防止 auth 未恢复时误重定向 - 添加调试日志(Router/Auth/ApiClient)定位 401 触发的 logout 链路 退出菜单 UI: - 去掉 ListTile,改用 Row + 自定义 padding,文字左对齐 - MouseRegion + AnimatedContainer 实现 hover 高亮(普通项蓝底/退出红底) - 菜单圆角 6px,elevation 8,分割线高度 1px Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../../widgets/page_scaffold.dart';
|
||||
import '../../widgets/data_table_card.dart';
|
||||
import '../../widgets/status_badge.dart';
|
||||
import '../../core/theme/app_theme.dart';
|
||||
import '../../models/stock_out.dart';
|
||||
import '../../providers/stock_out_provider.dart';
|
||||
import '../../widgets/data_table_card.dart';
|
||||
import '../../widgets/page_scaffold.dart';
|
||||
import '../../widgets/status_badge.dart';
|
||||
|
||||
class StockOutListScreen extends ConsumerStatefulWidget {
|
||||
const StockOutListScreen({super.key});
|
||||
@@ -14,128 +16,28 @@ class StockOutListScreen extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
||||
int _page = 1;
|
||||
final _searchCtrl = TextEditingController();
|
||||
String _statusFilter = '全部';
|
||||
String _typeFilter = '全部';
|
||||
String _statusFilter = '';
|
||||
DateTimeRange? _dateRange;
|
||||
|
||||
final List<Map<String, dynamic>> _mockOrders = [
|
||||
{
|
||||
'no': 'CK20260401001',
|
||||
'department': '餐饮部',
|
||||
'purpose': '宴会用酒',
|
||||
'warehouse': '主仓库',
|
||||
'amount': '15600.00',
|
||||
'status': OrderStatus.approved,
|
||||
'creator': '李四',
|
||||
'date': '2026-04-01',
|
||||
'type': '领用',
|
||||
},
|
||||
{
|
||||
'no': 'CK20260401002',
|
||||
'department': '客房部',
|
||||
'purpose': '客房迷你吧补货',
|
||||
'warehouse': '主仓库',
|
||||
'amount': '8200.00',
|
||||
'status': OrderStatus.approved,
|
||||
'creator': '王五',
|
||||
'date': '2026-04-01',
|
||||
'type': '领用',
|
||||
},
|
||||
{
|
||||
'no': 'CK20260402001',
|
||||
'department': '行政部',
|
||||
'purpose': '接待用酒',
|
||||
'warehouse': '副仓库',
|
||||
'amount': '32000.00',
|
||||
'status': OrderStatus.pending,
|
||||
'creator': '张三',
|
||||
'date': '2026-04-02',
|
||||
'type': '调拨',
|
||||
},
|
||||
{
|
||||
'no': 'CK20260402002',
|
||||
'department': '餐饮部',
|
||||
'purpose': '婚宴用酒',
|
||||
'warehouse': '主仓库',
|
||||
'amount': '68500.00',
|
||||
'status': OrderStatus.approved,
|
||||
'creator': '李四',
|
||||
'date': '2026-04-02',
|
||||
'type': '领用',
|
||||
},
|
||||
{
|
||||
'no': 'CK20260403001',
|
||||
'department': '采购部',
|
||||
'purpose': '退货',
|
||||
'warehouse': '副仓库',
|
||||
'amount': '12000.00',
|
||||
'status': OrderStatus.pending,
|
||||
'creator': '王五',
|
||||
'date': '2026-04-03',
|
||||
'type': '退货',
|
||||
},
|
||||
{
|
||||
'no': 'CK20260403002',
|
||||
'department': '餐饮部',
|
||||
'purpose': '散客用酒',
|
||||
'warehouse': '主仓库',
|
||||
'amount': '5400.00',
|
||||
'status': OrderStatus.draft,
|
||||
'creator': '张三',
|
||||
'date': '2026-04-03',
|
||||
'type': '领用',
|
||||
},
|
||||
{
|
||||
'no': 'CK20260404001',
|
||||
'department': '客房部',
|
||||
'purpose': 'VIP客房补充',
|
||||
'warehouse': '主仓库',
|
||||
'amount': '18900.00',
|
||||
'status': OrderStatus.approved,
|
||||
'creator': '李四',
|
||||
'date': '2026-04-04',
|
||||
'type': '领用',
|
||||
},
|
||||
{
|
||||
'no': 'CK20260404002',
|
||||
'department': '行政部',
|
||||
'purpose': '年度总结宴',
|
||||
'warehouse': '主仓库',
|
||||
'amount': '45000.00',
|
||||
'status': OrderStatus.pending,
|
||||
'creator': '王五',
|
||||
'date': '2026-04-04',
|
||||
'type': '领用',
|
||||
},
|
||||
];
|
||||
String? get _startDate => _dateRange != null
|
||||
? '${_dateRange!.start.year}-${_dateRange!.start.month.toString().padLeft(2, '0')}-${_dateRange!.start.day.toString().padLeft(2, '0')}'
|
||||
: null;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchCtrl.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
String? get _endDate => _dateRange != null
|
||||
? '${_dateRange!.end.year}-${_dateRange!.end.month.toString().padLeft(2, '0')}-${_dateRange!.end.day.toString().padLeft(2, '0')}'
|
||||
: null;
|
||||
|
||||
List<Map<String, dynamic>> get _filtered {
|
||||
return _mockOrders.where((o) {
|
||||
if (_typeFilter != '全部' && o['type'] != _typeFilter) return false;
|
||||
if (_statusFilter != '全部') {
|
||||
final map = {
|
||||
'草稿': OrderStatus.draft,
|
||||
'待审核': OrderStatus.pending,
|
||||
'已审核': OrderStatus.approved,
|
||||
'已拒绝': OrderStatus.rejected,
|
||||
};
|
||||
if (o['status'] != map[_statusFilter]) return false;
|
||||
}
|
||||
final q = _searchCtrl.text.toLowerCase();
|
||||
if (q.isNotEmpty) {
|
||||
final no = (o['no'] as String).toLowerCase();
|
||||
final dept = (o['department'] as String).toLowerCase();
|
||||
if (!no.contains(q) && !dept.contains(q)) return false;
|
||||
}
|
||||
return true;
|
||||
}).toList();
|
||||
OrderStatus _apiStatusToEnum(String status) {
|
||||
switch (status) {
|
||||
case 'pending':
|
||||
return OrderStatus.pending;
|
||||
case 'approved':
|
||||
return OrderStatus.approved;
|
||||
case 'rejected':
|
||||
return OrderStatus.rejected;
|
||||
default:
|
||||
return OrderStatus.draft;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -144,28 +46,61 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
||||
title: '出库管理',
|
||||
tabs: const [
|
||||
Tab(text: '出库单'),
|
||||
Tab(text: '出库查询'),
|
||||
Tab(text: '出库审核'),
|
||||
],
|
||||
tabViews: [
|
||||
_buildList(),
|
||||
_buildList(),
|
||||
_buildList(forceStatus: OrderStatus.pending),
|
||||
_buildListTab(filterStatus: null),
|
||||
_buildListTab(filterStatus: 'pending'),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildList({OrderStatus? forceStatus}) {
|
||||
final orders = forceStatus != null
|
||||
? _mockOrders
|
||||
.where((o) => o['status'] == forceStatus)
|
||||
.toList()
|
||||
: _filtered;
|
||||
Widget _buildListTab({String? filterStatus}) {
|
||||
final asyncOrders = ref.watch(stockOutListProvider);
|
||||
return asyncOrders.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('加载失败:$e',
|
||||
style: const TextStyle(color: AppTheme.danger)),
|
||||
const SizedBox(height: 12),
|
||||
ElevatedButton(
|
||||
onPressed: () =>
|
||||
ref.read(stockOutListProvider.notifier).reload(),
|
||||
child: const Text('重试'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
data: (result) {
|
||||
final orders = filterStatus != null
|
||||
? result.data
|
||||
.where((o) => o.status == filterStatus)
|
||||
.toList()
|
||||
: result.data;
|
||||
return _buildOrderTable(
|
||||
orders: orders,
|
||||
totalCount: filterStatus != null ? orders.length : result.total,
|
||||
page: result.page,
|
||||
showStatusFilter: filterStatus == null,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOrderTable({
|
||||
required List<StockOutOrder> orders,
|
||||
required int totalCount,
|
||||
required int page,
|
||||
required bool showStatusFilter,
|
||||
}) {
|
||||
return DataTableCard(
|
||||
totalCount: orders.length,
|
||||
page: _page,
|
||||
onPageChanged: (p) => setState(() => _page = p),
|
||||
totalCount: totalCount,
|
||||
page: page,
|
||||
onPageChanged: (p) =>
|
||||
ref.read(stockOutListProvider.notifier).setPage(p),
|
||||
toolbar: Row(
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
@@ -173,110 +108,136 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
||||
icon: const Icon(Icons.add, size: 16),
|
||||
label: const Text('新建出库单'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.file_download_outlined, size: 16),
|
||||
label: const Text('导出'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.print_outlined, size: 16),
|
||||
label: const Text('打印'),
|
||||
),
|
||||
const Spacer(),
|
||||
// Type filter
|
||||
_DropdownFilter(
|
||||
value: _typeFilter,
|
||||
items: ['全部', '领用', '调拨', '退货'],
|
||||
onChanged: (v) => setState(() => _typeFilter = v!),
|
||||
hint: '出库类型',
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
if (forceStatus == null)
|
||||
_DropdownFilter(
|
||||
if (showStatusFilter) ...[
|
||||
_StatusFilterDropdown(
|
||||
value: _statusFilter,
|
||||
items: ['全部', '草稿', '待审核', '已审核', '已拒绝'],
|
||||
onChanged: (v) => setState(() => _statusFilter = v!),
|
||||
hint: '状态',
|
||||
onChanged: (v) {
|
||||
setState(() => _statusFilter = v ?? '');
|
||||
ref
|
||||
.read(stockOutListProvider.notifier)
|
||||
.setStatus(v ?? '');
|
||||
},
|
||||
),
|
||||
if (forceStatus == null) const SizedBox(width: 8),
|
||||
SizedBox(
|
||||
width: 180,
|
||||
child: TextField(
|
||||
controller: _searchCtrl,
|
||||
decoration: const InputDecoration(
|
||||
hintText: '搜索单号/部门',
|
||||
prefixIcon: Icon(Icons.search, size: 16),
|
||||
hintStyle: TextStyle(fontSize: 13),
|
||||
),
|
||||
onChanged: (_) => setState(() => _page = 1),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
OutlinedButton.icon(
|
||||
onPressed: _pickDateRange,
|
||||
icon: const Icon(Icons.date_range, size: 16),
|
||||
label: Text(
|
||||
_dateRange == null
|
||||
? '选择日期'
|
||||
: '$_startDate ~ $_endDate',
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
),
|
||||
if (_dateRange != null) ...[
|
||||
const SizedBox(width: 4),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.clear, size: 16),
|
||||
onPressed: () {
|
||||
setState(() => _dateRange = null);
|
||||
ref
|
||||
.read(stockOutListProvider.notifier)
|
||||
.setDateRange(null, null);
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('出库单号')),
|
||||
DataColumn(label: Text('出库类型')),
|
||||
DataColumn(label: Text('领用部门')),
|
||||
DataColumn(label: Text('用途')),
|
||||
DataColumn(label: Text('客户/往来单位')),
|
||||
DataColumn(label: Text('仓库')),
|
||||
DataColumn(label: Text('金额'), numeric: true),
|
||||
DataColumn(label: Text('状态')),
|
||||
DataColumn(label: Text('日期')),
|
||||
DataColumn(label: Text('操作')),
|
||||
],
|
||||
rows: orders
|
||||
.map((o) => DataRow(cells: [
|
||||
DataCell(Text(o['no'] as String,
|
||||
style: const TextStyle(
|
||||
color: AppTheme.primary,
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12))),
|
||||
DataCell(_TypeBadge(o['type'] as String)),
|
||||
DataCell(Text(o['department'] as String)),
|
||||
DataCell(Text(o['purpose'] as String,
|
||||
overflow: TextOverflow.ellipsis)),
|
||||
DataCell(Text(o['warehouse'] as String)),
|
||||
DataCell(Text('¥${o['amount']}')),
|
||||
DataCell(StatusBadge(o['status'] as OrderStatus)),
|
||||
DataCell(Text(o['date'] as String)),
|
||||
DataCell(Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: const Text('查看',
|
||||
style: TextStyle(fontSize: 12))),
|
||||
if ((o['status'] as OrderStatus) ==
|
||||
OrderStatus.pending)
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: const Text('审核',
|
||||
style: TextStyle(
|
||||
color: AppTheme.success,
|
||||
fontSize: 12))),
|
||||
if ((o['status'] as OrderStatus) ==
|
||||
OrderStatus.draft) ...[
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: const Text('编辑',
|
||||
style: TextStyle(fontSize: 12))),
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: const Text('删除',
|
||||
style: TextStyle(
|
||||
color: AppTheme.danger,
|
||||
fontSize: 12))),
|
||||
rows: orders.isEmpty
|
||||
? [
|
||||
const DataRow(cells: [
|
||||
DataCell(SizedBox()),
|
||||
DataCell(Text('暂无出库单',
|
||||
style: TextStyle(color: AppTheme.textSecondary))),
|
||||
DataCell(SizedBox()),
|
||||
DataCell(SizedBox()),
|
||||
DataCell(SizedBox()),
|
||||
DataCell(SizedBox()),
|
||||
DataCell(SizedBox()),
|
||||
])
|
||||
]
|
||||
: orders
|
||||
.map((o) => DataRow(
|
||||
cells: [
|
||||
DataCell(Text(o.orderNo,
|
||||
style: const TextStyle(
|
||||
color: AppTheme.primary,
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12))),
|
||||
DataCell(Text(o.partnerName ?? '-')),
|
||||
DataCell(Text(o.warehouseName ?? '-')),
|
||||
DataCell(Text(o.totalAmount != null
|
||||
? '¥${o.totalAmount!.toStringAsFixed(2)}'
|
||||
: '-')),
|
||||
DataCell(StatusBadge(
|
||||
_apiStatusToEnum(o.status))),
|
||||
DataCell(Text(o.orderDate?.substring(0, 10) ?? '-')),
|
||||
DataCell(Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (o.status == 'draft')
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
_confirmSubmit(context, o),
|
||||
child: const Text('提交',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppTheme.primary)),
|
||||
),
|
||||
if (o.status == 'pending') ...[
|
||||
TextButton(
|
||||
key: Key('btn_approve_${o.id}'),
|
||||
onPressed: () =>
|
||||
_confirmApprove(context, o),
|
||||
child: const Text('通过',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppTheme.success)),
|
||||
),
|
||||
TextButton(
|
||||
key: Key('btn_reject_${o.id}'),
|
||||
onPressed: () =>
|
||||
_confirmReject(context, o),
|
||||
child: const Text('拒绝',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppTheme.danger)),
|
||||
),
|
||||
],
|
||||
],
|
||||
)),
|
||||
],
|
||||
],
|
||||
)),
|
||||
]))
|
||||
.toList(),
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _pickDateRange() async {
|
||||
final range = await showDateRangePicker(
|
||||
context: context,
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime(2030),
|
||||
initialDateRange: _dateRange,
|
||||
);
|
||||
if (range != null) {
|
||||
setState(() => _dateRange = range);
|
||||
ref
|
||||
.read(stockOutListProvider.notifier)
|
||||
.setDateRange(_startDate, _endDate);
|
||||
}
|
||||
}
|
||||
|
||||
void _showCreateDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -284,63 +245,144 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
||||
title: const Text('新建出库单'),
|
||||
content: const SizedBox(
|
||||
width: 400,
|
||||
child: Text('出库单创建功能完整实现请参考入库单流程。'),
|
||||
child: Text('出库单完整创建功能请参考入库单流程。'),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
child: const Text('关闭')),
|
||||
child: const Text('取消')),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TypeBadge extends StatelessWidget {
|
||||
final String type;
|
||||
const _TypeBadge(this.type);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Color color;
|
||||
switch (type) {
|
||||
case '领用':
|
||||
color = AppTheme.primary;
|
||||
break;
|
||||
case '调拨':
|
||||
color = AppTheme.accent;
|
||||
break;
|
||||
case '退货':
|
||||
color = AppTheme.danger;
|
||||
break;
|
||||
default:
|
||||
color = AppTheme.textSecondary;
|
||||
}
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
border: Border.all(color: color.withOpacity(0.3)),
|
||||
Future<void> _confirmSubmit(
|
||||
BuildContext context, StockOutOrder o) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('提交审核'),
|
||||
content: Text('确认提交出库单「${o.orderNo}」?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(false),
|
||||
child: const Text('取消')),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(true),
|
||||
child: const Text('提交')),
|
||||
],
|
||||
),
|
||||
child: Text(type,
|
||||
style: TextStyle(
|
||||
color: color, fontSize: 12, fontWeight: FontWeight.w500)),
|
||||
);
|
||||
if (confirmed == true && mounted) {
|
||||
try {
|
||||
await ref
|
||||
.read(stockOutListProvider.notifier)
|
||||
.submitOrder(o.id);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||
content: Text('已提交审核'),
|
||||
backgroundColor: AppTheme.success));
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text('提交失败:$e'),
|
||||
backgroundColor: AppTheme.danger));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _confirmApprove(
|
||||
BuildContext context, StockOutOrder o) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('审核确认'),
|
||||
content: Text('确认审核通过出库单「${o.orderNo}」?审核后将出库并扣减库存。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(false),
|
||||
child: const Text('取消')),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(true),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppTheme.success,
|
||||
foregroundColor: Colors.white),
|
||||
child: const Text('通过'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirmed == true && mounted) {
|
||||
try {
|
||||
await ref
|
||||
.read(stockOutListProvider.notifier)
|
||||
.approveOrder(o.id);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||
content: Text('审核通过'), backgroundColor: AppTheme.success));
|
||||
}
|
||||
} catch (e) {
|
||||
// Show inventory-shortage error clearly
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text('审核失败:$e'),
|
||||
backgroundColor: AppTheme.danger,
|
||||
duration: const Duration(seconds: 5)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _confirmReject(
|
||||
BuildContext context, StockOutOrder o) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('拒绝确认'),
|
||||
content: Text('确认拒绝出库单「${o.orderNo}」?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(false),
|
||||
child: const Text('取消')),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(true),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppTheme.danger,
|
||||
foregroundColor: Colors.white),
|
||||
child: const Text('拒绝'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirmed == true && mounted) {
|
||||
try {
|
||||
await ref
|
||||
.read(stockOutListProvider.notifier)
|
||||
.rejectOrder(o.id);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||
content: Text('已拒绝'), backgroundColor: AppTheme.accent));
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text('操作失败:$e'),
|
||||
backgroundColor: AppTheme.danger));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _DropdownFilter extends StatelessWidget {
|
||||
class _StatusFilterDropdown extends StatelessWidget {
|
||||
final String value;
|
||||
final List<String> items;
|
||||
final ValueChanged<String?> onChanged;
|
||||
final String hint;
|
||||
|
||||
const _DropdownFilter({
|
||||
const _StatusFilterDropdown({
|
||||
required this.value,
|
||||
required this.items,
|
||||
required this.onChanged,
|
||||
required this.hint,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -355,16 +397,16 @@ class _DropdownFilter extends StatelessWidget {
|
||||
),
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<String>(
|
||||
value: value,
|
||||
hint: Text(hint, style: const TextStyle(fontSize: 13)),
|
||||
items: items
|
||||
.map((s) => DropdownMenuItem(
|
||||
value: s,
|
||||
child: Text(s, style: const TextStyle(fontSize: 13))))
|
||||
.toList(),
|
||||
value: value.isEmpty ? '' : value,
|
||||
items: const [
|
||||
DropdownMenuItem(value: '', child: Text('全部状态', style: TextStyle(fontSize: 13))),
|
||||
DropdownMenuItem(value: 'draft', child: Text('草稿', style: TextStyle(fontSize: 13))),
|
||||
DropdownMenuItem(value: 'pending', child: Text('待审核', style: TextStyle(fontSize: 13))),
|
||||
DropdownMenuItem(value: 'approved', child: Text('已审核', style: TextStyle(fontSize: 13))),
|
||||
DropdownMenuItem(value: 'rejected', child: Text('已拒绝', style: TextStyle(fontSize: 13))),
|
||||
],
|
||||
onChanged: onChanged,
|
||||
style: const TextStyle(
|
||||
fontSize: 13, color: AppTheme.textPrimary),
|
||||
style: const TextStyle(fontSize: 13, color: AppTheme.textPrimary),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user