import { apiInterceptors, delDocument, editChunk, getDocumentList, // getKnowledgeAdmins, searchDocumentList, syncDocument, } from '@/client/api'; import { IDocument, ISpace } from '@/types/knowledge'; import { DeleteOutlined, DeploymentUnitOutlined, EditOutlined, EllipsisOutlined, ExperimentOutlined, EyeOutlined, LoadingOutlined, MinusCircleOutlined, PlusOutlined, SearchOutlined, SyncOutlined, ToolFilled, WarningOutlined, } from '@ant-design/icons'; import { useRequest } from 'ahooks'; import { Button, Card, Divider, Dropdown, Empty, Form, Input, Modal, Space, Spin, Tag, Tooltip, message } from 'antd'; import cls from 'classnames'; import moment from 'moment'; import { useRouter } from 'next/router'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import RecallTestModal from './RecallTestModal'; import ArgumentsModal from './arguments-modal'; import DocIcon from './doc-icon'; interface IProps { space: ISpace; addStatus?: string; onAddDoc: (spaceName: string) => void; onDeleteDoc: () => void; } const { confirm } = Modal; const SyncContent: React.FC<{ name: string; id: number }> = ({ name, id }) => { const [syncLoading, setSyncLoading] = useState(false); const { t } = useTranslation(); const handleSync = async (spaceName: string, id: number) => { setSyncLoading(true); const res = await apiInterceptors(syncDocument(spaceName, { doc_ids: [id] })); setSyncLoading(false); if (res[2]?.success) { message.success(t('Synchronization_initiated')); } }; if (syncLoading) { return } />; } return ( { handleSync(name, id); }} > {t('Sync')} ); }; export default function DocPanel(props: IProps) { const [form] = Form.useForm(); const { space, addStatus } = props; const { t } = useTranslation(); const router = useRouter(); const page_size = 18; // const [_, setAdmins] = useState([]); const [documents, setDocuments] = useState([]); const [searchDocuments, setSearchDocuments] = useState([]); const [argumentsShow, setArgumentsShow] = useState(false); const [total, setTotal] = useState(0); const [editOpen, setEditOpen] = useState(false); const [curDoc, setCurDoc] = useState(); // 召回测试弹窗 const [recallTestOpen, setRecallTestOpen] = useState(false); const currentPageRef = useRef(1); const hasMore = useMemo(() => { return documents?.length < total; }, [documents, total]); const showDeleteConfirm = (row: any) => { confirm({ title: t('Tips'), icon: , content: `${t('Del_Document_Tips')}?`, okText: 'Yes', okType: 'danger', cancelText: 'No', async onOk() { await handleDelete(row); }, }); }; const { run: fetchDocuments, refresh, loading: isLoading, } = useRequest( async () => await apiInterceptors( getDocumentList(space.name, { page: currentPageRef.current, page_size, }), ), { manual: true, onSuccess: res => { const [, data] = res; setDocuments(data?.data); setSearchDocuments(data?.data); setTotal(data?.total || 0); }, }, ); const loadMoreDocuments = async () => { if (!hasMore) { return; } currentPageRef.current += 1; const [_, data] = await apiInterceptors( getDocumentList(space.name, { page: currentPageRef.current, page_size, }), ); setDocuments([...documents, ...data!.data]); setSearchDocuments([...documents, ...data!.data]); }; const handleDelete = async (row: any) => { await apiInterceptors(delDocument(space.name, { doc_name: row.doc_name })); fetchDocuments(); props.onDeleteDoc(); }; const handleAddDocument = () => { props.onAddDoc(space.name); }; const handleArguments = () => { setArgumentsShow(true); }; const openGraphVisualPage = () => { router.push(`/knowledge/graph/?spaceName=${space.name}`); }; const renderResultTag = (status: string, result: string) => { let color; switch (status) { case 'TODO': color = 'gold'; break; case 'RUNNING': color = '#2db7f5'; break; case 'FINISHED': color = 'cyan'; break; case 'FAILED': color = 'red'; break; default: color = 'red'; break; } return ( {status} ); }; useEffect(() => { fetchDocuments(); // getAdmins(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { if (addStatus === 'finish') { fetchDocuments(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [addStatus]); // const updateAdmins = useCallback( // async (options: string[]) => { // const { data } = await updateKnowledgeAdmins({ // space_id: space.id as string, // user_nos: options as any, // }); // if (!data.success) { // // getAdmins(); // notification.error({ description: data.err_msg, message: 'Update Error' }); // } else { // message.success(t('Edit_Success')); // } // }, // // eslint-disable-next-line react-hooks/exhaustive-deps // [space.id], // ); // const handleChange = (value: string[]) => { // updateAdmins(value); // setAdmins(value); // }; const { run: search, loading: searchLoading } = useRequest( async (_, doc_name: string) => { const [, res] = await apiInterceptors(searchDocumentList(space.name, { doc_name })); return res; }, { manual: true, debounceWait: 500, onSuccess: data => { setSearchDocuments(data?.data); }, }, ); const { run: editChunkRun, loading: chunkLoading } = useRequest( async (values: any) => { return await editChunk(props.space.name, { questions: values.questions?.map((item: any) => item.question), doc_id: curDoc?.id || '', doc_name: values.doc_name, }); }, { manual: true, onSuccess: async res => { if (res.data.success) { message.success(t('Edit_Success')); await fetchDocuments(); setEditOpen(false); } else { message.error(res.data.err_msg); } }, }, ); const renderDocumentCard = () => { return (
{/*
管理员(工号,去前缀0):
*/}
{/* } placeholder={t('please_enter_the_keywords')} onChange={async e => { await search(space.id, e.target.value); }} allowClear />
<> {searchDocuments.length > 0 ? (
{searchDocuments.map((document: IDocument) => { return (
{document.doc_name}
} extra={ { router.push( `/construct/knowledge/chunk/?spaceName=${space.name}&id=${document.id}`, ); }} > {t('detail')} ), }, { key: `${t('Sync')}`, label: , }, { key: 'edit', label: ( { setEditOpen(true); setCurDoc(document); }} > {t('Edit')} ), }, { key: 'del', label: ( { showDeleteConfirm(document); }} > {t('Delete')} ), }, ], }} getPopupContainer={node => node.parentNode as HTMLElement} placement='bottomRight' autoAdjustOverflow={false} className='rounded-md' > } >

{t('Size')}:

{document.chunk_size} chunks

{t('Last_Sync')}:

{moment(document.last_sync).format('YYYY-MM-DD HH:MM:SS')}

{renderResultTag(document.status, document.result)}

); })}
) : ( )} {hasMore && ( {t('Load_more')} )}
) : ( )}
); }; useEffect(() => { if (!curDoc) { return; } form.setFieldsValue({ doc_name: curDoc.doc_name, questions: curDoc.questions?.map(ques => { return { question: ques, }; }), }); }, [curDoc, form]); return (
{space.vector_type === 'KnowledgeGraph' && ( )} {renderDocumentCard()} {/* 编辑弹窗 */} setEditOpen(false)} destroyOnClose={true} footer={[ , , ]} >
{ return { question: ques, }; }), }} > {(fields, { add, remove }) => ( <> {fields.map(({ key, name }) => (
{ remove(name); }} />
))} )}
{/* 召回测试弹窗 */}
); }