mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-06-04 11:24:00 +08:00
### What problem does this PR solve? fix: cannot save the system model setting #468 feat: rename file in FileManager feat: add FileManager feat: override useSelector type ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
parent
aa71462a9f
commit
6405041b4d
@ -27,7 +27,7 @@ export default defineConfig({
|
|||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
proxy: {
|
proxy: {
|
||||||
'/v1': {
|
'/v1': {
|
||||||
target: 'http://123.60.95.134:9380/',
|
target: 'http://192.168.200.233:9380/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
// pathRewrite: { '^/v1': '/v1' },
|
// pathRewrite: { '^/v1': '/v1' },
|
||||||
},
|
},
|
||||||
|
138
web/externals.d.ts
vendored
Normal file
138
web/externals.d.ts
vendored
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
// This file is generated by Umi automatically
|
||||||
|
// DO NOT CHANGE IT MANUALLY!
|
||||||
|
type CSSModuleClasses = { readonly [key: string]: string };
|
||||||
|
declare module '*.css' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.scss' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.sass' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.less' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.styl' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.stylus' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// images
|
||||||
|
declare module '*.jpg' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.jpeg' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.png' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.gif' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.svg' {
|
||||||
|
import * as React from 'react';
|
||||||
|
export const ReactComponent: React.FunctionComponent<
|
||||||
|
React.SVGProps<SVGSVGElement> & { title?: string }
|
||||||
|
>;
|
||||||
|
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.ico' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.webp' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.avif' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
// media
|
||||||
|
declare module '*.mp4' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.webm' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.ogg' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.mp3' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.wav' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.flac' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.aac' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fonts
|
||||||
|
declare module '*.woff' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.woff2' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.eot' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.ttf' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.otf' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
// other
|
||||||
|
declare module '*.wasm' {
|
||||||
|
const initWasm: (
|
||||||
|
options: WebAssembly.Imports,
|
||||||
|
) => Promise<WebAssembly.Exports>;
|
||||||
|
export default initWasm;
|
||||||
|
}
|
||||||
|
declare module '*.webmanifest' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.pdf' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
declare module '*.txt' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
80
web/src/hooks/fileManagerHooks.ts
Normal file
80
web/src/hooks/fileManagerHooks.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { IFileListRequestBody } from '@/interfaces/request/file-manager';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'umi';
|
||||||
|
|
||||||
|
export const useFetchFileList = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const fetchFileList = useCallback(
|
||||||
|
(payload: IFileListRequestBody) => {
|
||||||
|
return dispatch<any>({
|
||||||
|
type: 'fileManager/listFile',
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dispatch],
|
||||||
|
);
|
||||||
|
|
||||||
|
return fetchFileList;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRemoveFile = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const removeFile = useCallback(
|
||||||
|
(fileIds: string[]) => {
|
||||||
|
return dispatch<any>({
|
||||||
|
type: 'fileManager/removeFile',
|
||||||
|
payload: { fileIds },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dispatch],
|
||||||
|
);
|
||||||
|
|
||||||
|
return removeFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRenameFile = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const renameFile = useCallback(
|
||||||
|
(fileId: string, name: string) => {
|
||||||
|
return dispatch<any>({
|
||||||
|
type: 'fileManager/renameFile',
|
||||||
|
payload: { fileId, name },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dispatch],
|
||||||
|
);
|
||||||
|
|
||||||
|
return renameFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchParentFolderList = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const fetchParentFolderList = useCallback(
|
||||||
|
(fileId: string) => {
|
||||||
|
return dispatch<any>({
|
||||||
|
type: 'fileManager/getAllParentFolder',
|
||||||
|
payload: { fileId },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dispatch],
|
||||||
|
);
|
||||||
|
|
||||||
|
return fetchParentFolderList;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectFileList = () => {
|
||||||
|
const fileList = useSelector((state) => state.fileManager.fileList);
|
||||||
|
|
||||||
|
return fileList;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectParentFolderList = () => {
|
||||||
|
const parentFolderList = useSelector(
|
||||||
|
(state) => state.fileManager.parentFolderList,
|
||||||
|
);
|
||||||
|
return parentFolderList.toReversed();
|
||||||
|
};
|
30
web/src/interfaces/database/file-manager.ts
Normal file
30
web/src/interfaces/database/file-manager.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export interface IFile {
|
||||||
|
create_date: string;
|
||||||
|
create_time: number;
|
||||||
|
created_by: string;
|
||||||
|
id: string;
|
||||||
|
kb_ids: string[];
|
||||||
|
location: string;
|
||||||
|
name: string;
|
||||||
|
parent_id: string;
|
||||||
|
size: number;
|
||||||
|
tenant_id: string;
|
||||||
|
type: string;
|
||||||
|
update_date: string;
|
||||||
|
update_time: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFolder {
|
||||||
|
create_date: string;
|
||||||
|
create_time: number;
|
||||||
|
created_by: string;
|
||||||
|
id: string;
|
||||||
|
location: string;
|
||||||
|
name: string;
|
||||||
|
parent_id: string;
|
||||||
|
size: number;
|
||||||
|
tenant_id: string;
|
||||||
|
type: string;
|
||||||
|
update_date: string;
|
||||||
|
update_time: number;
|
||||||
|
}
|
7
web/src/interfaces/request/base.ts
Normal file
7
web/src/interfaces/request/base.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface IPaginationRequestBody {
|
||||||
|
keywords?: string;
|
||||||
|
page?: number;
|
||||||
|
page_size?: number; // name|create|doc_num|create_time|update_time,default:create_time
|
||||||
|
orderby?: string;
|
||||||
|
desc?: string;
|
||||||
|
}
|
5
web/src/interfaces/request/file-manager.ts
Normal file
5
web/src/interfaces/request/file-manager.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { IPaginationRequestBody } from './base';
|
||||||
|
|
||||||
|
export interface IFileListRequestBody extends IPaginationRequestBody {
|
||||||
|
parent_id?: string; // folder id
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import { ReactComponent as StarIon } from '@/assets/svg/chat-star.svg';
|
import { ReactComponent as StarIon } from '@/assets/svg/chat-star.svg';
|
||||||
|
// import { ReactComponent as FileIcon } from '@/assets/svg/file-management.svg';
|
||||||
import { ReactComponent as KnowledgeBaseIcon } from '@/assets/svg/knowledge-base.svg';
|
import { ReactComponent as KnowledgeBaseIcon } from '@/assets/svg/knowledge-base.svg';
|
||||||
import { ReactComponent as Logo } from '@/assets/svg/logo.svg';
|
import { ReactComponent as Logo } from '@/assets/svg/logo.svg';
|
||||||
import { useTranslate } from '@/hooks/commonHooks';
|
import { useTranslate } from '@/hooks/commonHooks';
|
||||||
|
@ -182,7 +182,14 @@ const DocumentToolbar = ({ selectedRowKeys, showCreateModal }: IProps) => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [handleDelete, handleRunClick, handleCancelClick, t]);
|
}, [
|
||||||
|
handleDelete,
|
||||||
|
handleRunClick,
|
||||||
|
handleCancelClick,
|
||||||
|
t,
|
||||||
|
handleDisableClick,
|
||||||
|
handleEnableClick,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.filter}>
|
<div className={styles.filter}>
|
||||||
|
0
web/src/pages/file-manager/action-cell/index.less
Normal file
0
web/src/pages/file-manager/action-cell/index.less
Normal file
91
web/src/pages/file-manager/action-cell/index.tsx
Normal file
91
web/src/pages/file-manager/action-cell/index.tsx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { useShowDeleteConfirm, useTranslate } from '@/hooks/commonHooks';
|
||||||
|
import { api_host } from '@/utils/api';
|
||||||
|
import { downloadFile } from '@/utils/fileUtil';
|
||||||
|
import {
|
||||||
|
DeleteOutlined,
|
||||||
|
DownloadOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
ToolOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { Button, Space, Tooltip } from 'antd';
|
||||||
|
|
||||||
|
import { useRemoveFile } from '@/hooks/fileManagerHooks';
|
||||||
|
import { IFile } from '@/interfaces/database/file-manager';
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
record: IFile;
|
||||||
|
setCurrentRecord: (record: any) => void;
|
||||||
|
showRenameModal: (record: IFile) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionCell = ({ record, setCurrentRecord, showRenameModal }: IProps) => {
|
||||||
|
const documentId = record.id;
|
||||||
|
const beingUsed = false;
|
||||||
|
const { t } = useTranslate('knowledgeDetails');
|
||||||
|
const removeDocument = useRemoveFile();
|
||||||
|
const showDeleteConfirm = useShowDeleteConfirm();
|
||||||
|
|
||||||
|
const onRmDocument = () => {
|
||||||
|
if (!beingUsed) {
|
||||||
|
showDeleteConfirm({
|
||||||
|
onOk: () => {
|
||||||
|
return removeDocument([documentId]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDownloadDocument = () => {
|
||||||
|
downloadFile({
|
||||||
|
url: `${api_host}/document/get/${documentId}`,
|
||||||
|
filename: record.name,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setRecord = () => {
|
||||||
|
setCurrentRecord(record);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onShowRenameModal = () => {
|
||||||
|
setRecord();
|
||||||
|
showRenameModal(record);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Space size={0}>
|
||||||
|
<Button type="text" className={styles.iconButton}>
|
||||||
|
<ToolOutlined size={20} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Tooltip title={t('rename', { keyPrefix: 'common' })}>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
disabled={beingUsed}
|
||||||
|
onClick={onShowRenameModal}
|
||||||
|
className={styles.iconButton}
|
||||||
|
>
|
||||||
|
<EditOutlined size={20} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
disabled={beingUsed}
|
||||||
|
onClick={onRmDocument}
|
||||||
|
className={styles.iconButton}
|
||||||
|
>
|
||||||
|
<DeleteOutlined size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
disabled={beingUsed}
|
||||||
|
onClick={onDownloadDocument}
|
||||||
|
className={styles.iconButton}
|
||||||
|
>
|
||||||
|
<DownloadOutlined size={20} />
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ActionCell;
|
153
web/src/pages/file-manager/file-toolbar.tsx
Normal file
153
web/src/pages/file-manager/file-toolbar.tsx
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import { ReactComponent as DeleteIcon } from '@/assets/svg/delete.svg';
|
||||||
|
import { useShowDeleteConfirm, useTranslate } from '@/hooks/commonHooks';
|
||||||
|
import {
|
||||||
|
DownOutlined,
|
||||||
|
FileOutlined,
|
||||||
|
FileTextOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbProps,
|
||||||
|
Button,
|
||||||
|
Dropdown,
|
||||||
|
Flex,
|
||||||
|
Input,
|
||||||
|
MenuProps,
|
||||||
|
Space,
|
||||||
|
} from 'antd';
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
useFetchDocumentListOnMount,
|
||||||
|
useGetPagination,
|
||||||
|
useHandleSearchChange,
|
||||||
|
useSelectBreadcrumbItems,
|
||||||
|
} from './hooks';
|
||||||
|
|
||||||
|
import { useRemoveFile } from '@/hooks/fileManagerHooks';
|
||||||
|
import { Link } from 'umi';
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
selectedRowKeys: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemRender: BreadcrumbProps['itemRender'] = (
|
||||||
|
currentRoute,
|
||||||
|
params,
|
||||||
|
items,
|
||||||
|
) => {
|
||||||
|
const isLast = currentRoute?.path === items[items.length - 1]?.path;
|
||||||
|
|
||||||
|
return isLast ? (
|
||||||
|
<span>{currentRoute.title}</span>
|
||||||
|
) : (
|
||||||
|
<Link to={`${currentRoute.path}`}>{currentRoute.title}</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FileToolbar = ({ selectedRowKeys }: IProps) => {
|
||||||
|
const { t } = useTranslate('knowledgeDetails');
|
||||||
|
const { fetchDocumentList } = useFetchDocumentListOnMount();
|
||||||
|
const { setPagination, searchString } = useGetPagination(fetchDocumentList);
|
||||||
|
const { handleInputChange } = useHandleSearchChange(setPagination);
|
||||||
|
const removeDocument = useRemoveFile();
|
||||||
|
const showDeleteConfirm = useShowDeleteConfirm();
|
||||||
|
const breadcrumbItems = useSelectBreadcrumbItems();
|
||||||
|
|
||||||
|
const actionItems: MenuProps['items'] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: (
|
||||||
|
<div>
|
||||||
|
<Button type="link">
|
||||||
|
<Space>
|
||||||
|
<FileTextOutlined />
|
||||||
|
{t('localFiles')}
|
||||||
|
</Space>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: (
|
||||||
|
<div>
|
||||||
|
<Button type="link">
|
||||||
|
<FileOutlined />
|
||||||
|
{t('emptyFiles')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
// disabled: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
|
const handleDelete = useCallback(() => {
|
||||||
|
showDeleteConfirm({
|
||||||
|
onOk: () => {
|
||||||
|
return removeDocument(selectedRowKeys);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [removeDocument, showDeleteConfirm, selectedRowKeys]);
|
||||||
|
|
||||||
|
const disabled = selectedRowKeys.length === 0;
|
||||||
|
|
||||||
|
const items: MenuProps['items'] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: '4',
|
||||||
|
onClick: handleDelete,
|
||||||
|
label: (
|
||||||
|
<Flex gap={10}>
|
||||||
|
<span className={styles.deleteIconWrapper}>
|
||||||
|
<DeleteIcon width={18} />
|
||||||
|
</span>
|
||||||
|
<b>{t('delete', { keyPrefix: 'common' })}</b>
|
||||||
|
</Flex>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, [handleDelete, t]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.filter}>
|
||||||
|
<Breadcrumb items={breadcrumbItems} itemRender={itemRender} />
|
||||||
|
<Space>
|
||||||
|
<Dropdown
|
||||||
|
menu={{ items }}
|
||||||
|
placement="bottom"
|
||||||
|
arrow={false}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
<Button>
|
||||||
|
<Space>
|
||||||
|
<b> {t('bulk')}</b>
|
||||||
|
<DownOutlined />
|
||||||
|
</Space>
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
<Input
|
||||||
|
placeholder={t('searchFiles')}
|
||||||
|
value={searchString}
|
||||||
|
style={{ width: 220 }}
|
||||||
|
allowClear
|
||||||
|
onChange={handleInputChange}
|
||||||
|
prefix={<SearchOutlined />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Dropdown menu={{ items: actionItems }} trigger={['click']}>
|
||||||
|
<Button type="primary" icon={<PlusOutlined />}>
|
||||||
|
{t('addFile')}
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FileToolbar;
|
193
web/src/pages/file-manager/hooks.ts
Normal file
193
web/src/pages/file-manager/hooks.ts
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
|
||||||
|
import {
|
||||||
|
useFetchFileList,
|
||||||
|
useFetchParentFolderList,
|
||||||
|
useRenameFile,
|
||||||
|
useSelectFileList,
|
||||||
|
useSelectParentFolderList,
|
||||||
|
} from '@/hooks/fileManagerHooks';
|
||||||
|
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||||
|
import { Pagination } from '@/interfaces/common';
|
||||||
|
import { IFile } from '@/interfaces/database/file-manager';
|
||||||
|
import { PaginationProps } from 'antd';
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useDispatch, useNavigate, useSearchParams, useSelector } from 'umi';
|
||||||
|
|
||||||
|
export const useGetFolderId = () => {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const id = searchParams.get('folderId') as string;
|
||||||
|
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchDocumentListOnMount = () => {
|
||||||
|
const fetchDocumentList = useFetchFileList();
|
||||||
|
const fileList = useSelectFileList();
|
||||||
|
const id = useGetFolderId();
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchDocumentList({ parent_id: id });
|
||||||
|
}, [dispatch, fetchDocumentList, id]);
|
||||||
|
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onPageChange: PaginationProps['onChange'] = useCallback(
|
||||||
|
(pageNumber: number, pageSize: number) => {
|
||||||
|
setPagination(pageNumber, pageSize);
|
||||||
|
fetchDocumentList();
|
||||||
|
},
|
||||||
|
[fetchDocumentList, 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]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
pagination,
|
||||||
|
setPagination,
|
||||||
|
total: kFModel.total,
|
||||||
|
searchString: kFModel.searchString,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useHandleSearchChange = (setPagination: () => void) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const throttledGetDocumentList = useCallback(() => {
|
||||||
|
dispatch({
|
||||||
|
type: 'kFModel/throttledGetDocumentList',
|
||||||
|
});
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const handleInputChange = useCallback(
|
||||||
|
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
dispatch({ type: 'kFModel/setSearchString', payload: value });
|
||||||
|
setPagination();
|
||||||
|
throttledGetDocumentList();
|
||||||
|
},
|
||||||
|
[setPagination, throttledGetDocumentList, dispatch],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { handleInputChange };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetRowSelection = () => {
|
||||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
|
|
||||||
|
const rowSelection = {
|
||||||
|
selectedRowKeys,
|
||||||
|
onChange: (newSelectedRowKeys: React.Key[]) => {
|
||||||
|
setSelectedRowKeys(newSelectedRowKeys);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return rowSelection;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useNavigateToOtherFolder = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const navigateToOtherFolder = useCallback(
|
||||||
|
(folderId: string) => {
|
||||||
|
navigate(`/file?folderId=${folderId}`);
|
||||||
|
},
|
||||||
|
[navigate],
|
||||||
|
);
|
||||||
|
|
||||||
|
return navigateToOtherFolder;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRenameCurrentFile = () => {
|
||||||
|
const [file, setFile] = useState<IFile>({} as IFile);
|
||||||
|
const {
|
||||||
|
visible: fileRenameVisible,
|
||||||
|
hideModal: hideFileRenameModal,
|
||||||
|
showModal: showFileRenameModal,
|
||||||
|
} = useSetModalState();
|
||||||
|
const renameFile = useRenameFile();
|
||||||
|
|
||||||
|
const onFileRenameOk = useCallback(
|
||||||
|
async (name: string) => {
|
||||||
|
const ret = await renameFile(file.id, name);
|
||||||
|
|
||||||
|
if (ret === 0) {
|
||||||
|
hideFileRenameModal();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[renameFile, file, hideFileRenameModal],
|
||||||
|
);
|
||||||
|
|
||||||
|
const loading = useOneNamespaceEffectsLoading('fileManager', ['renameFile']);
|
||||||
|
|
||||||
|
const handleShowFileRenameModal = useCallback(
|
||||||
|
async (record: IFile) => {
|
||||||
|
setFile(record);
|
||||||
|
showFileRenameModal();
|
||||||
|
},
|
||||||
|
[showFileRenameModal],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
fileRenameLoading: loading,
|
||||||
|
initialFileName: file.name,
|
||||||
|
onFileRenameOk,
|
||||||
|
fileRenameVisible,
|
||||||
|
hideFileRenameModal,
|
||||||
|
showFileRenameModal: handleShowFileRenameModal,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectBreadcrumbItems = () => {
|
||||||
|
const parentFolderList = useSelectParentFolderList();
|
||||||
|
const id = useGetFolderId();
|
||||||
|
const fetchParentFolderList = useFetchParentFolderList();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
fetchParentFolderList(id);
|
||||||
|
}
|
||||||
|
}, [id, fetchParentFolderList]);
|
||||||
|
|
||||||
|
return parentFolderList.length === 1
|
||||||
|
? []
|
||||||
|
: parentFolderList.map((x) => ({
|
||||||
|
title: x.name === '/' ? 'root' : x.name,
|
||||||
|
path: `/file?folderId=${x.id}`,
|
||||||
|
}));
|
||||||
|
};
|
18
web/src/pages/file-manager/index.less
Normal file
18
web/src/pages/file-manager/index.less
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.fileManagerWrapper {
|
||||||
|
flex-basis: 100%;
|
||||||
|
padding: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter {
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
margin: 10px 0;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 24px 0;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteIconWrapper {
|
||||||
|
width: 22px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
99
web/src/pages/file-manager/index.tsx
Normal file
99
web/src/pages/file-manager/index.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { useSelectFileList } from '@/hooks/fileManagerHooks';
|
||||||
|
import { IFile } from '@/interfaces/database/file-manager';
|
||||||
|
import { formatDate } from '@/utils/date';
|
||||||
|
import { Button, Table } from 'antd';
|
||||||
|
import { ColumnsType } from 'antd/es/table';
|
||||||
|
import ActionCell from './action-cell';
|
||||||
|
import FileToolbar from './file-toolbar';
|
||||||
|
import {
|
||||||
|
useGetRowSelection,
|
||||||
|
useNavigateToOtherFolder,
|
||||||
|
useRenameCurrentFile,
|
||||||
|
} from './hooks';
|
||||||
|
|
||||||
|
import RenameModal from '@/components/rename-modal';
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
|
const FileManager = () => {
|
||||||
|
const fileList = useSelectFileList();
|
||||||
|
const rowSelection = useGetRowSelection();
|
||||||
|
const navigateToOtherFolder = useNavigateToOtherFolder();
|
||||||
|
const {
|
||||||
|
fileRenameVisible,
|
||||||
|
fileRenameLoading,
|
||||||
|
hideFileRenameModal,
|
||||||
|
showFileRenameModal,
|
||||||
|
initialFileName,
|
||||||
|
onFileRenameOk,
|
||||||
|
} = useRenameCurrentFile();
|
||||||
|
|
||||||
|
const columns: ColumnsType<IFile> = [
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
render(value, record) {
|
||||||
|
return record.type === 'folder' ? (
|
||||||
|
<Button
|
||||||
|
type={'link'}
|
||||||
|
onClick={() => navigateToOtherFolder(record.id)}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
value
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Upload Date',
|
||||||
|
dataIndex: 'create_date',
|
||||||
|
key: 'create_date',
|
||||||
|
render(text) {
|
||||||
|
return formatDate(text);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Location',
|
||||||
|
dataIndex: 'location',
|
||||||
|
key: 'location',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Action',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
render: (text, record) => (
|
||||||
|
<ActionCell
|
||||||
|
record={record}
|
||||||
|
setCurrentRecord={(record: any) => {
|
||||||
|
console.info(record);
|
||||||
|
}}
|
||||||
|
showRenameModal={showFileRenameModal}
|
||||||
|
></ActionCell>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.fileManagerWrapper}>
|
||||||
|
<FileToolbar
|
||||||
|
selectedRowKeys={rowSelection.selectedRowKeys as string[]}
|
||||||
|
></FileToolbar>
|
||||||
|
<Table
|
||||||
|
dataSource={fileList}
|
||||||
|
columns={columns}
|
||||||
|
rowKey={'id'}
|
||||||
|
rowSelection={rowSelection}
|
||||||
|
/>
|
||||||
|
<RenameModal
|
||||||
|
visible={fileRenameVisible}
|
||||||
|
hideModal={hideFileRenameModal}
|
||||||
|
onOk={onFileRenameOk}
|
||||||
|
initialName={initialFileName}
|
||||||
|
loading={fileRenameLoading}
|
||||||
|
></RenameModal>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FileManager;
|
65
web/src/pages/file-manager/model.ts
Normal file
65
web/src/pages/file-manager/model.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { IFile, IFolder } from '@/interfaces/database/file-manager';
|
||||||
|
import fileManagerService from '@/services/fileManagerService';
|
||||||
|
import { DvaModel } from 'umi';
|
||||||
|
|
||||||
|
export interface FileManagerModelState {
|
||||||
|
fileList: IFile[];
|
||||||
|
parentFolderList: IFolder[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const model: DvaModel<FileManagerModelState> = {
|
||||||
|
namespace: 'fileManager',
|
||||||
|
state: { fileList: [], parentFolderList: [] },
|
||||||
|
reducers: {
|
||||||
|
setFileList(state, { payload }) {
|
||||||
|
return { ...state, fileList: payload };
|
||||||
|
},
|
||||||
|
setParentFolderList(state, { payload }) {
|
||||||
|
return { ...state, parentFolderList: payload };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
effects: {
|
||||||
|
*removeFile({ payload = {} }, { call, put }) {
|
||||||
|
const { data } = yield call(fileManagerService.removeFile, payload);
|
||||||
|
const { retcode } = data;
|
||||||
|
if (retcode === 0) {
|
||||||
|
yield put({
|
||||||
|
type: 'listFile',
|
||||||
|
payload: data.data?.files ?? [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*listFile({ payload = {} }, { call, put }) {
|
||||||
|
const { data } = yield call(fileManagerService.listFile, payload);
|
||||||
|
const { retcode, data: res } = data;
|
||||||
|
|
||||||
|
if (retcode === 0 && Array.isArray(res.files)) {
|
||||||
|
yield put({
|
||||||
|
type: 'setFileList',
|
||||||
|
payload: res.files,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*renameFile({ payload = {} }, { call, put }) {
|
||||||
|
const { data } = yield call(fileManagerService.renameFile, payload);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
yield put({ type: 'listFile' });
|
||||||
|
}
|
||||||
|
return data.retcode;
|
||||||
|
},
|
||||||
|
*getAllParentFolder({ payload = {} }, { call, put }) {
|
||||||
|
const { data } = yield call(
|
||||||
|
fileManagerService.getAllParentFolder,
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
yield put({
|
||||||
|
type: 'setParentFolderList',
|
||||||
|
payload: data.data?.parent_folders ?? [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data.retcode;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default model;
|
@ -1,50 +0,0 @@
|
|||||||
import { UploadOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Upload } from 'antd';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
const File: React.FC = () => {
|
|
||||||
const [fileList, setFileList] = useState([
|
|
||||||
{
|
|
||||||
uid: '0',
|
|
||||||
name: 'xxx.png',
|
|
||||||
status: 'uploading',
|
|
||||||
percent: 10,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const obj = {
|
|
||||||
uid: '-1',
|
|
||||||
name: 'yyy.png',
|
|
||||||
status: 'done',
|
|
||||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
|
||||||
thumbUrl:
|
|
||||||
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
|
||||||
};
|
|
||||||
useEffect(() => {
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
setFileList((fileList: any) => {
|
|
||||||
const percent = fileList[0]?.percent;
|
|
||||||
if (percent + 10 >= 100) {
|
|
||||||
clearInterval(timer);
|
|
||||||
return [obj];
|
|
||||||
}
|
|
||||||
const list = [{ ...fileList[0], percent: percent + 10 }];
|
|
||||||
console.log(list);
|
|
||||||
return list;
|
|
||||||
});
|
|
||||||
}, 300);
|
|
||||||
}, []);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Upload
|
|
||||||
action="https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188"
|
|
||||||
listType="picture"
|
|
||||||
fileList={[...fileList]}
|
|
||||||
multiple
|
|
||||||
>
|
|
||||||
<Button icon={<UploadOutlined />}>Upload</Button>
|
|
||||||
</Upload>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default File;
|
|
@ -167,20 +167,22 @@ const Login = () => {
|
|||||||
Sign in with Google
|
Sign in with Google
|
||||||
</div>
|
</div>
|
||||||
</Button> */}
|
</Button> */}
|
||||||
<Button
|
{location.host === 'demo.ragflow.io' && (
|
||||||
block
|
<Button
|
||||||
size="large"
|
block
|
||||||
onClick={toGoogle}
|
size="large"
|
||||||
style={{ marginTop: 15 }}
|
onClick={toGoogle}
|
||||||
>
|
style={{ marginTop: 15 }}
|
||||||
<div>
|
>
|
||||||
<Icon
|
<div>
|
||||||
icon="local:github"
|
<Icon
|
||||||
style={{ verticalAlign: 'middle', marginRight: 5 }}
|
icon="local:github"
|
||||||
/>
|
style={{ verticalAlign: 'middle', marginRight: 5 }}
|
||||||
Sign in with Github
|
/>
|
||||||
</div>
|
Sign in with Github
|
||||||
</Button>
|
</div>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -82,7 +82,7 @@ const routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/file',
|
path: '/file',
|
||||||
component: '@/pages/file',
|
component: '@/pages/file-manager',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
36
web/src/services/fileManagerService.ts
Normal file
36
web/src/services/fileManagerService.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import api from '@/utils/api';
|
||||||
|
import registerServer from '@/utils/registerServer';
|
||||||
|
import request from '@/utils/request';
|
||||||
|
|
||||||
|
const { listFile, removeFile, uploadFile, renameFile, getAllParentFolder } =
|
||||||
|
api;
|
||||||
|
|
||||||
|
const methods = {
|
||||||
|
listFile: {
|
||||||
|
url: listFile,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
removeFile: {
|
||||||
|
url: removeFile,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
uploadFile: {
|
||||||
|
url: uploadFile,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
renameFile: {
|
||||||
|
url: renameFile,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
getAllParentFolder: {
|
||||||
|
url: getAllParentFolder,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const fileManagerService = registerServer<keyof typeof methods>(
|
||||||
|
methods,
|
||||||
|
request,
|
||||||
|
);
|
||||||
|
|
||||||
|
export default fileManagerService;
|
@ -66,4 +66,11 @@ export default {
|
|||||||
createExternalConversation: `${api_host}/api/new_conversation`,
|
createExternalConversation: `${api_host}/api/new_conversation`,
|
||||||
getExternalConversation: `${api_host}/api/conversation`,
|
getExternalConversation: `${api_host}/api/conversation`,
|
||||||
completeExternalConversation: `${api_host}/api/completion`,
|
completeExternalConversation: `${api_host}/api/completion`,
|
||||||
|
|
||||||
|
// file manager
|
||||||
|
listFile: `${api_host}/file/list`,
|
||||||
|
uploadFile: `${api_host}/file/upload`,
|
||||||
|
removeFile: `${api_host}/file/rm`,
|
||||||
|
renameFile: `${api_host}/file/rename`,
|
||||||
|
getAllParentFolder: `${api_host}/file/all_parent_folder`,
|
||||||
};
|
};
|
||||||
|
@ -5,11 +5,18 @@ export const isFormData = (data: unknown): data is FormData => {
|
|||||||
return data instanceof FormData;
|
return data instanceof FormData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const excludedFields = ['img2txt_id'];
|
||||||
|
|
||||||
|
const isExcludedField = (key: string) => {
|
||||||
|
return excludedFields.includes(key);
|
||||||
|
};
|
||||||
|
|
||||||
export const convertTheKeysOfTheObjectToSnake = (data: unknown) => {
|
export const convertTheKeysOfTheObjectToSnake = (data: unknown) => {
|
||||||
if (isObject(data) && !isFormData(data)) {
|
if (isObject(data) && !isFormData(data)) {
|
||||||
return Object.keys(data).reduce<Record<string, any>>((pre, cur) => {
|
return Object.keys(data).reduce<Record<string, any>>((pre, cur) => {
|
||||||
const value = (data as Record<string, any>)[cur];
|
const value = (data as Record<string, any>)[cur];
|
||||||
pre[isFormData(value) ? cur : snakeCase(cur)] = value;
|
pre[isFormData(value) || isExcludedField(cur) ? cur : snakeCase(cur)] =
|
||||||
|
value;
|
||||||
return pre;
|
return pre;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
35
web/typings.d.ts
vendored
35
web/typings.d.ts
vendored
@ -1,8 +1,39 @@
|
|||||||
import 'umi/typings';
|
import { ChunkModelState } from '@/pages/add-knowledge/components/knowledge-chunk/model';
|
||||||
|
import { KFModelState } from '@/pages/add-knowledge/components/knowledge-file/model';
|
||||||
|
import { KSModelState } from '@/pages/add-knowledge/components/knowledge-setting/model';
|
||||||
|
import { TestingModelState } from '@/pages/add-knowledge/components/knowledge-testing/model';
|
||||||
|
import { kAModelState } from '@/pages/add-knowledge/model';
|
||||||
|
import { ChatModelState } from '@/pages/chat/model';
|
||||||
|
import { FileManagerModelState } from '@/pages/file-manager/model';
|
||||||
|
import { KnowledgeModelState } from '@/pages/knowledge/model';
|
||||||
|
import { LoginModelState } from '@/pages/login/model';
|
||||||
|
import { SettingModelState } from '@/pages/user-setting/model';
|
||||||
|
|
||||||
declare module 'lodash';
|
declare module 'lodash';
|
||||||
|
|
||||||
// declare type Nullable<T> = T | null; invalid
|
function useSelector<TState = RootState, TSelected = unknown>(
|
||||||
|
selector: (state: TState) => TSelected,
|
||||||
|
equalityFn?: (left: TSelected, right: TSelected) => boolean,
|
||||||
|
): TSelected;
|
||||||
|
|
||||||
|
export interface RootState {
|
||||||
|
// loading: Loading;
|
||||||
|
fileManager: FileManagerModelState;
|
||||||
|
chatModel: ChatModelState;
|
||||||
|
loginModel: LoginModelState;
|
||||||
|
knowledgeModel: KnowledgeModelState;
|
||||||
|
settingModel: SettingModelState;
|
||||||
|
kFModel: KFModelState;
|
||||||
|
kAModel: kAModelState;
|
||||||
|
chunkModel: ChunkModelState;
|
||||||
|
kSModel: KSModelState;
|
||||||
|
testingModel: TestingModelState;
|
||||||
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
type Nullable<T> = T | null;
|
type Nullable<T> = T | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module 'umi' {
|
||||||
|
export { useSelector };
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user