perf scroll components (#2676)

* perf: add scroll list && virtualist (#2665)

* perf: chatHistorySlider add virtualList

* perf: dataCard add scroll

* fix: ts

* perf: scroll list components

* perf: hook refresh

---------

Co-authored-by: papapatrick <109422393+Patrickill@users.noreply.github.com>
This commit is contained in:
Archer
2024-09-11 19:53:49 +08:00
committed by GitHub
parent 5101c7a6dc
commit 6331f4b845
17 changed files with 413 additions and 335 deletions

View File

@@ -120,11 +120,9 @@ const CollectionPageContextProvider = ({ children }: { children: ReactNode }) =>
searchText,
filterTags
},
defaultRequest: false
// defaultRequest: false,
refreshDeps: [parentId, searchText, filterTags]
});
useEffect(() => {
getData(1);
}, [parentId]);
const contextValue: CollectionPageContextType = {
openWebSyncConfirm: openWebSyncConfirm(onUpdateDatasetWebsiteConfig),

View File

@@ -60,15 +60,6 @@ const Header = ({}: {}) => {
const { searchText, setSearchText, total, getData, pageNum, onOpenWebsiteModal } =
useContextSelector(CollectionPageContext, (v) => v);
// change search
const debounceRefetch = useCallback(
debounce(() => {
getData(1);
lastSearch.current = searchText;
}, 300),
[]
);
const { data: paths = [] } = useQuery(['getDatasetCollectionPathById', parentId], () =>
getDatasetCollectionPathById(parentId)
);
@@ -189,17 +180,6 @@ const Header = ({}: {}) => {
}
onChange={(e) => {
setSearchText(e.target.value);
debounceRefetch();
}}
onBlur={() => {
if (searchText === lastSearch.current) return;
getData(1);
}}
onKeyDown={(e) => {
if (searchText === lastSearch.current) return;
if (e.key === 'Enter') {
getData(1);
}
}}
/>
)}

View File

