fix: cannot save the system model setting #468 (#508)

### 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:
balibabu 2024-04-23 17:46:56 +08:00 committed by GitHub
parent aa71462a9f
commit 6405041b4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 990 additions and 70 deletions

View File

@ -27,7 +27,7 @@ export default defineConfig({
devtool: 'source-map',
proxy: {
'/v1': {
target: 'http://123.60.95.134:9380/',
target: 'http://192.168.200.233:9380/',
changeOrigin: true,
// pathRewrite: { '^/v1': '/v1' },
},

138
web/externals.d.ts vendored Normal file
View 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;
}

View 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();
};

View 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;
}

View File

@ -0,0 +1,7 @@
export interface IPaginationRequestBody {
keywords?: string;
page?: number;
page_size?: number; // name|create|doc_num|create_time|update_timedefaultcreate_time
orderby?: string;
desc?: string;
}

View File

@ -0,0 +1,5 @@
import { IPaginationRequestBody } from './base';
export interface IFileListRequestBody extends IPaginationRequestBody {
parent_id?: string; // folder id
}

View File

@ -1,4 +1,5 @@
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 Logo } from '@/assets/svg/logo.svg';
import { useTranslate } from '@/hooks/commonHooks';

View File

@ -182,7 +182,14 @@ const DocumentToolbar = ({ selectedRowKeys, showCreateModal }: IProps) => {
),
},
];
}, [handleDelete, handleRunClick, handleCancelClick, t]);
}, [
handleDelete,
handleRunClick,
handleCancelClick,
t,
handleDisableClick,
handleEnableClick,
]);
return (
<div className={styles.filter}>

View 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;

View 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;

View 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}`,
}));
};

View 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;
}

View 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;

View 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;

View File

@ -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;

View File

@ -167,20 +167,22 @@ const Login = () => {
Sign in with Google
</div>
</Button> */}
<Button
block
size="large"
onClick={toGoogle}
style={{ marginTop: 15 }}
>
<div>
<Icon
icon="local:github"
style={{ verticalAlign: 'middle', marginRight: 5 }}
/>
Sign in with Github
</div>
</Button>
{location.host === 'demo.ragflow.io' && (
<Button
block
size="large"
onClick={toGoogle}
style={{ marginTop: 15 }}
>
<div>
<Icon
icon="local:github"
style={{ verticalAlign: 'middle', marginRight: 5 }}
/>
Sign in with Github
</div>
</Button>
)}
</>
)}
</Form>

View File

@ -82,7 +82,7 @@ const routes = [
},
{
path: '/file',
component: '@/pages/file',
component: '@/pages/file-manager',
},
],
},

View 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;

View File

@ -66,4 +66,11 @@ export default {
createExternalConversation: `${api_host}/api/new_conversation`,
getExternalConversation: `${api_host}/api/conversation`,
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`,
};

View File

@ -5,11 +5,18 @@ export const isFormData = (data: unknown): data is FormData => {
return data instanceof FormData;
};
const excludedFields = ['img2txt_id'];
const isExcludedField = (key: string) => {
return excludedFields.includes(key);
};
export const convertTheKeysOfTheObjectToSnake = (data: unknown) => {
if (isObject(data) && !isFormData(data)) {
return Object.keys(data).reduce<Record<string, any>>((pre, 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;
}, {});
}

35
web/typings.d.ts vendored
View File

@ -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 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 {
type Nullable<T> = T | null;
}
declare module 'umi' {
export { useSelector };
}