import { modelsExpandKeysAtom, modelsSessionAtom } from '@/atoms/models'; import IconFont from '@/components/icon-font'; import { FilterBar } from '@/components/page-tools'; import { PageAction } from '@/config'; import useBodyScroll from '@/hooks/use-body-scroll'; 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'; import React, { useCallback, useEffect, useState } from 'react'; import NoResult from '../_components/no-result'; import PageBox from '../_components/page-box'; import { createModel, queryCatalogItemSpec, queryCatalogList } from './apis'; import CatalogList from './components/catalog-list'; import DelopyBuiltInModal from './components/deploy-builtin-modal'; import { modelCategories, modelSourceMap } from './config'; import { CatalogItem as CatalogItemType, FormData } from './config/types'; const Catalog: React.FC = () => { const intl = useIntl(); const { saveScrollHeight, restoreScrollHeight } = useBodyScroll(); const navigate = useNavigate(); const [activeId, setActiveId] = React.useState(-1); 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, current: {}, source: modelSourceMap.huggingface_value }); const [modelsExpandKeys, 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 }); console.log('error', error); } }); const handleDeployModalCancel = () => { setOpenDeployModal({ ...openDeployModal, show: false }); restoreScrollHeight(); setActiveId(-1); }; const handleOnDeploy = useCallback(async (item: CatalogItemType) => { saveScrollHeight(); setActiveId(item.id); setOpenDeployModal({ show: true, source: sourceRef.current, current: item, width: 600 }); }, []); const handleCreateModel = useCallback( async (data: FormData) => { try { const modelData = await createModel({ data: { ..._.omit(data, ['size', 'quantization']) } }); writeState(IS_FIRST_LOGIN, false); setOpenDeployModal({ ...openDeployModal, show: false }); message.success(intl.formatMessage({ id: 'common.message.success' })); setModelsExpandKeys([modelData.id]); navigate('/models/deployments'); } catch (error) {} }, [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, categories: value }); }; const loadMore = useMemoizedFn((nextPage: number) => { fetchData({ ...queryParams, page: nextPage }); }); const handleDeployFromOtherHubs = async () => { console.log('sourceRef.current', sourceRef.current); try { setModelsSession({ source: sourceRef.current || modelSourceMap.huggingface_value }); } catch (error) {} navigate('/models/deployments'); }; useEffect(() => { fetchData(); }, []); useEffect(() => { if (dataSource.loadend) { const getCatalogSource = async () => { try { const id = dataSource.dataList?.[0]?.id; const res: any = await queryCatalogItemSpec({ id }); sourceRef.current = res?.items?.[0]?.source; } catch (error) {} }; getCatalogSource(); } }, [dataSource.loadend]); useEffect(() => { const handleScroll = async () => { // Determine the scrolling element const scrollingElement = document.documentElement || document.body; // Calculate if the user has scrolled to the bottom const isAtBottom = scrollingElement.scrollTop + scrollingElement.clientHeight >= scrollingElement.scrollHeight - 20; // Adding a small buffer for precision if (isAtBottom) { fetchData({ ...queryParams, page: queryParams.page + 1 }); } }; window.addEventListener('scroll', handleScroll); return () => { window.removeEventListener('scroll', handleScroll); }; }, [fetchData]); return ( } width={{ input: 230, select: 200 }} > } filters={queryParams} noFoundText={intl.formatMessage({ id: 'noresult.catalog.nofound' })} title={intl.formatMessage({ id: 'noresult.catalog.title' })} subTitle={intl.formatMessage({ id: 'noresult.catalog.subTitle' })} > ); }; export default Catalog;