import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../models/device.dart'; import '../../providers/dashboard_provider.dart'; import '../../providers/devices_provider.dart'; import '../../app/theme.dart'; import '../../utils/i10n.dart'; import '../../utils/responsive.dart'; import '../../providers/auth_provider.dart'; import '../../utils/app_update.dart'; import '../../utils/permissions.dart'; import '../../widgets/cloud_card.dart'; /// 首页:仪表盘概览(底部导航第 1 个 tab)。 /// /// 展示设备统计卡片与各模块快捷入口。 class HomePage extends ConsumerStatefulWidget { const HomePage({super.key}); @override ConsumerState createState() => _HomePageState(); } class _HomePageState extends ConsumerState { @override void initState() { super.initState(); // 加载仪表盘统计(设备/门店总数)。 Future.microtask( () => ref.read(dashboardStatsProvider.notifier).load(), ); // 启动后静默检测新版本(无更新/失败均不打扰,仅会话内首次有更新时弹窗)。 final orgId = ref.read(authProvider).user?.orgId; final ctx = context; Future.microtask(() { if (!ctx.mounted) return; checkAndPromptUpdate( ctx, silentIfNoUpdate: true, orgId: orgId, ); }); } @override Widget build(BuildContext context) { final devicesAsync = ref.watch(devicesProvider); final t = AppLocalizations.of(context); return Scaffold( appBar: AppBar(title: Text(t.appTitle)), body: devicesAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (_, __) => _buildDashboard(context, null), data: (devices) => _buildDashboard(context, devices), ), ); } Widget _buildDashboard(BuildContext context, List? devices) { final stats = ref.watch(dashboardStatsProvider); final totalCount = stats.deviceTotal; final storeTotal = stats.storeTotal; // 在线/离线统计改为后端真实聚合值(按 status 拉 totalCount), // 不再用首屏列表客户端统计,避免与设备总数(12 万级)严重不符。 final onlineCount = stats.onlineCount; final offlineCount = stats.offlineCount; final t = AppLocalizations.of(context); // 统计卡配色纳入橙白语义体系(仅改色,图标图形与 size 冻结不动)。 final scheme = Theme.of(context).colorScheme; final semantic = Theme.of(context).extension(); return RefreshIndicator( onRefresh: () async { final _ = await ref.refresh(devicesProvider.future); await ref.read(dashboardStatsProvider.notifier).load(); }, child: ListView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(16), children: [ _buildBrandBanner(context), const SizedBox(height: 16), // 统计卡片 GridView.count( crossAxisCount: context.responsive.gridCount(minTileWidth: 300), shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 1.4, children: [ _StatCard( title: t.totalDevices, value: '$totalCount', icon: Icons.devices, color: scheme.primary, ), _StatCard( title: t.totalStores, value: '$storeTotal', icon: Icons.store, color: scheme.tertiary, ), _StatCard( title: t.onlineDevices, value: '$onlineCount', icon: Icons.check_circle_outline, color: semantic?.success ?? scheme.tertiary, ), _StatCard( title: t.offlineDevices, value: '$offlineCount', icon: Icons.highlight_off, color: scheme.onSurfaceVariant, ), ], ), const SizedBox(height: 24), // 快捷操作 Text( t.quickActions, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), Row( children: [ Expanded( child: _QuickAction( label: t.deviceManagement, icon: Icons.devices, onTap: () => context.go('/device'), ), ), Expanded( child: _QuickAction( label: t.storeManagement, icon: Icons.store, onTap: () => context.go('/store'), ), ), if (SessionPermissions.canViewUserManagement( ref.read(authProvider).user)) Expanded( child: _QuickAction( label: t.userManagement, icon: Icons.people, onTap: () => context.push('/admin/sys/user'), ), ), ], ), ], ), ); } /// 品牌云卡横幅:首页招牌区块(橙→金渐变)。 Widget _buildBrandBanner(BuildContext context) { final t = AppLocalizations.of(context); return CloudCard( child: Row( children: [ const Icon(Icons.cloud, size: 36), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( t.appHeroTitle, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), const Text( '终端管控·内容运营·门店管理', style: TextStyle(fontSize: 13), ), ], ), ), ], ), ); } } class _StatCard extends StatelessWidget { const _StatCard({ required this.title, required this.value, required this.icon, required this.color, }); final String title; final String value; final IconData icon; final Color color; @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, color: color, size: 28), const SizedBox(height: 8), Text( value, style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, ), ), Text(title, style: Theme.of(context).textTheme.bodySmall), ], ), ), ); } } class _QuickAction extends StatelessWidget { const _QuickAction({ required this.label, required this.icon, required this.onTap, }); final String label; final IconData icon; final VoidCallback onTap; @override Widget build(BuildContext context) { return InkWell( onTap: onTap, child: Column( mainAxisSize: MainAxisSize.min, children: [ CircleAvatar( backgroundColor: Theme.of(context).colorScheme.primaryContainer, child: Icon(icon, color: Theme.of(context).colorScheme.primary), ), const SizedBox(height: 4), Text(label, style: Theme.of(context).textTheme.bodySmall), ], ), ); } }