refactor: catalog fetch data

This commit is contained in:
jialin
2026-01-06 17:21:32 +08:00
parent 17de9d98f4
commit 2816fedc36
9 changed files with 78 additions and 147 deletions
+1 -1
View File
@@ -56,7 +56,7 @@ const BarChart: React.FC<ChartProps & { maxItems?: number }> = (props) => {
overflow: 'truncate', overflow: 'truncate',
width: 75, width: 75,
ellipsis: '...', ellipsis: '...',
margin: 12, margin: 8,
formatter(value: string, index: number) { formatter(value: string, index: number) {
return `{a|${index + 1}}`; return `{a|${index + 1}}`;
}, },
+1 -1
View File
@@ -1,4 +1,4 @@
export default { export default {
'/playground/text-to-image': '/playground/text-to-image', '/playground/text-to-image': '/playground/text-to-image',
'/playground/speech': '/playground/speech' '/playground/speech': '/playground/speech'
}; } as Record<string, string>;
+3 -2
View File
@@ -2,7 +2,7 @@ import { COLOR_PRIMARY } from './index';
export default { export default {
'root-entry-name': 'variable', 'root-entry-name': 'variable',
cssVar: true, // cssVar: true,
hashed: false, hashed: false,
components: { components: {
Input: { Input: {
@@ -68,7 +68,8 @@ export default {
// colorBgSpotlight: '#333' // colorBgSpotlight: '#333'
}, },
Cascader: { Cascader: {
dropdownHeight: 240 dropdownHeight: 240,
optionSelectedFontWeight: 400
}, },
Slider: { Slider: {
handleSize: 8, handleSize: 8,
+3 -2
View File
@@ -2,7 +2,7 @@ import { COLOR_PRIMARY } from './index';
export default { export default {
'root-entry-name': 'variable', 'root-entry-name': 'variable',
cssVar: true, // cssVar: true,
hashed: false, hashed: false,
components: { components: {
Input: { Input: {
@@ -66,7 +66,8 @@ export default {
colorBgSpotlight: '#3e3e3e' colorBgSpotlight: '#3e3e3e'
}, },
Cascader: { Cascader: {
dropdownHeight: 240 dropdownHeight: 240,
optionSelectedFontWeight: 400
}, },
Slider: { Slider: {
handleSize: 8, handleSize: 8,
+17 -2
View File
@@ -24,11 +24,14 @@ type WatchConfig =
* @param events events to watch for chunked updates, default: ['UPDATE', 'DELETE'] * @param events events to watch for chunked updates, default: ['UPDATE', 'DELETE']
* @param defaultQueryParams default query parameters for fetching data * @param defaultQueryParams default query parameters for fetching data
* @param isInfiniteScroll whether to use infinite scroll mode , use in card list * @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 * @returns
*/ */
export default function useTableFetch<T>( export default function useTableFetch<T>(
options: { options: {
fetchAPI: (params: any) => Promise<Global.PageResponse<T>>; fetchAPI: (params: any, options?: any) => Promise<Global.PageResponse<T>>;
deleteAPI?: (id: number, params?: any) => Promise<any>; deleteAPI?: (id: number, params?: any) => Promise<any>;
contentForDelete?: string; contentForDelete?: string;
defaultData?: any[]; defaultData?: any[];
@@ -220,7 +223,7 @@ export default function useTableFetch<T>(
}, 5000); }, 5000);
}; };
// for selection change // for filters change
const handleQueryChange = (params: any) => { const handleQueryChange = (params: any) => {
setQueryParams({ setQueryParams({
...queryParams, ...queryParams,
@@ -258,6 +261,7 @@ export default function useTableFetch<T>(
} }
}; };
// for refresh button
const handleSearch = () => { const handleSearch = () => {
fetchData(); fetchData();
}; };
@@ -319,6 +323,16 @@ export default function useTableFetch<T>(
}); });
}; };
const loadMore = (nextPage: number) => {
fetchData({
query: {
...queryParams,
page: nextPage
},
loadmore: true
});
};
useEffect(() => { useEffect(() => {
if (dataSource.loadend) { if (dataSource.loadend) {
fetchAPIWithPolling(queryParams); fetchAPIWithPolling(queryParams);
@@ -360,6 +374,7 @@ export default function useTableFetch<T>(
handleTableChange, handleTableChange,
handleSearch, handleSearch,
handleQueryChange, handleQueryChange,
loadMore,
handleNameChange handleNameChange
}; };
} }
+3 -5
View File
@@ -1,13 +1,11 @@
// @ts-nocheck import { history, useIntl } from '@umijs/max';
import { history, useIntl, type IRoute } from '@umijs/max';
import { Button, Result } from 'antd'; import { Button, Result } from 'antd';
import React from 'react'; import React from 'react';
import { PageContainerInner } from '../pages/_components/page-box'; import { PageContainerInner } from '../pages/_components/page-box';
const Exception: React.FC<{ const Exception: React.FC<{
children: React.ReactNode; children: React.ReactNode;
route?: IRoute; route?: any;
notFound?: React.ReactNode; notFound?: React.ReactNode;
noAccessible?: React.ReactNode; noAccessible?: React.ReactNode;
unAccessible?: React.ReactNode; unAccessible?: React.ReactNode;
@@ -15,7 +13,7 @@ const Exception: React.FC<{
}> = (props) => { }> = (props) => {
const intl = useIntl(); const intl = useIntl();
// render custom 404 // render custom 404
console.log('exception====', props.route); console.log('exception====', props);
return ( return (
(!props.route && (props.noFound || props.notFound)) || (!props.route && (props.noFound || props.notFound)) ||
// render custom 403 // render custom 403
+24 -23
View File
@@ -1,5 +1,5 @@
import { routeCacheAtom, setRouteCache } from '@/atoms/route-cache'; 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 DarkMask from '@/components/dark-mask';
import IconFont from '@/components/icon-font'; import IconFont from '@/components/icon-font';
import routeCachekey from '@/config/route-cachekey'; import routeCachekey from '@/config/route-cachekey';
@@ -36,6 +36,7 @@ import { ExtraContent } from './extraRender';
import { patchRoutes } from './runtime'; import { patchRoutes } from './runtime';
import SiderMenu from './sider-menu'; import SiderMenu from './sider-menu';
// Pages that use the page container in the page
const NO_CONTAINER_PAGES = [ const NO_CONTAINER_PAGES = [
'chat', 'chat',
'rerank', 'rerank',
@@ -55,6 +56,11 @@ const CHECK_RESOURCE_PATH = [
'' ''
]; ];
type NewRoute = IRoute & {
children?: IRoute[];
routes?: IRoute[];
};
const loginPath = DEFAULT_ENTER_PAGE.login; 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 // 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 []; return [];
} }
let newRoutes = []; let newRoutes: NewRoute[] = [];
for (const route of routes) { for (const route of routes) {
const newRoute = { ...route }; const newRoute = { ...route };
if (filterFn(route)) { if (filterFn(route)) {
@@ -90,7 +96,7 @@ const mapRoutes = (routes: IRoute[], role: string) => {
return []; return [];
} }
return routes.map((route) => { return routes.map((route) => {
const newRoute = { ...route, role }; const newRoute: NewRoute = { ...route, role };
if (route.originPath) { if (route.originPath) {
newRoute.path = route.originPath; newRoute.path = route.originPath;
} }
@@ -111,7 +117,7 @@ export default (props: any) => {
const { initialize: initialize } = useOverlayScroller({ const { initialize: initialize } = useOverlayScroller({
defer: false defer: false
}); });
const [modal, contextHolder] = Modal.useModal(); const [, contextHolder] = Modal.useModal();
const { themeData, setUserSettings, userSettings } = useUserSettings(); const { themeData, setUserSettings, userSettings } = useUserSettings();
const [userInfo] = useAtom(userAtom); const [userInfo] = useAtom(userAtom);
const [routeCache] = useAtom(routeCacheAtom); const [routeCache] = useAtom(routeCacheAtom);
@@ -119,7 +125,6 @@ export default (props: any) => {
const navigate = useNavigate(); const navigate = useNavigate();
const intl = useIntl(); const intl = useIntl();
const { clientRoutes } = useAppData(); const { clientRoutes } = useAppData();
const [version] = useAtom(GPUStackVersionAtom);
const requestResourceRef = useRef<boolean>(false); const requestResourceRef = useRef<boolean>(false);
const { const {
@@ -128,7 +133,7 @@ export default (props: any) => {
NoResourceModal, NoResourceModal,
loadingStatus loadingStatus
} = useAddResource({ } = useAddResource({
onCreate() { onCreated() {
requestResourceRef.current = false; requestResourceRef.current = false;
} }
}); });
@@ -146,11 +151,11 @@ export default (props: any) => {
locale: true locale: true
}; };
const formatMessage = (args) => { const formatMessage = (args: { id: string }) => {
return intl.formatMessage({ id: args.id }); return intl.formatMessage({ id: args.id });
}; };
const initRouteCacheValue = (pathname) => { const initRouteCacheValue = (pathname: string) => {
if (routeCache.get(pathname) === undefined && routeCachekey[pathname]) { if (routeCache.get(pathname) === undefined && routeCachekey[pathname]) {
setRouteCache(pathname, false); setRouteCache(pathname, false);
} }
@@ -183,8 +188,8 @@ export default (props: any) => {
collapsed: !userSettings.collapsed collapsed: !userSettings.collapsed
}); });
}; };
const newRoutes = filterRoutes( const newRoutes = filterRoutes(
// @ts-ignore
clientRoutes.filter((route) => route.id === 'max-tabs'), clientRoutes.filter((route) => route.id === 'max-tabs'),
(route) => { (route) => {
return ( return (
@@ -208,11 +213,10 @@ export default (props: any) => {
); );
const isNoContainerPage = useMemo(() => { const isNoContainerPage = useMemo(() => {
return NO_CONTAINER_PAGES.includes(matchedRoute?.name); // @ts-ignore
return NO_CONTAINER_PAGES.includes(matchedRoute?.name as string);
}, [matchedRoute]); }, [matchedRoute]);
console.log('matchedRoute=========', matchedRoute, route);
useEffect(() => { useEffect(() => {
const body = document.querySelector('body'); const body = document.querySelector('body');
if (body) { if (body) {
@@ -338,19 +342,10 @@ export default (props: any) => {
}} }}
openKeys={false} openKeys={false}
disableMobile={true} disableMobile={true}
header={{
title: <div style={{ fontSize: 36 }}> gpuStack </div>
}}
siderWidth={220} siderWidth={220}
onCollapse={onCollapse} onCollapse={onCollapse}
onMenuHeaderClick={onMenuHeaderClick} onMenuHeaderClick={onMenuHeaderClick}
menuHeaderRender={renderMenuHeader} menuHeaderRender={renderMenuHeader}
extra={[
<ExtraContent
key="extra-content"
isDarkTheme={userSettings.isDarkTheme}
/>
]}
collapsed={userSettings.collapsed} collapsed={userSettings.collapsed}
onPageChange={onPageChange} onPageChange={onPageChange}
formatMessage={formatMessage} formatMessage={formatMessage}
@@ -361,14 +356,20 @@ export default (props: any) => {
splitMenus={true} splitMenus={true}
logo={userSettings.collapsed ? <SLogoIcon /> : <LogoIcon />} logo={userSettings.collapsed ? <SLogoIcon /> : <LogoIcon />}
menuContentRender={menuContentRender} menuContentRender={menuContentRender}
disableContentMargin
{...runtimeConfig} {...runtimeConfig}
ErrorBoundary={ErrorBoundary} ErrorBoundary={ErrorBoundary}
// @ts-ignore
extra={[
<ExtraContent
key="extra-content"
isDarkTheme={userSettings.isDarkTheme}
/>
]}
> >
<Exception <Exception
route={matchedRoute} route={matchedRoute}
noFound={runtimeConfig?.noFound}
notFound={runtimeConfig?.notFound} notFound={runtimeConfig?.notFound}
noFound={runtimeConfig?.noFound}
unAccessible={runtimeConfig?.unAccessible} unAccessible={runtimeConfig?.unAccessible}
noAccessible={runtimeConfig?.noAccessible} noAccessible={runtimeConfig?.noAccessible}
> >
+8 -8
View File
@@ -30,6 +30,8 @@ export const MY_MODELS_API = '/my-models';
export const DRAFT_MODELS_API = '/draft-models'; export const DRAFT_MODELS_API = '/draft-models';
export const CATALOG_LIST_API = '/model-sets';
const setProxyUrl = (url: string) => { const setProxyUrl = (url: string) => {
return `/proxy?url=${encodeURIComponent(url)}`; return `/proxy?url=${encodeURIComponent(url)}`;
}; };
@@ -357,13 +359,11 @@ export async function queryCatalogList(
params: Global.SearchParams, params: Global.SearchParams,
options?: any options?: any
) { ) {
return request<Global.PageResponse<CatalogItem>>( return request<Global.PageResponse<CatalogItem>>(`${CATALOG_LIST_API}`, {
`/model-sets?${qs.stringify(params)}`, method: 'GET',
{ params,
method: 'GET', cancelToken: options?.token
...options });
}
);
} }
export async function queryCatalogItemSpec( export async function queryCatalogItemSpec(
@@ -371,7 +371,7 @@ export async function queryCatalogItemSpec(
options?: any options?: any
) { ) {
return request<Global.PageResponse<CatalogSpec>>( return request<Global.PageResponse<CatalogSpec>>(
`/model-sets/${params.id}/specs`, `${CATALOG_LIST_API}/${params.id}/specs`,
{ {
method: 'GET', method: 'GET',
...options, ...options,
+18 -103
View File
@@ -3,11 +3,11 @@ import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools'; import { FilterBar } from '@/components/page-tools';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import useBodyScroll from '@/hooks/use-body-scroll'; 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 { ScrollerContext } from '@/pages/_components/infinite-scroller/use-scroller-context';
import { IS_FIRST_LOGIN, writeState } from '@/utils/localstore/index'; import { IS_FIRST_LOGIN, writeState } from '@/utils/localstore/index';
import { SearchOutlined } from '@ant-design/icons'; import { SearchOutlined } from '@ant-design/icons';
import { useIntl, useNavigate } from '@umijs/max'; import { useIntl, useNavigate } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { message } from 'antd'; import { message } from 'antd';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import _ from 'lodash'; import _ from 'lodash';
@@ -22,27 +22,24 @@ import { CatalogItem as CatalogItemType, FormData } from './config/types';
const Catalog: React.FC = () => { const Catalog: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const {
dataSource,
queryParams,
handleSearch,
handleQueryChange,
loadMore,
handleNameChange
} = useTableFetch<CatalogItemType>({
fetchAPI: queryCatalogList,
watch: false,
isInfiniteScroll: true,
defaultQueryParams: {
perPage: 24
}
});
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll(); const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
const navigate = useNavigate(); 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<any>({ const [openDeployModal, setOpenDeployModal] = useState<any>({
show: false, show: false,
width: 600, width: 600,
@@ -51,66 +48,12 @@ const Catalog: React.FC = () => {
}); });
const [, setModelsExpandKeys] = useAtom(modelsExpandKeysAtom); const [, setModelsExpandKeys] = useAtom(modelsExpandKeysAtom);
const [, setModelsSession] = useAtom(modelsSessionAtom); const [, setModelsSession] = useAtom(modelsSessionAtom);
const cacheData = React.useRef<CatalogItemType[]>([]);
const sourceRef = React.useRef<string>(''); const sourceRef = React.useRef<string>('');
const categoryOptions = [ const categoryOptions = [
...modelCategories.filter((item) => item.value) ...modelCategories.filter((item) => item.value)
] as Global.BaseOption<string>[]; ] as Global.BaseOption<string>[];
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 = () => { const handleDeployModalCancel = () => {
setOpenDeployModal({ setOpenDeployModal({
...openDeployModal, ...openDeployModal,
@@ -150,36 +93,12 @@ const Catalog: React.FC = () => {
[openDeployModal] [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) => { const handleCategoryChange = (value: any) => {
fetchData({ handleQueryChange({
...queryParams,
page: 1,
categories: value categories: value
}); });
}; };
const loadMore = useMemoizedFn((nextPage: number) => {
fetchData({
...queryParams,
page: nextPage
});
});
const handleDeployFromOtherHubs = async () => { const handleDeployFromOtherHubs = async () => {
try { try {
setModelsSession({ setModelsSession({
@@ -189,10 +108,6 @@ const Catalog: React.FC = () => {
navigate('/models/deployments'); navigate('/models/deployments');
}; };
useEffect(() => {
fetchData();
}, []);
useEffect(() => { useEffect(() => {
if (dataSource.loadend) { if (dataSource.loadend) {
const getCatalogSource = async () => { const getCatalogSource = async () => {