Просмотр исходного кода

fix(i18n): 修复令牌和充值页面货币显示不一致问题

- 令牌创建:快捷选项标签根据系统货币设置动态生成,替换硬编码美元
- 充值页面:输入框显示值与预设卡片统一换算为本地货币,内部值仍为原始单位
- 使用 getQuotaPerUnit() 替代魔数 500000
- 消除 RechargeCard 中 getCurrencyConfig() 的 N+1 重复调用

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
master
fengsilin 2 недель назад
Родитель
Сommit
d24d68a916
2 измененных файлов: 90 добавлений и 94 удалений
  1. +13
    -8
      web/src/components/table/tokens/modals/EditTokenModal.jsx
  2. +77
    -86
      web/src/components/topup/RechargeCard.jsx

+ 13
- 8
web/src/components/table/tokens/modals/EditTokenModal.jsx Просмотреть файл

@@ -27,7 +27,9 @@ import {
renderQuotaWithPrompt,
getModelCategories,
selectFilter,
getCurrencyConfig,
} from '../../../../helpers';
import { getQuotaPerUnit } from '../../../../helpers/quota';
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
import {
Button,
@@ -519,14 +521,17 @@ const EditTokenModal = (props) => {
? []
: [{ required: true, message: t('请输入额度') }]
}
data={[
{ value: 500000, label: '1$' },
{ value: 5000000, label: '10$' },
{ value: 25000000, label: '50$' },
{ value: 50000000, label: '100$' },
{ value: 250000000, label: '500$' },
{ value: 500000000, label: '1000$' },
]}
data={(() => {
const { symbol, rate, type } = getCurrencyConfig();
const unit = getQuotaPerUnit();
return [1, 10, 50, 100, 500, 1000].map((usd) => {
const value = Math.round(usd * unit);
const label = type === 'TOKENS'
? `${Math.round(usd * unit / 1000)}K`
: `${symbol}${Math.round(usd * rate)}`;
return { value, label };
});
})()}
/>
</Col>
<Col span={24}>


+ 77
- 86
web/src/components/topup/RechargeCard.jsx Просмотреть файл

@@ -234,60 +234,70 @@ const RechargeCard = ({
{(enableOnlineTopUp || enableStripeTopUp || enableWechatTopUp) && (
<Row gutter={12}>
<Col xs={24} sm={24} md={24} lg={10} xl={10}>
<Form.InputNumber
field='topUpCount'
label={t('充值数量')}
disabled={!enableOnlineTopUp && !enableStripeTopUp && !enableWechatTopUp}
placeholder={
t('充值数量,最低 ') + renderQuotaWithAmount(minTopUp)
}
value={topUpCount}
min={minTopUp}
max={999999999}
step={1}
precision={0}
onChange={async (value) => {
if (value && value >= 1) {
setTopUpCount(value);
setSelectedPreset(null);
await getAmount(value);
}
}}
onBlur={(e) => {
const value = parseInt(e.target.value);
if (!value || value < 1) {
setTopUpCount(1);
getAmount(1);
}
}}
formatter={(value) => (value ? `${value}` : '')}
parser={(value) =>
value ? parseInt(value.replace(/[^\d]/g, '')) : 0
}
extraText={
<Skeleton
loading={showAmountSkeleton}
active
{(() => {
const { symbol: curSymbol, rate: curRate, type: curType } = getCurrencyConfig();
const needsConvert = curType !== 'USD' && curType !== 'TOKENS';
const displayMin = needsConvert ? Math.ceil(minTopUp * curRate) : minTopUp;
return (
<Form.InputNumber
field='topUpCount'
label={t('充值数量')}
disabled={!enableOnlineTopUp && !enableStripeTopUp && !enableWechatTopUp}
placeholder={
<Skeleton.Title
style={{
width: 120,
height: 20,
borderRadius: 6,
}}
/>
t('充值数量,最低 ') + displayMin
}
>
<Text type='secondary' className='text-red-600'>
{t('实付金额:')}
<span style={{ color: 'red' }}>
{renderAmount()}
</span>
</Text>
</Skeleton>
}
style={{ width: '100%' }}
/>
value={needsConvert ? Math.round(topUpCount * curRate) : topUpCount}
min={displayMin}
max={needsConvert ? Math.round(999999999 * curRate) : 999999999}
step={needsConvert ? curRate : 1}
precision={0}
onChange={async (displayValue) => {
const rawValue = needsConvert ? Math.round(displayValue / curRate) : displayValue;
if (rawValue && rawValue >= 1) {
setTopUpCount(rawValue);
setSelectedPreset(null);
await getAmount(rawValue);
}
}}
onBlur={(e) => {
const displayVal = parseInt(e.target.value);
const rawVal = needsConvert ? Math.round(displayVal / curRate) : displayVal;
if (!rawVal || rawVal < 1) {
setTopUpCount(1);
getAmount(1);
}
}}
formatter={(value) => (value ? `${value}` : '')}
parser={(value) =>
value ? parseInt(value.replace(/[^\d]/g, '')) : 0
}
suffix={curSymbol}
style={{ width: '100%' }}
extraText={
<Skeleton
loading={showAmountSkeleton}
active
placeholder={
<Skeleton.Title
style={{
width: 120,
height: 20,
borderRadius: 6,
}}
/>
}
>
<Text type='secondary' className='text-red-600'>
{t('实付金额:')}
<span style={{ color: 'red' }}>
{renderAmount()}
</span>
</Text>
</Skeleton>
}
/>
);
})()}
</Col>
<Col xs={24} sm={24} md={24} lg={14} xl={14}>
<Form.Slot label={t('选择支付方式')}>
@@ -374,7 +384,6 @@ const RechargeCard = ({
{(() => {
const { symbol, rate, type } = getCurrencyConfig();
if (type === 'USD') return null;

return (
<span
style={{
@@ -390,6 +399,11 @@ const RechargeCard = ({
</div>
}
>
{(() => {
const { symbol: pSymbol, rate: pRate, type: pType } = getCurrencyConfig();
const needsConvert = pType !== 'USD' && pType !== 'TOKENS';

return (
<div className='grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2'>
{presetAmounts.map((preset, index) => {
const discount =
@@ -400,34 +414,9 @@ const RechargeCard = ({
const actualPay = discountedPrice;
const save = originalPrice - discountedPrice;

// 根据当前货币类型换算显示金额和数量
const { symbol, rate, type } = getCurrencyConfig();
const statusStr = localStorage.getItem('status');
let usdRate = 7; // 默认CNY汇率
try {
if (statusStr) {
const s = JSON.parse(statusStr);
usdRate = s?.usd_exchange_rate || 7;
}
} catch (e) { }

let displayValue = preset.value; // 显示的数量
let displayActualPay = actualPay;
let displaySave = save;

if (type === 'USD') {
// 数量保持USD,价格从CNY转USD
displayActualPay = actualPay / usdRate;
displaySave = save / usdRate;
} else if (type === 'CNY') {
// 数量转CNY,价格已是CNY
displayValue = preset.value * usdRate;
} else if (type === 'CUSTOM') {
// 数量和价格都转自定义货币
displayValue = preset.value * rate;
displayActualPay = (actualPay / usdRate) * rate;
displaySave = (save / usdRate) * rate;
}
const displayValue = needsConvert ? preset.value * pRate : preset.value;
const displayActualPay = pType === 'USD' ? actualPay / pRate : actualPay;
const displaySave = pType === 'USD' ? save / pRate : save;

return (
<Card
@@ -446,7 +435,7 @@ const RechargeCard = ({
selectPresetAmount(preset);
onlineFormApiRef.current?.setValue(
'topUpCount',
preset.value,
needsConvert ? Math.round(preset.value * pRate) : preset.value,
);
}}
>
@@ -456,7 +445,7 @@ const RechargeCard = ({
style={{ margin: '0 0 8px 0' }}
>
<Coins size={18} />
{formatLargeNumber(displayValue)} {symbol}
{formatLargeNumber(displayValue)} {pSymbol}
{hasDiscount && (
<Tag style={{ marginLeft: 4 }} color='green'>
{t('折').includes('off')
@@ -473,17 +462,19 @@ const RechargeCard = ({
margin: '4px 0',
}}
>
{t('实付')} {symbol}
{t('实付')} {pSymbol}
{displayActualPay.toFixed(2)},
{hasDiscount
? `${t('节省')} ${symbol}${displaySave.toFixed(2)}`
: `${t('节省')} ${symbol}0.00`}
? `${t('节省')} ${pSymbol}${displaySave.toFixed(2)}`
: `${t('节省')} ${pSymbol}0.00`}
</div>
</div>
</Card>
);
})}
</div>
);
})()}
</Form.Slot>
)}



Загрузка…
Отмена
Сохранить