Feat: Create empty document. #3221 (#7343)

### What problem does this PR solve?

Feat: Create empty document. #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2025-04-27 16:12:10 +08:00 committed by GitHub
parent bdebd1b2e3
commit 5043143bc5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 137 additions and 62 deletions

View File

@ -8,6 +8,7 @@ import {
import { LoadingButton } from '@/components/ui/loading-button'; import { LoadingButton } from '@/components/ui/loading-button';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { TagRenameId } from '@/pages/add-knowledge/constant'; import { TagRenameId } from '@/pages/add-knowledge/constant';
import { ReactNode } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { RenameForm } from './rename-form'; import { RenameForm } from './rename-form';
@ -16,14 +17,15 @@ export function RenameDialog({
initialName, initialName,
onOk, onOk,
loading, loading,
}: IModalProps<any> & { initialName?: string }) { title,
}: IModalProps<any> & { initialName?: string; title?: ReactNode }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Dialog open onOpenChange={hideModal}> <Dialog open onOpenChange={hideModal}>
<DialogContent className="sm:max-w-[425px]"> <DialogContent className="sm:max-w-[425px]">
<DialogHeader> <DialogHeader>
<DialogTitle>{t('common.rename')}</DialogTitle> <DialogTitle>{title || t('common.rename')}</DialogTitle>
</DialogHeader> </DialogHeader>
<RenameForm <RenameForm
initialName={initialName} initialName={initialName}

View File

@ -16,7 +16,10 @@ import {
useGetPaginationWithRouter, useGetPaginationWithRouter,
useHandleSearchChange, useHandleSearchChange,
} from './logic-hooks'; } from './logic-hooks';
import { useGetKnowledgeSearchParams } from './route-hook'; import {
useGetKnowledgeSearchParams,
useSetPaginationParams,
} from './route-hook';
export const enum DocumentApiAction { export const enum DocumentApiAction {
UploadDocument = 'uploadDocument', UploadDocument = 'uploadDocument',
@ -28,6 +31,7 @@ export const enum DocumentApiAction {
SetDocumentParser = 'setDocumentParser', SetDocumentParser = 'setDocumentParser',
SetDocumentMeta = 'setDocumentMeta', SetDocumentMeta = 'setDocumentMeta',
FetchAllDocumentList = 'fetchAllDocumentList', FetchAllDocumentList = 'fetchAllDocumentList',
CreateDocument = 'createDocument',
} }
export const useUploadNextDocument = () => { export const useUploadNextDocument = () => {
@ -363,3 +367,37 @@ export const useSetDocumentMeta = () => {
return { setDocumentMeta: mutateAsync, data, loading }; return { setDocumentMeta: mutateAsync, data, loading };
}; };
export const useCreateDocument = () => {
const { id } = useParams();
const { setPaginationParams, page } = useSetPaginationParams();
const queryClient = useQueryClient();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [DocumentApiAction.CreateDocument],
mutationFn: async (name: string) => {
const { data } = await kbService.document_create({
name,
kb_id: id,
});
if (data.code === 0) {
if (page === 1) {
queryClient.invalidateQueries({
queryKey: [DocumentApiAction.FetchDocumentList],
});
} else {
setPaginationParams(); // fetch document list
}
message.success(i18n.t('message.created'));
}
return data.code;
},
});
return { createDocument: mutateAsync, loading, data };
};

View File

