import { useEffect, useState, useRef, type FC } from 'react'; import { useNavigate, useParams, useLocation } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Switch, Button, Dropdown, Space, Modal, message } from 'antd'; import type { MenuProps } from 'antd'; import SearchInput from '@/components/SearchInput' import Table, { type TableRef } from '@/components/Table' import type { ColumnsType } from 'antd/es/table'; import type { AnyObject } from 'antd/es/_util/type'; import { MoreOutlined } from '@ant-design/icons'; import folderIcon from '@/assets/images/knowledgeBase/folder.png'; import textIcon from '@/assets/images/knowledgeBase/text.png'; import editIcon from '@/assets/images/knowledgeBase/edit.png'; import { getKnowledgeBaseDetail, deleteDocument, downloadFile, updateKnowledgeBase } from '@/api/knowledgeBase'; import type { CreateModalRef, KnowledgeBaseListItem, RecallTestDrawerRef, CreateFolderModalRef, CreateImageModalRef, ShareModalRef, CreateDatasetModalRef,FolderFormData, KnowledgeBaseDocumentData } from '@/views/KnowledgeBase/types'; import RecallTestDrawer from '../components/RecallTestDrawer'; import CreateFolderModal from '../components/CreateFolderModal'; import CreateModal from '../components/CreateModal'; import ShareModal from '../components/ShareModal'; import CreateDatasetModal from '../components/CreateDatasetModal'; import CreateImageDataset from '../components/CreateImageDataset'; import FolderTree, { type TreeNodeData } from '../components/FolderTree'; import { formatDateTime } from '@/utils/format'; import { useMenu } from '@/store/menu'; import './Private.css' const { confirm } = Modal // 树节点数据类型 const Private: FC = () => { const { t } = useTranslation(); const [messageApi, contextHolder] = message.useMessage(); const navigate = useNavigate(); const location = useLocation(); const { knowledgeBaseId } = useParams<{ knowledgeBaseId: string }>(); const [parentId, setParentId] = useState(knowledgeBaseId); const [loading, setLoading] = useState(false); const tableRef = useRef(null); const [tableApi, setTableApi] = useState(undefined); const recallTestDrawerRef = useRef(null); const createFolderModalRef = useRef(null); const createImageDataset = useRef(null) const [knowledgeBase, setKnowledgeBase] = useState(null); const [folder, setFolder] = useState({ kb_id:knowledgeBaseId ?? '', parent_id:parentId ?? '' }); const [query, setQuery] = useState>({ orderby: 'created_at', desc: true, }); const modalRef = useRef(null) const shareModalRef = useRef(null); const datasetModalRef = useRef(null); const [folderTreeRefreshKey, setFolderTreeRefreshKey] = useState(0); const { allBreadcrumbs, setCustomBreadcrumbs } = useMenu(); const [folderPath, setFolderPath] = useState>([]); const [selectedKeys, setSelectedKeys] = useState([]); useEffect(() => { if (knowledgeBaseId) { let url = `/documents/${knowledgeBaseId}/${parentId}/documents`; setTableApi(url); fetchKnowledgeBaseDetail(knowledgeBaseId); } }, [knowledgeBaseId]); // 更新面包屑 useEffect(() => { if (knowledgeBase) { updateBreadcrumbs(); } }, [knowledgeBase, folderPath]); // 监听 tableApi 变化,自动刷新表格数据 useEffect(() => { if (tableApi) { tableRef.current?.loadData(); } }, [tableApi]); // 监听 location state 变化,如果有 refresh 标志则刷新列表 useEffect(() => { const state = location.state as { refresh?: boolean; timestamp?: number } | null; if (state?.refresh) { tableRef.current?.loadData(); // 清除 state,避免重复刷新 navigate(location.pathname, { replace: true, state: {} }); } }, [location.state]); const fetchKnowledgeBaseDetail = async (id: string) => { setLoading(true); try { const res = await getKnowledgeBaseDetail(id); // 将 KnowledgeBase 转换为 KnowledgeBaseListItem const listItem = res as unknown as KnowledgeBaseListItem; setKnowledgeBase(listItem); } finally { setLoading(false); } }; // 更新面包屑,包含知识库名称和文件夹路径 const updateBreadcrumbs = () => { if (!knowledgeBase) return; const baseBreadcrumbs = allBreadcrumbs['space'] || []; // 只保留知识库菜单项之前的面包屑 const knowledgeBaseMenuIndex = baseBreadcrumbs.findIndex(item => item.path === '/knowledge-base'); const filteredBaseBreadcrumbs = knowledgeBaseMenuIndex >= 0 ? baseBreadcrumbs.slice(0, knowledgeBaseMenuIndex + 1) : baseBreadcrumbs; const customBreadcrumbs = [ ...filteredBaseBreadcrumbs, { id: 0, parent: 0, code: null, label: knowledgeBase.name, i18nKey: null, path: null, enable: true, display: true, level: 0, sort: 0, icon: null, iconActive: null, menuDesc: null, deleted: null, updateTime: 0, new_: null, keepAlive: false, master: null, disposable: false, appSystem: null, subs: [], onClick: (e?: React.MouseEvent) => { // 阻止默认行为和事件冒泡 e?.preventDefault(); e?.stopPropagation(); // 点击知识库名称,回到根目录 setParentId(knowledgeBaseId); setFolder({ kb_id: knowledgeBaseId ?? '', parent_id: knowledgeBaseId ?? '' }); setTableApi(`/documents/${knowledgeBaseId}/${knowledgeBaseId}/documents`); setFolderPath([]); setSelectedKeys([knowledgeBaseId ?? '']); return false; }, }, ...folderPath.map((folder, index) => ({ id: 0, parent: 0, code: null, label: folder.name, i18nKey: null, path: null, enable: true, display: true, level: 0, sort: 0, icon: null, iconActive: null, menuDesc: null, deleted: null, updateTime: 0, new_: null, keepAlive: false, master: null, disposable: false, appSystem: null, subs: [], onClick: (e?: React.MouseEvent) => { // 阻止默认行为和事件冒泡 e?.preventDefault(); e?.stopPropagation(); // 点击文件夹,回到该文件夹层级 setParentId(folder.id); setFolder({ kb_id: knowledgeBaseId ?? '', parent_id: folder.id }); setTableApi(`/documents/${knowledgeBaseId}/${folder.id}/documents`); // 更新文件夹路径,只保留到当前点击的文件夹 setFolderPath(folderPath.slice(0, index + 1)); setSelectedKeys([folder.id]); return false; }, })), ]; setCustomBreadcrumbs(customBreadcrumbs, 'space'); }; // 处理树节点选择 const onSelect = (keys: React.Key[]) => { if (!keys.length) return; if (!folder) return; const f = { ...folder, parent_id: String(keys[0]), } let url = `/documents/${knowledgeBaseId}/${String(keys[0])}/documents`; setTableApi(url); setParentId(String(keys[0])) setFolder(f) setSelectedKeys(keys) }; // 处理文件夹路径变化 const handleFolderPathChange = (path: Array<{ id: string; name: string }>) => { setFolderPath(path); }; // 处理树节点展开 const onExpand = (_expandedKeys: React.Key[], _info: any) => { // 展开节点时不需要特殊处理 }; // create / import list const createItems: MenuProps['items'] = [ { key: '1', icon: dataset, label: t('knowledgeBase.folder'), onClick: () => { let f: FolderFormData | null = null; f = { kb_id: knowledgeBase?.id ?? '', parent_id:folder?.parent_id ?? knowledgeBase?.id ?? '', } // setFolder(f); createFolderModalRef?.current?.handleOpen(f as FolderFormData); }, }, { key: '2', icon: text, label: ({t('knowledgeBase.text')} {t('knowledgeBase.dataset')}), onClick: () => { datasetModalRef?.current?.handleOpen(knowledgeBase?.id,folder?.parent_id ?? knowledgeBase?.id ?? ''); }, }, // 暂时未实现 // { // key: '3', // icon: image, // label: t('knowledgeBase.imageDataSet'), // onClick: () => { // createImageDataset?.current?.handleOpen(knowledgeBaseId || '', parentId || '') // }, // }, // { // key: '4', // icon: blank, // label: t('knowledgeBase.blankDataset'), // onClick: () => { // handleCreate('folder'); // 传入 type: 'folder' // }, // }, // { // key: '5', // type: 'divider', // }, // { // key: '6', // icon: import, // label: t('knowledgeBase.importTemplate'), // onClick: () => { // handleCreate('folder'); // 传入 type: 'folder' // }, // }, // { // key: '7', // icon: import, // label: t('knowledgeBase.importBackup'), // onClick: () => { // handleCreate('folder'); // 传入 type: 'folder' // }, // }, ]; // 处理开关 const onChange = (checked: boolean) => { updateKnowledgeBase(knowledgeBaseId || '', { status: checked ? 1 : 0, }); console.log(`switch to ${checked}`); }; // 处理搜索 const handleSearch = (value?: string) => { setQuery({ ...query, keywords: value }) } // 处理分享 const handleShare = () => { shareModalRef?.current?.handleOpen(knowledgeBaseId,knowledgeBase); } // 处理分享回调,接收选中的数据 const handleShareCallback = (selectedData: { checkedItems: any[], selectedItem: any | null }) => { console.log('选中的数据:', selectedData); // checkedItems: 所有 checked 为 true 的数据 // selectedItem: 当前选中的项(curIndex 对应的数据) // 在这里处理分享逻辑 } const handleCreateDatasetCallback = (payload: { value: number; title: string; description: string }) => { console.log('创建数据集:', payload); } // 处理设置 const handleSetting = () => { modalRef?.current?.handleOpen(knowledgeBase, ''); } // 处理召回测试 const handleRecallTest = () => { recallTestDrawerRef?.current?.handleOpen(knowledgeBaseId); } // new / import const handelCreateOrImport = () => { } // 生成下拉菜单项(根据当前 row) const getOptMenuItems = (row: KnowledgeBaseListItem): MenuProps['items'] => [ { key: '1', label: t('knowledgeBase.rechunking'), onClick: () => { handleRechunking(row); }, }, { key: '2', label: t('knowledgeBase.download'), onClick: () => { handleDownload(row); }, }, { key: '3', label: t('knowledgeBase.delete'), onClick: () => { handleDelete(row); }, } ]; const handleRechunking = (item: KnowledgeBaseListItem) => { if (!knowledgeBaseId) return; const document = item as unknown as KnowledgeBaseDocumentData; const targetFileId = document?.id || document?.file_id; navigate(`/knowledge-base/${knowledgeBaseId}/create-dataset`, { state: { source: 'local', knowledgeBaseId, parentId: parentId ?? knowledgeBaseId, startStep: 'parameterSettings', fileId: targetFileId, }, }); } const handleDownload = (item: KnowledgeBaseListItem) => { const document = item as unknown as KnowledgeBaseDocumentData; const targetFileId = document?.file_id ?? ''; const fileName = document?.file_name ?? ''; downloadFile(targetFileId, fileName); } const handleDelete = (item: any) => { confirm({ title: t('common.deleteWarning'), content: t('common.deleteWarningContent', { content: item.file_name }), onOk: () => { deleteDocument(item.id) .then(() => { messageApi.success(t('common.deleteSuccess')); // 刷新表格数据 tableRef.current?.loadData(); }) .catch((err: any) => { console.log('删除失败', err); }); }, onCancel: () => { console.log('取消删除'); }, }); } // 表格列配置 const columns: ColumnsType = [ { title: t('knowledgeBase.name'), dataIndex: 'file_name', key: 'file_name', render: (text: string, record: AnyObject) => { const document = record as KnowledgeBaseDocumentData; return ( { if (knowledgeBaseId && document.id) { navigate(`/knowledge-base/${knowledgeBaseId}/DocumentDetails`,{ state: { documentId: document.id, parentId: parentId ?? knowledgeBaseId, }, }); } }} > {text} ); }, }, { title: t('knowledgeBase.processingMode'), dataIndex: 'parser_id', key: 'parser_id', }, { title: t('knowledgeBase.dataSize'), dataIndex: 'file_size', key: 'file_size', }, { title: t('knowledgeBase.createUpdateTime'), dataIndex: 'created_at', key: 'created_at', render:(value:string) => { return( {formatDateTime(value,'YYYY-MM-DD HH:mm:ss')} ) } }, { title: t('knowledgeBase.status'), dataIndex: 'progress', key: 'progress', render: (value: string | number) => { return ( {value === 1 ? t('knowledgeBase.completed') : value === 0 ? t('knowledgeBase.pending') : t('knowledgeBase.processing')} ); } }, { title: t('common.operation'), key: 'action', fixed: 'right', width: 100, render: (_, record) => ( ), }, ]; // 刷新列表数据 if (loading) { return
加载中...
; } if (!knowledgeBase) { return
知识库不存在
; } const refreshDirectoryTree = async () => { // 先刷新知识库详情,确保数据是最新的 await fetchKnowledgeBaseDetail(knowledgeBase.id); // 添加短暂延迟,确保后端数据已经完全更新 await new Promise(resolve => setTimeout(resolve, 300)); // 然后刷新文件夹树 setFolderTreeRefreshKey((prev) => prev + 1); if (!folder) { setFolder({ kb_id: knowledgeBaseId ?? '', parent_id: parentId ?? knowledgeBaseId ?? '' }); } } const handleRootTreeLoad = (nodes: TreeNodeData[] | null) => { if (!nodes || nodes.length === 0) { setFolder(null); } else { // 如果有节点且 folder 为 null,重新设置 folder if (!folder) { setFolder({ kb_id: knowledgeBaseId ?? '', parent_id: parentId ?? knowledgeBaseId ?? '' }); } } }; const handleEditFolder = () => { const f = { id:knowledgeBase.id, parent_id:knowledgeBase.parent_id, kb_id:knowledgeBase.id, folder_name:knowledgeBase.name } // setFolder(f) createFolderModalRef?.current?.handleOpen(f,'edit'); } const handleRefreshTable = () => { // 刷新表格数据 tableRef.current?.loadData(); } return ( <> {contextHolder}
{folder && (
)}

{knowledgeBase.name}

edit {t('knowledgeBase.edit')} {t('knowledgeBase.name')}
{t('knowledgeBase.created')} {t('knowledgeBase.time')}: {formatDateTime(knowledgeBase.created_at) || '-'} {t('knowledgeBase.updated')} {t('knowledgeBase.time')}: {formatDateTime(knowledgeBase.updated_at) || '-'}
{/*
*/} {/*
*/}
} columns={columns} rowKey="id" scrollX={1500} /> ); }; export default Private;