import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../core/di/injection_container.dart'; import '../../../../core/services/leaderboard_service.dart'; /// 排行榜类型枚举 enum RankingType { daily, weekly, monthly } /// 筛选类型枚举 enum FilterType { all, previousDay, previousWeek, previousMonth } /// 排行榜数据模型 class RankingItem { final int rank; final String name; final String province; final String city; final int teamPlantingAmount; final String? avatarUrl; RankingItem({ required this.rank, required this.name, required this.province, required this.city, required this.teamPlantingAmount, this.avatarUrl, }); } /// 榜单状态 Provider final leaderboardStatusProvider = FutureProvider((ref) async { final leaderboardService = ref.watch(leaderboardServiceProvider); return leaderboardService.getStatus(); }); /// 龙虎榜页面 - 显示用户排行榜 /// 支持日榜、周榜、月榜切换,以及筛选功能 class RankingPage extends ConsumerStatefulWidget { const RankingPage({super.key}); @override ConsumerState createState() => _RankingPageState(); } class _RankingPageState extends ConsumerState { // 当前选中的排行榜类型 RankingType _selectedRankingType = RankingType.daily; // 当前选中的筛选类型 FilterType _selectedFilterType = FilterType.all; // 模拟排行榜数据 final List _mockRankingData = [ RankingItem( rank: 1, name: '环保先锋', province: '广东', city: '深圳', teamPlantingAmount: 1234567, ), RankingItem( rank: 2, name: '绿色卫士', province: '广东', city: '深圳', teamPlantingAmount: 1123456, ), RankingItem( rank: 3, name: '低碳达人', province: '广东', city: '深圳', teamPlantingAmount: 987654, ), RankingItem( rank: 4, name: '节能小能手', province: '广东', city: '深圳', teamPlantingAmount: 876543, ), RankingItem( rank: 5, name: '分类高手', province: '广东', city: '深圳', teamPlantingAmount: 765432, ), ]; /// 切换排行榜类型 void _selectRankingType(RankingType type) { setState(() { _selectedRankingType = type; }); } /// 切换筛选类型 void _selectFilterType(FilterType type) { setState(() { _selectedFilterType = type; }); } /// 格式化数字(添加千分位) String _formatNumber(int number) { return number.toString().replaceAllMapped( RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]},', ); } /// 检查当前选中的榜单是否开启 bool _isCurrentBoardEnabled(LeaderboardStatus status) { switch (_selectedRankingType) { case RankingType.daily: return status.dailyEnabled; case RankingType.weekly: return status.weeklyEnabled; case RankingType.monthly: return status.monthlyEnabled; } } @override Widget build(BuildContext context) { final statusAsync = ref.watch(leaderboardStatusProvider); return Scaffold( body: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0xFFFFF5E6), // 浅米色 Color(0xFFEAE0C8), // 浅橙色 ], ), ), child: statusAsync.when( data: (status) => _buildContent(status), loading: () => _buildLoadingContent(), error: (error, stack) => _buildContent(LeaderboardStatus.allDisabled()), ), ), ); } /// 构建加载中内容 Widget _buildLoadingContent() { return Column( children: [ _buildHeaderWithStatus(null), const Expanded( child: Center( child: CircularProgressIndicator( color: Color(0xFFD4AF37), ), ), ), ], ); } /// 构建主内容 Widget _buildContent(LeaderboardStatus status) { final isCurrentEnabled = _isCurrentBoardEnabled(status); return Column( children: [ // 顶部标题和Tab栏 _buildHeaderWithStatus(status), // 如果当前榜单开启,显示筛选栏和列表 if (isCurrentEnabled) ...[ _buildFilterBar(), Expanded(child: _buildRankingList()), ] else ...[ // 显示待开启状态 Expanded(child: _buildDisabledState()), ], ], ); } /// 构建待开启状态 Widget _buildDisabledState() { String boardName; switch (_selectedRankingType) { case RankingType.daily: boardName = '日榜'; break; case RankingType.weekly: boardName = '周榜'; break; case RankingType.monthly: boardName = '月榜'; break; } return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 图标 Container( width: 80, height: 80, decoration: BoxDecoration( color: const Color(0x1A8B5A2B), borderRadius: BorderRadius.circular(40), ), child: const Icon( Icons.hourglass_empty, size: 40, color: Color(0xFF8B5A2B), ), ), const SizedBox(height: 24), // 标题 Text( '$boardName待开启', style: const TextStyle( fontSize: 18, fontFamily: 'Inter', fontWeight: FontWeight.w700, color: Color(0xFF5D4037), ), ), const SizedBox(height: 8), // 描述 const Text( '该榜单暂未开启,请稍后再来', style: TextStyle( fontSize: 14, fontFamily: 'Inter', color: Color(0xFF8B5A2B), ), ), ], ), ); } /// 构建顶部标题和Tab栏(带状态) Widget _buildHeaderWithStatus(LeaderboardStatus? status) { return Container( color: const Color(0xCCFFF5E6), child: SafeArea( bottom: false, child: Column( children: [ // 标题 Container( height: 56, padding: const EdgeInsets.symmetric(horizontal: 16), child: const Center( child: Text( '龙虎榜', style: TextStyle( fontSize: 18, fontFamily: 'Inter', fontWeight: FontWeight.w700, height: 1.25, letterSpacing: -0.27, color: Color(0xFF5D4037), ), ), ), ), // Tab栏 _buildTabBarWithStatus(status), ], ), ), ); } /// 构建Tab栏(带状态) Widget _buildTabBarWithStatus(LeaderboardStatus? status) { return Container( height: 45, padding: const EdgeInsets.symmetric(horizontal: 16), decoration: const BoxDecoration( border: Border( bottom: BorderSide( width: 1, color: Color(0x338B5A2B), ), ), ), child: Row( children: [ // 日榜 Expanded( child: _buildTabItemWithStatus( title: '日榜', type: RankingType.daily, isSelected: _selectedRankingType == RankingType.daily, isEnabled: status?.dailyEnabled ?? false, onTap: () => _selectRankingType(RankingType.daily), ), ), // 周榜 Expanded( child: _buildTabItemWithStatus( title: '周榜', type: RankingType.weekly, isSelected: _selectedRankingType == RankingType.weekly, isEnabled: status?.weeklyEnabled ?? false, onTap: () => _selectRankingType(RankingType.weekly), ), ), // 月榜 Expanded( child: _buildTabItemWithStatus( title: '月榜', type: RankingType.monthly, isSelected: _selectedRankingType == RankingType.monthly, isEnabled: status?.monthlyEnabled ?? false, onTap: () => _selectRankingType(RankingType.monthly), ), ), ], ), ); } /// 构建Tab项(带状态) Widget _buildTabItemWithStatus({ required String title, required RankingType type, required bool isSelected, required bool isEnabled, required VoidCallback onTap, }) { // 如果榜单未开启,显示灰色且带"待开启"标记 final textColor = isSelected ? const Color(0xFFD4AF37) : isEnabled ? const Color(0xFF8B5A2B) : const Color(0xFFB0A090); return GestureDetector( onTap: onTap, child: Container( height: 44, decoration: BoxDecoration( border: Border( bottom: BorderSide( width: 3, color: isSelected ? const Color(0xFFD4AF37) : Colors.transparent, ), ), ), child: Center( child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( title, style: TextStyle( fontSize: 14, fontFamily: 'Inter', fontWeight: FontWeight.w700, height: 1.5, letterSpacing: 0.21, color: textColor, ), ), if (!isEnabled) ...[ const SizedBox(width: 4), Container( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), decoration: BoxDecoration( color: const Color(0x1A8B5A2B), borderRadius: BorderRadius.circular(4), ), child: const Text( '待开启', style: TextStyle( fontSize: 10, fontFamily: 'Inter', color: Color(0xFF8B5A2B), ), ), ), ], ], ), ), ), ); } /// 构建筛选栏 Widget _buildFilterBar() { return Container( height: 56, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: [ _buildFilterChip( title: '全部', isSelected: _selectedFilterType == FilterType.all, onTap: () => _selectFilterType(FilterType.all), ), const SizedBox(width: 12), _buildFilterChip( title: '上一日榜', isSelected: _selectedFilterType == FilterType.previousDay, onTap: () => _selectFilterType(FilterType.previousDay), ), const SizedBox(width: 12), _buildFilterChip( title: '上一周榜', isSelected: _selectedFilterType == FilterType.previousWeek, onTap: () => _selectFilterType(FilterType.previousWeek), ), const SizedBox(width: 12), _buildFilterChip( title: '上一月榜', isSelected: _selectedFilterType == FilterType.previousMonth, onTap: () => _selectFilterType(FilterType.previousMonth), ), ], ), ), ); } /// 构建筛选标签 Widget _buildFilterChip({ required String title, required bool isSelected, required VoidCallback onTap, }) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), decoration: BoxDecoration( color: isSelected ? const Color(0xFFD4AF37) : const Color(0x1A8B5A2B), borderRadius: BorderRadius.circular(9999), ), child: Text( title, style: TextStyle( fontSize: 14, fontFamily: 'Inter', fontWeight: FontWeight.w500, height: 1.5, color: isSelected ? Colors.white : const Color(0xFF8B5A2B), ), ), ), ); } /// 构建排行榜列表 Widget _buildRankingList() { return ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), itemCount: _mockRankingData.length, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.only(bottom: 4), child: _buildRankingItem(_mockRankingData[index]), ); }, ); } /// 构建排行榜项 Widget _buildRankingItem(RankingItem item) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFFFFFDF8), // 浅白色背景,与页面背景区分 borderRadius: BorderRadius.circular(12), ), child: Row( children: [ // 排名 SizedBox( width: 32, child: _buildRankBadge(item.rank), ), const SizedBox(width: 16), // 头像 _buildAvatar(item), const SizedBox(width: 16), // 用户信息 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.name, style: const TextStyle( fontSize: 16, fontFamily: 'Inter', fontWeight: FontWeight.w700, height: 1.5, color: Color(0xFF5D4037), ), ), Text( '省份: ${item.province}', style: const TextStyle( fontSize: 14, fontFamily: 'Inter', height: 1.5, color: Color(0xFF8B5A2B), ), ), Text( '城市: ${item.city}', style: const TextStyle( fontSize: 14, fontFamily: 'Inter', height: 1.5, color: Color(0xFF8B5A2B), ), ), ], ), ), // 团队认种量 ConstrainedBox( constraints: const BoxConstraints(maxWidth: 100), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ FittedBox( fit: BoxFit.scaleDown, alignment: Alignment.centerRight, child: Text( _formatNumber(item.teamPlantingAmount), style: const TextStyle( fontSize: 16, fontFamily: 'Inter', fontWeight: FontWeight.w700, height: 1.5, color: Color(0xFFD4AF37), ), ), ), const Text( '团队认种量', style: TextStyle( fontSize: 12, fontFamily: 'Inter', height: 1.5, color: Color(0xFF8B5A2B), ), ), ], ), ), ], ), ); } /// 构建排名徽章 Widget _buildRankBadge(int rank) { // 前三名使用皇冠图标 if (rank <= 3) { Color crownColor; switch (rank) { case 1: crownColor = const Color(0xFFFFD700); // 金色 break; case 2: crownColor = const Color(0xFFC0C0C0); // 银色 break; case 3: crownColor = const Color(0xFFCD7F32); // 铜色 break; default: crownColor = const Color(0xFF8B5A2B); } return Icon( Icons.workspace_premium, size: 32, color: crownColor, ); } // 其他排名显示数字 return Text( rank.toString(), style: const TextStyle( fontSize: 20, fontFamily: 'Inter', fontWeight: FontWeight.w700, height: 1.4, color: Color(0xFF8B5A2B), ), textAlign: TextAlign.center, ); } /// 构建头像 Widget _buildAvatar(RankingItem item) { return Container( width: 56, height: 56, decoration: BoxDecoration( color: const Color(0x33D4AF37), borderRadius: BorderRadius.circular(28), ), child: item.avatarUrl != null ? ClipRRect( borderRadius: BorderRadius.circular(28), child: Image.network( item.avatarUrl!, fit: BoxFit.cover, ), ) : const Icon( Icons.person, size: 32, color: Color(0xFF8B5A2B), ), ); } }