feat: 统一USDC金额格式化,添加formatUSDC工具函数
- 新增 formatUSDC 函数:最多显示4位小数,自动去除尾随零(截断,不四舍五入) - 统一导出所有工具函数到 utils/index.ts - 替换所有页面中的USDC显示为 formatUSDC 函数 - TemplateList.tsx - CopyTradingAdd.tsx - PositionList.tsx - AccountList.tsx - AccountDetail.tsx - OrderList.tsx - Statistics.tsx - 更新前端开发规范,添加USDC格式化规范说明 - 统一工具函数导入方式(从 ../utils 导入)
This commit is contained in:
@@ -176,6 +176,80 @@ export const useMarketStore = create<MarketStore>((set) => ({
|
||||
}));
|
||||
```
|
||||
|
||||
### USDC 金额格式化规范
|
||||
- **必须**使用 `formatUSDC` 函数格式化所有 USDC 金额显示
|
||||
- **禁止**直接使用 `toFixed()` 或 `parseFloat().toFixed()` 格式化 USDC
|
||||
- **禁止**硬编码小数位数格式化 USDC
|
||||
- 所有 USDC 金额显示必须统一使用 `formatUSDC` 函数
|
||||
|
||||
#### formatUSDC 函数说明
|
||||
- **位置**: `src/utils/index.ts`
|
||||
- **功能**: 格式化 USDC 金额,最多显示 4 位小数,自动去除尾随零(截断,不四舍五入)
|
||||
- **参数**: `value: string | number | undefined | null`
|
||||
- **返回值**: 格式化后的字符串,如果值为空或无效则返回 `'-'`
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:使用 formatUSDC 格式化 USDC 金额
|
||||
import { formatUSDC } from '../utils'
|
||||
|
||||
const BalanceDisplay: React.FC<{ balance: string }> = ({ balance }) => {
|
||||
return <span>{formatUSDC(balance)} USDC</span>
|
||||
}
|
||||
|
||||
// ✅ 正确:在表格列中使用
|
||||
const columns = [
|
||||
{
|
||||
title: '余额',
|
||||
dataIndex: 'balance',
|
||||
render: (balance: string) => `${formatUSDC(balance)} USDC`
|
||||
}
|
||||
]
|
||||
|
||||
// ✅ 正确:在统计组件中使用
|
||||
<Statistic
|
||||
title="总盈亏"
|
||||
value={formatUSDC(stats?.totalPnl || '0')}
|
||||
suffix="USDC"
|
||||
/>
|
||||
|
||||
// ❌ 错误:直接使用 toFixed
|
||||
const balance = parseFloat(value).toFixed(4) // 禁止
|
||||
|
||||
// ❌ 错误:硬编码格式化
|
||||
const balance = `${parseFloat(value).toFixed(2)} USDC` // 禁止
|
||||
|
||||
// ❌ 错误:使用自定义格式化函数
|
||||
const formatBalance = (value: string) => parseFloat(value).toFixed(4) // 禁止
|
||||
```
|
||||
|
||||
#### 格式化规则
|
||||
- **最多显示 4 位小数**:如果金额超过 4 位小数,截断到 4 位(不四舍五入)
|
||||
- **自动去除尾随零**:去除不必要的尾随零和小数点
|
||||
- **示例**:
|
||||
- `formatUSDC(1.23)` => `"1.23"`
|
||||
- `formatUSDC(1.23456)` => `"1.2345"`(截断,不四舍五入)
|
||||
- `formatUSDC(1.2)` => `"1.2"`
|
||||
- `formatUSDC(1)` => `"1"`
|
||||
- `formatUSDC(1.23459)` => `"1.2345"`(截断,不四舍五入)
|
||||
- `formatUSDC(null)` => `"-"`
|
||||
- `formatUSDC(undefined)` => `"-"`
|
||||
|
||||
#### 工具函数统一导出
|
||||
- 所有工具函数统一从 `src/utils/index.ts` 导出
|
||||
- **必须**从 `../utils` 导入工具函数,而不是从具体文件导入
|
||||
- `ethers.ts` 中的函数也会在 `index.ts` 中统一导出
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:从 utils 统一导入
|
||||
import { formatUSDC, getAddressFromPrivateKey, isValidWalletAddress } from '../utils'
|
||||
|
||||
// ❌ 错误:从具体文件导入
|
||||
import { formatUSDC } from '../utils/index'
|
||||
import { getAddressFromPrivateKey } from '../utils/ethers'
|
||||
```
|
||||
|
||||
## 移动端适配示例
|
||||
|
||||
### 响应式布局
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ArrowLeftOutlined, ReloadOutlined, EditOutlined } from '@ant-design/ico
|
||||
import { useAccountStore } from '../store/accountStore'
|
||||
import type { Account } from '../types'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { formatUSDC } from '../utils'
|
||||
|
||||
const { Title } = Typography
|
||||
|
||||
@@ -212,7 +213,7 @@ const AccountDetail: React.FC = () => {
|
||||
<Spin size="small" />
|
||||
) : balance ? (
|
||||
<span style={{ fontWeight: 'bold', color: '#1890ff' }}>
|
||||
{balance} USDC
|
||||
{formatUSDC(balance)} USDC
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ color: '#999' }}>-</span>
|
||||
@@ -307,7 +308,7 @@ const AccountDetail: React.FC = () => {
|
||||
fontWeight: 'bold',
|
||||
color: account.totalPnl.startsWith('-') ? '#ff4d4f' : '#52c41a'
|
||||
}}>
|
||||
{account.totalPnl} USDC
|
||||
{formatUSDC(account.totalPnl)} USDC
|
||||
</span>
|
||||
</Descriptions.Item>
|
||||
)}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
isValidWalletAddress,
|
||||
isValidPrivateKey,
|
||||
isValidMnemonic
|
||||
} from '../utils/ethers'
|
||||
} from '../utils'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
|
||||
const { Title } = Typography
|
||||
|
||||
@@ -5,6 +5,7 @@ import { PlusOutlined, StarOutlined, StarFilled, ReloadOutlined, EditOutlined }
|
||||
import { useAccountStore } from '../store/accountStore'
|
||||
import type { Account } from '../types'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { formatUSDC } from '../utils'
|
||||
|
||||
const { Title } = Typography
|
||||
|
||||
@@ -261,7 +262,7 @@ const AccountList: React.FC = () => {
|
||||
}
|
||||
const balanceObj = balanceMap[record.id]
|
||||
const balance = balanceObj?.total || record.balance || '-'
|
||||
return balance && balance !== '-' && typeof balance === 'string' ? `${balance} USDC` : '-'
|
||||
return balance && balance !== '-' && typeof balance === 'string' ? `${formatUSDC(balance)} USDC` : '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -359,7 +360,7 @@ const AccountList: React.FC = () => {
|
||||
总余额: {balanceLoading[record.id] ? (
|
||||
<Spin size="small" style={{ marginLeft: '4px' }} />
|
||||
) : balanceMap[record.id]?.total && balanceMap[record.id].total !== '-' ? (
|
||||
`${balanceMap[record.id].total} USDC`
|
||||
`${formatUSDC(balanceMap[record.id].total)} USDC`
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
@@ -370,7 +371,7 @@ const AccountList: React.FC = () => {
|
||||
color: '#666',
|
||||
marginTop: '4px'
|
||||
}}>
|
||||
可用: {balanceMap[record.id].available} USDC | 仓位: {balanceMap[record.id].position} USDC
|
||||
可用: {formatUSDC(balanceMap[record.id].available)} USDC | 仓位: {formatUSDC(balanceMap[record.id].position)} USDC
|
||||
</div>
|
||||
)}
|
||||
{(record.activeOrders !== undefined && record.activeOrders !== null) && (
|
||||
@@ -597,7 +598,7 @@ const AccountList: React.FC = () => {
|
||||
<Spin size="small" />
|
||||
) : detailBalance ? (
|
||||
<span style={{ fontWeight: 'bold', color: '#1890ff', fontSize: '16px' }}>
|
||||
{detailBalance.total} USDC
|
||||
{formatUSDC(detailBalance.total)} USDC
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ color: '#999' }}>-</span>
|
||||
@@ -608,7 +609,7 @@ const AccountList: React.FC = () => {
|
||||
<Spin size="small" />
|
||||
) : detailBalance ? (
|
||||
<span style={{ color: '#52c41a' }}>
|
||||
{detailBalance.available} USDC
|
||||
{formatUSDC(detailBalance.available)} USDC
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ color: '#999' }}>-</span>
|
||||
@@ -619,7 +620,7 @@ const AccountList: React.FC = () => {
|
||||
<Spin size="small" />
|
||||
) : detailBalance ? (
|
||||
<span style={{ color: '#1890ff' }}>
|
||||
{detailBalance.position} USDC
|
||||
{formatUSDC(detailBalance.position)} USDC
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ color: '#999' }}>-</span>
|
||||
@@ -696,7 +697,7 @@ const AccountList: React.FC = () => {
|
||||
fontWeight: 'bold',
|
||||
color: detailAccount.totalPnl && detailAccount.totalPnl.startsWith('-') ? '#ff4d4f' : '#52c41a'
|
||||
}}>
|
||||
{detailAccount.totalPnl} USDC
|
||||
{formatUSDC(detailAccount.totalPnl)} USDC
|
||||
</span>
|
||||
</Descriptions.Item>
|
||||
)}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { apiService } from '../services/api'
|
||||
import { useAccountStore } from '../store/accountStore'
|
||||
import type { Account, Leader, CopyTradingTemplate } from '../types'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { formatUSDC } from '../utils'
|
||||
|
||||
const { Title } = Typography
|
||||
const { Option } = Select
|
||||
@@ -114,7 +115,7 @@ const CopyTradingAdd: React.FC = () => {
|
||||
<Select placeholder="请选择模板">
|
||||
{templates.map(template => (
|
||||
<Option key={template.id} value={template.id}>
|
||||
{template.templateName} ({template.copyMode === 'RATIO' ? `比例 ${template.copyRatio}x` : `固定 ${template.fixedAmount ? parseFloat(template.fixedAmount).toFixed(4) : '0.0000'} USDC`})
|
||||
{template.templateName} ({template.copyMode === 'RATIO' ? `比例 ${template.copyRatio}x` : `固定 ${template.fixedAmount ? formatUSDC(template.fixedAmount) : '0.0000'} USDC`})
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Card, Table, Tag, message } from 'antd'
|
||||
import { apiService } from '../services/api'
|
||||
import type { CopyOrder } from '../types'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { formatUSDC } from '../utils'
|
||||
|
||||
const OrderList: React.FC = () => {
|
||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||
@@ -115,7 +116,7 @@ const OrderList: React.FC = () => {
|
||||
key: 'pnl',
|
||||
render: (pnl: string | undefined) => pnl ? (
|
||||
<span style={{ color: pnl.startsWith('-') ? 'red' : 'green' }}>
|
||||
{pnl} USDC
|
||||
{formatUSDC(pnl)} USDC
|
||||
</span>
|
||||
) : '-'
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import { getPositionKey } from '../types'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { useWebSocketSubscription } from '../hooks/useWebSocket'
|
||||
import { wsManager } from '../services/websocket'
|
||||
import { formatUSDC } from '../utils'
|
||||
|
||||
type PositionFilter = 'current' | 'historical'
|
||||
type ViewMode = 'card' | 'list'
|
||||
@@ -611,7 +612,7 @@ const PositionList: React.FC = () => {
|
||||
fontWeight: '500',
|
||||
color: isProfit ? '#52c41a' : '#f5222d'
|
||||
}}>
|
||||
{pnlNum >= 0 ? '+' : ''}{formatNumber(position.pnl, 2)} USDC
|
||||
{pnlNum >= 0 ? '+' : ''}{formatUSDC(position.pnl)} USDC
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -634,7 +635,7 @@ const PositionList: React.FC = () => {
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>
|
||||
<span style={{ fontSize: '13px', color: '#666' }}>开仓价值</span>
|
||||
<span style={{ fontSize: '13px', fontWeight: '500' }}>
|
||||
{formatNumber(position.initialValue, 2)} USDC
|
||||
{formatUSDC(position.initialValue)} USDC
|
||||
</span>
|
||||
</div>
|
||||
{positionFilter === 'current' && position.currentPrice && (
|
||||
@@ -648,7 +649,7 @@ const PositionList: React.FC = () => {
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>
|
||||
<span style={{ fontSize: '13px', color: '#666' }}>当前价值</span>
|
||||
<span style={{ fontSize: '13px', fontWeight: '600' }}>
|
||||
{formatNumber(position.currentValue, 2)} USDC
|
||||
{formatUSDC(position.currentValue)} USDC
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
@@ -691,7 +692,7 @@ const PositionList: React.FC = () => {
|
||||
fontWeight: 'bold',
|
||||
color: isProfit ? '#52c41a' : '#f5222d'
|
||||
}}>
|
||||
{pnlNum >= 0 ? '+' : ''}{formatNumber(position.pnl, 2)} USDC
|
||||
{pnlNum >= 0 ? '+' : ''}{formatUSDC(position.pnl)} USDC
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
@@ -718,7 +719,7 @@ const PositionList: React.FC = () => {
|
||||
color: parseFloat(position.realizedPnl) >= 0 ? '#52c41a' : '#f5222d',
|
||||
fontWeight: '500'
|
||||
}}>
|
||||
{parseFloat(position.realizedPnl) >= 0 ? '+' : ''}{formatNumber(position.realizedPnl, 2)} USDC
|
||||
{parseFloat(position.realizedPnl) >= 0 ? '+' : ''}{formatUSDC(position.realizedPnl)} USDC
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -872,7 +873,7 @@ const PositionList: React.FC = () => {
|
||||
key: 'initialValue',
|
||||
render: (value: string) => (
|
||||
<span>
|
||||
{formatNumber(value, 2)} USDC
|
||||
{formatUSDC(value)} USDC
|
||||
</span>
|
||||
),
|
||||
align: 'right' as const,
|
||||
@@ -897,7 +898,7 @@ const PositionList: React.FC = () => {
|
||||
key: 'currentValue',
|
||||
render: (value: string) => (
|
||||
<span style={{ fontWeight: 'bold' }}>
|
||||
{formatNumber(value, 2)} USDC
|
||||
{formatUSDC(value)} USDC
|
||||
</span>
|
||||
),
|
||||
align: 'right' as const,
|
||||
@@ -928,7 +929,7 @@ const PositionList: React.FC = () => {
|
||||
color: pnlNum >= 0 ? '#3f8600' : '#cf1322',
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
{pnlNum >= 0 ? '+' : ''}{formatNumber(pnl, 2)} USDC
|
||||
{pnlNum >= 0 ? '+' : ''}{formatUSDC(pnl)} USDC
|
||||
</div>
|
||||
<div style={{
|
||||
fontSize: '12px',
|
||||
@@ -961,7 +962,7 @@ const PositionList: React.FC = () => {
|
||||
color: pnlNum >= 0 ? '#3f8600' : '#cf1322',
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
{pnlNum >= 0 ? '+' : ''}{formatNumber(realizedPnl, 2)} USDC
|
||||
{pnlNum >= 0 ? '+' : ''}{formatUSDC(realizedPnl)} USDC
|
||||
</div>
|
||||
{record.percentRealizedPnl && (
|
||||
<div style={{
|
||||
@@ -1191,7 +1192,7 @@ const PositionList: React.FC = () => {
|
||||
borderColor: '#52c41a'
|
||||
}}
|
||||
>
|
||||
赎回 ({redeemableSummary.totalCount}个, {formatNumber(redeemableSummary.totalValue, 2)} USDC)
|
||||
赎回 ({redeemableSummary.totalCount}个, {formatUSDC(redeemableSummary.totalValue)} USDC)
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -1214,14 +1215,14 @@ const PositionList: React.FC = () => {
|
||||
<span>
|
||||
开仓价值合计:{' '}
|
||||
<span style={{ fontWeight: 600 }}>
|
||||
{formatNumber(positionTotals.totalInitialValue.toString(), 2)} USDC
|
||||
{formatUSDC(positionTotals.totalInitialValue.toString())} USDC
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
当前价值合计:{' '}
|
||||
<span style={{ fontWeight: 600 }}>
|
||||
{positionFilter === 'current'
|
||||
? `${formatNumber(positionTotals.totalCurrentValue.toString(), 2)} USDC`
|
||||
? `${formatUSDC(positionTotals.totalCurrentValue.toString())} USDC`
|
||||
: '-'}
|
||||
</span>
|
||||
</span>
|
||||
@@ -1234,7 +1235,7 @@ const PositionList: React.FC = () => {
|
||||
}}
|
||||
>
|
||||
{positionTotals.totalPnl >= 0 ? '+' : ''}
|
||||
{formatNumber(positionTotals.totalPnl.toString(), 2)} USDC
|
||||
{formatUSDC(positionTotals.totalPnl.toString())} USDC
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
@@ -1246,7 +1247,7 @@ const PositionList: React.FC = () => {
|
||||
}}
|
||||
>
|
||||
{positionTotals.totalRealizedPnl >= 0 ? '+' : ''}
|
||||
{formatNumber(positionTotals.totalRealizedPnl.toString(), 2)} USDC
|
||||
{formatUSDC(positionTotals.totalRealizedPnl.toString())} USDC
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -1460,7 +1461,7 @@ const PositionList: React.FC = () => {
|
||||
color: currentPnl.pnl >= 0 ? '#52c41a' : '#f5222d',
|
||||
marginBottom: '4px'
|
||||
}}>
|
||||
{currentPnl.pnl >= 0 ? '+' : ''}{currentPnl.pnl.toFixed(2)} USDC
|
||||
{currentPnl.pnl >= 0 ? '+' : ''}{formatUSDC(currentPnl.pnl)} USDC
|
||||
</div>
|
||||
<div style={{
|
||||
fontSize: '14px',
|
||||
@@ -1500,7 +1501,7 @@ const PositionList: React.FC = () => {
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="可赎回总价值">
|
||||
<span style={{ fontSize: '18px', fontWeight: 'bold', color: '#52c41a' }}>
|
||||
{formatNumber(redeemableSummary.totalValue, 2)} USDC
|
||||
{formatUSDC(redeemableSummary.totalValue)} USDC
|
||||
</span>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="涉及账户">
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Card, Row, Col, Statistic, message } from 'antd'
|
||||
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons'
|
||||
import { apiService } from '../services/api'
|
||||
import type { Statistics as StatisticsType } from '../types'
|
||||
import { formatUSDC } from '../utils'
|
||||
|
||||
const Statistics: React.FC = () => {
|
||||
const [stats, setStats] = useState<StatisticsType | null>(null)
|
||||
@@ -48,8 +49,7 @@ const Statistics: React.FC = () => {
|
||||
<Card>
|
||||
<Statistic
|
||||
title="总盈亏"
|
||||
value={stats?.totalPnl || '0'}
|
||||
precision={2}
|
||||
value={formatUSDC(stats?.totalPnl || '0')}
|
||||
prefix={stats?.totalPnl && parseFloat(stats.totalPnl) >= 0 ? <ArrowUpOutlined /> : <ArrowDownOutlined />}
|
||||
valueStyle={{ color: stats?.totalPnl && parseFloat(stats.totalPnl || '0') >= 0 ? '#3f8600' : '#cf1322' }}
|
||||
suffix="USDC"
|
||||
@@ -72,8 +72,7 @@ const Statistics: React.FC = () => {
|
||||
<Card>
|
||||
<Statistic
|
||||
title="平均盈亏"
|
||||
value={stats?.avgPnl || '0'}
|
||||
precision={2}
|
||||
value={formatUSDC(stats?.avgPnl || '0')}
|
||||
suffix="USDC"
|
||||
loading={loading}
|
||||
/>
|
||||
@@ -83,8 +82,7 @@ const Statistics: React.FC = () => {
|
||||
<Card>
|
||||
<Statistic
|
||||
title="最大盈利"
|
||||
value={stats?.maxProfit || '0'}
|
||||
precision={2}
|
||||
value={formatUSDC(stats?.maxProfit || '0')}
|
||||
prefix={<ArrowUpOutlined />}
|
||||
valueStyle={{ color: '#3f8600' }}
|
||||
suffix="USDC"
|
||||
@@ -96,8 +94,7 @@ const Statistics: React.FC = () => {
|
||||
<Card>
|
||||
<Statistic
|
||||
title="最大亏损"
|
||||
value={stats?.maxLoss || '0'}
|
||||
precision={2}
|
||||
value={formatUSDC(stats?.maxLoss || '0')}
|
||||
prefix={<ArrowDownOutlined />}
|
||||
valueStyle={{ color: '#cf1322' }}
|
||||
suffix="USDC"
|
||||
|
||||
@@ -5,6 +5,7 @@ import { PlusOutlined, EditOutlined, DeleteOutlined, CopyOutlined } from '@ant-d
|
||||
import { apiService } from '../services/api'
|
||||
import type { CopyTradingTemplate } from '../types'
|
||||
import { useMediaQuery } from 'react-responsive'
|
||||
import { formatUSDC } from '../utils'
|
||||
|
||||
const { Search } = Input
|
||||
|
||||
@@ -163,7 +164,7 @@ const TemplateList: React.FC = () => {
|
||||
if (record.copyMode === 'RATIO') {
|
||||
return `比例 ${record.copyRatio}x`
|
||||
} else if (record.copyMode === 'FIXED' && record.fixedAmount) {
|
||||
return `固定 ${parseFloat(record.fixedAmount).toFixed(4)} USDC`
|
||||
return `固定 ${formatUSDC(record.fixedAmount)} USDC`
|
||||
}
|
||||
return '-'
|
||||
}
|
||||
@@ -331,7 +332,7 @@ const TemplateList: React.FC = () => {
|
||||
{template.copyMode === 'RATIO'
|
||||
? `比例 ${template.copyRatio}x`
|
||||
: template.fixedAmount
|
||||
? `固定 ${parseFloat(template.fixedAmount).toFixed(4)} USDC`
|
||||
? `固定 ${formatUSDC(template.fixedAmount)} USDC`
|
||||
: '-'
|
||||
}
|
||||
</div>
|
||||
@@ -343,11 +344,11 @@ const TemplateList: React.FC = () => {
|
||||
<div style={{ fontSize: '12px', color: '#666', marginBottom: '4px' }}>金额限制</div>
|
||||
<div style={{ fontSize: '13px', color: '#333' }}>
|
||||
{template.maxOrderSize && (
|
||||
<span>最大: {parseFloat(template.maxOrderSize).toFixed(4)} USDC</span>
|
||||
<span>最大: {formatUSDC(template.maxOrderSize)} USDC</span>
|
||||
)}
|
||||
{template.maxOrderSize && template.minOrderSize && <span> | </span>}
|
||||
{template.minOrderSize && (
|
||||
<span>最小: {parseFloat(template.minOrderSize).toFixed(4)} USDC</span>
|
||||
<span>最小: {formatUSDC(template.minOrderSize)} USDC</span>
|
||||
)}
|
||||
{!template.maxOrderSize && !template.minOrderSize && <span style={{ color: '#999' }}>未设置</span>}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 格式化 USDC 金额
|
||||
* 最多显示 4 位小数,自动去除尾随零(截断,不四舍五入)
|
||||
* @param value - 金额值(字符串或数字)
|
||||
* @returns 格式化后的字符串,如果值为空或无效则返回 '-'
|
||||
* @example
|
||||
* formatUSDC(1.23) => "1.23"
|
||||
* formatUSDC(1.23456) => "1.2345"
|
||||
* formatUSDC(1.2) => "1.2"
|
||||
* formatUSDC(1) => "1"
|
||||
*/
|
||||
export const formatUSDC = (value: string | number | undefined | null): string => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return '-'
|
||||
}
|
||||
|
||||
const num = typeof value === 'string' ? parseFloat(value) : value
|
||||
if (isNaN(num)) {
|
||||
return '-'
|
||||
}
|
||||
|
||||
// 使用 Math.floor 截断到4位小数(不四舍五入)
|
||||
const multiplier = Math.pow(10, 4)
|
||||
const truncated = Math.floor(num * multiplier) / multiplier
|
||||
|
||||
// 使用 toFixed(4) 确保格式一致,然后去除尾随零和小数点
|
||||
return truncated.toFixed(4).replace(/\.?0+$/, '')
|
||||
}
|
||||
|
||||
// 统一导出 ethers 相关工具函数
|
||||
export {
|
||||
getAddressFromPrivateKey,
|
||||
getAddressFromMnemonic,
|
||||
getPrivateKeyFromMnemonic,
|
||||
isValidMnemonic,
|
||||
isValidWalletAddress,
|
||||
isValidPrivateKey
|
||||
} from './ethers'
|
||||
|
||||
Reference in New Issue
Block a user