Files
monisuo/flutter_monisuo/lib/ui/pages/mine/welfare_center_page.dart
2026-04-05 19:43:31 +08:00

613 lines
20 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../core/theme/app_spacing.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';
import '../../../providers/auth_provider.dart';
import '../../components/glass_panel.dart';
import '../../components/neon_glow.dart';
/// 福利中心页面
class WelfareCenterPage extends StatefulWidget {
const WelfareCenterPage({super.key});
@override
State<WelfareCenterPage> createState() => _WelfareCenterPageState();
}
class _WelfareCenterPageState extends State<WelfareCenterPage> {
Map<String, dynamic>? _welfareData;
bool _isLoading = true;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => _loadData());
}
Future<void> _loadData() async {
try {
final bonusService = context.read<BonusService>();
final response = await bonusService.getWelfareStatus();
if (mounted) {
setState(() {
_welfareData = response.data;
_isLoading = false;
});
}
} catch (_) {
if (mounted) setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Scaffold(
backgroundColor: colorScheme.background,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Text(
'福利中心',
style: GoogleFonts.spaceGrotesk(
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
leading: IconButton(
icon: Icon(LucideIcons.chevronLeft, color: colorScheme.onSurface),
onPressed: () => Navigator.of(context).pop(),
),
),
body: _isLoading
? Center(
child: CircularProgressIndicator(color: colorScheme.primary),
)
: RefreshIndicator(
onRefresh: _loadData,
color: colorScheme.primary,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: AppSpacing.pagePadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 推广码卡片
_buildReferralCodeCard(colorScheme),
SizedBox(height: AppSpacing.lg),
// 首充福利卡片
_buildNewUserBonusCard(colorScheme),
SizedBox(height: AppSpacing.lg),
// 推广奖励列表
_buildReferralRewardsSection(colorScheme),
SizedBox(height: AppSpacing.lg),
// 规则说明
_buildRulesCard(colorScheme),
],
),
),
),
);
}
/// 推广码卡片
Widget _buildReferralCodeCard(ColorScheme colorScheme) {
final referralCode = _welfareData?['referralCode'] as String? ?? '';
return GlassPanel(
padding: EdgeInsets.all(AppSpacing.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: EdgeInsets.all(AppSpacing.sm),
decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Icon(
LucideIcons.users,
color: colorScheme.primary,
size: 20,
),
),
SizedBox(width: AppSpacing.md),
Text(
'我的推广码',
style: GoogleFonts.spaceGrotesk(
fontSize: 16,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
],
),
SizedBox(height: AppSpacing.lg),
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(
horizontal: AppSpacing.lg,
vertical: AppSpacing.md,
),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.2),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
referralCode.isEmpty ? '暂无推广码' : referralCode,
style: GoogleFonts.spaceGrotesk(
fontSize: 24,
fontWeight: FontWeight.bold,
letterSpacing: 4,
color: referralCode.isEmpty
? colorScheme.onSurfaceVariant
: colorScheme.primary,
),
),
if (referralCode.isNotEmpty) ...[
SizedBox(width: AppSpacing.md),
GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: referralCode));
ToastUtils.show('推广码已复制');
},
child: Container(
padding: EdgeInsets.all(AppSpacing.xs + 2),
decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(AppRadius.sm),
),
child: Icon(
LucideIcons.copy,
size: 18,
color: colorScheme.primary,
),
),
),
],
],
),
),
SizedBox(height: AppSpacing.sm),
Text(
'分享推广码给好友好友注册并充值满1000u后您可领取100u奖励',
style: TextStyle(
fontSize: 11,
color: colorScheme.onSurfaceVariant,
),
),
],
),
);
}
/// 首充福利卡片
Widget _buildNewUserBonusCard(ColorScheme colorScheme) {
final newUserBonus = _welfareData?['newUserBonus'] as Map<String, dynamic>?;
final eligible = newUserBonus?['eligible'] as bool? ?? false;
final claimed = newUserBonus?['claimed'] as bool? ?? false;
final deposited = newUserBonus?['deposited'] as bool? ?? false;
String statusText;
Color statusColor;
String buttonText;
bool canClaim;
if (claimed) {
statusText = '100 USDT 已领取';
statusColor = AppColorScheme.up;
buttonText = '已领取';
canClaim = false;
} else if (eligible) {
statusText = '符合条件,立即领取!';
statusColor = colorScheme.primary;
buttonText = '领取 100u';
canClaim = true;
} else {
statusText = deposited ? '已完成充值' : '完成首次充值后可领取';
statusColor = colorScheme.onSurfaceVariant;
buttonText = '未解锁';
canClaim = false;
}
return GlassPanel(
padding: EdgeInsets.all(AppSpacing.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: EdgeInsets.all(AppSpacing.sm),
decoration: BoxDecoration(
color: (claimed ? AppColorScheme.up : colorScheme.primary)
.withOpacity(0.1),
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Icon(
claimed ? LucideIcons.check : LucideIcons.gift,
color: claimed ? AppColorScheme.up : colorScheme.primary,
size: 20,
),
),
SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'首充福利',
style: GoogleFonts.spaceGrotesk(
fontSize: 16,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
SizedBox(height: 2),
Text(
statusText,
style: TextStyle(
fontSize: 12,
color: statusColor,
),
),
],
),
),
SizedBox(
child: NeonButton(
text: buttonText,
type: canClaim ? NeonButtonType.primary : NeonButtonType.outline,
onPressed: canClaim ? () => _claimNewUserBonus() : null,
height: 36,
showGlow: canClaim,
),
),
],
),
SizedBox(height: AppSpacing.md),
Text(
'新用户首次充值完成后可领取 100 USDT',
style: TextStyle(
fontSize: 11,
color: colorScheme.onSurfaceVariant,
),
),
],
),
);
}
/// 推广奖励列表
Widget _buildReferralRewardsSection(ColorScheme colorScheme) {
final referralRewards =
_welfareData?['referralRewards'] as List<dynamic>? ?? [];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'推广奖励',
style: GoogleFonts.spaceGrotesk(
fontSize: 16,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
SizedBox(height: AppSpacing.sm),
Text(
'被推广人每累计充值满 1000u您可领取 100u 奖励每人最多8次',
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
SizedBox(height: AppSpacing.md),
if (referralRewards.isEmpty)
GlassPanel(
padding: EdgeInsets.all(AppSpacing.xl),
child: Center(
child: Column(
children: [
Icon(
LucideIcons.users,
size: 36,
color: colorScheme.onSurfaceVariant.withOpacity(0.4),
),
SizedBox(height: AppSpacing.sm),
Text(
'暂无推广用户',
style: TextStyle(
color: colorScheme.onSurfaceVariant,
fontSize: 13,
),
),
],
),
),
)
else
...referralRewards.map((item) {
final data = item as Map<String, dynamic>;
return _buildReferralRewardCard(data, colorScheme);
}),
],
);
}
Widget _buildReferralRewardCard(
Map<String, dynamic> data, ColorScheme colorScheme) {
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<dynamic>? ?? [];
return Padding(
padding: EdgeInsets.only(bottom: AppSpacing.md),
child: GlassPanel(
padding: EdgeInsets.all(AppSpacing.md),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
CircleAvatar(
radius: 16,
backgroundColor: colorScheme.primary.withOpacity(0.1),
child: Text(
username.isNotEmpty ? username[0].toUpperCase() : '?',
style: TextStyle(
color: colorScheme.primary,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
),
SizedBox(width: AppSpacing.sm),
Text(
username,
style: GoogleFonts.spaceGrotesk(
fontSize: 14,
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'累计充值: $totalDeposit U',
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
if (claimableCount > 0)
Text(
'$claimableCount 个可领取',
style: TextStyle(
fontSize: 11,
color: colorScheme.primary,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
SizedBox(height: AppSpacing.sm),
// 里程碑进度
Wrap(
spacing: 6,
runSpacing: 6,
children: milestones.map((m) {
final milestone = m as Map<String, dynamic>;
final earned = milestone['earned'] as bool? ?? false;
final claimed = milestone['claimed'] as bool? ?? false;
final claimable = milestone['claimable'] as bool? ?? false;
final milestoneNum = milestone['milestone'] as int? ?? 0;
Color bgColor;
Color textColor;
VoidCallback? onTap;
if (claimed) {
bgColor = AppColorScheme.up.withOpacity(0.15);
textColor = AppColorScheme.up;
onTap = null;
} else if (claimable) {
bgColor = colorScheme.primary.withOpacity(0.15);
textColor = colorScheme.primary;
onTap = () => _claimReferralBonus(
data['userId'] as int,
milestoneNum,
);
} else if (earned) {
bgColor = colorScheme.surfaceContainerHigh;
textColor = colorScheme.onSurfaceVariant;
onTap = null;
} else {
bgColor = colorScheme.surfaceContainerHighest
.withOpacity(0.5);
textColor = colorScheme.onSurfaceVariant.withOpacity(0.5);
onTap = null;
}
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: claimable
? colorScheme.primary.withOpacity(0.3)
: Colors.transparent,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (claimed)
Icon(LucideIcons.check, size: 12, color: textColor)
else if (claimable)
Icon(LucideIcons.gift, size: 12, color: textColor),
if (claimed || claimable) SizedBox(width: 4),
Text(
'${milestoneNum}k',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: textColor,
),
),
],
),
),
);
}).toList(),
),
],
),
),
);
}
/// 规则说明
Widget _buildRulesCard(ColorScheme colorScheme) {
return Container(
padding: EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHigh.withOpacity(0.3),
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.1),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(LucideIcons.info, size: 16, color: colorScheme.onSurfaceVariant),
SizedBox(width: AppSpacing.sm),
Text(
'规则说明',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
),
],
),
SizedBox(height: AppSpacing.sm),
_buildRuleItem('新用户首次充值完成后可领取 100 USDT 首充福利', colorScheme),
_buildRuleItem('被推广人累计充值每满 1000u推广人可领 100u', colorScheme),
_buildRuleItem('每位被推广人最多可触发 8 次推广奖励', colorScheme),
_buildRuleItem('所有奖励将直接存入资金账户', colorScheme),
],
),
);
}
Widget _buildRuleItem(String text, ColorScheme colorScheme) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 3),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(top: 6),
width: 4,
height: 4,
decoration: BoxDecoration(
color: colorScheme.onSurfaceVariant,
shape: BoxShape.circle,
),
),
SizedBox(width: AppSpacing.sm),
Expanded(
child: Text(
text,
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
),
],
),
);
}
Future<void> _claimNewUserBonus() async {
try {
final bonusService = context.read<BonusService>();
final response = await bonusService.claimNewUserBonus();
if (!mounted) return;
if (response.success) {
context.read<AssetProvider>().refreshAll(force: true);
context.read<AppEventBus>().fire(AppEventType.assetChanged);
ToastUtils.show('领取成功100 USDT 已到账');
_loadData();
} else {
ToastUtils.show(response.message ?? '领取失败');
}
} catch (e) {
ToastUtils.show('领取失败: $e');
}
}
Future<void> _claimReferralBonus(int referredUserId, int milestone) async {
try {
final bonusService = context.read<BonusService>();
final response = await bonusService.claimReferralBonus(
referredUserId,
milestone,
);
if (!mounted) return;
if (response.success) {
context.read<AssetProvider>().refreshAll(force: true);
context.read<AppEventBus>().fire(AppEventType.assetChanged);
ToastUtils.show('领取成功100 USDT 已到账');
_loadData();
} else {
ToastUtils.show(response.message ?? '领取失败');
}
} catch (e) {
ToastUtils.show('领取失败: $e');
}
}
}