diff --git a/client/public/imgs/files/folder.svg b/client/public/imgs/files/folder.svg
new file mode 100644
index 000000000..602393396
--- /dev/null
+++ b/client/public/imgs/files/folder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json
index 3b7369b61..9eff60c16 100644
--- a/client/public/locales/en/common.json
+++ b/client/public/locales/en/common.json
@@ -2,6 +2,10 @@
"App": "App",
"Cancel": "No",
"Confirm": "Yes",
+ "Create New": "Create",
+ "Dataset": "Dataset",
+ "Folder": "Folder",
+ "Name": "Name",
"Running": "Running",
"Select value is empty": "Select value is empty",
"UnKnow": "UnKnow",
@@ -74,12 +78,14 @@
"Copy Successful": "Copy Successful",
"Course": "",
"Delete": "Delete",
+ "Delete Warning": "Warning",
"Filed is repeat": "Filed is repeated",
"Filed is repeated": "",
"Input": "Input",
"Output": "Output",
- "export": "",
- "Password inconsistency": "Password inconsistency"
+ "Password inconsistency": "Password inconsistency",
+ "Rename": "Rename",
+ "export": ""
},
"dataset": {
"Confirm to delete the data": "Confirm to delete the data?",
@@ -148,6 +154,13 @@
"desc": "AI knowledge base question and answer platform based on LLM large model",
"slogan": "Let the AI know more about you"
},
+ "kb": {
+ "Create Folder": "Create Folder",
+ "Edit Folder": "Edit Folder",
+ "Folder Name": "Input folder name",
+ "deleteDatasetTips": "Are you sure to delete the knowledge base? Data cannot be recovered after deletion, please confirm!",
+ "deleteFolderTips": "Are you sure to delete this folder and all the knowledge bases it contains? Data cannot be recovered after deletion, please confirm!"
+ },
"navbar": {
"Account": "Account",
"Apps": "Apps",
diff --git a/client/public/locales/zh/common.json b/client/public/locales/zh/common.json
index 867bdb861..5285d14c4 100644
--- a/client/public/locales/zh/common.json
+++ b/client/public/locales/zh/common.json
@@ -2,6 +2,10 @@
"App": "应用",
"Cancel": "取消",
"Confirm": "确认",
+ "Create New": "新建",
+ "Dataset": "知识库",
+ "Folder": "文件夹",
+ "Name": "名称",
"Running": "运行中",
"Select value is empty": "选择的内容为空",
"UnKnow": "未知",
@@ -74,12 +78,14 @@
"Copy Successful": "复制成功",
"Course": "",
"Delete": "删除",
+ "Delete Warning": "删除警告",
"Filed is repeat": "",
"Filed is repeated": "字段重复了",
"Input": "输入",
"Output": "输出",
- "export": "",
- "Password inconsistency": "两次密码不一致"
+ "Password inconsistency": "两次密码不一致",
+ "Rename": "重命名",
+ "export": ""
},
"dataset": {
"Confirm to delete the data": "确认删除该数据?",
@@ -148,6 +154,13 @@
"desc": "基于 LLM 大模型的 AI 知识库问答平台",
"slogan": "让 AI 更懂你的知识"
},
+ "kb": {
+ "Create Folder": "创建文件夹",
+ "Edit Folder": "编辑文件夹",
+ "Folder Name": "输入文件夹名称",
+ "deleteDatasetTips": "确认删除该知识库?删除后数据无法恢复,请确认!",
+ "deleteFolderTips": "确认删除该文件夹及其包含的所有知识库?删除后数据无法恢复,请确认!"
+ },
"navbar": {
"Account": "账号",
"Apps": "应用",
diff --git a/client/src/api/plugins/kb.ts b/client/src/api/plugins/kb.ts
index ac24a5ef7..f71f4903c 100644
--- a/client/src/api/plugins/kb.ts
+++ b/client/src/api/plugins/kb.ts
@@ -16,7 +16,8 @@ import type { KbUpdateParams, CreateKbParams } from '../request/kb';
import { QuoteItemType } from '@/types/chat';
/* knowledge base */
-export const getKbList = () => GET(`/plugins/kb/list`);
+export const getKbList = (parentId?: string) =>
+ GET(`/plugins/kb/list`, { parentId });
export const getKbById = (id: string) => GET(`/plugins/kb/detail?id=${id}`);
diff --git a/client/src/api/request/kb.d.ts b/client/src/api/request/kb.d.ts
index 1ea227e7c..e139ca668 100644
--- a/client/src/api/request/kb.d.ts
+++ b/client/src/api/request/kb.d.ts
@@ -1,12 +1,15 @@
+import { KbTypeEnum } from '@/constants/kb';
export type KbUpdateParams = {
id: string;
- name: string;
- tags: string;
- avatar: string;
+ tags?: string;
+ name?: string;
+ avatar?: string;
};
export type CreateKbParams = {
+ parentId?: string;
name: string;
tags: string[];
avatar: string;
- vectorModel: string;
+ vectorModel?: string;
+ type: `${KbTypeEnum}`;
};
diff --git a/client/src/components/MyMenu/index.tsx b/client/src/components/MyMenu/index.tsx
new file mode 100644
index 000000000..76d35ff65
--- /dev/null
+++ b/client/src/components/MyMenu/index.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { Menu, MenuList, MenuItem } from '@chakra-ui/react';
+
+interface Props {
+ width: number;
+ offset?: [number, number];
+ Button: React.ReactNode;
+ menuList: {
+ isActive?: boolean;
+ child: React.ReactNode;
+ onClick: () => void;
+ }[];
+}
+
+const MyMenu = ({ width, offset = [0, 10], Button, menuList }: Props) => {
+ const menuItemStyles = {
+ borderRadius: 'sm',
+ py: 3,
+ display: 'flex',
+ alignItems: 'center',
+ _hover: {
+ backgroundColor: 'myWhite.600',
+ color: 'hover.blue'
+ }
+ };
+
+ return (
+
+ );
+};
+
+export default MyMenu;
diff --git a/client/src/constants/kb.ts b/client/src/constants/kb.ts
index 6d299566a..47742a1a5 100644
--- a/client/src/constants/kb.ts
+++ b/client/src/constants/kb.ts
@@ -14,3 +14,19 @@ export const defaultKbDetail: KbItemType = {
maxToken: 3000
}
};
+
+export enum KbTypeEnum {
+ folder = 'folder',
+ dataset = 'dataset'
+}
+
+export const KbTypeMap = {
+ [KbTypeEnum.folder]: {
+ name: 'folder'
+ },
+ [KbTypeEnum.dataset]: {
+ name: 'dataset'
+ }
+};
+
+export const FolderAvatarSrc = '/imgs/files/folder.svg';
diff --git a/client/src/hooks/useConfirm.tsx b/client/src/hooks/useConfirm.tsx
index ed08b7014..c8258b051 100644
--- a/client/src/hooks/useConfirm.tsx
+++ b/client/src/hooks/useConfirm.tsx
@@ -1,4 +1,4 @@
-import { useCallback, useRef } from 'react';
+import { useCallback, useRef, useState } from 'react';
import {
AlertDialog,
AlertDialogBody,
@@ -11,21 +11,25 @@ import {
} from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
-export const useConfirm = (props: { title?: string; content: string }) => {
+export const useConfirm = (props: { title?: string | null; content?: string | null }) => {
const { t } = useTranslation();
const { title = t('Warning'), content } = props;
+ const [customContent, setCustomContent] = useState(content);
const { isOpen, onOpen, onClose } = useDisclosure();
+
const cancelRef = useRef(null);
const confirmCb = useRef();
const cancelCb = useRef();
return {
openConfirm: useCallback(
- (confirm?: any, cancel?: any) => {
+ (confirm?: any, cancel?: any, customContent?: string) => {
confirmCb.current = confirm;
cancelCb.current = cancel;
+ customContent && setCustomContent(customContent);
+
return onOpen;
},
[onOpen]
@@ -44,7 +48,7 @@ export const useConfirm = (props: { title?: string; content: string }) => {
{title}
- {content}
+ {customContent}
+
+
+
+ {t('Create New')}
+
+
+ }
+ menuList={[
+ {
+ child: (
+
+
+ {t('Folder')}
+
+ ),
+ onClick: () => setEditFolderData({})
+ },
+ {
+ child: (
+
+
+ {t('Dataset')}
+
+ ),
+ onClick: onOpenCreateModal
+ }
+ ]}
+ />
{
py={4}
px={5}
cursor={'pointer'}
- h={'140px'}
+ h={'130px'}
border={theme.borders.md}
boxShadow={'none'}
userSelect={'none'}
@@ -98,14 +156,23 @@ const Kb = () => {
display: 'block'
}
}}
- onClick={() =>
- router.push({
- pathname: '/kb/detail',
- query: {
- kbId: kb._id
- }
- })
- }
+ onClick={() => {
+ if (kb.type === KbTypeEnum.folder) {
+ router.push({
+ pathname: '/kb/list',
+ query: {
+ parentId: kb._id
+ }
+ });
+ } else if (kb.type === KbTypeEnum.dataset) {
+ router.push({
+ pathname: '/kb/detail',
+ query: {
+ kbId: kb._id
+ }
+ });
+ }
+ }}
>
@@ -126,7 +193,11 @@ const Kb = () => {
}}
onClick={(e) => {
e.stopPropagation();
- openConfirm(() => onclickDelKb(kb._id))();
+ openConfirm(
+ () => onclickDelKb(kb._id),
+ undefined,
+ DeleteTipsMap.current[kb.type]
+ )();
}}
/>
@@ -140,8 +211,14 @@ const Kb = () => {
-
- {kb.vectorModel.name}
+ {kb.type === KbTypeEnum.folder ? (
+ {t('Folder')}
+ ) : (
+ <>
+
+ {kb.vectorModel.name}
+ >
+ )}
))}
@@ -155,7 +232,15 @@ const Kb = () => {
)}
- {isOpenCreateModal && }
+ {isOpenCreateModal && }
+ {!!editFolderData && (
+ setEditFolderData(undefined)}
+ onSuccess={refetch}
+ parentId={parentId}
+ {...editFolderData}
+ />
+ )}
);
};
diff --git a/client/src/service/models/kb.ts b/client/src/service/models/kb.ts
index 9c1856300..a1f9bea2a 100644
--- a/client/src/service/models/kb.ts
+++ b/client/src/service/models/kb.ts
@@ -1,7 +1,13 @@
import { Schema, model, models, Model } from 'mongoose';
import { kbSchema as SchemaType } from '@/types/mongoSchema';
+import { KbTypeMap } from '@/constants/kb';
const kbSchema = new Schema({
+ parentId: {
+ type: Schema.Types.ObjectId,
+ ref: 'kb',
+ default: null
+ },
userId: {
type: Schema.Types.ObjectId,
ref: 'user',
@@ -24,6 +30,11 @@ const kbSchema = new Schema({
required: true,
default: 'text-embedding-ada-002'
},
+ type: {
+ type: String,
+ enum: Object.keys(KbTypeMap),
+ required: true
+ },
tags: {
type: [String],
default: []
diff --git a/client/src/store/user.ts b/client/src/store/user.ts
index 41142d48d..b92b9c68b 100644
--- a/client/src/store/user.ts
+++ b/client/src/store/user.ts
@@ -8,7 +8,7 @@ import { getTokenLogin, putUserInfo } from '@/api/user';
import { defaultApp } from '@/constants/model';
import { AppListItemType, AppUpdateParams } from '@/types/app';
import type { KbItemType, KbListItemType } from '@/types/plugin';
-import { getKbList, getKbById } from '@/api/plugins/kb';
+import { getKbList, getKbById, putKbById } from '@/api/plugins/kb';
import { defaultKbDetail } from '@/constants/kb';
import type { AppSchema } from '@/types/mongoSchema';
@@ -26,7 +26,7 @@ type State = {
clearAppModules(): void;
// kb
myKbList: KbListItemType[];
- loadKbList: () => Promise;
+ loadKbList: (parentId: string) => Promise;
setKbList(val: KbListItemType[]): void;
kbDetail: KbItemType;
getKbDetail: (id: string, init?: boolean) => Promise;
@@ -108,14 +108,14 @@ export const useUserStore = create()(
});
},
myKbList: [],
- async loadKbList() {
- const res = await getKbList();
+ async loadKbList(parentId) {
+ const res = await getKbList(parentId);
set((state) => {
state.myKbList = res;
});
return res;
},
- setKbList(val: KbListItemType[]) {
+ setKbList(val) {
set((state) => {
state.myKbList = val;
});
diff --git a/client/src/types/mongoSchema.d.ts b/client/src/types/mongoSchema.d.ts
index 5be07d351..38840e043 100644
--- a/client/src/types/mongoSchema.d.ts
+++ b/client/src/types/mongoSchema.d.ts
@@ -6,6 +6,7 @@ import { TrainingModeEnum } from '@/constants/plugin';
import type { AppModuleItemType } from './app';
import { ChatSourceEnum, OutLinkTypeEnum } from '@/constants/chat';
import { AppTypeEnum } from '@/constants/app';
+import { KbTypeEnum } from '@/constants/kb';
export interface UserModelSchema {
_id: string;
@@ -166,15 +167,17 @@ export interface OutLinkSchema {
type: `${OutLinkTypeEnum}`;
}
-export interface kbSchema {
+export type kbSchema = {
_id: string;
userId: string;
+ parentId: string;
updateTime: Date;
avatar: string;
name: string;
vectorModel: string;
tags: string[];
-}
+ type: `${KbTypeEnum}`;
+};
export interface informSchema {
_id: string;
diff --git a/client/src/types/plugin.d.ts b/client/src/types/plugin.d.ts
index 3fcf3b788..d5090efb0 100644
--- a/client/src/types/plugin.d.ts
+++ b/client/src/types/plugin.d.ts
@@ -3,13 +3,10 @@ import type { kbSchema } from './mongoSchema';
export type SelectedKbType = { kbId: string; vectorModel: VectorModelItemType }[];
-export type KbListItemType = {
- _id: string;
- avatar: string;
- name: string;
- tags: string[];
+export type KbListItemType = Omit & {
vectorModel: VectorModelItemType;
};
+
/* kb type */
export interface KbItemType {
_id: string;