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 |
+
}
|