import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:sales_chat/providers/stats_provider.dart'; import 'package:sales_chat/widgets/user_avatar.dart'; import 'package:sales_chat/theme/app_theme.dart'; /// 看板页面 —— Twitter 风格 class DashboardPage extends StatefulWidget { const DashboardPage({super.key}); @override State createState() => _DashboardPageState(); } class _DashboardPageState extends State { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) => _loadData()); } Future _loadData() async { final statsProvider = context.read(); await statsProvider.loadMyStats(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('看板'), actions: [ IconButton( icon: const Icon(Icons.refresh), onPressed: _loadData, ), ], ), body: Consumer( builder: (context, statsProvider, _) { if (statsProvider.isLoading && statsProvider.myStats == null) { return const Center(child: CircularProgressIndicator()); } final stats = statsProvider.myStats; return RefreshIndicator( onRefresh: _loadData, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (stats != null) _buildStatsOverview(context, stats), const SizedBox(height: 8), _buildTodayStats(context, stats), const SizedBox(height: 8), _buildRankingSection(context, statsProvider), ], ), ), ); }, ), ); } /// 统计概览 —— 卡片区块,2x2 网格 Widget _buildStatsOverview(BuildContext context, dynamic myStats) { final total = myStats.total; return Container( width: double.infinity, color: AppTheme.cardBackground, padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '我的统计', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), const SizedBox(height: 16), GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisCount: 2, mainAxisSpacing: 16, crossAxisSpacing: 16, childAspectRatio: 2.0, children: [ _StatGridItem( value: _formatNumber(total.totalInvites), label: '总邀请数', icon: Icons.card_giftcard_outlined, color: AppTheme.primaryColor, ), _StatGridItem( value: _formatNumber(total.totalJoins), label: '总加入数', icon: Icons.person_add_outlined, color: AppTheme.successColor, ), _StatGridItem( value: '+${myStats.today.invitesCreated}', label: '今日邀请', icon: Icons.add_circle_outline, color: AppTheme.warningColor, ), _StatGridItem( value: '+${myStats.today.joins}', label: '今日加入', icon: Icons.people, color: AppTheme.infoColor, ), ], ), ], ), ); } /// 今日统计 —— 独立卡片区块 Widget _buildTodayStats(BuildContext context, dynamic myStats) { if (myStats == null) return const SizedBox.shrink(); final today = myStats.today; return Container( width: double.infinity, color: AppTheme.cardBackground, padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '今日数据', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: _TodayStatItem( value: '${today.invitesCreated}', label: '新建邀请', icon: Icons.add_circle_outline, color: AppTheme.primaryColor, ), ), Container( width: 0.5, height: 40, color: AppTheme.dividerColor, ), Expanded( child: _TodayStatItem( value: '${today.joins}', label: '新加入', icon: Icons.person_add, color: AppTheme.successColor, ), ), ], ), ], ), ); } /// 排行榜区块 Widget _buildRankingSection(BuildContext context, StatsProvider provider) { return Container( color: AppTheme.cardBackground, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 标题行 Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '排行榜', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), GestureDetector( onTap: () => provider.loadRanking(), child: const Text( '加载排行', style: TextStyle( fontSize: 13, color: AppTheme.primaryColor, ), ), ), ], ), ), if (provider.ranking.isEmpty) Padding( padding: const EdgeInsets.all(24), child: Center( child: Text( '暂无排行数据', style: TextStyle(color: AppTheme.textSecondary, fontSize: 14), ), ), ) else ..._buildRankingItems(provider), ], ), ); } /// 构建排行列表项(带分隔线) List _buildRankingItems(StatsProvider provider) { final rankings = provider.ranking.take(10).toList(); final items = []; for (int i = 0; i < rankings.length; i++) { final entry = rankings[i]; items.add(Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( children: [ // 排名序号 SizedBox( width: 28, child: Text( '${entry.rank}', style: TextStyle( fontSize: 16, fontWeight: i < 3 ? FontWeight.bold : FontWeight.w500, color: i < 3 ? [const Color(0xFFFFC300), const Color(0xFFC0C0C0), const Color(0xFFCD7F32)][i] : AppTheme.textHint, ), textAlign: TextAlign.center, ), ), const SizedBox(width: 12), // 头像 UserAvatar( displayName: entry.displayName, radius: 16, ), const SizedBox(width: 12), // 名字 Expanded( child: Text( entry.displayName, style: const TextStyle( fontSize: 15, color: AppTheme.textPrimary, ), ), ), // 加入数 Text( '${entry.totalJoins} 加入', style: const TextStyle( fontSize: 13, color: AppTheme.textSecondary, ), ), ], ), )); if (i < rankings.length - 1) { items.add(const Padding( padding: EdgeInsets.only(left: 56), child: Divider(height: 0.5, thickness: 0.5), )); } } return items; } /// 格式化数字 String _formatNumber(int number) { if (number >= 10000) { return '${(number / 10000).toStringAsFixed(1)}w'; } else if (number >= 1000) { return '${(number / 1000).toStringAsFixed(1)}k'; } return number.toString(); } } /// 统计网格项 —— 大数字 + 小标签 + 图标 class _StatGridItem extends StatelessWidget { final String value; final String label; final IconData icon; final Color color; const _StatGridItem({ required this.value, required this.label, required this.icon, required this.color, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withValues(alpha: 0.06), borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Row( children: [ Icon(icon, color: color, size: 18), const SizedBox(width: 6), Text( value, style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: AppTheme.textPrimary, ), ), ], ), const SizedBox(height: 4), Text( label, style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), ], ), ); } } /// 今日统计项 class _TodayStatItem extends StatelessWidget { final String value; final String label; final IconData icon; final Color color; const _TodayStatItem({ required this.value, required this.label, required this.icon, required this.color, }); @override Widget build(BuildContext context) { return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, color: color, size: 18), const SizedBox(width: 6), Text( value, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: AppTheme.textPrimary, ), ), ], ), const SizedBox(height: 4), Text( label, style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), ], ); } }