### What problem does this PR solve? feat: upload file in FileManager #345 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.3.1
| @@ -1,4 +1,8 @@ | |||
| import { IFileListRequestBody } from '@/interfaces/request/file-manager'; | |||
| import { | |||
| IConnectRequestBody, | |||
| IFileListRequestBody, | |||
| } from '@/interfaces/request/file-manager'; | |||
| import { UploadFile } from 'antd'; | |||
| import { useCallback } from 'react'; | |||
| import { useDispatch, useSelector } from 'umi'; | |||
| @@ -94,3 +98,47 @@ export const useSelectParentFolderList = () => { | |||
| ); | |||
| return parentFolderList.toReversed(); | |||
| }; | |||
| export const useUploadFile = () => { | |||
| const dispatch = useDispatch(); | |||
| const uploadFile = useCallback( | |||
| (file: UploadFile, parentId: string, path: string) => { | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'fileManager/uploadFile', | |||
| payload: { | |||
| file, | |||
| parentId, | |||
| path, | |||
| }, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| } | |||
| }, | |||
| [dispatch], | |||
| ); | |||
| return uploadFile; | |||
| }; | |||
| export const useConnectToKnowledge = () => { | |||
| const dispatch = useDispatch(); | |||
| const uploadFile = useCallback( | |||
| (payload: IConnectRequestBody) => { | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'fileManager/connectFileToKnowledge', | |||
| payload, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| } | |||
| }, | |||
| [dispatch], | |||
| ); | |||
| return uploadFile; | |||
| }; | |||
| @@ -3,7 +3,7 @@ export interface IFile { | |||
| create_time: number; | |||
| created_by: string; | |||
| id: string; | |||
| kb_ids: string[]; | |||
| kbs_info: { kb_id: string; kb_name: string }[]; | |||
| location: string; | |||
| name: string; | |||
| parent_id: string; | |||
| @@ -3,3 +3,12 @@ import { IPaginationRequestBody } from './base'; | |||
| export interface IFileListRequestBody extends IPaginationRequestBody { | |||
| parent_id?: string; // folder id | |||
| } | |||
| interface BaseRequestBody { | |||
| parentId: string; | |||
| } | |||
| export interface IConnectRequestBody extends BaseRequestBody { | |||
| fileIds: string[]; | |||
| kbIds: string[]; | |||
| } | |||
| @@ -382,7 +382,7 @@ export default { | |||
| passwordDescription: | |||
| 'Please enter your current password to change your password.', | |||
| model: 'Model Providers', | |||
| modelDescription: 'Manage your account settings and preferences here.', | |||
| modelDescription: 'Set the model parameter and API Key here.', | |||
| team: 'Team', | |||
| logout: 'Log out', | |||
| username: 'Username', | |||
| @@ -352,7 +352,7 @@ export default { | |||
| password: '密碼', | |||
| passwordDescription: '請輸入您當前的密碼以更改您的密碼。', | |||
| model: '模型提供商', | |||
| modelDescription: '在此管理您的帳戶設置和首選項。', | |||
| modelDescription: '在此設置模型參數和 API Key。', | |||
| team: '團隊', | |||
| logout: '登出', | |||
| username: '使用者名稱', | |||
| @@ -369,7 +369,7 @@ export default { | |||
| password: '密码', | |||
| passwordDescription: '请输入您当前的密码以更改您的密码。', | |||
| model: '模型提供商', | |||
| modelDescription: '在此管理您的帐户设置和首选项。', | |||
| modelDescription: '在此设置模型参数和 API Key。', | |||
| team: '团队', | |||
| logout: '登出', | |||
| username: '用户名', | |||
| @@ -17,9 +17,15 @@ interface IProps { | |||
| record: IFile; | |||
| setCurrentRecord: (record: any) => void; | |||
| showRenameModal: (record: IFile) => void; | |||
| showConnectToKnowledgeModal: (ids: string[]) => void; | |||
| } | |||
| const ActionCell = ({ record, setCurrentRecord, showRenameModal }: IProps) => { | |||
| const ActionCell = ({ | |||
| record, | |||
| setCurrentRecord, | |||
| showRenameModal, | |||
| showConnectToKnowledgeModal, | |||
| }: IProps) => { | |||
| const documentId = record.id; | |||
| const beingUsed = false; | |||
| const { t } = useTranslate('knowledgeDetails'); | |||
| @@ -41,9 +47,17 @@ const ActionCell = ({ record, setCurrentRecord, showRenameModal }: IProps) => { | |||
| showRenameModal(record); | |||
| }; | |||
| const onShowConnectToKnowledgeModal = () => { | |||
| showConnectToKnowledgeModal([documentId]); | |||
| }; | |||
| return ( | |||
| <Space size={0}> | |||
| <Button type="text" className={styles.iconButton}> | |||
| <Button | |||
| type="text" | |||
| className={styles.iconButton} | |||
| onClick={onShowConnectToKnowledgeModal} | |||
| > | |||
| <ToolOutlined size={20} /> | |||
| </Button> | |||
| @@ -0,0 +1,58 @@ | |||
| import { useFetchKnowledgeList } from '@/hooks/knowledgeHook'; | |||
| import { IModalProps } from '@/interfaces/common'; | |||
| import { Form, Modal, Select, SelectProps } from 'antd'; | |||
| const ConnectToKnowledgeModal = ({ | |||
| visible, | |||
| hideModal, | |||
| onOk, | |||
| }: IModalProps<string[]>) => { | |||
| const [form] = Form.useForm(); | |||
| const { list } = useFetchKnowledgeList(); | |||
| const options: SelectProps['options'] = list?.map((item) => ({ | |||
| label: item.name, | |||
| value: item.id, | |||
| })); | |||
| const handleOk = async () => { | |||
| const values = await form.getFieldsValue(); | |||
| const knowledgeIds = values.knowledgeIds ?? []; | |||
| if (knowledgeIds.length > 0) { | |||
| return onOk?.(knowledgeIds); | |||
| } | |||
| }; | |||
| return ( | |||
| <Modal | |||
| title="Add to Knowledge Base" | |||
| open={visible} | |||
| onOk={handleOk} | |||
| onCancel={hideModal} | |||
| > | |||
| <Form form={form}> | |||
| <Form.Item | |||
| name="knowledgeIds" | |||
| noStyle | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: 'Please select your favourite colors!', | |||
| type: 'array', | |||
| }, | |||
| ]} | |||
| > | |||
| <Select | |||
| mode="multiple" | |||
| allowClear | |||
| style={{ width: '100%' }} | |||
| placeholder="Please select" | |||
| options={options} | |||
| /> | |||
| </Form.Item> | |||
| </Form> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| export default ConnectToKnowledgeModal; | |||
| @@ -32,6 +32,7 @@ import styles from './index.less'; | |||
| interface IProps { | |||
| selectedRowKeys: string[]; | |||
| showFolderCreateModal: () => void; | |||
| showFileUploadModal: () => void; | |||
| } | |||
| const itemRender: BreadcrumbProps['itemRender'] = ( | |||
| @@ -48,7 +49,11 @@ const itemRender: BreadcrumbProps['itemRender'] = ( | |||
| ); | |||
| }; | |||
| const FileToolbar = ({ selectedRowKeys, showFolderCreateModal }: IProps) => { | |||
| const FileToolbar = ({ | |||
| selectedRowKeys, | |||
| showFolderCreateModal, | |||
| showFileUploadModal, | |||
| }: IProps) => { | |||
| const { t } = useTranslate('knowledgeDetails'); | |||
| const { fetchDocumentList } = useFetchDocumentListOnMount(); | |||
| const { setPagination, searchString } = useGetPagination(fetchDocumentList); | |||
| @@ -59,6 +64,7 @@ const FileToolbar = ({ selectedRowKeys, showFolderCreateModal }: IProps) => { | |||
| return [ | |||
| { | |||
| key: '1', | |||
| onClick: showFileUploadModal, | |||
| label: ( | |||
| <div> | |||
| <Button type="link"> | |||
| @@ -85,7 +91,7 @@ const FileToolbar = ({ selectedRowKeys, showFolderCreateModal }: IProps) => { | |||
| // disabled: true, | |||
| }, | |||
| ]; | |||
| }, [t, showFolderCreateModal]); | |||
| }, [t, showFolderCreateModal, showFileUploadModal]); | |||
| const { handleRemoveFile } = useHandleDeleteFile(selectedRowKeys); | |||
| @@ -1,61 +1,124 @@ | |||
| import { IModalProps } from '@/interfaces/common'; | |||
| import { InboxOutlined } from '@ant-design/icons'; | |||
| import { Modal, Segmented, Upload, UploadProps, message } from 'antd'; | |||
| import { useState } from 'react'; | |||
| import { | |||
| Flex, | |||
| Modal, | |||
| Segmented, | |||
| Tabs, | |||
| TabsProps, | |||
| Upload, | |||
| UploadFile, | |||
| UploadProps, | |||
| } from 'antd'; | |||
| import { Dispatch, SetStateAction, useState } from 'react'; | |||
| import { useHandleUploadFile } from '../hooks'; | |||
| const { Dragger } = Upload; | |||
| const FileUploadModal = () => { | |||
| const [isModalOpen, setIsModalOpen] = useState(false); | |||
| const FileUpload = ({ | |||
| directory, | |||
| fileList, | |||
| setFileList, | |||
| }: { | |||
| directory: boolean; | |||
| fileList: UploadFile[]; | |||
| setFileList: Dispatch<SetStateAction<UploadFile[]>>; | |||
| }) => { | |||
| const props: UploadProps = { | |||
| name: 'file', | |||
| multiple: true, | |||
| action: 'https://660d2bd96ddfa2943b33731c.mockapi.io/api/upload', | |||
| onChange(info) { | |||
| const { status } = info.file; | |||
| if (status !== 'uploading') { | |||
| console.log(info.file, info.fileList); | |||
| } | |||
| if (status === 'done') { | |||
| message.success(`${info.file.name} file uploaded successfully.`); | |||
| } else if (status === 'error') { | |||
| message.error(`${info.file.name} file upload failed.`); | |||
| } | |||
| onRemove: (file) => { | |||
| const index = fileList.indexOf(file); | |||
| const newFileList = fileList.slice(); | |||
| newFileList.splice(index, 1); | |||
| setFileList(newFileList); | |||
| }, | |||
| onDrop(e) { | |||
| console.log('Dropped files', e.dataTransfer.files); | |||
| beforeUpload: (file) => { | |||
| setFileList((pre) => { | |||
| return [...pre, file]; | |||
| }); | |||
| return false; | |||
| }, | |||
| directory, | |||
| fileList, | |||
| }; | |||
| const handleOk = () => { | |||
| setIsModalOpen(false); | |||
| }; | |||
| return ( | |||
| <Dragger {...props}> | |||
| <p className="ant-upload-drag-icon"> | |||
| <InboxOutlined /> | |||
| </p> | |||
| <p className="ant-upload-text"> | |||
| Click or drag file to this area to upload | |||
| </p> | |||
| <p className="ant-upload-hint"> | |||
| Support for a single or bulk upload. Strictly prohibited from uploading | |||
| company data or other banned files. | |||
| </p> | |||
| </Dragger> | |||
| ); | |||
| }; | |||
| const FileUploadModal = ({ visible, hideModal }: IModalProps<any>) => { | |||
| const [value, setValue] = useState<string | number>('local'); | |||
| const { onFileUploadOk, fileUploadLoading } = useHandleUploadFile(); | |||
| const [fileList, setFileList] = useState<UploadFile[]>([]); | |||
| const [directoryFileList, setDirectoryFileList] = useState<UploadFile[]>([]); | |||
| const handleCancel = () => { | |||
| setIsModalOpen(false); | |||
| const onOk = () => { | |||
| onFileUploadOk([...fileList, ...directoryFileList]); | |||
| }; | |||
| const items: TabsProps['items'] = [ | |||
| { | |||
| key: '1', | |||
| label: 'File', | |||
| children: ( | |||
| <FileUpload | |||
| directory={false} | |||
| fileList={fileList} | |||
| setFileList={setFileList} | |||
| ></FileUpload> | |||
| ), | |||
| }, | |||
| { | |||
| key: '2', | |||
| label: 'Directory', | |||
| children: ( | |||
| <FileUpload | |||
| directory | |||
| fileList={directoryFileList} | |||
| setFileList={setDirectoryFileList} | |||
| ></FileUpload> | |||
| ), | |||
| }, | |||
| ]; | |||
| return ( | |||
| <> | |||
| <Modal | |||
| title="File upload" | |||
| open={isModalOpen} | |||
| onOk={handleOk} | |||
| onCancel={handleCancel} | |||
| open={visible} | |||
| onOk={onOk} | |||
| onCancel={hideModal} | |||
| confirmLoading={fileUploadLoading} | |||
| > | |||
| <Segmented options={['Local uploads', 'S3 uploads']} block /> | |||
| <Dragger {...props}> | |||
| <p className="ant-upload-drag-icon"> | |||
| <InboxOutlined /> | |||
| </p> | |||
| <p className="ant-upload-text"> | |||
| Click or drag file to this area to upload | |||
| </p> | |||
| <p className="ant-upload-hint"> | |||
| Support for a single or bulk upload. Strictly prohibited from | |||
| uploading company data or other banned files. | |||
| </p> | |||
| </Dragger> | |||
| <Flex gap={'large'} vertical> | |||
| <Segmented | |||
| options={[ | |||
| { label: 'Local uploads', value: 'local' }, | |||
| { label: 'S3 uploads', value: 's3' }, | |||
| ]} | |||
| block | |||
| value={value} | |||
| onChange={setValue} | |||
| /> | |||
| {value === 'local' ? ( | |||
| <Tabs defaultActiveKey="1" items={items} /> | |||
| ) : ( | |||
| 'coming soon' | |||
| )} | |||
| </Flex> | |||
| </Modal> | |||
| </> | |||
| ); | |||
| @@ -4,6 +4,7 @@ import { | |||
| useTranslate, | |||
| } from '@/hooks/commonHooks'; | |||
| import { | |||
| useConnectToKnowledge, | |||
| useCreateFolder, | |||
| useFetchFileList, | |||
| useFetchParentFolderList, | |||
| @@ -11,11 +12,14 @@ import { | |||
| useRenameFile, | |||
| useSelectFileList, | |||
| useSelectParentFolderList, | |||
| useUploadFile, | |||
| } from '@/hooks/fileManagerHooks'; | |||
| import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; | |||
| import { Pagination } from '@/interfaces/common'; | |||
| import { IFile } from '@/interfaces/database/file-manager'; | |||
| import { getFilePathByWebkitRelativePath } from '@/utils/fileUtil'; | |||
| import { PaginationProps } from 'antd'; | |||
| import { UploadFile } from 'antd/lib'; | |||
| import { useCallback, useEffect, useMemo, useState } from 'react'; | |||
| import { useDispatch, useNavigate, useSearchParams, useSelector } from 'umi'; | |||
| @@ -250,3 +254,87 @@ export const useHandleDeleteFile = (fileIds: string[]) => { | |||
| export const useSelectFileListLoading = () => { | |||
| return useOneNamespaceEffectsLoading('fileManager', ['listFile']); | |||
| }; | |||
| export const useHandleUploadFile = () => { | |||
| const { | |||
| visible: fileUploadVisible, | |||
| hideModal: hideFileUploadModal, | |||
| showModal: showFileUploadModal, | |||
| } = useSetModalState(); | |||
| const uploadFile = useUploadFile(); | |||
| const id = useGetFolderId(); | |||
| const onFileUploadOk = useCallback( | |||
| async (fileList: UploadFile[]) => { | |||
| console.info('fileList', fileList); | |||
| if (fileList.length > 0) { | |||
| const ret = await uploadFile( | |||
| fileList[0], | |||
| id, | |||
| getFilePathByWebkitRelativePath(fileList[0] as any), | |||
| ); | |||
| if (ret === 0) { | |||
| hideFileUploadModal(); | |||
| } | |||
| } | |||
| }, | |||
| [uploadFile, hideFileUploadModal, id], | |||
| ); | |||
| const loading = useOneNamespaceEffectsLoading('fileManager', ['uploadFile']); | |||
| return { | |||
| fileUploadLoading: loading, | |||
| onFileUploadOk, | |||
| fileUploadVisible, | |||
| hideFileUploadModal, | |||
| showFileUploadModal, | |||
| }; | |||
| }; | |||
| export const useHandleConnectToKnowledge = () => { | |||
| const { | |||
| visible: connectToKnowledgeVisible, | |||
| hideModal: hideConnectToKnowledgeModal, | |||
| showModal: showConnectToKnowledgeModal, | |||
| } = useSetModalState(); | |||
| const connectToKnowledge = useConnectToKnowledge(); | |||
| const id = useGetFolderId(); | |||
| const [fileIds, setFileIds] = useState<string[]>([]); | |||
| const onConnectToKnowledgeOk = useCallback( | |||
| async (knowledgeIds: string[]) => { | |||
| const ret = await connectToKnowledge({ | |||
| parentId: id, | |||
| fileIds, | |||
| kbIds: knowledgeIds, | |||
| }); | |||
| if (ret === 0) { | |||
| hideConnectToKnowledgeModal(); | |||
| } | |||
| }, | |||
| [connectToKnowledge, hideConnectToKnowledgeModal, id, fileIds], | |||
| ); | |||
| const loading = useOneNamespaceEffectsLoading('fileManager', [ | |||
| 'connectFileToKnowledge', | |||
| ]); | |||
| const handleShowConnectToKnowledgeModal = useCallback( | |||
| (ids: string[]) => { | |||
| setFileIds(ids); | |||
| showConnectToKnowledgeModal(); | |||
| }, | |||
| [showConnectToKnowledgeModal], | |||
| ); | |||
| return { | |||
| connectToKnowledgeLoading: loading, | |||
| onConnectToKnowledgeOk, | |||
| connectToKnowledgeVisible, | |||
| hideConnectToKnowledgeModal, | |||
| showConnectToKnowledgeModal: handleShowConnectToKnowledgeModal, | |||
| }; | |||
| }; | |||
| @@ -7,13 +7,17 @@ import ActionCell from './action-cell'; | |||
| import FileToolbar from './file-toolbar'; | |||
| import { | |||
| useGetRowSelection, | |||
| useHandleConnectToKnowledge, | |||
| useHandleCreateFolder, | |||
| useHandleUploadFile, | |||
| useNavigateToOtherFolder, | |||
| useRenameCurrentFile, | |||
| useSelectFileListLoading, | |||
| } from './hooks'; | |||
| import RenameModal from '@/components/rename-modal'; | |||
| import ConnectToKnowledgeModal from './connect-to-knowledge-modal'; | |||
| import FileUploadModal from './file-upload-modal'; | |||
| import FolderCreateModal from './folder-create-modal'; | |||
| import styles from './index.less'; | |||
| @@ -37,6 +41,14 @@ const FileManager = () => { | |||
| folderCreateLoading, | |||
| onFolderCreateOk, | |||
| } = useHandleCreateFolder(); | |||
| const { fileUploadVisible, hideFileUploadModal, showFileUploadModal } = | |||
| useHandleUploadFile(); | |||
| const { | |||
| connectToKnowledgeVisible, | |||
| hideConnectToKnowledgeModal, | |||
| showConnectToKnowledgeModal, | |||
| onConnectToKnowledgeOk, | |||
| } = useHandleConnectToKnowledge(); | |||
| const columns: ColumnsType<IFile> = [ | |||
| { | |||
| @@ -64,6 +76,17 @@ const FileManager = () => { | |||
| return formatDate(text); | |||
| }, | |||
| }, | |||
| { | |||
| title: 'kbs_info', | |||
| dataIndex: 'kbs_info', | |||
| key: 'kbs_info', | |||
| render(value) { | |||
| console.info(value); | |||
| return Array.isArray(value) | |||
| ? value?.map((x) => x.kb_name).join(',') | |||
| : ''; | |||
| }, | |||
| }, | |||
| { | |||
| title: 'Location', | |||
| dataIndex: 'location', | |||
| @@ -80,6 +103,7 @@ const FileManager = () => { | |||
| console.info(record); | |||
| }} | |||
| showRenameModal={showFileRenameModal} | |||
| showConnectToKnowledgeModal={showConnectToKnowledgeModal} | |||
| ></ActionCell> | |||
| ), | |||
| }, | |||
| @@ -90,6 +114,7 @@ const FileManager = () => { | |||
| <FileToolbar | |||
| selectedRowKeys={rowSelection.selectedRowKeys as string[]} | |||
| showFolderCreateModal={showFolderCreateModal} | |||
| showFileUploadModal={showFileUploadModal} | |||
| ></FileToolbar> | |||
| <Table | |||
| dataSource={fileList} | |||
| @@ -111,6 +136,15 @@ const FileManager = () => { | |||
| hideModal={hideFolderCreateModal} | |||
| onOk={onFolderCreateOk} | |||
| ></FolderCreateModal> | |||
| <FileUploadModal | |||
| visible={fileUploadVisible} | |||
| hideModal={hideFileUploadModal} | |||
| ></FileUploadModal> | |||
| <ConnectToKnowledgeModal | |||
| visible={connectToKnowledgeVisible} | |||
| hideModal={hideConnectToKnowledgeModal} | |||
| onOk={onConnectToKnowledgeOk} | |||
| ></ConnectToKnowledgeModal> | |||
| </section> | |||
| ); | |||
| }; | |||
| @@ -56,6 +56,20 @@ const model: DvaModel<FileManagerModelState> = { | |||
| } | |||
| return data.retcode; | |||
| }, | |||
| *uploadFile({ payload = {} }, { call, put }) { | |||
| const formData = new FormData(); | |||
| formData.append('parent_id', payload.parentId); | |||
| formData.append('file', payload.file); | |||
| formData.append('path', payload.path); | |||
| const { data } = yield call(fileManagerService.uploadFile, formData); | |||
| if (data.retcode === 0) { | |||
| yield put({ | |||
| type: 'listFile', | |||
| payload: { parentId: payload.parentId }, | |||
| }); | |||
| } | |||
| return data.retcode; | |||
| }, | |||
| *createFolder({ payload = {} }, { call, put }) { | |||
| const { data } = yield call(fileManagerService.createFolder, payload); | |||
| if (data.retcode === 0) { | |||
| @@ -79,6 +93,19 @@ const model: DvaModel<FileManagerModelState> = { | |||
| } | |||
| return data.retcode; | |||
| }, | |||
| *connectFileToKnowledge({ payload = {} }, { call, put }) { | |||
| const { data } = yield call( | |||
| fileManagerService.connectFileToKnowledge, | |||
| omit(payload, 'parentId'), | |||
| ); | |||
| if (data.retcode === 0) { | |||
| yield put({ | |||
| type: 'listFile', | |||
| payload: { parentId: payload.parentId }, | |||
| }); | |||
| } | |||
| return data.retcode; | |||
| }, | |||
| }, | |||
| }; | |||
| export default model; | |||
| @@ -228,7 +228,7 @@ const UserSettingModel = () => { | |||
| <section className={styles.modelWrapper}> | |||
| <SettingTitle | |||
| title={t('model')} | |||
| description={t('profileDescription')} | |||
| description={t('modelDescription')} | |||
| showRightButton | |||
| clickButton={showSystemSettingModal} | |||
| ></SettingTitle> | |||
| @@ -9,6 +9,7 @@ const { | |||
| renameFile, | |||
| getAllParentFolder, | |||
| createFolder, | |||
| connectFileToKnowledge, | |||
| } = api; | |||
| const methods = { | |||
| @@ -36,6 +37,10 @@ const methods = { | |||
| url: createFolder, | |||
| method: 'post', | |||
| }, | |||
| connectFileToKnowledge: { | |||
| url: connectFileToKnowledge, | |||
| method: 'post', | |||
| }, | |||
| } as const; | |||
| const fileManagerService = registerServer<keyof typeof methods>( | |||
| @@ -74,4 +74,5 @@ export default { | |||
| renameFile: `${api_host}/file/rename`, | |||
| getAllParentFolder: `${api_host}/file/all_parent_folder`, | |||
| createFolder: `${api_host}/file/create`, | |||
| connectFileToKnowledge: `${api_host}/file2document/convert`, | |||
| }; | |||
| @@ -85,3 +85,12 @@ export const downloadFile = ({ | |||
| downloadElement.click(); | |||
| document.body.removeChild(downloadElement); | |||
| }; | |||
| export const getFilePathByWebkitRelativePath = (file: File) => { | |||
| const path = file.webkitRelativePath; | |||
| return path; | |||
| // if (path !== '') { | |||
| // return path.slice(0, path.lastIndexOf('/')); | |||
| // } | |||
| // return path; | |||
| }; | |||