import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:provider/provider.dart'; import '../../../core/theme/app_color_scheme.dart'; import '../../../core/theme/app_spacing.dart'; import '../../../core/theme/app_theme.dart'; import '../../../core/utils/toast_utils.dart'; import '../../../core/event/app_event_bus.dart'; import '../../../data/services/bonus_service.dart'; import '../../../providers/asset_provider.dart'; /// 福利中心页面 class WelfareCenterPage extends StatefulWidget { const WelfareCenterPage({super.key}); @override State createState() => _WelfareCenterPageState(); } class _WelfareCenterPageState extends State { Map? _welfareData; bool _isLoading = true; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) => _loadData()); } Future _loadData() async { try { final bonusService = context.read(); final response = await bonusService.getWelfareStatus(); if (mounted) { setState(() { _welfareData = response.data; _isLoading = false; }); } } catch (_) { if (mounted) setState(() => _isLoading = false); } } // ============================================ // 主题感知颜色辅助 // ============================================ bool get _isDark => Theme.of(context).brightness == Brightness.dark; /// 金色强调色 ($gold-accent) Color get _goldAccent => _isDark ? AppColorScheme.darkSecondary : const Color(0xFFF59E0B); /// 盈利绿色 ($profit-green) Color get _profitGreen => _isDark ? const Color(0xFF4ADE80) : const Color(0xFF16A34A); /// 盈利绿色背景 ($profit-green-bg) Color get _profitGreenBg => _isDark ? const Color(0xFF052E16) : const Color(0xFFF0FDF4); /// 文字静默色 ($text-muted) Color get _textMuted => _isDark ? AppColorScheme.darkOnSurfaceMuted : AppColorScheme.lightOnSurfaceMuted; /// 第三级背景色 ($bg-tertiary) Color get _bgTertiary => _isDark ? AppColorScheme.darkSurfaceContainerHigh : AppColorScheme.lightSurfaceContainer; /// 卡片表面色 ($surface-card) Color get _surfaceCard => _isDark ? AppColorScheme.darkSurfaceContainer : AppColorScheme.lightSurfaceLowest; /// 反色文字 ($text-inverse) Color get _textInverse => _isDark ? AppColorScheme.darkInverseSurface : AppColorScheme.lightSurfaceLowest; // ============================================ // 容器样式辅助 // ============================================ /// 标准卡片容器 BoxDecoration _cardDecoration({Color? borderColor}) { final scheme = Theme.of(context).colorScheme; return BoxDecoration( color: _surfaceCard, borderRadius: BorderRadius.circular(AppRadius.lg), border: Border.all( color: borderColor ?? scheme.outlineVariant.withValues(alpha: 0.15), ), ); } /// 金色渐变卡片容器 BoxDecoration _goldGradientDecoration() { return BoxDecoration( borderRadius: BorderRadius.circular(AppRadius.xl), gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ _goldAccent.withValues(alpha: 0.15), _surfaceCard, ], ), border: Border.all( color: _goldAccent.withValues(alpha: 0.3), width: 1, ), ); } /// 状态胶囊标签 Widget _statusBadge(String text, Color textColor, Color bgColor) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(AppRadius.sm), ), child: Text( text, style: AppTextStyles.labelSmall(context).copyWith(color: textColor), ), ); } /// 全宽按钮 Widget _fullWidthButton({ required String text, required Color backgroundColor, required Color foregroundColor, required VoidCallback? onPressed, Color? disabledBackgroundColor, }) { return SizedBox( width: double.infinity, height: 44, child: ElevatedButton( onPressed: onPressed, style: ElevatedButton.styleFrom( backgroundColor: backgroundColor, foregroundColor: foregroundColor, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppRadius.lg), ), disabledBackgroundColor: disabledBackgroundColor ?? backgroundColor.withValues(alpha: 0.3), disabledForegroundColor: foregroundColor.withValues(alpha: 0.7), ), child: Text( text, style: AppTextStyles.headlineMedium(context).copyWith( fontWeight: FontWeight.w700, color: foregroundColor, ), ), ), ); } @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Scaffold( backgroundColor: _isDark ? AppColorScheme.darkBackground : AppColorScheme.lightBackground, appBar: AppBar( backgroundColor: _isDark ? AppColorScheme.darkBackground : AppColorScheme.lightSurfaceLowest, elevation: 0, scrolledUnderElevation: 0, centerTitle: false, titleSpacing: 0, title: Text( '福利中心', style: AppTextStyles.headlineLarge(context), ), leading: IconButton( icon: Icon(LucideIcons.arrowLeft, color: colorScheme.onSurface, size: 24), onPressed: () => Navigator.of(context).pop(), ), ), body: _isLoading ? Center( child: CircularProgressIndicator( color: _goldAccent, strokeWidth: 2.5, ), ) : RefreshIndicator( onRefresh: _loadData, color: _goldAccent, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.fromLTRB(16, 8, 16, 32), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildReferralCodeCard(context), const SizedBox(height: 16), _buildNewUserBonusCard(context), const SizedBox(height: 16), _buildReferralRewardsSection(context), const SizedBox(height: 16), _buildRulesCard(context), ], ), ), ), ); } // ============================================ // 推广码卡片(金色渐变边框 Hero Card) // ============================================ Widget _buildReferralCodeCard(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final referralCode = _welfareData?['referralCode'] as String? ?? ''; return Container( width: double.infinity, padding: const EdgeInsets.all(24), decoration: _goldGradientDecoration(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Row: gift icon + 标题 Row( children: [ Icon(LucideIcons.gift, color: _goldAccent, size: 24), const SizedBox(width: 10), Text( '我的邀请码', style: AppTextStyles.headlineLarge(context).copyWith( fontWeight: FontWeight.w700, ), ), ], ), const SizedBox(height: 16), Text( referralCode.isEmpty ? '暂无邀请码' : referralCode, style: AppTextStyles.displayMedium(context).copyWith( fontWeight: FontWeight.w800, color: _goldAccent, letterSpacing: 2, ), ), const SizedBox(height: 16), SizedBox( width: double.infinity, height: 40, child: ElevatedButton( onPressed: referralCode.isEmpty ? null : () { Clipboard.setData(ClipboardData(text: referralCode)); ToastUtils.show('邀请码已复制'); }, style: ElevatedButton.styleFrom( backgroundColor: _goldAccent, foregroundColor: _textInverse, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppRadius.lg), ), disabledBackgroundColor: _goldAccent.withValues(alpha: 0.4), ), child: Text( '复制邀请码', style: AppTextStyles.headlineMedium(context).copyWith(color: _textInverse), ), ), ), ], ), ); } // ============================================ // 新人福利卡片 // ============================================ Widget _buildNewUserBonusCard(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final newUserBonus = _welfareData?['newUserBonus'] as Map?; final eligible = newUserBonus?['eligible'] as bool? ?? false; final claimed = newUserBonus?['claimed'] as bool? ?? false; final deposited = newUserBonus?['deposited'] as bool? ?? false; // 状态判定 String badgeText; bool showAvailableBadge; String buttonText; bool canClaim; String description; if (claimed) { badgeText = '已领取'; showAvailableBadge = false; buttonText = '已领取'; canClaim = false; description = '新人福利已领取'; } else if (eligible) { badgeText = '可领取'; showAvailableBadge = true; buttonText = '立即领取'; canClaim = true; description = '完成首次充值即可领取'; } else { badgeText = deposited ? '已充值' : '待解锁'; showAvailableBadge = false; buttonText = '未解锁'; canClaim = false; description = deposited ? '已充值,等待系统确认' : '完成首次充值即可领取'; } return Container( width: double.infinity, padding: const EdgeInsets.all(20), decoration: _cardDecoration(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header: 标题 + 状态标签 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '新人福利', style: AppTextStyles.headlineLarge(context).copyWith( fontWeight: FontWeight.w700, ), ), if (showAvailableBadge) _statusBadge(badgeText, _profitGreen, _profitGreenBg), ], ), const SizedBox(height: 12), Text( '+100 USDT', style: AppTextStyles.displayLarge(context).copyWith( fontWeight: FontWeight.w800, color: claimed ? colorScheme.onSurfaceVariant : _profitGreen, ), ), const SizedBox(height: 8), Text( description, style: AppTextStyles.bodyLarge(context).copyWith( color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 16), _fullWidthButton( text: buttonText, backgroundColor: _profitGreen, foregroundColor: colorScheme.onPrimary, onPressed: canClaim ? () => _claimNewUserBonus() : null, ), ], ), ); } // ============================================ // 推广奖励列表 // ============================================ Widget _buildReferralRewardsSection(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final referralRewards = _welfareData?['referralRewards'] as List? ?? []; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Section Header Text( '推广奖励', style: AppTextStyles.headlineLarge(context), ), const SizedBox(height: 4), Text( '每邀请一位好友充值达标,奖励100 USDT', style: AppTextStyles.bodySmall(context).copyWith( color: _textMuted, ), ), const SizedBox(height: 12), // 推广列表卡片 Container( width: double.infinity, decoration: _cardDecoration(), child: referralRewards.isEmpty ? _buildEmptyReferralList(context) : _buildReferralListItems(context, referralRewards), ), ], ); } Widget _buildEmptyReferralList(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Padding( padding: const EdgeInsets.all(32), child: Center( child: Column( children: [ Icon( LucideIcons.users, size: 36, color: _textMuted.withValues(alpha: 0.4), ), const SizedBox(height: 8), Text( '暂无推广用户', style: AppTextStyles.bodyLarge(context).copyWith( color: colorScheme.onSurfaceVariant, ), ), ], ), ), ); } Widget _buildReferralListItems(BuildContext context, List referralRewards) { final colorScheme = Theme.of(context).colorScheme; return Column( children: List.generate(referralRewards.length, (index) { final data = referralRewards[index] as Map; final username = data['username'] as String? ?? ''; final totalDeposit = data['totalDeposit']?.toString() ?? '0'; final claimableCount = data['claimableCount'] as int? ?? 0; final milestones = data['milestones'] as List? ?? []; final isLast = index == referralRewards.length - 1; // 进度计算 final progress = _computeProgress(milestones, totalDeposit); // 操作按钮 final actionWidget = _buildReferralAction( data: data, claimableCount: claimableCount, milestones: milestones, progress: progress, ); // 进度条颜色 final progressColor = _referralProgressColor(claimableCount, progress); return Column( children: [ Padding( padding: const EdgeInsets.all(16), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ _buildAvatar(username), const SizedBox(width: 10), Text( username, style: AppTextStyles.headlineSmall(context), ), const SizedBox(width: 10), Text( '充值: \u00A5$totalDeposit', style: AppTextStyles.bodySmall(context), ), ], ), if (actionWidget != null) actionWidget, ], ), const SizedBox(height: 10), ClipRRect( borderRadius: BorderRadius.circular(3), child: SizedBox( height: 6, child: LinearProgressIndicator( value: progress, backgroundColor: _bgTertiary, valueColor: AlwaysStoppedAnimation(progressColor), minHeight: 6, ), ), ), ], ), ), if (!isLast) Divider( height: 1, thickness: 1, color: colorScheme.outlineVariant.withValues(alpha: 0.15), ), ], ); }), ); } Widget _buildAvatar(String username) { final colorScheme = Theme.of(context).colorScheme; return Container( width: 32, height: 32, decoration: BoxDecoration( color: _bgTertiary, shape: BoxShape.circle, ), child: Center( child: Text( username.isNotEmpty ? username[0].toUpperCase() : '?', style: AppTextStyles.headlineSmall(context).copyWith( color: colorScheme.onSurfaceVariant, ), ), ), ); } /// 计算推荐奖励进度 double _computeProgress(List milestones, String totalDeposit) { if (milestones.isNotEmpty) { int earnedCount = milestones.where((m) { final milestone = m as Map; return milestone['earned'] as bool? ?? false; }).length; return earnedCount / milestones.length; } final deposit = double.tryParse(totalDeposit) ?? 0; return (deposit / 1000).clamp(0.0, 1.0); } /// 根据状态获取进度条颜色 Color _referralProgressColor(int claimableCount, double progress) { if (claimableCount > 0) return _profitGreen; if (progress > 0) return _goldAccent; return _bgTertiary; } /// 构建推荐奖励的操作按钮 Widget? _buildReferralAction({ required Map data, required int claimableCount, required List milestones, required double progress, }) { if (claimableCount > 0) { final int milestoneValue = milestones.isNotEmpty ? (milestones.firstWhere( (m) => (m as Map)['claimable'] == true, orElse: () => milestones.first, ) as Map)['milestone'] as int? ?? 1 : 1; return GestureDetector( onTap: () => _claimReferralBonus(data['userId'] as int, milestoneValue), child: _statusBadge('领取', _profitGreen, _profitGreenBg), ); } if (progress > 0) { return _statusBadge('进行中', const Color(0xFFD97706), const Color(0xFFFEF3C7)); } return Text( '待达标', style: AppTextStyles.labelLarge(context).copyWith(color: _textMuted), ); } // ============================================ // 奖励规则卡片 // ============================================ Widget _buildRulesCard(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Container( width: double.infinity, padding: const EdgeInsets.fromLTRB(20, 16, 20, 16), decoration: BoxDecoration( color: _bgTertiary, borderRadius: BorderRadius.circular(AppRadius.lg), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '奖励规则', style: AppTextStyles.headlineSmall(context), ), const SizedBox(height: 8), _buildRuleItem('新用户注册完成实名认证奖励 100 USDT'), _buildRuleItem('邀请好友充值每达 1000 USDT,双方各获得 100 USDT'), _buildRuleItem('奖励直接发放至资金账户'), ], ), ); } Widget _buildRuleItem(String text) { final ruleTextColor = _isDark ? AppColorScheme.darkOnSurfaceVariant : const Color(0xFF475569); return Padding( padding: const EdgeInsets.symmetric(vertical: 3), child: Text( '\u2022 $text', style: AppTextStyles.bodyMedium(context).copyWith( color: ruleTextColor, ), ), ); } // ============================================ // 业务逻辑 // ============================================ Future _claimNewUserBonus() async { try { final bonusService = context.read(); final response = await bonusService.claimNewUserBonus(); if (!mounted) return; if (response.success) { context.read().refreshAll(force: true); context.read().fire(AppEventType.assetChanged); ToastUtils.show('领取成功!100 USDT 已到账'); _loadData(); } else { ToastUtils.show(response.message ?? '领取失败'); } } catch (e) { ToastUtils.show('领取失败: $e'); } } Future _claimReferralBonus(int referredUserId, int milestone) async { try { final bonusService = context.read(); final response = await bonusService.claimReferralBonus( referredUserId, milestone, ); if (!mounted) return; if (response.success) { context.read().refreshAll(force: true); context.read().fire(AppEventType.assetChanged); ToastUtils.show('领取成功!100 USDT 已到账'); _loadData(); } else { ToastUtils.show(response.message ?? '领取失败'); } } catch (e) { ToastUtils.show('领取失败: $e'); } } }