import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; import 'package:go_router/go_router.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import '../../core/theme/app_colors.dart'; import '../../shared/widgets/section_card.dart'; import '../../core/i18n/app_localizations.dart'; import '../shell/nav_bar_config.dart'; import 'home_controller.dart'; class HomePage extends ConsumerWidget { const HomePage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final summaryAsync = ref.watch(homeSummaryProvider); final location = GoRouterState.of(context).uri.toString(); final l10n = AppLocalizations.of(context); if (location == '/') { ref .read(navBarConfigProvider.notifier) .update( NavBarConfig( title: l10n.get('appName'), showBack: true, leadingIcon: Icons.close, ), ); } return summaryAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (_, _) => Center(child: Text(l10n.get('loadFailed'))), data: (summary) => SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.fromLTRB(16, 16, 16, 24), child: Column( children: [ // Banner 区域 _buildBanner(), const SizedBox(height: 16), // 发起 SectionCard( title: l10n.get('initiate'), actionText: l10n.get('more'), actionIcon: Icons.chevron_right, onActionTap: () => context.push('/expense-apply/list'), children: [ _buildGrid([ _GridItem( icon: Icons.add_card, label: l10n.get('preApplication'), onTap: () => context.push('/expense-apply/apply'), ), _GridItem( icon: Icons.receipt_long, label: l10n.get('expenseReimbursement'), onTap: () => context.push('/expense/apply'), ), _GridItem( icon: Icons.directions_car, label: l10n.get('vehicleApplication'), onTap: () => context.push('/vehicle/apply'), ), _GridItem( icon: Icons.access_time, label: l10n.get('overtimeApplication'), onTap: () => context.push('/overtime/apply'), ), ]), ], ), const SizedBox(height: 16), // 记录 SectionCard( title: l10n.get('records'), actionText: l10n.get('more'), actionIcon: Icons.chevron_right, onActionTap: () => context.push('/expense-apply/list'), children: [ _buildGrid([ _GridItem( icon: Icons.folder_copy, label: l10n.get('applicationRecords'), onTap: () => context.push('/expense-apply/list'), ), _GridItem( icon: Icons.article_outlined, label: l10n.get('expenseRecords'), onTap: () => context.push('/expense/list'), ), _GridItem( icon: Icons.push_pin_outlined, label: l10n.get('outingLogs'), onTap: () => context.push('/outing-log/list'), ), _GridItem( icon: Icons.campaign, label: l10n.get('companyAnnouncements'), onTap: () => context.push('/announcement/list'), ), ]), ], ), const SizedBox(height: 16), // 我的快捷看板 SectionCard( title: l10n.get('myDashboard'), showAction: false, children: [_buildStatsRow(summary, l10n)], ), ], ), ), ); } static const _banners = [ 'assets/img/banner_1.png', 'assets/img/banner_2.png', 'assets/img/banner_3.png', ]; Widget _buildBanner() { return ClipRRect( borderRadius: BorderRadius.circular(8), child: SizedBox( height: 160, child: Swiper( autoplay: true, itemCount: _banners.length, loop: true, pagination: const SwiperPagination( alignment: Alignment.bottomCenter, builder: TDSwiperPagination.dotsBar, ), itemBuilder: (BuildContext context, int index) { return TDImage(assetUrl: _banners[index], fit: BoxFit.cover); }, ), ), ); } Widget _buildGrid(List<_GridItem> items) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: items.map((item) => _buildGridItem(item)).toList(), ); } Widget _buildGridItem(_GridItem item) { return GestureDetector( onTap: item.onTap, child: SizedBox( width: 64, child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 36, height: 36, decoration: BoxDecoration( color: AppColors.primaryLight, borderRadius: BorderRadius.circular(10), ), child: Icon(item.icon, size: 22, color: AppColors.primary), ), const SizedBox(height: 6), Text( item.label, style: const TextStyle( fontSize: AppFontSizes.caption, color: AppColors.textSecondary, ), textAlign: TextAlign.center, ), ], ), ), ); } Widget _buildStatsRow(HomeSummary summary, AppLocalizations l10n) { final pendingTotal = summary.expensePending + summary.overtimePending + summary.vehiclePending; final submittedTotal = summary.expensePending + summary.overtimePending + summary.vehiclePending + summary.expenseApplyPending; return Row( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildStat( '¥${summary.totalCount * 100}', l10n.get('monthlyTotalExpense'), AppColors.amountPrimary, ), _buildStat( '$submittedTotal', l10n.get('monthlySubmitted'), AppColors.textPrimary, ), _buildStat( '$pendingTotal', l10n.get('pendingDocuments'), AppColors.warning, ), ], ); } Widget _buildStat(String value, String label, Color valueColor) { return Column( mainAxisSize: MainAxisSize.min, children: [ Text( value, style: TextStyle( fontSize: 22, fontWeight: FontWeight.w700, color: valueColor, ), ), const SizedBox(height: AppSpacing.xs), Text( label, style: const TextStyle( fontSize: AppFontSizes.caption, color: AppColors.textSecondary, ), ), ], ); } } class _GridItem { final IconData icon; final String label; final VoidCallback onTap; const _GridItem({ required this.icon, required this.label, required this.onTap, }); }