2024-05-16 00:35:27 +08:00
|
|
|
|
import React, { useContext, useEffect, useRef, useMemo, useState } from 'react';
|
2024-09-22 17:44:57 +08:00
|
|
|
|
import { API, copy, showError, showInfo, showSuccess } from '../helpers';
|
2024-12-13 19:03:14 +08:00
|
|
|
|
import { useTranslation } from 'react-i18next';
|
2024-05-13 23:02:35 +08:00
|
|
|
|
|
2024-05-15 20:17:27 +08:00
|
|
|
|
import {
|
|
|
|
|
|
Banner,
|
|
|
|
|
|
Input,
|
|
|
|
|
|
Layout,
|
|
|
|
|
|
Modal,
|
|
|
|
|
|
Space,
|
|
|
|
|
|
Table,
|
|
|
|
|
|
Tag,
|
|
|
|
|
|
Tooltip,
|
2024-05-16 00:35:27 +08:00
|
|
|
|
Popover,
|
|
|
|
|
|
ImagePreview,
|
|
|
|
|
|
Button,
|
2024-05-15 20:17:27 +08:00
|
|
|
|
} from '@douyinfe/semi-ui';
|
2024-05-16 00:35:27 +08:00
|
|
|
|
import {
|
|
|
|
|
|
IconMore,
|
|
|
|
|
|
IconVerify,
|
|
|
|
|
|
IconUploadError,
|
|
|
|
|
|
IconHelpCircle,
|
|
|
|
|
|
} from '@douyinfe/semi-icons';
|
2024-05-13 23:02:35 +08:00
|
|
|
|
import { UserContext } from '../context/User/index.js';
|
|
|
|
|
|
import Text from '@douyinfe/semi-ui/lib/es/typography/text';
|
|
|
|
|
|
|
|
|
|
|
|
const ModelPricing = () => {
|
2024-12-13 19:03:14 +08:00
|
|
|
|
const { t } = useTranslation();
|
2024-05-15 20:17:27 +08:00
|
|
|
|
const [filteredValue, setFilteredValue] = useState([]);
|
|
|
|
|
|
const compositionRef = useRef({ isComposition: false });
|
2024-05-16 00:35:27 +08:00
|
|
|
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
|
|
|
|
|
const [modalImageUrl, setModalImageUrl] = useState('');
|
|
|
|
|
|
const [isModalOpenurl, setIsModalOpenurl] = useState(false);
|
2024-09-22 17:44:57 +08:00
|
|
|
|
const [selectedGroup, setSelectedGroup] = useState('default');
|
2024-05-16 00:35:27 +08:00
|
|
|
|
|
|
|
|
|
|
const rowSelection = useMemo(
|
|
|
|
|
|
() => ({
|
|
|
|
|
|
onChange: (selectedRowKeys, selectedRows) => {
|
|
|
|
|
|
setSelectedRowKeys(selectedRowKeys);
|
|
|
|
|
|
},
|
|
|
|
|
|
}),
|
|
|
|
|
|
[]
|
|
|
|
|
|
);
|
2024-05-15 20:17:27 +08:00
|
|
|
|
|
|
|
|
|
|
const handleChange = (value) => {
|
|
|
|
|
|
if (compositionRef.current.isComposition) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const newFilteredValue = value ? [value] : [];
|
|
|
|
|
|
setFilteredValue(newFilteredValue);
|
|
|
|
|
|
};
|
|
|
|
|
|
const handleCompositionStart = () => {
|
|
|
|
|
|
compositionRef.current.isComposition = true;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleCompositionEnd = (event) => {
|
|
|
|
|
|
compositionRef.current.isComposition = false;
|
|
|
|
|
|
const value = event.target.value;
|
|
|
|
|
|
const newFilteredValue = value ? [value] : [];
|
|
|
|
|
|
setFilteredValue(newFilteredValue);
|
|
|
|
|
|
};
|
2024-12-13 19:03:14 +08:00
|
|
|
|
|
|
|
|
|
|
function renderQuotaType(type) {
|
|
|
|
|
|
// Ensure all cases are string literals by adding quotes.
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Tag color='teal' size='large'>
|
|
|
|
|
|
{t('按次计费')}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
);
|
|
|
|
|
|
case 0:
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Tag color='violet' size='large'>
|
|
|
|
|
|
{t('按量计费')}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
);
|
|
|
|
|
|
default:
|
|
|
|
|
|
return t('未知');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function renderAvailable(available) {
|
2024-12-27 21:59:05 +08:00
|
|
|
|
return (
|
2024-12-13 19:03:14 +08:00
|
|
|
|
<Popover
|
2024-12-27 21:59:05 +08:00
|
|
|
|
content={
|
|
|
|
|
|
<div style={{ padding: 8 }}>{t('您的分组可以使用该模型')}</div>
|
|
|
|
|
|
}
|
|
|
|
|
|
position='top'
|
|
|
|
|
|
key={available}
|
|
|
|
|
|
style={{
|
|
|
|
|
|
backgroundColor: 'rgba(var(--semi-blue-4),1)',
|
|
|
|
|
|
borderColor: 'rgba(var(--semi-blue-4),1)',
|
|
|
|
|
|
color: 'var(--semi-color-white)',
|
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
|
borderStyle: 'solid',
|
|
|
|
|
|
}}
|
2024-12-13 19:03:14 +08:00
|
|
|
|
>
|
2024-12-27 21:59:05 +08:00
|
|
|
|
<IconVerify style={{ color: 'green' }} size="large" />
|
2024-12-13 19:03:14 +08:00
|
|
|
|
</Popover>
|
2024-12-27 21:59:05 +08:00
|
|
|
|
)
|
2024-12-13 19:03:14 +08:00
|
|
|
|
}
|
2024-05-15 20:17:27 +08:00
|
|
|
|
|
2024-05-13 23:02:35 +08:00
|
|
|
|
const columns = [
|
|
|
|
|
|
{
|
2024-12-13 19:03:14 +08:00
|
|
|
|
title: t('可用性'),
|
2024-05-13 23:02:35 +08:00
|
|
|
|
dataIndex: 'available',
|
|
|
|
|
|
render: (text, record, index) => {
|
2024-09-22 17:44:57 +08:00
|
|
|
|
// if record.enable_groups contains selectedGroup, then available is true
|
|
|
|
|
|
return renderAvailable(record.enable_groups.includes(selectedGroup));
|
2024-05-13 23:02:35 +08:00
|
|
|
|
},
|
2024-05-15 20:17:27 +08:00
|
|
|
|
sorter: (a, b) => a.available - b.available,
|
2024-05-13 23:02:35 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2024-12-13 19:03:14 +08:00
|
|
|
|
title: t('模型名称'),
|
|
|
|
|
|
dataIndex: 'model_name',
|
2024-05-13 23:02:35 +08:00
|
|
|
|
render: (text, record, index) => {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<Tag
|
2024-05-16 00:35:27 +08:00
|
|
|
|
color='green'
|
2024-05-13 23:02:35 +08:00
|
|
|
|
size='large'
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
copyText(text);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{text}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
</>
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
2024-05-17 12:50:43 +08:00
|
|
|
|
onFilter: (value, record) =>
|
2024-05-18 00:04:11 +08:00
|
|
|
|
record.model_name.toLowerCase().includes(value.toLowerCase()),
|
2024-05-15 20:17:27 +08:00
|
|
|
|
filteredValue,
|
2024-05-13 23:02:35 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2024-12-13 19:03:14 +08:00
|
|
|
|
title: t('计费类型'),
|
2024-05-13 23:02:35 +08:00
|
|
|
|
dataIndex: 'quota_type',
|
|
|
|
|
|
render: (text, record, index) => {
|
|
|
|
|
|
return renderQuotaType(parseInt(text));
|
|
|
|
|
|
},
|
2024-05-15 20:17:27 +08:00
|
|
|
|
sorter: (a, b) => a.quota_type - b.quota_type,
|
2024-05-13 23:02:35 +08:00
|
|
|
|
},
|
2024-09-22 17:44:57 +08:00
|
|
|
|
{
|
2024-12-13 19:03:14 +08:00
|
|
|
|
title: t('可用分组'),
|
2024-09-22 17:44:57 +08:00
|
|
|
|
dataIndex: 'enable_groups',
|
|
|
|
|
|
render: (text, record, index) => {
|
2024-12-24 19:23:29 +08:00
|
|
|
|
|
2024-09-22 17:44:57 +08:00
|
|
|
|
// enable_groups is a string array
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
{text.map((group) => {
|
2024-12-24 19:23:29 +08:00
|
|
|
|
if (usableGroup[group]) {
|
|
|
|
|
|
if (group === selectedGroup) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Tag
|
|
|
|
|
|
color='blue'
|
|
|
|
|
|
size='large'
|
|
|
|
|
|
prefixIcon={<IconVerify />}
|
|
|
|
|
|
>
|
|
|
|
|
|
{group}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Tag
|
|
|
|
|
|
color='blue'
|
|
|
|
|
|
size='large'
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setSelectedGroup(group);
|
|
|
|
|
|
showInfo(t('当前查看的分组为:{{group}},倍率为:{{ratio}}', {
|
|
|
|
|
|
group: group,
|
|
|
|
|
|
ratio: groupRatio[group]
|
|
|
|
|
|
}));
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{group}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2024-09-22 17:44:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
})}
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2024-05-13 23:02:35 +08:00
|
|
|
|
{
|
2024-05-16 00:35:27 +08:00
|
|
|
|
title: () => (
|
|
|
|
|
|
<span style={{'display':'flex','alignItems':'center'}}>
|
2024-12-13 19:03:14 +08:00
|
|
|
|
{t('倍率')}
|
2024-05-16 00:35:27 +08:00
|
|
|
|
<Popover
|
|
|
|
|
|
content={
|
2024-12-13 19:03:14 +08:00
|
|
|
|
<div style={{ padding: 8 }}>
|
|
|
|
|
|
{t('倍率是为了方便换算不同价格的模型')}<br/>
|
|
|
|
|
|
{t('点击查看倍率说明')}
|
|
|
|
|
|
</div>
|
2024-05-16 00:35:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
position='top'
|
|
|
|
|
|
style={{
|
|
|
|
|
|
backgroundColor: 'rgba(var(--semi-blue-4),1)',
|
|
|
|
|
|
borderColor: 'rgba(var(--semi-blue-4),1)',
|
|
|
|
|
|
color: 'var(--semi-color-white)',
|
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
|
borderStyle: 'solid',
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<IconHelpCircle
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setModalImageUrl('/ratio.png');
|
|
|
|
|
|
setIsModalOpenurl(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Popover>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
),
|
2024-05-13 23:02:35 +08:00
|
|
|
|
dataIndex: 'model_ratio',
|
|
|
|
|
|
render: (text, record, index) => {
|
2024-05-16 00:35:27 +08:00
|
|
|
|
let content = text;
|
|
|
|
|
|
let completionRatio = parseFloat(record.completion_ratio.toFixed(3));
|
|
|
|
|
|
content = (
|
|
|
|
|
|
<>
|
2024-12-13 19:03:14 +08:00
|
|
|
|
<Text>{t('模型倍率')}:{record.quota_type === 0 ? text : t('无')}</Text>
|
2024-05-16 00:35:27 +08:00
|
|
|
|
<br />
|
2024-12-13 19:03:14 +08:00
|
|
|
|
<Text>{t('补全倍率')}:{record.quota_type === 0 ? completionRatio : t('无')}</Text>
|
2024-09-22 17:44:57 +08:00
|
|
|
|
<br />
|
2024-12-13 19:03:14 +08:00
|
|
|
|
<Text>{t('分组倍率')}:{groupRatio[selectedGroup]}</Text>
|
2024-05-16 00:35:27 +08:00
|
|
|
|
</>
|
|
|
|
|
|
);
|
|
|
|
|
|
return <div>{content}</div>;
|
2024-05-13 23:02:35 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2024-12-13 19:03:14 +08:00
|
|
|
|
title: t('模型价格'),
|
2024-05-13 23:02:35 +08:00
|
|
|
|
dataIndex: 'model_price',
|
|
|
|
|
|
render: (text, record, index) => {
|
|
|
|
|
|
let content = text;
|
|
|
|
|
|
if (record.quota_type === 0) {
|
2024-05-21 21:16:17 +08:00
|
|
|
|
// 这里的 *2 是因为 1倍率=0.002刀,请勿删除
|
2024-09-22 17:44:57 +08:00
|
|
|
|
let inputRatioPrice = record.model_ratio * 2 * groupRatio[selectedGroup];
|
2024-05-13 23:02:35 +08:00
|
|
|
|
let completionRatioPrice =
|
|
|
|
|
|
record.model_ratio *
|
2024-05-21 21:12:38 +08:00
|
|
|
|
record.completion_ratio * 2 *
|
2024-09-22 17:44:57 +08:00
|
|
|
|
groupRatio[selectedGroup];
|
2024-05-13 23:02:35 +08:00
|
|
|
|
content = (
|
|
|
|
|
|
<>
|
2024-12-13 19:03:14 +08:00
|
|
|
|
<Text>{t('提示')} ${inputRatioPrice} / 1M tokens</Text>
|
2024-05-13 23:02:35 +08:00
|
|
|
|
<br />
|
2024-12-13 19:03:14 +08:00
|
|
|
|
<Text>{t('补全')} ${completionRatioPrice} / 1M tokens</Text>
|
2024-05-13 23:02:35 +08:00
|
|
|
|
</>
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
2024-09-22 17:44:57 +08:00
|
|
|
|
let price = parseFloat(text) * groupRatio[selectedGroup];
|
2024-12-13 19:03:14 +08:00
|
|
|
|
content = <>${t('模型价格')}:${price}</>;
|
2024-05-13 23:02:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
return <div>{content}</div>;
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const [models, setModels] = useState([]);
|
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
|
|
const [userState, userDispatch] = useContext(UserContext);
|
2024-09-22 17:44:57 +08:00
|
|
|
|
const [groupRatio, setGroupRatio] = useState({});
|
2024-12-24 19:23:29 +08:00
|
|
|
|
const [usableGroup, setUsableGroup] = useState({});
|
2024-05-13 23:02:35 +08:00
|
|
|
|
|
|
|
|
|
|
const setModelsFormat = (models, groupRatio) => {
|
|
|
|
|
|
for (let i = 0; i < models.length; i++) {
|
2024-05-16 00:35:27 +08:00
|
|
|
|
models[i].key = models[i].model_name;
|
2024-09-22 17:44:57 +08:00
|
|
|
|
models[i].group_ratio = groupRatio[models[i].model_name];
|
2024-05-13 23:02:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
// sort by quota_type
|
|
|
|
|
|
models.sort((a, b) => {
|
|
|
|
|
|
return a.quota_type - b.quota_type;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2024-05-15 20:17:27 +08:00
|
|
|
|
// sort by model_name, start with gpt is max, other use localeCompare
|
2024-05-13 23:02:35 +08:00
|
|
|
|
models.sort((a, b) => {
|
2024-05-15 20:17:27 +08:00
|
|
|
|
if (a.model_name.startsWith('gpt') && !b.model_name.startsWith('gpt')) {
|
2024-05-13 23:02:35 +08:00
|
|
|
|
return -1;
|
2024-05-15 20:17:27 +08:00
|
|
|
|
} else if (
|
|
|
|
|
|
!a.model_name.startsWith('gpt') &&
|
|
|
|
|
|
b.model_name.startsWith('gpt')
|
|
|
|
|
|
) {
|
2024-05-13 23:02:35 +08:00
|
|
|
|
return 1;
|
|
|
|
|
|
} else {
|
2024-05-15 20:17:27 +08:00
|
|
|
|
return a.model_name.localeCompare(b.model_name);
|
2024-05-13 23:02:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
setModels(models);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const loadPricing = async () => {
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
|
|
|
|
|
|
|
let url = '';
|
|
|
|
|
|
url = `/api/pricing`;
|
|
|
|
|
|
const res = await API.get(url);
|
2024-12-24 19:23:29 +08:00
|
|
|
|
const { success, message, data, group_ratio, usable_group } = res.data;
|
2024-05-13 23:02:35 +08:00
|
|
|
|
if (success) {
|
|
|
|
|
|
setGroupRatio(group_ratio);
|
2024-12-24 19:23:29 +08:00
|
|
|
|
setUsableGroup(usable_group);
|
2024-09-22 17:44:57 +08:00
|
|
|
|
setSelectedGroup(userState.user ? userState.user.group : 'default')
|
2024-05-13 23:02:35 +08:00
|
|
|
|
setModelsFormat(data, group_ratio);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showError(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
setLoading(false);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const refresh = async () => {
|
|
|
|
|
|
await loadPricing();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const copyText = async (text) => {
|
|
|
|
|
|
if (await copy(text)) {
|
|
|
|
|
|
showSuccess('已复制:' + text);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// setSearchKeyword(text);
|
|
|
|
|
|
Modal.error({ title: '无法复制到剪贴板,请手动复制', content: text });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
refresh().then();
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<Layout>
|
|
|
|
|
|
{userState.user ? (
|
|
|
|
|
|
<Banner
|
2024-05-16 00:35:27 +08:00
|
|
|
|
type="success"
|
|
|
|
|
|
fullMode={false}
|
|
|
|
|
|
closeIcon="null"
|
2024-12-13 19:03:14 +08:00
|
|
|
|
description={t('您的默认分组为:{{group}},分组倍率为:{{ratio}}', {
|
|
|
|
|
|
group: userState.user.group,
|
|
|
|
|
|
ratio: groupRatio[userState.user.group]
|
|
|
|
|
|
})}
|
2024-05-13 23:02:35 +08:00
|
|
|
|
/>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<Banner
|
|
|
|
|
|
type='warning'
|
2024-05-16 00:35:27 +08:00
|
|
|
|
fullMode={false}
|
|
|
|
|
|
closeIcon="null"
|
2024-12-13 19:03:14 +08:00
|
|
|
|
description={t('您还未登陆,显示的价格为默认分组倍率: {{ratio}}', {
|
|
|
|
|
|
ratio: groupRatio['default']
|
|
|
|
|
|
})}
|
2024-05-13 23:02:35 +08:00
|
|
|
|
/>
|
|
|
|
|
|
)}
|
2024-05-16 00:35:27 +08:00
|
|
|
|
<br/>
|
|
|
|
|
|
<Banner
|
|
|
|
|
|
type="info"
|
|
|
|
|
|
fullMode={false}
|
2024-12-13 19:03:14 +08:00
|
|
|
|
description={<div>{t('按量计费费用 = 分组倍率 × 模型倍率 × (提示token数 + 补全token数 × 补全倍率)/ 500000 (单位:美元)')}</div>}
|
2024-05-16 00:35:27 +08:00
|
|
|
|
closeIcon="null"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<br/>
|
2024-12-13 19:03:14 +08:00
|
|
|
|
<Space style={{ marginBottom: 16 }}>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
placeholder={t('模糊搜索模型名称')}
|
|
|
|
|
|
style={{ width: 200 }}
|
|
|
|
|
|
onCompositionStart={handleCompositionStart}
|
|
|
|
|
|
onCompositionEnd={handleCompositionEnd}
|
|
|
|
|
|
onChange={handleChange}
|
|
|
|
|
|
showClear
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
theme='light'
|
|
|
|
|
|
type='tertiary'
|
|
|
|
|
|
style={{width: 150}}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
copyText(selectedRowKeys);
|
|
|
|
|
|
}}
|
|
|
|
|
|
disabled={selectedRowKeys == ""}
|
|
|
|
|
|
>
|
|
|
|
|
|
{t('复制选中模型')}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Space>
|
2024-05-13 23:02:35 +08:00
|
|
|
|
<Table
|
|
|
|
|
|
style={{ marginTop: 5 }}
|
|
|
|
|
|
columns={columns}
|
|
|
|
|
|
dataSource={models}
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
pagination={{
|
2024-12-13 19:03:14 +08:00
|
|
|
|
formatPageText: (page) =>
|
|
|
|
|
|
t('第 {{start}} - {{end}} 条,共 {{total}} 条', {
|
|
|
|
|
|
start: page.currentStart,
|
|
|
|
|
|
end: page.currentEnd,
|
|
|
|
|
|
total: models.length
|
|
|
|
|
|
}),
|
2024-05-13 23:02:35 +08:00
|
|
|
|
pageSize: models.length,
|
|
|
|
|
|
showSizeChanger: false,
|
|
|
|
|
|
}}
|
2024-05-16 00:35:27 +08:00
|
|
|
|
rowSelection={rowSelection}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<ImagePreview
|
|
|
|
|
|
src={modalImageUrl}
|
|
|
|
|
|
visible={isModalOpenurl}
|
|
|
|
|
|
onVisibleChange={(visible) => setIsModalOpenurl(visible)}
|
2024-05-13 23:02:35 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</Layout>
|
|
|
|
|
|
</>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default ModelPricing;
|