balibabu
commited on
Commit
·
8c06509
1
Parent(s):
4b407a8
fix: cannot save the system model setting #468 (#508)
Browse files### What problem does this PR solve?
fix: cannot save the system model setting #468
feat: rename file in FileManager
feat: add FileManager
feat: override useSelector type
### Type of change
- [x] Bug Fix (non-breaking change which fixes an issue)
- web/.umirc.ts +1 -1
- web/externals.d.ts +138 -0
- web/src/hooks/fileManagerHooks.ts +80 -0
- web/src/interfaces/database/file-manager.ts +30 -0
- web/src/interfaces/request/base.ts +7 -0
- web/src/interfaces/request/file-manager.ts +5 -0
- web/src/layouts/components/header/index.tsx +1 -0
- web/src/pages/add-knowledge/components/knowledge-file/document-toolbar.tsx +8 -1
- web/src/pages/file-manager/action-cell/index.less +0 -0
- web/src/pages/file-manager/action-cell/index.tsx +91 -0
- web/src/pages/file-manager/file-toolbar.tsx +153 -0
- web/src/pages/file-manager/hooks.ts +193 -0
- web/src/pages/file-manager/index.less +18 -0
- web/src/pages/file-manager/index.tsx +99 -0
- web/src/pages/file-manager/model.ts +65 -0
- web/src/pages/file/index.tsx +0 -50
- web/src/pages/login/index.tsx +16 -14
- web/src/routes.ts +1 -1
- web/src/services/fileManagerService.ts +36 -0
- web/src/utils/api.ts +7 -0
- web/src/utils/commonUtil.ts +8 -1
- web/typings.d.ts +33 -2
web/.umirc.ts
CHANGED
|
@@ -27,7 +27,7 @@ export default defineConfig({
|
|
| 27 |
devtool: 'source-map',
|
| 28 |
proxy: {
|
| 29 |
'/v1': {
|
| 30 |
-
target: 'http://
|
| 31 |
changeOrigin: true,
|
| 32 |
// pathRewrite: { '^/v1': '/v1' },
|
| 33 |
},
|
|
|
|
| 27 |
devtool: 'source-map',
|
| 28 |
proxy: {
|
| 29 |
'/v1': {
|
| 30 |
+
target: 'http://192.168.200.233:9380/',
|
| 31 |
changeOrigin: true,
|
| 32 |
// pathRewrite: { '^/v1': '/v1' },
|
| 33 |
},
|
web/externals.d.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// This file is generated by Umi automatically
|
| 2 |
+
// DO NOT CHANGE IT MANUALLY!
|
| 3 |
+
type CSSModuleClasses = { readonly [key: string]: string };
|
| 4 |
+
declare module '*.css' {
|
| 5 |
+
const classes: CSSModuleClasses;
|
| 6 |
+
export default classes;
|
| 7 |
+
}
|
| 8 |
+
declare module '*.scss' {
|
| 9 |
+
const classes: CSSModuleClasses;
|
| 10 |
+
export default classes;
|
| 11 |
+
}
|
| 12 |
+
declare module '*.sass' {
|
| 13 |
+
const classes: CSSModuleClasses;
|
| 14 |
+
export default classes;
|
| 15 |
+
}
|
| 16 |
+
declare module '*.less' {
|
| 17 |
+
const classes: CSSModuleClasses;
|
| 18 |
+
export default classes;
|
| 19 |
+
}
|
| 20 |
+
declare module '*.styl' {
|
| 21 |
+
const classes: CSSModuleClasses;
|
| 22 |
+
export default classes;
|
| 23 |
+
}
|
| 24 |
+
declare module '*.stylus' {
|
| 25 |
+
const classes: CSSModuleClasses;
|
| 26 |
+
export default classes;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
// images
|
| 30 |
+
declare module '*.jpg' {
|
| 31 |
+
const src: string;
|
| 32 |
+
export default src;
|
| 33 |
+
}
|
| 34 |
+
declare module '*.jpeg' {
|
| 35 |
+
const src: string;
|
| 36 |
+
export default src;
|
| 37 |
+
}
|
| 38 |
+
declare module '*.png' {
|
| 39 |
+
const src: string;
|
| 40 |
+
export default src;
|
| 41 |
+
}
|
| 42 |
+
declare module '*.gif' {
|
| 43 |
+
const src: string;
|
| 44 |
+
export default src;
|
| 45 |
+
}
|
| 46 |
+
declare module '*.svg' {
|
| 47 |
+
import * as React from 'react';
|
| 48 |
+
export const ReactComponent: React.FunctionComponent<
|
| 49 |
+
React.SVGProps<SVGSVGElement> & { title?: string }
|
| 50 |
+
>;
|
| 51 |
+
|
| 52 |
+
const src: string;
|
| 53 |
+
export default src;
|
| 54 |
+
}
|
| 55 |
+
declare module '*.ico' {
|
| 56 |
+
const src: string;
|
| 57 |
+
export default src;
|
| 58 |
+
}
|
| 59 |
+
declare module '*.webp' {
|
| 60 |
+
const src: string;
|
| 61 |
+
export default src;
|
| 62 |
+
}
|
| 63 |
+
declare module '*.avif' {
|
| 64 |
+
const src: string;
|
| 65 |
+
export default src;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
// media
|
| 69 |
+
declare module '*.mp4' {
|
| 70 |
+
const src: string;
|
| 71 |
+
export default src;
|
| 72 |
+
}
|
| 73 |
+
declare module '*.webm' {
|
| 74 |
+
const src: string;
|
| 75 |
+
export default src;
|
| 76 |
+
}
|
| 77 |
+
declare module '*.ogg' {
|
| 78 |
+
const src: string;
|
| 79 |
+
export default src;
|
| 80 |
+
}
|
| 81 |
+
declare module '*.mp3' {
|
| 82 |
+
const src: string;
|
| 83 |
+
export default src;
|
| 84 |
+
}
|
| 85 |
+
declare module '*.wav' {
|
| 86 |
+
const src: string;
|
| 87 |
+
export default src;
|
| 88 |
+
}
|
| 89 |
+
declare module '*.flac' {
|
| 90 |
+
const src: string;
|
| 91 |
+
export default src;
|
| 92 |
+
}
|
| 93 |
+
declare module '*.aac' {
|
| 94 |
+
const src: string;
|
| 95 |
+
export default src;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
// fonts
|
| 99 |
+
declare module '*.woff' {
|
| 100 |
+
const src: string;
|
| 101 |
+
export default src;
|
| 102 |
+
}
|
| 103 |
+
declare module '*.woff2' {
|
| 104 |
+
const src: string;
|
| 105 |
+
export default src;
|
| 106 |
+
}
|
| 107 |
+
declare module '*.eot' {
|
| 108 |
+
const src: string;
|
| 109 |
+
export default src;
|
| 110 |
+
}
|
| 111 |
+
declare module '*.ttf' {
|
| 112 |
+
const src: string;
|
| 113 |
+
export default src;
|
| 114 |
+
}
|
| 115 |
+
declare module '*.otf' {
|
| 116 |
+
const src: string;
|
| 117 |
+
export default src;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
// other
|
| 121 |
+
declare module '*.wasm' {
|
| 122 |
+
const initWasm: (
|
| 123 |
+
options: WebAssembly.Imports,
|
| 124 |
+
) => Promise<WebAssembly.Exports>;
|
| 125 |
+
export default initWasm;
|
| 126 |
+
}
|
| 127 |
+
declare module '*.webmanifest' {
|
| 128 |
+
const src: string;
|
| 129 |
+
export default src;
|
| 130 |
+
}
|
| 131 |
+
declare module '*.pdf' {
|
| 132 |
+
const src: string;
|
| 133 |
+
export default src;
|
| 134 |
+
}
|
| 135 |
+
declare module '*.txt' {
|
| 136 |
+
const src: string;
|
| 137 |
+
export default src;
|
| 138 |
+
}
|
web/src/hooks/fileManagerHooks.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { IFileListRequestBody } from '@/interfaces/request/file-manager';
|
| 2 |
+
import { useCallback } from 'react';
|
| 3 |
+
import { useDispatch, useSelector } from 'umi';
|
| 4 |
+
|
| 5 |
+
export const useFetchFileList = () => {
|
| 6 |
+
const dispatch = useDispatch();
|
| 7 |
+
|
| 8 |
+
const fetchFileList = useCallback(
|
| 9 |
+
(payload: IFileListRequestBody) => {
|
| 10 |
+
return dispatch<any>({
|
| 11 |
+
type: 'fileManager/listFile',
|
| 12 |
+
payload,
|
| 13 |
+
});
|
| 14 |
+
},
|
| 15 |
+
[dispatch],
|
| 16 |
+
);
|
| 17 |
+
|
| 18 |
+
return fetchFileList;
|
| 19 |
+
};
|
| 20 |
+
|
| 21 |
+
export const useRemoveFile = () => {
|
| 22 |
+
const dispatch = useDispatch();
|
| 23 |
+
|
| 24 |
+
const removeFile = useCallback(
|
| 25 |
+
(fileIds: string[]) => {
|
| 26 |
+
return dispatch<any>({
|
| 27 |
+
type: 'fileManager/removeFile',
|
| 28 |
+
payload: { fileIds },
|
| 29 |
+
});
|
| 30 |
+
},
|
| 31 |
+
[dispatch],
|
| 32 |
+
);
|
| 33 |
+
|
| 34 |
+
return removeFile;
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
export const useRenameFile = () => {
|
| 38 |
+
const dispatch = useDispatch();
|
| 39 |
+
|
| 40 |
+
const renameFile = useCallback(
|
| 41 |
+
(fileId: string, name: string) => {
|
| 42 |
+
return dispatch<any>({
|
| 43 |
+
type: 'fileManager/renameFile',
|
| 44 |
+
payload: { fileId, name },
|
| 45 |
+
});
|
| 46 |
+
},
|
| 47 |
+
[dispatch],
|
| 48 |
+
);
|
| 49 |
+
|
| 50 |
+
return renameFile;
|
| 51 |
+
};
|
| 52 |
+
|
| 53 |
+
export const useFetchParentFolderList = () => {
|
| 54 |
+
const dispatch = useDispatch();
|
| 55 |
+
|
| 56 |
+
const fetchParentFolderList = useCallback(
|
| 57 |
+
(fileId: string) => {
|
| 58 |
+
return dispatch<any>({
|
| 59 |
+
type: 'fileManager/getAllParentFolder',
|
| 60 |
+
payload: { fileId },
|
| 61 |
+
});
|
| 62 |
+
},
|
| 63 |
+
[dispatch],
|
| 64 |
+
);
|
| 65 |
+
|
| 66 |
+
return fetchParentFolderList;
|
| 67 |
+
};
|
| 68 |
+
|
| 69 |
+
export const useSelectFileList = () => {
|
| 70 |
+
const fileList = useSelector((state) => state.fileManager.fileList);
|
| 71 |
+
|
| 72 |
+
return fileList;
|
| 73 |
+
};
|
| 74 |
+
|
| 75 |
+
export const useSelectParentFolderList = () => {
|
| 76 |
+
const parentFolderList = useSelector(
|
| 77 |
+
(state) => state.fileManager.parentFolderList,
|
| 78 |
+
);
|
| 79 |
+
return parentFolderList.toReversed();
|
| 80 |
+
};
|
web/src/interfaces/database/file-manager.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export interface IFile {
|
| 2 |
+
create_date: string;
|
| 3 |
+
create_time: number;
|
| 4 |
+
created_by: string;
|
| 5 |
+
id: string;
|
| 6 |
+
kb_ids: string[];
|
| 7 |
+
location: string;
|
| 8 |
+
name: string;
|
| 9 |
+
parent_id: string;
|
| 10 |
+
size: number;
|
| 11 |
+
tenant_id: string;
|
| 12 |
+
type: string;
|
| 13 |
+
update_date: string;
|
| 14 |
+
update_time: number;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
export interface IFolder {
|
| 18 |
+
create_date: string;
|
| 19 |
+
create_time: number;
|
| 20 |
+
created_by: string;
|
| 21 |
+
id: string;
|
| 22 |
+
location: string;
|
| 23 |
+
name: string;
|
| 24 |
+
parent_id: string;
|
| 25 |
+
size: number;
|
| 26 |
+
tenant_id: string;
|
| 27 |
+
type: string;
|
| 28 |
+
update_date: string;
|
| 29 |
+
update_time: number;
|
| 30 |
+
}
|
web/src/interfaces/request/base.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export interface IPaginationRequestBody {
|
| 2 |
+
keywords?: string;
|
| 3 |
+
page?: number;
|
| 4 |
+
page_size?: number; // name|create|doc_num|create_time|update_time,default:create_time
|
| 5 |
+
orderby?: string;
|
| 6 |
+
desc?: string;
|
| 7 |
+
}
|
web/src/interfaces/request/file-manager.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { IPaginationRequestBody } from './base';
|
| 2 |
+
|
| 3 |
+
export interface IFileListRequestBody extends IPaginationRequestBody {
|
| 4 |
+
parent_id?: string; // folder id
|
| 5 |
+
}
|
web/src/layouts/components/header/index.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
import { ReactComponent as StarIon } from '@/assets/svg/chat-star.svg';
|
|
|
|
| 2 |
import { ReactComponent as KnowledgeBaseIcon } from '@/assets/svg/knowledge-base.svg';
|
| 3 |
import { ReactComponent as Logo } from '@/assets/svg/logo.svg';
|
| 4 |
import { useTranslate } from '@/hooks/commonHooks';
|
|
|
|
| 1 |
import { ReactComponent as StarIon } from '@/assets/svg/chat-star.svg';
|
| 2 |
+
// import { ReactComponent as FileIcon } from '@/assets/svg/file-management.svg';
|
| 3 |
import { ReactComponent as KnowledgeBaseIcon } from '@/assets/svg/knowledge-base.svg';
|
| 4 |
import { ReactComponent as Logo } from '@/assets/svg/logo.svg';
|
| 5 |
import { useTranslate } from '@/hooks/commonHooks';
|
web/src/pages/add-knowledge/components/knowledge-file/document-toolbar.tsx
CHANGED
|
@@ -182,7 +182,14 @@ const DocumentToolbar = ({ selectedRowKeys, showCreateModal }: IProps) => {
|
|
| 182 |
),
|
| 183 |
},
|
| 184 |
];
|
| 185 |
-
}, [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
|
| 187 |
return (
|
| 188 |
<div className={styles.filter}>
|
|
|
|
| 182 |
),
|
| 183 |
},
|
| 184 |
];
|
| 185 |
+
}, [
|
| 186 |
+
handleDelete,
|
| 187 |
+
handleRunClick,
|
| 188 |
+
handleCancelClick,
|
| 189 |
+
t,
|
| 190 |
+
handleDisableClick,
|
| 191 |
+
handleEnableClick,
|
| 192 |
+
]);
|
| 193 |
|
| 194 |
return (
|
| 195 |
<div className={styles.filter}>
|
web/src/pages/file-manager/action-cell/index.less
ADDED
|
File without changes
|
web/src/pages/file-manager/action-cell/index.tsx
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useShowDeleteConfirm, useTranslate } from '@/hooks/commonHooks';
|
| 2 |
+
import { api_host } from '@/utils/api';
|
| 3 |
+
import { downloadFile } from '@/utils/fileUtil';
|
| 4 |
+
import {
|
| 5 |
+
DeleteOutlined,
|
| 6 |
+
DownloadOutlined,
|
| 7 |
+
EditOutlined,
|
| 8 |
+
ToolOutlined,
|
| 9 |
+
} from '@ant-design/icons';
|
| 10 |
+
import { Button, Space, Tooltip } from 'antd';
|
| 11 |
+
|
| 12 |
+
import { useRemoveFile } from '@/hooks/fileManagerHooks';
|
| 13 |
+
import { IFile } from '@/interfaces/database/file-manager';
|
| 14 |
+
import styles from './index.less';
|
| 15 |
+
|
| 16 |
+
interface IProps {
|
| 17 |
+
record: IFile;
|
| 18 |
+
setCurrentRecord: (record: any) => void;
|
| 19 |
+
showRenameModal: (record: IFile) => void;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
const ActionCell = ({ record, setCurrentRecord, showRenameModal }: IProps) => {
|
| 23 |
+
const documentId = record.id;
|
| 24 |
+
const beingUsed = false;
|
| 25 |
+
const { t } = useTranslate('knowledgeDetails');
|
| 26 |
+
const removeDocument = useRemoveFile();
|
| 27 |
+
const showDeleteConfirm = useShowDeleteConfirm();
|
| 28 |
+
|
| 29 |
+
const onRmDocument = () => {
|
| 30 |
+
if (!beingUsed) {
|
| 31 |
+
showDeleteConfirm({
|
| 32 |
+
onOk: () => {
|
| 33 |
+
return removeDocument([documentId]);
|
| 34 |
+
},
|
| 35 |
+
});
|
| 36 |
+
}
|
| 37 |
+
};
|
| 38 |
+
|
| 39 |
+
const onDownloadDocument = () => {
|
| 40 |
+
downloadFile({
|
| 41 |
+
url: `${api_host}/document/get/${documentId}`,
|
| 42 |
+
filename: record.name,
|
| 43 |
+
});
|
| 44 |
+
};
|
| 45 |
+
|
| 46 |
+
const setRecord = () => {
|
| 47 |
+
setCurrentRecord(record);
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
+
const onShowRenameModal = () => {
|
| 51 |
+
setRecord();
|
| 52 |
+
showRenameModal(record);
|
| 53 |
+
};
|
| 54 |
+
|
| 55 |
+
return (
|
| 56 |
+
<Space size={0}>
|
| 57 |
+
<Button type="text" className={styles.iconButton}>
|
| 58 |
+
<ToolOutlined size={20} />
|
| 59 |
+
</Button>
|
| 60 |
+
|
| 61 |
+
<Tooltip title={t('rename', { keyPrefix: 'common' })}>
|
| 62 |
+
<Button
|
| 63 |
+
type="text"
|
| 64 |
+
disabled={beingUsed}
|
| 65 |
+
onClick={onShowRenameModal}
|
| 66 |
+
className={styles.iconButton}
|
| 67 |
+
>
|
| 68 |
+
<EditOutlined size={20} />
|
| 69 |
+
</Button>
|
| 70 |
+
</Tooltip>
|
| 71 |
+
<Button
|
| 72 |
+
type="text"
|
| 73 |
+
disabled={beingUsed}
|
| 74 |
+
onClick={onRmDocument}
|
| 75 |
+
className={styles.iconButton}
|
| 76 |
+
>
|
| 77 |
+
<DeleteOutlined size={20} />
|
| 78 |
+
</Button>
|
| 79 |
+
<Button
|
| 80 |
+
type="text"
|
| 81 |
+
disabled={beingUsed}
|
| 82 |
+
onClick={onDownloadDocument}
|
| 83 |
+
className={styles.iconButton}
|
| 84 |
+
>
|
| 85 |
+
<DownloadOutlined size={20} />
|
| 86 |
+
</Button>
|
| 87 |
+
</Space>
|
| 88 |
+
);
|
| 89 |
+
};
|
| 90 |
+
|
| 91 |
+
export default ActionCell;
|
web/src/pages/file-manager/file-toolbar.tsx
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { ReactComponent as DeleteIcon } from '@/assets/svg/delete.svg';
|
| 2 |
+
import { useShowDeleteConfirm, useTranslate } from '@/hooks/commonHooks';
|
| 3 |
+
import {
|
| 4 |
+
DownOutlined,
|
| 5 |
+
FileOutlined,
|
| 6 |
+
FileTextOutlined,
|
| 7 |
+
PlusOutlined,
|
| 8 |
+
SearchOutlined,
|
| 9 |
+
} from '@ant-design/icons';
|
| 10 |
+
import {
|
| 11 |
+
Breadcrumb,
|
| 12 |
+
BreadcrumbProps,
|
| 13 |
+
Button,
|
| 14 |
+
Dropdown,
|
| 15 |
+
Flex,
|
| 16 |
+
Input,
|
| 17 |
+
MenuProps,
|
| 18 |
+
Space,
|
| 19 |
+
} from 'antd';
|
| 20 |
+
import { useCallback, useMemo } from 'react';
|
| 21 |
+
import {
|
| 22 |
+
useFetchDocumentListOnMount,
|
| 23 |
+
useGetPagination,
|
| 24 |
+
useHandleSearchChange,
|
| 25 |
+
useSelectBreadcrumbItems,
|
| 26 |
+
} from './hooks';
|
| 27 |
+
|
| 28 |
+
import { useRemoveFile } from '@/hooks/fileManagerHooks';
|
| 29 |
+
import { Link } from 'umi';
|
| 30 |
+
import styles from './index.less';
|
| 31 |
+
|
| 32 |
+
interface IProps {
|
| 33 |
+
selectedRowKeys: string[];
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
const itemRender: BreadcrumbProps['itemRender'] = (
|
| 37 |
+
currentRoute,
|
| 38 |
+
params,
|
| 39 |
+
items,
|
| 40 |
+
) => {
|
| 41 |
+
const isLast = currentRoute?.path === items[items.length - 1]?.path;
|
| 42 |
+
|
| 43 |
+
return isLast ? (
|
| 44 |
+
<span>{currentRoute.title}</span>
|
| 45 |
+
) : (
|
| 46 |
+
<Link to={`${currentRoute.path}`}>{currentRoute.title}</Link>
|
| 47 |
+
);
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
+
const FileToolbar = ({ selectedRowKeys }: IProps) => {
|
| 51 |
+
const { t } = useTranslate('knowledgeDetails');
|
| 52 |
+
const { fetchDocumentList } = useFetchDocumentListOnMount();
|
| 53 |
+
const { setPagination, searchString } = useGetPagination(fetchDocumentList);
|
| 54 |
+
const { handleInputChange } = useHandleSearchChange(setPagination);
|
| 55 |
+
const removeDocument = useRemoveFile();
|
| 56 |
+
const showDeleteConfirm = useShowDeleteConfirm();
|
| 57 |
+
const breadcrumbItems = useSelectBreadcrumbItems();
|
| 58 |
+
|
| 59 |
+
const actionItems: MenuProps['items'] = useMemo(() => {
|
| 60 |
+
return [
|
| 61 |
+
{
|
| 62 |
+
key: '1',
|
| 63 |
+
label: (
|
| 64 |
+
<div>
|
| 65 |
+
<Button type="link">
|
| 66 |
+
<Space>
|
| 67 |
+
<FileTextOutlined />
|
| 68 |
+
{t('localFiles')}
|
| 69 |
+
</Space>
|
| 70 |
+
</Button>
|
| 71 |
+
</div>
|
| 72 |
+
),
|
| 73 |
+
},
|
| 74 |
+
{ type: 'divider' },
|
| 75 |
+
{
|
| 76 |
+
key: '2',
|
| 77 |
+
label: (
|
| 78 |
+
<div>
|
| 79 |
+
<Button type="link">
|
| 80 |
+
<FileOutlined />
|
| 81 |
+
{t('emptyFiles')}
|
| 82 |
+
</Button>
|
| 83 |
+
</div>
|
| 84 |
+
),
|
| 85 |
+
// disabled: true,
|
| 86 |
+
},
|
| 87 |
+
];
|
| 88 |
+
}, [t]);
|
| 89 |
+
|
| 90 |
+
const handleDelete = useCallback(() => {
|
| 91 |
+
showDeleteConfirm({
|
| 92 |
+
onOk: () => {
|
| 93 |
+
return removeDocument(selectedRowKeys);
|
| 94 |
+
},
|
| 95 |
+
});
|
| 96 |
+
}, [removeDocument, showDeleteConfirm, selectedRowKeys]);
|
| 97 |
+
|
| 98 |
+
const disabled = selectedRowKeys.length === 0;
|
| 99 |
+
|
| 100 |
+
const items: MenuProps['items'] = useMemo(() => {
|
| 101 |
+
return [
|
| 102 |
+
{
|
| 103 |
+
key: '4',
|
| 104 |
+
onClick: handleDelete,
|
| 105 |
+
label: (
|
| 106 |
+
<Flex gap={10}>
|
| 107 |
+
<span className={styles.deleteIconWrapper}>
|
| 108 |
+
<DeleteIcon width={18} />
|
| 109 |
+
</span>
|
| 110 |
+
<b>{t('delete', { keyPrefix: 'common' })}</b>
|
| 111 |
+
</Flex>
|
| 112 |
+
),
|
| 113 |
+
},
|
| 114 |
+
];
|
| 115 |
+
}, [handleDelete, t]);
|
| 116 |
+
|
| 117 |
+
return (
|
| 118 |
+
<div className={styles.filter}>
|
| 119 |
+
<Breadcrumb items={breadcrumbItems} itemRender={itemRender} />
|
| 120 |
+
<Space>
|
| 121 |
+
<Dropdown
|
| 122 |
+
menu={{ items }}
|
| 123 |
+
placement="bottom"
|
| 124 |
+
arrow={false}
|
| 125 |
+
disabled={disabled}
|
| 126 |
+
>
|
| 127 |
+
<Button>
|
| 128 |
+
<Space>
|
| 129 |
+
<b> {t('bulk')}</b>
|
| 130 |
+
<DownOutlined />
|
| 131 |
+
</Space>
|
| 132 |
+
</Button>
|
| 133 |
+
</Dropdown>
|
| 134 |
+
<Input
|
| 135 |
+
placeholder={t('searchFiles')}
|
| 136 |
+
value={searchString}
|
| 137 |
+
style={{ width: 220 }}
|
| 138 |
+
allowClear
|
| 139 |
+
onChange={handleInputChange}
|
| 140 |
+
prefix={<SearchOutlined />}
|
| 141 |
+
/>
|
| 142 |
+
|
| 143 |
+
<Dropdown menu={{ items: actionItems }} trigger={['click']}>
|
| 144 |
+
<Button type="primary" icon={<PlusOutlined />}>
|
| 145 |
+
{t('addFile')}
|
| 146 |
+
</Button>
|
| 147 |
+
</Dropdown>
|
| 148 |
+
</Space>
|
| 149 |
+
</div>
|
| 150 |
+
);
|
| 151 |
+
};
|
| 152 |
+
|
| 153 |
+
export default FileToolbar;
|
web/src/pages/file-manager/hooks.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
|
| 2 |
+
import {
|
| 3 |
+
useFetchFileList,
|
| 4 |
+
useFetchParentFolderList,
|
| 5 |
+
useRenameFile,
|
| 6 |
+
useSelectFileList,
|
| 7 |
+
useSelectParentFolderList,
|
| 8 |
+
} from '@/hooks/fileManagerHooks';
|
| 9 |
+
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
| 10 |
+
import { Pagination } from '@/interfaces/common';
|
| 11 |
+
import { IFile } from '@/interfaces/database/file-manager';
|
| 12 |
+
import { PaginationProps } from 'antd';
|
| 13 |
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
| 14 |
+
import { useDispatch, useNavigate, useSearchParams, useSelector } from 'umi';
|
| 15 |
+
|
| 16 |
+
export const useGetFolderId = () => {
|
| 17 |
+
const [searchParams] = useSearchParams();
|
| 18 |
+
const id = searchParams.get('folderId') as string;
|
| 19 |
+
|
| 20 |
+
return id;
|
| 21 |
+
};
|
| 22 |
+
|
| 23 |
+
export const useFetchDocumentListOnMount = () => {
|
| 24 |
+
const fetchDocumentList = useFetchFileList();
|
| 25 |
+
const fileList = useSelectFileList();
|
| 26 |
+
const id = useGetFolderId();
|
| 27 |
+
|
| 28 |
+
const dispatch = useDispatch();
|
| 29 |
+
|
| 30 |
+
useEffect(() => {
|
| 31 |
+
fetchDocumentList({ parent_id: id });
|
| 32 |
+
}, [dispatch, fetchDocumentList, id]);
|
| 33 |
+
|
| 34 |
+
return { fetchDocumentList, fileList };
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
export const useGetPagination = (
|
| 38 |
+
fetchDocumentList: (payload: IFile) => any,
|
| 39 |
+
) => {
|
| 40 |
+
const dispatch = useDispatch();
|
| 41 |
+
const kFModel = useSelector((state: any) => state.kFModel);
|
| 42 |
+
const { t } = useTranslate('common');
|
| 43 |
+
|
| 44 |
+
const setPagination = useCallback(
|
| 45 |
+
(pageNumber = 1, pageSize?: number) => {
|
| 46 |
+
const pagination: Pagination = {
|
| 47 |
+
current: pageNumber,
|
| 48 |
+
} as Pagination;
|
| 49 |
+
if (pageSize) {
|
| 50 |
+
pagination.pageSize = pageSize;
|
| 51 |
+
}
|
| 52 |
+
dispatch({
|
| 53 |
+
type: 'kFModel/setPagination',
|
| 54 |
+
payload: pagination,
|
| 55 |
+
});
|
| 56 |
+
},
|
| 57 |
+
[dispatch],
|
| 58 |
+
);
|
| 59 |
+
|
| 60 |
+
const onPageChange: PaginationProps['onChange'] = useCallback(
|
| 61 |
+
(pageNumber: number, pageSize: number) => {
|
| 62 |
+
setPagination(pageNumber, pageSize);
|
| 63 |
+
fetchDocumentList();
|
| 64 |
+
},
|
| 65 |
+
[fetchDocumentList, setPagination],
|
| 66 |
+
);
|
| 67 |
+
|
| 68 |
+
const pagination: PaginationProps = useMemo(() => {
|
| 69 |
+
return {
|
| 70 |
+
showQuickJumper: true,
|
| 71 |
+
total: kFModel.total,
|
| 72 |
+
showSizeChanger: true,
|
| 73 |
+
current: kFModel.pagination.current,
|
| 74 |
+
pageSize: kFModel.pagination.pageSize,
|
| 75 |
+
pageSizeOptions: [1, 2, 10, 20, 50, 100],
|
| 76 |
+
onChange: onPageChange,
|
| 77 |
+
showTotal: (total) => `${t('total')} ${total}`,
|
| 78 |
+
};
|
| 79 |
+
}, [kFModel, onPageChange, t]);
|
| 80 |
+
|
| 81 |
+
return {
|
| 82 |
+
pagination,
|
| 83 |
+
setPagination,
|
| 84 |
+
total: kFModel.total,
|
| 85 |
+
searchString: kFModel.searchString,
|
| 86 |
+
};
|
| 87 |
+
};
|
| 88 |
+
|
| 89 |
+
export const useHandleSearchChange = (setPagination: () => void) => {
|
| 90 |
+
const dispatch = useDispatch();
|
| 91 |
+
|
| 92 |
+
const throttledGetDocumentList = useCallback(() => {
|
| 93 |
+
dispatch({
|
| 94 |
+
type: 'kFModel/throttledGetDocumentList',
|
| 95 |
+
});
|
| 96 |
+
}, [dispatch]);
|
| 97 |
+
|
| 98 |
+
const handleInputChange = useCallback(
|
| 99 |
+
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
| 100 |
+
const value = e.target.value;
|
| 101 |
+
dispatch({ type: 'kFModel/setSearchString', payload: value });
|
| 102 |
+
setPagination();
|
| 103 |
+
throttledGetDocumentList();
|
| 104 |
+
},
|
| 105 |
+
[setPagination, throttledGetDocumentList, dispatch],
|
| 106 |
+
);
|
| 107 |
+
|
| 108 |
+
return { handleInputChange };
|
| 109 |
+
};
|
| 110 |
+
|
| 111 |
+
export const useGetRowSelection = () => {
|
| 112 |
+
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
| 113 |
+
|
| 114 |
+
const rowSelection = {
|
| 115 |
+
selectedRowKeys,
|
| 116 |
+
onChange: (newSelectedRowKeys: React.Key[]) => {
|
| 117 |
+
setSelectedRowKeys(newSelectedRowKeys);
|
| 118 |
+
},
|
| 119 |
+
};
|
| 120 |
+
|
| 121 |
+
return rowSelection;
|
| 122 |
+
};
|
| 123 |
+
|
| 124 |
+
export const useNavigateToOtherFolder = () => {
|
| 125 |
+
const navigate = useNavigate();
|
| 126 |
+
const navigateToOtherFolder = useCallback(
|
| 127 |
+
(folderId: string) => {
|
| 128 |
+
navigate(`/file?folderId=${folderId}`);
|
| 129 |
+
},
|
| 130 |
+
[navigate],
|
| 131 |
+
);
|
| 132 |
+
|
| 133 |
+
return navigateToOtherFolder;
|
| 134 |
+
};
|
| 135 |
+
|
| 136 |
+
export const useRenameCurrentFile = () => {
|
| 137 |
+
const [file, setFile] = useState<IFile>({} as IFile);
|
| 138 |
+
const {
|
| 139 |
+
visible: fileRenameVisible,
|
| 140 |
+
hideModal: hideFileRenameModal,
|
| 141 |
+
showModal: showFileRenameModal,
|
| 142 |
+
} = useSetModalState();
|
| 143 |
+
const renameFile = useRenameFile();
|
| 144 |
+
|
| 145 |
+
const onFileRenameOk = useCallback(
|
| 146 |
+
async (name: string) => {
|
| 147 |
+
const ret = await renameFile(file.id, name);
|
| 148 |
+
|
| 149 |
+
if (ret === 0) {
|
| 150 |
+
hideFileRenameModal();
|
| 151 |
+
}
|
| 152 |
+
},
|
| 153 |
+
[renameFile, file, hideFileRenameModal],
|
| 154 |
+
);
|
| 155 |
+
|
| 156 |
+
const loading = useOneNamespaceEffectsLoading('fileManager', ['renameFile']);
|
| 157 |
+
|
| 158 |
+
const handleShowFileRenameModal = useCallback(
|
| 159 |
+
async (record: IFile) => {
|
| 160 |
+
setFile(record);
|
| 161 |
+
showFileRenameModal();
|
| 162 |
+
},
|
| 163 |
+
[showFileRenameModal],
|
| 164 |
+
);
|
| 165 |
+
|
| 166 |
+
return {
|
| 167 |
+
fileRenameLoading: loading,
|
| 168 |
+
initialFileName: file.name,
|
| 169 |
+
onFileRenameOk,
|
| 170 |
+
fileRenameVisible,
|
| 171 |
+
hideFileRenameModal,
|
| 172 |
+
showFileRenameModal: handleShowFileRenameModal,
|
| 173 |
+
};
|
| 174 |
+
};
|
| 175 |
+
|
| 176 |
+
export const useSelectBreadcrumbItems = () => {
|
| 177 |
+
const parentFolderList = useSelectParentFolderList();
|
| 178 |
+
const id = useGetFolderId();
|
| 179 |
+
const fetchParentFolderList = useFetchParentFolderList();
|
| 180 |
+
|
| 181 |
+
useEffect(() => {
|
| 182 |
+
if (id) {
|
| 183 |
+
fetchParentFolderList(id);
|
| 184 |
+
}
|
| 185 |
+
}, [id, fetchParentFolderList]);
|
| 186 |
+
|
| 187 |
+
return parentFolderList.length === 1
|
| 188 |
+
? []
|
| 189 |
+
: parentFolderList.map((x) => ({
|
| 190 |
+
title: x.name === '/' ? 'root' : x.name,
|
| 191 |
+
path: `/file?folderId=${x.id}`,
|
| 192 |
+
}));
|
| 193 |
+
};
|
web/src/pages/file-manager/index.less
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.fileManagerWrapper {
|
| 2 |
+
flex-basis: 100%;
|
| 3 |
+
padding: 32px;
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
.filter {
|
| 7 |
+
height: 32px;
|
| 8 |
+
display: flex;
|
| 9 |
+
margin: 10px 0;
|
| 10 |
+
justify-content: space-between;
|
| 11 |
+
padding: 24px 0;
|
| 12 |
+
align-items: center;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
.deleteIconWrapper {
|
| 16 |
+
width: 22px;
|
| 17 |
+
text-align: center;
|
| 18 |
+
}
|
web/src/pages/file-manager/index.tsx
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useSelectFileList } from '@/hooks/fileManagerHooks';
|
| 2 |
+
import { IFile } from '@/interfaces/database/file-manager';
|
| 3 |
+
import { formatDate } from '@/utils/date';
|
| 4 |
+
import { Button, Table } from 'antd';
|
| 5 |
+
import { ColumnsType } from 'antd/es/table';
|
| 6 |
+
import ActionCell from './action-cell';
|
| 7 |
+
import FileToolbar from './file-toolbar';
|
| 8 |
+
import {
|
| 9 |
+
useGetRowSelection,
|
| 10 |
+
useNavigateToOtherFolder,
|
| 11 |
+
useRenameCurrentFile,
|
| 12 |
+
} from './hooks';
|
| 13 |
+
|
| 14 |
+
import RenameModal from '@/components/rename-modal';
|
| 15 |
+
import styles from './index.less';
|
| 16 |
+
|
| 17 |
+
const FileManager = () => {
|
| 18 |
+
const fileList = useSelectFileList();
|
| 19 |
+
const rowSelection = useGetRowSelection();
|
| 20 |
+
const navigateToOtherFolder = useNavigateToOtherFolder();
|
| 21 |
+
const {
|
| 22 |
+
fileRenameVisible,
|
| 23 |
+
fileRenameLoading,
|
| 24 |
+
hideFileRenameModal,
|
| 25 |
+
showFileRenameModal,
|
| 26 |
+
initialFileName,
|
| 27 |
+
onFileRenameOk,
|
| 28 |
+
} = useRenameCurrentFile();
|
| 29 |
+
|
| 30 |
+
const columns: ColumnsType<IFile> = [
|
| 31 |
+
{
|
| 32 |
+
title: 'Name',
|
| 33 |
+
dataIndex: 'name',
|
| 34 |
+
key: 'name',
|
| 35 |
+
render(value, record) {
|
| 36 |
+
return record.type === 'folder' ? (
|
| 37 |
+
<Button
|
| 38 |
+
type={'link'}
|
| 39 |
+
onClick={() => navigateToOtherFolder(record.id)}
|
| 40 |
+
>
|
| 41 |
+
{value}
|
| 42 |
+
</Button>
|
| 43 |
+
) : (
|
| 44 |
+
value
|
| 45 |
+
);
|
| 46 |
+
},
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
title: 'Upload Date',
|
| 50 |
+
dataIndex: 'create_date',
|
| 51 |
+
key: 'create_date',
|
| 52 |
+
render(text) {
|
| 53 |
+
return formatDate(text);
|
| 54 |
+
},
|
| 55 |
+
},
|
| 56 |
+
{
|
| 57 |
+
title: 'Location',
|
| 58 |
+
dataIndex: 'location',
|
| 59 |
+
key: 'location',
|
| 60 |
+
},
|
| 61 |
+
{
|
| 62 |
+
title: 'Action',
|
| 63 |
+
dataIndex: 'action',
|
| 64 |
+
key: 'action',
|
| 65 |
+
render: (text, record) => (
|
| 66 |
+
<ActionCell
|
| 67 |
+
record={record}
|
| 68 |
+
setCurrentRecord={(record: any) => {
|
| 69 |
+
console.info(record);
|
| 70 |
+
}}
|
| 71 |
+
showRenameModal={showFileRenameModal}
|
| 72 |
+
></ActionCell>
|
| 73 |
+
),
|
| 74 |
+
},
|
| 75 |
+
];
|
| 76 |
+
|
| 77 |
+
return (
|
| 78 |
+
<section className={styles.fileManagerWrapper}>
|
| 79 |
+
<FileToolbar
|
| 80 |
+
selectedRowKeys={rowSelection.selectedRowKeys as string[]}
|
| 81 |
+
></FileToolbar>
|
| 82 |
+
<Table
|
| 83 |
+
dataSource={fileList}
|
| 84 |
+
columns={columns}
|
| 85 |
+
rowKey={'id'}
|
| 86 |
+
rowSelection={rowSelection}
|
| 87 |
+
/>
|
| 88 |
+
<RenameModal
|
| 89 |
+
visible={fileRenameVisible}
|
| 90 |
+
hideModal={hideFileRenameModal}
|
| 91 |
+
onOk={onFileRenameOk}
|
| 92 |
+
initialName={initialFileName}
|
| 93 |
+
loading={fileRenameLoading}
|
| 94 |
+
></RenameModal>
|
| 95 |
+
</section>
|
| 96 |
+
);
|
| 97 |
+
};
|
| 98 |
+
|
| 99 |
+
export default FileManager;
|
web/src/pages/file-manager/model.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { IFile, IFolder } from '@/interfaces/database/file-manager';
|
| 2 |
+
import fileManagerService from '@/services/fileManagerService';
|
| 3 |
+
import { DvaModel } from 'umi';
|
| 4 |
+
|
| 5 |
+
export interface FileManagerModelState {
|
| 6 |
+
fileList: IFile[];
|
| 7 |
+
parentFolderList: IFolder[];
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
const model: DvaModel<FileManagerModelState> = {
|
| 11 |
+
namespace: 'fileManager',
|
| 12 |
+
state: { fileList: [], parentFolderList: [] },
|
| 13 |
+
reducers: {
|
| 14 |
+
setFileList(state, { payload }) {
|
| 15 |
+
return { ...state, fileList: payload };
|
| 16 |
+
},
|
| 17 |
+
setParentFolderList(state, { payload }) {
|
| 18 |
+
return { ...state, parentFolderList: payload };
|
| 19 |
+
},
|
| 20 |
+
},
|
| 21 |
+
effects: {
|
| 22 |
+
*removeFile({ payload = {} }, { call, put }) {
|
| 23 |
+
const { data } = yield call(fileManagerService.removeFile, payload);
|
| 24 |
+
const { retcode } = data;
|
| 25 |
+
if (retcode === 0) {
|
| 26 |
+
yield put({
|
| 27 |
+
type: 'listFile',
|
| 28 |
+
payload: data.data?.files ?? [],
|
| 29 |
+
});
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
*listFile({ payload = {} }, { call, put }) {
|
| 33 |
+
const { data } = yield call(fileManagerService.listFile, payload);
|
| 34 |
+
const { retcode, data: res } = data;
|
| 35 |
+
|
| 36 |
+
if (retcode === 0 && Array.isArray(res.files)) {
|
| 37 |
+
yield put({
|
| 38 |
+
type: 'setFileList',
|
| 39 |
+
payload: res.files,
|
| 40 |
+
});
|
| 41 |
+
}
|
| 42 |
+
},
|
| 43 |
+
*renameFile({ payload = {} }, { call, put }) {
|
| 44 |
+
const { data } = yield call(fileManagerService.renameFile, payload);
|
| 45 |
+
if (data.retcode === 0) {
|
| 46 |
+
yield put({ type: 'listFile' });
|
| 47 |
+
}
|
| 48 |
+
return data.retcode;
|
| 49 |
+
},
|
| 50 |
+
*getAllParentFolder({ payload = {} }, { call, put }) {
|
| 51 |
+
const { data } = yield call(
|
| 52 |
+
fileManagerService.getAllParentFolder,
|
| 53 |
+
payload,
|
| 54 |
+
);
|
| 55 |
+
if (data.retcode === 0) {
|
| 56 |
+
yield put({
|
| 57 |
+
type: 'setParentFolderList',
|
| 58 |
+
payload: data.data?.parent_folders ?? [],
|
| 59 |
+
});
|
| 60 |
+
}
|
| 61 |
+
return data.retcode;
|
| 62 |
+
},
|
| 63 |
+
},
|
| 64 |
+
};
|
| 65 |
+
export default model;
|
web/src/pages/file/index.tsx
DELETED
|
@@ -1,50 +0,0 @@
|
|
| 1 |
-
import { UploadOutlined } from '@ant-design/icons';
|
| 2 |
-
import { Button, Upload } from 'antd';
|
| 3 |
-
import React, { useEffect, useState } from 'react';
|
| 4 |
-
|
| 5 |
-
const File: React.FC = () => {
|
| 6 |
-
const [fileList, setFileList] = useState([
|
| 7 |
-
{
|
| 8 |
-
uid: '0',
|
| 9 |
-
name: 'xxx.png',
|
| 10 |
-
status: 'uploading',
|
| 11 |
-
percent: 10,
|
| 12 |
-
},
|
| 13 |
-
]);
|
| 14 |
-
const obj = {
|
| 15 |
-
uid: '-1',
|
| 16 |
-
name: 'yyy.png',
|
| 17 |
-
status: 'done',
|
| 18 |
-
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
| 19 |
-
thumbUrl:
|
| 20 |
-
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
| 21 |
-
};
|
| 22 |
-
useEffect(() => {
|
| 23 |
-
const timer = setInterval(() => {
|
| 24 |
-
setFileList((fileList: any) => {
|
| 25 |
-
const percent = fileList[0]?.percent;
|
| 26 |
-
if (percent + 10 >= 100) {
|
| 27 |
-
clearInterval(timer);
|
| 28 |
-
return [obj];
|
| 29 |
-
}
|
| 30 |
-
const list = [{ ...fileList[0], percent: percent + 10 }];
|
| 31 |
-
console.log(list);
|
| 32 |
-
return list;
|
| 33 |
-
});
|
| 34 |
-
}, 300);
|
| 35 |
-
}, []);
|
| 36 |
-
return (
|
| 37 |
-
<>
|
| 38 |
-
<Upload
|
| 39 |
-
action="https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188"
|
| 40 |
-
listType="picture"
|
| 41 |
-
fileList={[...fileList]}
|
| 42 |
-
multiple
|
| 43 |
-
>
|
| 44 |
-
<Button icon={<UploadOutlined />}>Upload</Button>
|
| 45 |
-
</Upload>
|
| 46 |
-
</>
|
| 47 |
-
);
|
| 48 |
-
};
|
| 49 |
-
|
| 50 |
-
export default File;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
web/src/pages/login/index.tsx
CHANGED
|
@@ -167,20 +167,22 @@ const Login = () => {
|
|
| 167 |
Sign in with Google
|
| 168 |
</div>
|
| 169 |
</Button> */}
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
<
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
|
|
|
|
|
|
| 184 |
</>
|
| 185 |
)}
|
| 186 |
</Form>
|
|
|
|
| 167 |
Sign in with Google
|
| 168 |
</div>
|
| 169 |
</Button> */}
|
| 170 |
+
{location.host === 'demo.ragflow.io' && (
|
| 171 |
+
<Button
|
| 172 |
+
block
|
| 173 |
+
size="large"
|
| 174 |
+
onClick={toGoogle}
|
| 175 |
+
style={{ marginTop: 15 }}
|
| 176 |
+
>
|
| 177 |
+
<div>
|
| 178 |
+
<Icon
|
| 179 |
+
icon="local:github"
|
| 180 |
+
style={{ verticalAlign: 'middle', marginRight: 5 }}
|
| 181 |
+
/>
|
| 182 |
+
Sign in with Github
|
| 183 |
+
</div>
|
| 184 |
+
</Button>
|
| 185 |
+
)}
|
| 186 |
</>
|
| 187 |
)}
|
| 188 |
</Form>
|
web/src/routes.ts
CHANGED
|
@@ -82,7 +82,7 @@ const routes = [
|
|
| 82 |
},
|
| 83 |
{
|
| 84 |
path: '/file',
|
| 85 |
-
component: '@/pages/file',
|
| 86 |
},
|
| 87 |
],
|
| 88 |
},
|
|
|
|
| 82 |
},
|
| 83 |
{
|
| 84 |
path: '/file',
|
| 85 |
+
component: '@/pages/file-manager',
|
| 86 |
},
|
| 87 |
],
|
| 88 |
},
|
web/src/services/fileManagerService.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import api from '@/utils/api';
|
| 2 |
+
import registerServer from '@/utils/registerServer';
|
| 3 |
+
import request from '@/utils/request';
|
| 4 |
+
|
| 5 |
+
const { listFile, removeFile, uploadFile, renameFile, getAllParentFolder } =
|
| 6 |
+
api;
|
| 7 |
+
|
| 8 |
+
const methods = {
|
| 9 |
+
listFile: {
|
| 10 |
+
url: listFile,
|
| 11 |
+
method: 'get',
|
| 12 |
+
},
|
| 13 |
+
removeFile: {
|
| 14 |
+
url: removeFile,
|
| 15 |
+
method: 'post',
|
| 16 |
+
},
|
| 17 |
+
uploadFile: {
|
| 18 |
+
url: uploadFile,
|
| 19 |
+
method: 'post',
|
| 20 |
+
},
|
| 21 |
+
renameFile: {
|
| 22 |
+
url: renameFile,
|
| 23 |
+
method: 'post',
|
| 24 |
+
},
|
| 25 |
+
getAllParentFolder: {
|
| 26 |
+
url: getAllParentFolder,
|
| 27 |
+
method: 'get',
|
| 28 |
+
},
|
| 29 |
+
} as const;
|
| 30 |
+
|
| 31 |
+
const fileManagerService = registerServer<keyof typeof methods>(
|
| 32 |
+
methods,
|
| 33 |
+
request,
|
| 34 |
+
);
|
| 35 |
+
|
| 36 |
+
export default fileManagerService;
|
web/src/utils/api.ts
CHANGED
|
@@ -66,4 +66,11 @@ export default {
|
|
| 66 |
createExternalConversation: `${api_host}/api/new_conversation`,
|
| 67 |
getExternalConversation: `${api_host}/api/conversation`,
|
| 68 |
completeExternalConversation: `${api_host}/api/completion`,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
};
|
|
|
|
| 66 |
createExternalConversation: `${api_host}/api/new_conversation`,
|
| 67 |
getExternalConversation: `${api_host}/api/conversation`,
|
| 68 |
completeExternalConversation: `${api_host}/api/completion`,
|
| 69 |
+
|
| 70 |
+
// file manager
|
| 71 |
+
listFile: `${api_host}/file/list`,
|
| 72 |
+
uploadFile: `${api_host}/file/upload`,
|
| 73 |
+
removeFile: `${api_host}/file/rm`,
|
| 74 |
+
renameFile: `${api_host}/file/rename`,
|
| 75 |
+
getAllParentFolder: `${api_host}/file/all_parent_folder`,
|
| 76 |
};
|
web/src/utils/commonUtil.ts
CHANGED
|
@@ -5,11 +5,18 @@ export const isFormData = (data: unknown): data is FormData => {
|
|
| 5 |
return data instanceof FormData;
|
| 6 |
};
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
export const convertTheKeysOfTheObjectToSnake = (data: unknown) => {
|
| 9 |
if (isObject(data) && !isFormData(data)) {
|
| 10 |
return Object.keys(data).reduce<Record<string, any>>((pre, cur) => {
|
| 11 |
const value = (data as Record<string, any>)[cur];
|
| 12 |
-
pre[isFormData(value) ? cur : snakeCase(cur)] =
|
|
|
|
| 13 |
return pre;
|
| 14 |
}, {});
|
| 15 |
}
|
|
|
|
| 5 |
return data instanceof FormData;
|
| 6 |
};
|
| 7 |
|
| 8 |
+
const excludedFields = ['img2txt_id'];
|
| 9 |
+
|
| 10 |
+
const isExcludedField = (key: string) => {
|
| 11 |
+
return excludedFields.includes(key);
|
| 12 |
+
};
|
| 13 |
+
|
| 14 |
export const convertTheKeysOfTheObjectToSnake = (data: unknown) => {
|
| 15 |
if (isObject(data) && !isFormData(data)) {
|
| 16 |
return Object.keys(data).reduce<Record<string, any>>((pre, cur) => {
|
| 17 |
const value = (data as Record<string, any>)[cur];
|
| 18 |
+
pre[isFormData(value) || isExcludedField(cur) ? cur : snakeCase(cur)] =
|
| 19 |
+
value;
|
| 20 |
return pre;
|
| 21 |
}, {});
|
| 22 |
}
|
web/typings.d.ts
CHANGED
|
@@ -1,8 +1,39 @@
|
|
| 1 |
-
import '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
declare module 'lodash';
|
| 3 |
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
declare global {
|
| 7 |
type Nullable<T> = T | null;
|
| 8 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { ChunkModelState } from '@/pages/add-knowledge/components/knowledge-chunk/model';
|
| 2 |
+
import { KFModelState } from '@/pages/add-knowledge/components/knowledge-file/model';
|
| 3 |
+
import { KSModelState } from '@/pages/add-knowledge/components/knowledge-setting/model';
|
| 4 |
+
import { TestingModelState } from '@/pages/add-knowledge/components/knowledge-testing/model';
|
| 5 |
+
import { kAModelState } from '@/pages/add-knowledge/model';
|
| 6 |
+
import { ChatModelState } from '@/pages/chat/model';
|
| 7 |
+
import { FileManagerModelState } from '@/pages/file-manager/model';
|
| 8 |
+
import { KnowledgeModelState } from '@/pages/knowledge/model';
|
| 9 |
+
import { LoginModelState } from '@/pages/login/model';
|
| 10 |
+
import { SettingModelState } from '@/pages/user-setting/model';
|
| 11 |
+
|
| 12 |
declare module 'lodash';
|
| 13 |
|
| 14 |
+
function useSelector<TState = RootState, TSelected = unknown>(
|
| 15 |
+
selector: (state: TState) => TSelected,
|
| 16 |
+
equalityFn?: (left: TSelected, right: TSelected) => boolean,
|
| 17 |
+
): TSelected;
|
| 18 |
+
|
| 19 |
+
export interface RootState {
|
| 20 |
+
// loading: Loading;
|
| 21 |
+
fileManager: FileManagerModelState;
|
| 22 |
+
chatModel: ChatModelState;
|
| 23 |
+
loginModel: LoginModelState;
|
| 24 |
+
knowledgeModel: KnowledgeModelState;
|
| 25 |
+
settingModel: SettingModelState;
|
| 26 |
+
kFModel: KFModelState;
|
| 27 |
+
kAModel: kAModelState;
|
| 28 |
+
chunkModel: ChunkModelState;
|
| 29 |
+
kSModel: KSModelState;
|
| 30 |
+
testingModel: TestingModelState;
|
| 31 |
+
}
|
| 32 |
|
| 33 |
declare global {
|
| 34 |
type Nullable<T> = T | null;
|
| 35 |
}
|
| 36 |
+
|
| 37 |
+
declare module 'umi' {
|
| 38 |
+
export { useSelector };
|
| 39 |
+
}
|