@ -5,6 +5,7 @@ import {
HoverCardContent, HoverCardContent,
HoverCardTrigger, HoverCardTrigger,
} from '@/components/ui/hover-card'; } from '@/components/ui/hover-card';
import { DocumentType } from '@/constants/knowledge';
import { useRemoveDocument } from '@/hooks/use-document-request'; import { useRemoveDocument } from '@/hooks/use-document-request';
import { IDocumentInfo } from '@/interfaces/database/document'; import { IDocumentInfo } from '@/interfaces/database/document';
import { formatFileSize } from '@/utils/common-util'; import { formatFileSize } from '@/utils/common-util';
@ -27,8 +28,9 @@ export function DatasetActionCell({
record, record,
showRenameModal, showRenameModal,
}: { record: IDocumentInfo } & UseRenameDocumentShowType) { }: { record: IDocumentInfo } & UseRenameDocumentShowType) {
const { id, run } = record; const { id, run, type } = record;
const isRunning = isParserRunning(run); const isRunning = isParserRunning(run);
const isVirtualDocument = type === DocumentType.Virtual;
const { removeDocument } = useRemoveDocument(); const { removeDocument } = useRemoveDocument();
@ -83,14 +85,16 @@ export function DatasetActionCell({
> >
<Pencil /> <Pencil />
</Button> </Button>
<Button {isVirtualDocument || (
variant={'ghost'} <Button
size={'icon'} variant={'ghost'}
onClick={onDownloadDocument} size={'icon'}
disabled={isRunning} onClick={onDownloadDocument}
> disabled={isRunning}
<ArrowDownToLine /> >
</Button> <ArrowDownToLine />
</Button>
)}
<ConfirmDeleteDialog onOk={handleRemove}> <ConfirmDeleteDialog onOk={handleRemove}>
<Button variant={'ghost'} size={'icon'} disabled={isRunning}> <Button variant={'ghost'} size={'icon'} disabled={isRunning}>
<Trash2 className="text-text-delete-red" /> <Trash2 className="text-text-delete-red" />

View File

@ -24,9 +24,7 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from '@/components/ui/table'; } from '@/components/ui/table';
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
import { useFetchDocumentList } from '@/hooks/use-document-request'; import { useFetchDocumentList } from '@/hooks/use-document-request';
import { IDocumentInfo } from '@/interfaces/database/document';
import { getExtension } from '@/utils/document-util'; import { getExtension } from '@/utils/document-util';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { SetMetaDialog } from './set-meta-dialog'; import { SetMetaDialog } from './set-meta-dialog';
@ -53,8 +51,6 @@ export function DatasetTable({
React.useState<VisibilityState>({}); React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({}); const [rowSelection, setRowSelection] = React.useState({});
const { currentRecord, setRecord } = useSetSelectedRecord<IDocumentInfo>();
const { const {
changeParserLoading, changeParserLoading,
onChangeParserOk, onChangeParserOk,
@ -84,7 +80,6 @@ export function DatasetTable({
const columns = useDatasetTableColumns({ const columns = useDatasetTableColumns({
showChangeParserModal, showChangeParserModal,
setCurrentRecord: setRecord,
showRenameModal, showRenameModal,
showSetMetaModal, showSetMetaModal,
}); });

View File

@ -1,5 +1,5 @@
import { useSetModalState } from '@/hooks/common-hooks'; import { useSetModalState } from '@/hooks/common-hooks';
import { useCreateNextDocument, useNextWebCrawl } from '@/hooks/document-hooks'; import { useNextWebCrawl } from '@/hooks/document-hooks';
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook'; import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { useNavigate } from 'umi'; import { useNavigate } from 'umi';
@ -17,34 +17,6 @@ export const useNavigateToOtherPage = () => {
return { linkToUploadPage, toChunk }; return { linkToUploadPage, toChunk };
}; };
export const useCreateEmptyDocument = () => {
const { createDocument, loading } = useCreateNextDocument();
const {
visible: createVisible,
hideModal: hideCreateModal,
showModal: showCreateModal,
} = useSetModalState();
const onCreateOk = useCallback(
async (name: string) => {
const ret = await createDocument(name);
if (ret === 0) {
hideCreateModal();
}
},
[hideCreateModal, createDocument],
);
return {
createLoading: loading,
onCreateOk,
createVisible,
hideCreateModal,
showCreateModal,
};
};
export const useGetRowSelection = () => { export const useGetRowSelection = () => {
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);

View File

@ -1,15 +1,26 @@
import { BulkOperateBar } from '@/components/bulk-operate-bar'; import { BulkOperateBar } from '@/components/bulk-operate-bar';
import { FileUploadDialog } from '@/components/file-upload-dialog'; import { FileUploadDialog } from '@/components/file-upload-dialog';
import ListFilterBar from '@/components/list-filter-bar'; import ListFilterBar from '@/components/list-filter-bar';
import { RenameDialog } from '@/components/rename-dialog';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useFetchDocumentList } from '@/hooks/use-document-request'; import { useFetchDocumentList } from '@/hooks/use-document-request';
import { Upload } from 'lucide-react'; import { Upload } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { DatasetTable } from './dataset-table'; import { DatasetTable } from './dataset-table';
import { useBulkOperateDataset } from './use-bulk-operate-dataset'; import { useBulkOperateDataset } from './use-bulk-operate-dataset';
import { useCreateEmptyDocument } from './use-create-empty-document';
import { useSelectDatasetFilters } from './use-select-filters'; import { useSelectDatasetFilters } from './use-select-filters';
import { useHandleUploadDocument } from './use-upload-document'; import { useHandleUploadDocument } from './use-upload-document';
export default function Dataset() { export default function Dataset() {
const { t } = useTranslation();
const { const {
documentUploadVisible, documentUploadVisible,
hideDocumentUploadModal, hideDocumentUploadModal,
@ -29,6 +40,14 @@ export default function Dataset() {
} = useFetchDocumentList(); } = useFetchDocumentList();
const { filters } = useSelectDatasetFilters(); const { filters } = useSelectDatasetFilters();
const {
createLoading,
onCreateOk,
createVisible,
hideCreateModal,
showCreateModal,
} = useCreateEmptyDocument();
return ( return (
<section className="p-8"> <section className="p-8">
<ListFilterBar <ListFilterBar
@ -39,14 +58,23 @@ export default function Dataset() {
onChange={handleFilterSubmit} onChange={handleFilterSubmit}
filters={filters} filters={filters}
> >
<Button <DropdownMenu>
variant={'tertiary'} <DropdownMenuTrigger asChild>
size={'sm'} <Button variant={'tertiary'} size={'sm'}>
onClick={showDocumentUploadModal} <Upload />
> {t('knowledgeDetails.addFile')}
<Upload /> </Button>
Upload file </DropdownMenuTrigger>
</Button> <DropdownMenuContent className="w-56">
<DropdownMenuItem onClick={showDocumentUploadModal}>
{t('fileManager.uploadFile')}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={showCreateModal}>
{t('fileManager.newFolder')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</ListFilterBar> </ListFilterBar>
<BulkOperateBar list={list}></BulkOperateBar> <BulkOperateBar list={list}></BulkOperateBar>
<DatasetTable <DatasetTable
@ -61,6 +89,14 @@ export default function Dataset() {
loading={documentUploadLoading} loading={documentUploadLoading}
></FileUploadDialog> ></FileUploadDialog>
)} )}
{createVisible && (
<RenameDialog
hideModal={hideCreateModal}
onOk={onCreateOk}
loading={createLoading}
title={'File Name'}
></RenameDialog>
)}
</section> </section>
); );
} }

View File

@ -0,0 +1,31 @@
import { useSetModalState } from '@/hooks/common-hooks';
import { useCreateDocument } from '@/hooks/use-document-request';
import { useCallback } from 'react';
export const useCreateEmptyDocument = () => {
const { createDocument, loading } = useCreateDocument();
const {
visible: createVisible,
hideModal: hideCreateModal,
showModal: showCreateModal,
} = useSetModalState();
const onCreateOk = useCallback(
async (name: string) => {
const ret = await createDocument(name);
if (ret === 0) {
hideCreateModal();
}
},
[hideCreateModal, createDocument],
);
return {
createLoading: loading,
onCreateOk,
createVisible,
hideCreateModal,
showCreateModal,
};
};

View File

@ -22,14 +22,12 @@ import { UseChangeDocumentParserShowType } from './use-change-document-parser';
import { UseRenameDocumentShowType } from './use-rename-document'; import { UseRenameDocumentShowType } from './use-rename-document';
import { UseSaveMetaShowType } from './use-save-meta'; import { UseSaveMetaShowType } from './use-save-meta';
type UseDatasetTableColumnsType = UseChangeDocumentParserShowType & { type UseDatasetTableColumnsType = UseChangeDocumentParserShowType &
setCurrentRecord: (record: IDocumentInfo) => void; UseRenameDocumentShowType &
} & UseRenameDocumentShowType &
UseSaveMetaShowType; UseSaveMetaShowType;
export function useDatasetTableColumns({ export function useDatasetTableColumns({
showChangeParserModal, showChangeParserModal,
setCurrentRecord,
showRenameModal, showRenameModal,
showSetMetaModal, showSetMetaModal,
}: UseDatasetTableColumnsType) { }: UseDatasetTableColumnsType) {

View File

@ -31,7 +31,7 @@ export function SideBar() {
const { data } = useFetchKnowledgeBaseConfiguration(); const { data } = useFetchKnowledgeBaseConfiguration();
return ( return (
<aside className="w-[303px] relative border-r "> <aside className="w-60 relative border-r ">
<div className="p-6 space-y-2 border-b"> <div className="p-6 space-y-2 border-b">
<div <div
className="w-[70px] h-[70px] rounded-xl bg-cover" className="w-[70px] h-[70px] rounded-xl bg-cover"
@ -56,9 +56,6 @@ export function SideBar() {
> >
<item.icon className="w-6 h-6" /> <item.icon className="w-6 h-6" />
<span>{item.label}</span> <span>{item.label}</span>
{active && (
<div className="absolute right-0 w-[5px] h-[66px] bg-primary rounded-l-xl shadow-[0_0_5.94px_#7561ff,0_0_11.88px_#7561ff,0_0_41.58px_#7561ff,0_0_83.16px_#7561ff,0_0_142.56px_#7561ff,0_0_249.48px_#7561ff]" />
)}
</Button> </Button>
); );
})} })}

View File

@ -71,6 +71,8 @@
--sidebar-accent-foreground: 240 5.9% 10%; --sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%; --sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%; --sidebar-ring: 217.2 91.2% 59.8%;
--background-inverse-strong: rgba(255, 255, 255, 0.15);
} }
.dark { .dark {