import { Box, Flex, HStack } from '@chakra-ui/react'; import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useRouter } from 'next/router'; import { useTranslation } from 'react-i18next'; import DownloadButton from './DownloadButton'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { downloadFetch } from '@/web/common/system/utils'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { getDatasetDataPermission } from '@/web/core/dataset/api'; import ScoreTag from './ScoreTag'; import { formatScore } from '@/components/core/dataset/QuoteItem'; import NavButton from './NavButton'; import { useLinkedScroll } from '@fastgpt/web/hooks/useLinkedScroll'; import CollectionQuoteItem from './CollectionQuoteItem'; import { GetCollectionQuoteDataProps } from '@/web/core/chat/context/chatItemContext'; import { useUserStore } from '@/web/support/user/useUserStore'; import { getCollectionQuote } from '@/web/core/chat/api'; import MyIconButton from '@fastgpt/web/components/common/Icon/button'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyBox from '@fastgpt/web/components/common/MyBox'; import { getCollectionSourceAndOpen } from '@/web/core/dataset/hooks/readCollectionSource'; import { QuoteDataItemType } from '@/service/core/chat/constants'; const CollectionReader = ({ rawSearch, metadata, onClose }: { rawSearch: SearchDataResponseItemType[]; metadata: GetCollectionQuoteDataProps; onClose: () => void; }) => { const { t } = useTranslation(); const router = useRouter(); const { userInfo } = useUserStore(); const { collectionId, datasetId, chatItemDataId, sourceId, sourceName } = metadata; const [quoteIndex, setQuoteIndex] = useState(0); // Get dataset permission const { data: permissionData, loading: isPermissionLoading } = useRequest2( async () => await getDatasetDataPermission(datasetId), { manual: !userInfo && !datasetId, refreshDeps: [datasetId, userInfo] } ); const filterResults = useMemo(() => { const results = rawSearch.filter( (item) => item.collectionId === metadata.collectionId && item.sourceId === metadata.sourceId ); return results.sort((a, b) => (a.chunkIndex || 0) - (b.chunkIndex || 0)); }, [metadata, rawSearch]); const currentQuoteItem = filterResults[quoteIndex]; // Get quote list const { dataList: datasetDataList, setDataList: setDatasetDataList, isLoading, loadData, ScrollData, itemRefs, scrollToItem } = useLinkedScroll(getCollectionQuote, { refreshDeps: [collectionId], params: { collectionId, chatItemDataId, chatId: metadata.chatId, appId: metadata.appId, ...metadata.outLinkAuthData }, initialId: currentQuoteItem?.id, initialIndex: currentQuoteItem?.chunkIndex, canLoadData: !!currentQuoteItem?.id && !isPermissionLoading }); const loading = isLoading || isPermissionLoading; const isDeleted = useMemo( () => !datasetDataList.find((item) => item._id === currentQuoteItem?.id), [datasetDataList, currentQuoteItem?.id] ); const formatedDataList = useMemo( () => datasetDataList.map((item: QuoteDataItemType) => { const isCurrentSelected = currentQuoteItem?.id === item._id; const quoteIndex = filterResults.findIndex((res) => res.id === item._id); return { ...item, isCurrentSelected, quoteIndex }; }), [currentQuoteItem?.id, datasetDataList, filterResults] ); useEffect(() => { setQuoteIndex(0); setDatasetDataList([]); }, [collectionId, setDatasetDataList]); const { runAsync: handleDownload } = useRequest2(async () => { await downloadFetch({ url: '/api/core/dataset/collection/export', filename: 'data.csv', body: { collectionId: collectionId, chatItemDataId } }); }); const handleRead = getCollectionSourceAndOpen(metadata); const handleNavigate = useCallback( async (targetIndex: number) => { if (targetIndex < 0 || targetIndex >= filterResults.length) return; const targetItemId = filterResults[targetIndex].id; const targetItemIndex = filterResults[targetIndex].chunkIndex; setQuoteIndex(targetIndex); const dataIndex = datasetDataList.findIndex((item) => item._id === targetItemId); if (dataIndex !== -1) { setTimeout(() => { scrollToItem(dataIndex); }, 50); } else { try { await loadData({ id: targetItemId, index: targetItemIndex }); } catch (error) { console.error('Failed to navigate:', error); } } }, [filterResults, datasetDataList, scrollToItem, loadData] ); return ( {/* title */} {/* name */} {sourceName || t('common:common.UnKnow Source')} {!!userInfo && permissionData?.permission?.hasReadPer && ( { router.push( `/dataset/detail?datasetId=${datasetId}¤tTab=dataCard&collectionId=${collectionId}` ); }} /> )} {t('common:core.chat.quote.Quote Tip')} {/* header control */} {datasetDataList.length > 0 && ( {/* 引用序号 */} {t('common:core.chat.Quote')} {quoteIndex + 1} / {filterResults.length} {/* 检索分数 */} {!loading && (!isDeleted ? ( ) : ( {t('chat:chat.quote.deleted')} ))} {/* 检索按钮 */} handleNavigate(quoteIndex - 1)} /> handleNavigate(quoteIndex + 1)} /> )} {/* quote list */} {loading || datasetDataList.length > 0 ? ( {formatedDataList.map((item, index) => ( } quoteIndex={item.quoteIndex} setQuoteIndex={setQuoteIndex} refreshList={() => currentQuoteItem?.id && loadData({ id: currentQuoteItem.id, index: currentQuoteItem.chunkIndex }) } updated={item.updated} isCurrentSelected={item.isCurrentSelected} q={item.q} a={item.a} dataId={item._id} collectionId={collectionId} canEdit={!!userInfo && !!permissionData?.permission?.hasWritePer} /> ))} ) : ( {t('chat:chat.quote.No Data')} )} ); }; export default CollectionReader;