From fbdde0259a8c057984206369de31297667ad7ec4 Mon Sep 17 00:00:00 2001 From: balibabu Date: Thu, 21 Aug 2025 16:56:22 +0800 Subject: [PATCH] Feat: Allow users to parse documents directly after uploading files #3221 (#9633) ### What problem does this PR solve? Feat: Allow users to parse documents directly after uploading files #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../components/file-upload-dialog/index.tsx | 109 ++++++++++++++---- web/src/hooks/use-document-request.ts | 5 +- web/src/locales/en.ts | 1 + web/src/locales/zh.ts | 1 + web/src/pages/dataset/dataset/index.tsx | 1 + .../dataset/dataset/use-upload-document.ts | 22 +++- .../datasets/dataset-creating-dialog.tsx | 12 +- web/src/pages/files/use-upload-file.ts | 3 +- 8 files changed, 120 insertions(+), 34 deletions(-) diff --git a/web/src/components/file-upload-dialog/index.tsx b/web/src/components/file-upload-dialog/index.tsx index df1b03c51..162f6b8d1 100644 --- a/web/src/components/file-upload-dialog/index.tsx +++ b/web/src/components/file-upload-dialog/index.tsx @@ -8,42 +8,93 @@ import { } from '@/components/ui/dialog'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { IModalProps } from '@/interfaces/common'; -import { Dispatch, SetStateAction, useCallback, useState } from 'react'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { TFunction } from 'i18next'; +import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; import { FileUploader } from '../file-uploader'; +import { RAGFlowFormItem } from '../ragflow-form'; +import { Form } from '../ui/form'; +import { Switch } from '../ui/switch'; -type UploaderTabsProps = { - setFiles: Dispatch>; +function buildUploadFormSchema(t: TFunction) { + const FormSchema = z.object({ + parseOnCreation: z.boolean().optional(), + fileList: z + .array(z.instanceof(File)) + .min(1, { message: t('fileManager.pleaseUploadAtLeastOneFile') }), + }); + + return FormSchema; +} + +export type UploadFormSchemaType = z.infer< + ReturnType +>; + +const UploadFormId = 'UploadFormId'; + +type UploadFormProps = { + submit: (values?: UploadFormSchemaType) => void; + showParseOnCreation?: boolean; }; - -export function UploaderTabs({ setFiles }: UploaderTabsProps) { +function UploadForm({ submit, showParseOnCreation }: UploadFormProps) { const { t } = useTranslation(); + const FormSchema = buildUploadFormSchema(t); + + type UploadFormSchemaType = z.infer; + const form = useForm({ + resolver: zodResolver(FormSchema), + defaultValues: { + parseOnCreation: false, + fileList: [], + }, + }); return ( - - - {t('fileManager.local')} - {t('fileManager.s3')} - - - - - {t('common.comingSoon')} - +
+ + {showParseOnCreation && ( + + {(field) => ( + + )} + + )} + + {(field) => ( + + )} + +
+ ); } +type FileUploadDialogProps = IModalProps & + Pick; export function FileUploadDialog({ hideModal, onOk, loading, -}: IModalProps) { + showParseOnCreation = false, +}: FileUploadDialogProps) { const { t } = useTranslation(); - const [files, setFiles] = useState([]); - - const handleOk = useCallback(() => { - onOk?.(files); - }, [files, onOk]); return ( @@ -51,9 +102,21 @@ export function FileUploadDialog({ {t('fileManager.uploadFile')} - + + + {t('fileManager.local')} + {t('fileManager.s3')} + + + + + {t('common.comingSoon')} + - + {t('common.save')} diff --git a/web/src/hooks/use-document-request.ts b/web/src/hooks/use-document-request.ts index aad7ae3bc..e8f08eca0 100644 --- a/web/src/hooks/use-document-request.ts +++ b/web/src/hooks/use-document-request.ts @@ -1,4 +1,5 @@ import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-filter-submit'; +import { ResponseType } from '@/interfaces/database/base'; import { IDocumentInfo, IDocumentInfoFilter, @@ -45,9 +46,9 @@ export const useUploadNextDocument = () => { data, isPending: loading, mutateAsync, - } = useMutation({ + } = useMutation, Error, File[]>({ mutationKey: [DocumentApiAction.UploadDocument], - mutationFn: async (fileList: File[]) => { + mutationFn: async (fileList) => { const formData = new FormData(); formData.append('kb_id', id!); fileList.forEach((file: any) => { diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index e21588a45..a57817092 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -845,6 +845,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s uploadLimit: 'Each file must not exceed 10MB, and the total number of files must not exceed 128.', destinationFolder: 'Destination folder', + pleaseUploadAtLeastOneFile: 'Please upload at least one file', }, flow: { cite: 'Cite', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 5f6db93db..e4fae595b 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -799,6 +799,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 fileError: '文件错误', uploadLimit: '文件大小不能超过10M,文件总数不超过128个', destinationFolder: '目标文件夹', + pleaseUploadAtLeastOneFile: '请上传至少一个文件', }, flow: { flow: '工作流', diff --git a/web/src/pages/dataset/dataset/index.tsx b/web/src/pages/dataset/dataset/index.tsx index 670f7306a..5432fe129 100644 --- a/web/src/pages/dataset/dataset/index.tsx +++ b/web/src/pages/dataset/dataset/index.tsx @@ -111,6 +111,7 @@ export default function Dataset() { hideModal={hideDocumentUploadModal} onOk={onDocumentUploadOk} loading={documentUploadLoading} + showParseOnCreation > )} {createVisible && ( diff --git a/web/src/pages/dataset/dataset/use-upload-document.ts b/web/src/pages/dataset/dataset/use-upload-document.ts index 2e1915ce2..6a309031c 100644 --- a/web/src/pages/dataset/dataset/use-upload-document.ts +++ b/web/src/pages/dataset/dataset/use-upload-document.ts @@ -1,5 +1,9 @@ +import { UploadFormSchemaType } from '@/components/file-upload-dialog'; import { useSetModalState } from '@/hooks/common-hooks'; -import { useUploadNextDocument } from '@/hooks/use-document-request'; +import { + useRunDocument, + useUploadNextDocument, +} from '@/hooks/use-document-request'; import { getUnSupportedFilesCount } from '@/utils/document-util'; import { useCallback } from 'react'; @@ -10,14 +14,24 @@ export const useHandleUploadDocument = () => { showModal: showDocumentUploadModal, } = useSetModalState(); const { uploadDocument, loading } = useUploadNextDocument(); + const { runDocumentByIds } = useRunDocument(); const onDocumentUploadOk = useCallback( - async (fileList: File[]): Promise => { + async ({ fileList, parseOnCreation }: UploadFormSchemaType) => { if (fileList.length > 0) { - const ret: any = await uploadDocument(fileList); + const ret = await uploadDocument(fileList); if (typeof ret?.message !== 'string') { return; } + + if (ret.code === 0 && parseOnCreation) { + runDocumentByIds({ + documentIds: ret.data.map((x) => x.id), + run: 1, + shouldDelete: false, + }); + } + const count = getUnSupportedFilesCount(ret?.message); /// 500 error code indicates that some file types are not supported let code = ret?.code; @@ -31,7 +45,7 @@ export const useHandleUploadDocument = () => { return code; } }, - [uploadDocument, hideDocumentUploadModal], + [uploadDocument, runDocumentByIds, hideDocumentUploadModal], ); return { diff --git a/web/src/pages/datasets/dataset-creating-dialog.tsx b/web/src/pages/datasets/dataset-creating-dialog.tsx index 8d309179f..8e6dac7d1 100644 --- a/web/src/pages/datasets/dataset-creating-dialog.tsx +++ b/web/src/pages/datasets/dataset-creating-dialog.tsx @@ -1,4 +1,4 @@ -import { Button } from '@/components/ui/button'; +import { ButtonLoading } from '@/components/ui/button'; import { Dialog, DialogContent, @@ -74,7 +74,11 @@ export function InputForm({ onOk }: IModalProps) { ); } -export function DatasetCreatingDialog({ hideModal, onOk }: IModalProps) { +export function DatasetCreatingDialog({ + hideModal, + onOk, + loading, +}: IModalProps) { const { t } = useTranslation(); return ( @@ -85,9 +89,9 @@ export function DatasetCreatingDialog({ hideModal, onOk }: IModalProps) { - + diff --git a/web/src/pages/files/use-upload-file.ts b/web/src/pages/files/use-upload-file.ts index 6dabec461..95d8f5083 100644 --- a/web/src/pages/files/use-upload-file.ts +++ b/web/src/pages/files/use-upload-file.ts @@ -1,3 +1,4 @@ +import { UploadFormSchemaType } from '@/components/file-upload-dialog'; import { useSetModalState } from '@/hooks/common-hooks'; import { useUploadFile } from '@/hooks/use-file-request'; import { useCallback } from 'react'; @@ -13,7 +14,7 @@ export const useHandleUploadFile = () => { const id = useGetFolderId(); const onFileUploadOk = useCallback( - async (fileList: File[]): Promise => { + async ({ fileList }: UploadFormSchemaType): Promise => { if (fileList.length > 0) { const ret: number = await uploadFile({ fileList, parentId: id }); if (ret === 0) {