@@ -4,11 +4,11 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import Table, { type TableRef } from '@/components/Table'
|
||||
import type { AnyObject } from 'antd/es/_util/type';
|
||||
import type { UploadFileResponse,KnowledgeBaseDocumentData } from '../types';
|
||||
import type { UploadFileResponse,KnowledgeBaseDocumentData } from '@/views/KnowledgeBase/types';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import UploadFiles from '@/components/Upload/UploadFiles';
|
||||
import type { UploadRequestOption } from 'rc-upload/lib/interface';
|
||||
import { uploadFile, getDocumentList, previewDocumentChunk, parseDocument, updateDocument, deleteDocument } from '../service';
|
||||
import { uploadFile, getDocumentList, previewDocumentChunk, parseDocument, updateDocument, deleteDocument } from '@/api/knowledgeBase';
|
||||
import exitIcon from '@/assets/images/knowledgeBase/exit.png';
|
||||
import { NoData } from '../components/noData';
|
||||
import noDataIcon from '@/assets/images/knowledgeBase/noData.png';
|
||||
@@ -227,7 +227,7 @@ const CreateDataset = () => {
|
||||
return (
|
||||
<span className="rb:text-xs rb:border rb:border-[#DFE4ED] rb:bg-[#FBFDFF] rb:rounded rb:items-center rb:text-[#212332] rb:py-1 rb:px-2">
|
||||
<span className="rb:inline-block rb:w-[5px] rb:h-[5px] rb:mr-2 rb:rounded-full" style={{ backgroundColor: value === 1 ? '#369F21' : '#FF8A4C' }}></span>
|
||||
<span>{value === 1 ? 'Completed' : 'Processing'}</span>
|
||||
<span>{value === 1 ? t('knowledgeBase.completed') : value === 0 ? t('knowledgeBase.pending') : t('knowledgeBase.processing')}</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import { useEffect, useState, useRef, type FC } from 'react';
|
||||
import { useNavigate, useParams, useLocation } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Spin, message, Switch } from 'antd';
|
||||
import { getDocumentDetail, getDocumentChunkList, downloadFile, updateDocument, updateDocumentChunk, createDocumentChunk } from '../service';
|
||||
import type { KnowledgeBaseDocumentData, RecallTestData } from '../types';
|
||||
import { getDocumentDetail, getDocumentChunkList, downloadFile, updateDocument, updateDocumentChunk, createDocumentChunk } from '@/api/knowledgeBase';
|
||||
import type { KnowledgeBaseDocumentData, RecallTestData } from '@/views/KnowledgeBase/types';
|
||||
import { formatDateTime } from '@/utils/format';
|
||||
import InfoPanel, { type InfoItem } from '../components/InfoPanel';
|
||||
import RecallTestResult from '../components/RecallTestResult';
|
||||
|
||||
@@ -12,7 +12,7 @@ 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 '../service';
|
||||
import { getKnowledgeBaseDetail, deleteDocument, downloadFile, updateKnowledgeBase } from '@/api/knowledgeBase';
|
||||
import type {
|
||||
CreateModalRef,
|
||||
KnowledgeBaseListItem,
|
||||
@@ -22,7 +22,7 @@ import type {
|
||||
ShareModalRef,
|
||||
CreateDatasetModalRef,FolderFormData,
|
||||
KnowledgeBaseDocumentData
|
||||
} from '../types';
|
||||
} from '@/views/KnowledgeBase/types';
|
||||
import RecallTestDrawer from '../components/RecallTestDrawer';
|
||||
import CreateFolderModal from '../components/CreateFolderModal';
|
||||
import CreateModal from '../components/CreateModal';
|
||||
@@ -64,6 +64,7 @@ const Private: FC = () => {
|
||||
const [folderTreeRefreshKey, setFolderTreeRefreshKey] = useState(0);
|
||||
const { allBreadcrumbs, setCustomBreadcrumbs } = useMenu();
|
||||
const [folderPath, setFolderPath] = useState<Array<{ id: string; name: string }>>([]);
|
||||
const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
|
||||
useEffect(() => {
|
||||
if (knowledgeBaseId) {
|
||||
let url = `/documents/${knowledgeBaseId}/${parentId}/documents`;
|
||||
@@ -143,8 +144,23 @@ const Private: FC = () => {
|
||||
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) => ({
|
||||
...folderPath.map((folder, index) => ({
|
||||
id: 0,
|
||||
parent: 0,
|
||||
code: null,
|
||||
@@ -166,6 +182,22 @@ const Private: FC = () => {
|
||||
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;
|
||||
},
|
||||
})),
|
||||
];
|
||||
|
||||
@@ -173,17 +205,18 @@ const Private: FC = () => {
|
||||
};
|
||||
|
||||
// 处理树节点选择
|
||||
const onSelect = (selectedKeys: React.Key[]) => {
|
||||
if (!selectedKeys.length) return;
|
||||
const onSelect = (keys: React.Key[]) => {
|
||||
if (!keys.length) return;
|
||||
if (!folder) return;
|
||||
const f = {
|
||||
...folder,
|
||||
parent_id: String(selectedKeys[0]),
|
||||
parent_id: String(keys[0]),
|
||||
}
|
||||
let url = `/documents/${knowledgeBaseId}/${String(selectedKeys[0])}/documents`;
|
||||
let url = `/documents/${knowledgeBaseId}/${String(keys[0])}/documents`;
|
||||
setTableApi(url);
|
||||
setParentId(String(selectedKeys[0]))
|
||||
setParentId(String(keys[0]))
|
||||
setFolder(f)
|
||||
setSelectedKeys(keys)
|
||||
};
|
||||
|
||||
// 处理文件夹路径变化
|
||||
@@ -511,6 +544,7 @@ const Private: FC = () => {
|
||||
refreshKey={folderTreeRefreshKey}
|
||||
onRootLoad={handleRootTreeLoad}
|
||||
onFolderPathChange={handleFolderPathChange}
|
||||
selectedKeys={selectedKeys}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState, useRef, type FC } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { KnowledgeBaseListItem, RecallTestDrawerRef } from '../types';
|
||||
import type { KnowledgeBaseListItem, RecallTestDrawerRef } from '@/views/KnowledgeBase/types';
|
||||
import RecallTest from '../components/RecallTest';
|
||||
import InfoPanel, { type InfoItem } from '../components/InfoPanel';
|
||||
import shareUserIcon from '@/assets/images/knowledgeBase/share-user.png';
|
||||
@@ -13,8 +13,9 @@ import kbSizeIcon from '@/assets/images/knowledgeBase/kb-size.png';
|
||||
import kbModelIcon from '@/assets/images/knowledgeBase/kb-model.png';
|
||||
|
||||
import kbHistoryIcon from '@/assets/images/knowledgeBase/kb-history.png';
|
||||
import { getKnowledgeBaseDetail } from '../service';
|
||||
import { getKnowledgeBaseDetail } from '@/api/knowledgeBase';
|
||||
import { formatDateTime } from '@/utils/format';
|
||||
import { useMenu } from '@/store/menu';
|
||||
|
||||
const Share: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -24,6 +25,7 @@ const Share: FC = () => {
|
||||
const [knowledgeBase, setKnowledgeBase] = useState<KnowledgeBaseListItem | null>(null);
|
||||
const recallTestRef = useRef<RecallTestDrawerRef>(null);
|
||||
const [infoItems, setInfoItems] = useState<InfoItem[]>([]);
|
||||
const { allBreadcrumbs, setCustomBreadcrumbs } = useMenu();
|
||||
useEffect(() => {
|
||||
console.log('Share.tsx - useParams result:', params);
|
||||
console.log('Share.tsx - knowledgeBaseId:', knowledgeBaseId);
|
||||
@@ -40,6 +42,13 @@ const Share: FC = () => {
|
||||
console.warn('Share.tsx - knowledgeBaseId is undefined or empty');
|
||||
}
|
||||
}, [knowledgeBaseId]);
|
||||
|
||||
// 更新面包屑
|
||||
useEffect(() => {
|
||||
if (knowledgeBase) {
|
||||
updateBreadcrumbs();
|
||||
}
|
||||
}, [knowledgeBase]);
|
||||
const formatInfoItems = (data: KnowledgeBaseListItem): InfoItem[] => {
|
||||
const items: InfoItem[] = [
|
||||
{
|
||||
@@ -103,6 +112,47 @@ const Share: FC = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 更新面包屑,包含知识库名称
|
||||
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: [],
|
||||
},
|
||||
];
|
||||
|
||||
setCustomBreadcrumbs(customBreadcrumbs, 'space');
|
||||
};
|
||||
|
||||
// const handleBack = () => {
|
||||
// navigate('/knowledge-base');
|
||||
// };
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { RadioChangeEvent } from 'antd';
|
||||
import { Flex, Radio } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import type { CreateDatasetModalRef, CreateDatasetModalRefProps} from '../types';
|
||||
import type { CreateDatasetModalRef, CreateDatasetModalRefProps} from '@/views/KnowledgeBase/types';
|
||||
import RbModal from '@/components/RbModal'
|
||||
const style: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { FolderFormData, KnowledgeBaseFormData, CreateFolderModalRef, CreateFolderModalRefProps } from '../types';
|
||||
import type { FolderFormData, KnowledgeBaseFormData, CreateFolderModalRef, CreateFolderModalRefProps } from '@/views/KnowledgeBase/types';
|
||||
import RbModal from '@/components/RbModal'
|
||||
import { createFolder, updateKnowledgeBase } from '../service';
|
||||
import { createFolder, updateKnowledgeBase } from '@/api/knowledgeBase';
|
||||
const CreateFolderModal = forwardRef<CreateFolderModalRef,CreateFolderModalRefProps>(({
|
||||
refreshTable
|
||||
}, ref) => {
|
||||
|
||||
@@ -2,11 +2,11 @@ import { forwardRef, useImperativeHandle, useState, useRef } from 'react';
|
||||
import { Form, Input } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { UploadFile } from 'antd';
|
||||
import type { CreateImageModalRef, CreateImageMoealRefProps,UploadFileResponse } from '../types';
|
||||
import type { CreateImageModalRef, CreateImageMoealRefProps,UploadFileResponse } from '@/views/KnowledgeBase/types';
|
||||
import type { UploadRequestOption } from 'rc-upload/lib/interface';
|
||||
import RbModal from '@/components/RbModal';
|
||||
import UploadFiles from '@/components/Upload/UploadFiles';
|
||||
import { uploadFile } from '../service';
|
||||
import { uploadFile } from '@/api/knowledgeBase';
|
||||
|
||||
interface ImageDatasetFormData {
|
||||
name: string;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from 'react';
|
||||
import { Form, Input, Select, Modal } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { KnowledgeBaseListItem, KnowledgeBaseFormData, CreateModalRef, CreateModalRefProps } from '../types';
|
||||
import { getModelTypeList, getModelList, createKnowledgeBase, updateKnowledgeBase } from '../service'
|
||||
import type { KnowledgeBaseListItem, KnowledgeBaseFormData, CreateModalRef, CreateModalRefProps } from '@/views/KnowledgeBase/types';
|
||||
import { getModelTypeList, getModelList, createKnowledgeBase, updateKnowledgeBase } from '@/api/knowledgeBase'
|
||||
import RbModal from '@/components/RbModal'
|
||||
const { TextArea } = Input;
|
||||
const { confirm } = Modal
|
||||
@@ -15,7 +15,7 @@ const CreateModal = forwardRef<CreateModalRef, CreateModalRefProps>(({
|
||||
const [modelTypeList, setModelTypeList] = useState<string[]>([]);
|
||||
const [modelOptionsByType, setModelOptionsByType] = useState<Record<string, { label: string; value: string }[]>>({});
|
||||
const [datasets, setDatasets] = useState<KnowledgeBaseListItem | null>(null);
|
||||
const [currentType, setCurrentType] = useState<string>('General'); // 保存当前 type
|
||||
const [currentType, setCurrentType] = useState<'General' | 'Web' | 'Third-party' | 'Folder'>('General');
|
||||
const [form] = Form.useForm<KnowledgeBaseFormData>();
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
@@ -102,7 +102,7 @@ const CreateModal = forwardRef<CreateModalRef, CreateModalRefProps>(({
|
||||
const handleOpen = (record?: KnowledgeBaseListItem | null, type?: string) => {
|
||||
setDatasets(record || null);
|
||||
const nextType = type || currentType;
|
||||
setCurrentType(nextType);
|
||||
setCurrentType(nextType as any);
|
||||
setBaseFields(record || null, nextType);
|
||||
getTypeList(record || null);
|
||||
setVisible(true);
|
||||
|
||||
@@ -8,7 +8,7 @@ import textIcon from '@/assets/images/knowledgeBase/text.png';
|
||||
import imageIcon from '@/assets/images/knowledgeBase/image.png';
|
||||
import datasetsIcon from '@/assets/images/knowledgeBase/datasets.png';
|
||||
import switcherIcon from '@/assets/images/knowledgeBase/switcher.png';
|
||||
import { getFolderList } from '../service';
|
||||
import { getFolderList } from '@/api/knowledgeBase';
|
||||
|
||||
const { DirectoryTree } = Tree;
|
||||
|
||||
@@ -59,6 +59,7 @@ interface FolderTreeProps {
|
||||
refreshKey?: number;
|
||||
onRootLoad?: (nodes: TreeNodeData[] | null) => void;
|
||||
onFolderPathChange?: (path: Array<{ id: string; name: string }>) => void;
|
||||
selectedKeys?: React.Key[];
|
||||
}
|
||||
|
||||
const renderIcon = (icon?: string) => {
|
||||
@@ -273,6 +274,7 @@ const FolderTree: FC<FolderTreeProps> = ({
|
||||
refreshKey = 0,
|
||||
onRootLoad,
|
||||
onFolderPathChange,
|
||||
selectedKeys,
|
||||
}) => {
|
||||
const [treeData, setTreeData] = useState<TreeNodeData[]>([]);
|
||||
|
||||
@@ -396,6 +398,7 @@ const FolderTree: FC<FolderTreeProps> = ({
|
||||
onExpand={onExpand}
|
||||
loadData={onLoadData}
|
||||
treeData={treeNodes}
|
||||
selectedKeys={selectedKeys}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
|
||||
import { Form, Input, Select, Button, InputNumber } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { RecallTestDrawerRef, RecallTestData, RecallTestParams } from '../types';
|
||||
import type { RecallTestDrawerRef, RecallTestData, RecallTestParams } from '@/views/KnowledgeBase/types';
|
||||
// import refreshIcon from '@/assets/images/knowledgeBase/refresh-blue.png';
|
||||
import RecallTestResult from './RecallTestResult';
|
||||
import { reChunks, getRetrievalModeType } from '../service';
|
||||
import { reChunks, getRetrievalModeType } from '@/api/knowledgeBase';
|
||||
import { hybrid } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { forwardRef, useImperativeHandle, useState, useRef, useLayoutEffect, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import RbDrawer from '@/components/RbDrawer';
|
||||
import type { RecallTestDrawerRef } from '../types';
|
||||
import type { RecallTestDrawerRef } from '@/views/KnowledgeBase/types';
|
||||
import RecallTest from './RecallTest';
|
||||
|
||||
const RecallTestDrawer = forwardRef<RecallTestDrawerRef>(({},ref) => {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import { FileOutlined, FieldTimeOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import { Skeleton } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { RecallTestData } from '../types';
|
||||
import type { RecallTestData } from '@/views/KnowledgeBase/types';
|
||||
import { NoData } from './noData';
|
||||
import { formatDateTime } from '@/utils/format';
|
||||
import InfiniteScroll from 'react-infinite-scroll-component';
|
||||
|
||||
@@ -10,14 +10,14 @@ import { forwardRef, useImperativeHandle, useState, useRef } from 'react';
|
||||
import { Switch } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { message } from 'antd';
|
||||
import type { ShareModalRef, ShareModalRefProps, KnowledgeBase} from '../types';
|
||||
import type { ShareModalRef, ShareModalRefProps, KnowledgeBase} from '@/views/KnowledgeBase/types';
|
||||
import RbModal from '@/components/RbModal'
|
||||
// import betchControlIcon from '@/assets/images/knowledgeBase/betch-control.png';
|
||||
import kbIcon from '@/assets/images/knowledgeBase/knowledge-management.png';
|
||||
// import robotIcon from '@/assets/images/knowledgeBase/robot.png';
|
||||
import { updateKnowledgeBase, getWorkspaceAuthorizationList } from '../service';
|
||||
import { updateKnowledgeBase, getWorkspaceAuthorizationList } from '@/api/knowledgeBase';
|
||||
import { NoData } from './noData';
|
||||
import type { ListQuery, ShareSpaceModalRef } from '../types';
|
||||
import type { ListQuery, ShareSpaceModalRef } from '@/views/KnowledgeBase/types';
|
||||
import { formatDateTime } from '@/utils/format';
|
||||
import ShareSpaceModal from './ShareSpaceModal'
|
||||
const ShareModal = forwardRef<ShareModalRef,ShareModalRefProps>(({ handleShare: onShare }, ref) => {
|
||||
|
||||
@@ -10,14 +10,14 @@ import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Switch } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { message } from 'antd';
|
||||
import type { ShareModalRef, ShareModalRefProps, KnowledgeBase} from '../types';
|
||||
import type { ShareModalRef, ShareModalRefProps, KnowledgeBase} from '@/views/KnowledgeBase/types';
|
||||
import RbModal from '@/components/RbModal'
|
||||
// import betchControlIcon from '@/assets/images/knowledgeBase/betch-control.png';
|
||||
import kbIcon from '@/assets/images/knowledgeBase/knowledge-management.png';
|
||||
// import robotIcon from '@/assets/images/knowledgeBase/robot.png';
|
||||
import { getSpaceList, shareKnowledgeBase } from '../service';
|
||||
import { getSpaceList, shareKnowledgeBase } from '@/api/knowledgeBase';
|
||||
import { NoData } from './noData';
|
||||
import type { SpaceItem } from '../types';
|
||||
import type { SpaceItem } from '@/views/KnowledgeBase/types';
|
||||
import { formatDateTime } from '@/utils/format';
|
||||
const ShareModal = forwardRef<ShareModalRef,ShareModalRefProps>(({ handleShare: onShare }, ref) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button } from 'antd';
|
||||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||
|
||||
import { request } from '@/utils/request';
|
||||
import type { KnowledgeBase } from './types';
|
||||
import type { KnowledgeBase } from '@/views/KnowledgeBase/types';
|
||||
|
||||
const Datasets: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -10,14 +10,16 @@ import folderIcon from '@/assets/images/knowledgeBase/folder.png';
|
||||
import generalIcon from '@/assets/images/knowledgeBase/datasets.png';
|
||||
import webIcon from '@/assets/images/knowledgeBase/general.png';
|
||||
import tpIcon from '@/assets/images/knowledgeBase/text.png';
|
||||
import type { KnowledgeBaseListItem, CreateModalRef, KnowledgeBaseListResponse, ListQuery } from './types'
|
||||
import type { KnowledgeBaseListItem, CreateModalRef, KnowledgeBaseListResponse, ListQuery } from '@/views/KnowledgeBase/types'
|
||||
import CreateModal from './components/CreateModal'
|
||||
import RbCard from '@/components/RbCard'
|
||||
import SearchInput from '@/components/SearchInput'
|
||||
import Empty from '@/components/Empty'
|
||||
import { getKnowledgeBaseList, getModelList, getModelTypeList, deleteKnowledgeBase, getKnowledgeBaseTypeList } from './service'
|
||||
import { getKnowledgeBaseList, getModelList, getModelTypeList, deleteKnowledgeBase, getKnowledgeBaseTypeList } from '@/api/knowledgeBase'
|
||||
const { confirm } = Modal;
|
||||
import InfiniteScroll from 'react-infinite-scroll-component';
|
||||
import { useMenu } from '@/store/menu';
|
||||
|
||||
type ModelMenuInfo = {
|
||||
menu: NonNullable<MenuProps['items']>;
|
||||
summary: string[];
|
||||
@@ -41,6 +43,10 @@ const KnowledgeBaseManagement: FC = () => {
|
||||
const modalRef = useRef<CreateModalRef>(null)
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
|
||||
// 使用 menu store 管理面包屑
|
||||
const { allBreadcrumbs, setCustomBreadcrumbs } = useMenu();
|
||||
const [folderPath, setFolderPath] = useState<Array<{ id: string; name: string }>>([]);
|
||||
|
||||
|
||||
// 生成下拉菜单项(根据当前 item)
|
||||
const getOptMenuItems = (item: KnowledgeBaseListItem): MenuProps['items'] => {
|
||||
@@ -105,7 +111,17 @@ const KnowledgeBaseManagement: FC = () => {
|
||||
|
||||
// 处理创建
|
||||
const handleCreate = (type?: string) => {
|
||||
modalRef?.current?.handleOpen(null, type)
|
||||
// 如果在文件夹内,使用 folderPath 的最后一项作为 parent_id
|
||||
// 这样更可靠,因为 folderPath 是直接管理的状态
|
||||
const currentParentId = folderPath.length > 0
|
||||
? folderPath[folderPath.length - 1].id
|
||||
: query.parent_id; // 降级使用 query.parent_id
|
||||
|
||||
const record = currentParentId ? {
|
||||
parent_id: currentParentId as string,
|
||||
} as KnowledgeBaseListItem : null;
|
||||
|
||||
modalRef?.current?.handleOpen(record, type)
|
||||
}
|
||||
|
||||
// 动态生成 createItems
|
||||
@@ -118,7 +134,7 @@ const KnowledgeBaseManagement: FC = () => {
|
||||
handleCreate(type);
|
||||
},
|
||||
}));
|
||||
}, [knowledgeBaseTypes, t]);
|
||||
}, [knowledgeBaseTypes, t, folderPath, query]);
|
||||
const typeToFieldKey = (type: string) => {
|
||||
const normalized = (type || '').toLowerCase();
|
||||
switch (normalized) {
|
||||
@@ -176,7 +192,7 @@ const KnowledgeBaseManagement: FC = () => {
|
||||
const fetchKnowledgeBaseTypes = async () => {
|
||||
try {
|
||||
let types = await getKnowledgeBaseTypeList();
|
||||
types = types.filter(type => (type === 'General' )); //|| type === 'Folder'
|
||||
types = types.filter(type => (type === 'General' || type === 'Folder' )); //
|
||||
//暂时未实现 ,过滤掉未实现
|
||||
setKnowledgeBaseTypes(types);
|
||||
} catch (error) {
|
||||
@@ -337,6 +353,25 @@ const KnowledgeBaseManagement: FC = () => {
|
||||
};
|
||||
// 处理跳转详情
|
||||
const handleToDetail = (knowledgeBase: KnowledgeBaseListItem) => {
|
||||
// 如果是 Folder 类型,刷新当前页面,显示该文件夹下的知识库列表
|
||||
if (knowledgeBase.type === 'Folder' || knowledgeBase.type === 'folder') {
|
||||
// 添加到文件夹路径
|
||||
const newFolderPath = [
|
||||
...folderPath,
|
||||
{
|
||||
id: knowledgeBase.id,
|
||||
name: knowledgeBase.name,
|
||||
},
|
||||
];
|
||||
setFolderPath(newFolderPath);
|
||||
|
||||
setQuery((prev) => ({
|
||||
...prev,
|
||||
parent_id: knowledgeBase.id,
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据权限类型跳转到不同的详情页
|
||||
if (knowledgeBase.permission_id === 'Private' || knowledgeBase.permission_id === 'private') {
|
||||
navigate(`/knowledge-base/${knowledgeBase.id}/private`)
|
||||
@@ -344,6 +379,83 @@ const KnowledgeBaseManagement: FC = () => {
|
||||
navigate(`/knowledge-base/${knowledgeBase.id}/share`)
|
||||
}
|
||||
}
|
||||
// 更新面包屑的函数
|
||||
const updateBreadcrumbs = () => {
|
||||
const baseBreadcrumbs = allBreadcrumbs['space'] || [];
|
||||
// 只保留知识库菜单项之前的面包屑
|
||||
const knowledgeBaseMenuIndex = baseBreadcrumbs.findIndex(item => item.path === '/knowledge-base');
|
||||
const filteredBaseBreadcrumbs = knowledgeBaseMenuIndex >= 0
|
||||
? baseBreadcrumbs.slice(0, knowledgeBaseMenuIndex + 1)
|
||||
: baseBreadcrumbs;
|
||||
|
||||
// 给"知识库管理"添加点击事件,返回根目录
|
||||
const breadcrumbsWithClick = filteredBaseBreadcrumbs.map((item) => {
|
||||
if (item.path === '/knowledge-base') {
|
||||
return {
|
||||
...item,
|
||||
onClick: (e?: React.MouseEvent) => {
|
||||
e?.preventDefault();
|
||||
e?.stopPropagation();
|
||||
// 返回根目录
|
||||
setFolderPath([]);
|
||||
setQuery((prev) => ({
|
||||
...prev,
|
||||
parent_id: undefined,
|
||||
}));
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
const customBreadcrumbs = [
|
||||
...breadcrumbsWithClick,
|
||||
...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();
|
||||
// 点击文件夹,回到该文件夹层级
|
||||
const newFolderPath = folderPath.slice(0, index + 1);
|
||||
setFolderPath(newFolderPath);
|
||||
setQuery((prev) => ({
|
||||
...prev,
|
||||
parent_id: folder.id,
|
||||
}));
|
||||
return false;
|
||||
},
|
||||
})),
|
||||
];
|
||||
|
||||
setCustomBreadcrumbs(customBreadcrumbs, 'space');
|
||||
};
|
||||
|
||||
// 更新面包屑
|
||||
useEffect(() => {
|
||||
updateBreadcrumbs();
|
||||
}, [folderPath]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchModelTypes();
|
||||
fetchKnowledgeBaseTypes();
|
||||
|
||||
@@ -1,280 +0,0 @@
|
||||
import { request } from "@/utils/request";
|
||||
import type { AxiosProgressEvent } from "axios";
|
||||
import type {
|
||||
ShareRequestParams,
|
||||
SpaceItem,
|
||||
UploadFileFormData,
|
||||
FolderFormData,
|
||||
UploadFileResponse,
|
||||
Model,
|
||||
PageRequest,
|
||||
KnowledgeBase,
|
||||
KnowledgeBaseFormData,
|
||||
ListQuery,
|
||||
PathQuery,
|
||||
KnowledgeBaseDocumentData,
|
||||
KnowledgeBaseListResponse,
|
||||
KnowledgeBaseShareListResponse,
|
||||
} from "./types";
|
||||
|
||||
const apiPrefix = '';
|
||||
|
||||
// 从路由中获取空间ID (#号后第一个路径段)
|
||||
export const getSpaceIdFromRoute = (): string | null => {
|
||||
if (typeof window === 'undefined') return null;
|
||||
const hash = window.location.hash;
|
||||
if (!hash || hash === '#') return null;
|
||||
// 移除 # 号,然后分割路径
|
||||
const path = hash.slice(1); // 移除 #
|
||||
const segments = path.split('/').filter(Boolean); // 分割并过滤空字符串
|
||||
return segments.length > 0 ? segments[0] : null;
|
||||
};
|
||||
|
||||
export const spaceId = getSpaceIdFromRoute();
|
||||
//获取知识库类型 (返回字符串数组,每个字符串是 KnowledgeBase 的 type 值)
|
||||
export const getKnowledgeBaseTypeList = async (): Promise<string[]> => {
|
||||
const response = await request.get(`${apiPrefix}/knowledges/knowledgetype`);
|
||||
// 如果直接返回字符串数组,直接返回
|
||||
if (Array.isArray(response)) {
|
||||
return response.map(item => {
|
||||
// 如果是字符串,直接返回
|
||||
if (typeof item === 'string') {
|
||||
return item;
|
||||
}
|
||||
// 如果是对象且有 type 字段,提取 type 值
|
||||
if (typeof item === 'object' && item !== null && 'type' in item) {
|
||||
return String(item.type);
|
||||
}
|
||||
// 其他情况转换为字符串
|
||||
return String(item);
|
||||
});
|
||||
}
|
||||
// 如果不是数组,返回空数组
|
||||
return [];
|
||||
};
|
||||
// 知识库文档解析类型
|
||||
export const getKnowledgeBaseDocumentParseTypeList = async () => {
|
||||
const response = await request.get(`${apiPrefix}/knowledges/parsertype`);
|
||||
return response as any[];
|
||||
};
|
||||
|
||||
//获取模型类型
|
||||
export const getModelTypeList = async () => {
|
||||
const response = await request.get(`${apiPrefix}/models/type`);
|
||||
return response as any[];
|
||||
};
|
||||
// 获取模型列表
|
||||
export const getModelList = async (type: string | string[], pageInfo: PageRequest) => {
|
||||
const response = await request.get(`${apiPrefix}/models`, { type, ...pageInfo });
|
||||
return response as any;
|
||||
};
|
||||
//获取模型提供者
|
||||
export const getModelProviderList = async () => {
|
||||
const response = await request.get(`${apiPrefix}/models/provider`);
|
||||
return response as any[];
|
||||
};
|
||||
// 获取模型信息
|
||||
export const getModelDetail = async (id: string) => {
|
||||
const response = await request.get(`${apiPrefix}/models/${id}`);
|
||||
return response as Model;
|
||||
};
|
||||
|
||||
// 知识库列表
|
||||
export const getKnowledgeBaseList = async (parent_id?: string, query?: ListQuery) => {
|
||||
const response = await request.get(`${apiPrefix}/knowledges/knowledges`, query);
|
||||
return response as KnowledgeBaseListResponse;
|
||||
};
|
||||
// 知识库详情
|
||||
export const getKnowledgeBaseDetail = async (id: string) => {
|
||||
const response = await request.get(`${apiPrefix}/knowledges/${id}`);
|
||||
return response as KnowledgeBase;
|
||||
};
|
||||
// 创建知识库
|
||||
export const createKnowledgeBase = async (data: KnowledgeBaseFormData) => {
|
||||
const payload: KnowledgeBaseFormData = {
|
||||
...data,
|
||||
permission_id: data.permission_id ?? 'private',
|
||||
};
|
||||
const response = await request.post(`${apiPrefix}/knowledges/knowledge`, payload);
|
||||
return response as KnowledgeBase;
|
||||
};
|
||||
// 更新知识库
|
||||
export const updateKnowledgeBase = async (id: string, data: KnowledgeBaseFormData) => {
|
||||
const payload: KnowledgeBaseFormData = {
|
||||
...data,
|
||||
};
|
||||
const response = await request.put(`${apiPrefix}/knowledges/${id}`, payload);
|
||||
return response as any;
|
||||
};
|
||||
// 删除知识库(软删除)
|
||||
export const deleteKnowledgeBase = async (id: string) => {
|
||||
const response = await request.delete(`${apiPrefix}/knowledges/${id}`);
|
||||
return response as any;
|
||||
}
|
||||
|
||||
// 知识库分享 获取分享空间列表
|
||||
export const getShareSpaceList = async (id: string) => {
|
||||
const response = await request.get(`${apiPrefix}/knowledgeshares/${id}/knowledgeshares`);
|
||||
return response as KnowledgeBaseShareListResponse;
|
||||
}
|
||||
|
||||
// 获取文件夹列表
|
||||
export const getFolderList = async (query: FolderFormData) => {
|
||||
const id = query.parent_id ?? query.kb_id;
|
||||
const response = await request.get(`${apiPrefix}/files/${query.kb_id}/${id}/files`);
|
||||
return response as any;
|
||||
};
|
||||
// 创建文件夹
|
||||
export const createFolder = async (params: FolderFormData) => {
|
||||
const response = await request.post(`${apiPrefix}/files/folder`, undefined, {
|
||||
params,
|
||||
});
|
||||
return response as FolderFormData;
|
||||
};
|
||||
interface UploadFileOptions {
|
||||
kb_id?: string;
|
||||
parent_id?: string;
|
||||
onUploadProgress?: (event: AxiosProgressEvent) => void;
|
||||
}
|
||||
// 上传文件
|
||||
export const uploadFile = async (data: FormData, options?: UploadFileOptions) => {
|
||||
const { kb_id, parent_id, onUploadProgress } = options || {};
|
||||
const params: Record<string, string> = {};
|
||||
if (kb_id) params.kb_id = kb_id;
|
||||
if (parent_id) params.parent_id = parent_id;
|
||||
const response = await request.uploadFile(`${apiPrefix}/files/file`, data, {
|
||||
params,
|
||||
onUploadProgress,
|
||||
});
|
||||
return response as UploadFileResponse;
|
||||
};
|
||||
|
||||
// 下载文件
|
||||
export const downloadFile = async (fileId: string, fileName?: string) => {
|
||||
const token = localStorage.getItem('token');
|
||||
const url = `${apiPrefix}/files/${fileId}`;
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('下载失败');
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
|
||||
// 创建临时链接触发下载
|
||||
const link = document.createElement('a');
|
||||
link.href = blobUrl;
|
||||
link.style.display = 'none';
|
||||
if (fileName) {
|
||||
link.setAttribute('download', fileName);
|
||||
}
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
// 释放 blob URL
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
} catch (error) {
|
||||
console.error('下载文件失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
// 更新文件信息
|
||||
export const updateFile = async (id: string, data: UploadFileFormData) => {
|
||||
const response = await request.put(`${apiPrefix}/files/${id}`, data);
|
||||
return response as UploadFileResponse;
|
||||
};
|
||||
// 删除文件 文件夹 id
|
||||
export const deleteFile = async (id: string) => {
|
||||
const response = await request.delete(`${apiPrefix}/files/${id}`);
|
||||
return response as any;
|
||||
};
|
||||
|
||||
// 获取文档列表
|
||||
export const getDocumentList = async (query: PathQuery) => {
|
||||
const response = await request.get(`${apiPrefix}/documents/${query.kb_id}/${query.parent_id}/documents`, query);
|
||||
return response as KnowledgeBaseDocumentData[];
|
||||
};
|
||||
// 文档详情
|
||||
export const getDocumentDetail = async (id: string) => {
|
||||
const response = await request.get(`${apiPrefix}/documents/${id}`);
|
||||
return response as KnowledgeBaseDocumentData;
|
||||
};
|
||||
// 创建文档
|
||||
export const createDocument = async (data: KnowledgeBaseDocumentData) => {
|
||||
const response = await request.post(`${apiPrefix}/documents/document`, data);
|
||||
return response as KnowledgeBaseDocumentData;
|
||||
};
|
||||
// 更新文档
|
||||
export const updateDocument = async (id: string, data: KnowledgeBaseDocumentData) => {
|
||||
const response = await request.put(`${apiPrefix}/documents/${id}`, data);
|
||||
return response as KnowledgeBaseDocumentData;
|
||||
};
|
||||
// 删除文档
|
||||
export const deleteDocument = async (id: string) => {
|
||||
const response = await request.delete(`${apiPrefix}/documents/${id}`);
|
||||
return response;
|
||||
};
|
||||
// 文档解析
|
||||
export const parseDocument = async (id: string) => {
|
||||
const response = await request.post(`${apiPrefix}/documents/${id}/chunks`);
|
||||
return response as any;
|
||||
};
|
||||
// 文档分块预览
|
||||
export const previewDocumentChunk = async (kb_id:string,id: string) => { // id document_id
|
||||
const response = await request.get(`${apiPrefix}/chunks/${kb_id}/${id}/previewchunks`);
|
||||
return response as any;
|
||||
};
|
||||
//文档分块列表
|
||||
export const getDocumentChunkList = async (query: PathQuery) => {
|
||||
const response = await request.get(`${apiPrefix}/chunks/${query.kb_id}/${query.document_id}/chunks`, query);
|
||||
return response as any;
|
||||
};
|
||||
// 回归测试
|
||||
export const reChunks = async (data: any) => {
|
||||
const response = await request.post(`${apiPrefix}/chunks/retrieval`, data);
|
||||
return response as any;
|
||||
};
|
||||
// 知识库授权 分享空间列表
|
||||
export const getWorkspaceAuthorizationList = async (kb_id: string) => {
|
||||
const response = await request.get(`${apiPrefix}/knowledgeshares/${kb_id}/knowledgeshares`);
|
||||
return response as any;
|
||||
};
|
||||
// 知识库分享
|
||||
export const shareKnowledgeBase = async (data: ShareRequestParams) => {
|
||||
const response = await request.post(`${apiPrefix}/knowledgeshares/knowledgeshare`, data);
|
||||
return response as KnowledgeBase;
|
||||
}
|
||||
// 空间列表
|
||||
export const getSpaceList = async () => {
|
||||
const response = await request.get(`${apiPrefix}/workspaces`,{include_current:false});
|
||||
// API 返回的 data 直接是数组,需要包装成 { items: [] } 格式以保持一致性
|
||||
if (Array.isArray(response)) {
|
||||
return { items: response };
|
||||
}
|
||||
return response as { items: SpaceItem[] };
|
||||
};
|
||||
// 更新文档块儿
|
||||
export const updateDocumentChunk = async (kb_id:string, document_id:string, doc_id:string, data: any) => {
|
||||
const response = await request.put(`${apiPrefix}/chunks/${kb_id}/${document_id}/${doc_id}`, data);
|
||||
return response as any;
|
||||
};
|
||||
|
||||
// 文档块儿创建
|
||||
export const createDocumentChunk = async (kb_id:string, document_id:string, data: any) => {
|
||||
const response = await request.post(`${apiPrefix}/chunks/${kb_id}/${document_id}/chunk`, data);
|
||||
return response as any;
|
||||
};
|
||||
// 获取检索模式类型
|
||||
export const getRetrievalModeType = async () => {
|
||||
const response = await request.get(`${apiPrefix}/chunks/retrieve_type`);
|
||||
return response as any;
|
||||
};
|
||||
@@ -359,4 +359,4 @@ export interface ShareSpaceModalRef{
|
||||
|
||||
export interface ShareSpaceModalRefProps {
|
||||
handleShare?: () => void;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user