import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import 'package:easy_refresh/easy_refresh.dart'; import '../shell/nav_bar_config.dart'; import '../../core/theme/app_colors.dart'; import '../../core/utils/date_utils.dart' as du; import '../../shared/widgets/list_card.dart'; import '../../shared/widgets/status_tag.dart'; import '../../shared/widgets/empty_state.dart'; import '../../shared/widgets/skeleton_list_card.dart'; import '../../shared/widgets/filter_bar.dart'; import '../../core/i18n/app_localizations.dart'; import 'overtime_list_controller.dart'; import 'overtime_model.dart'; class OvertimeListPage extends ConsumerStatefulWidget { const OvertimeListPage({super.key}); @override ConsumerState createState() => _OvertimeListPageState(); } class _OvertimeListPageState extends ConsumerState with TickerProviderStateMixin { static const _tabLabels = ['全部', '草稿', '审批中', '已通过', '已拒绝']; static const _tabKeys = [ '', 'draft', 'pending', 'approved', 'rejected', ]; late final TabController _tabCtrl; @override void initState() { super.initState(); _tabCtrl = TabController(length: _tabLabels.length, vsync: this); _tabCtrl.addListener(() { if (!_tabCtrl.indexIsChanging) { ref.read(overtimeStatusFilterProvider.notifier).state = _tabKeys[_tabCtrl.index]; } }); } @override void dispose() { _tabCtrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final status = ref.watch(overtimeStatusFilterProvider); final dateStart = ref.watch(overtimeDateStartProvider); final dateEnd = ref.watch(overtimeDateEndProvider); ref.watch(overtimeTypeFilterProvider); final l10n = AppLocalizations.of(context); // Sync TabController with external filter changes final targetIdx = _tabKeys.indexOf(status); if (targetIdx >= 0 && _tabCtrl.index != targetIdx && !_tabCtrl.indexIsChanging) { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) _tabCtrl.animateTo(targetIdx); }); } ref .read(navBarConfigProvider.notifier) .update( NavBarConfig( title: l10n.get('overtimeList'), showBack: true, onBack: () => context.pop(), ), ); return Column( children: [ Container( color: AppColors.bgCard, padding: const EdgeInsets.symmetric(horizontal: 8), child: TDTabBar( tabs: _tabLabels.map((l) => TDTab(text: l)).toList(), controller: _tabCtrl, isScrollable: true, labelColor: AppColors.primary, unselectedLabelColor: AppColors.textSecondary, outlineType: TDTabBarOutlineType.filled, showIndicator: true, indicatorColor: AppColors.primary, indicatorHeight: 3, dividerHeight: 0, labelPadding: const EdgeInsets.symmetric(horizontal: 12), onTap: (index) { ref.invalidate(overtimeListProvider); ref.read(overtimeStatusFilterProvider.notifier).state = _tabKeys[index]; }, ), ), // 筛选栏(TDesign 组件) FilterBar( groups: [ FilterGroup(title: '日期范围', type: FilterGroupType.dateRange, sections: [ FilterSection( label: '起始日期', type: FilterSectionType.dateRange, startDate: dateStart, endDate: dateEnd, onStartChanged: (v) => ref.read(overtimeDateStartProvider.notifier).state = v, onEndChanged: (v) => ref.read(overtimeDateEndProvider.notifier).state = v, ), FilterSection( label: '结束日期', type: FilterSectionType.dateRange, startDate: dateStart, endDate: dateEnd, onStartChanged: (v) => ref.read(overtimeDateStartProvider.notifier).state = v, onEndChanged: (v) => ref.read(overtimeDateEndProvider.notifier).state = v, ), ]), FilterGroup(title: '其它', type: FilterGroupType.other, sections: [ FilterSection( label: '加班类型', type: FilterSectionType.singleSelect, options: const [ FilterOption(value: 'workday', label: '工作日加班'), FilterOption(value: 'weekend', label: '休息日加班'), FilterOption(value: 'holiday', label: '节假日加班'), ], onChanged: (v) => ref.read(overtimeTypeFilterProvider.notifier).state = v, ), ]), ], onReset: () { ref.read(overtimeDateStartProvider.notifier).state = null; ref.read(overtimeDateEndProvider.notifier).state = null; ref.read(overtimeTypeFilterProvider.notifier).state = null; }, onConfirm: () {}, ), Expanded( child: TabBarView( controller: _tabCtrl, children: List.generate(_tabKeys.length, (tabIdx) { return _buildTabContent(tabIdx); }), ), ), ], ); } Widget _buildTabContent(int tabIdx) { return _OvertimeTabContent(statusKey: _tabKeys[tabIdx]); } } class _OvertimeTabContent extends ConsumerWidget { final String statusKey; const _OvertimeTabContent({required this.statusKey}); @override Widget build(BuildContext context, WidgetRef ref) { final itemsAsync = ref.watch(overtimeListProvider(statusKey)); if (itemsAsync.isLoading && !itemsAsync.hasValue) { return const SkeletonLoadingList(); } return EasyRefresh( header: TDRefreshHeader(), onRefresh: () async { ref.read(overtimeRefreshProvider.notifier).state++; }, child: _buildContent(itemsAsync, context, ref), ); } Widget _buildContent( AsyncValue> itemsAsync, BuildContext context, WidgetRef ref, ) { if (itemsAsync.isReloading) { final oldItems = itemsAsync.valueOrNull ?? []; if (oldItems.isEmpty) return const SkeletonLoadingList(); return ListView.builder( padding: const EdgeInsets.fromLTRB(16, 16, 16, 24), itemCount: oldItems.length, itemBuilder: (_, i) => Padding( padding: const EdgeInsets.only(bottom: 16), child: ListCard( cardNo: oldItems[i].applicationNo, description: '${oldItems[i].otType} · ${oldItems[i].compensationType}', amount: '${oldItems[i].otHours.toStringAsFixed(1)}小时', date: du.DateUtils.formatDate(oldItems[i].otDate), statusTag: StatusTag.fromStatus(oldItems[i].status), onTap: () => context.push('/overtime/detail/${oldItems[i].id}'), ), ), ); } if (itemsAsync.hasError) { return ListView( children: const [ SizedBox(height: 120), EmptyState(message: '加载失败'), ], ); } final items = itemsAsync.requireValue; if (items.isEmpty) { return ListView( children: const [ SizedBox(height: 120), EmptyState(message: '暂无加班记录'), ], ); } return ListView.builder( padding: const EdgeInsets.fromLTRB(16, 16, 16, 24), itemCount: items.length, itemBuilder: (_, i) => Padding( padding: const EdgeInsets.only(bottom: 16), child: ListCard( cardNo: items[i].applicationNo, description: '${items[i].otType} · ${items[i].compensationType}', amount: '${items[i].otHours.toStringAsFixed(1)}小时', date: du.DateUtils.formatDate(items[i].otDate), statusTag: StatusTag.fromStatus(items[i].status), onTap: () => context.push('/overtime/detail/${items[i].id}'), ), ), ); } }