603 lines
18 KiB
Dart
603 lines
18 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
import '../../../../core/di/injection_container.dart';
|
||
import '../../../../routes/route_paths.dart';
|
||
|
||
/// 结算币种枚举
|
||
enum SettlementCurrency { bnb, og, usdt, dst }
|
||
|
||
/// 兑换页面 - 显示可结算收益和兑换功能
|
||
/// 支持一键结算和DST转USDT
|
||
class TradingPage extends ConsumerStatefulWidget {
|
||
const TradingPage({super.key});
|
||
|
||
@override
|
||
ConsumerState<TradingPage> createState() => _TradingPageState();
|
||
}
|
||
|
||
class _TradingPageState extends ConsumerState<TradingPage> {
|
||
// 当前选中的结算币种
|
||
SettlementCurrency _selectedCurrency = SettlementCurrency.usdt;
|
||
|
||
// 钱包数据(从 wallet-service 获取)
|
||
double _settleableAmount = 0.0;
|
||
double _dstBalance = 0.0;
|
||
double _usdtBalance = 0.0;
|
||
bool _isLoading = true;
|
||
bool _isSettling = false;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_loadWalletData();
|
||
}
|
||
|
||
/// 加载钱包数据
|
||
Future<void> _loadWalletData() async {
|
||
try {
|
||
debugPrint('[TradingPage] 开始加载数据...');
|
||
|
||
// 从 reward-service 获取可结算收益(与 Profile 页面保持一致)
|
||
final rewardService = ref.read(rewardServiceProvider);
|
||
final summary = await rewardService.getMyRewardSummary();
|
||
|
||
// 从 wallet-service 获取 DST 余额
|
||
final walletService = ref.read(walletServiceProvider);
|
||
final wallet = await walletService.getMyWallet();
|
||
|
||
if (mounted) {
|
||
setState(() {
|
||
_settleableAmount = summary.settleableUsdt;
|
||
_dstBalance = wallet.balances.dst.available;
|
||
_usdtBalance = wallet.balances.usdt.available;
|
||
_isLoading = false;
|
||
});
|
||
debugPrint('[TradingPage] 数据加载成功:');
|
||
debugPrint('[TradingPage] 可结算 USDT: $_settleableAmount (from reward-service)');
|
||
debugPrint('[TradingPage] DST 余额: $_dstBalance (from wallet-service)');
|
||
debugPrint('[TradingPage] USDT 余额: $_usdtBalance (from wallet-service)');
|
||
}
|
||
} catch (e, stackTrace) {
|
||
debugPrint('[TradingPage] 加载数据失败: $e');
|
||
debugPrint('[TradingPage] 堆栈: $stackTrace');
|
||
if (mounted) {
|
||
setState(() {
|
||
_isLoading = false;
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 选择结算币种
|
||
void _selectCurrency(SettlementCurrency currency) {
|
||
setState(() {
|
||
_selectedCurrency = currency;
|
||
});
|
||
}
|
||
|
||
/// 一键结算
|
||
void _onSettlement() {
|
||
if (_settleableAmount <= 0) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
const SnackBar(
|
||
content: Text('没有可结算的收益'),
|
||
backgroundColor: Color(0xFF8B5A2B),
|
||
),
|
||
);
|
||
return;
|
||
}
|
||
|
||
showDialog(
|
||
context: context,
|
||
builder: (context) => AlertDialog(
|
||
title: const Text('确认结算'),
|
||
content: Text(
|
||
'确定将 ${_formatNumber(_settleableAmount)} USDT 结算为 ${_getCurrencyName(_selectedCurrency)} 吗?',
|
||
),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () => Navigator.pop(context),
|
||
child: const Text('取消'),
|
||
),
|
||
TextButton(
|
||
onPressed: () {
|
||
Navigator.pop(context);
|
||
_doSettlement();
|
||
},
|
||
child: const Text('确认'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 执行结算
|
||
Future<void> _doSettlement() async {
|
||
if (_isSettling) return;
|
||
|
||
setState(() {
|
||
_isSettling = true;
|
||
});
|
||
|
||
try {
|
||
debugPrint('[TradingPage] 开始结算...');
|
||
debugPrint('[TradingPage] 金额: $_settleableAmount USDT');
|
||
debugPrint('[TradingPage] 币种: ${_getCurrencyName(_selectedCurrency)}');
|
||
|
||
final walletService = ref.read(walletServiceProvider);
|
||
final orderId = await walletService.settleRewards(
|
||
usdtAmount: _settleableAmount,
|
||
settleCurrency: _getCurrencyName(_selectedCurrency),
|
||
);
|
||
|
||
debugPrint('[TradingPage] 结算成功: orderId=$orderId');
|
||
|
||
if (mounted) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
const SnackBar(
|
||
content: Text('结算成功'),
|
||
backgroundColor: Color(0xFFD4AF37),
|
||
),
|
||
);
|
||
// 刷新钱包数据
|
||
_loadWalletData();
|
||
}
|
||
} catch (e) {
|
||
debugPrint('[TradingPage] 结算失败: $e');
|
||
if (mounted) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
SnackBar(
|
||
content: Text('结算失败: $e'),
|
||
backgroundColor: Colors.red,
|
||
),
|
||
);
|
||
}
|
||
} finally {
|
||
if (mounted) {
|
||
setState(() {
|
||
_isSettling = false;
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 卖出DST转换为USDT
|
||
void _onSellDst() {
|
||
showDialog(
|
||
context: context,
|
||
builder: (context) => AlertDialog(
|
||
title: const Text('卖出 DST'),
|
||
content: Text(
|
||
'确定将 ${_formatNumber(_dstBalance)} DST 转换为 USDT 吗?',
|
||
),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () => Navigator.pop(context),
|
||
child: const Text('取消'),
|
||
),
|
||
TextButton(
|
||
onPressed: () {
|
||
Navigator.pop(context);
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
const SnackBar(
|
||
content: Text('转换成功'),
|
||
backgroundColor: Color(0xFFD4AF37),
|
||
),
|
||
);
|
||
},
|
||
child: const Text('确认'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 格式化数字(添加千分位)
|
||
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 _getCurrencyName(SettlementCurrency currency) {
|
||
switch (currency) {
|
||
case SettlementCurrency.bnb:
|
||
return 'BNB';
|
||
case SettlementCurrency.og:
|
||
return 'OG';
|
||
case SettlementCurrency.usdt:
|
||
return 'USDT';
|
||
case SettlementCurrency.dst:
|
||
return 'DST';
|
||
}
|
||
}
|
||
|
||
@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: Column(
|
||
children: [
|
||
// 顶部标题栏
|
||
_buildAppBar(),
|
||
const SizedBox(height: 16),
|
||
// 可结算收益卡片
|
||
_buildSettleableCard(),
|
||
const SizedBox(height: 24),
|
||
// 一键结算按钮
|
||
_buildSettlementButton(),
|
||
const SizedBox(height: 16),
|
||
// 结算币种选择
|
||
_buildCurrencySelector(),
|
||
const SizedBox(height: 24),
|
||
// 分隔线
|
||
_buildDivider(),
|
||
const SizedBox(height: 24),
|
||
// 卖出DST按钮
|
||
_buildSellDstButton(),
|
||
const SizedBox(height: 8),
|
||
// DST余额显示
|
||
_buildDstBalance(),
|
||
const SizedBox(height: 24),
|
||
// 分隔线
|
||
_buildDivider(),
|
||
const SizedBox(height: 24),
|
||
// 提款/转账按钮
|
||
_buildWithdrawButton(),
|
||
const SizedBox(height: 8),
|
||
// USDT余额显示
|
||
_buildUsdtBalance(),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建顶部标题栏
|
||
Widget _buildAppBar() {
|
||
return Container(
|
||
height: 56,
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: const Center(
|
||
child: Text(
|
||
'兑换',
|
||
style: TextStyle(
|
||
fontSize: 18,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
letterSpacing: -0.27,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建可结算收益卡片
|
||
Widget _buildSettleableCard() {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: Container(
|
||
width: double.infinity,
|
||
padding: const EdgeInsets.all(24),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0x1A8B5A2B),
|
||
borderRadius: BorderRadius.circular(12),
|
||
),
|
||
child: Column(
|
||
children: [
|
||
const Text(
|
||
'可结算收益',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
height: 1.5,
|
||
color: Color(0xCC8B5A2B),
|
||
),
|
||
),
|
||
const SizedBox(height: 8),
|
||
_isLoading
|
||
? const SizedBox(
|
||
width: 24,
|
||
height: 24,
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 2,
|
||
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFFD4AF37)),
|
||
),
|
||
)
|
||
: Text(
|
||
'${_formatNumber(_settleableAmount)} USDT',
|
||
style: const TextStyle(
|
||
fontSize: 32,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.25,
|
||
letterSpacing: -0.8,
|
||
color: Color(0xFF5D4037),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建一键结算按钮
|
||
/// 注意:一键结算功能暂时禁用
|
||
Widget _buildSettlementButton() {
|
||
// 功能暂时禁用,始终不可点击
|
||
const bool canSettle = false;
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: GestureDetector(
|
||
onTap: null, // 功能暂时禁用
|
||
child: Container(
|
||
width: double.infinity,
|
||
height: 56,
|
||
decoration: BoxDecoration(
|
||
color: const Color(0x80D4AF37), // 禁用状态颜色
|
||
borderRadius: BorderRadius.circular(12),
|
||
),
|
||
child: const Center(
|
||
child: Text(
|
||
'一键结算(暂未开放)',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
letterSpacing: 0.24,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建结算币种选择器
|
||
Widget _buildCurrencySelector() {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: Column(
|
||
children: [
|
||
const Text(
|
||
'结算的币种',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
letterSpacing: 0.21,
|
||
color: Color(0xCC8B5A2B),
|
||
),
|
||
),
|
||
const SizedBox(height: 12),
|
||
Container(
|
||
padding: const EdgeInsets.all(6),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0x1A8B5A2B),
|
||
borderRadius: BorderRadius.circular(12),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
_buildCurrencyChip(SettlementCurrency.bnb, 'BNB'),
|
||
const SizedBox(width: 8),
|
||
_buildCurrencyChip(SettlementCurrency.og, 'OG'),
|
||
const SizedBox(width: 8),
|
||
_buildCurrencyChip(SettlementCurrency.usdt, 'USDT'),
|
||
const SizedBox(width: 8),
|
||
_buildCurrencyChip(SettlementCurrency.dst, 'DST'),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建币种选择标签
|
||
Widget _buildCurrencyChip(SettlementCurrency currency, String label) {
|
||
final isSelected = _selectedCurrency == currency;
|
||
return Expanded(
|
||
child: GestureDetector(
|
||
onTap: () => _selectCurrency(currency),
|
||
child: Container(
|
||
height: 40,
|
||
decoration: BoxDecoration(
|
||
color: isSelected ? const Color(0xFFD4AF37) : Colors.transparent,
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: Center(
|
||
child: Text(
|
||
label,
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w600,
|
||
height: 1.43,
|
||
color: isSelected ? Colors.white : const Color(0xCC5D4037),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建分隔线
|
||
Widget _buildDivider() {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: Container(
|
||
height: 1,
|
||
color: const Color(0x1A8B5A2B),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建卖出DST按钮
|
||
/// 注意:卖出DST功能暂时禁用
|
||
Widget _buildSellDstButton() {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: GestureDetector(
|
||
onTap: null, // 功能暂时禁用
|
||
child: Container(
|
||
width: double.infinity,
|
||
height: 56,
|
||
decoration: BoxDecoration(
|
||
color: const Color(0x1A8B5A2B), // 禁用状态颜色更浅
|
||
borderRadius: BorderRadius.circular(12),
|
||
border: Border.all(
|
||
color: const Color(0x408B5A2B), // 禁用状态边框更浅
|
||
width: 1,
|
||
),
|
||
),
|
||
child: const Center(
|
||
child: Text(
|
||
'卖出 DST 转换为 USDT(暂未开放)',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
letterSpacing: 0.24,
|
||
color: Color(0x808B5A2B), // 禁用状态文字颜色更浅
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建DST余额显示
|
||
Widget _buildDstBalance() {
|
||
return _isLoading
|
||
? const SizedBox(
|
||
width: 16,
|
||
height: 16,
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 2,
|
||
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFFD4AF37)),
|
||
),
|
||
)
|
||
: Text(
|
||
'DST 余额: ${_formatNumber(_dstBalance)}',
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
height: 1.5,
|
||
color: Color(0x995D4037),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建提款/转账按钮
|
||
/// 当 USDT 余额为 0 时禁用
|
||
Widget _buildWithdrawButton() {
|
||
// 余额为0时禁用
|
||
final bool canWithdraw = !_isLoading && _usdtBalance > 0;
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: GestureDetector(
|
||
onTap: canWithdraw
|
||
? () {
|
||
context.push(RoutePaths.withdrawUsdt);
|
||
}
|
||
: null,
|
||
child: Container(
|
||
width: double.infinity,
|
||
height: 56,
|
||
decoration: BoxDecoration(
|
||
color: canWithdraw
|
||
? const Color(0xFFD4AF37)
|
||
: const Color(0x80D4AF37), // 禁用状态颜色
|
||
borderRadius: BorderRadius.circular(12),
|
||
boxShadow: canWithdraw
|
||
? const [
|
||
BoxShadow(
|
||
color: Color(0x4DD4AF37),
|
||
blurRadius: 14,
|
||
offset: Offset(0, 4),
|
||
),
|
||
]
|
||
: null, // 禁用时无阴影
|
||
),
|
||
child: Center(
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Icon(
|
||
Icons.account_balance_wallet_outlined,
|
||
color: canWithdraw ? Colors.white : Colors.white70,
|
||
size: 20,
|
||
),
|
||
const SizedBox(width: 8),
|
||
Text(
|
||
'提款 / 转账',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontFamily: 'Inter',
|
||
fontWeight: FontWeight.w700,
|
||
height: 1.5,
|
||
letterSpacing: 0.24,
|
||
color: canWithdraw ? Colors.white : Colors.white70,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 构建USDT余额显示
|
||
Widget _buildUsdtBalance() {
|
||
return _isLoading
|
||
? const SizedBox(
|
||
width: 16,
|
||
height: 16,
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 2,
|
||
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFFD4AF37)),
|
||
),
|
||
)
|
||
: Text(
|
||
'USDT 余额: ${_formatNumber(_usdtBalance)}',
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontFamily: 'Inter',
|
||
height: 1.5,
|
||
color: Color(0x995D4037),
|
||
),
|
||
);
|
||
}
|
||
}
|