feat: Extract quota conversion helpers to shared utils

Move quota display/conversion helpers into web/src/helpers/quota.js and update the subscription plan editor to import and use the shared utilities instead of inline functions.
This commit is contained in:
t0ng7u
2026-02-01 15:40:26 +08:00
parent f0e60df96e
commit de90e11cdf
5 changed files with 59 additions and 35 deletions

View File

@@ -27,8 +27,9 @@ import {
Popover,
Divider,
Badge,
Tooltip,
} from '@douyinfe/semi-ui';
import { IconEdit, IconStop, IconPlay } from '@douyinfe/semi-icons';
import { renderQuota } from '../../../helpers';
import { convertUSDToCurrency } from '../../../helpers/render';
const { Text } = Typography;
@@ -81,7 +82,13 @@ const renderPlanTitle = (text, record, t) => {
{convertUSDToCurrency(Number(plan?.price_amount || 0), 2)}
</Text>
<Text type='tertiary'>{t('总额度')}</Text>
<Text>{plan?.total_amount > 0 ? plan.total_amount : t('不限')}</Text>
{plan?.total_amount > 0 ? (
<Tooltip content={`${t('原生额度')}${plan.total_amount}`}>
<Text>{renderQuota(plan.total_amount)}</Text>
</Tooltip>
) : (
<Text>{t('不限')}</Text>
)}
<Text type='tertiary'>{t('升级分组')}</Text>
<Text>{plan?.upgrade_group ? plan.upgrade_group : t('不升级')}</Text>
<Text type='tertiary'>{t('购买上限')}</Text>
@@ -165,7 +172,13 @@ const renderTotalAmount = (text, record, t) => {
const total = Number(record?.plan?.total_amount || 0);
return (
<Text type={total > 0 ? 'secondary' : 'tertiary'}>
{total > 0 ? total : t('不限')}
{total > 0 ? (
<Tooltip content={`${t('原生额度')}${total}`}>
<span>{renderQuota(total)}</span>
</Tooltip>
) : (
t('不限')
)}
</Text>
);
};
@@ -236,30 +249,22 @@ const renderOperations = (text, record, { openEdit, setPlanEnabled, t }) => {
return (
<Space spacing={8}>
<Button
theme='borderless'
theme='light'
type='tertiary'
size='small'
icon={<IconEdit />}
onClick={() => openEdit(record)}
>
{t('编辑')}
</Button>
{isEnabled ? (
<Button
theme='borderless'
type='danger'
size='small'
icon={<IconStop />}
onClick={handleToggle}
>
<Button theme='light' type='danger' size='small' onClick={handleToggle}>
{t('禁用')}
</Button>
) : (
<Button
theme='borderless'
theme='light'
type='primary'
size='small'
icon={<IconPlay />}
onClick={handleToggle}
>
{t('启用')}

View File

@@ -39,12 +39,11 @@ import {
IconSave,
} from '@douyinfe/semi-icons';
import { Clock, RefreshCw } from 'lucide-react';
import { API, showError, showSuccess } from '../../../../helpers';
import {
API,
showError,
showSuccess,
renderQuotaWithPrompt,
} from '../../../../helpers';
quotaToDisplayAmount,
displayAmountToQuota,
} from '../../../../helpers/quota';
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
const { Text, Title } = Typography;
@@ -118,7 +117,9 @@ const AddEditSubscriptionModal = ({
enabled: p.enabled !== false,
sort_order: Number(p.sort_order || 0),
max_purchase_per_user: Number(p.max_purchase_per_user || 0),
total_amount: Number(p.total_amount || 0),
total_amount: Number(
quotaToDisplayAmount(p.total_amount || 0).toFixed(2),
),
upgrade_group: p.upgrade_group || '',
stripe_price_id: p.stripe_price_id || '',
creem_product_id: p.creem_product_id || '',
@@ -161,7 +162,7 @@ const AddEditSubscriptionModal = ({
: 0,
sort_order: Number(values.sort_order || 0),
max_purchase_per_user: Number(values.max_purchase_per_user || 0),
total_amount: Number(values.total_amount || 0),
total_amount: displayAmountToQuota(values.total_amount),
upgrade_group: values.upgrade_group || '',
},
};
@@ -307,23 +308,16 @@ const AddEditSubscriptionModal = ({
</Col>
<Col span={12}>
<Form.AutoComplete
<Form.InputNumber
field='total_amount'
label={t('总额度')}
required
type='number'
min={0}
precision={2}
rules={[{ required: true, message: t('请输入总额度') }]}
extraText={`${t('0 表示不限')} · ${renderQuotaWithPrompt(
Number(values.total_amount || 0),
extraText={`${t('0 表示不限')} · ${t('原生额度')}${displayAmountToQuota(
values.total_amount,
)}`}
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' },
]}
style={{ width: '100%' }}
/>
</Col>

View File

@@ -359,7 +359,8 @@ const SubscriptionPlansCard = ({
</div>
<Button
size='small'
theme='borderless'
theme='light'
type='tertiary'
icon={
<RefreshCw
size={12}

View File

@@ -115,7 +115,6 @@ const SubscriptionPurchaseModal = ({
visible={visible}
onCancel={onCancel}
footer={null}
maskClosable={false}
size='small'
centered
>

25
web/src/helpers/quota.js Normal file
View File

@@ -0,0 +1,25 @@
import { getCurrencyConfig } from './render';
export const getQuotaPerUnit = () => {
const raw = parseFloat(localStorage.getItem('quota_per_unit') || '1');
return Number.isFinite(raw) && raw > 0 ? raw : 1;
};
export const quotaToDisplayAmount = (quota) => {
const q = Number(quota || 0);
if (!Number.isFinite(q) || q <= 0) return 0;
const { type, rate } = getCurrencyConfig();
if (type === 'TOKENS') return q;
const usd = q / getQuotaPerUnit();
if (type === 'USD') return usd;
return usd * (rate || 1);
};
export const displayAmountToQuota = (amount) => {
const val = Number(amount || 0);
if (!Number.isFinite(val) || val <= 0) return 0;
const { type, rate } = getCurrencyConfig();
if (type === 'TOKENS') return Math.round(val);
const usd = type === 'USD' ? val : val / (rate || 1);
return Math.round(usd * getQuotaPerUnit());
};