Browse Source

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

### 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)
tags/v0.3.1
balibabu 1 year ago
parent
commit
1dcd439c58
No account linked to committer's email address

+ 18
- 0
web/src/assets/svg/file-icon/folder.svg View File

<svg width="24" height="18" viewBox="0 0 24 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M1.32202e-08 2.54731L21.5 2.54731C22.8807 2.54731 24 3.4977 24 4.67006L24 15.2838C24 16.4562 22.8807 17.4066 21.5 17.4066L12 17.4066L2.5 17.4066C1.11929 17.4066 8.54054e-08 16.4562 7.9321e-08 15.2838L1.32202e-08 2.54731Z"
fill="#FBBC1A" />
<path
d="M2.97454e-08 5.73144L7.49143e-08 14.4347C8.09987e-08 15.6071 1.11929 16.5575 2.5 16.5575L21.5 16.5575C22.8807 16.5575 24 15.6071 24 14.4347L24 5.51916C24 4.3468 22.8807 3.39641 21.5 3.39641L11 3.39641L11 4.45779C11 5.16121 10.3284 5.73144 9.5 5.73144L2.97454e-08 5.73144Z"
fill="url(#paint0_linear_2323_8307)" />
<path
d="M8.81345e-09 1.6982C3.94591e-09 0.760312 0.89543 -4.64716e-09 2 -1.03797e-08L9 -4.67088e-08C10.1046 -5.24413e-08 11 0.760312 11 1.6982L11 2.54731L1.32202e-08 2.54731L8.81345e-09 1.6982Z"
fill="#FBBC1A" />
<defs>
<linearGradient id="paint0_linear_2323_8307" x1="0" y1="0" x2="28.8004" y2="20.3231"
gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE69C" />
<stop offset="1" stop-color="#FFC937" />
</linearGradient>
</defs>
</svg>

+ 48
- 0
web/src/base.ts View File

import isObject from 'lodash/isObject';
import { DvaModel } from 'umi';
import { BaseState } from './interfaces/common';

type State = Record<string, any>;
type DvaModelKey<T> = keyof DvaModel<T>;

export const modelExtend = <T>(
baseModel: Partial<DvaModel<any>>,
extendModel: DvaModel<any>,
): DvaModel<T> => {
return Object.keys(extendModel).reduce<DvaModel<T>>((pre, cur) => {
const baseValue = baseModel[cur as DvaModelKey<State>];
const value = extendModel[cur as DvaModelKey<State>];

if (isObject(value) && isObject(baseValue) && typeof value !== 'string') {
const key = cur as Exclude<DvaModelKey<State>, 'namespace'>;

pre[key] = {
...baseValue,
...value,
} as any;
} else {
pre[cur as DvaModelKey<State>] = value as any;
}

return pre;
}, {} as DvaModel<T>);
};

export const paginationModel: Partial<DvaModel<BaseState>> = {
state: {
searchString: '',
pagination: {
total: 0,
current: 1,
pageSize: 10,
},
},
reducers: {
setSearchString(state, { payload }) {
return { ...state, searchString: payload };
},
setPagination(state, { payload }) {
return { ...state, pagination: { ...state.pagination, ...payload } };
},
},
};

+ 3
- 3
web/src/hooks/fileManagerHooks.ts View File

const dispatch = useDispatch(); const dispatch = useDispatch();


