chore: catalog: scroll to load more data

This commit is contained in:
jialin
2025-05-07 16:34:12 +08:00
parent 42f9a27851
commit 816c22979f
8 changed files with 126 additions and 82 deletions
-1
View File
@@ -28,7 +28,6 @@ const HighlightCode: React.FC<{
const currentTheme = React.useMemo(() => { const currentTheme = React.useMemo(() => {
const res = theme || userSettings.theme === 'realDark' ? 'dark' : 'light'; const res = theme || userSettings.theme === 'realDark' ? 'dark' : 'light';
console.log('currentTheme:', res, userSettings.theme, '===>', theme);
return res; return res;
}, [theme, userSettings.theme]); }, [theme, userSettings.theme]);
+1 -2
View File
@@ -75,7 +75,6 @@ export default {
borderRadiusSM: 2, borderRadiusSM: 2,
colorBgContainer: '#fff', colorBgContainer: '#fff',
fontSize: 14, fontSize: 14,
motion: true, motion: true
colorFillTertiary: '#f4f5f4'
} }
}; };
+1
View File
@@ -14,6 +14,7 @@ export default function useBodyScroll() {
window.__GPUSTACK_BODY_SCROLLER__?.elements()?.scrollEventElement; window.__GPUSTACK_BODY_SCROLLER__?.elements()?.scrollEventElement;
instanceRef.current = window.__GPUSTACK_BODY_SCROLLER__; instanceRef.current = window.__GPUSTACK_BODY_SCROLLER__;
console.log('bodyScroller', bodyScroller.current, instanceRef.current);
}; };
const saveScrollHeight = React.useCallback(() => { const saveScrollHeight = React.useCallback(() => {
+96 -57
View File
@@ -10,12 +10,18 @@ import { Button, Input, Pagination, Select, Space, message } from 'antd';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import _ from 'lodash'; import _ from 'lodash';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';
import { createModel, queryCatalogList } from './apis'; import { createModel, queryCatalogList } from './apis';
import CatalogList from './components/catalog-list'; import CatalogList from './components/catalog-list';
import DelopyBuiltInModal from './components/deploy-builtin-modal'; import DelopyBuiltInModal from './components/deploy-builtin-modal';
import { modelCategories, modelSourceMap } from './config'; import { modelCategories, modelSourceMap } from './config';
import { CatalogItem as CatalogItemType, FormData } from './config/types'; import { CatalogItem as CatalogItemType, FormData } from './config/types';
const PageWrapper = styled.div`
display: none;
margin-block: 32px 16px;
`;
const Catalog: React.FC = () => { const Catalog: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll(); const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
@@ -26,14 +32,16 @@ const Catalog: React.FC = () => {
dataList: CatalogItemType[]; dataList: CatalogItemType[];
loading: boolean; loading: boolean;
total: number; total: number;
totalPage: number;
}>({ }>({
dataList: [], dataList: [],
loading: false, loading: false,
total: 0 total: 0,
totalPage: 0
}); });
const [queryParams, setQueryParams] = useState({ const [queryParams, setQueryParams] = useState({
page: 1, page: 1,
perPage: 100, perPage: 12,
search: '', search: '',
categories: '' categories: ''
}); });
@@ -71,39 +79,62 @@ const Catalog: React.FC = () => {
[cacheData.current] [cacheData.current]
); );
const fetchData = useCallback(async () => { const fetchData = useCallback(
setDataSource((pre) => { async (query?: any) => {
pre.loading = true; const searchQuery = {
return { ...pre }; ...queryParams,
}); ...query
try {
const params = {
..._.pick(queryParams, ['page', 'perPage'])
}; };
const res: any = await queryCatalogList(params); if (
dataSource.loading ||
(searchQuery.page > dataSource.totalPage && dataSource.totalPage > 0)
) {
return;
}
setDataSource((pre) => {
pre.loading = true;
cacheData.current = res.items || []; return { ...pre };
const dataList = filterData({
search: queryParams.search,
categories: queryParams.categories
}); });
setDataSource({ try {
dataList: dataList, const params = {
loading: false, ..._.pickBy(searchQuery, (val: string | number) => !!val)
total: res.pagination.total };
}); const res: any = await queryCatalogList(params);
} catch (error) {
cacheData.current = []; const dataList =
setDataSource({ searchQuery.page === 1
dataList: [], ? res.items
loading: false, : _.concat(dataSource.dataList, res.items);
total: dataSource.total setDataSource({
}); dataList: dataList,
console.log('error', error); loading: false,
} finally { total: res.pagination.total,
setIsFirst(false); totalPage: res.pagination.totalPage
} });
}, [queryParams, cacheData.current]); setQueryParams({
...queryParams,
...query
});
} catch (error) {
cacheData.current = [];
setDataSource({
dataList: [],
loading: false,
total: dataSource.total,
totalPage: dataSource.totalPage
});
setQueryParams({
...queryParams,
...query
});
console.log('error', error);
} finally {
setIsFirst(false);
}
},
[queryParams, cacheData.current]
);
const handleDeployModalCancel = () => { const handleDeployModalCancel = () => {
setOpenDeployModal({ setOpenDeployModal({
@@ -160,49 +191,57 @@ const Catalog: React.FC = () => {
); );
const handleSearch = (e: any) => { const handleSearch = (e: any) => {
fetchData(); fetchData({
...queryParams,
page: 1
});
}; };
const handleNameChange = _.debounce((e: any) => { const handleNameChange = _.debounce((e: any) => {
const dataList = filterData({ fetchData({
search: e.target.value,
categories: queryParams.categories
});
setQueryParams({
...queryParams, ...queryParams,
page: 1, page: 1,
search: e.target.value search: e.target.value
}); });
setDataSource({
dataList,
loading: false,
total: dataSource.total
});
}, 200); }, 200);
const handleCategoryChange = (value: any) => { const handleCategoryChange = (value: any) => {
const dataList = filterData({ fetchData({
search: queryParams.search,
categories: value
});
setQueryParams({
...queryParams, ...queryParams,
page: 1, page: 1,
categories: value categories: value
}); });
setDataSource({
dataList,
loading: false,
total: dataSource.total
});
}; };
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
}, []); }, []);
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 ( return (
<PageContainer <PageContainer
ghost ghost
@@ -259,7 +298,7 @@ const Catalog: React.FC = () => {
activeId={-1} activeId={-1}
isFirst={isFirst} isFirst={isFirst}
></CatalogList> ></CatalogList>
<div style={{ marginBlock: '32px 16px' }}> <PageWrapper>
<Pagination <Pagination
hideOnSinglePage={queryParams.perPage === 100} hideOnSinglePage={queryParams.perPage === 100}
align="end" align="end"
@@ -269,7 +308,7 @@ const Catalog: React.FC = () => {
showSizeChanger showSizeChanger
onChange={handleOnPageChange} onChange={handleOnPageChange}
/> />
</div> </PageWrapper>
<DelopyBuiltInModal <DelopyBuiltInModal
open={openDeployModal.show} open={openDeployModal.show}
action={PageAction.CREATE} action={PageAction.CREATE}
+15 -14
View File
@@ -3,10 +3,23 @@ import { Col, FloatButton, Row, Spin } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import ResizeObserver from 'rc-resize-observer'; import ResizeObserver from 'rc-resize-observer';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import styled from 'styled-components';
import { CatalogItem as CatalogItemType } from '../config/types'; import { CatalogItem as CatalogItemType } from '../config/types';
import CatalogItem from './catalog-item'; import CatalogItem from './catalog-item';
import CatalogSkelton from './catalog-skelton'; import CatalogSkelton from './catalog-skelton';
const SpinWrapper = styled.div`
width: 100%;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
top: 0;
left: 0;
height: 400px;
right: 0;
`;
interface CatalogListProps { interface CatalogListProps {
dataList: any[]; dataList: any[];
loading: boolean; loading: boolean;
@@ -23,19 +36,7 @@ const ListSkeleton: React.FC<{
return ( return (
<div> <div>
{loading && ( {loading && (
<div <SpinWrapper>
style={{
width: '100%',
position: 'absolute',
display: 'flex',
alignItems: 'flex-start',
justifyContent: 'center',
top: 0,
left: 0,
height: 400,
right: 0
}}
>
<Spin <Spin
spinning={loading} spinning={loading}
style={{ style={{
@@ -45,7 +46,7 @@ const ListSkeleton: React.FC<{
> >
{isFirst && <CatalogSkelton span={span}></CatalogSkelton>} {isFirst && <CatalogSkelton span={span}></CatalogSkelton>}
</Spin> </Spin>
</div> </SpinWrapper>
)} )}
</div> </div>
); );
@@ -91,7 +91,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
return undefined; return undefined;
}; };
const handleOnValuesChange = (data: any) => { const handleOnValuesChange = _.debounce((data: any) => {
const formdata = form.getFieldsValue?.(); const formdata = form.getFieldsValue?.();
let alldata = {}; let alldata = {};
@@ -116,7 +116,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
} }
const originalData = _.pick(originFormData.current, Object.keys(alldata)); const originalData = _.pick(originFormData.current, Object.keys(alldata));
console.log('alldata:', alldata, originalData); console.log('alldata:', formdata, alldata, originalData);
const isEqual = _.isEqualWith( const isEqual = _.isEqualWith(
_.omit(alldata, updateIgnoreFields), _.omit(alldata, updateIgnoreFields),
@@ -137,7 +137,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
}) })
}); });
} }
}; }, 100);
// voxbox is not support multi gpu // voxbox is not support multi gpu
const handleSetGPUIds = (backend: string) => { const handleSetGPUIds = (backend: string) => {
+1
View File
@@ -1,3 +1,4 @@
// this list is copied from llamacpp repo
enum FileType { enum FileType {
F32 = 0, F32 = 0,
F16 = 1, F16 = 1,
@@ -407,10 +407,13 @@ const GroundEmbedding: React.FC<MessageProps> = forwardRef((props, ref) => {
key: 'json', key: 'json',
label: 'JSON', label: 'JSON',
children: ( children: (
<div style={{ padding: 10, backgroundColor: '#fafafa' }}> <div
style={{
backgroundColor: 'var(--ant-color-bg-container)'
}}
>
<HighlightCode <HighlightCode
height={outputHeight - 20} height={outputHeight - 32}
theme="light"
code={embeddingData.code} code={embeddingData.code}
copyValue={embeddingData.copyValue} copyValue={embeddingData.copyValue}
lang="json" lang="json"
@@ -671,9 +674,10 @@ const GroundEmbedding: React.FC<MessageProps> = forwardRef((props, ref) => {
style={{ style={{
border: '1px solid var(--ant-color-border)', border: '1px solid var(--ant-color-border)',
borderRadius: 'var(--border-radius-base)', borderRadius: 'var(--border-radius-base)',
width: '100%' width: '100%',
overflow: 'hidden'
}} }}
className="scatter " className="scatter"
> >
<Tabs <Tabs
defaultActiveKey={outputType} defaultActiveKey={outputType}