overtime_list_page.dart 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import 'package:go_router/go_router.dart';
  4. import '../shell/nav_bar_config.dart';
  5. import '../../core/utils/date_utils.dart' as du;
  6. import '../../shared/widgets/filter_tabs.dart';
  7. import '../../shared/widgets/list_card.dart';
  8. import '../../shared/widgets/status_tag.dart';
  9. import '../../shared/widgets/empty_state.dart';
  10. import '../../shared/widgets/loading_widget.dart';
  11. import 'overtime_list_controller.dart';
  12. import '../../core/i18n/app_localizations.dart';
  13. import 'overtime_model.dart';
  14. class OvertimeListPage extends ConsumerStatefulWidget {
  15. const OvertimeListPage({super.key});
  16. @override
  17. ConsumerState<OvertimeListPage> createState() => _OvertimeListPageState();
  18. }
  19. class _OvertimeListPageState extends ConsumerState<OvertimeListPage> {
  20. final _scrollCtrl = ScrollController();
  21. static const _tabs = ['全部', '草稿', '审批中', '已通过', '已拒绝'];
  22. static const _tabStatusKeys = [
  23. '',
  24. 'draft',
  25. 'pending',
  26. 'approved',
  27. 'rejected',
  28. ];
  29. @override
  30. void initState() {
  31. super.initState();
  32. _scrollCtrl.addListener(_onScroll);
  33. }
  34. void _onScroll() {
  35. if (_scrollCtrl.position.pixels >=
  36. _scrollCtrl.position.maxScrollExtent - 100) {
  37. ref.read(overtimePageProvider.notifier).state++;
  38. }
  39. }
  40. @override
  41. void dispose() {
  42. _scrollCtrl.removeListener(_onScroll);
  43. _scrollCtrl.dispose();
  44. super.dispose();
  45. }
  46. @override
  47. Widget build(BuildContext context) {
  48. final status = ref.watch(overtimeStatusFilterProvider);
  49. final itemsAsync = ref.watch(overtimeListProvider);
  50. final l10n = AppLocalizations.of(context);
  51. int selectedIndex = _tabStatusKeys.indexOf(status);
  52. if (selectedIndex < 0) selectedIndex = 0;
  53. ref
  54. .read(navBarConfigProvider.notifier)
  55. .update(
  56. NavBarConfig(
  57. title: l10n.get('overtimeList'),
  58. showBack: true,
  59. onBack: () => context.pop(),
  60. ),
  61. );
  62. return Column(
  63. children: [
  64. FilterTabs(
  65. tabs: _tabs,
  66. selectedIndex: selectedIndex,
  67. onChanged: (index) {
  68. ref.read(overtimeStatusFilterProvider.notifier).state =
  69. _tabStatusKeys[index];
  70. },
  71. ),
  72. Expanded(
  73. child: itemsAsync.when(
  74. loading: () => const LoadingWidget(),
  75. error: (_, _) => const EmptyState(message: '加载失败'),
  76. data: (items) => items.isEmpty
  77. ? const EmptyState(message: '暂无加班记录')
  78. : RefreshIndicator(
  79. onRefresh: () async {
  80. ref.invalidate(overtimeListProvider);
  81. },
  82. child: ListView.builder(
  83. controller: _scrollCtrl,
  84. padding: const EdgeInsets.fromLTRB(16, 16, 16, 24),
  85. itemCount: items.length,
  86. itemBuilder: (_, i) => Padding(
  87. padding: const EdgeInsets.only(bottom: 16),
  88. child: _buildItem(items[i]),
  89. ),
  90. ),
  91. ),
  92. ),
  93. ),
  94. ],
  95. );
  96. }
  97. Widget _buildItem(OvertimeModel item) {
  98. return ListCard(
  99. cardNo: item.applicationNo,
  100. description: '${item.otType} · ${item.compensationType}',
  101. amount: '${item.otHours.toStringAsFixed(1)}小时',
  102. date: du.DateUtils.formatDate(item.otDate),
  103. statusTag: StatusTag.fromStatus(item.status),
  104. onTap: () => context.push('/overtime/detail/${item.id}'),
  105. );
  106. }
  107. }