diff --git a/frontend/mining-app/lib/core/utils/format_utils.dart b/frontend/mining-app/lib/core/utils/format_utils.dart index 9c4152da..8962369f 100644 --- a/frontend/mining-app/lib/core/utils/format_utils.dart +++ b/frontend/mining-app/lib/core/utils/format_utils.dart @@ -44,6 +44,94 @@ String formatWithCommas(String? value) { } } +/// 将小数点向左移动 [positions] 位(纯字符串操作,不丢精度) +/// e.g., _shiftDecimalLeft("9813194959.8105", 8) → "98.131949598105" +String _shiftDecimalLeft(String numStr, int positions) { + String sign = ''; + String v = numStr.trim(); + if (v.startsWith('-')) { + sign = '-'; + v = v.substring(1); + } + String intPart, decPart; + final dotIndex = v.indexOf('.'); + if (dotIndex >= 0) { + intPart = v.substring(0, dotIndex); + decPart = v.substring(dotIndex + 1); + } else { + intPart = v; + decPart = ''; + } + String digits = intPart + decPart; + int newDecPos = intPart.length - positions; + while (newDecPos <= 0) { + digits = '0$digits'; + newDecPos++; + } + final newInt = digits.substring(0, newDecPos); + var newDec = digits.substring(newDecPos); + newDec = newDec.replaceFirst(RegExp(r'0+$'), ''); + if (newDec.isEmpty) return '$sign$newInt'; + return '$sign$newInt.$newDec'; +} + +/// 整数部分加逗号千位分隔 + 保留原始小数位(纯字符串操作,不丢精度) +String _addCommasFullPrecision(String numStr) { + String sign = ''; + String v = numStr.trim(); + if (v.startsWith('-')) { + sign = '-'; + v = v.substring(1); + } + String intPart, decPart; + final dotIndex = v.indexOf('.'); + if (dotIndex >= 0) { + intPart = v.substring(0, dotIndex); + decPart = v.substring(dotIndex); // 包含小数点 + } else { + intPart = v; + decPart = ''; + } + final formatted = NumberFormat('#,##0', 'zh_CN').format(int.parse(intPart)); + if (decPart.isNotEmpty) { + decPart = decPart.replaceFirst(RegExp(r'0+$'), ''); + if (decPart == '.') decPart = ''; + } + return '$sign$formatted$decPart'; +} + +/// 与 formatCompact 相同的万/亿缩写,但使用字符串小数点移位保留后端返回的全部精度 +String formatCompactFull(String? value) { + if (value == null || value.isEmpty) return '0'; + try { + final d = Decimal.parse(value); + final abs = d.abs(); + if (abs >= Decimal.parse('1000000000000')) { + return '${_shiftDecimalLeft(value, 12)}万亿'; + } + if (abs >= Decimal.parse('100000000')) { + return '${_shiftDecimalLeft(value, 8)}亿'; + } + if (abs >= Decimal.parse('10000')) { + return '${_shiftDecimalLeft(value, 4)}万'; + } + return _addCommasFullPrecision(value); + } catch (e) { + return '0'; + } +} + +/// 原始数值 + 逗号千位分隔,保留后端返回的全部小数位(不受 double 精度限制) +String formatWithCommasFull(String? value) { + if (value == null || value.isEmpty) return '0'; + try { + Decimal.parse(value); // validate + return _addCommasFullPrecision(value); + } catch (e) { + return '0'; + } +} + /// 原始整数 + 逗号千位分隔(不保留小数) String formatIntWithCommas(String? value) { if (value == null || value.isEmpty) return '0'; diff --git a/frontend/mining-app/lib/presentation/pages/contribution/contribution_page.dart b/frontend/mining-app/lib/presentation/pages/contribution/contribution_page.dart index b3f73695..ace44918 100644 --- a/frontend/mining-app/lib/presentation/pages/contribution/contribution_page.dart +++ b/frontend/mining-app/lib/presentation/pages/contribution/contribution_page.dart @@ -231,7 +231,7 @@ class ContributionPage extends ConsumerWidget { hideAmounts ? '******' : market != null - ? formatAmount( + ? formatCompactFull( (Decimal.parse(market.totalShares) - Decimal.parse(market.totalMined) - Decimal.parse(market.blackHoleAmount)) diff --git a/frontend/mining-app/lib/presentation/pages/trading/trading_page.dart b/frontend/mining-app/lib/presentation/pages/trading/trading_page.dart index 1d93fd97..00f1cd44 100644 --- a/frontend/mining-app/lib/presentation/pages/trading/trading_page.dart +++ b/frontend/mining-app/lib/presentation/pages/trading/trading_page.dart @@ -360,12 +360,11 @@ class _TradingPageState extends ConsumerState { _buildMarketDataItem( '剩余积分股', market != null - ? formatCompact( + ? formatCompactFull( (Decimal.parse(market.totalShares) - Decimal.parse(market.totalMined) - Decimal.parse(market.blackHoleAmount)) - .toString(), - precision: 4) + .toString()) : null, _orange, isLoading, @@ -376,7 +375,7 @@ class _TradingPageState extends ConsumerState { // 而非交易流通池(circulationPool,目前无交易故为0) _buildMarketDataItem( '已分配积分股', - market != null ? formatCompact(market.totalMined) : null, + market != null ? formatCompactFull(market.totalMined) : null, _orange, isLoading, ), @@ -389,7 +388,7 @@ class _TradingPageState extends ConsumerState { children: [ _buildMarketDataItem( '积分股池', - market != null ? formatWithCommas(market.greenPoints) : null, + market != null ? formatWithCommasFull(market.greenPoints) : null, _orange, isLoading, ),