diff --git a/flutter_monisuo/build/99111e0c5b6228829e100ef67db14ea2.cache.dill.track.dill b/flutter_monisuo/build/99111e0c5b6228829e100ef67db14ea2.cache.dill.track.dill new file mode 100644 index 0000000..e5cafcb Binary files /dev/null and b/flutter_monisuo/build/99111e0c5b6228829e100ef67db14ea2.cache.dill.track.dill differ diff --git a/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/.filecache b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/.filecache new file mode 100644 index 0000000..891131a --- /dev/null +++ b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/.filecache @@ -0,0 +1 @@ +{"version":2,"files":[{"path":"D:\\flutter\\bin\\cache\\dart-sdk\\version","hash":"800169ad7335b889bf428af171476466"},{"path":"D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\.dart_tool\\package_config.json","hash":"d6b4a7aa67aeb750be9e5aec884f1f73"},{"path":"D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\pubspec.yaml","hash":"03c567345af5a72ca098cfa0a67b3423"},{"path":"D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\build\\9a5d09bec60a9bd952a3f584c1b9bd3b\\dart_build_result.json","hash":"932fae7a247d7a3fd85340e755adb05b"},{"path":"D:\\flutter\\packages\\flutter_tools\\lib\\src\\build_system\\targets\\native_assets.dart","hash":"f78c405bcece3968277b212042da9ed6"},{"path":"d:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\.dart_tool\\package_config.json","hash":"d6b4a7aa67aeb750be9e5aec884f1f73"}]} \ No newline at end of file diff --git a/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/_composite.stamp b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/_composite.stamp new file mode 100644 index 0000000..1b2d28c --- /dev/null +++ b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/_composite.stamp @@ -0,0 +1 @@ +{"inputs":[],"outputs":[]} \ No newline at end of file diff --git a/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/dart_build.d b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/dart_build.d new file mode 100644 index 0000000..cd94f18 --- /dev/null +++ b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/dart_build.d @@ -0,0 +1 @@ + D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\build\\9a5d09bec60a9bd952a3f584c1b9bd3b\\dart_build_result.json: D:\\flutter\\bin\\cache\\dart-sdk\\version D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\.dart_tool\\package_config.json D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\pubspec.yaml d:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\.dart_tool\\package_config.json \ No newline at end of file diff --git a/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/dart_build.stamp b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/dart_build.stamp new file mode 100644 index 0000000..db85c13 --- /dev/null +++ b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/dart_build.stamp @@ -0,0 +1 @@ +{"inputs":["D:\\flutter\\packages\\flutter_tools\\lib\\src\\build_system\\targets\\native_assets.dart","D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\.dart_tool\\package_config.json","D:\\flutter\\bin\\cache\\dart-sdk\\version","D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\.dart_tool\\package_config.json","D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\pubspec.yaml","d:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\.dart_tool\\package_config.json"],"outputs":["D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\build\\9a5d09bec60a9bd952a3f584c1b9bd3b\\dart_build_result.json","D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\build\\9a5d09bec60a9bd952a3f584c1b9bd3b\\dart_build_result.json"]} \ No newline at end of file diff --git a/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/dart_build_result.json b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/dart_build_result.json new file mode 100644 index 0000000..4464929 --- /dev/null +++ b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/dart_build_result.json @@ -0,0 +1 @@ +{"build_start":"2026-04-06T01:07:05.140833","build_end":"2026-04-06T01:07:05.218007","dependencies":["file:///D:/flutter/bin/cache/dart-sdk/version","file:///D:/workspace/project/com-rattan-spccloud/flutter_monisuo/.dart_tool/package_config.json","file:///D:/workspace/project/com-rattan-spccloud/flutter_monisuo/pubspec.yaml","file:///d:/workspace/project/com-rattan-spccloud/flutter_monisuo/.dart_tool/package_config.json"],"code_assets":[],"data_assets":[]} \ No newline at end of file diff --git a/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/gen_dart_plugin_registrant.stamp b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/gen_dart_plugin_registrant.stamp new file mode 100644 index 0000000..1b2d28c --- /dev/null +++ b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/gen_dart_plugin_registrant.stamp @@ -0,0 +1 @@ +{"inputs":[],"outputs":[]} \ No newline at end of file diff --git a/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/gen_localizations.stamp b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/gen_localizations.stamp new file mode 100644 index 0000000..1b2d28c --- /dev/null +++ b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/gen_localizations.stamp @@ -0,0 +1 @@ +{"inputs":[],"outputs":[]} \ No newline at end of file diff --git a/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/outputs.json b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/outputs.json new file mode 100644 index 0000000..52723e7 --- /dev/null +++ b/flutter_monisuo/build/9a5d09bec60a9bd952a3f584c1b9bd3b/outputs.json @@ -0,0 +1 @@ +["D:\\workspace\\project\\com-rattan-spccloud\\flutter_monisuo\\build\\9a5d09bec60a9bd952a3f584c1b9bd3b\\dart_build_result.json"] \ No newline at end of file diff --git a/flutter_monisuo/lib/ui/pages/asset/asset_page.dart b/flutter_monisuo/lib/ui/pages/asset/asset_page.dart index b48914d..e12311d 100644 --- a/flutter_monisuo/lib/ui/pages/asset/asset_page.dart +++ b/flutter_monisuo/lib/ui/pages/asset/asset_page.dart @@ -5,7 +5,6 @@ import '../../../core/theme/app_spacing.dart'; import '../../../core/theme/app_theme.dart'; import '../../../core/event/app_event_bus.dart'; import '../../../providers/asset_provider.dart'; -import 'components/account_tab_switcher.dart'; import 'components/action_buttons_row.dart'; import 'components/asset_dialogs.dart'; import 'components/balance_card.dart'; @@ -23,7 +22,6 @@ class AssetPage extends StatefulWidget { } class _AssetPageState extends State with AutomaticKeepAliveClientMixin { - int _activeTab = 0; StreamSubscription? _eventSub; @override @@ -76,7 +74,7 @@ class _AssetPageState extends State with AutomaticKeepAliveClientMixi child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Page title: "资产" 22px bold — matching .pen titleFrame padding [16,0,8,0] + // Page title Padding( padding: const EdgeInsets.only(top: 16, bottom: 8), child: Text( @@ -85,16 +83,25 @@ class _AssetPageState extends State with AutomaticKeepAliveClientMixi ), ), const SizedBox(height: AppSpacing.sm), - // Account tab switcher — pill-style matching .pen UE6xC - AccountTabSwitcher( - selectedIndex: _activeTab, - onChanged: (index) => setState(() => _activeTab = index), - ), - const SizedBox(height: AppSpacing.md), - // Balance card — matching .pen 59637 (cornerRadius lg, stroke, padding 20, gap 12) - BalanceCard( - provider: provider, - activeTab: _activeTab, + // 资金账户 + 交易账户 左右并排 + Row( + children: [ + Expanded( + child: BalanceCard( + provider: provider, + label: '资金账户', + balance: provider.fundAccount?.balance ?? provider.overview?.fundBalance ?? '0.00', + ), + ), + const SizedBox(width: AppSpacing.sm), + Expanded( + child: BalanceCard( + provider: provider, + label: '交易账户', + balance: _calculateTradeTotal(provider), + ), + ), + ], ), const SizedBox(height: AppSpacing.md), // Action buttons row — matching .pen pIpHe (gap 12) @@ -112,8 +119,8 @@ class _AssetPageState extends State with AutomaticKeepAliveClientMixi ), ), const SizedBox(height: AppSpacing.md), - // Holdings section — matching .pen th9BG + 6X6tC - HoldingsSection(holdings: _activeTab == 1 ? provider.holdings : []), + // Holdings section + HoldingsSection(holdings: provider.holdings), ], ), ), @@ -123,6 +130,14 @@ class _AssetPageState extends State with AutomaticKeepAliveClientMixi ); } + String _calculateTradeTotal(AssetProvider provider) { + double total = 0; + for (var h in provider.holdings) { + total += double.tryParse(h.currentValue?.toString() ?? '0') ?? 0; + } + return total.toStringAsFixed(2); + } + void _navigateToTransfer(BuildContext context) async { final result = await Navigator.push( context, diff --git a/flutter_monisuo/lib/ui/pages/asset/components/asset_dialogs.dart b/flutter_monisuo/lib/ui/pages/asset/components/asset_dialogs.dart index 94646c4..dab3d02 100644 --- a/flutter_monisuo/lib/ui/pages/asset/components/asset_dialogs.dart +++ b/flutter_monisuo/lib/ui/pages/asset/components/asset_dialogs.dart @@ -360,8 +360,30 @@ void showWithdrawDialog(BuildContext context, String? balance) { final addressController = TextEditingController(); final contactController = TextEditingController(); final formKey = GlobalKey(); + final feeNotifier = ValueNotifier('提现将扣除10%手续费'); + final networksNotifier = ValueNotifier>([]); + final selectedNetworkNotifier = ValueNotifier(null); final colorScheme = Theme.of(context).colorScheme; + amountController.addListener(() { + final amount = double.tryParse(amountController.text) ?? 0; + if (amount > 0) { + final fee = amount * 0.1; + final receivable = amount - fee; + feeNotifier.value = '手续费(10%): -${fee.toStringAsFixed(2)} USDT | 应收款: ${receivable.toStringAsFixed(2)} USDT'; + } else { + feeNotifier.value = '提现将扣除10%手续费'; + } + }); + + // 获取网络列表 + context.read().getWalletNetworks().then((list) { + networksNotifier.value = list; + if (list.isNotEmpty) { + selectedNetworkNotifier.value = list.first; + } + }); + showShadDialog( context: context, builder: (ctx) => Dialog( @@ -451,6 +473,65 @@ void showWithdrawDialog(BuildContext context, String? balance) { validator: Validators.amount, ), const SizedBox(height: AppSpacing.md), + // 手续费/应收款提示 + ValueListenableBuilder( + valueListenable: feeNotifier, + builder: (_, feeText, __) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md, vertical: AppSpacing.sm), + decoration: BoxDecoration( + color: colorScheme.surfaceContainerHigh.withValues(alpha: 0.5), + borderRadius: BorderRadius.circular(AppRadius.md), + ), + child: Row( + children: [ + Icon(Icons.info_outline, size: 14, color: colorScheme.onSurfaceVariant), + const SizedBox(width: AppSpacing.xs), + Expanded( + child: Text( + feeText, + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + ), + ], + ), + ); + }, + ), + const SizedBox(height: AppSpacing.md), + // 提现网络选择 + ValueListenableBuilder>( + valueListenable: networksNotifier, + builder: (_, networks, __) { + if (networks.isEmpty) return const SizedBox.shrink(); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('提现网络', style: AppTextStyles.bodyMedium(context).copyWith( + fontWeight: FontWeight.w500, + )), + const SizedBox(height: AppSpacing.xs), + ValueListenableBuilder( + valueListenable: selectedNetworkNotifier, + builder: (_, selected, __) { + return ShadSelect( + placeholder: const Text('选择提现网络'), + initialValue: selected, + selectedOptionBuilder: (context, val) => Text(val), + onChanged: (value) { + if (value != null) selectedNetworkNotifier.value = value; + }, + options: networks.map((n) => ShadOption(value: n, child: Text(n))).toList(), + ); + }, + ), + ], + ); + }, + ), + const SizedBox(height: AppSpacing.md), ShadInputFormField( id: 'address', controller: addressController, @@ -494,6 +575,7 @@ void showWithdrawDialog(BuildContext context, String? balance) { withdrawContact: contactController.text.isNotEmpty ? contactController.text : null, + network: selectedNetworkNotifier.value, ); if (context.mounted) { showResultDialog( diff --git a/flutter_monisuo/lib/ui/pages/asset/components/balance_card.dart b/flutter_monisuo/lib/ui/pages/asset/components/balance_card.dart index 5c780d3..a60789a 100644 --- a/flutter_monisuo/lib/ui/pages/asset/components/balance_card.dart +++ b/flutter_monisuo/lib/ui/pages/asset/components/balance_card.dart @@ -2,29 +2,22 @@ import 'package:flutter/material.dart'; import '../../../../core/theme/app_theme.dart'; import '../../../../core/theme/app_theme_extension.dart'; import '../../../../core/theme/app_spacing.dart'; -import '../../../../providers/asset_provider.dart'; import '../../../components/glass_panel.dart'; -/// 余额卡片 — .pen node 59637 -/// cornerRadius: lg, fill: $surface-card, padding: 20, stroke: $border-default 1px, gap: 12 -/// balLabel: "USDT 余额" 12px normal $text-secondary -/// balAmount: "25,680.50" 28px w700 $text-primary -/// balSubRow: "≈ $25,680.50 USD" 12px normal $text-muted +/// 余额卡片 — 显示单个账户的 USDT 余额 class BalanceCard extends StatelessWidget { - final AssetProvider provider; - final int activeTab; + final String label; + final String balance; const BalanceCard({ super.key, - required this.provider, - required this.activeTab, + required this.label, + required this.balance, }); @override Widget build(BuildContext context) { - final displayBalance = activeTab == 0 - ? (provider.fundAccount?.balance ?? provider.overview?.fundBalance ?? '0.00') - : _calculateTradeTotal(); + final displayBalance = balance; return SizedBox( width: double.infinity, // 确保卡片撑满宽度 @@ -35,7 +28,7 @@ class BalanceCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'USDT 余额', + label, style: AppTextStyles.bodyMedium(context).copyWith( color: context.colors.onSurfaceVariant, fontWeight: FontWeight.w400, @@ -60,14 +53,6 @@ class BalanceCard extends StatelessWidget { ); } - String _calculateTradeTotal() { - double total = 0; - for (var h in provider.holdings) { - total += double.tryParse(h.currentValue?.toString() ?? '0') ?? 0; - } - return total.toStringAsFixed(2); - } - String _formatBalance(String balance) { final d = double.tryParse(balance) ?? 0; return d.toStringAsFixed(2).replaceAllMapped( diff --git a/flutter_monisuo/lib/ui/pages/asset/components/holdings_section.dart b/flutter_monisuo/lib/ui/pages/asset/components/holdings_section.dart index 49bb991..bb46c4c 100644 --- a/flutter_monisuo/lib/ui/pages/asset/components/holdings_section.dart +++ b/flutter_monisuo/lib/ui/pages/asset/components/holdings_section.dart @@ -5,8 +5,8 @@ import '../../../../core/theme/app_spacing.dart'; import '../../../../data/models/account_models.dart'; import '../../../components/glass_panel.dart'; -/// 持仓区域 — .pen nodes th9BG (header) + 6X6tC (card) -/// Holdings Header: "交易账户持仓" 16px w600 $text-primary | "查看全部 >" 12px normal $text-secondary +/// 持仓区域 +/// Header: "我的资产" + "查看全部 >" /// Holdings Card: cornerRadius lg, fill $surface-card, stroke $border-default 1px class HoldingsSection extends StatelessWidget { final List holdings; @@ -19,14 +19,14 @@ class HoldingsSection extends StatelessWidget { return Column( children: [ - // Header row: "交易账户持仓" + "查看全部 >" + // Header row: "我的资产" + "查看全部 >" Padding( padding: const EdgeInsets.only(bottom: AppSpacing.sm), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '交易账户持仓', + '我的资产', style: AppTextStyles.headlineLarge(context), ), Text( diff --git a/flutter_monisuo/lib/ui/pages/asset/components/records_link_row.dart b/flutter_monisuo/lib/ui/pages/asset/components/records_link_row.dart index 4531e9d..9ec097c 100644 --- a/flutter_monisuo/lib/ui/pages/asset/components/records_link_row.dart +++ b/flutter_monisuo/lib/ui/pages/asset/components/records_link_row.dart @@ -5,10 +5,8 @@ import '../../../../core/theme/app_theme_extension.dart'; import '../../../../core/theme/app_spacing.dart'; import '../../../components/glass_panel.dart'; -/// 充提记录链接行 — .pen node fLHtq +/// 订单记录链接行 /// cornerRadius: lg, fill: $surface-card, padding: [14, 16], stroke: $border-default 1px -/// recordsText: "充提记录" 14px w500 $text-primary -/// recordsChevron: lucide chevron-right 16px $text-muted class RecordsLinkRow extends StatelessWidget { final VoidCallback onTap; @@ -27,7 +25,7 @@ class RecordsLinkRow extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '充提记录', + '订单记录', style: AppTextStyles.headlineMedium(context).copyWith( fontWeight: FontWeight.w500, ), diff --git a/flutter_monisuo/lib/ui/pages/home/bills_page.dart b/flutter_monisuo/lib/ui/pages/home/bills_page.dart new file mode 100644 index 0000000..18591bd --- /dev/null +++ b/flutter_monisuo/lib/ui/pages/home/bills_page.dart @@ -0,0 +1,450 @@ +import 'package:flutter/material.dart'; +import 'package:lucide_icons_flutter/lucide_icons.dart'; +import 'package:provider/provider.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/theme/app_color_scheme.dart'; +import '../../../core/theme/app_theme_extension.dart'; +import '../../../core/theme/app_spacing.dart'; +import '../../../data/models/account_models.dart'; +import '../../../data/services/bonus_service.dart'; +import '../../../providers/asset_provider.dart'; + +/// 账单页面 — 代币盈亏账单 + 新人福利账单 + 推广福利账单 +class BillsPage extends StatefulWidget { + const BillsPage({super.key}); + + @override + State createState() => _BillsPageState(); +} + +class _BillsPageState extends State with SingleTickerProviderStateMixin { + late TabController _tabController; + List _holdings = []; + List> _welfareRecords = []; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 3, vsync: this); + WidgetsBinding.instance.addPostFrameCallback((_) => _loadData()); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + Future _loadData() async { + setState(() => _isLoading = true); + try { + final provider = context.read(); + final bonusService = context.read(); + + // 并行加载持仓和福利记录 + await provider.loadTradeAccount(force: true); + final welfareResponse = await bonusService.getWelfareStatus(); + + if (mounted) { + setState(() { + _holdings = provider.holdings; + if (welfareResponse.success && welfareResponse.data != null) { + _welfareRecords = _parseWelfareRecords(welfareResponse.data!); + } + _isLoading = false; + }); + } + } catch (_) { + if (mounted) { + setState(() { + _holdings = context.read().holdings; + _isLoading = false; + }); + } + } + } + + List> _parseWelfareRecords(Map data) { + final records = >[]; + // 新人福利 + final newUser = data['newUserBonus'] as Map?; + if (newUser != null) { + records.add({ + 'type': 'new_user', + 'title': '新人福利', + 'amount': newUser['amount']?.toString() ?? '0.00', + 'status': newUser['status'] ?? 0, + 'time': newUser['claimTime'] ?? newUser['createTime'], + }); + } + // 推广福利列表 + final referrals = data['referralBonuses'] as List?; + if (referrals != null) { + for (var r in referrals) { + final map = r as Map; + records.add({ + 'type': 'referral', + 'title': '推广福利 - ${map['referredUsername'] ?? '用户'}', + 'amount': map['amount']?.toString() ?? '0.00', + 'status': map['status'] ?? 0, + 'time': map['claimTime'] ?? map['createTime'], + }); + } + } + return records; + } + + bool get _isDark => Theme.of(context).brightness == Brightness.dark; + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + + return Scaffold( + backgroundColor: _isDark ? AppColorScheme.darkBackground : AppColorScheme.lightBackground, + appBar: AppBar( + leading: IconButton( + icon: const Icon(LucideIcons.arrowLeft, size: 20), + onPressed: () => Navigator.of(context).pop(), + ), + title: Text('我的账单', style: AppTextStyles.headlineLarge(context)), + backgroundColor: _isDark ? AppColorScheme.darkBackground : AppColorScheme.lightBackground, + elevation: 0, + scrolledUnderElevation: 0, + centerTitle: true, + bottom: TabBar( + controller: _tabController, + labelColor: colorScheme.primary, + unselectedLabelColor: colorScheme.onSurfaceVariant, + indicatorColor: colorScheme.primary, + labelStyle: AppTextStyles.headlineMedium(context).copyWith(fontWeight: FontWeight.w600), + unselectedLabelStyle: AppTextStyles.headlineMedium(context), + tabs: const [ + Tab(text: '代币盈亏'), + Tab(text: '新人福利'), + Tab(text: '推广福利'), + ], + ), + ), + body: _isLoading + ? const Center(child: CircularProgressIndicator()) + : TabBarView( + controller: _tabController, + children: [ + _buildCoinProfitTab(), + _buildWelfareTab('new_user'), + _buildWelfareTab('referral'), + ], + ), + ); + } + + // ============================================ + // 代币盈亏账单 + // ============================================ + Widget _buildCoinProfitTab() { + final colorScheme = Theme.of(context).colorScheme; + + if (_holdings.isEmpty) { + return _buildEmptyState(LucideIcons.wallet, '暂无持仓记录'); + } + + // 汇总统计 + double totalCost = 0; + double totalValue = 0; + double totalProfit = 0; + for (var h in _holdings) { + totalCost += double.tryParse(h.totalCost) ?? 0; + totalValue += double.tryParse(h.currentValue) ?? 0; + totalProfit += double.tryParse(h.profit) ?? 0; + } + final profitRate = totalCost > 0 ? (totalProfit / totalCost * 100) : 0.0; + final isProfit = totalProfit >= 0; + final profitColor = isProfit ? context.appColors.up : context.appColors.down; + + return RefreshIndicator( + onRefresh: _loadData, + child: ListView( + padding: const EdgeInsets.all(AppSpacing.md), + children: [ + // 汇总卡片 + Container( + padding: const EdgeInsets.all(AppSpacing.lg), + decoration: BoxDecoration( + color: _isDark ? AppColorScheme.darkSurfaceContainer : AppColorScheme.lightSurfaceLowest, + borderRadius: BorderRadius.circular(AppRadius.xl), + border: Border.all( + color: _isDark + ? AppColorScheme.darkOutlineVariant.withValues(alpha: 0.15) + : AppColorScheme.lightOutlineVariant.withValues(alpha: 0.5), + ), + ), + child: Column( + children: [ + Text('总盈亏 (USDT)', style: AppTextStyles.bodyMedium(context).copyWith( + color: colorScheme.onSurfaceVariant, + )), + const SizedBox(height: AppSpacing.xs), + Text( + '${isProfit ? '+' : ''}${totalProfit.toStringAsFixed(2)}', + style: AppTextStyles.displaySmall(context).copyWith( + fontWeight: FontWeight.bold, + color: profitColor, + ), + ), + const SizedBox(height: AppSpacing.sm), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildSummaryItem('总成本', totalCost.toStringAsFixed(2)), + Container(width: 1, height: 16, color: colorScheme.outlineVariant.withValues(alpha: 0.3)), + _buildSummaryItem('总市值', totalValue.toStringAsFixed(2)), + Container(width: 1, height: 16, color: colorScheme.outlineVariant.withValues(alpha: 0.3)), + _buildSummaryItem('收益率', '${profitRate >= 0 ? '+' : ''}${profitRate.toStringAsFixed(2)}%'), + ], + ), + ], + ), + ), + const SizedBox(height: AppSpacing.md), + + // 各币种盈亏明细 + ..._holdings.map((h) => _buildCoinProfitCard(h)), + ], + ), + ); + } + + Widget _buildSummaryItem(String label, String value) { + final colorScheme = Theme.of(context).colorScheme; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), + child: Column( + children: [ + Text(label, style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + )), + const SizedBox(height: 2), + Text(value, style: AppTextStyles.labelMedium(context).copyWith( + fontWeight: FontWeight.w600, + )), + ], + ), + ); + } + + Widget _buildCoinProfitCard(AccountTrade h) { + final colorScheme = Theme.of(context).colorScheme; + final profit = double.tryParse(h.profit) ?? 0; + final isProfit = profit >= 0; + final profitColor = isProfit ? context.appColors.up : context.appColors.down; + + return Container( + margin: const EdgeInsets.only(bottom: AppSpacing.sm), + padding: const EdgeInsets.all(AppSpacing.md), + decoration: BoxDecoration( + color: _isDark ? AppColorScheme.darkSurfaceContainer : AppColorScheme.lightSurfaceLowest, + borderRadius: BorderRadius.circular(AppRadius.lg), + border: Border.all( + color: _isDark + ? AppColorScheme.darkOutlineVariant.withValues(alpha: 0.15) + : AppColorScheme.lightOutlineVariant.withValues(alpha: 0.5), + ), + ), + child: Column( + children: [ + // 币名 + 盈亏金额 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + CircleAvatar( + radius: 16, + backgroundColor: colorScheme.primary.withValues(alpha: 0.1), + child: Text( + h.coinCode.substring(0, 1), + style: AppTextStyles.labelLarge(context).copyWith( + color: colorScheme.primary, + fontWeight: FontWeight.bold, + ), + ), + ), + const SizedBox(width: AppSpacing.sm), + Text(h.coinCode, style: AppTextStyles.headlineMedium(context).copyWith( + fontWeight: FontWeight.bold, + )), + const SizedBox(width: AppSpacing.xs), + Text('x ${double.tryParse(h.quantity)?.toStringAsFixed(4) ?? h.quantity}', + style: AppTextStyles.bodySmall(context).copyWith(color: colorScheme.onSurfaceVariant), + ), + ], + ), + Text( + '${isProfit ? '+' : ''}${profit.toStringAsFixed(2)} USDT', + style: AppTextStyles.headlineMedium(context).copyWith( + color: profitColor, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: AppSpacing.sm), + // 明细行 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('均价: ${h.avgPrice}', style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + )), + Text('市值: ${h.currentValue} USDT', style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + )), + Text(h.formattedProfitRate, style: AppTextStyles.bodySmall(context).copyWith( + color: profitColor, + fontWeight: FontWeight.w600, + )), + ], + ), + ], + ), + ); + } + + // ============================================ + // 福利账单 + // ============================================ + Widget _buildWelfareTab(String type) { + final records = _welfareRecords.where((r) => r['type'] == type).toList(); + + if (records.isEmpty) { + return _buildEmptyState( + LucideIcons.gift, + type == 'new_user' ? '暂无新人福利记录' : '暂无推广福利记录', + ); + } + + return RefreshIndicator( + onRefresh: _loadData, + child: ListView.builder( + padding: const EdgeInsets.all(AppSpacing.md), + itemCount: records.length, + itemBuilder: (context, index) => _buildWelfareCard(records[index]), + ), + ); + } + + Widget _buildWelfareCard(Map record) { + final colorScheme = Theme.of(context).colorScheme; + final amount = double.tryParse(record['amount']?.toString() ?? '0') ?? 0; + final status = record['status'] as int? ?? 0; + + // status: 0=待领取, 1=已领取, 2=已过期 + String statusText; + Color statusColor; + switch (status) { + case 1: + statusText = '已领取'; + statusColor = context.appColors.up; + break; + case 2: + statusText = '已过期'; + statusColor = colorScheme.onSurfaceVariant; + break; + default: + statusText = '待领取'; + statusColor = AppColorScheme.warning; + } + + return Container( + margin: const EdgeInsets.only(bottom: AppSpacing.sm), + padding: const EdgeInsets.all(AppSpacing.md), + decoration: BoxDecoration( + color: _isDark ? AppColorScheme.darkSurfaceContainer : AppColorScheme.lightSurfaceLowest, + borderRadius: BorderRadius.circular(AppRadius.lg), + border: Border.all( + color: _isDark + ? AppColorScheme.darkOutlineVariant.withValues(alpha: 0.15) + : AppColorScheme.lightOutlineVariant.withValues(alpha: 0.5), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(record['title'] ?? '', style: AppTextStyles.headlineMedium(context).copyWith( + fontWeight: FontWeight.bold, + )), + const SizedBox(height: AppSpacing.xs), + if (record['time'] != null) + Text( + _formatTime(record['time']), + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + '+${amount.toStringAsFixed(2)} USDT', + style: AppTextStyles.headlineMedium(context).copyWith( + color: context.appColors.up, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 2), + Container( + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.sm, vertical: 2), + decoration: BoxDecoration( + color: statusColor.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(AppRadius.sm), + ), + child: Text(statusText, style: AppTextStyles.bodySmall(context).copyWith( + color: statusColor, + fontWeight: FontWeight.w600, + fontSize: 11, + )), + ), + ], + ), + ], + ), + ); + } + + // ============================================ + // 通用组件 + // ============================================ + Widget _buildEmptyState(IconData icon, String text) { + final colorScheme = Theme.of(context).colorScheme; + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, size: 64, color: colorScheme.onSurfaceVariant.withValues(alpha: 0.4)), + const SizedBox(height: AppSpacing.md), + Text(text, style: AppTextStyles.headlineMedium(context).copyWith( + color: colorScheme.onSurfaceVariant, + )), + ], + ), + ); + } + + String _formatTime(dynamic time) { + if (time == null) return '-'; + if (time is DateTime) { + return '${time.year}-${time.month.toString().padLeft(2, '0')}-${time.day.toString().padLeft(2, '0')} ' + '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}'; + } + return time.toString(); + } +} diff --git a/flutter_monisuo/lib/ui/pages/home/quick_actions_row.dart b/flutter_monisuo/lib/ui/pages/home/quick_actions_row.dart index ddd03ed..2b02d1c 100644 --- a/flutter_monisuo/lib/ui/pages/home/quick_actions_row.dart +++ b/flutter_monisuo/lib/ui/pages/home/quick_actions_row.dart @@ -3,19 +3,21 @@ import 'package:shadcn_ui/shadcn_ui.dart'; import '../../../core/theme/app_theme.dart'; import '../../../core/theme/app_spacing.dart'; -/// 首页快捷操作栏 - 充值/提现/划转/账单 +/// 首页快捷操作栏 - 充值/提现/划转/盈亏/账单 class QuickActionsRow extends StatelessWidget { const QuickActionsRow({ super.key, this.onDeposit, this.onWithdraw, this.onTransfer, + this.onProfit, this.onBills, }); final VoidCallback? onDeposit; final VoidCallback? onWithdraw; final VoidCallback? onTransfer; + final VoidCallback? onProfit; final VoidCallback? onBills; @override @@ -64,6 +66,12 @@ class QuickActionsRow extends StatelessWidget { colorScheme: colorScheme, onTap: onTransfer, ), + _ActionItem( + icon: LucideIcons.chartPie, + label: '盈亏', + colorScheme: colorScheme, + onTap: onProfit, + ), _ActionItem( icon: LucideIcons.fileText, label: '账单', diff --git a/flutter_monisuo/lib/ui/pages/orders/fund_order_card.dart b/flutter_monisuo/lib/ui/pages/orders/fund_order_card.dart index 0015f88..997cba7 100644 --- a/flutter_monisuo/lib/ui/pages/orders/fund_order_card.dart +++ b/flutter_monisuo/lib/ui/pages/orders/fund_order_card.dart @@ -127,7 +127,7 @@ class _FundOrderCard extends StatelessWidget { SizedBox(height: AppSpacing.xs), Row( children: [ - Text('应付款: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)), + Text('应收款: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)), Text('${order.receivableAmount ?? "0"} USDT', style: AppTextStyles.bodyMedium(context).copyWith(fontWeight: FontWeight.w700)), ], ), diff --git a/monisuo-admin/src/pages/monisuo/finance-orders.vue b/monisuo-admin/src/pages/monisuo/finance-orders.vue index 7b4ef14..f7e7d88 100644 --- a/monisuo-admin/src/pages/monisuo/finance-orders.vue +++ b/monisuo-admin/src/pages/monisuo/finance-orders.vue @@ -104,7 +104,7 @@ function copyToClipboard(text: string) { 手续费 - 到账金额 + 应出款 审批人