From 8ce4648ab0045b783b87695dc554e09b08904fea Mon Sep 17 00:00:00 2001 From: sion <450702724@qq.com> Date: Wed, 25 Mar 2026 09:23:24 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AE=8C=E6=95=B4=E8=BF=98=E5=8E=9F?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=20f69f05c=20=E7=9A=84=E6=89=80=E6=9C=89?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 还原 main_page.dart: 恢复首页Tab导航 - 还原 asset_page.dart: 恢复原始资产页面设计 - 撤销3月24日的页面重构,恢复应用正常功能 --- .../lib/ui/pages/asset/asset_page.dart | 633 ++++++++---------- 1 file changed, 267 insertions(+), 366 deletions(-) diff --git a/flutter_monisuo/lib/ui/pages/asset/asset_page.dart b/flutter_monisuo/lib/ui/pages/asset/asset_page.dart index 67a46be..082ca4b 100644 --- a/flutter_monisuo/lib/ui/pages/asset/asset_page.dart +++ b/flutter_monisuo/lib/ui/pages/asset/asset_page.dart @@ -11,7 +11,7 @@ import '../../components/glass_panel.dart'; import '../../components/neon_glow.dart'; import '../orders/fund_orders_page.dart'; -/// 资产页面 - 钱包风格 +/// 资产页面 - Material Design 3 风格 class AssetPage extends StatefulWidget { const AssetPage({super.key}); @@ -20,6 +20,8 @@ class AssetPage extends StatefulWidget { } class _AssetPageState extends State with AutomaticKeepAliveClientMixin { + int _activeTab = 0; + @override bool get wantKeepAlive => true; @@ -52,22 +54,16 @@ class _AssetPageState extends State with AutomaticKeepAliveClientMixi physics: const AlwaysScrollableScrollPhysics(), padding: AppSpacing.pagePadding, child: Column( - crossAxisAlignment: CrossAxisAlignment.start, children: [ - // 1. 总资产估值卡片 - _TotalAssetCard(overview: provider.overview), - SizedBox(height: AppSpacing.lg), - - // 2. 操作按钮 - _ActionButtons(provider: provider), - SizedBox(height: AppSpacing.lg), - - // 3. 资产组合 - _AssetComposition(provider: provider), - SizedBox(height: AppSpacing.lg), - - // 4. 代币列表 - _TokenList(holdings: provider.holdings), + _TabSelector( + tabs: const ['资金账户', '交易账户'], + selectedIndex: _activeTab, + onChanged: (index) => setState(() => _activeTab = index), + ), + SizedBox(height: AppSpacing.md), + _activeTab == 0 + ? _FundAccountCard(provider: provider) + : _TradeAccountCard(holdings: provider.holdings), ], ), ), @@ -78,11 +74,11 @@ class _AssetPageState extends State with AutomaticKeepAliveClientMixi } } -/// 总资产估值卡片 -class _TotalAssetCard extends StatelessWidget { +/// 资产总览卡片 - Material Design 3 风格 +class _AssetCard extends StatelessWidget { final dynamic overview; - const _TotalAssetCard({required this.overview}); + const _AssetCard({required this.overview}); @override Widget build(BuildContext context) { @@ -91,7 +87,7 @@ class _TotalAssetCard extends StatelessWidget { return Container( width: double.infinity, - padding: EdgeInsets.all(AppSpacing.xl), + padding: EdgeInsets.all(AppSpacing.lg + AppSpacing.sm), decoration: BoxDecoration( gradient: AppColorScheme.assetCardGradient, borderRadius: BorderRadius.circular(AppRadius.xl), @@ -103,71 +99,221 @@ class _TotalAssetCard extends StatelessWidget { ], ), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '总资产估值', + 'PORTFOLIO VALUE', style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - letterSpacing: 0.5, + fontSize: 10, + fontWeight: FontWeight.w700, + letterSpacing: 0.2, color: Colors.white.withOpacity(0.7), ), ), SizedBox(height: AppSpacing.sm), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - '\$${overview?.totalAsset ?? '0.00'}', - style: GoogleFonts.spaceGrotesk( - fontSize: 40, - fontWeight: FontWeight.bold, - color: Colors.white, + Text( + '\$${overview?.totalAsset ?? '0.00'}', + style: GoogleFonts.spaceGrotesk( + fontSize: 36, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + SizedBox(height: AppSpacing.md), + Container( + padding: EdgeInsets.symmetric( + horizontal: AppSpacing.md, + vertical: AppSpacing.xs + AppSpacing.xs, + ), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.1), + borderRadius: BorderRadius.circular(AppRadius.full), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + LucideIcons.trendingUp, + color: Colors.white.withOpacity(0.7), + size: 14, ), - ), - SizedBox(width: AppSpacing.sm), - Padding( - padding: EdgeInsets.only(bottom: 8), - child: Text( - 'USDT', + SizedBox(width: AppSpacing.xs), + Text( + '总盈亏: ${overview?.totalProfit ?? '0.00'} USDT', style: TextStyle( - fontSize: 14, + fontSize: 12, color: Colors.white.withOpacity(0.7), ), ), + ], + ), + ), + ], + ), + ); + } +} + +/// Tab 选择器 - Material Design 3 风格 +class _TabSelector extends StatelessWidget { + final List tabs; + final int selectedIndex; + final ValueChanged onChanged; + + const _TabSelector({ + required this.tabs, + required this.selectedIndex, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + final isDark = Theme.of(context).brightness == Brightness.dark; + + return Container( + padding: EdgeInsets.all(AppSpacing.xs), + decoration: BoxDecoration( + color: colorScheme.surfaceContainerHighest, + borderRadius: BorderRadius.circular(AppRadius.lg), + ), + child: Row( + children: tabs.asMap().entries.map((entry) { + final index = entry.key; + final label = entry.value; + final isSelected = index == selectedIndex; + + return Expanded( + child: GestureDetector( + onTap: () => onChanged(index), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: EdgeInsets.symmetric(vertical: AppSpacing.sm + AppSpacing.xs), + decoration: BoxDecoration( + color: isSelected ? colorScheme.primary : Colors.transparent, + borderRadius: BorderRadius.circular(AppRadius.md), + boxShadow: isSelected + ? [ + BoxShadow( + color: colorScheme.primary.withOpacity(isDark ? 0.15 : 0.08), + blurRadius: 10, + ), + ] + : null, + ), + child: Center( + child: Text( + label, + style: TextStyle( + color: isSelected ? colorScheme.onPrimary : colorScheme.onSurfaceVariant, + fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, + ), + ), + ), + ), + ), + ); + }).toList(), + ), + ); + } +} + +/// 资金账户卡片 - Glass Panel 风格 +class _FundAccountCard extends StatelessWidget { + final AssetProvider provider; + + const _FundAccountCard({required this.provider}); + + @override + Widget build(BuildContext context) { + final fund = provider.fundAccount; + final overview = provider.overview; + final colorScheme = Theme.of(context).colorScheme; + + // 优先使用fund数据,如果为null则使用overview的fundBalance + final displayBalance = fund?.balance ?? overview?.fundBalance ?? '0.00'; + + return GlassPanel( + padding: EdgeInsets.all(AppSpacing.lg + AppSpacing.xs), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'USDT 余额', + style: TextStyle( + fontSize: 12, + color: colorScheme.onSurfaceVariant, + ), + ), + GestureDetector( + onTap: () => Navigator.push( + context, + MaterialPageRoute(builder: (_) => const FundOrdersPage()), + ), + child: Row( + children: [ + Text( + '充提记录', + style: TextStyle( + color: colorScheme.primary, + fontSize: 12, + ), + ), + Icon( + LucideIcons.chevronRight, + size: 14, + color: colorScheme.primary, + ), + ], + ), ), ], ), - SizedBox(height: AppSpacing.md), + SizedBox(height: AppSpacing.sm), + Text( + displayBalance, + style: GoogleFonts.spaceGrotesk( + fontSize: 28, + fontWeight: FontWeight.bold, + color: colorScheme.onSurface, + ), + ), + SizedBox(height: AppSpacing.lg), Row( children: [ - Container( - padding: EdgeInsets.symmetric( - horizontal: AppSpacing.sm, - vertical: AppSpacing.xs, + Expanded( + child: NeonButton( + text: '充值', + type: NeonButtonType.tertiary, + icon: Icons.add, + onPressed: () => _showDepositDialog(context), + height: 44, + showGlow: false, ), - decoration: BoxDecoration( - color: Colors.white.withOpacity(0.15), - borderRadius: BorderRadius.circular(AppRadius.full), + ), + SizedBox(width: AppSpacing.sm), + Expanded( + child: NeonButton( + text: '提现', + type: NeonButtonType.secondary, + icon: Icons.remove, + onPressed: () => _showWithdrawDialog(context, fund?.balance), + height: 44, + showGlow: false, ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - LucideIcons.trendingUp, - color: Colors.white.withOpacity(0.9), - size: 12, - ), - SizedBox(width: AppSpacing.xs), - Text( - '今日收益: ${overview?.totalProfit ?? '0.00'} USDT', - style: TextStyle( - fontSize: 11, - color: Colors.white.withOpacity(0.9), - ), - ), - ], + ), + SizedBox(width: AppSpacing.sm), + Expanded( + child: NeonButton( + text: '划转', + type: NeonButtonType.outline, + icon: Icons.swap_horiz, + onPressed: () => _showTransferDialog(context), + height: 44, + showGlow: false, ), ), ], @@ -178,264 +324,46 @@ class _TotalAssetCard extends StatelessWidget { } } -/// 操作按钮 -class _ActionButtons extends StatelessWidget { - final AssetProvider provider; - - const _ActionButtons({required this.provider}); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - Expanded( - child: _ActionButton( - icon: LucideIcons.download, - label: '充币', - onTap: () => _showDepositDialog(context), - ), - ), - SizedBox(width: AppSpacing.sm), - Expanded( - child: _ActionButton( - icon: LucideIcons.upload, - label: '提币', - onTap: () => _showWithdrawDialog(context, provider.fundAccount?.balance), - ), - ), - SizedBox(width: AppSpacing.sm), - Expanded( - child: _ActionButton( - icon: LucideIcons.arrowLeftRight, - label: '划转', - onTap: () => _showTransferDialog(context), - ), - ), - ], - ); - } -} - -/// 操作按钮项 -class _ActionButton extends StatelessWidget { - final IconData icon; - final String label; - final VoidCallback onTap; - - const _ActionButton({ - required this.icon, - required this.label, - required this.onTap, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - - return GestureDetector( - onTap: onTap, - child: Container( - padding: EdgeInsets.symmetric(vertical: AppSpacing.md), - decoration: BoxDecoration( - color: colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(AppRadius.md), - border: Border.all( - color: colorScheme.outlineVariant.withOpacity(0.2), - ), - ), - child: Column( - children: [ - Icon( - icon, - color: colorScheme.primary, - size: 24, - ), - SizedBox(height: AppSpacing.xs), - Text( - label, - style: TextStyle( - fontSize: 12, - color: colorScheme.onSurface, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - ); - } -} - -/// 资产组合 -class _AssetComposition extends StatelessWidget { - final AssetProvider provider; - - const _AssetComposition({required this.provider}); - - @override - Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - final fund = provider.fundAccount; - final overview = provider.overview; - - final fundBalance = fund?.balance ?? overview?.fundBalance ?? '0.00'; - final tradeBalance = overview?.tradeBalance ?? '0'; - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '资产组合', - style: GoogleFonts.spaceGrotesk( - fontSize: 16, - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - SizedBox(height: AppSpacing.md), - Row( - children: [ - Expanded( - child: _AssetCard( - icon: LucideIcons.wallet, - title: '资金账户', - amount: '$fundBalance USDT', - onTap: () {}, - ), - ), - SizedBox(width: AppSpacing.sm), - Expanded( - child: _AssetCard( - icon: LucideIcons.trendingUp, - title: '交易账户', - amount: '$tradeBalance USDT', - onTap: () {}, - ), - ), - ], - ), - ], - ); - } -} - -/// 资产卡片 -class _AssetCard extends StatelessWidget { - final IconData icon; - final String title; - final String amount; - final VoidCallback onTap; - - const _AssetCard({ - required this.icon, - required this.title, - required this.amount, - required this.onTap, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - - return GestureDetector( - onTap: onTap, - child: Container( - padding: EdgeInsets.all(AppSpacing.md), - decoration: BoxDecoration( - color: colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(AppRadius.md), - border: Border.all( - color: colorScheme.outlineVariant.withOpacity(0.2), - ), - ), - child: Column( - children: [ - Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: colorScheme.primary.withOpacity(0.1), - shape: BoxShape.circle, - ), - child: Center( - child: Icon( - icon, - color: colorScheme.primary, - size: 20, - ), - ), - ), - SizedBox(height: AppSpacing.sm), - Text( - title, - style: TextStyle( - fontSize: 11, - color: colorScheme.onSurfaceVariant, - ), - ), - SizedBox(height: AppSpacing.xs), - Text( - amount, - style: GoogleFonts.spaceGrotesk( - fontSize: 14, - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ], - ), - ), - ); - } -} - -/// 资产列表 -class _TokenList extends StatelessWidget { +/// 交易账户卡片 - Glass Panel 风格 +class _TradeAccountCard extends StatelessWidget { final List holdings; - const _TokenList({required this.holdings}); + const _TradeAccountCard({required this.holdings}); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - // 对持仓进行排序:USDT 放在最上面 - final sortedHoldings = List.from(holdings); - sortedHoldings.sort((a, b) { - final codeA = (a.coinCode ?? a['coinCode'] ?? '').toString().toUpperCase(); - final codeB = (b.coinCode ?? b['coinCode'] ?? '').toString().toUpperCase(); - // USDT 排在最前面 - if (codeA == 'USDT') return -1; - if (codeB == 'USDT') return 1; - return 0; - }); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '资产列表', - style: GoogleFonts.spaceGrotesk( - fontSize: 16, - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - SizedBox(height: AppSpacing.md), - if (sortedHoldings.isEmpty) - _EmptyState(icon: LucideIcons.wallet, message: '暂无持仓') - else - ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: sortedHoldings.length, - separatorBuilder: (_, __) => Divider( - height: 1, - color: colorScheme.outlineVariant.withOpacity(0.2), + return GlassPanel( + padding: AppSpacing.cardPadding, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '持仓列表', + style: GoogleFonts.spaceGrotesk( + fontSize: 16, + fontWeight: FontWeight.bold, + color: colorScheme.onSurface, ), - itemBuilder: (context, index) => _TokenItem(holding: sortedHoldings[index]), ), - ], + SizedBox(height: AppSpacing.md), + if (holdings.isEmpty) + const _EmptyState(icon: LucideIcons.wallet, message: '暂无持仓') + else + ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: holdings.length, + separatorBuilder: (_, __) => Container( + margin: EdgeInsets.only(left: 56), + height: 1, + color: AppColorScheme.glassPanelBorder, + ), + itemBuilder: (context, index) => _HoldingItem(holding: holdings[index]), + ), + ], + ), ); } } @@ -473,11 +401,11 @@ class _EmptyState extends StatelessWidget { } } -/// 代币项 -class _TokenItem extends StatelessWidget { +/// 持仓项 +class _HoldingItem extends StatelessWidget { final dynamic holding; - const _TokenItem({required this.holding}); + const _HoldingItem({required this.holding}); @override Widget build(BuildContext context) { @@ -485,12 +413,12 @@ class _TokenItem extends StatelessWidget { final isDark = Theme.of(context).brightness == Brightness.dark; return Padding( - padding: EdgeInsets.symmetric(vertical: AppSpacing.md), + padding: EdgeInsets.symmetric(vertical: AppSpacing.sm), child: Row( children: [ Container( - width: 44, - height: 44, + width: 40, + height: 40, decoration: BoxDecoration( color: colorScheme.primary.withOpacity(0.1), borderRadius: BorderRadius.circular(AppRadius.md), @@ -501,50 +429,25 @@ class _TokenItem extends StatelessWidget { style: TextStyle( color: colorScheme.primary, fontWeight: FontWeight.bold, - fontSize: 18, ), ), ), ), - SizedBox(width: AppSpacing.md), + SizedBox(width: AppSpacing.sm + AppSpacing.xs), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Text( - holding.coinCode, - style: GoogleFonts.spaceGrotesk( - fontSize: 15, - fontWeight: FontWeight.w600, - color: colorScheme.onSurface, - ), - ), - SizedBox(width: AppSpacing.sm), - Container( - padding: EdgeInsets.symmetric( - horizontal: AppSpacing.xs, - vertical: 2, - ), - decoration: BoxDecoration( - color: AppColorScheme.getUpColor(isDark).withOpacity(0.1), - borderRadius: BorderRadius.circular(AppRadius.sm), - ), - child: Text( - '+5.2%', - style: TextStyle( - fontSize: 10, - color: AppColorScheme.getUpColor(isDark), - fontWeight: FontWeight.w600, - ), - ), - ), - ], - ), - SizedBox(height: AppSpacing.xs), Text( - holding.quantity, + holding.coinCode, + style: GoogleFonts.spaceGrotesk( + fontSize: 14, + fontWeight: FontWeight.w600, + color: colorScheme.onSurface, + ), + ), + Text( + '数量: ${holding.quantity}', style: TextStyle( fontSize: 12, color: colorScheme.onSurfaceVariant, @@ -558,13 +461,11 @@ class _TokenItem extends StatelessWidget { children: [ Text( '${holding.currentValue} USDT', - style: GoogleFonts.spaceGrotesk( - fontSize: 15, - fontWeight: FontWeight.w600, + style: TextStyle( + fontSize: 12, color: colorScheme.onSurface, ), ), - SizedBox(height: AppSpacing.xs), Text( holding.formattedProfitRate, style: TextStyle( @@ -608,7 +509,7 @@ void _showDepositDialog(BuildContext context) { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '充币', + 'Deposit (充值)', style: GoogleFonts.spaceGrotesk( fontSize: 24, fontWeight: FontWeight.bold, @@ -949,7 +850,7 @@ void _showWithdrawDialog(BuildContext context, String? balance) { ), SizedBox(width: AppSpacing.sm), Text( - '提币', + '提现 (Withdraw)', style: GoogleFonts.spaceGrotesk( fontSize: 20, fontWeight: FontWeight.bold, @@ -1011,8 +912,8 @@ void _showWithdrawDialog(BuildContext context, String? balance) { ShadInputFormField( id: 'amount', controller: amountController, - label: const Text('提币金额'), - placeholder: const Text('请输入提币金额(USDT)'), + label: const Text('提现金额'), + placeholder: const Text('请输入提现金额(USDT)'), keyboardType: const TextInputType.numberWithOptions(decimal: true), validator: Validators.amount, ), @@ -1021,8 +922,8 @@ void _showWithdrawDialog(BuildContext context, String? balance) { id: 'address', controller: addressController, label: const Text('目标地址'), - placeholder: const Text('请输入提币地址'), - validator: (v) => Validators.required(v, '提币地址'), + placeholder: const Text('请输入提现地址'), + validator: (v) => Validators.required(v, '提现地址'), ), SizedBox(height: AppSpacing.md), ShadInputFormField(