Selaa lähdekoodia

feat: render display pricing on pricing page

master
fengsilin 1 viikko sitten
vanhempi
commit
fee9504107
6 muutettua tiedostoa jossa 165 lisäystä ja 7 poistoa
  1. +69
    -2
      web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx
  2. +9
    -2
      web/src/components/table/model-pricing/modal/components/UserPriceComparison.jsx
  3. +41
    -1
      web/src/components/table/model-pricing/view/card/PricingCardView.jsx
  4. +26
    -0
      web/src/components/table/model-pricing/view/table/PricingTableColumns.jsx
  5. +10
    -1
      web/src/i18n/locales/en.json
  6. +10
    -1
      web/src/i18n/locales/zh-CN.json

+ 69
- 2
web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx Näytä tiedosto

@@ -22,9 +22,12 @@ import { Card, Avatar, Typography, Table, Tag } from '@douyinfe/semi-ui';
import { IconCoinMoneyStroked } from '@douyinfe/semi-icons';
import {
calculateModelPrice,
formatDisplayPricingItem,
formatMatrixPrice,
getMatrixPricingUnit,
hasDisplayPricing,
hasMatrixPricing,
normalizeDisplayPricingItems,
} from '../../../../../helpers';

const { Text } = Typography;
@@ -44,6 +47,62 @@ const ModelPricingTable = ({
? modelData.enable_groups
: [];
const autoChain = autoGroups.filter((g) => modelEnableGroups.includes(g));
const renderDisplayPricingTable = () => {
const items = normalizeDisplayPricingItems(modelData).map((item) =>
formatDisplayPricingItem({ item, displayPrice, t }),
);
const columns = [
{
title: t('规格'),
dataIndex: 'specification',
render: (text) => <span className='font-semibold'>{text}</span>,
},
{
title: t('供应商说明'),
dataIndex: 'official_supplier_tip',
render: (text) => <span className='text-gray-600'>{text || '-'}</span>,
},
{
title: t('价格'),
dataIndex: 'formattedPrice',
render: (text, record) => (
<div>
<div className='font-semibold text-orange-600'>{text}</div>
{record.originalPrice && (
<div className='text-xs text-gray-400 line-through'>
{t('原价')} {record.originalPrice}
</div>
)}
</div>
),
},
{
title: t('折扣'),
dataIndex: 'discountText',
render: (text) => text || '-',
},
];

return (
<>
<div className='mb-3 rounded-lg border border-orange-100 bg-orange-50 px-3 py-2 text-xs text-orange-700'>
{t('展示价格仅用于估算,实际消耗以任务完成后的积分扣减为准。')}
</div>
<Table
dataSource={items.map((item, index) => ({
...item,
key: `${item.specification}-${index}`,
}))}
columns={columns}
pagination={false}
size='small'
bordered={false}
className='!rounded-lg'
/>
</>
);
};

const renderMatrixPriceTable = () => {
const cfg = modelData?.pricing_config;
const dimensions = cfg?.dimensions || [];
@@ -124,6 +183,9 @@ const ModelPricingTable = ({
};

const renderGroupPriceTable = () => {
if (hasDisplayPricing(modelData)) {
return renderDisplayPricingTable();
}
if (hasMatrixPricing(modelData)) {
return renderMatrixPriceTable();
}
@@ -144,6 +206,7 @@ const ModelPricingTable = ({
tokenUnit,
displayPrice,
currency,
t,
})
: { inputPrice: '-', outputPrice: '-', price: '-' };

@@ -322,9 +385,13 @@ const ModelPricingTable = ({
<IconCoinMoneyStroked size={16} />
</Avatar>
<div>
<Text className='text-lg font-medium'>{t('分组价格')}</Text>
<Text className='text-lg font-medium'>
{hasDisplayPricing(modelData) ? t('展示价格') : t('分组价格')}
</Text>
<div className='text-xs text-gray-600'>
{t('不同用户分组的价格信息')}
{hasDisplayPricing(modelData)
? t('展示价格,仅用于估算')
: t('不同用户分组的价格信息')}
</div>
</div>
</div>


+ 9
- 2
web/src/components/table/model-pricing/modal/components/UserPriceComparison.jsx Näytä tiedosto

@@ -7,17 +7,22 @@ const { Text } = Typography;

const UserPriceComparison = ({
modelName,
modelData,
currency,
tokenUnit,
displayPrice,
t,
}) => {
const hasDisplayPricing =
Array.isArray(modelData?.display_pricing) &&
modelData.display_pricing.length > 0;

const [loading, setLoading] = useState(false);
const [priceData, setPriceData] = useState(null);

useEffect(() => {
const fetchUserPrice = async () => {
if (!modelName) return;
if (!modelName || hasDisplayPricing) return;
setLoading(true);
try {
const res = await API.get(
@@ -37,7 +42,7 @@ const UserPriceComparison = ({
}
};
fetchUserPrice();
}, [modelName]);
}, [modelName, hasDisplayPricing]);

const currencySymbol = useMemo(() => {
if (currency === 'CNY') return '¥';
@@ -74,6 +79,8 @@ const UserPriceComparison = ({
return `${currencySymbol}${num.toFixed(4)}`;
};

if (hasDisplayPricing) return null;

if (loading) {
return (
<Card className='!rounded-2xl shadow-sm border-0 mb-4'>


+ 41
- 1
web/src/components/table/model-pricing/view/card/PricingCardView.jsx Näytä tiedosto

@@ -38,6 +38,7 @@ import {
import {
stringToColor,
getLobeHubIcon,
hasDisplayPricing,
hasMatrixPricing,
calculateModelPrice,
} from '../../../../../helpers';
@@ -163,8 +164,41 @@ const PricingCardView = ({
tokenUnit,
displayPrice,
currency,
t,
});

if (priceData?.isDisplayPricing) {
return (
<div className='mb-3 text-xs'>
{String(priceData.price || '')
.split('\n')
.map((line) => (
<div key={line} className='font-semibold text-gray-900'>
{line}
</div>
))}
<div className='mt-1 text-gray-500'>{t('展示价格')}</div>
{(priceData.details || []).map((detail, detailIndex) => (
<div
key={`${detail.specification}-${detailIndex}`}
className='mt-1 flex items-center justify-between gap-2 text-gray-500'
title={`${detail.specification} ${detail.formattedPrice}`}
>
<span className='min-w-0 truncate'>{detail.specification}</span>
<span className='shrink-0 text-gray-700'>
{detail.formattedPrice}
</span>
</div>
))}
{priceData.hiddenDetailCount > 0 && (
<div className='mt-1 text-gray-400'>
+ {priceData.hiddenDetailCount}
</div>
)}
</div>
);
}

if (!priceData?.isMatrix) {
return null;
}
@@ -206,7 +240,13 @@ const PricingCardView = ({
-
</Tag>
);
if (hasMatrixPricing(record)) {
if (hasDisplayPricing(record)) {
billingTag = (
<Tag key='billing' shape='circle' color='orange' size='small'>
{t('展示价格')}
</Tag>
);
} else if (hasMatrixPricing(record)) {
billingTag = (
<Tag key='billing' shape='circle' color='purple' size='small'>
{t('矩阵计费')}


+ 26
- 0
web/src/components/table/model-pricing/view/table/PricingTableColumns.jsx Näytä tiedosto

@@ -24,6 +24,7 @@ import {
renderModelTag,
stringToColor,
calculateModelPrice,
hasDisplayPricing,
hasMatrixPricing,
getLobeHubIcon,
} from '../../../../../helpers';
@@ -53,6 +54,13 @@ function renderQuotaType(type, t) {
}

function renderBillingType(record, t) {
if (hasDisplayPricing(record)) {
return (
<Tag color='orange' shape='circle'>
{t('展示价格')}
</Tag>
);
}
if (hasMatrixPricing(record)) {
return (
<Tag color='purple' shape='circle'>
@@ -137,6 +145,7 @@ export const getPricingTableColumns = ({
tokenUnit,
displayPrice,
currency,
t,
});
priceDataCache.set(record, cache);
}
@@ -244,6 +253,23 @@ export const getPricingTableColumns = ({
render: (text, record, index) => {
const priceData = getPriceData(record);

if (priceData.isDisplayPricing) {
return (
<div className='space-y-1'>
{String(priceData.price || '')
.split('\n')
.map((line) => (
<div key={line} className='text-gray-700 font-semibold'>
{line}
</div>
))}
<div className='text-xs text-gray-500'>
{t('展示价格,仅用于估算')}
</div>
</div>
);
}

if (priceData.isMatrix) {
return (
<div className='space-y-1'>


+ 10
- 1
web/src/i18n/locales/en.json Näytä tiedosto

@@ -3295,5 +3295,14 @@
"重试启动失败": "Failed to start retry",
"重试已启动": "Retry started",
"验证完成": "Verification complete",
"暂无数据": "No data"
"暂无数据": "No data",
"展示价格,仅用于估算": "Display price, estimate only",
"展示价格仅用于估算,实际消耗以任务完成后的积分扣减为准。": "Display price is only an estimate. Actual consumption is based on the final quota deduction after the task completes.",
"规格": "Specification",
"供应商说明": "Supplier tip",
"折扣": "Discount",
"second": "second",
"call": "call",
"image": "image",
"video": "video"
}

+ 10
- 1
web/src/i18n/locales/zh-CN.json Näytä tiedosto

@@ -3249,5 +3249,14 @@
"重试启动失败": "重试启动失败",
"重试已启动": "重试已启动",
"验证完成": "验证完成",
"暂无数据": "暂无数据"
"暂无数据": "暂无数据",
"展示价格,仅用于估算": "展示价格,仅用于估算",
"展示价格仅用于估算,实际消耗以任务完成后的积分扣减为准。": "展示价格仅用于估算,实际消耗以任务完成后的积分扣减为准。",
"规格": "规格",
"供应商说明": "供应商说明",
"折扣": "折扣",
"second": "秒",
"call": "次",
"image": "张图片",
"video": "个视频"
}

Ladataan…
Peruuta
Tallenna