feat: Move files in file manager #1826 (#1837)

### What problem does this PR solve?

feat: Move files in file manager #1826

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2024-08-07 10:12:11 +08:00
committed by GitHub
parent 4c2906d6fd
commit c55e9d16da
14 changed files with 292 additions and 24 deletions

View File

@ -1,6 +1,12 @@
import NewDocumentLink from '@/components/new-document-link';
import SvgIcon from '@/components/svg-icon';
import { useTranslate } from '@/hooks/common-hooks';
import { IFile } from '@/interfaces/database/file-manager';
import { api_host } from '@/utils/api';
import {
getExtension,
isSupportedPreviewDocumentType,
} from '@/utils/document-util';
import { downloadFile } from '@/utils/file-util';
import {
DeleteOutlined,
@ -11,18 +17,13 @@ import {
} from '@ant-design/icons';
import { Button, Space, Tooltip } from 'antd';
import { useHandleDeleteFile } from '../hooks';
import NewDocumentLink from '@/components/new-document-link';
import {
getExtension,
isSupportedPreviewDocumentType,
} from '@/utils/document-util';
import styles from './index.less';
interface IProps {
record: IFile;
setCurrentRecord: (record: any) => void;
showRenameModal: (record: IFile) => void;
showMoveFileModal: (ids: string[]) => void;
showConnectToKnowledgeModal: (record: IFile) => void;
setSelectedRowKeys(keys: string[]): void;
}
@ -33,6 +34,7 @@ const ActionCell = ({
showRenameModal,
showConnectToKnowledgeModal,
setSelectedRowKeys,
showMoveFileModal,
}: IProps) => {
const documentId = record.id;
const beingUsed = false;
@ -64,6 +66,10 @@ const ActionCell = ({
showConnectToKnowledgeModal(record);
};
const onShowMoveFileModal = () => {
showMoveFileModal([documentId]);
};
return (
<Space size={0}>
{isKnowledgeBase || (
@ -90,6 +96,18 @@ const ActionCell = ({
</Button>
</Tooltip>
)}
{isKnowledgeBase || (
<Tooltip title={t('move', { keyPrefix: 'common' })}>
<Button
type="text"
disabled={beingUsed}
onClick={onShowMoveFileModal}
className={styles.iconButton}
>
<SvgIcon name={`move`} width={16}></SvgIcon>
</Button>
</Tooltip>
)}
{isKnowledgeBase || (
<Tooltip title={t('delete', { keyPrefix: 'common' })}>
<Button

View File

@ -17,13 +17,14 @@ import {
MenuProps,
Space,
} from 'antd';
import { useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import {
useHandleBreadcrumbClick,
useHandleDeleteFile,
useSelectBreadcrumbItems,
} from './hooks';
import SvgIcon from '@/components/svg-icon';
import {
IListResult,
useFetchParentFolderList,
@ -36,6 +37,7 @@ interface IProps
showFolderCreateModal: () => void;
showFileUploadModal: () => void;
setSelectedRowKeys: (keys: string[]) => void;
showMoveFileModal: (ids: string[]) => void;
}
const FileToolbar = ({
@ -45,6 +47,7 @@ const FileToolbar = ({
setSelectedRowKeys,
searchString,
handleInputChange,
showMoveFileModal,
}: IProps) => {
const { t } = useTranslate('knowledgeDetails');
const breadcrumbItems = useSelectBreadcrumbItems();
@ -111,6 +114,10 @@ const FileToolbar = ({
setSelectedRowKeys,
);
const handleShowMoveFileModal = useCallback(() => {
showMoveFileModal(selectedRowKeys);
}, [selectedRowKeys, showMoveFileModal]);
const disabled = selectedRowKeys.length === 0;
const items: MenuProps['items'] = useMemo(() => {
@ -127,8 +134,20 @@ const FileToolbar = ({
</Flex>
),
},
{
key: '5',
onClick: handleShowMoveFileModal,
label: (
<Flex gap={10}>
<span className={styles.deleteIconWrapper}>
<SvgIcon name={`move`} width={18}></SvgIcon>
</span>
<b>{t('move', { keyPrefix: 'common' })}</b>
</Flex>
),
},
];
}, [handleRemoveFile, t]);
}, [handleShowMoveFileModal, t, handleRemoveFile]);
return (
<div className={styles.filter}>

View File

@ -21,24 +21,12 @@ const FolderCreateModal = ({ visible, hideModal, loading, onOk }: IProps) => {
return onOk(ret.name);
};
const handleCancel = () => {
hideModal();
};
const onFinish = (values: any) => {
console.log('Success:', values);
};
const onFinishFailed = (errorInfo: any) => {
console.log('Failed:', errorInfo);
};
return (
<Modal
title={t('newFolder', { keyPrefix: 'fileManager' })}
open={visible}
onOk={handleOk}
onCancel={handleCancel}
onCancel={hideModal}
okButtonProps={{ loading }}
confirmLoading={loading}
>
@ -47,8 +35,6 @@ const FolderCreateModal = ({ visible, hideModal, loading, onOk }: IProps) => {
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
style={{ maxWidth: 600 }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
form={form}
>

View File

@ -4,6 +4,7 @@ import {
useCreateFolder,
useDeleteFile,
useFetchParentFolderList,
useMoveFile,
useRenameFile,
useUploadFile,
} from '@/hooks/file-manager-hooks';
@ -246,3 +247,48 @@ export const useHandleBreadcrumbClick = () => {
return { handleBreadcrumbClick };
};
export const useHandleMoveFile = (
setSelectedRowKeys: (keys: string[]) => void,
) => {
const {
visible: moveFileVisible,
hideModal: hideMoveFileModal,
showModal: showMoveFileModal,
} = useSetModalState();
const { moveFile, loading } = useMoveFile();
const [sourceFileIds, setSourceFileIds] = useState<string[]>([]);
const onMoveFileOk = useCallback(
async (targetFolderId: string) => {
const ret = await moveFile({
src_file_ids: sourceFileIds,
dest_file_id: targetFolderId,
});
if (ret === 0) {
setSelectedRowKeys([]);
hideMoveFileModal();
}
return ret;
},
[moveFile, hideMoveFileModal, sourceFileIds, setSelectedRowKeys],
);
const handleShowMoveFileModal = useCallback(
(ids: string[]) => {
setSourceFileIds(ids);
showMoveFileModal();
},
[showMoveFileModal],
);
return {
initialValue: '',
moveFileLoading: loading,
onMoveFileOk,
moveFileVisible,
hideMoveFileModal,
showMoveFileModal: handleShowMoveFileModal,
};
};

View File

@ -9,6 +9,7 @@ import {
useGetRowSelection,
useHandleConnectToKnowledge,
useHandleCreateFolder,
useHandleMoveFile,
useHandleUploadFile,
useNavigateToOtherFolder,
useRenameCurrentFile,
@ -23,6 +24,7 @@ import { getExtension } from '@/utils/document-util';
import ConnectToKnowledgeModal from './connect-to-knowledge-modal';
import FolderCreateModal from './folder-create-modal';
import styles from './index.less';
import FileMovingModal from './move-file-modal';
const { Text } = Typography;
@ -61,7 +63,13 @@ const FileManager = () => {
initialValue,
connectToKnowledgeLoading,
} = useHandleConnectToKnowledge();
// const { pagination } = useGetFilesPagination();
const {
showMoveFileModal,
moveFileVisible,
onMoveFileOk,
hideMoveFileModal,
moveFileLoading,
} = useHandleMoveFile(setSelectedRowKeys);
const { pagination, data, searchString, handleInputChange, loading } =
useFetchFileList();
const columns: ColumnsType<IFile> = [
@ -139,6 +147,7 @@ const FileManager = () => {
console.info(record);
}}
showRenameModal={showFileRenameModal}
showMoveFileModal={showMoveFileModal}
showConnectToKnowledgeModal={showConnectToKnowledgeModal}
setSelectedRowKeys={setSelectedRowKeys}
></ActionCell>
@ -155,6 +164,7 @@ const FileManager = () => {
showFolderCreateModal={showFolderCreateModal}
showFileUploadModal={showFileUploadModal}
setSelectedRowKeys={setSelectedRowKeys}
showMoveFileModal={showMoveFileModal}
></FileToolbar>
<Table
dataSource={data?.files}
@ -191,6 +201,14 @@ const FileManager = () => {
onOk={onConnectToKnowledgeOk}
loading={connectToKnowledgeLoading}
></ConnectToKnowledgeModal>
{moveFileVisible && (
<FileMovingModal
visible={moveFileVisible}
hideModal={hideMoveFileModal}
onOk={onMoveFileOk}
loading={moveFileLoading}
></FileMovingModal>
)}
</section>
);
};

View File

@ -0,0 +1,64 @@
import { useFetchPureFileList } from '@/hooks/file-manager-hooks';
import { IFile } from '@/interfaces/database/file-manager';
import type { GetProp, TreeSelectProps } from 'antd';
import { TreeSelect } from 'antd';
import { useCallback, useEffect, useState } from 'react';
type DefaultOptionType = GetProp<TreeSelectProps, 'treeData'>[number];
interface IProps {
value?: string;
onChange?: (value: string) => void;
}
const AsyncTreeSelect = ({ value, onChange }: IProps) => {
const { fetchList } = useFetchPureFileList();
const [treeData, setTreeData] = useState<Omit<DefaultOptionType, 'label'>[]>(
[],
);
const onLoadData: TreeSelectProps['loadData'] = useCallback(
async ({ id }) => {
const ret = await fetchList(id);
if (ret.retcode === 0) {
setTreeData((tree) => {
return tree.concat(
ret.data.files
.filter((x: IFile) => x.type === 'folder')
.map((x: IFile) => ({
id: x.id,
pId: x.parent_id,
value: x.id,
title: x.name,
isLeaf: false,
})),
);
});
}
},
[fetchList],
);
const handleChange = (newValue: string) => {
onChange?.(newValue);
};
useEffect(() => {
onLoadData?.({ id: '', props: '' });
}, [onLoadData]);
return (
<TreeSelect
treeDataSimpleMode
style={{ width: '100%' }}
value={value}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
placeholder="Please select"
onChange={handleChange}
loadData={onLoadData}
treeData={treeData}
/>
);
};
export default AsyncTreeSelect;

View File

@ -0,0 +1,54 @@
import { IModalManagerChildrenProps } from '@/components/modal-manager';
import { useTranslate } from '@/hooks/common-hooks';
import { Form, Modal } from 'antd';
import AsyncTreeSelect from './async-tree-select';
interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> {
loading: boolean;
onOk: (id: string) => void;
}
const FileMovingModal = ({ visible, hideModal, loading, onOk }: IProps) => {
const [form] = Form.useForm();
const { t } = useTranslate('fileManager');
type FieldType = {
name?: string;
};
const handleOk = async () => {
const ret = await form.validateFields();
return onOk(ret.name);
};
return (
<Modal
title={t('move', { keyPrefix: 'common' })}
open={visible}
onOk={handleOk}
onCancel={hideModal}
okButtonProps={{ loading }}
confirmLoading={loading}
width={600}
>
<Form
name="basic"
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
autoComplete="off"
form={form}
>
<Form.Item<FieldType>
label={t('destinationFolder')}
name="name"
rules={[{ required: true, message: t('pleaseSelect') }]}
>
<AsyncTreeSelect></AsyncTreeSelect>
</Form.Item>
</Form>
</Modal>
);
};
export default FileMovingModal;