import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import '../../../core/constants/app_colors.dart'; import '../../../core/utils/format_utils.dart'; import '../../../domain/entities/contribution.dart'; import '../../../domain/entities/contribution_record.dart'; import '../../providers/user_providers.dart'; import '../../providers/contribution_providers.dart'; class ContributionPage extends ConsumerWidget { const ContributionPage({super.key}); // 设计色彩 static const Color _orange = Color(0xFFFF6B00); static const Color _green = Color(0xFF22C55E); static const Color _grayText = Color(0xFF6B7280); static const Color _darkText = Color(0xFF1F2937); static const Color _bgGray = Color(0xFFF3F4F6); static const Color _lightGray = Color(0xFFF9FAFB); @override Widget build(BuildContext context, WidgetRef ref) { final user = ref.watch(userNotifierProvider); final accountSequence = user.accountSequence ?? ''; final contributionAsync = ref.watch(contributionProvider(accountSequence)); final recordsParams = ContributionRecordsParams( accountSequence: accountSequence, page: 1, pageSize: 3, ); final recordsAsync = ref.watch(contributionRecordsProvider(recordsParams)); return Scaffold( backgroundColor: const Color(0xFFF5F5F5), body: SafeArea( child: RefreshIndicator( onRefresh: () async { ref.invalidate(contributionProvider(accountSequence)); ref.invalidate(contributionRecordsProvider(recordsParams)); }, child: contributionAsync.when( data: (contribution) { return CustomScrollView( slivers: [ // 顶部导航栏 SliverToBoxAdapter(child: _buildAppBar(context)), // 内容 SliverPadding( padding: const EdgeInsets.all(16), sliver: SliverList( delegate: SliverChildListDelegate([ // 总贡献值卡片 _buildTotalContributionCard(contribution), const SizedBox(height: 16), // 三栏统计 _buildThreeColumnStats(contribution), const SizedBox(height: 16), // 今日预估收益 _buildTodayEstimateCard(contribution), const SizedBox(height: 16), // 贡献值明细 _buildContributionDetailCard(context, ref, recordsAsync), const SizedBox(height: 16), // 团队层级统计 _buildTeamStatsCard(contribution), const SizedBox(height: 16), // 贡献值失效倒计时 _buildExpirationCard(contribution, recordsAsync), const SizedBox(height: 24), ]), ), ), ], ); }, loading: () => const Center(child: CircularProgressIndicator()), error: (error, _) => Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, size: 48, color: AppColors.error), const SizedBox(height: 16), Text('加载失败: $error'), const SizedBox(height: 16), ElevatedButton( onPressed: () => ref.invalidate(contributionProvider(accountSequence)), child: const Text('重试'), ), ], ), ), ), ), ), ); } Widget _buildAppBar(BuildContext context) { return Container( color: _lightGray, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( children: [ // Logo Container( width: 32, height: 32, decoration: BoxDecoration( color: _orange.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: const Icon(Icons.eco, color: _orange, size: 20), ), const SizedBox(width: 8), const Text( '榴莲生态', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: _darkText, letterSpacing: 0.45, ), ), const Spacer(), // 客服 IconButton( icon: const Icon(Icons.headset_mic_outlined, color: _grayText), onPressed: () {}, ), // 通知(带红点) Stack( children: [ IconButton( icon: const Icon(Icons.notifications_outlined, color: _grayText), onPressed: () {}, ), Positioned( right: 10, top: 10, child: Container( width: 8, height: 8, decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), ), ), ], ), ], ), ); } Widget _buildTotalContributionCard(Contribution? contribution) { final total = contribution?.effectiveContribution ?? '0'; return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '总贡献值', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: _grayText), ), Icon(Icons.visibility_outlined, color: _grayText.withOpacity(0.5), size: 18), ], ), const SizedBox(height: 8), Text( formatAmount(total), style: const TextStyle( fontSize: 30, fontWeight: FontWeight.bold, color: _orange, letterSpacing: -0.75, ), ), const SizedBox(height: 12), // 有效期标签 Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: _lightGray, borderRadius: BorderRadius.circular(999), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.info_outline, size: 14, color: _grayText.withOpacity(0.7)), const SizedBox(width: 6), Text( '贡献值有效期: 730 天', style: TextStyle(fontSize: 12, color: _grayText.withOpacity(0.9)), ), ], ), ), ], ), ); } Widget _buildThreeColumnStats(Contribution? contribution) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: Row( children: [ _buildStatColumn('个人贡献值', contribution?.personalContribution ?? '0', false), _buildStatColumn('团队贡献值', contribution?.teamLevelContribution ?? '0', true), _buildStatColumn('省市公司', contribution?.systemContribution ?? '0', true), ], ), ); } Widget _buildStatColumn(String label, String value, bool showLeftBorder) { return Expanded( child: Container( decoration: showLeftBorder ? const BoxDecoration( border: Border(left: BorderSide(color: Color(0xFFE5E7EB), width: 1)), ) : null, padding: const EdgeInsets.symmetric(horizontal: 4), child: Column( children: [ Text(label, style: const TextStyle(fontSize: 12, color: _grayText)), const SizedBox(height: 4), Text( formatAmount(value), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: _darkText), ), ], ), ), ); } Widget _buildTodayEstimateCard(Contribution? contribution) { // 基于贡献值计算预估收益(暂时显示占位符,后续可接入实际计算API) final effectiveContribution = double.tryParse(contribution?.effectiveContribution ?? '0') ?? 0; // 简单估算:假设每日发放总量为 10000 积分股,按贡献值占比分配 // 这里先显示"--"表示暂无数据,后续可接入实际计算 final hasContribution = effectiveContribution > 0; return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: Row( children: [ // 图标 Container( width: 40, height: 40, decoration: BoxDecoration( color: _green.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: const Icon(Icons.trending_up, color: _green, size: 24), ), const SizedBox(width: 12), // 文字说明 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '今日预估收益', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: _grayText), ), Text( '基于当前贡献值占比计算', style: TextStyle(fontSize: 12, color: _grayText.withOpacity(0.7)), ), ], ), ), // 收益数值 Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text.rich( TextSpan( children: [ TextSpan( text: hasContribution ? '计算中' : '--', style: TextStyle( fontSize: hasContribution ? 14 : 18, fontWeight: FontWeight.bold, color: _green, ), ), const TextSpan( text: ' 积分股', style: TextStyle(fontSize: 12, color: _green), ), ], ), ), ], ), ], ), ); } Widget _buildContributionDetailCard( BuildContext context, WidgetRef ref, AsyncValue recordsAsync, ) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: Column( children: [ // 标题行 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '贡献值明细', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: _darkText), ), GestureDetector( onTap: () { // TODO: 跳转到完整记录页面 }, child: const Row( children: [ Text('查看全部', style: TextStyle(fontSize: 12, color: _orange)), Icon(Icons.chevron_right, size: 14, color: _orange), ], ), ), ], ), const SizedBox(height: 16), // 明细列表 recordsAsync.when( data: (recordsPage) { if (recordsPage == null || recordsPage.data.isEmpty) { return const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: Text( '暂无贡献值记录', style: TextStyle(fontSize: 14, color: _grayText), ), ); } return Column( children: recordsPage.data.asMap().entries.map((entry) { final index = entry.key; final record = entry.value; return Column( children: [ _buildDetailRow(record), if (index < recordsPage.data.length - 1) const Divider(height: 24), ], ); }).toList(), ); }, loading: () => const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: SizedBox( width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2), ), ), error: (error, _) => Padding( padding: const EdgeInsets.symmetric(vertical: 20), child: Text( '加载失败', style: TextStyle(fontSize: 14, color: _grayText.withOpacity(0.7)), ), ), ), ], ), ); } Widget _buildDetailRow(ContributionRecord record) { final dateFormat = DateFormat('yyyy-MM-dd HH:mm'); final formattedDate = dateFormat.format(record.createdAt); final amount = '+${formatAmount(record.finalContribution)}'; return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( record.displayTitle, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: _darkText), ), const SizedBox(height: 2), Text(formattedDate, style: const TextStyle(fontSize: 12, color: _grayText)), ], ), Text( amount, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: _green), ), ], ); } Widget _buildTeamStatsCard(Contribution? contribution) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '团队层级统计', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: _darkText), ), const SizedBox(height: 16), // 第一行 Row( children: [ _buildTeamStatItem('直推人数', '${contribution?.directReferralAdoptedCount ?? 0}', '人'), const SizedBox(width: 16), _buildTeamStatItem('已解锁奖励', '${contribution?.unlockedBonusTiers ?? 0}', '档'), ], ), const SizedBox(height: 16), // 第二行 Row( children: [ _buildTeamStatItem('已解锁层级', '${contribution?.unlockedLevelDepth ?? 0}', '级'), const SizedBox(width: 16), _buildTeamStatItem('是否认种', contribution?.hasAdopted == true ? '是' : '否', ''), ], ), ], ), ); } Widget _buildTeamStatItem(String label, String value, String unit) { return Expanded( child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: _bgGray, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: const TextStyle(fontSize: 12, color: _grayText)), const SizedBox(height: 4), Text.rich( TextSpan( children: [ TextSpan( text: '$value ', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: _orange), ), if (unit.isNotEmpty) TextSpan( text: unit, style: const TextStyle(fontSize: 12, color: _grayText), ), ], ), ), ], ), ), ); } Widget _buildExpirationCard( Contribution? contribution, AsyncValue recordsAsync, ) { // 从记录中获取最近的过期日期 DateTime? nearestExpireDate; recordsAsync.whenData((recordsPage) { if (recordsPage != null && recordsPage.data.isNotEmpty) { // 找到未过期记录中最近的过期日期 final activeRecords = recordsPage.data.where((r) => !r.isExpired).toList(); if (activeRecords.isNotEmpty) { activeRecords.sort((a, b) => a.expireDate.compareTo(b.expireDate)); nearestExpireDate = activeRecords.first.expireDate; } } }); // 计算剩余天数和进度 final now = DateTime.now(); int daysRemaining = 730; // 默认值 double progress = 1.0; String expireDateText = '暂无过期信息'; if (nearestExpireDate != null) { daysRemaining = nearestExpireDate!.difference(now).inDays; if (daysRemaining < 0) daysRemaining = 0; // 假设总有效期为730天 progress = daysRemaining / 730; if (progress > 1) progress = 1; if (progress < 0) progress = 0; expireDateText = '您的贡献值将于 ${DateFormat('yyyy-MM-dd').format(nearestExpireDate!)} 失效'; } return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 标题 const Row( children: [ Icon(Icons.timer_outlined, color: _orange, size: 24), SizedBox(width: 8), Text( '贡献值失效倒计时', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: _darkText), ), ], ), const SizedBox(height: 12), // 进度条 ClipRRect( borderRadius: BorderRadius.circular(5), child: LinearProgressIndicator( value: progress, minHeight: 10, backgroundColor: _bgGray, valueColor: const AlwaysStoppedAnimation(_orange), ), ), const SizedBox(height: 12), // 说明文字 Text( expireDateText, style: const TextStyle(fontSize: 12, color: _grayText), ), const SizedBox(height: 4), Text( '剩余 $daysRemaining 天', style: const TextStyle(fontSize: 12, color: _orange, fontWeight: FontWeight.w500), ), const SizedBox(height: 8), // 提示 Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: _bgGray, borderRadius: BorderRadius.circular(4), ), child: const Text( '* 运营账号贡献值永不失效', style: TextStyle(fontSize: 10, color: _orange), ), ), ], ), ); } }