@@ -5,15 +5,13 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
import { useContextSelector } from 'use-context-selector';
import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext';
import { useTranslation } from 'next-i18next';
import { useCallback, useState } from 'react';
import { CollectionPageContext } from './Context';
import { debounce, isEqual } from 'lodash';
import { isEqual } from 'lodash';
import TagManageModal from './TagManageModal';
import { DatasetTagType } from '@fastgpt/global/core/dataset/type';
const HeaderTagPopOver = () => {
const { t } = useTranslation();
const [checkedTags, setCheckedTags] = useState<string[]>([]);
const {
searchDatasetTagsResult,
@@ -29,12 +27,8 @@ const HeaderTagPopOver = () => {
CollectionPageContext,
(v) => v
);
const debounceRefetch = useCallback(
debounce(() => {
getData(1);
}, 300),
[]
);
const checkedTags = filterTags;
const {
isOpen: isTagManageModalOpen,
@@ -46,16 +40,13 @@ const HeaderTagPopOver = () => {
let currentCheckedTags = [];
if (checkedTags.includes(tag._id)) {
currentCheckedTags = checkedTags.filter((t) => t !== tag._id);
setCheckedTags(currentCheckedTags);
setCheckedDatasetTag(checkedDatasetTag.filter((t) => t._id !== tag._id));
} else {
currentCheckedTags = [...checkedTags, tag._id];
setCheckedTags([...checkedTags, tag._id]);
setCheckedDatasetTag([...checkedDatasetTag, tag]);
}
if (isEqual(currentCheckedTags, filterTags)) return;
setFilterTags(currentCheckedTags);
debounceRefetch();
};
return (
@@ -181,9 +172,7 @@ const HeaderTagPopOver = () => {
variant={'unstyled'}
onClick={() => {
setSearchTagKey('');
setCheckedTags([]);
setFilterTags([]);
debounceRefetch();
onClose();
}}
>
@@ -211,7 +200,7 @@ const HeaderTagPopOver = () => {
<TagManageModal
onClose={() => {
onCloseTagManageModal();
debounceRefetch();
getData(1);
}}
/>
)}

View File

@@ -121,14 +121,15 @@ const TagManageModal = ({ onClose }: { onClose: () => void }) => {
// Tags list
const {
list,
scrollDataList: renderTags,
totalData: collectionTags,
ScrollList,
isLoading: isRequesting,
fetchData,
total: tagsTotal
} = useScrollPagination(getDatasetCollectionTags, {
refreshDeps: [''],
debounceWait: 300,
// debounceWait: 300,
itemHeight: 56,
overscan: 10,
@@ -142,12 +143,12 @@ const TagManageModal = ({ onClose }: { onClose: () => void }) => {
// Collections list
const {
list: collectionsList,
scrollDataList: collectionsList,
ScrollList: ScrollListCollections,
isLoading: collectionsListLoading
} = useScrollPagination(getScrollCollectionList, {
refreshDeps: [searchText],
debounceWait: 300,
// debounceWait: 300,
itemHeight: 37,
overscan: 10,
@@ -221,7 +222,7 @@ const TagManageModal = ({ onClose }: { onClose: () => void }) => {
ref={tagInputRef}
w={'200px'}
onBlur={() => {
if (newTag && !list.map((item) => item.data.tag).includes(newTag)) {
if (newTag && !collectionTags.map((item) => item.tag).includes(newTag)) {
onCreateCollectionTag(newTag);
}
setNewTag(undefined);
@@ -236,7 +237,7 @@ const TagManageModal = ({ onClose }: { onClose: () => void }) => {
fontSize={'sm'}
EmptyChildren={<EmptyTip text={t('dataset:dataset.no_tags')} />}
>
{list.map((listItem) => {
{renderTags.map((listItem) => {
const item = listItem.data;
const tagUsage = tagUsages?.find((tagUsage) => tagUsage.tagId === item._id);
const collections = tagUsage?.collections || [];
@@ -292,7 +293,9 @@ const TagManageModal = ({ onClose }: { onClose: () => void }) => {
onBlur={() => {
if (
currentEditTagContent &&
!list.map((item) => item.data.tag).includes(currentEditTagContent)
!collectionTags
.map((item) => item.tag)
.includes(currentEditTagContent)
) {
onUpdateCollectionTag({
tag: currentEditTagContent,

View File

@@ -29,11 +29,10 @@ import TagsPopOver from './CollectionCard/TagsPopOver';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyDivider from '@fastgpt/web/components/common/MyDivider';
import Markdown from '@/components/Markdown';
import { DatasetDataListItemType } from '@/global/core/dataset/type';
const DataCard = () => {
const BoxRef = useRef<HTMLDivElement>(null);
const theme = useTheme();
const lastSearch = useRef('');
const router = useRouter();
const { isPc } = useSystem();
const { collectionId = '', datasetId } = router.query as {
@@ -51,44 +50,30 @@ const DataCard = () => {
type: 'delete'
});
const {
data: datasetDataList,
Pagination,
total,
getData,
pageNum,
pageSize,
isLoading: isRequesting
} = usePagination({
api: getDatasetDataList,
pageSize: 24,
defaultRequest: false,
params: {
const scrollParams = useMemo(
() => ({
collectionId,
searchText
},
onChange() {
if (BoxRef.current) {
BoxRef.current.scrollTop = 0;
}
}
}),
[collectionId, searchText]
);
const {
data: datasetDataList,
ScrollData,
total,
isLoading,
refresh,
setData: setDatasetDataList
} = usePagination<DatasetDataListItemType>({
api: getDatasetDataList,
pageSize: 10,
type: 'scroll',
params: scrollParams,
refreshDeps: [searchText, collectionId]
});
const [editDataId, setEditDataId] = useState<string>();
// get first page data
useRequest2(
async () => {
getData(1);
lastSearch.current = searchText;
},
{
manual: false,
debounceWait: 300,
refreshDeps: [searchText]
}
);
// get file info
const { data: collection } = useQuery(
['getDatasetCollectionById', collectionId],
@@ -106,17 +91,9 @@ const DataCard = () => {
const canWrite = useMemo(() => datasetDetail.permission.hasWritePer, [datasetDetail]);
const { loading } = useRequest2(putDatasetDataById, {
onSuccess() {
getData(pageNum);
}
});
const isLoading = isRequesting || loading;
return (
<MyBox isLoading={isLoading} position={'relative'} py={[1, 0]} h={'100%'}>
<Flex ref={BoxRef} flexDirection={'column'} h={'100%'}>
<MyBox position={'relative'} py={[1, 0]} h={'100%'}>
<Flex flexDirection={'column'} h={'100%'}>
{/* Header */}
<Flex alignItems={'center'} px={6}>
<Flex className="textEllipsis" flex={'1 0 0'} mr={[3, 5]} alignItems={'center'}>
@@ -185,7 +162,7 @@ const DataCard = () => {
/>
</Flex>
{/* data */}
<Box flex={'1 0 0'} overflow={'auto'} px={5} pb={5}>
<ScrollData flex={'1 0 0'} px={5} pb={5}>
<Flex flexDir={'column'} gap={2}>
{datasetDataList.map((item, index) => (
<Card
@@ -203,7 +180,6 @@ const DataCard = () => {
boxShadow: 'lg',
'& .header': { visibility: 'visible' },
'& .footer': { visibility: 'visible' },
'& .forbid-switch': { display: 'flex' },
bg: index % 2 === 1 ? 'myGray.200' : 'blue.100'
}}
onClick={(e) => {
@@ -298,13 +274,18 @@ const DataCard = () => {
icon={<MyIcon name={'common/trash'} w={'14px'} color={'myGray.600'} />}
variant={'whiteDanger'}
size={'xsSquare'}
aria-label={'delete'}
onClick={(e) => {
e.stopPropagation();
openConfirm(async () => {
try {
await delOneDatasetDataById(item._id);
getData(pageNum);
setDatasetDataList((prev) => {
return prev.filter((data) => data._id !== item._id);
});
toast({
title: t('common:common.Delete Success'),
status: 'success'
});
} catch (error) {
toast({
title: getErrText(error),
@@ -313,19 +294,17 @@ const DataCard = () => {
}
})();
}}
aria-label={''}
/>
)}
</Flex>
</Card>
))}
</Flex>
{total > pageSize && (
<Flex mt={2} justifyContent={'center'}>
<Pagination />
</Flex>
)}
{total === 0 && <EmptyTip text={t('common:core.dataset.data.Empty Tip')}></EmptyTip>}
</Box>
</ScrollData>
{total === 0 && !isLoading && (
<EmptyTip text={t('common:core.dataset.data.Empty Tip')}></EmptyTip>
)}
</Flex>
{editDataId !== undefined && collection && (
@@ -333,7 +312,23 @@ const DataCard = () => {
collectionId={collection._id}
dataId={editDataId}
onClose={() => setEditDataId(undefined)}
onSuccess={() => getData(pageNum)}
onSuccess={(data) => {
if (editDataId === '') {
refresh();
return;
}
setDatasetDataList((prev) => {
return prev.map((item) => {
if (item._id === editDataId) {
return {
...item,
...data
};
}
return item;
});
});
}}
/>
)}
<ConfirmModal />