import React, { useCallback, useState, useRef, useEffect } from 'react'; import { Box, Card, IconButton, Flex, Button, useDisclosure, Menu, MenuButton, MenuList, MenuItem, Input, Grid } from '@chakra-ui/react'; import type { KbDataItemType } from '@/types/plugin'; import { usePagination } from '@/hooks/usePagination'; import { getKbDataList, getExportDataList, delOneKbDataByDataId, getTrainingData } from '@/api/plugins/kb'; import { DeleteIcon, RepeatIcon } from '@chakra-ui/icons'; import { fileDownload } from '@/utils/file'; import { useMutation, useQuery } from '@tanstack/react-query'; import { useToast } from '@/hooks/useToast'; import Papa from 'papaparse'; import dynamic from 'next/dynamic'; import InputModal, { FormData as InputDataType } from './InputDataModal'; import { debounce } from 'lodash'; import { getErrText } from '@/utils/tools'; const SelectFileModal = dynamic(() => import('./SelectFileModal')); const SelectCsvModal = dynamic(() => import('./SelectCsvModal')); const DataCard = ({ kbId }: { kbId: string }) => { const lastSearch = useRef(''); const [searchText, setSearchText] = useState(''); const { toast } = useToast(); const [isDeleting, setIsDeleting] = useState(false); const { data: kbDataList, isLoading, Pagination, total, getData, pageNum } = usePagination({ api: getKbDataList, pageSize: 24, defaultRequest: false, params: { kbId, searchText } }); const [editInputData, setEditInputData] = useState(); const { isOpen: isOpenSelectFileModal, onOpen: onOpenSelectFileModal, onClose: onCloseSelectFileModal } = useDisclosure(); const { isOpen: isOpenSelectCsvModal, onOpen: onOpenSelectCsvModal, onClose: onCloseSelectCsvModal } = useDisclosure(); const { data: { qaListLen = 0, vectorListLen = 0 } = {}, refetch } = useQuery( ['getModelSplitDataList', kbId], () => getTrainingData({ kbId, init: false }), { onError(err) { console.log(err); } } ); const refetchData = useCallback( (num = pageNum) => { getData(num); refetch(); return null; }, [getData, pageNum, refetch] ); // get al data and export csv const { mutate: onclickExport, isLoading: isLoadingExport = false } = useMutation({ mutationFn: () => getExportDataList(kbId), onSuccess(res) { try { const text = Papa.unparse({ fields: ['question', 'answer', 'source'], data: res }); fileDownload({ text, type: 'text/csv', filename: 'data.csv' }); toast({ title: '导出成功,下次导出需要半小时后', status: 'success' }); } catch (error) { error; } }, onError(err: any) { toast({ title: typeof err === 'string' ? err : err?.message || '导出异常', status: 'error' }); console.log(err); } }); const getFirstData = useCallback( debounce(() => { getData(1); lastSearch.current = searchText; }, 300), [] ); // interval get data useQuery(['refetchData'], () => refetchData(1), { refetchInterval: 5000, enabled: qaListLen > 0 || vectorListLen > 0 }); useEffect(() => { setSearchText(''); getData(1); }, [kbId]); return ( 知识库数据: {total}组 } aria-label={'refresh'} variant={'base'} isLoading={isLoading} mr={[2, 4]} size={'sm'} onClick={() => { getData(pageNum); getTrainingData({ kbId, init: true }); }} /> 导入 setEditInputData({ a: '', q: '' }) } > 手动输入 文本/文件拆分 csv 问答对导入 {qaListLen > 0 || vectorListLen > 0 ? ( {qaListLen > 0 ? `${qaListLen}条数据正在拆分,` : ''} {vectorListLen > 0 ? `${vectorListLen}条数据正在生成索引,` : ''} 请耐心等待... ) : ( 所有数据已就绪~ )} { setSearchText(e.target.value); getFirstData(); }} onBlur={() => { if (searchText === lastSearch.current) return; getFirstData(); }} onKeyDown={(e) => { if (searchText === lastSearch.current) return; if (e.key === 'Enter') { getFirstData(); } }} /> {kbDataList.map((item) => ( setEditInputData({ dataId: item.id, q: item.q, a: item.a }) } > {item.q} {item.a} {item.source?.trim()} } variant={'base'} colorScheme={'gray'} aria-label={'delete'} size={'xs'} borderRadius={'md'} _hover={{ color: 'red.600' }} isLoading={isDeleting} onClick={async (e) => { e.stopPropagation(); try { setIsDeleting(true); await delOneKbDataByDataId(item.id); refetchData(pageNum); } catch (error) { toast({ title: getErrText(error), status: 'error' }); } setIsDeleting(false); }} /> ))} {editInputData !== undefined && ( setEditInputData(undefined)} onSuccess={() => refetchData()} /> )} {isOpenSelectFileModal && ( )} {isOpenSelectCsvModal && ( )} ); }; export default React.memo(DataCard);