mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-14 01:15:51 +08:00
### What problem does this PR solve? fix: #567 use modal to upload files in the knowledge base ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
parent
6874c6f3a7
commit
38f0cc016f
8
web/src/components/file-upload-modal/index.less
Normal file
8
web/src/components/file-upload-modal/index.less
Normal file
@ -0,0 +1,8 @@
|
||||
.uploader {
|
||||
:global {
|
||||
.ant-upload-list {
|
||||
max-height: 40vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
135
web/src/components/file-upload-modal/index.tsx
Normal file
135
web/src/components/file-upload-modal/index.tsx
Normal file
@ -0,0 +1,135 @@
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { InboxOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Flex,
|
||||
Modal,
|
||||
Segmented,
|
||||
Tabs,
|
||||
TabsProps,
|
||||
Upload,
|
||||
UploadFile,
|
||||
UploadProps,
|
||||
} from 'antd';
|
||||
import { Dispatch, SetStateAction, useState } from 'react';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
const { Dragger } = Upload;
|
||||
|
||||
const FileUpload = ({
|
||||
directory,
|
||||
fileList,
|
||||
setFileList,
|
||||
}: {
|
||||
directory: boolean;
|
||||
fileList: UploadFile[];
|
||||
setFileList: Dispatch<SetStateAction<UploadFile[]>>;
|
||||
}) => {
|
||||
const { t } = useTranslate('fileManager');
|
||||
const props: UploadProps = {
|
||||
multiple: true,
|
||||
onRemove: (file) => {
|
||||
const index = fileList.indexOf(file);
|
||||
const newFileList = fileList.slice();
|
||||
newFileList.splice(index, 1);
|
||||
setFileList(newFileList);
|
||||
},
|
||||
beforeUpload: (file) => {
|
||||
setFileList((pre) => {
|
||||
return [...pre, file];
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
directory,
|
||||
fileList,
|
||||
};
|
||||
|
||||
return (
|
||||
<Dragger {...props} className={styles.uploader}>
|
||||
<p className="ant-upload-drag-icon">
|
||||
<InboxOutlined />
|
||||
</p>
|
||||
<p className="ant-upload-text">{t('uploadTitle')}</p>
|
||||
<p className="ant-upload-hint">{t('uploadDescription')}</p>
|
||||
</Dragger>
|
||||
);
|
||||
};
|
||||
|
||||
const FileUploadModal = ({
|
||||
visible,
|
||||
hideModal,
|
||||
loading,
|
||||
onOk: onFileUploadOk,
|
||||
}: IModalProps<UploadFile[]>) => {
|
||||
const { t } = useTranslate('fileManager');
|
||||
const [value, setValue] = useState<string | number>('local');
|
||||
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
||||
const [directoryFileList, setDirectoryFileList] = useState<UploadFile[]>([]);
|
||||
|
||||
const onOk = async () => {
|
||||
const ret = await onFileUploadOk?.([...fileList, ...directoryFileList]);
|
||||
if (ret !== undefined && ret === 0) {
|
||||
setFileList([]);
|
||||
setDirectoryFileList([]);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
const items: TabsProps['items'] = [
|
||||
{
|
||||
key: '1',
|
||||
label: t('file'),
|
||||
children: (
|
||||
<FileUpload
|
||||
directory={false}
|
||||
fileList={fileList}
|
||||
setFileList={setFileList}
|
||||
></FileUpload>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: t('directory'),
|
||||
children: (
|
||||
<FileUpload
|
||||
directory
|
||||
fileList={directoryFileList}
|
||||
setFileList={setDirectoryFileList}
|
||||
></FileUpload>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title={t('uploadFile')}
|
||||
open={visible}
|
||||
onOk={onOk}
|
||||
onCancel={hideModal}
|
||||
confirmLoading={loading}
|
||||
>
|
||||
<Flex gap={'large'} vertical>
|
||||
<Segmented
|
||||
options={[
|
||||
{ label: t('local'), value: 'local' },
|
||||
{ label: t('s3'), value: 's3' },
|
||||
]}
|
||||
block
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
/>
|
||||
{value === 'local' ? (
|
||||
<Tabs defaultActiveKey="1" items={items} />
|
||||
) : (
|
||||
t('comingSoon', { keyPrefix: 'common' })
|
||||
)}
|
||||
</Flex>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileUploadModal;
|
@ -184,12 +184,12 @@ export const useUploadDocument = () => {
|
||||
const { knowledgeId } = useGetKnowledgeSearchParams();
|
||||
|
||||
const uploadDocument = useCallback(
|
||||
(file: UploadFile) => {
|
||||
(fileList: UploadFile[]) => {
|
||||
try {
|
||||
return dispatch<any>({
|
||||
type: 'kFModel/upload_document',
|
||||
payload: {
|
||||
file,
|
||||
fileList,
|
||||
kb_id: knowledgeId,
|
||||
},
|
||||
});
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
IThirdOAIModelCollection,
|
||||
} from '@/interfaces/database/llm';
|
||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
|
||||
import { sortLLmFactoryListBySpecifiedOrder } from '@/utils/commonUtil';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'umi';
|
||||
|
||||
@ -110,13 +111,12 @@ export const useFetchLlmFactoryListOnMount = () => {
|
||||
const factoryList = useSelectLlmFactoryList();
|
||||
const myLlmList = useSelectMyLlmList();
|
||||
|
||||
const list = useMemo(
|
||||
() =>
|
||||
factoryList.filter((x) =>
|
||||
const list = useMemo(() => {
|
||||
const currentList = factoryList.filter((x) =>
|
||||
Object.keys(myLlmList).every((y) => y !== x.name),
|
||||
),
|
||||
[factoryList, myLlmList],
|
||||
);
|
||||
return sortLLmFactoryListBySpecifiedOrder(currentList);
|
||||
}, [factoryList, myLlmList]);
|
||||
|
||||
const fetchLlmFactoryList = useCallback(() => {
|
||||
dispatch({
|
||||
|
@ -30,9 +30,14 @@ import styles from './index.less';
|
||||
interface IProps {
|
||||
selectedRowKeys: string[];
|
||||
showCreateModal(): void;
|
||||
showDocumentUploadModal(): void;
|
||||
}
|
||||
|
||||
const DocumentToolbar = ({ selectedRowKeys, showCreateModal }: IProps) => {
|
||||
const DocumentToolbar = ({
|
||||
selectedRowKeys,
|
||||
showCreateModal,
|
||||
showDocumentUploadModal,
|
||||
}: IProps) => {
|
||||
const { t } = useTranslate('knowledgeDetails');
|
||||
const { fetchDocumentList } = useFetchDocumentListOnMount();
|
||||
const { setPagination, searchString } = useGetPagination(fetchDocumentList);
|
||||
@ -48,7 +53,7 @@ const DocumentToolbar = ({ selectedRowKeys, showCreateModal }: IProps) => {
|
||||
return [
|
||||
{
|
||||
key: '1',
|
||||
onClick: linkToUploadPage,
|
||||
onClick: showDocumentUploadModal,
|
||||
label: (
|
||||
<div>
|
||||
<Button type="link">
|
||||
@ -75,7 +80,7 @@ const DocumentToolbar = ({ selectedRowKeys, showCreateModal }: IProps) => {
|
||||
// disabled: true,
|
||||
},
|
||||
];
|
||||
}, [linkToUploadPage, showCreateModal, t]);
|
||||
}, [showDocumentUploadModal, showCreateModal, t]);
|
||||
|
||||
const handleDelete = useCallback(() => {
|
||||
showDeleteConfirm({
|
||||
|
@ -4,13 +4,15 @@ import {
|
||||
useFetchDocumentList,
|
||||
useSaveDocumentName,
|
||||
useSetDocumentParser,
|
||||
useUploadDocument,
|
||||
} from '@/hooks/documentHooks';
|
||||
import { useGetKnowledgeSearchParams } from '@/hooks/routeHook';
|
||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||
import { useFetchTenantInfo } from '@/hooks/userSettingHook';
|
||||
import { Pagination } from '@/interfaces/common';
|
||||
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
|
||||
import { PaginationProps } from 'antd';
|
||||
import { getUnSupportedFilesCount } from '@/utils/documentUtils';
|
||||
import { PaginationProps, UploadFile } from 'antd';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDispatch, useNavigate, useSelector } from 'umi';
|
||||
import { KnowledgeRouteKey } from './constant';
|
||||
@ -242,3 +244,42 @@ export const useGetRowSelection = () => {
|
||||
|
||||
return rowSelection;
|
||||
};
|
||||
|
||||
export const useHandleUploadDocument = () => {
|
||||
const {
|
||||
visible: documentUploadVisible,
|
||||
hideModal: hideDocumentUploadModal,
|
||||
showModal: showDocumentUploadModal,
|
||||
} = useSetModalState();
|
||||
const uploadDocument = useUploadDocument();
|
||||
|
||||
const onDocumentUploadOk = useCallback(
|
||||
async (fileList: UploadFile[]): Promise<number | undefined> => {
|
||||
if (fileList.length > 0) {
|
||||
const ret: any = await uploadDocument(fileList);
|
||||
const count = getUnSupportedFilesCount(ret.retmsg);
|
||||
/// 500 error code indicates that some file types are not supported
|
||||
let retcode = ret.retcode;
|
||||
if (
|
||||
ret.retcode === 0 ||
|
||||
(ret.retcode === 500 && count !== fileList.length) // Some files were not uploaded successfully, but some were uploaded successfully.
|
||||
) {
|
||||
retcode = 0;
|
||||
hideDocumentUploadModal();
|
||||
}
|
||||
return retcode;
|
||||
}
|
||||
},
|
||||
[uploadDocument, hideDocumentUploadModal],
|
||||
);
|
||||
|
||||
const loading = useOneNamespaceEffectsLoading('kFModel', ['upload_document']);
|
||||
|
||||
return {
|
||||
documentUploadLoading: loading,
|
||||
onDocumentUploadOk,
|
||||
documentUploadVisible,
|
||||
hideDocumentUploadModal,
|
||||
showDocumentUploadModal,
|
||||
};
|
||||
};
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
useFetchDocumentListOnMount,
|
||||
useGetPagination,
|
||||
useGetRowSelection,
|
||||
useHandleUploadDocument,
|
||||
useNavigateToOtherPage,
|
||||
useRenameDocument,
|
||||
} from './hooks';
|
||||
@ -26,6 +27,7 @@ import ParsingActionCell from './parsing-action-cell';
|
||||
import ParsingStatusCell from './parsing-status-cell';
|
||||
import RenameModal from './rename-modal';
|
||||
|
||||
import FileUploadModal from '@/components/file-upload-modal';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import styles from './index.less';
|
||||
|
||||
@ -58,6 +60,13 @@ const KnowledgeFile = () => {
|
||||
hideChangeParserModal,
|
||||
showChangeParserModal,
|
||||
} = useChangeDocumentParser(currentRecord.id);
|
||||
const {
|
||||
documentUploadVisible,
|
||||
hideDocumentUploadModal,
|
||||
showDocumentUploadModal,
|
||||
onDocumentUploadOk,
|
||||
documentUploadLoading,
|
||||
} = useHandleUploadDocument();
|
||||
const { t } = useTranslation('translation', {
|
||||
keyPrefix: 'knowledgeDetails',
|
||||
});
|
||||
@ -157,6 +166,7 @@ const KnowledgeFile = () => {
|
||||
<DocumentToolbar
|
||||
selectedRowKeys={rowSelection.selectedRowKeys as string[]}
|
||||
showCreateModal={showCreateModal}
|
||||
showDocumentUploadModal={showDocumentUploadModal}
|
||||
></DocumentToolbar>
|
||||
<Table
|
||||
rowKey="id"
|
||||
@ -190,6 +200,12 @@ const KnowledgeFile = () => {
|
||||
hideModal={hideRenameModal}
|
||||
initialName={currentRecord.name}
|
||||
></RenameModal>
|
||||
<FileUploadModal
|
||||
visible={documentUploadVisible}
|
||||
hideModal={hideDocumentUploadModal}
|
||||
loading={documentUploadLoading}
|
||||
onOk={onDocumentUploadOk}
|
||||
></FileUploadModal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -210,11 +210,15 @@ const model: DvaModel<KFModelState> = {
|
||||
}
|
||||
},
|
||||
*upload_document({ payload = {} }, { call, put }) {
|
||||
const fileList = payload.fileList;
|
||||
const formData = new FormData();
|
||||
formData.append('file', payload.file);
|
||||
formData.append('kb_id', payload.kb_id);
|
||||
fileList.forEach((file: any) => {
|
||||
formData.append('file', file);
|
||||
});
|
||||
|
||||
const { data } = yield call(kbService.document_upload, formData);
|
||||
if (data.retcode === 0) {
|
||||
if (data.retcode === 0 || data.retcode === 500) {
|
||||
yield put({
|
||||
type: 'getKfList',
|
||||
payload: { kb_id: payload.kb_id },
|
||||
|
@ -85,8 +85,6 @@ const model: DvaModel<FileManagerModelState> = {
|
||||
const pathList = payload.path;
|
||||
const formData = new FormData();
|
||||
formData.append('parent_id', payload.parentId);
|
||||
// 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]);
|
||||
|
@ -1,5 +1,9 @@
|
||||
.modelWrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modelContainer {
|
||||
width: 100%;
|
||||
.factoryOperationWrapper {
|
||||
text-align: right;
|
||||
}
|
||||
|
@ -223,9 +223,9 @@ const UserSettingModel = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<section id="xx" className={styles.modelWrapper}>
|
||||
<Spin spinning={loading}>
|
||||
<section className={styles.modelWrapper}>
|
||||
<section className={styles.modelContainer}>
|
||||
<SettingTitle
|
||||
title={t('model')}
|
||||
description={t('modelDescription')}
|
||||
@ -257,7 +257,7 @@ const UserSettingModel = () => {
|
||||
loading={llmAddingLoading}
|
||||
llmFactory={selectedLlmFactory}
|
||||
></OllamaModal>
|
||||
</>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { IFactory } from '@/interfaces/database/llm';
|
||||
import isObject from 'lodash/isObject';
|
||||
import snakeCase from 'lodash/snakeCase';
|
||||
|
||||
@ -33,3 +34,29 @@ export const formatNumberWithThousandsSeparator = (numberStr: string) => {
|
||||
const formattedNumber = numberStr.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
return formattedNumber;
|
||||
};
|
||||
|
||||
const orderFactoryList = [
|
||||
'OpenAI',
|
||||
'Moonshot',
|
||||
'ZHIPU-AI',
|
||||
'Ollama',
|
||||
'Xinference',
|
||||
];
|
||||
|
||||
export const sortLLmFactoryListBySpecifiedOrder = (list: IFactory[]) => {
|
||||
const finalList: IFactory[] = [];
|
||||
orderFactoryList.forEach((orderItem) => {
|
||||
const index = list.findIndex((item) => item.name === orderItem);
|
||||
if (index !== -1) {
|
||||
finalList.push(list[index]);
|
||||
}
|
||||
});
|
||||
|
||||
list.forEach((item) => {
|
||||
if (finalList.every((x) => x.name !== item.name)) {
|
||||
finalList.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return finalList;
|
||||
};
|
||||
|
@ -42,3 +42,7 @@ export const getExtension = (name: string) =>
|
||||
export const isPdf = (name: string) => {
|
||||
return getExtension(name) === 'pdf';
|
||||
};
|
||||
|
||||
export const getUnSupportedFilesCount = (message: string) => {
|
||||
return message.split('\n').length;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user