const uploadFile = useCallback( const uploadFile = useCallback(
(file: UploadFile, parentId: string, path: string) => {
(fileList: UploadFile[], parentId: string) => {
try { try {
return dispatch<any>({ return dispatch<any>({
type: 'fileManager/uploadFile', type: 'fileManager/uploadFile',
payload: { payload: {
file,
file: fileList,
parentId, parentId,
path,
path: fileList.map((file) => (file as any).webkitRelativePath),
}, },
}); });
} catch (errorInfo) { } catch (errorInfo) {

+ 3
- 3
web/src/hooks/knowledgeHook.ts View File



export const useFetchKnowledgeList = ( export const useFetchKnowledgeList = (
shouldFilterListWithoutDocument: boolean = false, shouldFilterListWithoutDocument: boolean = false,
): { list: IKnowledge[]; loading: boolean } => {
) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const loading = useOneNamespaceEffectsLoading('knowledgeModel', ['getList']); const loading = useOneNamespaceEffectsLoading('knowledgeModel', ['getList']);


const knowledgeModel = useSelector((state: any) => state.knowledgeModel); const knowledgeModel = useSelector((state: any) => state.knowledgeModel);
const { data = [] } = knowledgeModel; const { data = [] } = knowledgeModel;
const list = useMemo(() => {
const list: IKnowledge[] = useMemo(() => {
return shouldFilterListWithoutDocument return shouldFilterListWithoutDocument
? data.filter((x: IKnowledge) => x.chunk_num > 0) ? data.filter((x: IKnowledge) => x.chunk_num > 0)
: data; : data;
fetchList(); fetchList();
}, [fetchList]); }, [fetchList]);


return { list, loading };
return { list, loading, fetchList };
}; };


export const useSelectFileThumbnails = () => { export const useSelectFileThumbnails = () => {

+ 53
- 2
web/src/hooks/logicHooks.ts View File

import { LanguageTranslationMap } from '@/constants/common'; import { LanguageTranslationMap } from '@/constants/common';
import { Pagination } from '@/interfaces/common';
import { IKnowledgeFile } from '@/interfaces/database/knowledge'; import { IKnowledgeFile } from '@/interfaces/database/knowledge';
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
import { useCallback, useState } from 'react';
import { PaginationProps } from 'antd';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useSetModalState } from './commonHooks';
import { useDispatch } from 'umi';
import { useSetModalState, useTranslate } from './commonHooks';
import { useSetDocumentParser } from './documentHooks'; import { useSetDocumentParser } from './documentHooks';
import { useOneNamespaceEffectsLoading } from './storeHooks'; import { useOneNamespaceEffectsLoading } from './storeHooks';
import { useSaveSetting } from './userSettingHook'; import { useSaveSetting } from './userSettingHook';


return changeLanguage; return changeLanguage;
}; };

export const useGetPagination = (
total: number,
page: number,
pageSize: number,
onPageChange: PaginationProps['onChange'],
) => {
const { t } = useTranslate('common');

const pagination: PaginationProps = useMemo(() => {
return {
showQuickJumper: true,
total,
showSizeChanger: true,
current: page,
pageSize: pageSize,
pageSizeOptions: [1, 2, 10, 20, 50, 100],
onChange: onPageChange,
showTotal: (total) => `${t('total')} ${total}`,
};
}, [t, onPageChange, page, pageSize, total]);

return {
pagination,
};
};

export const useSetPagination = (namespace: string) => {
const dispatch = useDispatch();

const setPagination = useCallback(
(pageNumber = 1, pageSize?: number) => {
const pagination: Pagination = {
current: pageNumber,
} as Pagination;
if (pageSize) {
pagination.pageSize = pageSize;
}
dispatch({
type: `${namespace}/setPagination`,
payload: pagination,
});
},
[dispatch, namespace],
);

return setPagination;
};

+ 1
- 0
web/src/interfaces/common.ts View File

export interface Pagination { export interface Pagination {
current: number; current: number;
pageSize: number; pageSize: number;
total: number;
} }


export interface BaseState { export interface BaseState {

+ 1
- 1
web/src/locales/en.ts View File

namePlaceholder: 'Please input name!', namePlaceholder: 'Please input name!',
doc: 'Docs', doc: 'Docs',
datasetDescription: datasetDescription:
"Hey, don't forget to adjust the chunk after adding the dataset! 😉",
'😉 Questions and answers can only be answered after the parsing is successful.',
addFile: 'Add file', addFile: 'Add file',
searchFiles: 'Search your files', searchFiles: 'Search your files',
localFiles: 'Local files', localFiles: 'Local files',

+ 1
- 1
web/src/locales/zh-traditional.ts View File

name: '名稱', name: '名稱',
namePlaceholder: '請輸入名稱', namePlaceholder: '請輸入名稱',
doc: '文件', doc: '文件',
datasetDescription: '嘿,添加數據集後別忘了調整解析塊!😉',
datasetDescription: '😉 解析成功後才能問答哦。',
addFile: '新增文件', addFile: '新增文件',
searchFiles: '搜索文件', searchFiles: '搜索文件',
localFiles: '本地文件', localFiles: '本地文件',

+ 1
- 1
web/src/locales/zh.ts View File

name: '名称', name: '名称',
namePlaceholder: '请输入名称', namePlaceholder: '请输入名称',
doc: '文档', doc: '文档',
datasetDescription: '嘿,添加数据集后别忘了调整解析块! 😉',
datasetDescription: '😉 解析成功后才能问答哦。',
addFile: '新增文件', addFile: '新增文件',
searchFiles: '搜索文件', searchFiles: '搜索文件',
localFiles: '本地文件', localFiles: '本地文件',

+ 18
- 11
web/src/pages/file-manager/action-cell/index.tsx View File

record: IFile; record: IFile;
setCurrentRecord: (record: any) => void; setCurrentRecord: (record: any) => void;
showRenameModal: (record: IFile) => void; showRenameModal: (record: IFile) => void;
showConnectToKnowledgeModal: (ids: string[]) => void;
showConnectToKnowledgeModal: (record: IFile) => void;
setSelectedRowKeys(keys: string[]): void;
} }


const ActionCell = ({ const ActionCell = ({
setCurrentRecord, setCurrentRecord,
showRenameModal, showRenameModal,
showConnectToKnowledgeModal, showConnectToKnowledgeModal,
setSelectedRowKeys,
}: IProps) => { }: IProps) => {
const documentId = record.id; const documentId = record.id;
const beingUsed = false; const beingUsed = false;
const { t } = useTranslate('knowledgeDetails'); const { t } = useTranslate('knowledgeDetails');
const { handleRemoveFile } = useHandleDeleteFile([documentId]);
const { handleRemoveFile } = useHandleDeleteFile(
[documentId],
setSelectedRowKeys,
);


const onDownloadDocument = () => { const onDownloadDocument = () => {
downloadFile({ downloadFile({
}; };


const onShowConnectToKnowledgeModal = () => { const onShowConnectToKnowledgeModal = () => {
showConnectToKnowledgeModal([documentId]);
showConnectToKnowledgeModal(record);
}; };


return ( return (
> >
<DeleteOutlined size={20} /> <DeleteOutlined size={20} />
</Button> </Button>
<Button
type="text"
disabled={beingUsed}
onClick={onDownloadDocument}
className={styles.iconButton}
>
<DownloadOutlined size={20} />
</Button>
{record.type !== 'folder' && (
<Button
type="text"
disabled={beingUsed}
onClick={onDownloadDocument}
className={styles.iconButton}
>
<DownloadOutlined size={20} />
</Button>
)}
</Space> </Space>
); );
}; };

+ 13
- 16
web/src/pages/file-manager/connect-to-knowledge-modal/index.tsx View File

import { useFetchKnowledgeList } from '@/hooks/knowledgeHook'; import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { Form, Modal, Select, SelectProps } from 'antd'; import { Form, Modal, Select, SelectProps } from 'antd';
import { useEffect } from 'react';


const ConnectToKnowledgeModal = ({ const ConnectToKnowledgeModal = ({
visible, visible,
hideModal, hideModal,
onOk, onOk,
}: IModalProps<string[]>) => {
initialValue,
}: IModalProps<string[]> & { initialValue: string[] }) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const { list } = useFetchKnowledgeList();
const { list, fetchList } = useFetchKnowledgeList();


const options: SelectProps['options'] = list?.map((item) => ({ const options: SelectProps['options'] = list?.map((item) => ({
label: item.name, label: item.name,
const handleOk = async () => { const handleOk = async () => {
const values = await form.getFieldsValue(); const values = await form.getFieldsValue();
const knowledgeIds = values.knowledgeIds ?? []; const knowledgeIds = values.knowledgeIds ?? [];
if (knowledgeIds.length > 0) {
return onOk?.(knowledgeIds);
}
return onOk?.(knowledgeIds);
}; };


useEffect(() => {
if (visible) {
form.setFieldValue('knowledgeIds', initialValue);
fetchList();
}
}, [visible, fetchList, initialValue, form]);

return ( return (
<Modal <Modal
title="Add to Knowledge Base" title="Add to Knowledge Base"
onCancel={hideModal} onCancel={hideModal}
> >
<Form form={form}> <Form form={form}>
<Form.Item
name="knowledgeIds"
noStyle
rules={[
{
required: true,
message: 'Please select your favourite colors!',
type: 'array',
},
]}
>
<Form.Item name="knowledgeIds" noStyle>
<Select <Select
mode="multiple" mode="multiple"
allowClear allowClear

+ 8
- 5
web/src/pages/file-manager/file-toolbar.tsx View File

import { useMemo } from 'react'; import { useMemo } from 'react';
import { import {
useFetchDocumentListOnMount, useFetchDocumentListOnMount,
useGetPagination,
useHandleDeleteFile, useHandleDeleteFile,
useHandleSearchChange, useHandleSearchChange,
useSelectBreadcrumbItems, useSelectBreadcrumbItems,
selectedRowKeys: string[]; selectedRowKeys: string[];
showFolderCreateModal: () => void; showFolderCreateModal: () => void;
showFileUploadModal: () => void; showFileUploadModal: () => void;
setSelectedRowKeys: (keys: string[]) => void;
} }


const itemRender: BreadcrumbProps['itemRender'] = ( const itemRender: BreadcrumbProps['itemRender'] = (
selectedRowKeys, selectedRowKeys,
showFolderCreateModal, showFolderCreateModal,
showFileUploadModal, showFileUploadModal,
setSelectedRowKeys,
}: IProps) => { }: IProps) => {
const { t } = useTranslate('knowledgeDetails'); const { t } = useTranslate('knowledgeDetails');
const { fetchDocumentList } = useFetchDocumentListOnMount();
const { setPagination, searchString } = useGetPagination(fetchDocumentList);
const { handleInputChange } = useHandleSearchChange(setPagination);
useFetchDocumentListOnMount();
const { handleInputChange, searchString } = useHandleSearchChange();
const breadcrumbItems = useSelectBreadcrumbItems(); const breadcrumbItems = useSelectBreadcrumbItems();


const actionItems: MenuProps['items'] = useMemo(() => { const actionItems: MenuProps['items'] = useMemo(() => {
]; ];
}, [t, showFolderCreateModal, showFileUploadModal]); }, [t, showFolderCreateModal, showFileUploadModal]);


const { handleRemoveFile } = useHandleDeleteFile(selectedRowKeys);
const { handleRemoveFile } = useHandleDeleteFile(
selectedRowKeys,
setSelectedRowKeys,
);


const disabled = selectedRowKeys.length === 0; const disabled = selectedRowKeys.length === 0;



+ 8
- 5
web/src/pages/file-manager/file-upload-modal/index.tsx View File

UploadProps, UploadProps,
} from 'antd'; } from 'antd';
import { Dispatch, SetStateAction, useState } from 'react'; import { Dispatch, SetStateAction, useState } from 'react';
import { useHandleUploadFile } from '../hooks';


const { Dragger } = Upload; const { Dragger } = Upload;


); );
}; };


const FileUploadModal = ({ visible, hideModal }: IModalProps<any>) => {
const FileUploadModal = ({
visible,
hideModal,
loading,
onOk: onFileUploadOk,
}: IModalProps<UploadFile[]>) => {
const [value, setValue] = useState<string | number>('local'); const [value, setValue] = useState<string | number>('local');
const { onFileUploadOk, fileUploadLoading } = useHandleUploadFile();
const [fileList, setFileList] = useState<UploadFile[]>([]); const [fileList, setFileList] = useState<UploadFile[]>([]);
const [directoryFileList, setDirectoryFileList] = useState<UploadFile[]>([]); const [directoryFileList, setDirectoryFileList] = useState<UploadFile[]>([]);


const onOk = () => { const onOk = () => {
onFileUploadOk([...fileList, ...directoryFileList]);
return onFileUploadOk?.([...fileList, ...directoryFileList]);
}; };


const items: TabsProps['items'] = [ const items: TabsProps['items'] = [
open={visible} open={visible}
onOk={onOk} onOk={onOk}
onCancel={hideModal} onCancel={hideModal}
confirmLoading={fileUploadLoading}
confirmLoading={loading}
> >
<Flex gap={'large'} vertical> <Flex gap={'large'} vertical>
<Segmented <Segmented

+ 57
- 75
web/src/pages/file-manager/hooks.ts View File

import {
useSetModalState,
useShowDeleteConfirm,
useTranslate,
} from '@/hooks/commonHooks';
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks';
import { import {
useConnectToKnowledge, useConnectToKnowledge,
useCreateFolder, useCreateFolder,
useSelectParentFolderList, useSelectParentFolderList,
useUploadFile, useUploadFile,
} from '@/hooks/fileManagerHooks'; } from '@/hooks/fileManagerHooks';
import { useGetPagination, useSetPagination } from '@/hooks/logicHooks';
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
import { Pagination } from '@/interfaces/common';
import { IFile } from '@/interfaces/database/file-manager'; import { IFile } from '@/interfaces/database/file-manager';
import { getFilePathByWebkitRelativePath } from '@/utils/fileUtil';
import { PaginationProps } from 'antd'; import { PaginationProps } from 'antd';
import { UploadFile } from 'antd/lib'; import { UploadFile } from 'antd/lib';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const id = searchParams.get('folderId') as string; const id = searchParams.get('folderId') as string;


return id;
return id ?? '';
}; };


export const useFetchDocumentListOnMount = () => { export const useFetchDocumentListOnMount = () => {
const fetchDocumentList = useFetchFileList(); const fetchDocumentList = useFetchFileList();
const fileList = useSelectFileList(); const fileList = useSelectFileList();
const id = useGetFolderId(); const id = useGetFolderId();
const { searchString, pagination } = useSelector(
(state) => state.fileManager,
);
const { pageSize, current } = pagination;


const dispatch = useDispatch(); const dispatch = useDispatch();


useEffect(() => { useEffect(() => {
fetchDocumentList({ parent_id: id });
}, [dispatch, fetchDocumentList, id]);
fetchDocumentList({
parent_id: id,
keywords: searchString,
page_size: pageSize,
page: current,
});
}, [dispatch, fetchDocumentList, id, current, pageSize, searchString]);


return { fetchDocumentList, fileList }; return { fetchDocumentList, fileList };
}; };


export const useGetPagination = (
fetchDocumentList: (payload: IFile) => any,
) => {
const dispatch = useDispatch();
const kFModel = useSelector((state: any) => state.kFModel);
const { t } = useTranslate('common');

const setPagination = useCallback(
(pageNumber = 1, pageSize?: number) => {
const pagination: Pagination = {
current: pageNumber,
} as Pagination;
if (pageSize) {
pagination.pageSize = pageSize;
}
dispatch({
type: 'kFModel/setPagination',
payload: pagination,
});
},
[dispatch],
);
export const useGetFilesPagination = () => {
const { pagination } = useSelector((state) => state.fileManager);

const setPagination = useSetPagination('fileManager');


const onPageChange: PaginationProps['onChange'] = useCallback( const onPageChange: PaginationProps['onChange'] = useCallback(
(pageNumber: number, pageSize: number) => { (pageNumber: number, pageSize: number) => {
setPagination(pageNumber, pageSize); setPagination(pageNumber, pageSize);
fetchDocumentList();
}, },
[fetchDocumentList, setPagination],
[setPagination],
); );


const pagination: PaginationProps = useMemo(() => {
return {
showQuickJumper: true,
total: kFModel.total,
showSizeChanger: true,
current: kFModel.pagination.current,
pageSize: kFModel.pagination.pageSize,
pageSizeOptions: [1, 2, 10, 20, 50, 100],
onChange: onPageChange,
showTotal: (total) => `${t('total')} ${total}`,
};
}, [kFModel, onPageChange, t]);
const { pagination: paginationInfo } = useGetPagination(
pagination.total,
pagination.current,
pagination.pageSize,
onPageChange,
);


return { return {
pagination,
pagination: paginationInfo,
setPagination, setPagination,
total: kFModel.total,
searchString: kFModel.searchString,
}; };
}; };


export const useHandleSearchChange = (setPagination: () => void) => {
export const useHandleSearchChange = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();

const throttledGetDocumentList = useCallback(() => {
dispatch({
type: 'kFModel/throttledGetDocumentList',
});
}, [dispatch]);
const { searchString } = useSelector((state) => state.fileManager);
const setPagination = useSetPagination('fileManager');


const handleInputChange = useCallback( const handleInputChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const value = e.target.value; const value = e.target.value;
dispatch({ type: 'kFModel/setSearchString', payload: value });
dispatch({ type: 'fileManager/setSearchString', payload: value });
setPagination(); setPagination();
throttledGetDocumentList();
}, },
[setPagination, throttledGetDocumentList, dispatch],
[setPagination, dispatch],
); );


return { handleInputChange };
return { handleInputChange, searchString };
}; };


export const useGetRowSelection = () => { export const useGetRowSelection = () => {
}, },
}; };


return rowSelection;
return { rowSelection, setSelectedRowKeys };
}; };


export const useNavigateToOtherFolder = () => { export const useNavigateToOtherFolder = () => {
}; };
}; };


export const useHandleDeleteFile = (fileIds: string[]) => {
export const useHandleDeleteFile = (
fileIds: string[],
setSelectedRowKeys: (keys: string[]) => void,
) => {
const removeDocument = useRemoveFile(); const removeDocument = useRemoveFile();
const showDeleteConfirm = useShowDeleteConfirm(); const showDeleteConfirm = useShowDeleteConfirm();
const parentId = useGetFolderId(); const parentId = useGetFolderId();


const handleRemoveFile = () => { const handleRemoveFile = () => {
showDeleteConfirm({ showDeleteConfirm({
onOk: () => {
return removeDocument(fileIds, parentId);
onOk: async () => {
const retcode = await removeDocument(fileIds, parentId);
if (retcode === 0) {
setSelectedRowKeys([]);
}
return;
}, },
}); });
}; };
async (fileList: UploadFile[]) => { async (fileList: UploadFile[]) => {
console.info('fileList', fileList); console.info('fileList', fileList);
if (fileList.length > 0) { if (fileList.length > 0) {
const ret = await uploadFile(
fileList[0],
id,
getFilePathByWebkitRelativePath(fileList[0] as any),
);

const ret = await uploadFile(fileList, id);
console.info(ret);
if (ret === 0) { if (ret === 0) {
hideFileUploadModal(); hideFileUploadModal();
} }
} = useSetModalState(); } = useSetModalState();
const connectToKnowledge = useConnectToKnowledge(); const connectToKnowledge = useConnectToKnowledge();
const id = useGetFolderId(); const id = useGetFolderId();
const [fileIds, setFileIds] = useState<string[]>([]);
const [record, setRecord] = useState<IFile>({} as IFile);

const initialValue = useMemo(() => {
return Array.isArray(record?.kbs_info)
? record?.kbs_info?.map((x) => x.kb_id)
: [];
}, [record?.kbs_info]);


const onConnectToKnowledgeOk = useCallback( const onConnectToKnowledgeOk = useCallback(
async (knowledgeIds: string[]) => { async (knowledgeIds: string[]) => {
const ret = await connectToKnowledge({ const ret = await connectToKnowledge({
parentId: id, parentId: id,
fileIds,
fileIds: [record.id],
kbIds: knowledgeIds, kbIds: knowledgeIds,
}); });


hideConnectToKnowledgeModal(); hideConnectToKnowledgeModal();
} }
}, },
[connectToKnowledge, hideConnectToKnowledgeModal, id, fileIds],
[connectToKnowledge, hideConnectToKnowledgeModal, id, record.id],
); );


const loading = useOneNamespaceEffectsLoading('fileManager', [ const loading = useOneNamespaceEffectsLoading('fileManager', [
]); ]);


const handleShowConnectToKnowledgeModal = useCallback( const handleShowConnectToKnowledgeModal = useCallback(
(ids: string[]) => {
setFileIds(ids);
(record: IFile) => {
setRecord(record);
showConnectToKnowledgeModal(); showConnectToKnowledgeModal();
}, },
[showConnectToKnowledgeModal], [showConnectToKnowledgeModal],
); );


return { return {
initialValue,
connectToKnowledgeLoading: loading, connectToKnowledgeLoading: loading,
onConnectToKnowledgeOk, onConnectToKnowledgeOk,
connectToKnowledgeVisible, connectToKnowledgeVisible,

+ 4
- 0
web/src/pages/file-manager/index.less View File

width: 22px; width: 22px;
text-align: center; text-align: center;
} }

.linkButton {
padding: 0;
}

+ 39
- 15
web/src/pages/file-manager/index.tsx View File

import { useSelectFileList } from '@/hooks/fileManagerHooks'; import { useSelectFileList } from '@/hooks/fileManagerHooks';
import { IFile } from '@/interfaces/database/file-manager'; import { IFile } from '@/interfaces/database/file-manager';
import { formatDate } from '@/utils/date'; import { formatDate } from '@/utils/date';
import { Button, Table } from 'antd';
import { Button, Flex, Table } from 'antd';
import { ColumnsType } from 'antd/es/table'; import { ColumnsType } from 'antd/es/table';
import ActionCell from './action-cell'; import ActionCell from './action-cell';
import FileToolbar from './file-toolbar'; import FileToolbar from './file-toolbar';
import { import {
useGetFilesPagination,
useGetRowSelection, useGetRowSelection,
useHandleConnectToKnowledge, useHandleConnectToKnowledge,
useHandleCreateFolder, useHandleCreateFolder,
} from './hooks'; } from './hooks';


import RenameModal from '@/components/rename-modal'; import RenameModal from '@/components/rename-modal';
import SvgIcon from '@/components/svg-icon';
import { getExtension } from '@/utils/documentUtils';
import ConnectToKnowledgeModal from './connect-to-knowledge-modal'; import ConnectToKnowledgeModal from './connect-to-knowledge-modal';
import FileUploadModal from './file-upload-modal'; import FileUploadModal from './file-upload-modal';
import FolderCreateModal from './folder-create-modal'; import FolderCreateModal from './folder-create-modal';


const FileManager = () => { const FileManager = () => {
const fileList = useSelectFileList(); const fileList = useSelectFileList();
const rowSelection = useGetRowSelection();
const { rowSelection, setSelectedRowKeys } = useGetRowSelection();
const loading = useSelectFileListLoading(); const loading = useSelectFileListLoading();
const navigateToOtherFolder = useNavigateToOtherFolder(); const navigateToOtherFolder = useNavigateToOtherFolder();
const { const {
folderCreateLoading, folderCreateLoading,
onFolderCreateOk, onFolderCreateOk,
} = useHandleCreateFolder(); } = useHandleCreateFolder();
const { fileUploadVisible, hideFileUploadModal, showFileUploadModal } =
useHandleUploadFile();
const {
fileUploadVisible,
hideFileUploadModal,
showFileUploadModal,
fileUploadLoading,
onFileUploadOk,
} = useHandleUploadFile();
const { const {
connectToKnowledgeVisible, connectToKnowledgeVisible,
hideConnectToKnowledgeModal, hideConnectToKnowledgeModal,
showConnectToKnowledgeModal, showConnectToKnowledgeModal,
onConnectToKnowledgeOk, onConnectToKnowledgeOk,
initialValue,
} = useHandleConnectToKnowledge(); } = useHandleConnectToKnowledge();
const { pagination } = useGetFilesPagination();


const columns: ColumnsType<IFile> = [ const columns: ColumnsType<IFile> = [
{ {
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
render(value, record) { render(value, record) {
return record.type === 'folder' ? (
<Button
type={'link'}
onClick={() => navigateToOtherFolder(record.id)}
>
{value}
</Button>
) : (
value
return (
<Flex gap={10} align="center">
<SvgIcon
name={`file-icon/${record.type === 'folder' ? 'folder' : getExtension(value)}`}
width={24}
></SvgIcon>
{record.type === 'folder' ? (
<Button
type={'link'}
className={styles.linkButton}
onClick={() => navigateToOtherFolder(record.id)}
>
{value}
</Button>
) : (
value
)}
</Flex>
); );
}, },
}, },
}, },
}, },
{ {
title: 'kbs_info',
title: 'Knowledge Base',
dataIndex: 'kbs_info', dataIndex: 'kbs_info',
key: 'kbs_info', key: 'kbs_info',
render(value) { render(value) {
console.info(value);
return Array.isArray(value) return Array.isArray(value)
? value?.map((x) => x.kb_name).join(',') ? value?.map((x) => x.kb_name).join(',')
: ''; : '';
}} }}
showRenameModal={showFileRenameModal} showRenameModal={showFileRenameModal}
showConnectToKnowledgeModal={showConnectToKnowledgeModal} showConnectToKnowledgeModal={showConnectToKnowledgeModal}
setSelectedRowKeys={setSelectedRowKeys}
></ActionCell> ></ActionCell>
), ),
}, },
selectedRowKeys={rowSelection.selectedRowKeys as string[]} selectedRowKeys={rowSelection.selectedRowKeys as string[]}
showFolderCreateModal={showFolderCreateModal} showFolderCreateModal={showFolderCreateModal}
showFileUploadModal={showFileUploadModal} showFileUploadModal={showFileUploadModal}
setSelectedRowKeys={setSelectedRowKeys}
></FileToolbar> ></FileToolbar>
<Table <Table
dataSource={fileList} dataSource={fileList}
rowKey={'id'} rowKey={'id'}
rowSelection={rowSelection} rowSelection={rowSelection}
loading={loading} loading={loading}
pagination={pagination}
/> />
<RenameModal <RenameModal
visible={fileRenameVisible} visible={fileRenameVisible}
<FileUploadModal <FileUploadModal
visible={fileUploadVisible} visible={fileUploadVisible}
hideModal={hideFileUploadModal} hideModal={hideFileUploadModal}
loading={fileUploadLoading}
onOk={onFileUploadOk}
></FileUploadModal> ></FileUploadModal>
<ConnectToKnowledgeModal <ConnectToKnowledgeModal
initialValue={initialValue}
visible={connectToKnowledgeVisible} visible={connectToKnowledgeVisible}
hideModal={hideConnectToKnowledgeModal} hideModal={hideConnectToKnowledgeModal}
onOk={onConnectToKnowledgeOk} onOk={onConnectToKnowledgeOk}

