import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/theme/app_colors.dart'; import '../shell/nav_bar_config.dart'; import '../../core/utils/date_utils.dart' as du; import '../../shared/widgets/filter_tabs.dart'; import '../../shared/widgets/empty_state.dart'; import '../../shared/widgets/loading_widget.dart'; import '../../core/i18n/app_localizations.dart'; import 'vehicle_list_controller.dart'; import 'vehicle_model.dart'; class VehicleListPage extends ConsumerStatefulWidget { const VehicleListPage({super.key}); @override ConsumerState createState() => _VehicleListPageState(); } class _VehicleListPageState extends ConsumerState { final _scrollCtrl = ScrollController(); static const _tabs = ['全部', '草稿', '审批中', '已通过', '已拒绝', '已还车']; static const _tabStatusKeys = [ '', 'draft', 'pending', 'approved', 'rejected', 'returned', ]; @override void initState() { super.initState(); _scrollCtrl.addListener(_onScroll); } void _onScroll() { if (_scrollCtrl.position.pixels >= _scrollCtrl.position.maxScrollExtent - 100) { ref.read(vehiclePageProvider.notifier).state++; } } @override void dispose() { _scrollCtrl.removeListener(_onScroll); _scrollCtrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final status = ref.watch(vehicleStatusFilterProvider); final itemsAsync = ref.watch(vehicleListProvider); final l10n = AppLocalizations.of(context); int selectedIndex = _tabStatusKeys.indexOf(status); if (selectedIndex < 0) selectedIndex = 0; ref .read(navBarConfigProvider.notifier) .update( NavBarConfig( title: l10n.get('vehicleList'), showBack: true, onBack: () => context.pop(), ), ); return Column( children: [ FilterTabs( tabs: _tabs, selectedIndex: selectedIndex, onChanged: (index) { ref.read(vehicleStatusFilterProvider.notifier).state = _tabStatusKeys[index]; }, ), Expanded( child: itemsAsync.when( loading: () => const LoadingWidget(), error: (_, _) => const EmptyState(message: '加载失败'), data: (items) => items.isEmpty ? const EmptyState(message: '暂无用车记录') : RefreshIndicator( onRefresh: () async { ref.invalidate(vehicleListProvider); }, child: ListView.builder( controller: _scrollCtrl, padding: const EdgeInsets.all(16), itemCount: items.length, itemBuilder: (_, i) => Padding( padding: const EdgeInsets.only(bottom: 16), child: _buildVehicleListItem(items[i]), ), ), ), ), ), ], ); } Widget _buildVehicleListItem(VehicleModel item) { Color bg, fg; String label; switch (item.status) { case 'pending': bg = AppColors.warningBg; fg = AppColors.warning; label = '审批中'; case 'approved': bg = AppColors.successBg; fg = AppColors.success; label = '已通过'; case 'rejected': bg = AppColors.dangerBg; fg = AppColors.danger; label = '已拒绝'; case 'draft': bg = AppColors.bgPage; fg = AppColors.statusGray; label = '草稿'; case 'revoked': bg = AppColors.revokedBg; fg = AppColors.revokedText; label = '已撤回'; case 'returned': bg = const Color(0xFFEDF2FC); fg = const Color(0xFF5A8CDB); label = '已还车'; default: bg = AppColors.bgPage; fg = AppColors.statusGray; label = item.status; } return GestureDetector( onTap: () => context.push('/vehicle/detail/${item.id}'), child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppColors.bgCard, borderRadius: BorderRadius.circular(8), ), child: Column( children: [ // R1: 车牌号 + 状态标签 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( item.licensePlate.isNotEmpty ? item.licensePlate : '未指定车辆', style: const TextStyle( fontSize: AppFontSizes.subtitle, fontWeight: FontWeight.w700, color: AppColors.textPrimary, ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2, ), decoration: BoxDecoration( color: bg, borderRadius: BorderRadius.circular(4), ), child: Text( label, style: TextStyle( fontSize: AppFontSizes.caption, fontWeight: FontWeight.w500, color: fg, ), ), ), ], ), const SizedBox(height: 6), // R2: 申请单号 + 用途标签 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( item.applicationNo, style: const TextStyle( fontSize: AppFontSizes.caption, color: AppColors.textSecondary, ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 1, ), decoration: BoxDecoration( color: AppColors.primaryLight, borderRadius: BorderRadius.circular(3), ), child: Text( item.purpose.isNotEmpty ? item.purpose : '公务', style: const TextStyle( fontSize: 10, color: AppColors.primary, ), ), ), ], ), const SizedBox(height: 6), // R3: 路线 + 时间 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( child: Text( '${item.origin.isNotEmpty ? item.origin : '未知'} → ${item.destination.isNotEmpty ? item.destination : '未知'}', style: const TextStyle( fontSize: 13, color: AppColors.textSecondary, ), overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 8), Text( '${du.DateUtils.formatMonthDay(item.startTime)} ${du.DateUtils.formatTime(item.startTime)}', style: const TextStyle( fontSize: AppFontSizes.caption, color: AppColors.textPlaceholder, ), ), ], ), ], ), ), ); } }