balibabu commited on
Commit
bd39551
·
1 Parent(s): caa112b

feat: add file icon to table of FileManager #345 (#543)

Browse files

### What problem does this PR solve?

feat: add file icon to table of FileManager #345
fix: modify datasetDescription

### Type of change

- [x] New Feature (non-breaking change which adds functionality)

web/src/assets/svg/file-icon/folder.svg ADDED
web/src/base.ts ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import isObject from 'lodash/isObject';
2
+ import { DvaModel } from 'umi';
3
+ import { BaseState } from './interfaces/common';
4
+
5
+ type State = Record<string, any>;
6
+ type DvaModelKey<T> = keyof DvaModel<T>;
7
+
8
+ export const modelExtend = <T>(
9
+ baseModel: Partial<DvaModel<any>>,
10
+ extendModel: DvaModel<any>,
11
+ ): DvaModel<T> => {
12
+ return Object.keys(extendModel).reduce<DvaModel<T>>((pre, cur) => {
13
+ const baseValue = baseModel[cur as DvaModelKey<State>];
14
+ const value = extendModel[cur as DvaModelKey<State>];
15
+
16
+ if (isObject(value) && isObject(baseValue) && typeof value !== 'string') {
17
+ const key = cur as Exclude<DvaModelKey<State>, 'namespace'>;
18
+
19
+ pre[key] = {
20
+ ...baseValue,
21
+ ...value,
22
+ } as any;
23
+ } else {
24
+ pre[cur as DvaModelKey<State>] = value as any;
25
+ }
26
+
27
+ return pre;
28
+ }, {} as DvaModel<T>);
29
+ };
30
+
31
+ export const paginationModel: Partial<DvaModel<BaseState>> = {
32
+ state: {
33
+ searchString: '',
34
+ pagination: {
35
+ total: 0,
36
+ current: 1,
37
+ pageSize: 10,
38
+ },
39
+ },
40
+ reducers: {
41
+ setSearchString(state, { payload }) {
42
+ return { ...state, searchString: payload };
43
+ },
44
+ setPagination(state, { payload }) {
45
+ return { ...state, pagination: { ...state.pagination, ...payload } };
46
+ },
47
+ },
48
+ };
web/src/hooks/fileManagerHooks.ts CHANGED
@@ -103,14 +103,14 @@ export const useUploadFile = () => {
103
  const dispatch = useDispatch();
104
 
105
  const uploadFile = useCallback(
106
- (file: UploadFile, parentId: string, path: string) => {
107
  try {
108
  return dispatch<any>({
109
  type: 'fileManager/uploadFile',
110
  payload: {
111
- file,
112
  parentId,
113
- path,
114
  },
115
  });
116
  } catch (errorInfo) {
 
103
  const dispatch = useDispatch();
104
 
105
  const uploadFile = useCallback(
106
+ (fileList: UploadFile[], parentId: string) => {
107
  try {
108
  return dispatch<any>({
109
  type: 'fileManager/uploadFile',
110
  payload: {
111
+ file: fileList,
112
  parentId,
113
+ path: fileList.map((file) => (file as any).webkitRelativePath),
114
  },
115
  });
116
  } catch (errorInfo) {
web/src/hooks/knowledgeHook.ts CHANGED
@@ -127,13 +127,13 @@ export const useFetchKnowledgeBaseConfiguration = () => {
127
 
128
  export const useFetchKnowledgeList = (
129
  shouldFilterListWithoutDocument: boolean = false,
130
- ): { list: IKnowledge[]; loading: boolean } => {
131
  const dispatch = useDispatch();
132
  const loading = useOneNamespaceEffectsLoading('knowledgeModel', ['getList']);
133
 
134
  const knowledgeModel = useSelector((state: any) => state.knowledgeModel);
135
  const { data = [] } = knowledgeModel;
136
- const list = useMemo(() => {
137
  return shouldFilterListWithoutDocument
138
  ? data.filter((x: IKnowledge) => x.chunk_num > 0)
139
  : data;
@@ -149,7 +149,7 @@ export const useFetchKnowledgeList = (
149
  fetchList();
150
  }, [fetchList]);
151
 
152
- return { list, loading };
153
  };
154
 
155
  export const useSelectFileThumbnails = () => {
 
127
 
128
  export const useFetchKnowledgeList = (
129
  shouldFilterListWithoutDocument: boolean = false,
130
+ ) => {
131
  const dispatch = useDispatch();
132
  const loading = useOneNamespaceEffectsLoading('knowledgeModel', ['getList']);
133
 
134
  const knowledgeModel = useSelector((state: any) => state.knowledgeModel);
135
  const { data = [] } = knowledgeModel;
136
+ const list: IKnowledge[] = useMemo(() => {
137
  return shouldFilterListWithoutDocument
138
  ? data.filter((x: IKnowledge) => x.chunk_num > 0)
139
  : data;
 
149
  fetchList();
150
  }, [fetchList]);
151
 
152
+ return { list, loading, fetchList };
153
  };
154
 
155
  export const useSelectFileThumbnails = () => {
web/src/hooks/logicHooks.ts CHANGED
@@ -1,9 +1,12 @@
1
  import { LanguageTranslationMap } from '@/constants/common';
 
2
  import { IKnowledgeFile } from '@/interfaces/database/knowledge';
3
  import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
4
- import { useCallback, useState } from 'react';
 
5
  import { useTranslation } from 'react-i18next';
6
- import { useSetModalState } from './commonHooks';
 
7
  import { useSetDocumentParser } from './documentHooks';
8
  import { useOneNamespaceEffectsLoading } from './storeHooks';
9
  import { useSaveSetting } from './userSettingHook';
@@ -62,3 +65,51 @@ export const useChangeLanguage = () => {
62
 
63
  return changeLanguage;
64
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import { LanguageTranslationMap } from '@/constants/common';
2
+ import { Pagination } from '@/interfaces/common';
3
  import { IKnowledgeFile } from '@/interfaces/database/knowledge';
4
  import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
5
+ import { PaginationProps } from 'antd';
6
+ import { useCallback, useMemo, useState } from 'react';
7
  import { useTranslation } from 'react-i18next';
8
+ import { useDispatch } from 'umi';
9
+ import { useSetModalState, useTranslate } from './commonHooks';
10
  import { useSetDocumentParser } from './documentHooks';
11
  import { useOneNamespaceEffectsLoading } from './storeHooks';
12
  import { useSaveSetting } from './userSettingHook';
 
65
 
66
  return changeLanguage;
67
  };
68
+
69
+ export const useGetPagination = (
70
+ total: number,
71
+ page: number,
72
+ pageSize: number,
73
+ onPageChange: PaginationProps['onChange'],
74
+ ) => {
75
+ const { t } = useTranslate('common');
76
+
77
+ const pagination: PaginationProps = useMemo(() => {
78
+ return {
79
+ showQuickJumper: true,
80
+ total,
81
+ showSizeChanger: true,
82
+ current: page,
83
+ pageSize: pageSize,
84
+ pageSizeOptions: [1, 2, 10, 20, 50, 100],
85
+ onChange: onPageChange,
86
+ showTotal: (total) => `${t('total')} ${total}`,
87
+ };
88
+ }, [t, onPageChange, page, pageSize, total]);
89
+
90
+ return {
91
+ pagination,
92
+ };
93
+ };
94
+
95
+ export const useSetPagination = (namespace: string) => {
96
+ const dispatch = useDispatch();
97
+
98
+ const setPagination = useCallback(
99
+ (pageNumber = 1, pageSize?: number) => {
100
+ const pagination: Pagination = {
101
+ current: pageNumber,
102
+ } as Pagination;
103
+ if (pageSize) {
104
+ pagination.pageSize = pageSize;
105
+ }
106
+ dispatch({
107
+ type: `${namespace}/setPagination`,
108
+ payload: pagination,
109
+ });
110
+ },
111
+ [dispatch, namespace],
112
+ );
113
+
114
+ return setPagination;
115
+ };
web/src/interfaces/common.ts CHANGED
@@ -1,6 +1,7 @@
1
  export interface Pagination {
2
  current: number;
3
  pageSize: number;
 
4
  }
5
 
6
  export interface BaseState {
 
1
  export interface Pagination {
2
  current: number;
3
  pageSize: number;
4
+ total: number;
5
  }
6
 
7
  export interface BaseState {
web/src/locales/en.ts CHANGED
@@ -70,7 +70,7 @@ export default {
70
  namePlaceholder: 'Please input name!',
71
  doc: 'Docs',
72
  datasetDescription:
73
- "Hey, don't forget to adjust the chunk after adding the dataset! 😉",
74
  addFile: 'Add file',
75
  searchFiles: 'Search your files',
76
  localFiles: 'Local files',
 
70
  namePlaceholder: 'Please input name!',
71
  doc: 'Docs',
72
  datasetDescription:
73
+ '😉 Questions and answers can only be answered after the parsing is successful.',
74
  addFile: 'Add file',
75
  searchFiles: 'Search your files',
76
  localFiles: 'Local files',
web/src/locales/zh-traditional.ts CHANGED
@@ -69,7 +69,7 @@ export default {
69
  name: '名稱',
70
  namePlaceholder: '請輸入名稱',
71
  doc: '文件',
72
- datasetDescription: '嘿,添加數據集後別忘了調整解析塊!😉',
73
  addFile: '新增文件',
74
  searchFiles: '搜索文件',
75
  localFiles: '本地文件',
 
69
  name: '名稱',
70
  namePlaceholder: '請輸入名稱',
71
  doc: '文件',
72
+ datasetDescription: '😉 解析成功後才能問答哦。',
73
  addFile: '新增文件',
74
  searchFiles: '搜索文件',
75
  localFiles: '本地文件',
web/src/locales/zh.ts CHANGED
@@ -69,7 +69,7 @@ export default {
69
  name: '名称',
70
  namePlaceholder: '请输入名称',
71
  doc: '文档',
72
- datasetDescription: '嘿,添加数据集后别忘了调整解析块! 😉',
73
  addFile: '新增文件',
74
  searchFiles: '搜索文件',
75
  localFiles: '本地文件',
 
69
  name: '名称',
70
  namePlaceholder: '请输入名称',
71
  doc: '文档',
72
+ datasetDescription: '😉 解析成功后才能问答哦。',
73
  addFile: '新增文件',
74
  searchFiles: '搜索文件',
75
  localFiles: '本地文件',
web/src/pages/file-manager/action-cell/index.tsx CHANGED
@@ -17,7 +17,8 @@ interface IProps {
17
  record: IFile;
18
  setCurrentRecord: (record: any) => void;
19
  showRenameModal: (record: IFile) => void;
20
- showConnectToKnowledgeModal: (ids: string[]) => void;
 
21
  }
22
 
23
  const ActionCell = ({
@@ -25,11 +26,15 @@ const ActionCell = ({
25
  setCurrentRecord,
26
  showRenameModal,
27
  showConnectToKnowledgeModal,
 
28
  }: IProps) => {
29
  const documentId = record.id;
30
  const beingUsed = false;
31
  const { t } = useTranslate('knowledgeDetails');
32
- const { handleRemoveFile } = useHandleDeleteFile([documentId]);
 
 
 
33
 
34
  const onDownloadDocument = () => {
35
  downloadFile({
@@ -48,7 +53,7 @@ const ActionCell = ({
48
  };
49
 
50
  const onShowConnectToKnowledgeModal = () => {
51
- showConnectToKnowledgeModal([documentId]);
52
  };
53
 
54
  return (
@@ -79,14 +84,16 @@ const ActionCell = ({
79
  >
80
  <DeleteOutlined size={20} />
81
  </Button>
82
- <Button
83
- type="text"
84
- disabled={beingUsed}
85
- onClick={onDownloadDocument}
86
- className={styles.iconButton}
87
- >
88
- <DownloadOutlined size={20} />
89
- </Button>
 
 
90
  </Space>
91
  );
92
  };
 
17
  record: IFile;
18
  setCurrentRecord: (record: any) => void;
19
  showRenameModal: (record: IFile) => void;
20
+ showConnectToKnowledgeModal: (record: IFile) => void;
21
+ setSelectedRowKeys(keys: string[]): void;
22
  }
23
 
24
  const ActionCell = ({
 
26
  setCurrentRecord,
27
  showRenameModal,
28
  showConnectToKnowledgeModal,
29
+ setSelectedRowKeys,
30
  }: IProps) => {
31
  const documentId = record.id;
32
  const beingUsed = false;
33
  const { t } = useTranslate('knowledgeDetails');
34
+ const { handleRemoveFile } = useHandleDeleteFile(
35
+ [documentId],
36
+ setSelectedRowKeys,
37
+ );
38
 
39
  const onDownloadDocument = () => {
40
  downloadFile({
 
53
  };
54
 
55
  const onShowConnectToKnowledgeModal = () => {
56
+ showConnectToKnowledgeModal(record);
57
  };
58
 
59
  return (
 
84
  >
85
  <DeleteOutlined size={20} />
86
  </Button>
87
+ {record.type !== 'folder' && (
88
+ <Button
89
+ type="text"
90
+ disabled={beingUsed}
91
+ onClick={onDownloadDocument}
92
+ className={styles.iconButton}
93
+ >
94
+ <DownloadOutlined size={20} />
95
+ </Button>
96
+ )}
97
  </Space>
98
  );
99
  };
web/src/pages/file-manager/connect-to-knowledge-modal/index.tsx CHANGED
@@ -1,14 +1,16 @@
1
  import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
2
  import { IModalProps } from '@/interfaces/common';
3
  import { Form, Modal, Select, SelectProps } from 'antd';
 
4
 
5
  const ConnectToKnowledgeModal = ({
6
  visible,
7
  hideModal,
8
  onOk,
9
- }: IModalProps<string[]>) => {
 
10
  const [form] = Form.useForm();
11
- const { list } = useFetchKnowledgeList();
12
 
13
  const options: SelectProps['options'] = list?.map((item) => ({
14
  label: item.name,
@@ -18,11 +20,16 @@ const ConnectToKnowledgeModal = ({
18
  const handleOk = async () => {
19
  const values = await form.getFieldsValue();
20
  const knowledgeIds = values.knowledgeIds ?? [];
21
- if (knowledgeIds.length > 0) {
22
- return onOk?.(knowledgeIds);
23
- }
24
  };
25
 
 
 
 
 
 
 
 
26
  return (
27
  <Modal
28
  title="Add to Knowledge Base"
@@ -31,17 +38,7 @@ const ConnectToKnowledgeModal = ({
31
  onCancel={hideModal}
32
  >
33
  <Form form={form}>
34
- <Form.Item
35
- name="knowledgeIds"
36
- noStyle
37
- rules={[
38
- {
39
- required: true,
40
- message: 'Please select your favourite colors!',
41
- type: 'array',
42
- },
43
- ]}
44
- >
45
  <Select
46
  mode="multiple"
47
  allowClear
 
1
  import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
2
  import { IModalProps } from '@/interfaces/common';
3
  import { Form, Modal, Select, SelectProps } from 'antd';
4
+ import { useEffect } from 'react';
5
 
6
  const ConnectToKnowledgeModal = ({
7
  visible,
8
  hideModal,
9
  onOk,
10
+ initialValue,
11
+ }: IModalProps<string[]> & { initialValue: string[] }) => {
12
  const [form] = Form.useForm();
13
+ const { list, fetchList } = useFetchKnowledgeList();
14
 
15
  const options: SelectProps['options'] = list?.map((item) => ({
16
  label: item.name,
 
20
  const handleOk = async () => {
21
  const values = await form.getFieldsValue();
22
  const knowledgeIds = values.knowledgeIds ?? [];
23
+ return onOk?.(knowledgeIds);
 
 
24
  };
25
 
26
+ useEffect(() => {
27
+ if (visible) {
28
+ form.setFieldValue('knowledgeIds', initialValue);
29
+ fetchList();
30
+ }
31
+ }, [visible, fetchList, initialValue, form]);
32
+
33
  return (
34
  <Modal
35
  title="Add to Knowledge Base"
 
38
  onCancel={hideModal}
39
  >
40
  <Form form={form}>
41
+ <Form.Item name="knowledgeIds" noStyle>
 
 
 
 
 
 
 
 
 
 
42
  <Select
43
  mode="multiple"
44
  allowClear
web/src/pages/file-manager/file-toolbar.tsx CHANGED
@@ -20,7 +20,6 @@ import {
20
  import { useMemo } from 'react';
21
  import {
22
  useFetchDocumentListOnMount,
23
- useGetPagination,
24
  useHandleDeleteFile,
25
  useHandleSearchChange,
26
  useSelectBreadcrumbItems,
@@ -33,6 +32,7 @@ interface IProps {
33
  selectedRowKeys: string[];
34
  showFolderCreateModal: () => void;
35
  showFileUploadModal: () => void;
 
36
  }
37
 
38
  const itemRender: BreadcrumbProps['itemRender'] = (
@@ -53,11 +53,11 @@ const FileToolbar = ({
53
  selectedRowKeys,
54
  showFolderCreateModal,
55
  showFileUploadModal,
 
56
  }: IProps) => {
57
  const { t } = useTranslate('knowledgeDetails');
58
- const { fetchDocumentList } = useFetchDocumentListOnMount();
59
- const { setPagination, searchString } = useGetPagination(fetchDocumentList);
60
- const { handleInputChange } = useHandleSearchChange(setPagination);
61
  const breadcrumbItems = useSelectBreadcrumbItems();
62
 
63
  const actionItems: MenuProps['items'] = useMemo(() => {
@@ -93,7 +93,10 @@ const FileToolbar = ({
93
  ];
94
  }, [t, showFolderCreateModal, showFileUploadModal]);
95
 
96
- const { handleRemoveFile } = useHandleDeleteFile(selectedRowKeys);
 
 
 
97
 
98
  const disabled = selectedRowKeys.length === 0;
99
 
 
20
  import { useMemo } from 'react';
21
  import {
22
  useFetchDocumentListOnMount,
 
23
  useHandleDeleteFile,
24
  useHandleSearchChange,
25
  useSelectBreadcrumbItems,
 
32
  selectedRowKeys: string[];
33
  showFolderCreateModal: () => void;
34
  showFileUploadModal: () => void;
35
+ setSelectedRowKeys: (keys: string[]) => void;
36
  }
37
 
38
  const itemRender: BreadcrumbProps['itemRender'] = (
 
53
  selectedRowKeys,
54
  showFolderCreateModal,
55
  showFileUploadModal,
56
+ setSelectedRowKeys,
57
  }: IProps) => {
58
  const { t } = useTranslate('knowledgeDetails');
59
+ useFetchDocumentListOnMount();
60
+ const { handleInputChange, searchString } = useHandleSearchChange();
 
61
  const breadcrumbItems = useSelectBreadcrumbItems();
62
 
63
  const actionItems: MenuProps['items'] = useMemo(() => {
 
93
  ];
94
  }, [t, showFolderCreateModal, showFileUploadModal]);
95
 
96
+ const { handleRemoveFile } = useHandleDeleteFile(
97
+ selectedRowKeys,
98
+ setSelectedRowKeys,
99
+ );
100
 
101
  const disabled = selectedRowKeys.length === 0;
102
 
web/src/pages/file-manager/file-upload-modal/index.tsx CHANGED
@@ -11,7 +11,6 @@ import {
11
  UploadProps,
12
  } from 'antd';
13
  import { Dispatch, SetStateAction, useState } from 'react';
14
- import { useHandleUploadFile } from '../hooks';
15
 
16
  const { Dragger } = Upload;
17
 
@@ -59,14 +58,18 @@ const FileUpload = ({
59
  );
60
  };
61
 
62
- const FileUploadModal = ({ visible, hideModal }: IModalProps<any>) => {
 
 
 
 
 
63
  const [value, setValue] = useState<string | number>('local');
64
- const { onFileUploadOk, fileUploadLoading } = useHandleUploadFile();
65
  const [fileList, setFileList] = useState<UploadFile[]>([]);
66
  const [directoryFileList, setDirectoryFileList] = useState<UploadFile[]>([]);
67
 
68
  const onOk = () => {
69
- onFileUploadOk([...fileList, ...directoryFileList]);
70
  };
71
 
72
  const items: TabsProps['items'] = [
@@ -101,7 +104,7 @@ const FileUploadModal = ({ visible, hideModal }: IModalProps<any>) => {
101
  open={visible}
102
  onOk={onOk}
103
  onCancel={hideModal}
104
- confirmLoading={fileUploadLoading}
105
  >
106
  <Flex gap={'large'} vertical>
107
  <Segmented
 
11
  UploadProps,
12
  } from 'antd';
13
  import { Dispatch, SetStateAction, useState } from 'react';
 
14
 
15
  const { Dragger } = Upload;
16
 
 
58
  );
59
  };
60
 
61
+ const FileUploadModal = ({
62
+ visible,
63
+ hideModal,
64
+ loading,
65
+ onOk: onFileUploadOk,
66
+ }: IModalProps<UploadFile[]>) => {
67
  const [value, setValue] = useState<string | number>('local');
 
68
  const [fileList, setFileList] = useState<UploadFile[]>([]);
69
  const [directoryFileList, setDirectoryFileList] = useState<UploadFile[]>([]);
70
 
71
  const onOk = () => {
72
+ return onFileUploadOk?.([...fileList, ...directoryFileList]);
73
  };
74
 
75
  const items: TabsProps['items'] = [
 
104
  open={visible}
105
  onOk={onOk}
106
  onCancel={hideModal}
107
+ confirmLoading={loading}
108
  >
109
  <Flex gap={'large'} vertical>
110
  <Segmented
web/src/pages/file-manager/hooks.ts CHANGED
@@ -1,8 +1,4 @@
1
- import {
2
- useSetModalState,
3
- useShowDeleteConfirm,
4
- useTranslate,
5
- } from '@/hooks/commonHooks';
6
  import {
7
  useConnectToKnowledge,
8
  useCreateFolder,
@@ -14,10 +10,9 @@ import {
14
  useSelectParentFolderList,
15
  useUploadFile,
16
  } from '@/hooks/fileManagerHooks';
 
17
  import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
18
- import { Pagination } from '@/interfaces/common';
19
  import { IFile } from '@/interfaces/database/file-manager';
20
- import { getFilePathByWebkitRelativePath } from '@/utils/fileUtil';
21
  import { PaginationProps } from 'antd';
22
  import { UploadFile } from 'antd/lib';
23
  import { useCallback, useEffect, useMemo, useState } from 'react';
@@ -27,95 +22,72 @@ export const useGetFolderId = () => {
27
  const [searchParams] = useSearchParams();
28
  const id = searchParams.get('folderId') as string;
29
 
30
- return id;
31
  };
32
 
33
  export const useFetchDocumentListOnMount = () => {
34
  const fetchDocumentList = useFetchFileList();
35
  const fileList = useSelectFileList();
36
  const id = useGetFolderId();
 
 
 
 
37
 
38
  const dispatch = useDispatch();
39
 
40
  useEffect(() => {
41
- fetchDocumentList({ parent_id: id });
42
- }, [dispatch, fetchDocumentList, id]);
 
 
 
 
 
43
 
44
  return { fetchDocumentList, fileList };
45
  };
46
 
47
- export const useGetPagination = (
48
- fetchDocumentList: (payload: IFile) => any,
49
- ) => {
50
- const dispatch = useDispatch();
51
- const kFModel = useSelector((state: any) => state.kFModel);
52
- const { t } = useTranslate('common');
53
-
54
- const setPagination = useCallback(
55
- (pageNumber = 1, pageSize?: number) => {
56
- const pagination: Pagination = {
57
- current: pageNumber,
58
- } as Pagination;
59
- if (pageSize) {
60
- pagination.pageSize = pageSize;
61
- }
62
- dispatch({
63
- type: 'kFModel/setPagination',
64
- payload: pagination,
65
- });
66
- },
67
- [dispatch],
68
- );
69
 
70
  const onPageChange: PaginationProps['onChange'] = useCallback(
71
  (pageNumber: number, pageSize: number) => {
72
  setPagination(pageNumber, pageSize);
73
- fetchDocumentList();
74
  },
75
- [fetchDocumentList, setPagination],
76
  );
77
 
78
- const pagination: PaginationProps = useMemo(() => {
79
- return {
80
- showQuickJumper: true,
81
- total: kFModel.total,
82
- showSizeChanger: true,
83
- current: kFModel.pagination.current,
84
- pageSize: kFModel.pagination.pageSize,
85
- pageSizeOptions: [1, 2, 10, 20, 50, 100],
86
- onChange: onPageChange,
87
- showTotal: (total) => `${t('total')} ${total}`,
88
- };
89
- }, [kFModel, onPageChange, t]);
90
 
91
  return {
92
- pagination,
93
  setPagination,
94
- total: kFModel.total,
95
- searchString: kFModel.searchString,
96
  };
97
  };
98
 
99
- export const useHandleSearchChange = (setPagination: () => void) => {
100
  const dispatch = useDispatch();
101
-
102
- const throttledGetDocumentList = useCallback(() => {
103
- dispatch({
104
- type: 'kFModel/throttledGetDocumentList',
105
- });
106
- }, [dispatch]);
107
 
108
  const handleInputChange = useCallback(
109
  (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
110
  const value = e.target.value;
111
- dispatch({ type: 'kFModel/setSearchString', payload: value });
112
  setPagination();
113
- throttledGetDocumentList();
114
  },
115
- [setPagination, throttledGetDocumentList, dispatch],
116
  );
117
 
118
- return { handleInputChange };
119
  };
120
 
121
  export const useGetRowSelection = () => {
@@ -128,7 +100,7 @@ export const useGetRowSelection = () => {
128
  },
129
  };
130
 
131
- return rowSelection;
132
  };
133
 
134
  export const useNavigateToOtherFolder = () => {
@@ -235,15 +207,22 @@ export const useHandleCreateFolder = () => {
235
  };
236
  };
237
 
238
- export const useHandleDeleteFile = (fileIds: string[]) => {
 
 
 
239
  const removeDocument = useRemoveFile();
240
  const showDeleteConfirm = useShowDeleteConfirm();
241
  const parentId = useGetFolderId();
242
 
243
  const handleRemoveFile = () => {
244
  showDeleteConfirm({
245
- onOk: () => {
246
- return removeDocument(fileIds, parentId);
 
 
 
 
247
  },
248
  });
249
  };
@@ -268,12 +247,8 @@ export const useHandleUploadFile = () => {
268
  async (fileList: UploadFile[]) => {
269
  console.info('fileList', fileList);
270
  if (fileList.length > 0) {
271
- const ret = await uploadFile(
272
- fileList[0],
273
- id,
274
- getFilePathByWebkitRelativePath(fileList[0] as any),
275
- );
276
-
277
  if (ret === 0) {
278
  hideFileUploadModal();
279
  }
@@ -301,13 +276,19 @@ export const useHandleConnectToKnowledge = () => {
301
  } = useSetModalState();
302
  const connectToKnowledge = useConnectToKnowledge();
303
  const id = useGetFolderId();
304
- const [fileIds, setFileIds] = useState<string[]>([]);
 
 
 
 
 
 
305
 
306
  const onConnectToKnowledgeOk = useCallback(
307
  async (knowledgeIds: string[]) => {
308
  const ret = await connectToKnowledge({
309
  parentId: id,
310
- fileIds,
311
  kbIds: knowledgeIds,
312
  });
313
 
@@ -315,7 +296,7 @@ export const useHandleConnectToKnowledge = () => {
315
  hideConnectToKnowledgeModal();
316
  }
317
  },
318
- [connectToKnowledge, hideConnectToKnowledgeModal, id, fileIds],
319
  );
320
 
321
  const loading = useOneNamespaceEffectsLoading('fileManager', [
@@ -323,14 +304,15 @@ export const useHandleConnectToKnowledge = () => {
323
  ]);
324
 
325
  const handleShowConnectToKnowledgeModal = useCallback(
326
- (ids: string[]) => {
327
- setFileIds(ids);
328
  showConnectToKnowledgeModal();
329
  },
330
  [showConnectToKnowledgeModal],
331
  );
332
 
333
  return {
 
334
  connectToKnowledgeLoading: loading,
335
  onConnectToKnowledgeOk,
336
  connectToKnowledgeVisible,
 
1
+ import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks';
 
 
 
 
2
  import {
3
  useConnectToKnowledge,
4
  useCreateFolder,
 
10
  useSelectParentFolderList,
11
  useUploadFile,
12
  } from '@/hooks/fileManagerHooks';
13
+ import { useGetPagination, useSetPagination } from '@/hooks/logicHooks';
14
  import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
 
15
  import { IFile } from '@/interfaces/database/file-manager';
 
16
  import { PaginationProps } from 'antd';
17
  import { UploadFile } from 'antd/lib';
18
  import { useCallback, useEffect, useMemo, useState } from 'react';
 
22
  const [searchParams] = useSearchParams();
23
  const id = searchParams.get('folderId') as string;
24
 
25
+ return id ?? '';
26
  };
27
 
28
  export const useFetchDocumentListOnMount = () => {
29
  const fetchDocumentList = useFetchFileList();
30
  const fileList = useSelectFileList();
31
  const id = useGetFolderId();
32
+ const { searchString, pagination } = useSelector(
33
+ (state) => state.fileManager,
34
+ );
35
+ const { pageSize, current } = pagination;
36
 
37
  const dispatch = useDispatch();
38
 
39
  useEffect(() => {
40
+ fetchDocumentList({
41
+ parent_id: id,
42
+ keywords: searchString,
43
+ page_size: pageSize,
44
+ page: current,
45
+ });
46
+ }, [dispatch, fetchDocumentList, id, current, pageSize, searchString]);
47
 
48
  return { fetchDocumentList, fileList };
49
  };
50
 
51
+ export const useGetFilesPagination = () => {
52
+ const { pagination } = useSelector((state) => state.fileManager);
53
+
54
+ const setPagination = useSetPagination('fileManager');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  const onPageChange: PaginationProps['onChange'] = useCallback(
57
  (pageNumber: number, pageSize: number) => {
58
  setPagination(pageNumber, pageSize);
 
59
  },
60
+ [setPagination],
61
  );
62
 
63
+ const { pagination: paginationInfo } = useGetPagination(
64
+ pagination.total,
65
+ pagination.current,
66
+ pagination.pageSize,
67
+ onPageChange,
68
+ );
 
 
 
 
 
 
69
 
70
  return {
71
+ pagination: paginationInfo,
72
  setPagination,
 
 
73
  };
74
  };
75
 
76
+ export const useHandleSearchChange = () => {
77
  const dispatch = useDispatch();
78
+ const { searchString } = useSelector((state) => state.fileManager);
79
+ const setPagination = useSetPagination('fileManager');
 
 
 
 
80
 
81
  const handleInputChange = useCallback(
82
  (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
83
  const value = e.target.value;
84
+ dispatch({ type: 'fileManager/setSearchString', payload: value });
85
  setPagination();
 
86
  },
87
+ [setPagination, dispatch],
88
  );
89
 
90
+ return { handleInputChange, searchString };
91
  };
92
 
93
  export const useGetRowSelection = () => {
 
100
  },
101
  };
102
 
103
+ return { rowSelection, setSelectedRowKeys };
104
  };
105
 
106
  export const useNavigateToOtherFolder = () => {
 
207
  };
208
  };
209
 
210
+ export const useHandleDeleteFile = (
211
+ fileIds: string[],
212
+ setSelectedRowKeys: (keys: string[]) => void,
213
+ ) => {
214
  const removeDocument = useRemoveFile();
215
  const showDeleteConfirm = useShowDeleteConfirm();
216
  const parentId = useGetFolderId();
217
 
218
  const handleRemoveFile = () => {
219
  showDeleteConfirm({
220
+ onOk: async () => {
221
+ const retcode = await removeDocument(fileIds, parentId);
222
+ if (retcode === 0) {
223
+ setSelectedRowKeys([]);
224
+ }
225
+ return;
226
  },
227
  });
228
  };
 
247
  async (fileList: UploadFile[]) => {
248
  console.info('fileList', fileList);
249
  if (fileList.length > 0) {
250
+ const ret = await uploadFile(fileList, id);
251
+ console.info(ret);
 
 
 
 
252
  if (ret === 0) {
253
  hideFileUploadModal();
254
  }
 
276
  } = useSetModalState();
277
  const connectToKnowledge = useConnectToKnowledge();
278
  const id = useGetFolderId();
279
+ const [record, setRecord] = useState<IFile>({} as IFile);
280
+
281
+ const initialValue = useMemo(() => {
282
+ return Array.isArray(record?.kbs_info)
283
+ ? record?.kbs_info?.map((x) => x.kb_id)
284
+ : [];
285
+ }, [record?.kbs_info]);
286
 
287
  const onConnectToKnowledgeOk = useCallback(
288
  async (knowledgeIds: string[]) => {
289
  const ret = await connectToKnowledge({
290
  parentId: id,
291
+ fileIds: [record.id],
292
  kbIds: knowledgeIds,
293
  });
294
 
 
296
  hideConnectToKnowledgeModal();
297
  }
298
  },
299
+ [connectToKnowledge, hideConnectToKnowledgeModal, id, record.id],
300
  );
301
 
302
  const loading = useOneNamespaceEffectsLoading('fileManager', [
 
304
  ]);
305
 
306
  const handleShowConnectToKnowledgeModal = useCallback(
307
+ (record: IFile) => {
308
+ setRecord(record);
309
  showConnectToKnowledgeModal();
310
  },
311
  [showConnectToKnowledgeModal],
312
  );
313
 
314
  return {
315
+ initialValue,
316
  connectToKnowledgeLoading: loading,
317
  onConnectToKnowledgeOk,
318
  connectToKnowledgeVisible,
web/src/pages/file-manager/index.less CHANGED
@@ -16,3 +16,7 @@
16
  width: 22px;
17
  text-align: center;
18
  }
 
 
 
 
 
16
  width: 22px;
17
  text-align: center;
18
  }
19
+
20
+ .linkButton {
21
+ padding: 0;
22
+ }
web/src/pages/file-manager/index.tsx CHANGED
@@ -1,11 +1,12 @@
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
  useHandleConnectToKnowledge,
11
  useHandleCreateFolder,
@@ -16,6 +17,8 @@ import {
16
  } from './hooks';
17
 
18
  import RenameModal from '@/components/rename-modal';
 
 
19
  import ConnectToKnowledgeModal from './connect-to-knowledge-modal';
20
  import FileUploadModal from './file-upload-modal';
21
  import FolderCreateModal from './folder-create-modal';
@@ -23,7 +26,7 @@ import styles from './index.less';
23
 
24
  const FileManager = () => {
25
  const fileList = useSelectFileList();
26
- const rowSelection = useGetRowSelection();
27
  const loading = useSelectFileListLoading();
28
  const navigateToOtherFolder = useNavigateToOtherFolder();
29
  const {
@@ -41,14 +44,21 @@ const FileManager = () => {
41
  folderCreateLoading,
42
  onFolderCreateOk,
43
  } = useHandleCreateFolder();
44
- const { fileUploadVisible, hideFileUploadModal, showFileUploadModal } =
45
- useHandleUploadFile();
 
 
 
 
 
46
  const {
47
  connectToKnowledgeVisible,
48
  hideConnectToKnowledgeModal,
49
  showConnectToKnowledgeModal,
50
  onConnectToKnowledgeOk,
 
51
  } = useHandleConnectToKnowledge();
 
52
 
53
  const columns: ColumnsType<IFile> = [
54
  {
@@ -56,15 +66,24 @@ const FileManager = () => {
56
  dataIndex: 'name',
57
  key: 'name',
58
  render(value, record) {
59
- return record.type === 'folder' ? (
60
- <Button
61
- type={'link'}
62
- onClick={() => navigateToOtherFolder(record.id)}
63
- >
64
- {value}
65
- </Button>
66
- ) : (
67
- value
 
 
 
 
 
 
 
 
 
68
  );
69
  },
70
  },
@@ -77,11 +96,10 @@ const FileManager = () => {
77
  },
78
  },
79
  {
80
- title: 'kbs_info',
81
  dataIndex: 'kbs_info',
82
  key: 'kbs_info',
83
  render(value) {
84
- console.info(value);
85
  return Array.isArray(value)
86
  ? value?.map((x) => x.kb_name).join(',')
87
  : '';
@@ -104,6 +122,7 @@ const FileManager = () => {
104
  }}
105
  showRenameModal={showFileRenameModal}
106
  showConnectToKnowledgeModal={showConnectToKnowledgeModal}
 
107
  ></ActionCell>
108
  ),
109
  },
@@ -115,6 +134,7 @@ const FileManager = () => {
115
  selectedRowKeys={rowSelection.selectedRowKeys as string[]}
116
  showFolderCreateModal={showFolderCreateModal}
117
  showFileUploadModal={showFileUploadModal}
 
118
  ></FileToolbar>
119
  <Table
120
  dataSource={fileList}
@@ -122,6 +142,7 @@ const FileManager = () => {
122
  rowKey={'id'}
123
  rowSelection={rowSelection}
124
  loading={loading}
 
125
  />
126
  <RenameModal
127
  visible={fileRenameVisible}
@@ -139,8 +160,11 @@ const FileManager = () => {
139
  <FileUploadModal
140
  visible={fileUploadVisible}
141
  hideModal={hideFileUploadModal}
 
 
142
  ></FileUploadModal>
143
  <ConnectToKnowledgeModal
 
144
  visible={connectToKnowledgeVisible}
145
  hideModal={hideConnectToKnowledgeModal}
146
  onOk={onConnectToKnowledgeOk}
 
1
  import { useSelectFileList } from '@/hooks/fileManagerHooks';
2
  import { IFile } from '@/interfaces/database/file-manager';
3
  import { formatDate } from '@/utils/date';
4
+ import { Button, Flex, 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
+ useGetFilesPagination,
10
  useGetRowSelection,
11
  useHandleConnectToKnowledge,
12
  useHandleCreateFolder,
 
17
  } from './hooks';
18
 
19
  import RenameModal from '@/components/rename-modal';
20
+ import SvgIcon from '@/components/svg-icon';
21
+ import { getExtension } from '@/utils/documentUtils';
22
  import ConnectToKnowledgeModal from './connect-to-knowledge-modal';
23
  import FileUploadModal from './file-upload-modal';
24
  import FolderCreateModal from './folder-create-modal';
 
26
 
27
  const FileManager = () => {
28
  const fileList = useSelectFileList();
29
+ const { rowSelection, setSelectedRowKeys } = useGetRowSelection();
30
  const loading = useSelectFileListLoading();
31
  const navigateToOtherFolder = useNavigateToOtherFolder();
32
  const {
 
44
  folderCreateLoading,
45
  onFolderCreateOk,
46
  } = useHandleCreateFolder();
47
+ const {
48
+ fileUploadVisible,
49
+ hideFileUploadModal,
50
+ showFileUploadModal,
51
+ fileUploadLoading,
52
+ onFileUploadOk,
53
+ } = useHandleUploadFile();
54
  const {
55
  connectToKnowledgeVisible,
56
  hideConnectToKnowledgeModal,
57
  showConnectToKnowledgeModal,
58
  onConnectToKnowledgeOk,
59
+ initialValue,
60
  } = useHandleConnectToKnowledge();
61
+ const { pagination } = useGetFilesPagination();
62
 
63
  const columns: ColumnsType<IFile> = [
64
  {
 
66
  dataIndex: 'name',
67
  key: 'name',
68
  render(value, record) {
69
+ return (
70
+ <Flex gap={10} align="center">
71
+ <SvgIcon
72
+ name={`file-icon/${record.type === 'folder' ? 'folder' : getExtension(value)}`}
73
+ width={24}
74
+ ></SvgIcon>
75
+ {record.type === 'folder' ? (
76
+ <Button
77
+ type={'link'}
78
+ className={styles.linkButton}
79
+ onClick={() => navigateToOtherFolder(record.id)}
80
+ >
81
+ {value}
82
+ </Button>
83
+ ) : (
84
+ value
85
+ )}
86
+ </Flex>
87
  );
88
  },
89
  },
 
96
  },
97
  },
98
  {
99
+ title: 'Knowledge Base',
100
  dataIndex: 'kbs_info',
101
  key: 'kbs_info',
102
  render(value) {
 
103
  return Array.isArray(value)
104
  ? value?.map((x) => x.kb_name).join(',')
105
  : '';
 
122
  }}
123
  showRenameModal={showFileRenameModal}
124
  showConnectToKnowledgeModal={showConnectToKnowledgeModal}
125
+ setSelectedRowKeys={setSelectedRowKeys}
126
  ></ActionCell>
127
  ),
128
  },
 
134
  selectedRowKeys={rowSelection.selectedRowKeys as string[]}
135
  showFolderCreateModal={showFolderCreateModal}
136
  showFileUploadModal={showFileUploadModal}
137
+ setSelectedRowKeys={setSelectedRowKeys}
138
  ></FileToolbar>
139
  <Table
140
  dataSource={fileList}
 
142
  rowKey={'id'}
143
  rowSelection={rowSelection}
144
  loading={loading}
145
+ pagination={pagination}
146
  />
147
  <RenameModal
148
  visible={fileRenameVisible}
 
160
  <FileUploadModal
161
  visible={fileUploadVisible}
162
  hideModal={hideFileUploadModal}
163
+ loading={fileUploadLoading}
164
+ onOk={onFileUploadOk}
165
  ></FileUploadModal>
166
  <ConnectToKnowledgeModal
167
+ initialValue={initialValue}
168
  visible={connectToKnowledgeVisible}
169
  hideModal={hideConnectToKnowledgeModal}
170
  onOk={onConnectToKnowledgeOk}
web/src/pages/file-manager/model.ts CHANGED
@@ -1,16 +1,22 @@
 
 
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 {
7
  fileList: IFile[];
8
  parentFolderList: IFolder[];
9
  }
10
 
11
  const model: DvaModel<FileManagerModelState> = {
12
  namespace: 'fileManager',
13
- state: { fileList: [], parentFolderList: [] },
 
 
 
 
14
  reducers: {
15
  setFileList(state, { payload }) {
16
  return { ...state, fileList: payload };
@@ -18,6 +24,7 @@ const model: DvaModel<FileManagerModelState> = {
18
  setParentFolderList(state, { payload }) {
19
  return { ...state, parentFolderList: payload };
20
  },
 
21
  },
22
  effects: {
23
  *removeFile({ payload = {} }, { call, put }) {
@@ -31,16 +38,29 @@ const model: DvaModel<FileManagerModelState> = {
31
  payload: { parentId: payload.parentId },
32
  });
33
  }
 
34
  },
35
- *listFile({ payload = {} }, { call, put }) {
36
- const { data } = yield call(fileManagerService.listFile, payload);
 
 
 
 
 
 
 
 
 
37
  const { retcode, data: res } = data;
38
-
39
  if (retcode === 0 && Array.isArray(res.files)) {
40
  yield put({
41
  type: 'setFileList',
42
  payload: res.files,
43
  });
 
 
 
 
44
  }
45
  },
46
  *renameFile({ payload = {} }, { call, put }) {
@@ -57,10 +77,16 @@ const model: DvaModel<FileManagerModelState> = {
57
  return data.retcode;
58
  },
59
  *uploadFile({ payload = {} }, { call, put }) {
 
 
60
  const formData = new FormData();
61
  formData.append('parent_id', payload.parentId);
62
- formData.append('file', payload.file);
63
- formData.append('path', payload.path);
 
 
 
 
64
  const { data } = yield call(fileManagerService.uploadFile, formData);
65
  if (data.retcode === 0) {
66
  yield put({
@@ -108,4 +134,12 @@ const model: DvaModel<FileManagerModelState> = {
108
  },
109
  },
110
  };
 
 
 
 
 
 
 
 
111
  export default model;
 
1
+ import { paginationModel } from '@/base';
2
+ import { BaseState } from '@/interfaces/common';
3
  import { IFile, IFolder } from '@/interfaces/database/file-manager';
4
  import fileManagerService from '@/services/fileManagerService';
5
  import omit from 'lodash/omit';
6
  import { DvaModel } from 'umi';
7
 
8
+ export interface FileManagerModelState extends BaseState {
9
  fileList: IFile[];
10
  parentFolderList: IFolder[];
11
  }
12
 
13
  const model: DvaModel<FileManagerModelState> = {
14
  namespace: 'fileManager',
15
+ state: {
16
+ fileList: [],
17
+ parentFolderList: [],
18
+ ...(paginationModel.state as BaseState),
19
+ },
20
  reducers: {
21
  setFileList(state, { payload }) {
22
  return { ...state, fileList: payload };
 
24
  setParentFolderList(state, { payload }) {
25
  return { ...state, parentFolderList: payload };
26
  },
27
+ ...paginationModel.reducers,
28
  },
29
  effects: {
30
  *removeFile({ payload = {} }, { call, put }) {
 
38
  payload: { parentId: payload.parentId },
39
  });
40
  }
41
+ return retcode;
42
  },
43
+ *listFile({ payload = {} }, { call, put, select }) {
44
+ const { searchString, pagination } = yield select(
45
+ (state: any) => state.fileManager,
46
+ );
47
+ const { current, pageSize } = pagination;
48
+ const { data } = yield call(fileManagerService.listFile, {
49
+ ...payload,
50
+ keywords: searchString.trim(),
51
+ page: current,
52
+ pageSize,
53
+ });
54
  const { retcode, data: res } = data;
 
55
  if (retcode === 0 && Array.isArray(res.files)) {
56
  yield put({
57
  type: 'setFileList',
58
  payload: res.files,
59
  });
60
+ yield put({
61
+ type: 'setPagination',
62
+ payload: { total: res.total },
63
+ });
64
  }
65
  },
66
  *renameFile({ payload = {} }, { call, put }) {
 
77
  return data.retcode;
78
  },
79
  *uploadFile({ payload = {} }, { call, put }) {
80
+ const fileList = payload.file;
81
+ const pathList = payload.path;
82
  const formData = new FormData();
83
  formData.append('parent_id', payload.parentId);
84
+ // formData.append('file', payload.file);
85
+ // formData.append('path', payload.path);
86
+ fileList.forEach((file: any, index: number) => {
87
+ formData.append('file', file);
88
+ formData.append('path', pathList[index]);
89
+ });
90
  const { data } = yield call(fileManagerService.uploadFile, formData);
91
  if (data.retcode === 0) {
92
  yield put({
 
134
  },
135
  },
136
  };
137
+
138
+ // const finalModel = modelExtend<DvaModel<FileManagerModelState & BaseState>>(
139
+ // paginationModel,
140
+ // model,
141
+ // );
142
+
143
+ // console.info(finalModel);
144
+
145
  export default model;
web/src/pages/knowledge/model.ts CHANGED
@@ -3,7 +3,7 @@ import kbService from '@/services/kbService';
3
  import { DvaModel } from 'umi';
4
 
5
  export interface KnowledgeModelState {
6
- data: any[];
7
  knowledge: IKnowledge;
8
  }
9
 
 
3
  import { DvaModel } from 'umi';
4
 
5
  export interface KnowledgeModelState {
6
+ data: IKnowledge[];
7
  knowledge: IKnowledge;
8
  }
9
 
web/src/utils/fileUtil.ts CHANGED
@@ -85,12 +85,3 @@ export const downloadFile = ({
85
  downloadElement.click();
86
  document.body.removeChild(downloadElement);
87
  };
88
-
89
- export const getFilePathByWebkitRelativePath = (file: File) => {
90
- const path = file.webkitRelativePath;
91
- return path;
92
- // if (path !== '') {
93
- // return path.slice(0, path.lastIndexOf('/'));
94
- // }
95
- // return path;
96
- };
 
85
  downloadElement.click();
86
  document.body.removeChild(downloadElement);
87
  };