ragflow
/
web
/src
/pages
/add-knowledge
/components
/knowledge-dataset
/knowledge-upload-file
/index.tsx
| import { ReactComponent as SelectFilesEndIcon } from '@/assets/svg/select-files-end.svg'; | |
| import { ReactComponent as SelectFilesStartIcon } from '@/assets/svg/select-files-start.svg'; | |
| import ChunkMethodModal from '@/components/chunk-method-modal'; | |
| import { KnowledgeRouteKey } from '@/constants/knowledge'; | |
| import { | |
| useRunDocument, | |
| useSelectDocumentList, | |
| useUploadDocument, | |
| } from '@/hooks/documentHooks'; | |
| import { | |
| useDeleteDocumentById, | |
| useFetchKnowledgeDetail, | |
| useKnowledgeBaseId, | |
| } from '@/hooks/knowledgeHook'; | |
| import { | |
| useChangeDocumentParser, | |
| useSetSelectedRecord, | |
| } from '@/hooks/logicHooks'; | |
| import { useFetchTenantInfo } from '@/hooks/userSettingHook'; | |
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |
| import { getExtension, isFileUploadDone } from '@/utils/documentUtils'; | |
| import { | |
| ArrowLeftOutlined, | |
| CloudUploadOutlined, | |
| DeleteOutlined, | |
| EditOutlined, | |
| FileDoneOutlined, | |
| } from '@ant-design/icons'; | |
| import { | |
| Button, | |
| Card, | |
| Flex, | |
| Progress, | |
| Space, | |
| Upload, | |
| UploadFile, | |
| UploadProps, | |
| } from 'antd'; | |
| import classNames from 'classnames'; | |
| import { ReactElement, useCallback, useMemo, useRef, useState } from 'react'; | |
| import { Link, useNavigate } from 'umi'; | |
| import { useTranslate } from '@/hooks/commonHooks'; | |
| import styles from './index.less'; | |
| const { Dragger } = Upload; | |
| type UploadRequestOption = Parameters< | |
| NonNullable<UploadProps['customRequest']> | |
| >[0]; | |
| const UploaderItem = ({ | |
| file, | |
| isUpload, | |
| remove, | |
| handleEdit, | |
| }: { | |
| isUpload: boolean; | |
| originNode: ReactElement; | |
| file: UploadFile; | |
| fileList: object[]; | |
| showModal: () => void; | |
| remove: (id: string) => void; | |
| setRecord: (record: IKnowledgeFile) => void; | |
| handleEdit: (id: string) => void; | |
| }) => { | |
| const { removeDocument } = useDeleteDocumentById(); | |
| const documentId = file?.response?.id; | |
| const handleRemove = async () => { | |
| if (file.status === 'error') { | |
| remove(documentId); | |
| } else { | |
| const ret: any = await removeDocument(documentId); | |
| if (ret === 0) { | |
| remove(documentId); | |
| } | |
| } | |
| }; | |
| const handleEditClick = () => { | |
| if (file.status === 'done') { | |
| handleEdit(documentId); | |
| } | |
| }; | |
| return ( | |
| <Card className={styles.uploaderItem}> | |
| <Flex justify="space-between"> | |
| <FileDoneOutlined className={styles.fileIcon} /> | |
| <section className={styles.uploaderItemTextWrapper}> | |
| <div> | |
| <b>{file.name}</b> | |
| </div> | |
| <span>{file.size}</span> | |
| </section> | |
| {isUpload ? ( | |
| <DeleteOutlined | |
| className={styles.deleteIcon} | |
| onClick={handleRemove} | |
| /> | |
| ) : ( | |
| <EditOutlined onClick={handleEditClick} /> | |
| )} | |
| </Flex> | |
| <Flex> | |
| <Progress | |
| showInfo={false} | |
| percent={100} | |
| className={styles.uploaderItemProgress} | |
| strokeColor=" | |
| rgba(127, 86, 217, 1) | |
| " | |
| /> | |
| <span>100%</span> | |
| </Flex> | |
| </Card> | |
| ); | |
| }; | |
| const KnowledgeUploadFile = () => { | |
| const knowledgeBaseId = useKnowledgeBaseId(); | |
| const [isUpload, setIsUpload] = useState(true); | |
| const [uploadedFileIds, setUploadedFileIds] = useState<string[]>([]); | |
| const fileListRef = useRef<UploadFile[]>([]); | |
| const navigate = useNavigate(); | |
| const { currentRecord, setRecord } = useSetSelectedRecord(); | |
| const { | |
| changeParserLoading, | |
| onChangeParserOk, | |
| changeParserVisible, | |
| hideChangeParserModal, | |
| showChangeParserModal, | |
| } = useChangeDocumentParser(currentRecord.id); | |
| const documentList = useSelectDocumentList(); | |
| const runDocumentByIds = useRunDocument(); | |
| const uploadDocument = useUploadDocument(); | |
| const { t } = useTranslate('knowledgeDetails'); | |
| const enabled = useMemo(() => { | |
| if (isUpload) { | |
| return ( | |
| uploadedFileIds.length > 0 && | |
| fileListRef.current.filter((x) => isFileUploadDone(x)).length === | |
| uploadedFileIds.length | |
| ); | |
| } | |
| return true; | |
| }, [uploadedFileIds, isUpload]); | |
| const createRequest: (props: UploadRequestOption) => void = async function ({ | |
| file, | |
| onSuccess, | |
| onError, | |
| // onProgress, | |
| }) { | |
| const data = await uploadDocument(file as UploadFile); | |
| if (data?.retcode === 0) { | |
| setUploadedFileIds((pre) => { | |
| return pre.concat(data.data.id); | |
| }); | |
| if (onSuccess) { | |
| onSuccess(data.data); | |
| } | |
| } else { | |
| if (onError) { | |
| onError(data?.data); | |
| } | |
| } | |
| }; | |
| const removeIdFromUploadedIds = useCallback((id: string) => { | |
| setUploadedFileIds((pre) => { | |
| return pre.filter((x) => x !== id); | |
| }); | |
| }, []); | |
| const handleItemEdit = useCallback( | |
| (id: string) => { | |
| const document = documentList.find((x) => x.id === id); | |
| if (document) { | |
| setRecord(document); | |
| } | |
| showChangeParserModal(); | |
| }, | |
| [documentList, showChangeParserModal, setRecord], | |
| ); | |
| const props: UploadProps = { | |
| name: 'file', | |
| multiple: true, | |
| itemRender(originNode, file, fileList, actions) { | |
| fileListRef.current = fileList; | |
| const remove = (id: string) => { | |
| if (isFileUploadDone(file)) { | |
| removeIdFromUploadedIds(id); | |
| } | |
| actions.remove(); | |
| }; | |
| return ( | |
| <UploaderItem | |
| isUpload={isUpload} | |
| file={file} | |
| fileList={fileList} | |
| originNode={originNode} | |
| remove={remove} | |
| showModal={showChangeParserModal} | |
| setRecord={setRecord} | |
| handleEdit={handleItemEdit} | |
| ></UploaderItem> | |
| ); | |
| }, | |
| customRequest: createRequest, | |
| onDrop(e) { | |
| console.log('Dropped files', e.dataTransfer.files); | |
| }, | |
| }; | |
| const runSelectedDocument = () => { | |
| const ids = fileListRef.current.map((x) => x.response.id); | |
| runDocumentByIds({ doc_ids: ids, run: 1 }); | |
| }; | |
| const handleNextClick = () => { | |
| if (!isUpload) { | |
| runSelectedDocument(); | |
| navigate(`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`); | |
| } else { | |
| setIsUpload(false); | |
| } | |
| }; | |
| useFetchTenantInfo(); | |
| useFetchKnowledgeDetail(); | |
| return ( | |
| <> | |
| <div className={styles.uploadWrapper}> | |
| <section> | |
| <Space className={styles.backToList}> | |
| <ArrowLeftOutlined /> | |
| <Link to={`/knowledge/dataset?id=${knowledgeBaseId}`}> | |
| Back to select files | |
| </Link> | |
| </Space> | |
| <div className={styles.progressWrapper}> | |
| <Flex align="center" justify="center"> | |
| <SelectFilesStartIcon></SelectFilesStartIcon> | |
| <Progress | |
| percent={100} | |
| showInfo={false} | |
| className={styles.progress} | |
| strokeColor=" | |
| rgba(127, 86, 217, 1) | |
| " | |
| /> | |
| <SelectFilesEndIcon></SelectFilesEndIcon> | |
| </Flex> | |
| <Flex justify="space-around"> | |
| <p className={styles.selectFilesText}> | |
| <b>{t('selectFiles')}</b> | |
| </p> | |
| <p className={styles.changeSpecificCategoryText}> | |
| <b>{t('changeSpecificCategory')}</b> | |
| </p> | |
| </Flex> | |
| </div> | |
| </section> | |
| <section className={styles.uploadContent}> | |
| <Dragger | |
| {...props} | |
| className={classNames(styles.uploader, { | |
| [styles.hiddenUploader]: !isUpload, | |
| })} | |
| > | |
| <Button className={styles.uploaderButton}> | |
| <CloudUploadOutlined className={styles.uploaderIcon} /> | |
| </Button> | |
| <p className="ant-upload-text">{t('uploadTitle')}</p> | |
| <p className="ant-upload-hint">{t('uploadDescription')}</p> | |
| </Dragger> | |
| </section> | |
| <section className={styles.footer}> | |
| <Button | |
| type="primary" | |
| className={styles.nextButton} | |
| onClick={handleNextClick} | |
| disabled={!enabled} | |
| size="large" | |
| > | |
| {t('next', { keyPrefix: 'common' })} | |
| </Button> | |
| </section> | |
| </div> | |
| <ChunkMethodModal | |
| documentId={currentRecord.id} | |
| parserId={currentRecord.parser_id} | |
| parserConfig={currentRecord.parser_config} | |
| documentExtension={getExtension(currentRecord.name)} | |
| onOk={onChangeParserOk} | |
| visible={changeParserVisible} | |
| hideModal={hideChangeParserModal} | |
| loading={changeParserLoading} | |
| /> | |
| </> | |
| ); | |
| }; | |
| export default KnowledgeUploadFile; | |