rwadurian/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart

2663 lines
89 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 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:device_info_plus/device_info_plus.dart';
import '../../../../core/di/injection_container.dart';
import '../../../../core/services/referral_service.dart';
import '../../../../core/services/reward_service.dart';
import '../../../../routes/route_paths.dart';
import '../../../../routes/app_router.dart';
import '../widgets/team_tree_widget.dart';
/// 个人中心页面 - 显示用户信息、社区数据、收益和设置
/// 包含用户资料、推荐信息、社区考核、收益领取等功能
class ProfilePage extends ConsumerStatefulWidget {
const ProfilePage({super.key});
@override
ConsumerState<ProfilePage> createState() => _ProfilePageState();
}
class _ProfilePageState extends ConsumerState<ProfilePage> {
// 用户数据(从存储/API加载
String _nickname = '加载中...';
String _serialNumber = '--';
String? _avatarSvg;
String? _avatarUrl;
String? _localAvatarPath; // 本地头像文件路径
String _referrerSerial = '--'; // 推荐人序列号从API获取
String _referralCode = '--'; // 我的推荐码
// 授权数据(从 authorization-service 获取)
String _community = '--';
String _parentCommunity = '--';
String _childCommunity = '--';
String _authCityCompany = '--'; // 市团队
String _cityCompany = '--'; // 市区域
String _authProvinceCompany = '--'; // 省团队
String _provinceCompany = '--'; // 省区域
String _province = '--';
// 团队数据(从 referral-service 获取)
int _directReferralCount = 0;
int _totalTeamCount = 0;
int _personalPlantingCount = 0;
int _teamPlantingCount = 0;
double _leaderboardScore = 0;
int? _leaderboardRank;
// 直推数据(从 referral-service 获取)
List<Map<String, dynamic>> _referrals = [];
// 社区考核数据(从 authorization-service 获取)
bool _hasCommunityAuth = false; // 是否有社区授权
bool _communityBenefitActive = false; // 社区权益是否激活
int _communityCurrentTreeCount = 0; // 当前团队认种数量
int _communityInitialTarget = 10; // 初始考核目标社区固定10
int _communityMonthlyTarget = 10; // 月度考核目标
int _communityMonthIndex = 0; // 当前考核月份
// 市团队考核数据
bool _hasAuthCityCompanyAuth = false;
bool _authCityCompanyBenefitActive = false;
int _authCityCompanyCurrentTreeCount = 0;
int _authCityCompanyInitialTarget = 100;
int _authCityCompanyMonthlyTarget = 100;
int _authCityCompanyMonthIndex = 0;
// 省团队考核数据
bool _hasAuthProvinceCompanyAuth = false;
bool _authProvinceCompanyBenefitActive = false;
int _authProvinceCompanyCurrentTreeCount = 0;
int _authProvinceCompanyInitialTarget = 500;
int _authProvinceCompanyMonthlyTarget = 500;
int _authProvinceCompanyMonthIndex = 0;
// 市区域考核数据
bool _hasCityCompanyAuth = false;
bool _cityCompanyBenefitActive = false;
int _cityCompanyCurrentTreeCount = 0;
int _cityCompanyInitialTarget = 100;
int _cityCompanyMonthlyTarget = 100;
int _cityCompanyMonthIndex = 0;
// 省区域考核数据
bool _hasProvinceCompanyAuth = false;
bool _provinceCompanyBenefitActive = false;
int _provinceCompanyCurrentTreeCount = 0;
int _provinceCompanyInitialTarget = 500;
int _provinceCompanyMonthlyTarget = 500;
int _provinceCompanyMonthIndex = 0;
// 收益数据(从 reward-service 直接获取)
double _pendingUsdt = 0.0;
double _pendingPower = 0.0;
double _settleableUsdt = 0.0;
double _settledUsdt = 0.0;
double _expiredUsdt = 0.0;
double _expiredPower = 0.0;
bool _isLoadingWallet = true;
String? _walletError;
// 待领取奖励列表(每一笔单独显示)
List<PendingRewardItem> _pendingRewards = [];
// 倒计时
Timer? _timer;
int _remainingSeconds = 0;
// 应用版本信息
String _appVersion = '--';
String _buildNumber = '--';
String _packageName = '--';
String _deviceModel = '--';
String _osVersion = '--';
String _platform = '--';
@override
void initState() {
super.initState();
// 先同步检查本地头像,再异步加载其他数据
_checkLocalAvatarSync();
_loadUserData();
_loadAppInfo();
// 加载推荐和授权数据
_loadReferralData();
_loadAuthorizationData();
// 加载钱包和收益数据
_loadWalletData();
}
/// 加载应用信息
Future<void> _loadAppInfo() async {
try {
// 加载包信息
final packageInfo = await PackageInfo.fromPlatform();
// 加载设备信息
final deviceInfoPlugin = DeviceInfoPlugin();
String deviceModel = '';
String osVersion = '';
String platform = '';
if (Platform.isAndroid) {
final androidInfo = await deviceInfoPlugin.androidInfo;
deviceModel = '${androidInfo.brand} ${androidInfo.model}';
osVersion = 'Android ${androidInfo.version.release} (SDK ${androidInfo.version.sdkInt})';
platform = 'Android';
} else if (Platform.isIOS) {
final iosInfo = await deviceInfoPlugin.iosInfo;
deviceModel = iosInfo.model;
osVersion = '${iosInfo.systemName} ${iosInfo.systemVersion}';
platform = 'iOS';
}
if (mounted) {
setState(() {
_appVersion = packageInfo.version;
_buildNumber = packageInfo.buildNumber;
_packageName = packageInfo.packageName;
_deviceModel = deviceModel;
_osVersion = osVersion;
_platform = platform;
});
}
} catch (e) {
debugPrint('[ProfilePage] 加载应用信息失败: $e');
}
}
/// 同步检查本地头像文件(在 build 之前快速获取)
void _checkLocalAvatarSync() {
// 使用 WidgetsBinding 确保在第一帧渲染前执行
WidgetsBinding.instance.addPostFrameCallback((_) async {
final accountService = ref.read(accountServiceProvider);
final localPath = await accountService.getLocalAvatarPath();
if (mounted && localPath != null && _localAvatarPath == null) {
setState(() {
_localAvatarPath = localPath;
});
}
});
}
/// 加载用户数据
Future<void> _loadUserData() async {
debugPrint('[ProfilePage] _loadUserData() - 开始加载本地存储数据...');
final accountService = ref.read(accountServiceProvider);
// 并行加载本地存储数据
final results = await Future.wait([
accountService.getUsername(),
accountService.getUserSerialNum(),
accountService.getAvatarSvg(),
accountService.getAvatarUrl(),
accountService.getLocalAvatarPath(),
accountService.getReferralCode(),
]);
final username = results[0] as String?;
final serialNum = results[1] as String?;
final avatarSvg = results[2] as String?;
final avatarUrl = results[3] as String?;
final localAvatarPath = results[4] as String?;
final referralCode = results[5] as String?;
debugPrint('[ProfilePage] _loadUserData() - 本地存储数据:');
debugPrint('[ProfilePage] username: $username');
debugPrint('[ProfilePage] serialNum: $serialNum');
debugPrint('[ProfilePage] avatarSvg: ${avatarSvg != null ? "长度=${avatarSvg.length}" : "null"}');
debugPrint('[ProfilePage] avatarUrl: $avatarUrl');
debugPrint('[ProfilePage] localAvatarPath: $localAvatarPath');
if (mounted) {
setState(() {
_nickname = username ?? '未设置昵称';
_serialNumber = serialNum?.toString() ?? '--';
_avatarSvg = avatarSvg;
_avatarUrl = avatarUrl;
_localAvatarPath = localAvatarPath;
_referralCode = referralCode ?? '--';
});
// 如果有远程URL但没有本地缓存后台下载并缓存
if (avatarUrl != null && avatarUrl.isNotEmpty && localAvatarPath == null) {
_downloadAndCacheAvatar(avatarUrl);
}
}
// 从API获取完整用户信息包括推荐人
_loadMeData();
}
/// 从API加载完整用户信息
Future<void> _loadMeData() async {
try {
debugPrint('[ProfilePage] _loadMeData() - 开始加载用户信息...');
final accountService = ref.read(accountServiceProvider);
final meData = await accountService.getMe();
debugPrint('[ProfilePage] _loadMeData() - API返回数据:');
debugPrint('[ProfilePage] nickname: ${meData.nickname}');
debugPrint('[ProfilePage] avatarUrl: ${meData.avatarUrl != null ? "长度=${meData.avatarUrl!.length}" : "null"}');
debugPrint('[ProfilePage] avatarUrl内容前100字符: ${meData.avatarUrl?.substring(0, meData.avatarUrl!.length > 100 ? 100 : meData.avatarUrl!.length) ?? "null"}');
debugPrint('[ProfilePage] 当前_avatarSvg: ${_avatarSvg != null ? "长度=${_avatarSvg!.length}" : "null"}');
if (mounted) {
setState(() {
_referrerSerial = meData.inviterSequence?.toString() ?? '';
// 如果API返回了更新的数据也更新本地显示
if (meData.nickname.isNotEmpty) {
_nickname = meData.nickname;
}
if (meData.referralCode.isNotEmpty) {
_referralCode = meData.referralCode;
}
// 如果本地没有头像但API返回了头像更新显示
debugPrint('[ProfilePage] _loadMeData() - 检查头像同步条件:');
debugPrint('[ProfilePage] _avatarSvg == null: ${_avatarSvg == null}');
debugPrint('[ProfilePage] meData.avatarUrl != null: ${meData.avatarUrl != null}');
debugPrint('[ProfilePage] meData.avatarUrl.isNotEmpty: ${meData.avatarUrl?.isNotEmpty ?? false}');
if (_avatarSvg == null && meData.avatarUrl != null && meData.avatarUrl!.isNotEmpty) {
debugPrint('[ProfilePage] _loadMeData() - 同步头像到本地!');
_avatarSvg = meData.avatarUrl;
// 同时保存到本地存储避免下次还需要从API获取
accountService.updateLocalAvatarSvg(meData.avatarUrl!);
} else {
debugPrint('[ProfilePage] _loadMeData() - 不需要同步头像 (条件不满足)');
}
});
}
} catch (e, stackTrace) {
debugPrint('[ProfilePage] 加载用户信息失败: $e');
debugPrint('[ProfilePage] 堆栈: $stackTrace');
// 失败时保持现有数据,不影响页面显示
}
}
/// 加载推荐数据 (from referral-service)
Future<void> _loadReferralData() async {
try {
debugPrint('[ProfilePage] 开始加载推荐数据...');
final referralService = ref.read(referralServiceProvider);
// 并行加载推荐信息和直推列表
final results = await Future.wait([
referralService.getMyReferralInfo(),
referralService.getDirectReferrals(limit: 10),
]);
final referralInfo = results[0] as ReferralInfoResponse;
final directReferrals = results[1] as DirectReferralsResponse;
debugPrint('[ProfilePage] 推荐数据加载成功: directReferralCount=${referralInfo.directReferralCount}, totalTeamCount=${referralInfo.totalTeamCount}');
if (mounted) {
setState(() {
_directReferralCount = referralInfo.directReferralCount;
_totalTeamCount = referralInfo.totalTeamCount;
_personalPlantingCount = referralInfo.personalPlantingCount;
_teamPlantingCount = referralInfo.teamPlantingCount;
_leaderboardScore = referralInfo.leaderboardScore;
_leaderboardRank = referralInfo.leaderboardRank;
// 转换直推列表格式
_referrals = directReferrals.referrals.map((r) => <String, dynamic>{
'serial': r.accountSequence, // 使用8位账户序列号显示
'personal': r.personalPlantingCount, // 个人认种量
'team': r.teamPlantingCount, // 团队认种量
}).toList();
});
}
} catch (e, stackTrace) {
debugPrint('[ProfilePage] 加载推荐数据失败: $e');
debugPrint('[ProfilePage] 堆栈: $stackTrace');
// 失败时保持默认数据
}
}
/// 加载授权数据 (from authorization-service)
Future<void> _loadAuthorizationData() async {
try {
debugPrint('[ProfilePage] 开始加载授权数据...');
final authorizationService = ref.read(authorizationServiceProvider);
final summary = await authorizationService.getMyAuthorizationSummary();
debugPrint('[ProfilePage] 授权数据加载成功:');
debugPrint('[ProfilePage] 社区授权: ${summary.community != null}');
debugPrint('[ProfilePage] 市团队授权: ${summary.authCityCompany != null}');
debugPrint('[ProfilePage] 市区域授权: ${summary.cityCompany != null}');
debugPrint('[ProfilePage] 省团队授权: ${summary.authProvinceCompany != null}');
debugPrint('[ProfilePage] 省区域授权: ${summary.provinceCompany != null}');
// 获取社区层级数据
try {
final hierarchy = await authorizationService.getMyCommunityHierarchy();
if (mounted) {
setState(() {
_parentCommunity = hierarchy.parentCommunity.communityName;
_childCommunity = hierarchy.childCommunityCount > 0
? '${hierarchy.childCommunities.first.communityName}${hierarchy.childCommunityCount > 1 ? "${hierarchy.childCommunityCount}个" : ""}'
: '--';
});
}
debugPrint('[ProfilePage] 社区层级: 上级=${hierarchy.parentCommunity.communityName}, 下级数量=${hierarchy.childCommunityCount}');
} catch (e) {
debugPrint('[ProfilePage] 获取社区层级失败: $e');
// 失败时保持默认值
}
if (mounted) {
setState(() {
_community = summary.communityName ?? '--';
_authCityCompany = summary.authCityCompanyName ?? '--';
_cityCompany = summary.cityCompanyName ?? '--';
_authProvinceCompany = summary.authProvinceCompanyName ?? '--';
_provinceCompany = summary.provinceCompanyName ?? '--';
// 更新社区考核数据
if (summary.community != null) {
_hasCommunityAuth = true;
_communityBenefitActive = summary.community!.benefitActive;
_communityCurrentTreeCount = summary.community!.currentTreeCount;
_communityInitialTarget = summary.community!.initialTargetTreeCount;
_communityMonthlyTarget = summary.community!.monthlyTargetTreeCount;
_communityMonthIndex = summary.community!.currentMonthIndex;
debugPrint('[ProfilePage] 社区考核数据:');
debugPrint('[ProfilePage] 权益激活: $_communityBenefitActive');
debugPrint('[ProfilePage] 当前数量: $_communityCurrentTreeCount');
debugPrint('[ProfilePage] 初始目标: $_communityInitialTarget');
debugPrint('[ProfilePage] 月度目标: $_communityMonthlyTarget');
debugPrint('[ProfilePage] 考核月份: $_communityMonthIndex');
} else {
_hasCommunityAuth = false;
}
// 更新市团队考核数据
if (summary.authCityCompany != null) {
_hasAuthCityCompanyAuth = true;
_authCityCompanyBenefitActive = summary.authCityCompany!.benefitActive;
_authCityCompanyCurrentTreeCount = summary.authCityCompany!.currentTreeCount;
_authCityCompanyInitialTarget = summary.authCityCompany!.initialTargetTreeCount;
_authCityCompanyMonthlyTarget = summary.authCityCompany!.monthlyTargetTreeCount;
_authCityCompanyMonthIndex = summary.authCityCompany!.currentMonthIndex;
} else {
_hasAuthCityCompanyAuth = false;
}
// 更新省团队考核数据
if (summary.authProvinceCompany != null) {
_hasAuthProvinceCompanyAuth = true;
_authProvinceCompanyBenefitActive = summary.authProvinceCompany!.benefitActive;
_authProvinceCompanyCurrentTreeCount = summary.authProvinceCompany!.currentTreeCount;
_authProvinceCompanyInitialTarget = summary.authProvinceCompany!.initialTargetTreeCount;
_authProvinceCompanyMonthlyTarget = summary.authProvinceCompany!.monthlyTargetTreeCount;
_authProvinceCompanyMonthIndex = summary.authProvinceCompany!.currentMonthIndex;
} else {
_hasAuthProvinceCompanyAuth = false;
}
// 更新市区域考核数据
if (summary.cityCompany != null) {
_hasCityCompanyAuth = true;
_cityCompanyBenefitActive = summary.cityCompany!.benefitActive;
_cityCompanyCurrentTreeCount = summary.cityCompany!.currentTreeCount;
_cityCompanyInitialTarget = summary.cityCompany!.initialTargetTreeCount;
_cityCompanyMonthlyTarget = summary.cityCompany!.monthlyTargetTreeCount;
_cityCompanyMonthIndex = summary.cityCompany!.currentMonthIndex;
} else {
_hasCityCompanyAuth = false;
}
// 更新省区域考核数据
if (summary.provinceCompany != null) {
_hasProvinceCompanyAuth = true;
_provinceCompanyBenefitActive = summary.provinceCompany!.benefitActive;
_provinceCompanyCurrentTreeCount = summary.provinceCompany!.currentTreeCount;
_provinceCompanyInitialTarget = summary.provinceCompany!.initialTargetTreeCount;
_provinceCompanyMonthlyTarget = summary.provinceCompany!.monthlyTargetTreeCount;
_provinceCompanyMonthIndex = summary.provinceCompany!.currentMonthIndex;
} else {
_hasProvinceCompanyAuth = false;
}
});
}
} catch (e, stackTrace) {
debugPrint('[ProfilePage] 加载授权数据失败: $e');
debugPrint('[ProfilePage] 堆栈: $stackTrace');
// 失败时保持默认数据
}
}
/// 加载收益数据 (直接从 reward-service 获取)
Future<void> _loadWalletData() async {
try {
debugPrint('[ProfilePage] ========== 加载收益数据 ==========');
debugPrint('[ProfilePage] mounted: $mounted');
setState(() {
_isLoadingWallet = true;
_walletError = null;
});
debugPrint('[ProfilePage] 获取 rewardServiceProvider...');
final rewardService = ref.read(rewardServiceProvider);
// 并行加载汇总数据和待领取列表
debugPrint('[ProfilePage] 调用 getMyRewardSummary() 和 getPendingRewards()...');
final results = await Future.wait([
rewardService.getMyRewardSummary(),
rewardService.getPendingRewards(),
]);
final summary = results[0] as RewardSummary;
final pendingRewards = results[1] as List<PendingRewardItem>;
debugPrint('[ProfilePage] -------- 收益数据加载成功 --------');
debugPrint('[ProfilePage] 待领取 USDT: ${summary.pendingUsdt}');
debugPrint('[ProfilePage] 待领取 算力: ${summary.pendingHashpower}');
debugPrint('[ProfilePage] 可结算 USDT: ${summary.settleableUsdt}');
debugPrint('[ProfilePage] 已结算 USDT: ${summary.settledTotalUsdt}');
debugPrint('[ProfilePage] 已过期 USDT: ${summary.expiredTotalUsdt}');
debugPrint('[ProfilePage] 已过期 算力: ${summary.expiredTotalHashpower}');
debugPrint('[ProfilePage] 过期时间: ${summary.pendingExpireAt}');
debugPrint('[ProfilePage] 剩余秒数: ${summary.pendingRemainingSeconds}');
debugPrint('[ProfilePage] 待领取奖励条目数: ${pendingRewards.length}');
if (mounted) {
debugPrint('[ProfilePage] 更新 UI 状态...');
setState(() {
_pendingUsdt = summary.pendingUsdt;
_pendingPower = summary.pendingHashpower;
_settleableUsdt = summary.settleableUsdt;
_settledUsdt = summary.settledTotalUsdt;
_expiredUsdt = summary.expiredTotalUsdt;
_expiredPower = summary.expiredTotalHashpower;
_remainingSeconds = summary.pendingRemainingSeconds;
_pendingRewards = pendingRewards;
_isLoadingWallet = false;
});
debugPrint('[ProfilePage] UI 状态更新完成');
debugPrint('[ProfilePage] _pendingUsdt: $_pendingUsdt');
debugPrint('[ProfilePage] _remainingSeconds: $_remainingSeconds');
debugPrint('[ProfilePage] _pendingRewards: ${_pendingRewards.length}');
// 启动倒计时(如果有待领取收益)
if (_remainingSeconds > 0) {
debugPrint('[ProfilePage] 启动倒计时: $_remainingSeconds');
_startCountdown();
} else {
debugPrint('[ProfilePage] 无需启动倒计时剩余时间为0');
}
} else {
debugPrint('[ProfilePage] 组件已卸载,跳过 UI 更新');
}
debugPrint('[ProfilePage] ================================');
} catch (e, stackTrace) {
debugPrint('[ProfilePage] !!!!!!!!!! 加载收益数据失败 !!!!!!!!!!');
debugPrint('[ProfilePage] 错误类型: ${e.runtimeType}');
debugPrint('[ProfilePage] 错误信息: $e');
debugPrint('[ProfilePage] 堆栈跟踪:');
debugPrint('$stackTrace');
if (mounted) {
setState(() {
_isLoadingWallet = false;
_walletError = '加载失败,点击重试';
});
}
}
}
/// 后台下载并缓存头像
Future<void> _downloadAndCacheAvatar(String url) async {
final accountService = ref.read(accountServiceProvider);
final localPath = await accountService.downloadAndCacheAvatar(url);
if (mounted && localPath != null) {
setState(() {
_localAvatarPath = localPath;
});
}
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
/// 开始倒计时
void _startCountdown() {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_remainingSeconds > 0) {
setState(() {
_remainingSeconds--;
});
}
});
}
/// 格式化倒计时
String _formatCountdown() {
final hours = (_remainingSeconds ~/ 3600).toString().padLeft(2, '0');
final minutes = ((_remainingSeconds % 3600) ~/ 60).toString().padLeft(2, '0');
final seconds = (_remainingSeconds % 60).toString().padLeft(2, '0');
return '$hours : $minutes : $seconds';
}
/// 复制序列号
void _copySerialNumber() {
Clipboard.setData(ClipboardData(text: _serialNumber));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('序列号已复制'),
backgroundColor: Color(0xFFD4AF37),
),
);
}
/// 跳转到分享页面
void _goToSharePage() {
context.push(
RoutePaths.share,
extra: SharePageParams(
shareLink: 'https://s3.szaiai.com/rwadurian/app-release.apk',
referralCode: _referralCode,
),
);
}
/// 领取全部收益
void _claimAllEarnings() {
// 如果没有待领取收益,提示用户
if (_pendingUsdt <= 0 && _pendingPower <= 0) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('暂无可领取的收益'),
backgroundColor: Color(0xFF8B5A2B),
),
);
return;
}
showDialog(
context: context,
builder: (dialogContext) => AlertDialog(
title: const Text('确认领取'),
content: Text(
'确定领取全部收益吗?\nUSDT: ${_formatNumber(_pendingUsdt)}\n算力: ${_formatNumber(_pendingPower)}',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext),
child: const Text('取消'),
),
TextButton(
onPressed: () async {
Navigator.pop(dialogContext);
await _doClaimRewards();
},
child: const Text('确认'),
),
],
),
);
}
/// 执行领取奖励
Future<void> _doClaimRewards() async {
try {
debugPrint('[ProfilePage] ========== 执行领取奖励 ==========');
debugPrint('[ProfilePage] 当前待领取: USDT=$_pendingUsdt, 算力=$_pendingPower');
final walletService = ref.read(walletServiceProvider);
debugPrint('[ProfilePage] 调用 claimRewards()...');
await walletService.claimRewards();
debugPrint('[ProfilePage] 领取成功,准备刷新数据');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('领取成功'),
backgroundColor: Color(0xFFD4AF37),
),
);
// 刷新钱包数据
debugPrint('[ProfilePage] 刷新钱包数据...');
_loadWalletData();
}
debugPrint('[ProfilePage] ================================');
} catch (e, stackTrace) {
debugPrint('[ProfilePage] !!!!!!!!!! 领取奖励失败 !!!!!!!!!!');
debugPrint('[ProfilePage] 错误: $e');
debugPrint('[ProfilePage] 堆栈: $stackTrace');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('领取失败: $e'),
backgroundColor: Colors.red,
),
);
}
}
}
/// 结算
void _onSettlement() {
context.go(RoutePaths.trading);
}
/// 充值USDT
void _onDeposit() {
context.push(RoutePaths.depositUsdt);
}
/// 进入交易
void _goToTrading() {
context.go(RoutePaths.trading);
}
/// 进入认种
void _goToPlanting() {
context.push(RoutePaths.plantingQuantity);
}
/// 谷歌验证器
void _goToGoogleAuth() {
context.push(RoutePaths.googleAuth);
}
/// 修改密码
void _goToChangePassword() {
context.push(RoutePaths.changePassword);
}
/// 绑定邮箱
void _goToBindEmail() {
context.push(RoutePaths.bindEmail);
}
/// 编辑资料
Future<void> _goToEditProfile() async {
final result = await context.push<bool>(RoutePaths.editProfile);
// 如果编辑页面返回 true说明有更新刷新用户数据
if (result == true && mounted) {
_loadUserData();
}
}
/// 格式化数字
String _formatNumber(double number) {
final parts = number.toStringAsFixed(2).split('.');
final intPart = parts[0].replaceAllMapped(
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
(Match m) => '${m[1]},',
);
return '$intPart.${parts[1]}';
}
/// 格式化整数
String _formatInt(int number) {
return number.toString().replaceAllMapped(
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
(Match m) => '${m[1]},',
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFFFF5E6),
Color(0xFFFFE4B5),
],
),
),
child: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 16),
// 用户头像和基本信息
_buildUserHeader(),
const SizedBox(height: 16),
// 推荐人信息卡片
_buildReferralInfoCard(),
const SizedBox(height: 16),
// 社区/省份标签
_buildCommunityLabel(),
const SizedBox(height: 8),
// 认种按钮
_buildPlantingButton(),
const SizedBox(height: 16),
// 主要内容卡片
_buildMainContentCard(),
const SizedBox(height: 24),
],
),
),
),
),
),
);
}
/// 构建用户头像和基本信息
Widget _buildUserHeader() {
return Row(
children: [
// 头像(点击跳转到编辑资料页面)
GestureDetector(
onTap: _goToEditProfile,
child: Container(
width: 80,
height: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
color: const Color(0xFFFFF5E6),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(40),
child: _buildAvatarContent(),
),
),
),
const SizedBox(width: 16),
// 用户信息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_nickname,
style: const TextStyle(
fontSize: 20,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
letterSpacing: -0.3,
color: Color(0xFF5D4037),
),
),
const SizedBox(height: 3),
Row(
children: [
Text(
'序列号: $_serialNumber',
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
height: 1.5,
color: Color(0xCC5D4037),
),
),
const SizedBox(width: 8),
GestureDetector(
onTap: _copySerialNumber,
child: const Icon(
Icons.copy,
size: 16,
color: Color(0xCC5D4037),
),
),
],
),
],
),
),
],
);
}
/// 构建头像内容优先本地文件其次网络URL最后SVG
Widget _buildAvatarContent() {
debugPrint('[ProfilePage] _buildAvatarContent() - 开始构建头像');
debugPrint('[ProfilePage] _localAvatarPath: $_localAvatarPath');
debugPrint('[ProfilePage] _avatarUrl: $_avatarUrl');
debugPrint('[ProfilePage] _avatarSvg: ${_avatarSvg != null ? "长度=${_avatarSvg!.length}" : "null"}');
// 1. 优先显示本地缓存的头像文件
if (_localAvatarPath != null && _localAvatarPath!.isNotEmpty) {
final file = File(_localAvatarPath!);
// 同步检查文件是否存在
if (file.existsSync()) {
debugPrint('[ProfilePage] _buildAvatarContent() - 使用本地文件: $_localAvatarPath');
return Image.file(
file,
width: 80,
height: 80,
fit: BoxFit.cover,
gaplessPlayback: true, // 防止图片切换时闪烁
errorBuilder: (context, error, stackTrace) {
debugPrint('[ProfilePage] _buildAvatarContent() - 本地文件加载失败: $error');
// 本地文件加载失败尝试网络URL
return _buildNetworkOrSvgAvatar();
},
);
} else {
debugPrint('[ProfilePage] _buildAvatarContent() - 本地文件不存在');
}
}
// 2. 没有本地缓存尝试网络URL或SVG
return _buildNetworkOrSvgAvatar();
}
/// 构建网络URL或SVG头像
Widget _buildNetworkOrSvgAvatar() {
debugPrint('[ProfilePage] _buildNetworkOrSvgAvatar() - 尝试网络URL或SVG');
// 尝试显示网络图片URL
if (_avatarUrl != null && _avatarUrl!.isNotEmpty) {
debugPrint('[ProfilePage] _buildNetworkOrSvgAvatar() - 使用网络URL: $_avatarUrl');
return Image.network(
_avatarUrl!,
width: 80,
height: 80,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(
child: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFFD4AF37)),
),
),
);
},
errorBuilder: (context, error, stackTrace) {
debugPrint('[ProfilePage] _buildNetworkOrSvgAvatar() - 网络图片加载失败: $error');
// 加载失败时显示SVG或默认头像
return _buildSvgOrDefaultAvatar();
},
);
}
// 显示SVG或默认头像
debugPrint('[ProfilePage] _buildNetworkOrSvgAvatar() - 无网络URL尝试SVG');
return _buildSvgOrDefaultAvatar();
}
/// 构建SVG或默认头像
/// 注意_avatarSvg 可能存储的是 URL用户上传的图片或 SVG 字符串(随机生成的头像)
Widget _buildSvgOrDefaultAvatar() {
if (_avatarSvg != null && _avatarSvg!.isNotEmpty) {
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 检查头像内容,长度=${_avatarSvg!.length}');
debugPrint('[ProfilePage] 内容前50字符: ${_avatarSvg!.substring(0, _avatarSvg!.length > 50 ? 50 : _avatarSvg!.length)}');
// 检测是否是 URL用户上传的头像图片
if (_avatarSvg!.startsWith('http://') || _avatarSvg!.startsWith('https://')) {
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 检测到是URL使用网络图片');
return Image.network(
_avatarSvg!,
width: 80,
height: 80,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(
child: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFFD4AF37)),
),
),
);
},
errorBuilder: (context, error, stackTrace) {
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 网络图片加载失败: $error');
return const Icon(
Icons.person,
size: 40,
color: Color(0xFF8B5A2B),
);
},
);
}
// 检测是否是 SVG 字符串(随机生成的 SVG 头像)
if (_avatarSvg!.contains('<svg') || _avatarSvg!.startsWith('<?xml')) {
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 检测到是SVG字符串使用SvgPicture');
return SvgPicture.string(
_avatarSvg!,
width: 80,
height: 80,
fit: BoxFit.cover,
);
}
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 未知格式,使用默认图标');
}
debugPrint('[ProfilePage] _buildSvgOrDefaultAvatar() - 无头像数据,使用默认图标');
return const Icon(
Icons.person,
size: 40,
color: Color(0xFF8B5A2B),
);
}
/// 构建推荐人信息卡片
Widget _buildReferralInfoCard() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0x80FFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 第一行:推荐人序列号 | 下级社区
Row(
children: [
Expanded(
child: _buildInfoItem('推荐人的序列号', _referrerSerial),
),
Expanded(
child: _buildInfoItem('下级社区', _childCommunity),
),
],
),
const SizedBox(height: 12),
// 第二行:所属社区 | 市团队
Row(
children: [
Expanded(
child: _buildInfoItem('所属社区', _community),
),
Expanded(
child: _buildInfoItem('市团队', _authCityCompany),
),
],
),
const SizedBox(height: 12),
// 第三行:上级社区 | 市区域
Row(
children: [
Expanded(
child: _buildInfoItem('上级社区', _parentCommunity),
),
Expanded(
child: _buildInfoItem('市区域', _cityCompany),
),
],
),
const SizedBox(height: 12),
// 第四行:省团队 | 省区域
Row(
children: [
Expanded(
child: _buildInfoItem('省团队', _authProvinceCompany),
),
Expanded(
child: _buildInfoItem('省区域', _provinceCompany),
),
],
),
],
),
);
}
/// 构建信息项(标签 + 值)
Widget _buildInfoItem(String label, String value) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'$label:',
style: const TextStyle(
fontSize: 12,
fontFamily: 'Inter',
height: 1.5,
color: Color(0x995D4037),
),
),
const SizedBox(height: 2),
Text(
value,
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w600,
height: 1.5,
color: Color(0xFF5D4037),
),
),
],
);
}
/// 构建社区/省份标签
Widget _buildCommunityLabel() {
return const Text(
'社区 / 省份',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
height: 1.5,
color: Color(0xFF5D4037),
),
);
}
/// 构建认种按钮
Widget _buildPlantingButton() {
return GestureDetector(
onTap: _goToPlanting,
child: Container(
width: double.infinity,
height: 48,
decoration: BoxDecoration(
color: const Color(0xFFD4AF37),
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: Text(
'认种 / Planting',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.5,
letterSpacing: 0.24,
color: Colors.white,
),
),
),
),
);
}
/// 构建主要内容卡片
Widget _buildMainContentCard() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0x80FFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: Column(
children: [
// 收益区域
_buildEarningsSection(),
const SizedBox(height: 16),
// 结算区域
_buildSettlementSection(),
const SizedBox(height: 16),
// 已过期区域
_buildExpiredSection(),
const SizedBox(height: 16),
// 操作按钮
_buildActionButtons(),
const SizedBox(height: 16),
// 团队统计
_buildTeamStats(),
const SizedBox(height: 16),
// 直推列表
_buildReferralList(),
const SizedBox(height: 16),
// 我的伞下
_buildMyTeamTree(),
const SizedBox(height: 16),
// 分享邀请按钮
_buildShareButton(),
const SizedBox(height: 16),
// 社区权益考核
_buildCommunityAssessment(),
// 市团队权益考核
if (_hasAuthCityCompanyAuth) ...[
const SizedBox(height: 16),
_buildRoleAssessment(
title: '市团队权益考核',
hasAuth: _hasAuthCityCompanyAuth,
benefitActive: _authCityCompanyBenefitActive,
currentTreeCount: _authCityCompanyCurrentTreeCount,
initialTarget: _authCityCompanyInitialTarget,
monthlyTarget: _authCityCompanyMonthlyTarget,
monthIndex: _authCityCompanyMonthIndex,
rewardDescription: '每新增认种 1 棵可获得 40 USDT',
),
],
// 省团队权益考核
if (_hasAuthProvinceCompanyAuth) ...[
const SizedBox(height: 16),
_buildRoleAssessment(
title: '省团队权益考核',
hasAuth: _hasAuthProvinceCompanyAuth,
benefitActive: _authProvinceCompanyBenefitActive,
currentTreeCount: _authProvinceCompanyCurrentTreeCount,
initialTarget: _authProvinceCompanyInitialTarget,
monthlyTarget: _authProvinceCompanyMonthlyTarget,
monthIndex: _authProvinceCompanyMonthIndex,
rewardDescription: '每新增认种 1 棵可获得 20 USDT',
),
],
// 市区域权益考核
if (_hasCityCompanyAuth) ...[
const SizedBox(height: 16),
_buildRoleAssessment(
title: '市区域权益考核',
hasAuth: _hasCityCompanyAuth,
benefitActive: _cityCompanyBenefitActive,
currentTreeCount: _cityCompanyCurrentTreeCount,
initialTarget: _cityCompanyInitialTarget,
monthlyTarget: _cityCompanyMonthlyTarget,
monthIndex: _cityCompanyMonthIndex,
rewardDescription: '每新增认种 1 棵可获得 35 USDT',
),
],
// 省区域权益考核
if (_hasProvinceCompanyAuth) ...[
const SizedBox(height: 16),
_buildRoleAssessment(
title: '省区域权益考核',
hasAuth: _hasProvinceCompanyAuth,
benefitActive: _provinceCompanyBenefitActive,
currentTreeCount: _provinceCompanyCurrentTreeCount,
initialTarget: _provinceCompanyInitialTarget,
monthlyTarget: _provinceCompanyMonthlyTarget,
monthIndex: _provinceCompanyMonthIndex,
rewardDescription: '每新增认种 1 棵可获得 15 USDT',
),
],
const SizedBox(height: 16),
// 设置菜单
_buildSettingsMenu(),
const SizedBox(height: 16),
// 应用版本信息
_buildAppVersionInfo(),
],
),
);
}
/// 构建收益区域
Widget _buildEarningsSection() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFFFF5E6),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: _isLoadingWallet
? _buildLoadingState()
: _walletError != null
? _buildErrorState()
: _buildEarningsContent(),
);
}
/// 构建加载状态
Widget _buildLoadingState() {
return const SizedBox(
height: 200,
child: Center(
child: CircularProgressIndicator(
color: Color(0xFFD4AF37),
strokeWidth: 2,
),
),
);
}
/// 构建错误状态
Widget _buildErrorState() {
return GestureDetector(
onTap: _loadWalletData,
child: SizedBox(
height: 200,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
color: Color(0xFFE65100),
size: 40,
),
const SizedBox(height: 8),
Text(
_walletError!,
style: const TextStyle(
fontSize: 14,
color: Color(0xFFE65100),
),
),
],
),
),
),
);
}
/// 构建收益内容
Widget _buildEarningsContent() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 汇总信息区域
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 倒计时
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'24小时倒计时',
style: TextStyle(
fontSize: 12,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xCC5D4037),
),
),
Text(
_formatCountdown(),
style: const TextStyle(
fontSize: 14,
fontFamily: 'Consolas',
fontWeight: FontWeight.w700,
height: 1.25,
letterSpacing: 0.7,
color: Color(0xFFD4AF37),
),
),
],
),
),
// 汇总待领取 USDT
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'待领取 (USDT)',
style: TextStyle(
fontSize: 12,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xCC5D4037),
),
),
Text(
_formatNumber(_pendingUsdt),
style: const TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
color: Color(0xFF5D4037),
),
),
],
),
),
// 汇总待领取算力
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'待领取 (算力)',
style: TextStyle(
fontSize: 12,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xCC5D4037),
),
),
Text(
_formatNumber(_pendingPower),
style: const TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
color: Color(0xFF5D4037),
),
),
],
),
),
],
),
// 待领取奖励列表
if (_pendingRewards.isNotEmpty) ...[
const SizedBox(height: 16),
const Divider(color: Color(0x33D4AF37), height: 1),
const SizedBox(height: 12),
const Text(
'待领取明细',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w600,
color: Color(0xFF5D4037),
),
),
const SizedBox(height: 8),
// 奖励条目列表
...(_pendingRewards.map((item) => _buildPendingRewardItem(item))),
],
const SizedBox(height: 15),
// 领取全部按钮
GestureDetector(
onTap: _claimAllEarnings,
child: Container(
width: double.infinity,
height: 40,
decoration: BoxDecoration(
color: const Color(0xFFD4AF37),
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: Text(
'领取全部',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.5,
letterSpacing: 0.21,
color: Colors.white,
),
),
),
),
),
],
);
}
/// 构建单条待领取奖励项
Widget _buildPendingRewardItem(PendingRewardItem item) {
// 确保剩余时间不为负数
final remainingSeconds = item.remainingSeconds > 0 ? item.remainingSeconds : 0;
final hours = (remainingSeconds ~/ 3600).toString().padLeft(2, '0');
final minutes = ((remainingSeconds % 3600) ~/ 60).toString().padLeft(2, '0');
final seconds = (remainingSeconds % 60).toString().padLeft(2, '0');
final countdown = '$hours:$minutes:$seconds';
// 构建金额显示文本
final List<String> amountParts = [];
if (item.usdtAmount > 0) {
amountParts.add('${_formatNumber(item.usdtAmount)} USDT');
}
if (item.hashpowerAmount > 0) {
amountParts.add('${_formatNumber(item.hashpowerAmount)} 算力');
}
// 如果都为0显示默认文本
final amountText = amountParts.isNotEmpty ? amountParts.join(' ') : '0 USDT';
return Container(
margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: const Color(0x22D4AF37),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 第一行:权益类型 + 倒计时
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
item.rightTypeName,
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w600,
color: Color(0xFF5D4037),
),
),
Row(
children: [
const Icon(
Icons.timer_outlined,
size: 14,
color: Color(0xFFD4AF37),
),
const SizedBox(width: 4),
Text(
countdown,
style: const TextStyle(
fontSize: 12,
fontFamily: 'Consolas',
fontWeight: FontWeight.w600,
color: Color(0xFFD4AF37),
),
),
],
),
],
),
const SizedBox(height: 8),
// 第二行:金额信息
Text(
amountText,
style: const TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
color: Color(0xFF5D4037),
),
),
// 第三行:备注信息(如果有)
if (item.memo.isNotEmpty) ...[
const SizedBox(height: 4),
Text(
item.memo,
style: const TextStyle(
fontSize: 11,
fontFamily: 'Inter',
color: Color(0x995D4037),
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
],
),
);
}
/// 构建结算区域
Widget _buildSettlementSection() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFFFF5E6),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 可结算 USDT
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'可结算 (USDT)',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xCC5D4037),
),
),
Text(
_formatNumber(_settleableUsdt),
style: const TextStyle(
fontSize: 20,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
color: Color(0xFF5D4037),
),
),
],
),
const SizedBox(height: 11),
// 已结算 USDT
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'已结算 (USDT)',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xCC5D4037),
),
),
GestureDetector(
onTap: _onSettlement,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 7),
decoration: BoxDecoration(
color: const Color(0xFFD4AF37),
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'结算',
style: TextStyle(
fontSize: 12,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.5,
letterSpacing: 0.18,
color: Colors.white,
),
),
),
),
],
),
const SizedBox(height: 4),
Text(
_formatNumber(_settledUsdt),
style: const TextStyle(
fontSize: 20,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
color: Color(0xFF5D4037),
),
),
],
),
);
}
/// 构建已过期区域
Widget _buildExpiredSection() {
return SizedBox(
width: double.infinity,
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFFFF5E6),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'已过期',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.5,
color: Color(0xFF5D4037),
),
),
const SizedBox(height: 7),
// 已过期 USDT
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'已过期 (USDT)',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xCC5D4037),
),
),
Text(
_formatNumber(_expiredUsdt),
style: const TextStyle(
fontSize: 20,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
color: Color(0xFF5D4037),
),
),
],
),
const SizedBox(height: 11),
// 已过期 算力
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'已过期 (算力)',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xCC5D4037),
),
),
Text(
_formatNumber(_expiredPower),
style: const TextStyle(
fontSize: 20,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
color: Color(0xFF5D4037),
),
),
],
),
],
),
),
);
}
/// 构建操作按钮
Widget _buildActionButtons() {
return Column(
children: [
// 充值 USDT 按钮
GestureDetector(
onTap: _onDeposit,
child: Container(
width: double.infinity,
height: 48,
decoration: BoxDecoration(
color: const Color(0xFFD4AF37),
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: Text(
'充值 USDT (KAVA / BSC)',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.5,
letterSpacing: 0.24,
color: Colors.white,
),
),
),
),
),
const SizedBox(height: 12),
// 进入交易按钮
GestureDetector(
onTap: _goToTrading,
child: Container(
width: double.infinity,
height: 48,
decoration: BoxDecoration(
color: const Color(0xFF8B5A2B),
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: Text(
'进入交易 (卖出 DST → USDT)',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.5,
letterSpacing: 0.24,
color: Colors.white,
),
),
),
),
),
],
);
}
/// 构建团队统计
Widget _buildTeamStats() {
return Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFFFF5E6),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: Column(
children: [
const Text(
'直推人数',
style: TextStyle(
fontSize: 12,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xCC5D4037),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 6),
Text(
_formatInt(_directReferralCount),
style: const TextStyle(
fontSize: 20,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
color: Color(0xFF5D4037),
),
textAlign: TextAlign.center,
),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFFFF5E6),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: Column(
children: [
const Text(
'个人种植数',
style: TextStyle(
fontSize: 12,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xCC5D4037),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 6),
Text(
_formatInt(_personalPlantingCount),
style: const TextStyle(
fontSize: 20,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
color: Color(0xFF5D4037),
),
textAlign: TextAlign.center,
),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFFFF5E6),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: Column(
children: [
const Text(
'团队种植数',
style: TextStyle(
fontSize: 12,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xCC5D4037),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 6),
Text(
_formatInt(_teamPlantingCount),
style: const TextStyle(
fontSize: 20,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
color: Color(0xFF5D4037),
),
textAlign: TextAlign.center,
),
],
),
),
),
],
);
}
/// 构建直推列表
Widget _buildReferralList() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.only(top: 8),
child: Text(
'直推列表',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.5,
color: Color(0xFF5D4037),
),
),
),
const SizedBox(height: 8),
..._referrals.map((referral) => Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: const Color(0xCCFFF5E6),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'序列号: ${referral['serial']}',
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
height: 1.43,
color: Color(0xFF5D4037),
),
),
Text(
'个人/团队: ${referral['personal']} / ${referral['team']}',
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
height: 1.43,
color: Color(0xCC5D4037),
),
),
],
),
),
)),
],
);
}
/// 构建我的伞下树
Widget _buildMyTeamTree() {
// 创建根节点(自己)
final rootNode = TeamTreeNode.createRoot(
accountSequence: _serialNumber,
personalPlantingCount: _personalPlantingCount,
teamPlantingCount: _teamPlantingCount,
directReferralCount: _directReferralCount,
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.only(top: 8),
child: Text(
'我的伞下',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.5,
color: Color(0xFF5D4037),
),
),
),
const SizedBox(height: 8),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xCCFFF5E6),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: _directReferralCount == 0
? const Center(
child: Padding(
padding: EdgeInsets.all(20),
child: Text(
'暂无下级成员',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
color: Color(0x995D4037),
),
),
),
)
: TeamTreeWidget(
rootNode: rootNode,
referralService: ref.read(referralServiceProvider),
),
),
],
);
}
/// 构建分享按钮
Widget _buildShareButton() {
return GestureDetector(
onTap: _goToSharePage,
child: Container(
width: double.infinity,
height: 48,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: const Color(0x33D4AF37),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.share,
size: 24,
color: Color(0xFFD4AF37),
),
SizedBox(width: 8),
Text(
'分享邀请',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.5,
letterSpacing: 0.24,
color: Color(0xFFD4AF37),
),
),
],
),
),
);
}
/// 构建社区权益考核
Widget _buildCommunityAssessment() {
// 如果没有社区授权,显示红色提示信息
if (!_hasCommunityAuth) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0x15F44336), // 浅红色背景
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0x33F44336), // 红色边框
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'社区权益考核',
style: TextStyle(
fontSize: 18,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.56,
color: Color(0xFF5D4037),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: const Color(0x20F44336),
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'未授权',
style: TextStyle(
fontSize: 12,
fontFamily: 'Inter',
fontWeight: FontWeight.w600,
color: Color(0xFFF44336), // 红色
),
),
),
],
),
const SizedBox(height: 15),
const Text(
'暂无社区授权',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: Color(0xFFF44336), // 红色
),
),
const SizedBox(height: 8),
const Text(
'请联系管理员申请社区授权',
style: TextStyle(
fontSize: 12,
fontFamily: 'Inter',
height: 1.5,
color: Color(0x99F44336), // 浅红色
),
),
],
),
);
}
// 计算进度百分比(用于进度条)
final progressRatio = _communityInitialTarget > 0
? (_communityCurrentTreeCount / _communityInitialTarget).clamp(0.0, 1.0)
: 0.0;
// 确定状态文字和颜色
final String statusText;
final Color statusColor;
if (_communityBenefitActive) {
statusText = '已激活';
statusColor = const Color(0xFF4CAF50); // 绿色
} else {
statusText = '待激活';
statusColor = const Color(0xFFFF9800); // 橙色
}
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0x80FFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题和状态
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'社区权益考核',
style: TextStyle(
fontSize: 18,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.56,
color: Color(0xFF5D4037),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.15),
borderRadius: BorderRadius.circular(4),
),
child: Text(
statusText,
style: TextStyle(
fontSize: 12,
fontFamily: 'Inter',
fontWeight: FontWeight.w600,
color: statusColor,
),
),
),
],
),
const SizedBox(height: 15),
// 考核月份(仅在权益激活后显示)
if (_communityBenefitActive && _communityMonthIndex > 0) ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'当前考核月份',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: Color(0xCC5D4037),
),
),
Text(
'$_communityMonthIndex',
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.43,
color: Color(0xFF5D4037),
),
),
],
),
const SizedBox(height: 16),
],
// 团队认种数量 / 目标
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
_communityBenefitActive ? '团队认种 / 月度目标' : '团队认种 / 激活目标',
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: Color(0xCC5D4037),
),
),
Text(
'$_communityCurrentTreeCount / ${_communityBenefitActive ? _communityMonthlyTarget : _communityInitialTarget}',
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: Color(0xFF5D4037),
),
),
],
),
const SizedBox(height: 4),
// 进度条
Container(
height: 6,
decoration: BoxDecoration(
color: const Color(0x33D4AF37),
borderRadius: BorderRadius.circular(3),
),
child: FractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor: progressRatio,
child: Container(
decoration: BoxDecoration(
color: const Color(0xFFD4AF37),
borderRadius: BorderRadius.circular(3),
),
),
),
),
const SizedBox(height: 16),
// 社区贡献奖励
Row(
children: [
const Text(
'社区贡献奖励',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: Color(0xCC5D4037),
),
),
const SizedBox(width: 39),
Expanded(
child: Text(
_communityBenefitActive
? '每新增认种 1 棵可获得 80 USDT'
: '需团队认种达到 $_communityInitialTarget 棵激活',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: _communityBenefitActive
? const Color(0xFFD4AF37)
: const Color(0x995D4037),
),
),
),
],
),
],
),
);
}
/// 构建通用权益考核组件
/// [title] 标题,如"市团队权益考核"
/// [hasAuth] 是否有该授权
/// [benefitActive] 权益是否激活
/// [currentTreeCount] 当前团队认种数量
/// [initialTarget] 初始考核目标
/// [monthlyTarget] 月度考核目标
/// [monthIndex] 当前考核月份
/// [rewardDescription] 奖励描述
Widget _buildRoleAssessment({
required String title,
required bool hasAuth,
required bool benefitActive,
required int currentTreeCount,
required int initialTarget,
required int monthlyTarget,
required int monthIndex,
required String rewardDescription,
}) {
// 如果没有该授权,不显示
if (!hasAuth) {
return const SizedBox.shrink();
}
// 计算进度百分比(用于进度条)
final target = benefitActive ? monthlyTarget : initialTarget;
final progressRatio = target > 0
? (currentTreeCount / target).clamp(0.0, 1.0)
: 0.0;
// 确定状态文字和颜色
final String statusText;
final Color statusColor;
if (benefitActive) {
statusText = '已激活';
statusColor = const Color(0xFF4CAF50); // 绿色
} else {
statusText = '待激活';
statusColor = const Color(0xFFFF9800); // 橙色
}
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0x80FFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题和状态
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: const TextStyle(
fontSize: 18,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.56,
color: Color(0xFF5D4037),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.15),
borderRadius: BorderRadius.circular(4),
),
child: Text(
statusText,
style: TextStyle(
fontSize: 12,
fontFamily: 'Inter',
fontWeight: FontWeight.w600,
color: statusColor,
),
),
),
],
),
const SizedBox(height: 15),
// 考核月份(仅在权益激活后显示)
if (benefitActive && monthIndex > 0) ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'当前考核月份',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: Color(0xCC5D4037),
),
),
Text(
'$monthIndex',
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.43,
color: Color(0xFF5D4037),
),
),
],
),
const SizedBox(height: 16),
],
// 团队认种数量 / 目标
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
benefitActive ? '团队认种 / 月度目标' : '团队认种 / 激活目标',
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: Color(0xCC5D4037),
),
),
Text(
'$currentTreeCount / $target',
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: Color(0xFF5D4037),
),
),
],
),
const SizedBox(height: 4),
// 进度条
Container(
height: 6,
decoration: BoxDecoration(
color: const Color(0x33D4AF37),
borderRadius: BorderRadius.circular(3),
),
child: FractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor: progressRatio,
child: Container(
decoration: BoxDecoration(
color: const Color(0xFFD4AF37),
borderRadius: BorderRadius.circular(3),
),
),
),
),
const SizedBox(height: 16),
// 贡献奖励
Row(
children: [
const Text(
'贡献奖励',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: Color(0xCC5D4037),
),
),
const SizedBox(width: 39),
Expanded(
child: Text(
benefitActive
? rewardDescription
: '需团队认种达到 $initialTarget 棵激活',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.43,
color: benefitActive
? const Color(0xFFD4AF37)
: const Color(0x995D4037),
),
),
),
],
),
],
),
);
}
/// 构建设置菜单
Widget _buildSettingsMenu() {
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0x80FFFFFF),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: Column(
children: [
_buildSettingItem(
icon: Icons.verified_user,
title: '谷歌验证器',
onTap: _goToGoogleAuth,
),
_buildSettingItem(
icon: Icons.lock,
title: '修改登录密码',
onTap: _goToChangePassword,
),
_buildSettingItem(
icon: Icons.email,
title: '绑定邮箱',
onTap: _goToBindEmail,
),
],
),
);
}
/// 构建设置项
Widget _buildSettingItem({
required IconData icon,
required String title,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Container(
height: 48,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
child: Row(
children: [
Icon(
icon,
size: 24,
color: const Color(0xFF8B5A2B),
),
const SizedBox(width: 16),
Expanded(
child: Text(
title,
style: const TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xFF5D4037),
),
),
),
const Icon(
Icons.chevron_right,
size: 24,
color: Color(0xFF8B5A2B),
),
],
),
),
);
}
/// 构建应用版本信息
Widget _buildAppVersionInfo() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFFFF5E6),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: const Color(0x33D4AF37),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题
const Row(
children: [
Icon(
Icons.info_outline,
size: 20,
color: Color(0xFF8B5A2B),
),
SizedBox(width: 8),
Text(
'应用信息',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w600,
height: 1.5,
color: Color(0xFF5D4037),
),
),
],
),
const SizedBox(height: 12),
// 版本信息列表
_buildVersionInfoRow('应用版本', 'v$_appVersion'),
const SizedBox(height: 8),
_buildVersionInfoRow('构建号', _buildNumber),
const SizedBox(height: 8),
_buildVersionInfoRow('包名', _packageName),
const Divider(
height: 24,
color: Color(0x33D4AF37),
),
// 设备信息
_buildVersionInfoRow('设备型号', _deviceModel),
const SizedBox(height: 8),
_buildVersionInfoRow('系统版本', _osVersion),
const SizedBox(height: 8),
_buildVersionInfoRow('平台', _platform),
const SizedBox(height: 12),
// 版权信息
Center(
child: Text(
'© ${DateTime.now().year} RWADurian. All rights reserved.',
style: const TextStyle(
fontSize: 11,
fontFamily: 'Inter',
height: 1.5,
color: Color(0x995D4037),
),
),
),
],
),
);
}
/// 构建版本信息行
Widget _buildVersionInfoRow(String label, String value) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
height: 1.5,
color: Color(0xCC5D4037),
),
),
Flexible(
child: Text(
value,
style: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.5,
color: Color(0xFF5D4037),
),
textAlign: TextAlign.right,
overflow: TextOverflow.ellipsis,
),
),
],
);
}
}