From 2816fedc361ee1a97515aa2c0fb0ac6fcc501dec Mon Sep 17 00:00:00 2001 From: jialin Date: Wed, 31 Dec 2025 16:24:40 +0800 Subject: [PATCH] refactor: catalog fetch data --- src/components/echarts/h-bar.tsx | 2 +- src/config/route-cachekey.ts | 2 +- src/config/theme/dark.ts | 5 +- src/config/theme/light.ts | 5 +- src/hooks/use-table-fetch.ts | 19 ++++- src/layouts/Exception.tsx | 8 +- src/layouts/index.tsx | 47 ++++++------ src/pages/llmodels/apis/index.ts | 16 ++-- src/pages/llmodels/catalog.tsx | 121 +++++-------------------------- 9 files changed, 78 insertions(+), 147 deletions(-) diff --git a/src/components/echarts/h-bar.tsx b/src/components/echarts/h-bar.tsx index 4fc3d481..629ecb84 100644 --- a/src/components/echarts/h-bar.tsx +++ b/src/components/echarts/h-bar.tsx @@ -56,7 +56,7 @@ const BarChart: React.FC = (props) => { overflow: 'truncate', width: 75, ellipsis: '...', - margin: 12, + margin: 8, formatter(value: string, index: number) { return `{a|${index + 1}}`; }, diff --git a/src/config/route-cachekey.ts b/src/config/route-cachekey.ts index 002b9a81..bda960aa 100644 --- a/src/config/route-cachekey.ts +++ b/src/config/route-cachekey.ts @@ -1,4 +1,4 @@ export default { '/playground/text-to-image': '/playground/text-to-image', '/playground/speech': '/playground/speech' -}; +} as Record; diff --git a/src/config/theme/dark.ts b/src/config/theme/dark.ts index 34671a48..dcc88897 100644 --- a/src/config/theme/dark.ts +++ b/src/config/theme/dark.ts @@ -2,7 +2,7 @@ import { COLOR_PRIMARY } from './index'; export default { 'root-entry-name': 'variable', - cssVar: true, + // cssVar: true, hashed: false, components: { Input: { @@ -68,7 +68,8 @@ export default { // colorBgSpotlight: '#333' }, Cascader: { - dropdownHeight: 240 + dropdownHeight: 240, + optionSelectedFontWeight: 400 }, Slider: { handleSize: 8, diff --git a/src/config/theme/light.ts b/src/config/theme/light.ts index 69678ed6..62087b4e 100644 --- a/src/config/theme/light.ts +++ b/src/config/theme/light.ts @@ -2,7 +2,7 @@ import { COLOR_PRIMARY } from './index'; export default { 'root-entry-name': 'variable', - cssVar: true, + // cssVar: true, hashed: false, components: { Input: { @@ -66,7 +66,8 @@ export default { colorBgSpotlight: '#3e3e3e' }, Cascader: { - dropdownHeight: 240 + dropdownHeight: 240, + optionSelectedFontWeight: 400 }, Slider: { handleSize: 8, diff --git a/src/hooks/use-table-fetch.ts b/src/hooks/use-table-fetch.ts index 40112e01..84e2de78 100644 --- a/src/hooks/use-table-fetch.ts +++ b/src/hooks/use-table-fetch.ts @@ -24,11 +24,14 @@ type WatchConfig = * @param events events to watch for chunked updates, default: ['UPDATE', 'DELETE'] * @param defaultQueryParams default query parameters for fetching data * @param isInfiniteScroll whether to use infinite scroll mode , use in card list + * @param API use to create chunked request url when watch is true + * @param watch whether to watch for chunked updates + * @param polling whether to enable polling for data fetching * @returns */ export default function useTableFetch( options: { - fetchAPI: (params: any) => Promise>; + fetchAPI: (params: any, options?: any) => Promise>; deleteAPI?: (id: number, params?: any) => Promise; contentForDelete?: string; defaultData?: any[]; @@ -220,7 +223,7 @@ export default function useTableFetch( }, 5000); }; - // for selection change + // for filters change const handleQueryChange = (params: any) => { setQueryParams({ ...queryParams, @@ -258,6 +261,7 @@ export default function useTableFetch( } }; + // for refresh button const handleSearch = () => { fetchData(); }; @@ -319,6 +323,16 @@ export default function useTableFetch( }); }; + const loadMore = (nextPage: number) => { + fetchData({ + query: { + ...queryParams, + page: nextPage + }, + loadmore: true + }); + }; + useEffect(() => { if (dataSource.loadend) { fetchAPIWithPolling(queryParams); @@ -360,6 +374,7 @@ export default function useTableFetch( handleTableChange, handleSearch, handleQueryChange, + loadMore, handleNameChange }; } diff --git a/src/layouts/Exception.tsx b/src/layouts/Exception.tsx index 72ab1e68..6e3ccf1a 100644 --- a/src/layouts/Exception.tsx +++ b/src/layouts/Exception.tsx @@ -1,13 +1,11 @@ -// @ts-nocheck - -import { history, useIntl, type IRoute } from '@umijs/max'; +import { history, useIntl } from '@umijs/max'; import { Button, Result } from 'antd'; import React from 'react'; import { PageContainerInner } from '../pages/_components/page-box'; const Exception: React.FC<{ children: React.ReactNode; - route?: IRoute; + route?: any; notFound?: React.ReactNode; noAccessible?: React.ReactNode; unAccessible?: React.ReactNode; @@ -15,7 +13,7 @@ const Exception: React.FC<{ }> = (props) => { const intl = useIntl(); // render custom 404 - console.log('exception====', props.route); + console.log('exception====', props); return ( (!props.route && (props.noFound || props.notFound)) || // render custom 403 diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 9e3f72c8..8b723785 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -1,5 +1,5 @@ import { routeCacheAtom, setRouteCache } from '@/atoms/route-cache'; -import { GPUStackVersionAtom, userAtom } from '@/atoms/user'; +import { userAtom } from '@/atoms/user'; import DarkMask from '@/components/dark-mask'; import IconFont from '@/components/icon-font'; import routeCachekey from '@/config/route-cachekey'; @@ -36,6 +36,7 @@ import { ExtraContent } from './extraRender'; import { patchRoutes } from './runtime'; import SiderMenu from './sider-menu'; +// Pages that use the page container in the page const NO_CONTAINER_PAGES = [ 'chat', 'rerank', @@ -55,6 +56,11 @@ const CHECK_RESOURCE_PATH = [ '' ]; +type NewRoute = IRoute & { + children?: IRoute[]; + routes?: IRoute[]; +}; + const loginPath = DEFAULT_ENTER_PAGE.login; // Filter out the routes that need to be displayed, where filterFn indicates the levels that should not be shown @@ -66,7 +72,7 @@ const filterRoutes = ( return []; } - let newRoutes = []; + let newRoutes: NewRoute[] = []; for (const route of routes) { const newRoute = { ...route }; if (filterFn(route)) { @@ -90,7 +96,7 @@ const mapRoutes = (routes: IRoute[], role: string) => { return []; } return routes.map((route) => { - const newRoute = { ...route, role }; + const newRoute: NewRoute = { ...route, role }; if (route.originPath) { newRoute.path = route.originPath; } @@ -111,7 +117,7 @@ export default (props: any) => { const { initialize: initialize } = useOverlayScroller({ defer: false }); - const [modal, contextHolder] = Modal.useModal(); + const [, contextHolder] = Modal.useModal(); const { themeData, setUserSettings, userSettings } = useUserSettings(); const [userInfo] = useAtom(userAtom); const [routeCache] = useAtom(routeCacheAtom); @@ -119,7 +125,6 @@ export default (props: any) => { const navigate = useNavigate(); const intl = useIntl(); const { clientRoutes } = useAppData(); - const [version] = useAtom(GPUStackVersionAtom); const requestResourceRef = useRef(false); const { @@ -128,7 +133,7 @@ export default (props: any) => { NoResourceModal, loadingStatus } = useAddResource({ - onCreate() { + onCreated() { requestResourceRef.current = false; } }); @@ -146,11 +151,11 @@ export default (props: any) => { locale: true }; - const formatMessage = (args) => { + const formatMessage = (args: { id: string }) => { return intl.formatMessage({ id: args.id }); }; - const initRouteCacheValue = (pathname) => { + const initRouteCacheValue = (pathname: string) => { if (routeCache.get(pathname) === undefined && routeCachekey[pathname]) { setRouteCache(pathname, false); } @@ -183,8 +188,8 @@ export default (props: any) => { collapsed: !userSettings.collapsed }); }; - const newRoutes = filterRoutes( + // @ts-ignore clientRoutes.filter((route) => route.id === 'max-tabs'), (route) => { return ( @@ -208,11 +213,10 @@ export default (props: any) => { ); const isNoContainerPage = useMemo(() => { - return NO_CONTAINER_PAGES.includes(matchedRoute?.name); + // @ts-ignore + return NO_CONTAINER_PAGES.includes(matchedRoute?.name as string); }, [matchedRoute]); - console.log('matchedRoute=========', matchedRoute, route); - useEffect(() => { const body = document.querySelector('body'); if (body) { @@ -338,19 +342,10 @@ export default (props: any) => { }} openKeys={false} disableMobile={true} - header={{ - title:
gpuStack
- }} siderWidth={220} onCollapse={onCollapse} onMenuHeaderClick={onMenuHeaderClick} menuHeaderRender={renderMenuHeader} - extra={[ - - ]} collapsed={userSettings.collapsed} onPageChange={onPageChange} formatMessage={formatMessage} @@ -361,14 +356,20 @@ export default (props: any) => { splitMenus={true} logo={userSettings.collapsed ? : } menuContentRender={menuContentRender} - disableContentMargin {...runtimeConfig} ErrorBoundary={ErrorBoundary} + // @ts-ignore + extra={[ + + ]} > diff --git a/src/pages/llmodels/apis/index.ts b/src/pages/llmodels/apis/index.ts index f9665c9c..68802287 100644 --- a/src/pages/llmodels/apis/index.ts +++ b/src/pages/llmodels/apis/index.ts @@ -30,6 +30,8 @@ export const MY_MODELS_API = '/my-models'; export const DRAFT_MODELS_API = '/draft-models'; +export const CATALOG_LIST_API = '/model-sets'; + const setProxyUrl = (url: string) => { return `/proxy?url=${encodeURIComponent(url)}`; }; @@ -357,13 +359,11 @@ export async function queryCatalogList( params: Global.SearchParams, options?: any ) { - return request>( - `/model-sets?${qs.stringify(params)}`, - { - method: 'GET', - ...options - } - ); + return request>(`${CATALOG_LIST_API}`, { + method: 'GET', + params, + cancelToken: options?.token + }); } export async function queryCatalogItemSpec( @@ -371,7 +371,7 @@ export async function queryCatalogItemSpec( options?: any ) { return request>( - `/model-sets/${params.id}/specs`, + `${CATALOG_LIST_API}/${params.id}/specs`, { method: 'GET', ...options, diff --git a/src/pages/llmodels/catalog.tsx b/src/pages/llmodels/catalog.tsx index 25dc39b6..01f04d4a 100644 --- a/src/pages/llmodels/catalog.tsx +++ b/src/pages/llmodels/catalog.tsx @@ -3,11 +3,11 @@ import IconFont from '@/components/icon-font'; import { FilterBar } from '@/components/page-tools'; import { PageAction } from '@/config'; import useBodyScroll from '@/hooks/use-body-scroll'; +import useTableFetch from '@/hooks/use-table-fetch'; import { ScrollerContext } from '@/pages/_components/infinite-scroller/use-scroller-context'; import { IS_FIRST_LOGIN, writeState } from '@/utils/localstore/index'; import { SearchOutlined } from '@ant-design/icons'; import { useIntl, useNavigate } from '@umijs/max'; -import { useMemoizedFn } from 'ahooks'; import { message } from 'antd'; import { useAtom } from 'jotai'; import _ from 'lodash'; @@ -22,27 +22,24 @@ import { CatalogItem as CatalogItemType, FormData } from './config/types'; const Catalog: React.FC = () => { const intl = useIntl(); + const { + dataSource, + queryParams, + handleSearch, + handleQueryChange, + loadMore, + handleNameChange + } = useTableFetch({ + fetchAPI: queryCatalogList, + watch: false, + isInfiniteScroll: true, + defaultQueryParams: { + perPage: 24 + } + }); const { saveScrollHeight, restoreScrollHeight } = useBodyScroll(); const navigate = useNavigate(); - const [dataSource, setDataSource] = useState<{ - dataList: CatalogItemType[]; - loading: boolean; - total: number; - loadend: boolean; - totalPage: number; - }>({ - dataList: [], - loading: false, - loadend: false, - total: 0, - totalPage: 0 - }); - const [queryParams, setQueryParams] = useState({ - page: 1, - perPage: 24, - search: '', - categories: '' - }); + const [openDeployModal, setOpenDeployModal] = useState({ show: false, width: 600, @@ -51,66 +48,12 @@ const Catalog: React.FC = () => { }); const [, setModelsExpandKeys] = useAtom(modelsExpandKeysAtom); const [, setModelsSession] = useAtom(modelsSessionAtom); - const cacheData = React.useRef([]); const sourceRef = React.useRef(''); const categoryOptions = [ ...modelCategories.filter((item) => item.value) ] as Global.BaseOption[]; - const fetchData = useMemoizedFn(async (query?: any) => { - const searchQuery = { - ...queryParams, - ...query - }; - if ( - dataSource.loading || - (searchQuery.page > dataSource.totalPage && dataSource.totalPage > 0) - ) { - return; - } - setDataSource((pre) => { - pre.loading = true; - - return { ...pre }; - }); - try { - const params = { - ..._.pickBy(searchQuery, (val: string | number) => !!val) - }; - const res: any = await queryCatalogList(params); - - const dataList = - searchQuery.page === 1 - ? res.items - : _.concat(dataSource.dataList, res.items); - setDataSource({ - dataList: dataList, - loading: false, - loadend: true, - total: res.pagination.total, - totalPage: res.pagination.totalPage - }); - setQueryParams({ - ...queryParams, - ...query - }); - } catch (error) { - cacheData.current = []; - setDataSource({ - dataList: [], - loading: false, - loadend: true, - total: dataSource.total, - totalPage: dataSource.totalPage - }); - setQueryParams({ - ...queryParams, - ...query - }); - } - }); - const handleDeployModalCancel = () => { setOpenDeployModal({ ...openDeployModal, @@ -150,36 +93,12 @@ const Catalog: React.FC = () => { [openDeployModal] ); - const handleSearch = () => { - fetchData({ - ...queryParams, - page: 1 - }); - }; - - const handleNameChange = _.debounce((e: any) => { - fetchData({ - ...queryParams, - page: 1, - search: e.target.value - }); - }, 200); - const handleCategoryChange = (value: any) => { - fetchData({ - ...queryParams, - page: 1, + handleQueryChange({ categories: value }); }; - const loadMore = useMemoizedFn((nextPage: number) => { - fetchData({ - ...queryParams, - page: nextPage - }); - }); - const handleDeployFromOtherHubs = async () => { try { setModelsSession({ @@ -189,10 +108,6 @@ const Catalog: React.FC = () => { navigate('/models/deployments'); }; - useEffect(() => { - fetchData(); - }, []); - useEffect(() => { if (dataSource.loadend) { const getCatalogSource = async () => {