+ 41
- 7
web/src/pages/file-manager/model.ts View File

import { paginationModel } from '@/base';
import { BaseState } from '@/interfaces/common';
import { IFile, IFolder } from '@/interfaces/database/file-manager'; import { IFile, IFolder } from '@/interfaces/database/file-manager';
import fileManagerService from '@/services/fileManagerService'; import fileManagerService from '@/services/fileManagerService';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import { DvaModel } from 'umi'; import { DvaModel } from 'umi';


export interface FileManagerModelState {
export interface FileManagerModelState extends BaseState {
fileList: IFile[]; fileList: IFile[];
parentFolderList: IFolder[]; parentFolderList: IFolder[];
} }


const model: DvaModel<FileManagerModelState> = { const model: DvaModel<FileManagerModelState> = {
namespace: 'fileManager', namespace: 'fileManager',
state: { fileList: [], parentFolderList: [] },
state: {
fileList: [],
parentFolderList: [],
...(paginationModel.state as BaseState),
},
reducers: { reducers: {
setFileList(state, { payload }) { setFileList(state, { payload }) {
return { ...state, fileList: payload }; return { ...state, fileList: payload };
setParentFolderList(state, { payload }) { setParentFolderList(state, { payload }) {
return { ...state, parentFolderList: payload }; return { ...state, parentFolderList: payload };
}, },
...paginationModel.reducers,
}, },
effects: { effects: {
*removeFile({ payload = {} }, { call, put }) { *removeFile({ payload = {} }, { call, put }) {
payload: { parentId: payload.parentId }, payload: { parentId: payload.parentId },
}); });
} }
return retcode;
}, },
*listFile({ payload = {} }, { call, put }) {
const { data } = yield call(fileManagerService.listFile, payload);
*listFile({ payload = {} }, { call, put, select }) {
const { searchString, pagination } = yield select(
(state: any) => state.fileManager,
);
const { current, pageSize } = pagination;
const { data } = yield call(fileManagerService.listFile, {
...payload,
keywords: searchString.trim(),
page: current,
pageSize,
});
const { retcode, data: res } = data; const { retcode, data: res } = data;

if (retcode === 0 && Array.isArray(res.files)) { if (retcode === 0 && Array.isArray(res.files)) {
yield put({ yield put({
type: 'setFileList', type: 'setFileList',
payload: res.files, payload: res.files,
}); });
yield put({
type: 'setPagination',
payload: { total: res.total },
});
} }
}, },
*renameFile({ payload = {} }, { call, put }) { *renameFile({ payload = {} }, { call, put }) {
return data.retcode; return data.retcode;
}, },
*uploadFile({ payload = {} }, { call, put }) { *uploadFile({ payload = {} }, { call, put }) {
const fileList = payload.file;
const pathList = payload.path;
const formData = new FormData(); const formData = new FormData();
formData.append('parent_id', payload.parentId); formData.append('parent_id', payload.parentId);
formData.append('file', payload.file);
formData.append('path', payload.path);
// formData.append('file', payload.file);
// formData.append('path', payload.path);
fileList.forEach((file: any, index: number) => {
formData.append('file', file);
formData.append('path', pathList[index]);
});
const { data } = yield call(fileManagerService.uploadFile, formData); const { data } = yield call(fileManagerService.uploadFile, formData);
if (data.retcode === 0) { if (data.retcode === 0) {
yield put({ yield put({
}, },
}, },
}; };

// const finalModel = modelExtend<DvaModel<FileManagerModelState & BaseState>>(
// paginationModel,
// model,
// );

// console.info(finalModel);

export default model; export default model;

+ 1
- 1
web/src/pages/knowledge/model.ts View File

import { DvaModel } from 'umi'; import { DvaModel } from 'umi';
export interface KnowledgeModelState { export interface KnowledgeModelState {
data: any[];
data: IKnowledge[];
knowledge: IKnowledge; knowledge: IKnowledge;
} }

+ 0
- 9
web/src/utils/fileUtil.ts View File

downloadElement.click(); downloadElement.click();
document.body.removeChild(downloadElement); 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;
};

Loading…
Cancel
Save