diff --git a/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart b/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart index a165ebf4..a1eeeecc 100644 --- a/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart +++ b/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart @@ -18,8 +18,7 @@ import '../../../../routes/route_paths.dart'; import '../../../../routes/app_router.dart'; import '../../../auth/presentation/providers/auth_provider.dart'; import '../widgets/team_tree_widget.dart'; -// TODO: 暂时禁用堆叠卡片组件,测试华为恶意软件检测问题 -// import '../widgets/stacked_cards_widget.dart'; +import '../widgets/stacked_cards_widget.dart'; /// 个人中心页面 - 显示用户信息、社区数据、收益和设置 /// 包含用户资料、推荐信息、社区考核、收益领取等功能 @@ -1843,151 +1842,145 @@ class _ProfilePageState extends ConsumerState { ], ), const SizedBox(height: 8), - // TODO: 暂时禁用堆叠卡片展示,测试华为恶意软件检测问题 - // StackedCardsView( - // items: _pendingRewards, - // peekHeight: 28, - // expandedCardHeight: 110, - // enableSound: true, - // itemBuilder: (item, isSelected, index) => _buildStackedPendingRewardCard(item, isSelected), - // ), - // 恢复原始列表展示 - ..._pendingRewards.map((item) => Padding( - padding: const EdgeInsets.only(bottom: 8), - child: _buildPendingRewardItem(item), - )), + // 堆叠卡片展示 + StackedCardsView( + items: _pendingRewards, + peekHeight: 28, + expandedCardHeight: 110, + enableSound: true, + itemBuilder: (item, isSelected, index) => _buildStackedPendingRewardCard(item, isSelected), + ), ], ], ); } - // TODO: 暂时禁用堆叠卡片组件,测试华为恶意软件检测问题 - // /// 构建堆叠卡片样式的待领取奖励项 - // Widget _buildStackedPendingRewardCard(PendingRewardItem item, bool isSelected) { - // // 确保剩余时间不为负数 - // 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 amountParts = []; - // if (item.usdtAmount > 0) { - // amountParts.add('${_formatNumber(item.usdtAmount)} 绿积分'); - // } - // if (item.hashpowerAmount > 0) { - // amountParts.add('${_formatNumber(item.hashpowerAmount)} 算力'); - // } - // final amountText = amountParts.isNotEmpty ? amountParts.join(' ') : '0 绿积分'; - // - // return Container( - // height: isSelected ? 110 : 48, - // decoration: BoxDecoration( - // color: isSelected ? Colors.white : const Color(0xFFFFFDF8), - // borderRadius: BorderRadius.circular(8), - // border: Border.all( - // color: isSelected ? const Color(0x44D4AF37) : const Color(0x22D4AF37), - // width: isSelected ? 1.5 : 1, - // ), - // ), - // child: isSelected - // ? Padding( - // padding: const EdgeInsets.all(12), - // 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, - // ), - // ], - // ], - // ), - // ) - // : Padding( - // // 未选中状态:只显示卡片头部预览 - // padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - // child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Text( - // item.rightTypeName, - // style: const TextStyle( - // fontSize: 13, - // fontFamily: 'Inter', - // fontWeight: FontWeight.w500, - // color: Color(0xFF5D4037), - // ), - // ), - // Text( - // amountText, - // style: const TextStyle( - // fontSize: 13, - // fontFamily: 'Inter', - // fontWeight: FontWeight.w600, - // color: Color(0xFFD4AF37), - // ), - // ), - // ], - // ), - // ), - // ); - // } + /// 构建堆叠卡片样式的待领取奖励项 + Widget _buildStackedPendingRewardCard(PendingRewardItem item, bool isSelected) { + // 确保剩余时间不为负数 + 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 amountParts = []; + if (item.usdtAmount > 0) { + amountParts.add('${_formatNumber(item.usdtAmount)} 绿积分'); + } + if (item.hashpowerAmount > 0) { + amountParts.add('${_formatNumber(item.hashpowerAmount)} 算力'); + } + final amountText = amountParts.isNotEmpty ? amountParts.join(' ') : '0 绿积分'; + + return Container( + height: isSelected ? 110 : 48, + decoration: BoxDecoration( + color: isSelected ? Colors.white : const Color(0xFFFFFDF8), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: isSelected ? const Color(0x44D4AF37) : const Color(0x22D4AF37), + width: isSelected ? 1.5 : 1, + ), + ), + child: isSelected + ? Padding( + padding: const EdgeInsets.all(12), + 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, + ), + ], + ], + ), + ) + : Padding( + // 未选中状态:只显示卡片头部预览 + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + item.rightTypeName, + style: const TextStyle( + fontSize: 13, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + color: Color(0xFF5D4037), + ), + ), + Text( + amountText, + style: const TextStyle( + fontSize: 13, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + color: Color(0xFFD4AF37), + ), + ), + ], + ), + ), + ); + } /// 构建单条待领取奖励项 Widget _buildPendingRewardItem(PendingRewardItem item) { @@ -2133,18 +2126,37 @@ class _ProfilePageState extends ConsumerState { 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), - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + '可结算明细', + style: TextStyle( + fontSize: 14, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + color: Color(0xFF5D4037), + ), + ), + Text( + '共 ${_settleableRewards.length} 笔', + style: const TextStyle( + fontSize: 12, + fontFamily: 'Inter', + color: Color(0xFF8B5A2B), + ), + ), + ], ), const SizedBox(height: 8), - // 奖励条目列表 - ...(_settleableRewards.map((item) => _buildSettleableRewardItem(item))), + // 堆叠卡片展示 + StackedCardsView( + items: _settleableRewards, + peekHeight: 28, + expandedCardHeight: 90, + enableSound: true, + itemBuilder: (item, isSelected, index) => _buildStackedSettleableRewardCard(item, isSelected), + ), ], const SizedBox(height: 11), // 已结算 USDT @@ -2279,6 +2291,115 @@ class _ProfilePageState extends ConsumerState { ); } + /// 构建堆叠卡片样式的可结算奖励项 + Widget _buildStackedSettleableRewardCard(SettleableRewardItem item, bool isSelected) { + // 格式化结算时间 + final settledDate = '${item.settledAt.month}/${item.settledAt.day} ${item.settledAt.hour.toString().padLeft(2, '0')}:${item.settledAt.minute.toString().padLeft(2, '0')}'; + + // 构建金额显示文本 + final List amountParts = []; + if (item.usdtAmount > 0) { + amountParts.add('${_formatNumber(item.usdtAmount)} 绿积分'); + } + if (item.hashpowerAmount > 0) { + amountParts.add('${_formatNumber(item.hashpowerAmount)} 算力'); + } + final amountText = amountParts.isNotEmpty ? amountParts.join(' ') : '0 绿积分'; + + return Container( + height: isSelected ? 90 : 48, + decoration: BoxDecoration( + color: isSelected ? Colors.white : const Color(0xFFFFFDF8), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: isSelected ? const Color(0x44D4AF37) : const Color(0x22D4AF37), + width: isSelected ? 1.5 : 1, + ), + ), + child: isSelected + ? Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 第一行:权益类型 + 结算时间 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + item.allocationTypeName, + style: const TextStyle( + fontSize: 14, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + color: Color(0xFF5D4037), + ), + ), + Row( + children: [ + const Icon( + Icons.check_circle_outline, + size: 14, + color: Color(0xFF4CAF50), + ), + const SizedBox(width: 4), + Text( + settledDate, + style: const TextStyle( + fontSize: 12, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + color: Color(0xFF4CAF50), + ), + ), + ], + ), + ], + ), + const SizedBox(height: 8), + // 第二行:金额信息 + Text( + amountText, + style: const TextStyle( + fontSize: 16, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + color: Color(0xFF5D4037), + ), + ), + ], + ), + ) + : Padding( + // 未选中状态:只显示卡片头部预览 + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + item.allocationTypeName, + style: const TextStyle( + fontSize: 13, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + color: Color(0xFF5D4037), + ), + ), + Text( + amountText, + style: const TextStyle( + fontSize: 13, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + color: Color(0xFF4CAF50), + ), + ), + ], + ), + ), + ); + } + /// 构建已过期区域 Widget _buildExpiredSection() { return SizedBox( @@ -2365,18 +2486,37 @@ class _ProfilePageState extends ConsumerState { 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), - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + '已过期明细', + style: TextStyle( + fontSize: 14, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + color: Color(0xFF5D4037), + ), + ), + Text( + '共 ${_expiredRewards.length} 笔', + style: const TextStyle( + fontSize: 12, + fontFamily: 'Inter', + color: Color(0xFF8B5A2B), + ), + ), + ], ), const SizedBox(height: 8), - // 奖励条目列表 - ...(_expiredRewards.map((item) => _buildExpiredRewardItem(item))), + // 堆叠卡片展示 + StackedCardsView( + items: _expiredRewards, + peekHeight: 28, + expandedCardHeight: 90, + enableSound: true, + itemBuilder: (item, isSelected, index) => _buildStackedExpiredRewardCard(item, isSelected), + ), ], ], ), @@ -2463,6 +2603,115 @@ class _ProfilePageState extends ConsumerState { ); } + /// 构建堆叠卡片样式的已过期奖励项 + Widget _buildStackedExpiredRewardCard(ExpiredRewardItem item, bool isSelected) { + // 格式化过期时间 + final expiredDate = '${item.expiredAt.month}/${item.expiredAt.day} ${item.expiredAt.hour.toString().padLeft(2, '0')}:${item.expiredAt.minute.toString().padLeft(2, '0')}'; + + // 构建金额显示文本 + final List amountParts = []; + if (item.usdtAmount > 0) { + amountParts.add('${_formatNumber(item.usdtAmount)} 绿积分'); + } + if (item.hashpowerAmount > 0) { + amountParts.add('${_formatNumber(item.hashpowerAmount)} 算力'); + } + final amountText = amountParts.isNotEmpty ? amountParts.join(' ') : '0 绿积分'; + + return Container( + height: isSelected ? 90 : 48, + decoration: BoxDecoration( + color: isSelected ? const Color(0xFFF5F5F5) : const Color(0xFFFAFAFA), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: isSelected ? const Color(0x44999999) : const Color(0x22999999), + width: isSelected ? 1.5 : 1, + ), + ), + child: isSelected + ? Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 第一行:权益类型 + 过期时间 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + item.allocationTypeName, + style: const TextStyle( + fontSize: 14, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + color: Color(0xFF999999), + ), + ), + Row( + children: [ + const Icon( + Icons.cancel_outlined, + size: 14, + color: Color(0xFFE57373), + ), + const SizedBox(width: 4), + Text( + expiredDate, + style: const TextStyle( + fontSize: 12, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + color: Color(0xFFE57373), + ), + ), + ], + ), + ], + ), + const SizedBox(height: 8), + // 第二行:金额信息 + Text( + amountText, + style: const TextStyle( + fontSize: 16, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + color: Color(0xFF999999), + ), + ), + ], + ), + ) + : Padding( + // 未选中状态:只显示卡片头部预览 + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + item.allocationTypeName, + style: const TextStyle( + fontSize: 13, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + color: Color(0xFF999999), + ), + ), + Text( + amountText, + style: const TextStyle( + fontSize: 13, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + color: Color(0xFFE57373), + ), + ), + ], + ), + ), + ); + } + /// 构建操作按钮 Widget _buildActionButtons() { return Column(