balibabu
commited on
Commit
·
4056097
1
Parent(s):
977d825
feat: create folder #345 (#518)
Browse files### What problem does this PR solve?
feat: create folder
feat: ensure that all files in the current folder can be correctly
requested after renaming the folder
#345
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
- web/src/hooks/fileManagerHooks.ts +20 -4
- web/src/pages/file-manager/action-cell/index.tsx +5 -16
- web/src/pages/file-manager/file-toolbar.tsx +13 -19
- web/src/pages/file-manager/file-upload-modal/index.tsx +64 -0
- web/src/pages/file-manager/folder-create-modal/index.tsx +67 -0
- web/src/pages/file-manager/hooks.ts +61 -2
- web/src/pages/file-manager/index.tsx +19 -0
- web/src/pages/file-manager/model.ts +23 -4
- web/src/services/fileManagerService.ts +12 -2
- web/src/utils/api.ts +1 -0
web/src/hooks/fileManagerHooks.ts
CHANGED
|
@@ -22,10 +22,10 @@ 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],
|
|
@@ -38,10 +38,10 @@ 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],
|
|
@@ -66,6 +66,22 @@ export const useFetchParentFolderList = () => {
|
|
| 66 |
return fetchParentFolderList;
|
| 67 |
};
|
| 68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
export const useSelectFileList = () => {
|
| 70 |
const fileList = useSelector((state) => state.fileManager.fileList);
|
| 71 |
|
|
|
|
| 22 |
const dispatch = useDispatch();
|
| 23 |
|
| 24 |
const removeFile = useCallback(
|
| 25 |
+
(fileIds: string[], parentId: string) => {
|
| 26 |
return dispatch<any>({
|
| 27 |
type: 'fileManager/removeFile',
|
| 28 |
+
payload: { fileIds, parentId },
|
| 29 |
});
|
| 30 |
},
|
| 31 |
[dispatch],
|
|
|
|
| 38 |
const dispatch = useDispatch();
|
| 39 |
|
| 40 |
const renameFile = useCallback(
|
| 41 |
+
(fileId: string, name: string, parentId: string) => {
|
| 42 |
return dispatch<any>({
|
| 43 |
type: 'fileManager/renameFile',
|
| 44 |
+
payload: { fileId, name, parentId },
|
| 45 |
});
|
| 46 |
},
|
| 47 |
[dispatch],
|
|
|
|
| 66 |
return fetchParentFolderList;
|
| 67 |
};
|
| 68 |
|
| 69 |
+
export const useCreateFolder = () => {
|
| 70 |
+
const dispatch = useDispatch();
|
| 71 |
+
|
| 72 |
+
const createFolder = useCallback(
|
| 73 |
+
(parentId: string, name: string) => {
|
| 74 |
+
return dispatch<any>({
|
| 75 |
+
type: 'fileManager/createFolder',
|
| 76 |
+
payload: { parentId, name, type: 'folder' },
|
| 77 |
+
});
|
| 78 |
+
},
|
| 79 |
+
[dispatch],
|
| 80 |
+
);
|
| 81 |
+
|
| 82 |
+
return createFolder;
|
| 83 |
+
};
|
| 84 |
+
|
| 85 |
export const useSelectFileList = () => {
|
| 86 |
const fileList = useSelector((state) => state.fileManager.fileList);
|
| 87 |
|
web/src/pages/file-manager/action-cell/index.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
-
import {
|
|
|
|
| 2 |
import { api_host } from '@/utils/api';
|
| 3 |
import { downloadFile } from '@/utils/fileUtil';
|
| 4 |
import {
|
|
@@ -8,9 +9,8 @@ import {
|
|
| 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 {
|
|
@@ -23,18 +23,7 @@ const ActionCell = ({ record, setCurrentRecord, showRenameModal }: IProps) => {
|
|
| 23 |
const documentId = record.id;
|
| 24 |
const beingUsed = false;
|
| 25 |
const { t } = useTranslate('knowledgeDetails');
|
| 26 |
-
const
|
| 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({
|
|
@@ -71,7 +60,7 @@ const ActionCell = ({ record, setCurrentRecord, showRenameModal }: IProps) => {
|
|
| 71 |
<Button
|
| 72 |
type="text"
|
| 73 |
disabled={beingUsed}
|
| 74 |
-
onClick={
|
| 75 |
className={styles.iconButton}
|
| 76 |
>
|
| 77 |
<DeleteOutlined size={20} />
|
|
|
|
| 1 |
+
import { useTranslate } from '@/hooks/commonHooks';
|
| 2 |
+
import { IFile } from '@/interfaces/database/file-manager';
|
| 3 |
import { api_host } from '@/utils/api';
|
| 4 |
import { downloadFile } from '@/utils/fileUtil';
|
| 5 |
import {
|
|
|
|
| 9 |
ToolOutlined,
|
| 10 |
} from '@ant-design/icons';
|
| 11 |
import { Button, Space, Tooltip } from 'antd';
|
| 12 |
+
import { useHandleDeleteFile } from '../hooks';
|
| 13 |
|
|
|
|
|
|
|
| 14 |
import styles from './index.less';
|
| 15 |
|
| 16 |
interface IProps {
|
|
|
|
| 23 |
const documentId = record.id;
|
| 24 |
const beingUsed = false;
|
| 25 |
const { t } = useTranslate('knowledgeDetails');
|
| 26 |
+
const { handleRemoveFile } = useHandleDeleteFile([documentId]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
const onDownloadDocument = () => {
|
| 29 |
downloadFile({
|
|
|
|
| 60 |
<Button
|
| 61 |
type="text"
|
| 62 |
disabled={beingUsed}
|
| 63 |
+
onClick={handleRemoveFile}
|
| 64 |
className={styles.iconButton}
|
| 65 |
>
|
| 66 |
<DeleteOutlined size={20} />
|
web/src/pages/file-manager/file-toolbar.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
| 1 |
import { ReactComponent as DeleteIcon } from '@/assets/svg/delete.svg';
|
| 2 |
-
import {
|
| 3 |
import {
|
| 4 |
DownOutlined,
|
| 5 |
-
FileOutlined,
|
| 6 |
FileTextOutlined,
|
|
|
|
| 7 |
PlusOutlined,
|
| 8 |
SearchOutlined,
|
| 9 |
} from '@ant-design/icons';
|
|
@@ -17,20 +17,21 @@ import {
|
|
| 17 |
MenuProps,
|
| 18 |
Space,
|
| 19 |
} from 'antd';
|
| 20 |
-
import {
|
| 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'] = (
|
|
@@ -47,13 +48,11 @@ const itemRender: BreadcrumbProps['itemRender'] = (
|
|
| 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(() => {
|
|
@@ -74,26 +73,21 @@ const FileToolbar = ({ selectedRowKeys }: IProps) => {
|
|
| 74 |
{ type: 'divider' },
|
| 75 |
{
|
| 76 |
key: '2',
|
|
|
|
| 77 |
label: (
|
| 78 |
<div>
|
| 79 |
<Button type="link">
|
| 80 |
-
<
|
| 81 |
-
|
| 82 |
</Button>
|
| 83 |
</div>
|
| 84 |
),
|
| 85 |
// disabled: true,
|
| 86 |
},
|
| 87 |
];
|
| 88 |
-
}, [t]);
|
| 89 |
|
| 90 |
-
const
|
| 91 |
-
showDeleteConfirm({
|
| 92 |
-
onOk: () => {
|
| 93 |
-
return removeDocument(selectedRowKeys);
|
| 94 |
-
},
|
| 95 |
-
});
|
| 96 |
-
}, [removeDocument, showDeleteConfirm, selectedRowKeys]);
|
| 97 |
|
| 98 |
const disabled = selectedRowKeys.length === 0;
|
| 99 |
|
|
@@ -101,7 +95,7 @@ const FileToolbar = ({ selectedRowKeys }: IProps) => {
|
|
| 101 |
return [
|
| 102 |
{
|
| 103 |
key: '4',
|
| 104 |
-
onClick:
|
| 105 |
label: (
|
| 106 |
<Flex gap={10}>
|
| 107 |
<span className={styles.deleteIconWrapper}>
|
|
@@ -112,7 +106,7 @@ const FileToolbar = ({ selectedRowKeys }: IProps) => {
|
|
| 112 |
),
|
| 113 |
},
|
| 114 |
];
|
| 115 |
-
}, [
|
| 116 |
|
| 117 |
return (
|
| 118 |
<div className={styles.filter}>
|
|
|
|
| 1 |
import { ReactComponent as DeleteIcon } from '@/assets/svg/delete.svg';
|
| 2 |
+
import { useTranslate } from '@/hooks/commonHooks';
|
| 3 |
import {
|
| 4 |
DownOutlined,
|
|
|
|
| 5 |
FileTextOutlined,
|
| 6 |
+
FolderOpenOutlined,
|
| 7 |
PlusOutlined,
|
| 8 |
SearchOutlined,
|
| 9 |
} from '@ant-design/icons';
|
|
|
|
| 17 |
MenuProps,
|
| 18 |
Space,
|
| 19 |
} from 'antd';
|
| 20 |
+
import { useMemo } from 'react';
|
| 21 |
import {
|
| 22 |
useFetchDocumentListOnMount,
|
| 23 |
useGetPagination,
|
| 24 |
+
useHandleDeleteFile,
|
| 25 |
useHandleSearchChange,
|
| 26 |
useSelectBreadcrumbItems,
|
| 27 |
} from './hooks';
|
| 28 |
|
|
|
|
| 29 |
import { Link } from 'umi';
|
| 30 |
import styles from './index.less';
|
| 31 |
|
| 32 |
interface IProps {
|
| 33 |
selectedRowKeys: string[];
|
| 34 |
+
showFolderCreateModal: () => void;
|
| 35 |
}
|
| 36 |
|
| 37 |
const itemRender: BreadcrumbProps['itemRender'] = (
|
|
|
|
| 48 |
);
|
| 49 |
};
|
| 50 |
|
| 51 |
+
const FileToolbar = ({ selectedRowKeys, showFolderCreateModal }: IProps) => {
|
| 52 |
const { t } = useTranslate('knowledgeDetails');
|
| 53 |
const { fetchDocumentList } = useFetchDocumentListOnMount();
|
| 54 |
const { setPagination, searchString } = useGetPagination(fetchDocumentList);
|
| 55 |
const { handleInputChange } = useHandleSearchChange(setPagination);
|
|
|
|
|
|
|
| 56 |
const breadcrumbItems = useSelectBreadcrumbItems();
|
| 57 |
|
| 58 |
const actionItems: MenuProps['items'] = useMemo(() => {
|
|
|
|
| 73 |
{ type: 'divider' },
|
| 74 |
{
|
| 75 |
key: '2',
|
| 76 |
+
onClick: showFolderCreateModal,
|
| 77 |
label: (
|
| 78 |
<div>
|
| 79 |
<Button type="link">
|
| 80 |
+
<FolderOpenOutlined />
|
| 81 |
+
New Folder
|
| 82 |
</Button>
|
| 83 |
</div>
|
| 84 |
),
|
| 85 |
// disabled: true,
|
| 86 |
},
|
| 87 |
];
|
| 88 |
+
}, [t, showFolderCreateModal]);
|
| 89 |
|
| 90 |
+
const { handleRemoveFile } = useHandleDeleteFile(selectedRowKeys);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
const disabled = selectedRowKeys.length === 0;
|
| 93 |
|
|
|
|
| 95 |
return [
|
| 96 |
{
|
| 97 |
key: '4',
|
| 98 |
+
onClick: handleRemoveFile,
|
| 99 |
label: (
|
| 100 |
<Flex gap={10}>
|
| 101 |
<span className={styles.deleteIconWrapper}>
|
|
|
|
| 106 |
),
|
| 107 |
},
|
| 108 |
];
|
| 109 |
+
}, [handleRemoveFile, t]);
|
| 110 |
|
| 111 |
return (
|
| 112 |
<div className={styles.filter}>
|
web/src/pages/file-manager/file-upload-modal/index.tsx
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { InboxOutlined } from '@ant-design/icons';
|
| 2 |
+
import { Modal, Segmented, Upload, UploadProps, message } from 'antd';
|
| 3 |
+
import { useState } from 'react';
|
| 4 |
+
|
| 5 |
+
const { Dragger } = Upload;
|
| 6 |
+
|
| 7 |
+
const FileUploadModal = () => {
|
| 8 |
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
| 9 |
+
|
| 10 |
+
const props: UploadProps = {
|
| 11 |
+
name: 'file',
|
| 12 |
+
multiple: true,
|
| 13 |
+
action: 'https://660d2bd96ddfa2943b33731c.mockapi.io/api/upload',
|
| 14 |
+
onChange(info) {
|
| 15 |
+
const { status } = info.file;
|
| 16 |
+
if (status !== 'uploading') {
|
| 17 |
+
console.log(info.file, info.fileList);
|
| 18 |
+
}
|
| 19 |
+
if (status === 'done') {
|
| 20 |
+
message.success(`${info.file.name} file uploaded successfully.`);
|
| 21 |
+
} else if (status === 'error') {
|
| 22 |
+
message.error(`${info.file.name} file upload failed.`);
|
| 23 |
+
}
|
| 24 |
+
},
|
| 25 |
+
onDrop(e) {
|
| 26 |
+
console.log('Dropped files', e.dataTransfer.files);
|
| 27 |
+
},
|
| 28 |
+
};
|
| 29 |
+
|
| 30 |
+
const handleOk = () => {
|
| 31 |
+
setIsModalOpen(false);
|
| 32 |
+
};
|
| 33 |
+
|
| 34 |
+
const handleCancel = () => {
|
| 35 |
+
setIsModalOpen(false);
|
| 36 |
+
};
|
| 37 |
+
|
| 38 |
+
return (
|
| 39 |
+
<>
|
| 40 |
+
<Modal
|
| 41 |
+
title="File upload"
|
| 42 |
+
open={isModalOpen}
|
| 43 |
+
onOk={handleOk}
|
| 44 |
+
onCancel={handleCancel}
|
| 45 |
+
>
|
| 46 |
+
<Segmented options={['Local uploads', 'S3 uploads']} block />
|
| 47 |
+
<Dragger {...props}>
|
| 48 |
+
<p className="ant-upload-drag-icon">
|
| 49 |
+
<InboxOutlined />
|
| 50 |
+
</p>
|
| 51 |
+
<p className="ant-upload-text">
|
| 52 |
+
Click or drag file to this area to upload
|
| 53 |
+
</p>
|
| 54 |
+
<p className="ant-upload-hint">
|
| 55 |
+
Support for a single or bulk upload. Strictly prohibited from
|
| 56 |
+
uploading company data or other banned files.
|
| 57 |
+
</p>
|
| 58 |
+
</Dragger>
|
| 59 |
+
</Modal>
|
| 60 |
+
</>
|
| 61 |
+
);
|
| 62 |
+
};
|
| 63 |
+
|
| 64 |
+
export default FileUploadModal;
|
web/src/pages/file-manager/folder-create-modal/index.tsx
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { IModalManagerChildrenProps } from '@/components/modal-manager';
|
| 2 |
+
import { useTranslate } from '@/hooks/commonHooks';
|
| 3 |
+
import { Form, Input, Modal } from 'antd';
|
| 4 |
+
|
| 5 |
+
interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> {
|
| 6 |
+
loading: boolean;
|
| 7 |
+
onOk: (name: string) => void;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
const FolderCreateModal = ({ visible, hideModal, loading, onOk }: IProps) => {
|
| 11 |
+
const [form] = Form.useForm();
|
| 12 |
+
const { t } = useTranslate('common');
|
| 13 |
+
|
| 14 |
+
type FieldType = {
|
| 15 |
+
name?: string;
|
| 16 |
+
};
|
| 17 |
+
|
| 18 |
+
const handleOk = async () => {
|
| 19 |
+
const ret = await form.validateFields();
|
| 20 |
+
|
| 21 |
+
return onOk(ret.name);
|
| 22 |
+
};
|
| 23 |
+
|
| 24 |
+
const handleCancel = () => {
|
| 25 |
+
hideModal();
|
| 26 |
+
};
|
| 27 |
+
|
| 28 |
+
const onFinish = (values: any) => {
|
| 29 |
+
console.log('Success:', values);
|
| 30 |
+
};
|
| 31 |
+
|
| 32 |
+
const onFinishFailed = (errorInfo: any) => {
|
| 33 |
+
console.log('Failed:', errorInfo);
|
| 34 |
+
};
|
| 35 |
+
|
| 36 |
+
return (
|
| 37 |
+
<Modal
|
| 38 |
+
title={'New Folder'}
|
| 39 |
+
open={visible}
|
| 40 |
+
onOk={handleOk}
|
| 41 |
+
onCancel={handleCancel}
|
| 42 |
+
okButtonProps={{ loading }}
|
| 43 |
+
confirmLoading={loading}
|
| 44 |
+
>
|
| 45 |
+
<Form
|
| 46 |
+
name="basic"
|
| 47 |
+
labelCol={{ span: 4 }}
|
| 48 |
+
wrapperCol={{ span: 20 }}
|
| 49 |
+
style={{ maxWidth: 600 }}
|
| 50 |
+
onFinish={onFinish}
|
| 51 |
+
onFinishFailed={onFinishFailed}
|
| 52 |
+
autoComplete="off"
|
| 53 |
+
form={form}
|
| 54 |
+
>
|
| 55 |
+
<Form.Item<FieldType>
|
| 56 |
+
label={t('name')}
|
| 57 |
+
name="name"
|
| 58 |
+
rules={[{ required: true, message: t('namePlaceholder') }]}
|
| 59 |
+
>
|
| 60 |
+
<Input />
|
| 61 |
+
</Form.Item>
|
| 62 |
+
</Form>
|
| 63 |
+
</Modal>
|
| 64 |
+
);
|
| 65 |
+
};
|
| 66 |
+
|
| 67 |
+
export default FolderCreateModal;
|
web/src/pages/file-manager/hooks.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
| 1 |
-
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
|
| 2 |
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
useFetchFileList,
|
| 4 |
useFetchParentFolderList,
|
|
|
|
| 5 |
useRenameFile,
|
| 6 |
useSelectFileList,
|
| 7 |
useSelectParentFolderList,
|
|
@@ -144,7 +150,7 @@ export const useRenameCurrentFile = () => {
|
|
| 144 |
|
| 145 |
const onFileRenameOk = useCallback(
|
| 146 |
async (name: string) => {
|
| 147 |
-
const ret = await renameFile(file.id, name);
|
| 148 |
|
| 149 |
if (ret === 0) {
|
| 150 |
hideFileRenameModal();
|
|
@@ -191,3 +197,56 @@ export const useSelectBreadcrumbItems = () => {
|
|
| 191 |
path: `/file?folderId=${x.id}`,
|
| 192 |
}));
|
| 193 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import {
|
| 2 |
+
useSetModalState,
|
| 3 |
+
useShowDeleteConfirm,
|
| 4 |
+
useTranslate,
|
| 5 |
+
} from '@/hooks/commonHooks';
|
| 6 |
+
import {
|
| 7 |
+
useCreateFolder,
|
| 8 |
useFetchFileList,
|
| 9 |
useFetchParentFolderList,
|
| 10 |
+
useRemoveFile,
|
| 11 |
useRenameFile,
|
| 12 |
useSelectFileList,
|
| 13 |
useSelectParentFolderList,
|
|
|
|
| 150 |
|
| 151 |
const onFileRenameOk = useCallback(
|
| 152 |
async (name: string) => {
|
| 153 |
+
const ret = await renameFile(file.id, name, file.parent_id);
|
| 154 |
|
| 155 |
if (ret === 0) {
|
| 156 |
hideFileRenameModal();
|
|
|
|
| 197 |
path: `/file?folderId=${x.id}`,
|
| 198 |
}));
|
| 199 |
};
|
| 200 |
+
|
| 201 |
+
export const useHandleCreateFolder = () => {
|
| 202 |
+
const {
|
| 203 |
+
visible: folderCreateModalVisible,
|
| 204 |
+
hideModal: hideFolderCreateModal,
|
| 205 |
+
showModal: showFolderCreateModal,
|
| 206 |
+
} = useSetModalState();
|
| 207 |
+
const createFolder = useCreateFolder();
|
| 208 |
+
const id = useGetFolderId();
|
| 209 |
+
|
| 210 |
+
const onFolderCreateOk = useCallback(
|
| 211 |
+
async (name: string) => {
|
| 212 |
+
const ret = await createFolder(id, name);
|
| 213 |
+
|
| 214 |
+
if (ret === 0) {
|
| 215 |
+
hideFolderCreateModal();
|
| 216 |
+
}
|
| 217 |
+
},
|
| 218 |
+
[createFolder, hideFolderCreateModal, id],
|
| 219 |
+
);
|
| 220 |
+
|
| 221 |
+
const loading = useOneNamespaceEffectsLoading('fileManager', [
|
| 222 |
+
'createFolder',
|
| 223 |
+
]);
|
| 224 |
+
|
| 225 |
+
return {
|
| 226 |
+
folderCreateLoading: loading,
|
| 227 |
+
onFolderCreateOk,
|
| 228 |
+
folderCreateModalVisible,
|
| 229 |
+
hideFolderCreateModal,
|
| 230 |
+
showFolderCreateModal,
|
| 231 |
+
};
|
| 232 |
+
};
|
| 233 |
+
|
| 234 |
+
export const useHandleDeleteFile = (fileIds: string[]) => {
|
| 235 |
+
const removeDocument = useRemoveFile();
|
| 236 |
+
const showDeleteConfirm = useShowDeleteConfirm();
|
| 237 |
+
const parentId = useGetFolderId();
|
| 238 |
+
|
| 239 |
+
const handleRemoveFile = () => {
|
| 240 |
+
showDeleteConfirm({
|
| 241 |
+
onOk: () => {
|
| 242 |
+
return removeDocument(fileIds, parentId);
|
| 243 |
+
},
|
| 244 |
+
});
|
| 245 |
+
};
|
| 246 |
+
|
| 247 |
+
return { handleRemoveFile };
|
| 248 |
+
};
|
| 249 |
+
|
| 250 |
+
export const useSelectFileListLoading = () => {
|
| 251 |
+
return useOneNamespaceEffectsLoading('fileManager', ['listFile']);
|
| 252 |
+
};
|
web/src/pages/file-manager/index.tsx
CHANGED
|
@@ -7,16 +7,20 @@ 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,
|
|
@@ -26,6 +30,13 @@ const FileManager = () => {
|
|
| 26 |
initialFileName,
|
| 27 |
onFileRenameOk,
|
| 28 |
} = useRenameCurrentFile();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
const columns: ColumnsType<IFile> = [
|
| 31 |
{
|
|
@@ -78,12 +89,14 @@ const FileManager = () => {
|
|
| 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}
|
|
@@ -92,6 +105,12 @@ const FileManager = () => {
|
|
| 92 |
initialName={initialFileName}
|
| 93 |
loading={fileRenameLoading}
|
| 94 |
></RenameModal>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
</section>
|
| 96 |
);
|
| 97 |
};
|
|
|
|
| 7 |
import FileToolbar from './file-toolbar';
|
| 8 |
import {
|
| 9 |
useGetRowSelection,
|
| 10 |
+
useHandleCreateFolder,
|
| 11 |
useNavigateToOtherFolder,
|
| 12 |
useRenameCurrentFile,
|
| 13 |
+
useSelectFileListLoading,
|
| 14 |
} from './hooks';
|
| 15 |
|
| 16 |
import RenameModal from '@/components/rename-modal';
|
| 17 |
+
import FolderCreateModal from './folder-create-modal';
|
| 18 |
import styles from './index.less';
|
| 19 |
|
| 20 |
const FileManager = () => {
|
| 21 |
const fileList = useSelectFileList();
|
| 22 |
const rowSelection = useGetRowSelection();
|
| 23 |
+
const loading = useSelectFileListLoading();
|
| 24 |
const navigateToOtherFolder = useNavigateToOtherFolder();
|
| 25 |
const {
|
| 26 |
fileRenameVisible,
|
|
|
|
| 30 |
initialFileName,
|
| 31 |
onFileRenameOk,
|
| 32 |
} = useRenameCurrentFile();
|
| 33 |
+
const {
|
| 34 |
+
folderCreateModalVisible,
|
| 35 |
+
showFolderCreateModal,
|
| 36 |
+
hideFolderCreateModal,
|
| 37 |
+
folderCreateLoading,
|
| 38 |
+
onFolderCreateOk,
|
| 39 |
+
} = useHandleCreateFolder();
|
| 40 |
|
| 41 |
const columns: ColumnsType<IFile> = [
|
| 42 |
{
|
|
|
|
| 89 |
<section className={styles.fileManagerWrapper}>
|
| 90 |
<FileToolbar
|
| 91 |
selectedRowKeys={rowSelection.selectedRowKeys as string[]}
|
| 92 |
+
showFolderCreateModal={showFolderCreateModal}
|
| 93 |
></FileToolbar>
|
| 94 |
<Table
|
| 95 |
dataSource={fileList}
|
| 96 |
columns={columns}
|
| 97 |
rowKey={'id'}
|
| 98 |
rowSelection={rowSelection}
|
| 99 |
+
loading={loading}
|
| 100 |
/>
|
| 101 |
<RenameModal
|
| 102 |
visible={fileRenameVisible}
|
|
|
|
| 105 |
initialName={initialFileName}
|
| 106 |
loading={fileRenameLoading}
|
| 107 |
></RenameModal>
|
| 108 |
+
<FolderCreateModal
|
| 109 |
+
loading={folderCreateLoading}
|
| 110 |
+
visible={folderCreateModalVisible}
|
| 111 |
+
hideModal={hideFolderCreateModal}
|
| 112 |
+
onOk={onFolderCreateOk}
|
| 113 |
+
></FolderCreateModal>
|
| 114 |
</section>
|
| 115 |
);
|
| 116 |
};
|
web/src/pages/file-manager/model.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 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 {
|
|
@@ -20,12 +21,14 @@ const model: DvaModel<FileManagerModelState> = {
|
|
| 20 |
},
|
| 21 |
effects: {
|
| 22 |
*removeFile({ payload = {} }, { call, put }) {
|
| 23 |
-
const { data } = yield call(fileManagerService.removeFile,
|
|
|
|
|
|
|
| 24 |
const { retcode } = data;
|
| 25 |
if (retcode === 0) {
|
| 26 |
yield put({
|
| 27 |
type: 'listFile',
|
| 28 |
-
payload:
|
| 29 |
});
|
| 30 |
}
|
| 31 |
},
|
|
@@ -41,9 +44,25 @@ const model: DvaModel<FileManagerModelState> = {
|
|
| 41 |
}
|
| 42 |
},
|
| 43 |
*renameFile({ payload = {} }, { call, put }) {
|
| 44 |
-
const { data } = yield call(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
if (data.retcode === 0) {
|
| 46 |
-
yield put({
|
|
|
|
|
|
|
|
|
|
| 47 |
}
|
| 48 |
return data.retcode;
|
| 49 |
},
|
|
|
|
| 1 |
import { IFile, IFolder } from '@/interfaces/database/file-manager';
|
| 2 |
import fileManagerService from '@/services/fileManagerService';
|
| 3 |
+
import omit from 'lodash/omit';
|
| 4 |
import { DvaModel } from 'umi';
|
| 5 |
|
| 6 |
export interface FileManagerModelState {
|
|
|
|
| 21 |
},
|
| 22 |
effects: {
|
| 23 |
*removeFile({ payload = {} }, { call, put }) {
|
| 24 |
+
const { data } = yield call(fileManagerService.removeFile, {
|
| 25 |
+
fileIds: payload.fileIds,
|
| 26 |
+
});
|
| 27 |
const { retcode } = data;
|
| 28 |
if (retcode === 0) {
|
| 29 |
yield put({
|
| 30 |
type: 'listFile',
|
| 31 |
+
payload: { parentId: payload.parentId },
|
| 32 |
});
|
| 33 |
}
|
| 34 |
},
|
|
|
|
| 44 |
}
|
| 45 |
},
|
| 46 |
*renameFile({ payload = {} }, { call, put }) {
|
| 47 |
+
const { data } = yield call(
|
| 48 |
+
fileManagerService.renameFile,
|
| 49 |
+
omit(payload, ['parentId']),
|
| 50 |
+
);
|
| 51 |
+
if (data.retcode === 0) {
|
| 52 |
+
yield put({
|
| 53 |
+
type: 'listFile',
|
| 54 |
+
payload: { parentId: payload.parentId },
|
| 55 |
+
});
|
| 56 |
+
}
|
| 57 |
+
return data.retcode;
|
| 58 |
+
},
|
| 59 |
+
*createFolder({ payload = {} }, { call, put }) {
|
| 60 |
+
const { data } = yield call(fileManagerService.createFolder, payload);
|
| 61 |
if (data.retcode === 0) {
|
| 62 |
+
yield put({
|
| 63 |
+
type: 'listFile',
|
| 64 |
+
payload: { parentId: payload.parentId },
|
| 65 |
+
});
|
| 66 |
}
|
| 67 |
return data.retcode;
|
| 68 |
},
|
web/src/services/fileManagerService.ts
CHANGED
|
@@ -2,8 +2,14 @@ import api from '@/utils/api';
|
|
| 2 |
import registerServer from '@/utils/registerServer';
|
| 3 |
import request from '@/utils/request';
|
| 4 |
|
| 5 |
-
const {
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
const methods = {
|
| 9 |
listFile: {
|
|
@@ -26,6 +32,10 @@ const methods = {
|
|
| 26 |
url: getAllParentFolder,
|
| 27 |
method: 'get',
|
| 28 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
} as const;
|
| 30 |
|
| 31 |
const fileManagerService = registerServer<keyof typeof methods>(
|
|
|
|
| 2 |
import registerServer from '@/utils/registerServer';
|
| 3 |
import request from '@/utils/request';
|
| 4 |
|
| 5 |
+
const {
|
| 6 |
+
listFile,
|
| 7 |
+
removeFile,
|
| 8 |
+
uploadFile,
|
| 9 |
+
renameFile,
|
| 10 |
+
getAllParentFolder,
|
| 11 |
+
createFolder,
|
| 12 |
+
} = api;
|
| 13 |
|
| 14 |
const methods = {
|
| 15 |
listFile: {
|
|
|
|
| 32 |
url: getAllParentFolder,
|
| 33 |
method: 'get',
|
| 34 |
},
|
| 35 |
+
createFolder: {
|
| 36 |
+
url: createFolder,
|
| 37 |
+
method: 'post',
|
| 38 |
+
},
|
| 39 |
} as const;
|
| 40 |
|
| 41 |
const fileManagerService = registerServer<keyof typeof methods>(
|
web/src/utils/api.ts
CHANGED
|
@@ -73,4 +73,5 @@ export default {
|
|
| 73 |
removeFile: `${api_host}/file/rm`,
|
| 74 |
renameFile: `${api_host}/file/rename`,
|
| 75 |
getAllParentFolder: `${api_host}/file/all_parent_folder`,
|
|
|
|
| 76 |
};
|
|
|
|
| 73 |
removeFile: `${api_host}/file/rm`,
|
| 74 |
renameFile: `${api_host}/file/rename`,
|
| 75 |
getAllParentFolder: `${api_host}/file/all_parent_folder`,
|
| 76 |
+
createFolder: `${api_host}/file/create`,
|
| 77 |
};
|