feat(ui): 将待领取、可结算、已过期列表改为堆叠卡片展示

- 恢复待领取列表的堆叠卡片展示(StackedCardsView)
- 将可结算明细列表改为堆叠卡片展示
- 将已过期明细列表改为堆叠卡片展示
- 新增 _buildStackedSettleableRewardCard 方法
- 新增 _buildStackedExpiredRewardCard 方法
- 添加各列表的条目数量显示

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-17 00:13:43 -08:00
parent cceae5452a
commit 1dfa02b386
1 changed files with 411 additions and 162 deletions

View File

@ -18,8 +18,7 @@ import '../../../../routes/route_paths.dart';
import '../../../../routes/app_router.dart'; import '../../../../routes/app_router.dart';
import '../../../auth/presentation/providers/auth_provider.dart'; import '../../../auth/presentation/providers/auth_provider.dart';
import '../widgets/team_tree_widget.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<ProfilePage> {
], ],
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
// TODO: //
// StackedCardsView<PendingRewardItem>( StackedCardsView<PendingRewardItem>(
// items: _pendingRewards, items: _pendingRewards,
// peekHeight: 28, peekHeight: 28,
// expandedCardHeight: 110, expandedCardHeight: 110,
// enableSound: true, enableSound: true,
// itemBuilder: (item, isSelected, index) => _buildStackedPendingRewardCard(item, isSelected), itemBuilder: (item, isSelected, index) => _buildStackedPendingRewardCard(item, isSelected),
// ), ),
//
..._pendingRewards.map((item) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: _buildPendingRewardItem(item),
)),
], ],
], ],
); );
} }
// TODO: ///
// /// Widget _buildStackedPendingRewardCard(PendingRewardItem item, bool isSelected) {
// Widget _buildStackedPendingRewardCard(PendingRewardItem item, bool isSelected) { //
// // final remainingSeconds = item.remainingSeconds > 0 ? item.remainingSeconds : 0;
// final remainingSeconds = item.remainingSeconds > 0 ? item.remainingSeconds : 0; final hours = (remainingSeconds ~/ 3600).toString().padLeft(2, '0');
// final hours = (remainingSeconds ~/ 3600).toString().padLeft(2, '0'); final minutes = ((remainingSeconds % 3600) ~/ 60).toString().padLeft(2, '0');
// final minutes = ((remainingSeconds % 3600) ~/ 60).toString().padLeft(2, '0'); final seconds = (remainingSeconds % 60).toString().padLeft(2, '0');
// final seconds = (remainingSeconds % 60).toString().padLeft(2, '0'); final countdown = '$hours:$minutes:$seconds';
// final countdown = '$hours:$minutes:$seconds';
// //
// // final List<String> amountParts = [];
// final List<String> amountParts = []; if (item.usdtAmount > 0) {
// if (item.usdtAmount > 0) { amountParts.add('${_formatNumber(item.usdtAmount)} 绿积分');
// amountParts.add('${_formatNumber(item.usdtAmount)} 绿积分'); }
// } if (item.hashpowerAmount > 0) {
// if (item.hashpowerAmount > 0) { amountParts.add('${_formatNumber(item.hashpowerAmount)} 算力');
// amountParts.add('${_formatNumber(item.hashpowerAmount)} 算力'); }
// } final amountText = amountParts.isNotEmpty ? amountParts.join(' ') : '0 绿积分';
// final amountText = amountParts.isNotEmpty ? amountParts.join(' ') : '0 绿积分';
// return Container(
// return Container( height: isSelected ? 110 : 48,
// height: isSelected ? 110 : 48, decoration: BoxDecoration(
// decoration: BoxDecoration( color: isSelected ? Colors.white : const Color(0xFFFFFDF8),
// color: isSelected ? Colors.white : const Color(0xFFFFFDF8), borderRadius: BorderRadius.circular(8),
// borderRadius: BorderRadius.circular(8), border: Border.all(
// border: Border.all( color: isSelected ? const Color(0x44D4AF37) : const Color(0x22D4AF37),
// color: isSelected ? const Color(0x44D4AF37) : const Color(0x22D4AF37), width: isSelected ? 1.5 : 1,
// width: isSelected ? 1.5 : 1, ),
// ), ),
// ), child: isSelected
// child: isSelected ? Padding(
// ? Padding( padding: const EdgeInsets.all(12),
// padding: const EdgeInsets.all(12), child: Column(
// child: Column( crossAxisAlignment: CrossAxisAlignment.start,
// crossAxisAlignment: CrossAxisAlignment.start, children: [
// children: [ // +
// // + Row(
// Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
// mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
// children: [ Text(
// Text( item.rightTypeName,
// item.rightTypeName, style: const TextStyle(
// style: const TextStyle( fontSize: 14,
// fontSize: 14, fontFamily: 'Inter',
// fontFamily: 'Inter', fontWeight: FontWeight.w600,
// fontWeight: FontWeight.w600, color: Color(0xFF5D4037),
// color: Color(0xFF5D4037), ),
// ), ),
// ), Row(
// Row( children: [
// children: [ const Icon(
// const Icon( Icons.timer_outlined,
// Icons.timer_outlined, size: 14,
// size: 14, color: Color(0xFFD4AF37),
// color: Color(0xFFD4AF37), ),
// ), const SizedBox(width: 4),
// const SizedBox(width: 4), Text(
// Text( countdown,
// countdown, style: const TextStyle(
// style: const TextStyle( fontSize: 12,
// fontSize: 12, fontFamily: 'Consolas',
// fontFamily: 'Consolas', fontWeight: FontWeight.w600,
// fontWeight: FontWeight.w600, color: Color(0xFFD4AF37),
// color: Color(0xFFD4AF37), ),
// ), ),
// ), ],
// ], ),
// ), ],
// ], ),
// ), const SizedBox(height: 8),
// const SizedBox(height: 8), //
// // Text(
// Text( amountText,
// amountText, style: const TextStyle(
// style: const TextStyle( fontSize: 16,
// fontSize: 16, fontFamily: 'Inter',
// fontFamily: 'Inter', fontWeight: FontWeight.w700,
// fontWeight: FontWeight.w700, color: Color(0xFF5D4037),
// color: Color(0xFF5D4037), ),
// ), ),
// ), //
// // if (item.memo.isNotEmpty) ...[
// if (item.memo.isNotEmpty) ...[ const SizedBox(height: 4),
// const SizedBox(height: 4), Text(
// Text( item.memo,
// item.memo, style: const TextStyle(
// style: const TextStyle( fontSize: 11,
// fontSize: 11, fontFamily: 'Inter',
// fontFamily: 'Inter', color: Color(0x995D4037),
// color: Color(0x995D4037), ),
// ), maxLines: 2,
// maxLines: 2, overflow: TextOverflow.ellipsis,
// overflow: TextOverflow.ellipsis, ),
// ), ],
// ], ],
// ], ),
// ), )
// ) : Padding(
// : Padding( //
// // padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
// padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row(
// child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
// mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
// children: [ Text(
// Text( item.rightTypeName,
// item.rightTypeName, style: const TextStyle(
// style: const TextStyle( fontSize: 13,
// fontSize: 13, fontFamily: 'Inter',
// fontFamily: 'Inter', fontWeight: FontWeight.w500,
// fontWeight: FontWeight.w500, color: Color(0xFF5D4037),
// color: Color(0xFF5D4037), ),
// ), ),
// ), Text(
// Text( amountText,
// amountText, style: const TextStyle(
// style: const TextStyle( fontSize: 13,
// fontSize: 13, fontFamily: 'Inter',
// fontFamily: 'Inter', fontWeight: FontWeight.w600,
// fontWeight: FontWeight.w600, color: Color(0xFFD4AF37),
// color: Color(0xFFD4AF37), ),
// ), ),
// ), ],
// ], ),
// ), ),
// ), );
// ); }
// }
/// ///
Widget _buildPendingRewardItem(PendingRewardItem item) { Widget _buildPendingRewardItem(PendingRewardItem item) {
@ -2133,18 +2126,37 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
const SizedBox(height: 16), const SizedBox(height: 16),
const Divider(color: Color(0x33D4AF37), height: 1), const Divider(color: Color(0x33D4AF37), height: 1),
const SizedBox(height: 12), const SizedBox(height: 12),
const Text( Row(
'可结算明细', mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: TextStyle( children: [
fontSize: 14, const Text(
fontFamily: 'Inter', '可结算明细',
fontWeight: FontWeight.w600, style: TextStyle(
color: Color(0xFF5D4037), 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), const SizedBox(height: 8),
// //
...(_settleableRewards.map((item) => _buildSettleableRewardItem(item))), StackedCardsView<SettleableRewardItem>(
items: _settleableRewards,
peekHeight: 28,
expandedCardHeight: 90,
enableSound: true,
itemBuilder: (item, isSelected, index) => _buildStackedSettleableRewardCard(item, isSelected),
),
], ],
const SizedBox(height: 11), const SizedBox(height: 11),
// USDT // USDT
@ -2279,6 +2291,115 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
); );
} }
///
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<String> 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() { Widget _buildExpiredSection() {
return SizedBox( return SizedBox(
@ -2365,18 +2486,37 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
const SizedBox(height: 16), const SizedBox(height: 16),
const Divider(color: Color(0x33D4AF37), height: 1), const Divider(color: Color(0x33D4AF37), height: 1),
const SizedBox(height: 12), const SizedBox(height: 12),
const Text( Row(
'已过期明细', mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: TextStyle( children: [
fontSize: 14, const Text(
fontFamily: 'Inter', '已过期明细',
fontWeight: FontWeight.w600, style: TextStyle(
color: Color(0xFF5D4037), 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), const SizedBox(height: 8),
// //
...(_expiredRewards.map((item) => _buildExpiredRewardItem(item))), StackedCardsView<ExpiredRewardItem>(
items: _expiredRewards,
peekHeight: 28,
expandedCardHeight: 90,
enableSound: true,
itemBuilder: (item, isSelected, index) => _buildStackedExpiredRewardCard(item, isSelected),
),
], ],
], ],
), ),
@ -2463,6 +2603,115 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
); );
} }
///
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<String> 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() { Widget _buildActionButtons() {
return Column( return Column(