This commit is contained in:
2026-04-08 02:13:29 +08:00
12 changed files with 903 additions and 82 deletions

View File

@@ -140,9 +140,11 @@ class _TransferPageState extends State<TransferPage> {
void _setQuickAmount(double percent) {
final available = double.tryParse(_availableBalance) ?? 0;
final amount = available * percent;
_amountController.text = amount
.toStringAsFixed(8)
.replaceAll(RegExp(r'\.?0+$'), '');
// 向下截斷到2位小數避免四捨五入超出餘額
_amountController.text =
((amount * 100).truncateToDouble() / 100).toStringAsFixed(2);
// Trigger haptic feedback
HapticFeedback.selectionClick();
}

View File

@@ -88,6 +88,11 @@ class _FundOrderCard extends StatelessWidget {
final isDeposit = order.type == 1;
final statusColor = _getStatusColor(order.status, isDeposit);
// 已出款的提現訂單顯示到賬金額,其餘顯示應付金額
final displayAmount = (!isDeposit && order.status == 2 && order.receivableAmount != null)
? order.receivableAmount
: order.amount;
return ShadCard(
padding: AppSpacing.cardPadding,
child: Column(
@@ -97,7 +102,7 @@ class _FundOrderCard extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${isDeposit ? '+' : '-'}${order.amount} USDT',
'${isDeposit ? '+' : '-'}$displayAmount USDT',
style: AppTextStyles.headlineMedium(context).copyWith(
color: statusColor,
fontWeight: FontWeight.w700,

View File

@@ -340,7 +340,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
const SizedBox(height: AppSpacing.sm - AppSpacing.xs),
],
if (order.fee != null && !order.isDeposit) ...[
_buildDetailRow('手續費', '${order.fee}%'),
_buildDetailRow('手續費', '${order.fee} USDT'),
const SizedBox(height: AppSpacing.sm - AppSpacing.xs),
],
_buildDetailRow(
@@ -400,7 +400,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('應付金額', style: AppTextStyles.headlineSmall(context).copyWith(color: context.colors.onSurfaceVariant)),
Text(order.status == 2 ? '到賬金額' : '應付金額', style: AppTextStyles.headlineSmall(context).copyWith(color: context.colors.onSurfaceVariant)),
Text('${order.receivableAmount} USDT', style: AppTextStyles.headlineMedium(context).copyWith(color: context.colors.onSurface)),
],
),

View File

@@ -97,19 +97,27 @@ class _TradePageState extends State<TradePage>
if (_tradeType == 0) {
return _availableUsdt;
} else {
// 賣出qty * price 截斷到2位
final qty = double.tryParse(_availableCoinQty) ?? 0;
return (qty * price).toStringAsFixed(2);
return ((qty * price * 100).truncateToDouble() / 100).toStringAsFixed(2);
}
}
/// 計算數量
/// 計算數量向下截斷到4位小數確保 price * quantity <= amount
String get _calculatedQuantity {
final amount = double.tryParse(_amountController.text) ?? 0;
final price = _selectedCoin?.price ?? 0;
if (price <= 0 || amount <= 0) return '0';
// 向下截斷到4位小數避免回算超出金額
final qty = amount / price;
return ((qty * 10000).truncateToDouble() / 10000).toStringAsFixed(4);
// 使用與後端一致的截斷邏輯先算原始數量截斷到4位再回算金額確保不超
final rawQty = amount / price;
final truncatedQty = (rawQty * 10000).truncateToDouble() / 10000;
// 回算roundedPrice * truncatedQty確保不超過 amount
final roundedPrice = (price * 100).truncateToDouble() / 100;
if (roundedPrice * truncatedQty > amount) {
// 回退一個最小單位0.0001
return (truncatedQty - 0.0001).toStringAsFixed(4);
}
return truncatedQty.toStringAsFixed(4);
}
@override
@@ -208,11 +216,16 @@ class _TradePageState extends State<TradePage>
void _fillPercent(double pct) {
final max = double.tryParse(_maxAmount) ?? 0;
final value = max * pct;
// 向下截斷到2位小數避免四捨五入超出可用餘額
// 向下截斷到2位小數再減0.01作為安全緩衝,避免精度問題導致餘額不足
final truncated = ((value * 100).truncateToDouble() / 100);
final safe = truncated > 0.01 ? truncated - 0.01 : truncated;
_amountController.text = safe.toStringAsFixed(2);
if (_tradeType == 0) {
// 買入向下截斷到2位小數
_amountController.text =
((value * 100).truncateToDouble() / 100).toStringAsFixed(2);
} else {
// 賣出_maxAmount 已是 qty*price 的四捨五入值,直接截斷
_amountController.text =
((value * 100).truncateToDouble() / 100).toStringAsFixed(2);
}
setState(() {});
}