mirror of
https://github.com/labring/FastGPT.git
synced 2025-08-02 20:58:12 +00:00
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:
@@ -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),
|
||||
|
@@ -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);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
@@ -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);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
@@ -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,
|
||||
|
@@ -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 />
|
||||
|
Reference in New Issue
Block a user