);
diff --git a/web/src/components/table/model-pricing/PricingPage.jsx b/web/src/components/table/model-pricing/layout/PricingPage.jsx
similarity index 57%
rename from web/src/components/table/model-pricing/PricingPage.jsx
rename to web/src/components/table/model-pricing/layout/PricingPage.jsx
index eb76944f..0f150122 100644
--- a/web/src/components/table/model-pricing/PricingPage.jsx
+++ b/web/src/components/table/model-pricing/layout/PricingPage.jsx
@@ -21,56 +21,46 @@ import React from 'react';
import { Layout, ImagePreview } from '@douyinfe/semi-ui';
import PricingSidebar from './PricingSidebar';
import PricingContent from './PricingContent';
-import { useModelPricingData } from '../../../hooks/model-pricing/useModelPricingData';
-import { useIsMobile } from '../../../hooks/common/useIsMobile';
+import { useModelPricingData } from '../../../../hooks/model-pricing/useModelPricingData';
+import { useIsMobile } from '../../../../hooks/common/useIsMobile';
const PricingPage = () => {
const pricingData = useModelPricingData();
const { Sider, Content } = Layout;
const isMobile = useIsMobile();
-
- // 显示倍率状态
const [showRatio, setShowRatio] = React.useState(false);
+ const [viewMode, setViewMode] = React.useState('card');
+ const allProps = {
+ ...pricingData,
+ showRatio,
+ setShowRatio,
+ viewMode,
+ setViewMode
+ };
return (
);
};
diff --git a/web/src/components/table/model-pricing/index.jsx b/web/src/components/table/model-pricing/layout/PricingView.jsx
similarity index 69%
rename from web/src/components/table/model-pricing/index.jsx
rename to web/src/components/table/model-pricing/layout/PricingView.jsx
index 948285f0..16e9db99 100644
--- a/web/src/components/table/model-pricing/index.jsx
+++ b/web/src/components/table/model-pricing/layout/PricingView.jsx
@@ -17,5 +17,17 @@ along with this program. If not, see
.
For commercial licensing, please contact support@quantumnous.com
*/
-// 为了向后兼容,这里重新导出新的 PricingPage 组件
-export { default } from './PricingPage';
\ No newline at end of file
+import React from 'react';
+import PricingTable from '../view/PricingTable';
+import PricingCardView from '../view/PricingCardView';
+
+const PricingView = ({
+ viewMode = 'table',
+ ...props
+}) => {
+ return viewMode === 'card' ?
+
;
+};
+
+export default PricingView;
\ No newline at end of file
diff --git a/web/src/components/table/model-pricing/modal/PricingFilterModal.jsx b/web/src/components/table/model-pricing/modal/PricingFilterModal.jsx
index 84edb454..3d0601b8 100644
--- a/web/src/components/table/model-pricing/modal/PricingFilterModal.jsx
+++ b/web/src/components/table/model-pricing/modal/PricingFilterModal.jsx
@@ -40,10 +40,14 @@ const PricingFilterModal = ({
setActiveKey,
showRatio,
setShowRatio,
+ viewMode,
+ setViewMode,
filterGroup,
setFilterGroup,
filterQuotaType,
setFilterQuotaType,
+ currentPage,
+ setCurrentPage,
loading,
...categoryProps
} = sidebarProps;
@@ -56,14 +60,12 @@ const PricingFilterModal = ({
setShowWithRecharge,
setCurrency,
setShowRatio,
+ setViewMode,
setFilterGroup,
setFilterQuotaType,
+ setCurrentPage,
});
- const handleConfirm = () => {
- onClose();
- };
-
const footer = (
{t('确定')}
@@ -106,6 +108,8 @@ const PricingFilterModal = ({
setCurrency={setCurrency}
showRatio={showRatio}
setShowRatio={setShowRatio}
+ viewMode={viewMode}
+ setViewMode={setViewMode}
loading={loading}
t={t}
/>
diff --git a/web/src/components/table/model-pricing/view/PricingCardView.jsx b/web/src/components/table/model-pricing/view/PricingCardView.jsx
new file mode 100644
index 00000000..1d743412
--- /dev/null
+++ b/web/src/components/table/model-pricing/view/PricingCardView.jsx
@@ -0,0 +1,444 @@
+/*
+Copyright (C) 2025 QuantumNous
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see
.
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+
+import React, { useState, useRef, useEffect } from 'react';
+import { Card, Tag, Tooltip, Checkbox, Empty, Pagination, Button, Skeleton } from '@douyinfe/semi-ui';
+import { IconHelpCircle, IconCopy } from '@douyinfe/semi-icons';
+import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations';
+import { stringToColor, getModelCategories, calculateModelPrice, formatPriceInfo } from '../../../../helpers';
+
+const PricingCardView = ({
+ filteredModels,
+ loading,
+ rowSelection,
+ pageSize,
+ setPageSize,
+ currentPage,
+ setCurrentPage,
+ selectedGroup,
+ groupRatio,
+ copyText,
+ setModalImageUrl,
+ setIsModalOpenurl,
+ currency,
+ tokenUnit,
+ setTokenUnit,
+ displayPrice,
+ showRatio,
+ t
+}) => {
+ const [showSkeleton, setShowSkeleton] = useState(loading);
+ const [skeletonCount] = useState(10);
+ const loadingStartRef = useRef(Date.now());
+
+ useEffect(() => {
+ if (loading) {
+ loadingStartRef.current = Date.now();
+ setShowSkeleton(true);
+ } else {
+ const elapsed = Date.now() - loadingStartRef.current;
+ const remaining = Math.max(0, 1000 - elapsed);
+ if (remaining === 0) {
+ setShowSkeleton(false);
+ } else {
+ const timer = setTimeout(() => setShowSkeleton(false), remaining);
+ return () => clearTimeout(timer);
+ }
+ }
+ }, [loading]);
+
+ // 计算当前页面要显示的数据
+ const startIndex = (currentPage - 1) * pageSize;
+ const endIndex = startIndex + pageSize;
+ const paginatedModels = filteredModels.slice(startIndex, endIndex);
+
+ // 渲染骨架屏卡片
+ const renderSkeletonCards = () => {
+ const placeholder = (
+
+
+ {Array.from({ length: skeletonCount }).map((_, index) => (
+
+ {/* 头部:图标 + 模型名称 + 操作按钮 */}
+
+
+ {/* 模型图标骨架 */}
+
+
+
+ {/* 模型名称骨架 */}
+
+
+
+
+
+
+ {/* 操作按钮骨架 */}
+
+ {rowSelection && (
+
+ )}
+
+
+
+ {/* 价格信息骨架 */}
+
+
+
+
+ {/* 模型描述骨架 */}
+
+
+
+
+ {/* 标签区域骨架 */}
+
+ {Array.from({ length: 3 + (index % 2) }).map((_, tagIndex) => (
+
+ ))}
+
+
+ {/* 倍率信息骨架(可选) */}
+ {showRatio && (
+
+
+
+
+
+
+ {Array.from({ length: 3 }).map((_, ratioIndex) => (
+
+ ))}
+
+
+ )}
+
+ ))}
+
+
+ {/* 分页骨架 */}
+
+
+
+
+ );
+
+ return (
+
+ );
+ };
+
+ // 获取模型图标
+ const getModelIcon = (modelName) => {
+ const categories = getModelCategories(t);
+ let icon = null;
+
+ // 遍历分类,找到匹配的模型图标
+ for (const [key, category] of Object.entries(categories)) {
+ if (key !== 'all' && category.filter({ model_name: modelName })) {
+ icon = category.icon;
+ break;
+ }
+ }
+
+ // 如果找到了匹配的图标,返回包装后的图标
+ if (icon) {
+ return (
+
+
+ {React.cloneElement(icon, { size: 32 })}
+
+
+ );
+ }
+
+ // 默认图标(如果没有匹配到任何分类)
+ return (
+
+ {/* 默认的螺旋图案 */}
+
+
+
+
+ );
+ };
+
+ // 获取模型描述
+ const getModelDescription = (modelName) => {
+ // 根据模型名称返回描述,这里可以扩展
+ if (modelName.includes('gpt-3.5-turbo')) {
+ return t('该模型目前指向gpt-35-turbo-0125模型,综合能力强,过去使用最广泛的文本模型。');
+ }
+ if (modelName.includes('gpt-4')) {
+ return t('更强大的GPT-4模型,具有更好的推理能力和更准确的输出。');
+ }
+ if (modelName.includes('claude')) {
+ return t('Anthropic开发的Claude模型,以安全性和有用性著称。');
+ }
+ return t('高性能AI模型,适用于各种文本生成和理解任务。');
+ };
+
+ // 渲染价格信息
+ const renderPriceInfo = (record) => {
+ const priceData = calculateModelPrice({
+ record,
+ selectedGroup,
+ groupRatio,
+ tokenUnit,
+ displayPrice,
+ currency,
+ precision: 4
+ });
+ return formatPriceInfo(priceData, t);
+ };
+
+ // 渲染标签
+ const renderTags = (record) => {
+ const tags = [];
+
+ // 计费类型标签
+ if (record.quota_type === 1) {
+ tags.push(
+
+ {t('按次计费')}
+
+ );
+ } else {
+ tags.push(
+
+ {t('按量计费')}
+
+ );
+ }
+
+ // 热度标签(示例)
+ if (record.model_name.includes('gpt-3.5-turbo') || record.model_name.includes('gpt-4')) {
+ tags.push(
+
+ {t('热')}
+
+ );
+ }
+
+ // 端点类型标签
+ if (record.supported_endpoint_types && record.supported_endpoint_types.length > 0) {
+ record.supported_endpoint_types.slice(0, 2).forEach((endpoint, index) => {
+ tags.push(
+
+ {endpoint}
+
+ );
+ });
+ }
+
+ // 上下文长度标签(示例)
+ if (record.model_name.includes('16k')) {
+ tags.push(
16K );
+ } else if (record.model_name.includes('32k')) {
+ tags.push(
32K );
+ } else {
+ tags.push(
4K );
+ }
+
+ return tags;
+ };
+
+ // 显示骨架屏
+ if (showSkeleton) {
+ return renderSkeletonCards();
+ }
+
+ if (!filteredModels || filteredModels.length === 0) {
+ return (
+
+ }
+ darkModeImage={ }
+ description={t('搜索无结果')}
+ />
+
+ );
+ }
+
+ return (
+
+
+ {paginatedModels.map((model, index) => {
+ const isSelected = rowSelection?.selectedRowKeys?.includes(model.id);
+
+ return (
+
+ {/* 头部:图标 + 模型名称 + 操作按钮 */}
+
+
+ {getModelIcon(model.model_name)}
+
+
+ {model.model_name}
+
+
+
+
+
+ {/* 复制按钮 */}
+ }
+ onClick={() => copyText(model.model_name)}
+ />
+
+ {/* 选择框 */}
+ {rowSelection && (
+ {
+ if (checked) {
+ rowSelection.onSelect(model, true);
+ } else {
+ rowSelection.onSelect(model, false);
+ }
+ }}
+ />
+ )}
+
+
+
+ {/* 价格信息 */}
+
+
+ {renderPriceInfo(model)}
+
+
+
+ {/* 模型描述 */}
+
+
+ {getModelDescription(model.model_name)}
+
+
+
+ {/* 标签区域 */}
+
+ {renderTags(model)}
+
+
+ {/* 倍率信息(可选) */}
+ {showRatio && (
+
+
+ {t('倍率信息')}
+
+ {
+ setModalImageUrl('/ratio.png');
+ setIsModalOpenurl(true);
+ }}
+ />
+
+
+
+
+ {t('模型')}: {model.quota_type === 0 ? model.model_ratio : t('无')}
+
+
+ {t('补全')}: {model.quota_type === 0 ? parseFloat(model.completion_ratio.toFixed(3)) : t('无')}
+
+
+ {t('分组')}: {groupRatio[selectedGroup]}
+
+
+
+ )}
+
+ );
+ })}
+
+
+ {/* 分页 */}
+ {filteredModels.length > 0 && (
+
+
setCurrentPage(page)}
+ onPageSizeChange={(size) => {
+ setPageSize(size);
+ setCurrentPage(1);
+ }}
+ />
+
+ )}
+
+ );
+};
+
+export default PricingCardView;
\ No newline at end of file
diff --git a/web/src/components/table/model-pricing/PricingTable.jsx b/web/src/components/table/model-pricing/view/PricingTable.jsx
similarity index 91%
rename from web/src/components/table/model-pricing/PricingTable.jsx
rename to web/src/components/table/model-pricing/view/PricingTable.jsx
index 4fb2a8e8..26c7edbb 100644
--- a/web/src/components/table/model-pricing/PricingTable.jsx
+++ b/web/src/components/table/model-pricing/view/PricingTable.jsx
@@ -32,18 +32,15 @@ const PricingTable = ({
pageSize,
setPageSize,
selectedGroup,
- usableGroup,
groupRatio,
copyText,
setModalImageUrl,
setIsModalOpenurl,
currency,
- showWithRecharge,
tokenUnit,
setTokenUnit,
displayPrice,
- filteredValue,
- handleGroupClick,
+ searchValue,
showRatio,
compactMode = false,
t
@@ -53,43 +50,37 @@ const PricingTable = ({
return getPricingTableColumns({
t,
selectedGroup,
- usableGroup,
groupRatio,
copyText,
setModalImageUrl,
setIsModalOpenurl,
currency,
- showWithRecharge,
tokenUnit,
setTokenUnit,
displayPrice,
- handleGroupClick,
showRatio,
});
}, [
t,
selectedGroup,
- usableGroup,
groupRatio,
copyText,
setModalImageUrl,
setIsModalOpenurl,
currency,
- showWithRecharge,
tokenUnit,
setTokenUnit,
displayPrice,
- handleGroupClick,
showRatio,
]);
- // 更新列定义中的 filteredValue
+ // 更新列定义中的 searchValue
const processedColumns = useMemo(() => {
const cols = columns.map(column => {
if (column.dataIndex === 'model_name') {
return {
...column,
- filteredValue
+ filteredValue: searchValue ? [searchValue] : []
};
}
return column;
@@ -100,7 +91,7 @@ const PricingTable = ({
return cols.map(({ fixed, ...rest }) => rest);
}
return cols;
- }, [columns, filteredValue, compactMode]);
+ }, [columns, searchValue, compactMode]);
const ModelTable = useMemo(() => (
diff --git a/web/src/components/table/model-pricing/PricingTableColumns.js b/web/src/components/table/model-pricing/view/PricingTableColumns.js
similarity index 79%
rename from web/src/components/table/model-pricing/PricingTableColumns.js
rename to web/src/components/table/model-pricing/view/PricingTableColumns.js
index f0c9783d..54b3889c 100644
--- a/web/src/components/table/model-pricing/PricingTableColumns.js
+++ b/web/src/components/table/model-pricing/view/PricingTableColumns.js
@@ -20,7 +20,7 @@ For commercial licensing, please contact support@quantumnous.com
import React from 'react';
import { Tag, Space, Tooltip, Switch } from '@douyinfe/semi-ui';
import { IconHelpCircle } from '@douyinfe/semi-icons';
-import { renderModelTag, stringToColor } from '../../../helpers';
+import { renderModelTag, stringToColor, calculateModelPrice } from '../../../../helpers';
function renderQuotaType(type, t) {
switch (type) {
@@ -158,38 +158,30 @@ export const getPricingTableColumns = ({
),
dataIndex: 'model_price',
render: (text, record, index) => {
- if (record.quota_type === 0) {
- const inputRatioPriceUSD = record.model_ratio * 2 * groupRatio[selectedGroup];
- const completionRatioPriceUSD =
- record.model_ratio * record.completion_ratio * 2 * groupRatio[selectedGroup];
+ const priceData = calculateModelPrice({
+ record,
+ selectedGroup,
+ groupRatio,
+ tokenUnit,
+ displayPrice,
+ currency
+ });
- const unitDivisor = tokenUnit === 'K' ? 1000 : 1;
- const unitLabel = tokenUnit === 'K' ? 'K' : 'M';
-
- const rawDisplayInput = displayPrice(inputRatioPriceUSD);
- const rawDisplayCompletion = displayPrice(completionRatioPriceUSD);
-
- const numInput = parseFloat(rawDisplayInput.replace(/[^0-9.]/g, '')) / unitDivisor;
- const numCompletion = parseFloat(rawDisplayCompletion.replace(/[^0-9.]/g, '')) / unitDivisor;
-
- const displayInput = `${currency === 'CNY' ? '¥' : '$'}${numInput.toFixed(3)}`;
- const displayCompletion = `${currency === 'CNY' ? '¥' : '$'}${numCompletion.toFixed(3)}`;
+ if (priceData.isPerToken) {
return (
- {t('提示')} {displayInput} / 1{unitLabel} tokens
+ {t('提示')} {priceData.inputPrice} / 1{priceData.unitLabel} tokens
- {t('补全')} {displayCompletion} / 1{unitLabel} tokens
+ {t('补全')} {priceData.completionPrice} / 1{priceData.unitLabel} tokens
);
} else {
- const priceUSD = parseFloat(text) * groupRatio[selectedGroup];
- const displayVal = displayPrice(priceUSD);
return (
- {t('模型价格')}:{displayVal}
+ {t('模型价格')}:{priceData.price}
);
}
diff --git a/web/src/components/table/usage-logs/UsageLogsActions.jsx b/web/src/components/table/usage-logs/UsageLogsActions.jsx
index 72db01e4..c14ffcbf 100644
--- a/web/src/components/table/usage-logs/UsageLogsActions.jsx
+++ b/web/src/components/table/usage-logs/UsageLogsActions.jsx
@@ -40,7 +40,7 @@ const LogsActions = ({
setShowSkeleton(true);
} else {
const elapsed = Date.now() - loadingStartRef.current;
- const remaining = Math.max(0, 500 - elapsed);
+ const remaining = Math.max(0, 1000 - elapsed);
if (remaining === 0) {
setShowSkeleton(false);
} else {
diff --git a/web/src/helpers/utils.js b/web/src/helpers/utils.js
index 265be6c2..22b4fbc6 100644
--- a/web/src/helpers/utils.js
+++ b/web/src/helpers/utils.js
@@ -568,6 +568,59 @@ export const modelSelectFilter = (input, option) => {
return val.includes(input.trim().toLowerCase());
};
+// -------------------------------
+// 模型定价计算工具函数
+export const calculateModelPrice = ({
+ record,
+ selectedGroup,
+ groupRatio,
+ tokenUnit,
+ displayPrice,
+ currency,
+ precision = 3
+}) => {
+ if (record.quota_type === 0) {
+ // 按量计费
+ const inputRatioPriceUSD = record.model_ratio * 2 * groupRatio[selectedGroup];
+ const completionRatioPriceUSD =
+ record.model_ratio * record.completion_ratio * 2 * groupRatio[selectedGroup];
+
+ const unitDivisor = tokenUnit === 'K' ? 1000 : 1;
+ const unitLabel = tokenUnit === 'K' ? 'K' : 'M';
+
+ const rawDisplayInput = displayPrice(inputRatioPriceUSD);
+ const rawDisplayCompletion = displayPrice(completionRatioPriceUSD);
+
+ const numInput = parseFloat(rawDisplayInput.replace(/[^0-9.]/g, '')) / unitDivisor;
+ const numCompletion = parseFloat(rawDisplayCompletion.replace(/[^0-9.]/g, '')) / unitDivisor;
+
+ return {
+ inputPrice: `${currency === 'CNY' ? '¥' : '$'}${numInput.toFixed(precision)}`,
+ completionPrice: `${currency === 'CNY' ? '¥' : '$'}${numCompletion.toFixed(precision)}`,
+ unitLabel,
+ isPerToken: true
+ };
+ } else {
+ // 按次计费
+ const priceUSD = parseFloat(record.model_price) * groupRatio[selectedGroup];
+ const displayVal = displayPrice(priceUSD);
+
+ return {
+ price: displayVal,
+ isPerToken: false
+ };
+ }
+};
+
+// 格式化价格信息为字符串(用于卡片视图)
+export const formatPriceInfo = (priceData, t) => {
+ if (priceData.isPerToken) {
+ return `${t('输入')} ${priceData.inputPrice}/${priceData.unitLabel} ${t('输出')} ${priceData.completionPrice}/${priceData.unitLabel}`;
+ } else {
+ return `${t('模型价格')} ${priceData.price}`;
+ }
+};
+
// -------------------------------
// CardPro 分页配置函数
// 用于创建 CardPro 的 paginationArea 配置
@@ -626,8 +679,10 @@ export const resetPricingFilters = ({
setShowWithRecharge,
setCurrency,
setShowRatio,
+ setViewMode,
setFilterGroup,
setFilterQuotaType,
+ setCurrentPage,
}) => {
// 重置搜索
if (typeof handleChange === 'function') {
@@ -658,6 +713,11 @@ export const resetPricingFilters = ({
setShowRatio(false);
}
+ // 重置视图模式
+ if (typeof setViewMode === 'function') {
+ setViewMode('card');
+ }
+
// 重置分组筛选
if (typeof setFilterGroup === 'function') {
setFilterGroup('all');
@@ -667,4 +727,9 @@ export const resetPricingFilters = ({
if (typeof setFilterQuotaType === 'function') {
setFilterQuotaType('all');
}
+
+ // 重置当前页面
+ if (typeof setCurrentPage === 'function') {
+ setCurrentPage(1);
+ }
};
diff --git a/web/src/hooks/dashboard/useDashboardData.js b/web/src/hooks/dashboard/useDashboardData.js
index 4eaeca77..255f48d3 100644
--- a/web/src/hooks/dashboard/useDashboardData.js
+++ b/web/src/hooks/dashboard/useDashboardData.js
@@ -178,7 +178,7 @@ export const useDashboardData = (userState, userDispatch, statusState) => {
}
} finally {
const elapsed = Date.now() - startTime;
- const remainingTime = Math.max(0, 500 - elapsed);
+ const remainingTime = Math.max(0, 1000 - elapsed);
setTimeout(() => {
setLoading(false);
}, remainingTime);
diff --git a/web/src/hooks/model-pricing/useModelPricingData.js b/web/src/hooks/model-pricing/useModelPricingData.js
index ac58d817..c32ddf84 100644
--- a/web/src/hooks/model-pricing/useModelPricingData.js
+++ b/web/src/hooks/model-pricing/useModelPricingData.js
@@ -26,18 +26,17 @@ import { StatusContext } from '../../context/Status/index.js';
export const useModelPricingData = () => {
const { t } = useTranslation();
- const [filteredValue, setFilteredValue] = useState([]);
+ const [searchValue, setSearchValue] = useState('');
const compositionRef = useRef({ isComposition: false });
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [modalImageUrl, setModalImageUrl] = useState('');
const [isModalOpenurl, setIsModalOpenurl] = useState(false);
const [selectedGroup, setSelectedGroup] = useState('default');
- // 用于 Table 的可用分组筛选,“all” 表示不过滤
- const [filterGroup, setFilterGroup] = useState('all');
- // 计费类型筛选: 'all' | 0 | 1
- const [filterQuotaType, setFilterQuotaType] = useState('all');
+ const [filterGroup, setFilterGroup] = useState('all'); // 用于 Table 的可用分组筛选,“all” 表示不过滤
+ const [filterQuotaType, setFilterQuotaType] = useState('all'); // 计费类型筛选: 'all' | 0 | 1
const [activeKey, setActiveKey] = useState('all');
const [pageSize, setPageSize] = useState(10);
+ const [currentPage, setCurrentPage] = useState(1);
const [currency, setCurrency] = useState('USD');
const [showWithRecharge, setShowWithRecharge] = useState(false);
const [tokenUnit, setTokenUnit] = useState('M');
@@ -95,15 +94,15 @@ export const useModelPricingData = () => {
}
// 搜索筛选
- if (filteredValue.length > 0) {
- const searchTerm = filteredValue[0].toLowerCase();
+ if (searchValue.length > 0) {
+ const searchTerm = searchValue.toLowerCase();
result = result.filter(model =>
model.model_name.toLowerCase().includes(searchTerm)
);
}
return result;
- }, [activeKey, models, filteredValue, filterGroup, filterQuotaType]);
+ }, [activeKey, models, searchValue, filterGroup, filterQuotaType]);
const rowSelection = useMemo(
() => ({
@@ -183,8 +182,8 @@ export const useModelPricingData = () => {
if (compositionRef.current.isComposition) {
return;
}
- const newFilteredValue = value ? [value] : [];
- setFilteredValue(newFilteredValue);
+ const newSearchValue = value ? value : '';
+ setSearchValue(newSearchValue);
};
const handleCompositionStart = () => {
@@ -194,8 +193,8 @@ export const useModelPricingData = () => {
const handleCompositionEnd = (event) => {
compositionRef.current.isComposition = false;
const value = event.target.value;
- const newFilteredValue = value ? [value] : [];
- setFilteredValue(newFilteredValue);
+ const newSearchValue = value ? value : '';
+ setSearchValue(newSearchValue);
};
const handleGroupClick = (group) => {
@@ -214,10 +213,15 @@ export const useModelPricingData = () => {
refresh().then();
}, []);
+ // 当筛选条件变化时重置到第一页
+ useEffect(() => {
+ setCurrentPage(1);
+ }, [activeKey, filterGroup, filterQuotaType, searchValue]);
+
return {
// 状态
- filteredValue,
- setFilteredValue,
+ searchValue,
+ setSearchValue,
selectedRowKeys,
setSelectedRowKeys,
modalImageUrl,
@@ -234,6 +238,8 @@ export const useModelPricingData = () => {
setActiveKey,
pageSize,
setPageSize,
+ currentPage,
+ setCurrentPage,
currency,
setCurrency,
showWithRecharge,
diff --git a/web/src/index.css b/web/src/index.css
index afbb7862..b624d749 100644
--- a/web/src/index.css
+++ b/web/src/index.css
@@ -617,4 +617,59 @@ html:not(.dark) .blur-ball-teal {
height: calc(100vh - 77px);
max-height: calc(100vh - 77px);
}
+}
+
+/* ==================== 模型定价页面布局 ==================== */
+.pricing-layout {
+ height: calc(100vh - 60px);
+ overflow: hidden;
+ margin-top: 60px;
+}
+
+.pricing-sidebar {
+ min-width: 460px;
+ max-width: 460px;
+ height: calc(100vh - 60px);
+ background-color: var(--semi-color-bg-0);
+ border-right: 1px solid var(--semi-color-border);
+ overflow: auto;
+}
+
+.pricing-content {
+ height: calc(100vh - 60px);
+ background-color: var(--semi-color-bg-0);
+ display: flex;
+ flex-direction: column;
+}
+
+.pricing-pagination-divider {
+ border-color: var(--semi-color-border);
+}
+
+.pricing-content-mobile {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ overflow: auto;
+}
+
+.pricing-search-header {
+ padding: 16px 24px;
+ border-bottom: 1px solid var(--semi-color-border);
+ background-color: var(--semi-color-bg-0);
+ flex-shrink: 0;
+ position: sticky;
+ top: 0;
+ z-index: 5;
+}
+
+.pricing-view-container {
+ flex: 1;
+ overflow: auto;
+}
+
+.pricing-view-container-mobile {
+ flex: 1;
+ overflow: auto;
+ min-height: 0;
}
\ No newline at end of file
diff --git a/web/src/pages/Pricing/index.js b/web/src/pages/Pricing/index.js
index c1066203..e37167d8 100644
--- a/web/src/pages/Pricing/index.js
+++ b/web/src/pages/Pricing/index.js
@@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React from 'react';
-import ModelPricingPage from '../../components/table/model-pricing';
+import ModelPricingPage from '../../components/table/model-pricing/layout/PricingPage';
const Pricing = () => (
<>