fix: Change document status with @tanstack/react-query #13306 (#2788)

### What problem does this PR solve?

fix: Change document status with @tanstack/react-query #13306

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
- [ ] New Feature (non-breaking change which adds functionality)
- [ ] Documentation Update
- [ ] Refactoring
- [ ] Performance Improvement
- [ ] Other (please describe):
This commit is contained in:
balibabu 2024-10-11 08:45:10 +08:00 committed by GitHub
parent 2c56d274d8
commit a2f9c03a95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 375 additions and 865 deletions

View File

@ -18,8 +18,7 @@ export default defineConfig({
history: { history: {
type: 'browser', type: 'browser',
}, },
plugins: ['@react-dev-inspector/umi4-plugin', '@umijs/plugins/dist/dva'], plugins: ['@react-dev-inspector/umi4-plugin'],
dva: {},
jsMinifier: 'terser', jsMinifier: 'terser',
lessLoader: { lessLoader: {
modifyVars: { modifyVars: {

View File

@ -1,7 +1,5 @@
import MaxTokenNumber from '@/components/max-token-number'; import MaxTokenNumber from '@/components/max-token-number';
import { IModalManagerChildrenProps } from '@/components/modal-manager'; import { IModalManagerChildrenProps } from '@/components/modal-manager';
import { IKnowledgeFileParserConfig } from '@/interfaces/database/knowledge';
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
import { import {
MinusCircleOutlined, MinusCircleOutlined,
PlusOutlined, PlusOutlined,
@ -22,6 +20,8 @@ import React, { useEffect, useMemo } from 'react';
import { useFetchParserListOnMount } from './hooks'; import { useFetchParserListOnMount } from './hooks';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { IParserConfig } from '@/interfaces/database/document';
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
import Delimiter from '../delimiter'; import Delimiter from '../delimiter';
import EntityTypesItem from '../entity-types-item'; import EntityTypesItem from '../entity-types-item';
import ExcelToHtml from '../excel-to-html'; import ExcelToHtml from '../excel-to-html';
@ -39,7 +39,7 @@ interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> {
) => void; ) => void;
showModal?(): void; showModal?(): void;
parserId: string; parserId: string;
parserConfig: IKnowledgeFileParserConfig; parserConfig: IParserConfig;
documentExtension: string; documentExtension: string;
documentId: string; documentId: string;
} }
@ -115,7 +115,7 @@ const ChunkMethodModal: React.FC<IProps> = ({
useEffect(() => { useEffect(() => {
if (visible) { if (visible) {
const pages = const pages =
parserConfig.pages?.map((x) => ({ from: x[0], to: x[1] })) ?? []; parserConfig?.pages?.map((x) => ({ from: x[0], to: x[1] })) ?? [];
form.setFieldsValue({ form.setFieldsValue({
pages: pages.length > 0 ? pages : [{ from: 1, to: 1024 }], pages: pages.length > 0 ? pages : [{ from: 1, to: 1024 }],
parser_config: omit(parserConfig, 'pages'), parser_config: omit(parserConfig, 'pages'),

View File

@ -1,7 +1,8 @@
import { getExtension } from '@/utils/document-util'; import { getExtension } from '@/utils/document-util';
import SvgIcon from '../svg-icon'; import SvgIcon from '../svg-icon';
import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks'; import { useFetchDocumentThumbnailsByIds } from '@/hooks/document-hooks';
import { useEffect } from 'react';
import styles from './index.less'; import styles from './index.less';
interface IProps { interface IProps {
@ -11,10 +12,15 @@ interface IProps {
const FileIcon = ({ name, id }: IProps) => { const FileIcon = ({ name, id }: IProps) => {
const fileExtension = getExtension(name); const fileExtension = getExtension(name);
// TODO: replace this line with react query
const fileThumbnails = useSelectFileThumbnails(); const { data: fileThumbnails, setDocumentIds } =
useFetchDocumentThumbnailsByIds();
const fileThumbnail = fileThumbnails[id]; const fileThumbnail = fileThumbnails[id];
useEffect(() => {
setDocumentIds([id]);
}, [id, setDocumentIds]);
return fileThumbnail ? ( return fileThumbnail ? (
<img src={fileThumbnail} className={styles.thumbnailImg}></img> <img src={fileThumbnail} className={styles.thumbnailImg}></img>
) : ( ) : (

View File

@ -1,7 +1,6 @@
import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg'; import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
import { MessageType } from '@/constants/chat'; import { MessageType } from '@/constants/chat';
import { useSetModalState } from '@/hooks/common-hooks'; import { useSetModalState } from '@/hooks/common-hooks';
import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks';
import { IReference } from '@/interfaces/database/chat'; import { IReference } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge'; import { IChunk } from '@/interfaces/database/knowledge';
import classNames from 'classnames'; import classNames from 'classnames';
@ -50,7 +49,6 @@ const MessageItem = ({
}: IProps) => { }: IProps) => {
const isAssistant = item.role === MessageType.Assistant; const isAssistant = item.role === MessageType.Assistant;
const isUser = item.role === MessageType.User; const isUser = item.role === MessageType.User;
const fileThumbnails = useSelectFileThumbnails();
const { data: documentList, setDocumentIds } = useFetchDocumentInfosByIds(); const { data: documentList, setDocumentIds } = useFetchDocumentInfosByIds();
const { data: documentThumbnails, setDocumentIds: setIds } = const { data: documentThumbnails, setDocumentIds: setIds } =
useFetchDocumentThumbnailsByIds(); useFetchDocumentThumbnailsByIds();
@ -77,12 +75,12 @@ const MessageItem = ({
const ids = item?.doc_ids ?? []; const ids = item?.doc_ids ?? [];
if (ids.length) { if (ids.length) {
setDocumentIds(ids); setDocumentIds(ids);
const documentIds = ids.filter((x) => !(x in fileThumbnails)); const documentIds = ids.filter((x) => !(x in documentThumbnails));
if (documentIds.length) { if (documentIds.length) {
setIds(documentIds); setIds(documentIds);
} }
} }
}, [item.doc_ids, setDocumentIds, setIds, fileThumbnails]); }, [item.doc_ids, setDocumentIds, setIds, documentThumbnails]);
return ( return (
<div <div
@ -184,7 +182,7 @@ const MessageItem = ({
renderItem={(item) => { renderItem={(item) => {
// TODO: // TODO:
const fileThumbnail = const fileThumbnail =
documentThumbnails[item.id] || fileThumbnails[item.id]; documentThumbnails[item.id] || documentThumbnails[item.id];
const fileExtension = getExtension(item.name); const fileExtension = getExtension(item.name);
return ( return (
<List.Item> <List.Item>

View File

@ -1,17 +1,24 @@
import { IDocumentInfo } from '@/interfaces/database/document'; import { IDocumentInfo } from '@/interfaces/database/document';
import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge'; import { IChunk } from '@/interfaces/database/knowledge';
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
import i18n from '@/locales/config';
import chatService from '@/services/chat-service'; import chatService from '@/services/chat-service';
import kbService from '@/services/knowledge-service'; import kbService from '@/services/knowledge-service';
import { api_host } from '@/utils/api'; import { api_host } from '@/utils/api';
import { buildChunkHighlights } from '@/utils/document-util'; import { buildChunkHighlights } from '@/utils/document-util';
import { useMutation, useQuery } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { UploadFile } from 'antd'; import { UploadFile, message } from 'antd';
import { get } from 'lodash';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { IHighlight } from 'react-pdf-highlighter'; import { IHighlight } from 'react-pdf-highlighter';
import { useDispatch, useSelector } from 'umi'; import {
import { useGetKnowledgeSearchParams } from './route-hook'; useGetPaginationWithRouter,
import { useOneNamespaceEffectsLoading } from './store-hooks'; useHandleSearchChange,
} from './logic-hooks';
import {
useGetKnowledgeSearchParams,
useSetPaginationParams,
} from './route-hook';
export const useGetDocumentUrl = (documentId?: string) => { export const useGetDocumentUrl = (documentId?: string) => {
const getDocumentUrl = useCallback( const getDocumentUrl = useCallback(
@ -43,219 +50,284 @@ export const useGetChunkHighlights = (selectedChunk: IChunk) => {
return { highlights, setWidthAndHeight }; return { highlights, setWidthAndHeight };
}; };
export const useFetchDocumentList = () => { export const useFetchNextDocumentList = () => {
const { knowledgeId } = useGetKnowledgeSearchParams(); const { knowledgeId } = useGetKnowledgeSearchParams();
const { searchString, handleInputChange } = useHandleSearchChange();
const { pagination, setPagination } = useGetPaginationWithRouter();
const dispatch = useDispatch(); const { data, isFetching: loading } = useQuery<{
docs: IDocumentInfo[];
const fetchKfList = useCallback(() => { total: number;
return dispatch<any>({ }>({
type: 'kFModel/getKfList', queryKey: ['fetchDocumentList', searchString, pagination],
payload: { initialData: { docs: [], total: 0 },
refetchInterval: 15000,
queryFn: async () => {
const ret = await kbService.get_document_list({
kb_id: knowledgeId, kb_id: knowledgeId,
}, keywords: searchString,
}); page_size: pagination.pageSize,
}, [dispatch, knowledgeId]); page: pagination.current,
return fetchKfList;
};
export const useSetDocumentStatus = () => {
const dispatch = useDispatch();
const { knowledgeId } = useGetKnowledgeSearchParams();
const setDocumentStatus = useCallback(
(status: boolean, documentId: string) => {
dispatch({
type: 'kFModel/updateDocumentStatus',
payload: {
doc_id: documentId,
status: Number(status),
kb_id: knowledgeId,
},
}); });
if (ret.data.retcode === 0) {
return ret.data.data;
}
return {
docs: [],
total: 0,
};
}, },
[dispatch, knowledgeId], });
const onInputChange: React.ChangeEventHandler<HTMLInputElement> = useCallback(
(e) => {
setPagination({ page: 1 });
handleInputChange(e);
},
[handleInputChange, setPagination],
); );
return setDocumentStatus; return {
loading,
searchString,
documents: data.docs,
pagination: { ...pagination, total: data?.total },
handleInputChange: onInputChange,
setPagination,
};
}; };
export const useSelectDocumentList = () => { export const useSetNextDocumentStatus = () => {
const list: IKnowledgeFile[] = useSelector( const queryClient = useQueryClient();
(state: any) => state.kFModel.data,
);
return list;
};
export const useSaveDocumentName = () => { const {
const dispatch = useDispatch(); data,
const { knowledgeId } = useGetKnowledgeSearchParams(); isPending: loading,
mutateAsync,
const saveName = useCallback( } = useMutation({
(documentId: string, name: string) => { mutationKey: ['updateDocumentStatus'],
return dispatch<any>({ mutationFn: async ({
type: 'kFModel/document_rename', status,
payload: { documentId,
doc_id: documentId, }: {
name: name, status: boolean;
kb_id: knowledgeId, documentId: string;
}, }) => {
const { data } = await kbService.document_change_status({
doc_id: documentId,
status: Number(status),
}); });
if (data.retcode === 0) {
message.success(i18n.t('message.modified'));
queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] });
}
return data;
}, },
[dispatch, knowledgeId], });
);
return saveName; return { setDocumentStatus: mutateAsync, data, loading };
}; };
export const useCreateDocument = () => { export const useSaveNextDocumentName = () => {
const dispatch = useDispatch(); const queryClient = useQueryClient();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['saveDocumentName'],
mutationFn: async ({
name,
documentId,
}: {
name: string;
documentId: string;
}) => {
const { data } = await kbService.document_rename({
doc_id: documentId,
name: name,
});
if (data.retcode === 0) {
message.success(i18n.t('message.renamed'));
queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] });
}
return data.retcode;
},
});
return { loading, saveName: mutateAsync, data };
};
export const useCreateNextDocument = () => {
const { knowledgeId } = useGetKnowledgeSearchParams();
const { setPaginationParams, page } = useSetPaginationParams();
const queryClient = useQueryClient();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['createDocument'],
mutationFn: async (name: string) => {
const { data } = await kbService.document_create({
name,
kb_id: knowledgeId,
});
if (data.retcode === 0) {
if (page === 1) {
queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] });
} else {
setPaginationParams(); // fetch document list
}
message.success(i18n.t('message.created'));
}
return data.retcode;
},
});
return { createDocument: mutateAsync, loading, data };
};
export const useSetNextDocumentParser = () => {
const queryClient = useQueryClient();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['setDocumentParser'],
mutationFn: async ({
parserId,
documentId,
parserConfig,
}: {
parserId: string;
documentId: string;
parserConfig: IChangeParserConfigRequestBody;
}) => {
const { data } = await kbService.document_change_parser({
parser_id: parserId,
doc_id: documentId,
parser_config: parserConfig,
});
if (data.retcode === 0) {
queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] });
message.success(i18n.t('message.modified'));
}
return data.retcode;
},
});
return { setDocumentParser: mutateAsync, data, loading };
};
export const useUploadNextDocument = () => {
const queryClient = useQueryClient();
const { knowledgeId } = useGetKnowledgeSearchParams(); const { knowledgeId } = useGetKnowledgeSearchParams();
const createDocument = useCallback( const {
(name: string) => { data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['uploadDocument'],
mutationFn: async (fileList: UploadFile[]) => {
const formData = new FormData();
formData.append('kb_id', knowledgeId);
fileList.forEach((file: any) => {
formData.append('file', file);
});
try { try {
return dispatch<any>({ const ret = await kbService.document_upload(formData);
type: 'kFModel/document_create', const retcode = get(ret, 'data.retcode');
payload: { if (retcode === 0) {
name, message.success(i18n.t('message.uploaded'));
kb_id: knowledgeId, }
},
}); if (retcode === 0 || retcode === 500) {
} catch (errorInfo) { queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] });
console.log('Failed:', errorInfo); }
return ret?.data;
} catch (error) {
console.warn(error);
return {};
} }
}, },
[dispatch, knowledgeId], });
);
return createDocument; return { uploadDocument: mutateAsync, loading, data };
}; };
export const useSetDocumentParser = () => { export const useNextWebCrawl = () => {
const dispatch = useDispatch();
const { knowledgeId } = useGetKnowledgeSearchParams(); const { knowledgeId } = useGetKnowledgeSearchParams();
const setDocumentParser = useCallback( const {
( data,
parserId: string, isPending: loading,
documentId: string, mutateAsync,
parserConfig: IChangeParserConfigRequestBody, } = useMutation({
) => { mutationKey: ['webCrawl'],
try { mutationFn: async ({ name, url }: { name: string; url: string }) => {
return dispatch<any>({ const formData = new FormData();
type: 'kFModel/document_change_parser', formData.append('name', name);
payload: { formData.append('url', url);
parser_id: parserId, formData.append('kb_id', knowledgeId);
doc_id: documentId,
kb_id: knowledgeId,
parser_config: parserConfig,
},
});
} catch (errorInfo) {
console.log('Failed:', errorInfo);
}
},
[dispatch, knowledgeId],
);
return setDocumentParser; const ret = await kbService.web_crawl(formData);
const retcode = get(ret, 'data.retcode');
if (retcode === 0) {
message.success(i18n.t('message.uploaded'));
}
return retcode;
},
});
return {
data,
loading,
webCrawl: mutateAsync,
};
}; };
export const useRemoveDocument = () => { export const useRunNextDocument = () => {
const dispatch = useDispatch(); const queryClient = useQueryClient();
const { knowledgeId } = useGetKnowledgeSearchParams();
const removeDocument = useCallback( const {
(documentIds: string[]) => { data,
try { isPending: loading,
return dispatch<any>({ mutateAsync,
type: 'kFModel/document_rm', } = useMutation({
payload: { mutationKey: ['runDocumentByIds'],
doc_id: documentIds, mutationFn: async ({
kb_id: knowledgeId, documentIds,
}, run,
}); }: {
} catch (errorInfo) { documentIds: string[];
console.log('Failed:', errorInfo); run: number;
}) => {
const ret = await kbService.document_run({
doc_ids: documentIds,
run,
});
const retcode = get(ret, 'data.retcode');
if (retcode === 0) {
queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] });
message.success(i18n.t('message.operated'));
} }
return retcode;
}, },
[dispatch, knowledgeId], });
);
return removeDocument; return { runDocumentByIds: mutateAsync, loading, data };
};
export const useUploadDocument = () => {
const dispatch = useDispatch();
const { knowledgeId } = useGetKnowledgeSearchParams();
const uploadDocument = useCallback(
(fileList: UploadFile[]) => {
try {
return dispatch<any>({
type: 'kFModel/upload_document',
payload: {
fileList,
kb_id: knowledgeId,
},
});
} catch (errorInfo) {
console.log('Failed:', errorInfo);
}
},
[dispatch, knowledgeId],
);
return uploadDocument;
};
export const useWebCrawl = () => {
const dispatch = useDispatch();
const { knowledgeId } = useGetKnowledgeSearchParams();
return useCallback(
(name: string, url: string) => {
try {
return dispatch<any>({
type: 'kFModel/web_crawl',
payload: {
name,
url,
kb_id: knowledgeId,
},
});
} catch (errorInfo) {
console.log('Failed:', errorInfo);
}
},
[dispatch],
);
};
export const useRunDocument = () => {
const dispatch = useDispatch();
const runDocumentByIds = useCallback(
(payload: any) => {
try {
return dispatch<any>({
type: 'kFModel/document_run',
payload,
});
} catch (errorInfo) {
console.log('Failed:', errorInfo);
}
},
[dispatch],
);
return runDocumentByIds;
};
export const useSelectRunDocumentLoading = () => {
const loading = useOneNamespaceEffectsLoading('kFModel', ['document_run']);
return loading;
}; };
export const useFetchDocumentInfosByIds = () => { export const useFetchDocumentInfosByIds = () => {
@ -296,19 +368,20 @@ export const useFetchDocumentThumbnailsByIds = () => {
}; };
export const useRemoveNextDocument = () => { export const useRemoveNextDocument = () => {
// const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { const {
data, data,
isPending: loading, isPending: loading,
mutateAsync, mutateAsync,
} = useMutation({ } = useMutation({
mutationKey: ['removeDocument'], mutationKey: ['removeDocument'],
mutationFn: async (documentId: string) => { mutationFn: async (documentIds: string | string[]) => {
const data = await kbService.document_rm({ doc_id: documentId }); const { data } = await kbService.document_rm({ doc_id: documentIds });
// if (data.retcode === 0) { if (data.retcode === 0) {
// queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] }); message.success(i18n.t('message.deleted'));
// } queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] });
return data; }
return data.retcode;
}, },
}); });

View File

@ -61,8 +61,7 @@ export const useFetchFileList = (): ResponseType<any> & IListResult => {
], ],
initialData: {}, initialData: {},
gcTime: 0, gcTime: 0,
queryFn: async (params: any) => { queryFn: async () => {
console.info(params);
const { data } = await fileManagerService.listFile({ const { data } = await fileManagerService.listFile({
parent_id: id, parent_id: id,
keywords: searchString, keywords: searchString,

View File

@ -1,4 +1,3 @@
import { useShowDeleteConfirm } from '@/hooks/common-hooks';
import { ResponsePostType } from '@/interfaces/database/base'; import { ResponsePostType } from '@/interfaces/database/base';
import { IKnowledge, ITestingResult } from '@/interfaces/database/knowledge'; import { IKnowledge, ITestingResult } from '@/interfaces/database/knowledge';
import i18n from '@/locales/config'; import i18n from '@/locales/config';
@ -11,8 +10,7 @@ import {
useQueryClient, useQueryClient,
} from '@tanstack/react-query'; } from '@tanstack/react-query';
import { message } from 'antd'; import { message } from 'antd';
import { useCallback, useEffect } from 'react'; import { useSearchParams } from 'umi';
import { useDispatch, useSearchParams, useSelector } from 'umi';
import { useSetPaginationParams } from './route-hook'; import { useSetPaginationParams } from './route-hook';
export const useKnowledgeBaseId = (): string => { export const useKnowledgeBaseId = (): string => {
@ -22,32 +20,6 @@ export const useKnowledgeBaseId = (): string => {
return knowledgeBaseId || ''; return knowledgeBaseId || '';
}; };
export const useDeleteDocumentById = (): {
removeDocument: (documentId: string) => Promise<number>;
} => {
const dispatch = useDispatch();
const knowledgeBaseId = useKnowledgeBaseId();
const showDeleteConfirm = useShowDeleteConfirm();
const removeDocument = (documentId: string) => () => {
return dispatch({
type: 'kFModel/document_rm',
payload: {
doc_id: documentId,
kb_id: knowledgeBaseId,
},
});
};
const onRmDocument = (documentId: string): Promise<number> => {
return showDeleteConfirm({ onOk: removeDocument(documentId) });
};
return {
removeDocument: onRmDocument,
};
};
export const useFetchKnowledgeBaseConfiguration = () => { export const useFetchKnowledgeBaseConfiguration = () => {
const knowledgeBaseId = useKnowledgeBaseId(); const knowledgeBaseId = useKnowledgeBaseId();
@ -132,37 +104,6 @@ export const useDeleteKnowledge = () => {
return { data, loading, deleteKnowledge: mutateAsync }; return { data, loading, deleteKnowledge: mutateAsync };
}; };
export const useSelectFileThumbnails = () => {
const fileThumbnails: Record<string, string> = useSelector(
(state: any) => state.kFModel.fileThumbnails,
);
return fileThumbnails;
};
export const useFetchFileThumbnails = (docIds?: Array<string>) => {
const dispatch = useDispatch();
const fileThumbnails = useSelectFileThumbnails();
const fetchFileThumbnails = useCallback(
(docIds: Array<string>) => {
dispatch({
type: 'kFModel/fetch_document_thumbnails',
payload: { doc_ids: docIds.join(',') },
});
},
[dispatch],
);
useEffect(() => {
if (docIds) {
fetchFileThumbnails(docIds);
}
}, [docIds, fetchFileThumbnails]);
return { fileThumbnails, fetchFileThumbnails };
};
//#region knowledge configuration //#region knowledge configuration
export const useUpdateKnowledge = () => { export const useUpdateKnowledge = () => {

View File

@ -1,11 +1,9 @@
import { Authorization } from '@/constants/authorization'; import { Authorization } from '@/constants/authorization';
import { MessageType } from '@/constants/chat'; import { MessageType } from '@/constants/chat';
import { LanguageTranslationMap } from '@/constants/common'; import { LanguageTranslationMap } from '@/constants/common';
import { Pagination } from '@/interfaces/common';
import { ResponseType } from '@/interfaces/database/base'; import { ResponseType } from '@/interfaces/database/base';
import { IAnswer, Message } from '@/interfaces/database/chat'; import { IAnswer, Message } from '@/interfaces/database/chat';
import { IKnowledgeFile } from '@/interfaces/database/knowledge'; import { IKnowledgeFile } from '@/interfaces/database/knowledge';
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
import { IClientConversation, IMessage } from '@/pages/chat/interface'; import { IClientConversation, IMessage } from '@/pages/chat/interface';
import api from '@/utils/api'; import api from '@/utils/api';
import { getAuthorization } from '@/utils/authorization-util'; import { getAuthorization } from '@/utils/authorization-util';
@ -23,45 +21,11 @@ import {
useState, useState,
} from 'react'; } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useDispatch } from 'umi';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { useSetModalState, useTranslate } from './common-hooks'; import { useTranslate } from './common-hooks';
import { useSetDocumentParser } from './document-hooks';
import { useSetPaginationParams } from './route-hook'; import { useSetPaginationParams } from './route-hook';
import { useOneNamespaceEffectsLoading } from './store-hooks';
import { useFetchTenantInfo, useSaveSetting } from './user-setting-hooks'; import { useFetchTenantInfo, useSaveSetting } from './user-setting-hooks';
export const useChangeDocumentParser = (documentId: string) => {
const setDocumentParser = useSetDocumentParser();
const {
visible: changeParserVisible,
hideModal: hideChangeParserModal,
showModal: showChangeParserModal,
} = useSetModalState();
const loading = useOneNamespaceEffectsLoading('kFModel', [
'document_change_parser',
]);
const onChangeParserOk = useCallback(
async (parserId: string, parserConfig: IChangeParserConfigRequestBody) => {
const ret = await setDocumentParser(parserId, documentId, parserConfig);
if (ret === 0) {
hideChangeParserModal();
}
},
[hideChangeParserModal, setDocumentParser, documentId],
);
return {
changeParserLoading: loading,
onChangeParserOk,
changeParserVisible,
hideChangeParserModal,
showChangeParserModal,
};
};
export const useSetSelectedRecord = <T = IKnowledgeFile>() => { export const useSetSelectedRecord = <T = IKnowledgeFile>() => {
const [currentRecord, setCurrentRecord] = useState<T>({} as T); const [currentRecord, setCurrentRecord] = useState<T>({} as T);
@ -170,28 +134,6 @@ export const useGetPagination = () => {
}; };
}; };
export const useSetPagination = (namespace: string) => {
const dispatch = useDispatch();
const setPagination = useCallback(
(pageNumber = 1, pageSize?: number) => {
const pagination: Pagination = {
current: pageNumber,
} as Pagination;
if (pageSize) {
pagination.pageSize = pageSize;
}
dispatch({
type: `${namespace}/setPagination`,
payload: pagination,
});
},
[dispatch, namespace],
);
return setPagination;
};
export interface AppConf { export interface AppConf {
appName: string; appName: string;
} }

View File

@ -1,11 +0,0 @@
import { getOneNamespaceEffectsLoading } from '@/utils/store-util';
import { useSelector } from 'umi';
// Get the loading status of given effects under a certain namespace
export const useOneNamespaceEffectsLoading = (
namespace: string,
effectNames: Array<string>,
) => {
const effects = useSelector((state: any) => state.loading.effects);
return getOneNamespaceEffectsLoading(namespace, effects, effectNames);
};

View File

@ -1,3 +1,5 @@
import { RunningStatus } from '@/constants/knowledge';
export interface IDocumentInfo { export interface IDocumentInfo {
chunk_num: number; chunk_num: number;
create_date: string; create_date: string;
@ -7,13 +9,13 @@ export interface IDocumentInfo {
kb_id: string; kb_id: string;
location: string; location: string;
name: string; name: string;
parser_config: Parserconfig; parser_config: IParserConfig;
parser_id: string; parser_id: string;
process_begin_at: null; process_begin_at: null;
process_duation: number; process_duation: number;
progress: number; progress: number;
progress_msg: string; progress_msg: string;
run: string; run: RunningStatus;
size: number; size: number;
source_type: string; source_type: string;
status: string; status: string;
@ -24,9 +26,11 @@ export interface IDocumentInfo {
update_time: number; update_time: number;
} }
interface Parserconfig { export interface IParserConfig {
chunk_token_num: number; delimiter: string;
html4excel: boolean;
layout_recognize: boolean; layout_recognize: boolean;
pages: any[];
raptor: Raptor; raptor: Raptor;
} }

View File

@ -5,11 +5,10 @@ import { ReactComponent as EnableIcon } from '@/assets/svg/enable.svg';
import { ReactComponent as RunIcon } from '@/assets/svg/run.svg'; import { ReactComponent as RunIcon } from '@/assets/svg/run.svg';
import { useShowDeleteConfirm, useTranslate } from '@/hooks/common-hooks'; import { useShowDeleteConfirm, useTranslate } from '@/hooks/common-hooks';
import { import {
useRemoveDocument, useRemoveNextDocument,
useRunDocument, useRunNextDocument,
useSetDocumentStatus, useSetNextDocumentStatus,
} from '@/hooks/document-hooks'; } from '@/hooks/document-hooks';
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
import { import {
DownOutlined, DownOutlined,
FileOutlined, FileOutlined,
@ -19,11 +18,7 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
import { Button, Dropdown, Flex, Input, MenuProps, Space } from 'antd'; import { Button, Dropdown, Flex, Input, MenuProps, Space } from 'antd';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import {
useFetchDocumentListOnMount,
useGetPagination,
useHandleSearchChange,
} from './hooks';
import styles from './index.less'; import styles from './index.less';
interface IProps { interface IProps {
@ -31,23 +26,22 @@ interface IProps {
showCreateModal(): void; showCreateModal(): void;
showWebCrawlModal(): void; showWebCrawlModal(): void;
showDocumentUploadModal(): void; showDocumentUploadModal(): void;
searchString: string;
handleInputChange: React.ChangeEventHandler<HTMLInputElement>;
} }
const DocumentToolbar = ({ const DocumentToolbar = ({
searchString,
selectedRowKeys, selectedRowKeys,
showCreateModal, showCreateModal,
showWebCrawlModal,
showDocumentUploadModal, showDocumentUploadModal,
handleInputChange,
}: IProps) => { }: IProps) => {
const { t } = useTranslate('knowledgeDetails'); const { t } = useTranslate('knowledgeDetails');
const { fetchDocumentList } = useFetchDocumentListOnMount(); const { removeDocument } = useRemoveNextDocument();
const { setPagination, searchString } = useGetPagination(fetchDocumentList);
const { handleInputChange } = useHandleSearchChange(setPagination);
const removeDocument = useRemoveDocument();
const showDeleteConfirm = useShowDeleteConfirm(); const showDeleteConfirm = useShowDeleteConfirm();
const runDocumentByIds = useRunDocument(); const { runDocumentByIds } = useRunNextDocument();
const { knowledgeId } = useGetKnowledgeSearchParams(); const { setDocumentStatus } = useSetNextDocumentStatus();
const changeStatus = useSetDocumentStatus();
const actionItems: MenuProps['items'] = useMemo(() => { const actionItems: MenuProps['items'] = useMemo(() => {
return [ return [
@ -66,19 +60,6 @@ const DocumentToolbar = ({
), ),
}, },
{ type: 'divider' }, { type: 'divider' },
// {
// key: '2',
// onClick: showWebCrawlModal,
// label: (
// <div>
// <Button type="link">
// <FileTextOutlined />
// {t('webCrawl')}
// </Button>
// </div>
// ),
// },
{ type: 'divider' },
{ {
key: '3', key: '3',
onClick: showCreateModal, onClick: showCreateModal,
@ -105,12 +86,11 @@ const DocumentToolbar = ({
const runDocument = useCallback( const runDocument = useCallback(
(run: number) => { (run: number) => {
runDocumentByIds({ runDocumentByIds({
doc_ids: selectedRowKeys, documentIds: selectedRowKeys,
run, run,
knowledgeBaseId: knowledgeId,
}); });
}, },
[runDocumentByIds, selectedRowKeys, knowledgeId], [runDocumentByIds, selectedRowKeys],
); );
const handleRunClick = useCallback(() => { const handleRunClick = useCallback(() => {
@ -124,10 +104,10 @@ const DocumentToolbar = ({
const onChangeStatus = useCallback( const onChangeStatus = useCallback(
(enabled: boolean) => { (enabled: boolean) => {
selectedRowKeys.forEach((id) => { selectedRowKeys.forEach((id) => {
changeStatus(enabled, id); setDocumentStatus({ status: enabled, documentId: id });
}); });
}, },
[selectedRowKeys, changeStatus], [selectedRowKeys, setDocumentStatus],
); );
const handleEnableClick = useCallback(() => { const handleEnableClick = useCallback(() => {

View File

@ -1,104 +1,20 @@
import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; import { useSetModalState } from '@/hooks/common-hooks';
import { import {
useCreateDocument, useCreateNextDocument,
useFetchDocumentList, useNextWebCrawl,
useRunDocument, useRunNextDocument,
useSaveDocumentName, useSaveNextDocumentName,
useSelectRunDocumentLoading, useSetNextDocumentParser,
useSetDocumentParser, useUploadNextDocument,
useUploadDocument,
useWebCrawl,
} from '@/hooks/document-hooks'; } from '@/hooks/document-hooks';
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook'; import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks';
import { Pagination } from '@/interfaces/common';
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
import { getUnSupportedFilesCount } from '@/utils/document-util'; import { getUnSupportedFilesCount } from '@/utils/document-util';
import { PaginationProps, UploadFile } from 'antd'; import { UploadFile } from 'antd';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useState } from 'react';
import { useDispatch, useNavigate, useSelector } from 'umi'; import { useNavigate } from 'umi';
import { KnowledgeRouteKey } from './constant'; import { KnowledgeRouteKey } from './constant';
export const useFetchDocumentListOnMount = () => {
const { knowledgeId } = useGetKnowledgeSearchParams();
const fetchDocumentList = useFetchDocumentList();
const dispatch = useDispatch();
useEffect(() => {
if (knowledgeId) {
fetchDocumentList();
dispatch({
type: 'kFModel/pollGetDocumentList-start',
payload: knowledgeId,
});
}
return () => {
dispatch({
type: 'kFModel/pollGetDocumentList-stop',
});
};
}, [knowledgeId, dispatch, fetchDocumentList]);
return { fetchDocumentList };
};
export const useGetPagination = (fetchDocumentList: () => void) => {
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 useSelectDocumentListLoading = () => {
return useOneNamespaceEffectsLoading('kFModel', [
'getKfList',
'updateDocumentStatus',
]);
};
export const useNavigateToOtherPage = () => { export const useNavigateToOtherPage = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { knowledgeId } = useGetKnowledgeSearchParams(); const { knowledgeId } = useGetKnowledgeSearchParams();
@ -119,43 +35,18 @@ export const useNavigateToOtherPage = () => {
return { linkToUploadPage, toChunk }; return { linkToUploadPage, toChunk };
}; };
export const useHandleSearchChange = (setPagination: () => void) => {
const dispatch = useDispatch();
const { knowledgeId } = useGetKnowledgeSearchParams();
const throttledGetDocumentList = useCallback(() => {
dispatch({
type: 'kFModel/throttledGetDocumentList',
payload: knowledgeId,
});
}, [dispatch, knowledgeId]);
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 useRenameDocument = (documentId: string) => { export const useRenameDocument = (documentId: string) => {
const saveName = useSaveDocumentName(); const { saveName, loading } = useSaveNextDocumentName();
const { const {
visible: renameVisible, visible: renameVisible,
hideModal: hideRenameModal, hideModal: hideRenameModal,
showModal: showRenameModal, showModal: showRenameModal,
} = useSetModalState(); } = useSetModalState();
const loading = useOneNamespaceEffectsLoading('kFModel', ['document_rename']);
const onRenameOk = useCallback( const onRenameOk = useCallback(
async (name: string) => { async (name: string) => {
const ret = await saveName(documentId, name); const ret = await saveName({ documentId, name });
if (ret === 0) { if (ret === 0) {
hideRenameModal(); hideRenameModal();
} }
@ -173,14 +64,13 @@ export const useRenameDocument = (documentId: string) => {
}; };
export const useCreateEmptyDocument = () => { export const useCreateEmptyDocument = () => {
const createDocument = useCreateDocument(); const { createDocument, loading } = useCreateNextDocument();
const { const {
visible: createVisible, visible: createVisible,
hideModal: hideCreateModal, hideModal: hideCreateModal,
showModal: showCreateModal, showModal: showCreateModal,
} = useSetModalState(); } = useSetModalState();
const loading = useOneNamespaceEffectsLoading('kFModel', ['document_create']);
const onCreateOk = useCallback( const onCreateOk = useCallback(
async (name: string) => { async (name: string) => {
@ -202,20 +92,21 @@ export const useCreateEmptyDocument = () => {
}; };
export const useChangeDocumentParser = (documentId: string) => { export const useChangeDocumentParser = (documentId: string) => {
const setDocumentParser = useSetDocumentParser(); const { setDocumentParser, loading } = useSetNextDocumentParser();
const { const {
visible: changeParserVisible, visible: changeParserVisible,
hideModal: hideChangeParserModal, hideModal: hideChangeParserModal,
showModal: showChangeParserModal, showModal: showChangeParserModal,
} = useSetModalState(); } = useSetModalState();
const loading = useOneNamespaceEffectsLoading('kFModel', [
'document_change_parser',
]);
const onChangeParserOk = useCallback( const onChangeParserOk = useCallback(
async (parserId: string, parserConfig: IChangeParserConfigRequestBody) => { async (parserId: string, parserConfig: IChangeParserConfigRequestBody) => {
const ret = await setDocumentParser(parserId, documentId, parserConfig); const ret = await setDocumentParser({
parserId,
documentId,
parserConfig,
});
if (ret === 0) { if (ret === 0) {
hideChangeParserModal(); hideChangeParserModal();
} }
@ -251,18 +142,21 @@ export const useHandleUploadDocument = () => {
hideModal: hideDocumentUploadModal, hideModal: hideDocumentUploadModal,
showModal: showDocumentUploadModal, showModal: showDocumentUploadModal,
} = useSetModalState(); } = useSetModalState();
const uploadDocument = useUploadDocument(); const { uploadDocument, loading } = useUploadNextDocument();
const onDocumentUploadOk = useCallback( const onDocumentUploadOk = useCallback(
async (fileList: UploadFile[]): Promise<number | undefined> => { async (fileList: UploadFile[]): Promise<number | undefined> => {
if (fileList.length > 0) { if (fileList.length > 0) {
const ret: any = await uploadDocument(fileList); const ret: any = await uploadDocument(fileList);
const count = getUnSupportedFilesCount(ret.retmsg); if (typeof ret?.retmsg !== 'string') {
return;
}
const count = getUnSupportedFilesCount(ret?.retmsg);
/// 500 error code indicates that some file types are not supported /// 500 error code indicates that some file types are not supported
let retcode = ret.retcode; let retcode = ret?.retcode;
if ( if (
ret.retcode === 0 || ret?.retcode === 0 ||
(ret.retcode === 500 && count !== fileList.length) // Some files were not uploaded successfully, but some were uploaded successfully. (ret?.retcode === 500 && count !== fileList.length) // Some files were not uploaded successfully, but some were uploaded successfully.
) { ) {
retcode = 0; retcode = 0;
hideDocumentUploadModal(); hideDocumentUploadModal();
@ -273,8 +167,6 @@ export const useHandleUploadDocument = () => {
[uploadDocument, hideDocumentUploadModal], [uploadDocument, hideDocumentUploadModal],
); );
const loading = useOneNamespaceEffectsLoading('kFModel', ['upload_document']);
return { return {
documentUploadLoading: loading, documentUploadLoading: loading,
onDocumentUploadOk, onDocumentUploadOk,
@ -290,11 +182,11 @@ export const useHandleWebCrawl = () => {
hideModal: hideWebCrawlUploadModal, hideModal: hideWebCrawlUploadModal,
showModal: showWebCrawlUploadModal, showModal: showWebCrawlUploadModal,
} = useSetModalState(); } = useSetModalState();
const webCrawl = useWebCrawl(); const { webCrawl, loading } = useNextWebCrawl();
const onWebCrawlUploadOk = useCallback( const onWebCrawlUploadOk = useCallback(
async (name: string, url: string) => { async (name: string, url: string) => {
const ret = await webCrawl(name, url); const ret = await webCrawl({ name, url });
if (ret === 0) { if (ret === 0) {
hideWebCrawlUploadModal(); hideWebCrawlUploadModal();
return 0; return 0;
@ -304,8 +196,6 @@ export const useHandleWebCrawl = () => {
[webCrawl, hideWebCrawlUploadModal], [webCrawl, hideWebCrawlUploadModal],
); );
const loading = useOneNamespaceEffectsLoading('kFModel', ['web_crawl']);
return { return {
webCrawlUploadLoading: loading, webCrawlUploadLoading: loading,
onWebCrawlUploadOk, onWebCrawlUploadOk,
@ -316,14 +206,12 @@ export const useHandleWebCrawl = () => {
}; };
export const useHandleRunDocumentByIds = (id: string) => { export const useHandleRunDocumentByIds = (id: string) => {
const loading = useSelectRunDocumentLoading(); const { runDocumentByIds, loading } = useRunNextDocument();
const runDocumentByIds = useRunDocument();
const [currentId, setCurrentId] = useState<string>(''); const [currentId, setCurrentId] = useState<string>('');
const isLoading = loading && currentId !== '' && currentId === id; const isLoading = loading && currentId !== '' && currentId === id;
const handleRunDocumentByIds = async ( const handleRunDocumentByIds = async (
documentId: string, documentId: string,
knowledgeBaseId: string,
isRunning: boolean, isRunning: boolean,
) => { ) => {
if (isLoading) { if (isLoading) {
@ -332,9 +220,8 @@ export const useHandleRunDocumentByIds = (id: string) => {
setCurrentId(documentId); setCurrentId(documentId);
try { try {
await runDocumentByIds({ await runDocumentByIds({
doc_ids: [documentId], documentIds: [documentId],
run: isRunning ? 2 : 1, run: isRunning ? 2 : 1,
knowledgeBaseId,
}); });
setCurrentId(''); setCurrentId('');
} catch (error) { } catch (error) {

View File

@ -1,12 +1,11 @@
import ChunkMethodModal from '@/components/chunk-method-modal'; import ChunkMethodModal from '@/components/chunk-method-modal';
import SvgIcon from '@/components/svg-icon'; import SvgIcon from '@/components/svg-icon';
import { import {
useSelectDocumentList, useFetchNextDocumentList,
useSetDocumentStatus, useSetNextDocumentStatus,
} from '@/hooks/document-hooks'; } from '@/hooks/document-hooks';
import { useSetSelectedRecord } from '@/hooks/logic-hooks'; import { useSetSelectedRecord } from '@/hooks/logic-hooks';
import { useSelectParserList } from '@/hooks/user-setting-hooks'; import { useSelectParserList } from '@/hooks/user-setting-hooks';
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
import { getExtension } from '@/utils/document-util'; import { getExtension } from '@/utils/document-util';
import { Divider, Flex, Switch, Table, Typography } from 'antd'; import { Divider, Flex, Switch, Table, Typography } from 'antd';
import type { ColumnsType } from 'antd/es/table'; import type { ColumnsType } from 'antd/es/table';
@ -16,8 +15,6 @@ import DocumentToolbar from './document-toolbar';
import { import {
useChangeDocumentParser, useChangeDocumentParser,
useCreateEmptyDocument, useCreateEmptyDocument,
useFetchDocumentListOnMount,
useGetPagination,
useGetRowSelection, useGetRowSelection,
useHandleUploadDocument, useHandleUploadDocument,
useHandleWebCrawl, useHandleWebCrawl,
@ -30,19 +27,19 @@ import RenameModal from './rename-modal';
import WebCrawlModal from './web-crawl-modal'; import WebCrawlModal from './web-crawl-modal';
import FileUploadModal from '@/components/file-upload-modal'; import FileUploadModal from '@/components/file-upload-modal';
import { IDocumentInfo } from '@/interfaces/database/document';
import { formatDate } from '@/utils/date'; import { formatDate } from '@/utils/date';
import styles from './index.less'; import styles from './index.less';
const { Text } = Typography; const { Text } = Typography;
const KnowledgeFile = () => { const KnowledgeFile = () => {
const data = useSelectDocumentList(); const { searchString, documents, pagination, handleInputChange } =
const { fetchDocumentList } = useFetchDocumentListOnMount(); useFetchNextDocumentList();
const parserList = useSelectParserList(); const parserList = useSelectParserList();
const { pagination } = useGetPagination(fetchDocumentList); const { setDocumentStatus } = useSetNextDocumentStatus();
const onChangeStatus = useSetDocumentStatus();
const { toChunk } = useNavigateToOtherPage(); const { toChunk } = useNavigateToOtherPage();
const { currentRecord, setRecord } = useSetSelectedRecord(); const { currentRecord, setRecord } = useSetSelectedRecord<IDocumentInfo>();
const { const {
renameLoading, renameLoading,
onRenameOk, onRenameOk,
@ -84,7 +81,7 @@ const KnowledgeFile = () => {
const rowSelection = useGetRowSelection(); const rowSelection = useGetRowSelection();
const columns: ColumnsType<IKnowledgeFile> = [ const columns: ColumnsType<IDocumentInfo> = [
{ {
title: t('name'), title: t('name'),
dataIndex: 'name', dataIndex: 'name',
@ -138,7 +135,7 @@ const KnowledgeFile = () => {
<Switch <Switch
checked={status === '1'} checked={status === '1'}
onChange={(e) => { onChange={(e) => {
onChangeStatus(e, id); setDocumentStatus({ status: e, documentId: id });
}} }}
/> />
</> </>
@ -181,12 +178,13 @@ const KnowledgeFile = () => {
showCreateModal={showCreateModal} showCreateModal={showCreateModal}
showWebCrawlModal={showWebCrawlUploadModal} showWebCrawlModal={showWebCrawlUploadModal}
showDocumentUploadModal={showDocumentUploadModal} showDocumentUploadModal={showDocumentUploadModal}
searchString={searchString}
handleInputChange={handleInputChange}
></DocumentToolbar> ></DocumentToolbar>
<Table <Table
rowKey="id" rowKey="id"
columns={finalColumns} columns={finalColumns}
dataSource={data} dataSource={documents}
// loading={loading}
pagination={pagination} pagination={pagination}
rowSelection={rowSelection} rowSelection={rowSelection}
className={styles.documentTable} className={styles.documentTable}

View File

@ -1,276 +0,0 @@
import { BaseState } from '@/interfaces/common';
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
import i18n from '@/locales/config';
import kbService, { getDocumentFile } from '@/services/knowledge-service';
import { message } from 'antd';
import omit from 'lodash/omit';
import pick from 'lodash/pick';
import { DvaModel } from 'umi';
export interface KFModelState extends BaseState {
tenantIfo: any;
data: IKnowledgeFile[];
total: number;
currentRecord: Nullable<IKnowledgeFile>;
fileThumbnails: Record<string, string>;
}
const model: DvaModel<KFModelState> = {
namespace: 'kFModel',
state: {
tenantIfo: {},
data: [],
total: 0,
currentRecord: null,
searchString: '',
pagination: {
current: 1,
pageSize: 10,
},
fileThumbnails: {} as Record<string, string>,
},
reducers: {
updateState(state, { payload }) {
return {
...state,
...payload,
};
},
setCurrentRecord(state, { payload }) {
return { ...state, currentRecord: payload };
},
setSearchString(state, { payload }) {
return { ...state, searchString: payload };
},
setPagination(state, { payload }) {
return { ...state, pagination: { ...state.pagination, ...payload } };
},
setFileThumbnails(state, { payload }) {
return { ...state, fileThumbnails: payload };
},
},
effects: {
*createKf({ payload = {} }, { call }) {
const { data } = yield call(kbService.createKb, payload);
const { retcode } = data;
if (retcode === 0) {
message.success(i18n.t('message.created'));
}
},
*updateKf({ payload = {} }, { call }) {
const { data } = yield call(kbService.updateKb, payload);
const { retcode } = data;
if (retcode === 0) {
message.success(i18n.t('message.modified'));
}
},
*getKfDetail({ payload = {} }, { call }) {
const { data } = yield call(kbService.get_kb_detail, payload);
},
*getKfList({ payload = {} }, { call, put, select }) {
const state: KFModelState = yield select((state: any) => state.kFModel);
const requestBody = {
...payload,
page: state.pagination.current,
page_size: state.pagination.pageSize,
};
if (state.searchString) {
requestBody['keywords'] = state.searchString;
}
const { data } = yield call(kbService.get_document_list, requestBody);
const { retcode, data: res } = data;
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
data: res.docs,
total: res.total,
},
});
}
},
throttledGetDocumentList: [
function* ({ payload }, { call, put }) {
yield put({ type: 'getKfList', payload: { kb_id: payload } });
},
{ type: 'throttle', ms: 1000 }, // TODO: Provide type support for this effect
],
pollGetDocumentList: [
function* ({ payload }, { call, put }) {
yield put({ type: 'getKfList', payload: { kb_id: payload } });
},
{ type: 'poll', delay: 15000 }, // TODO: Provide type support for this effect
],
*updateDocumentStatus({ payload = {} }, { call, put }) {
const { data } = yield call(
kbService.document_change_status,
pick(payload, ['doc_id', 'status']),
);
const { retcode } = data;
if (retcode === 0) {
message.success(i18n.t('message.modified'));
yield put({
type: 'getKfList',
payload: { kb_id: payload.kb_id },
});
}
},
*document_rm({ payload = {} }, { call, put }) {
const { data } = yield call(kbService.document_rm, {
doc_id: payload.doc_id,
});
const { retcode } = data;
if (retcode === 0) {
message.success(i18n.t('message.deleted'));
yield put({
type: 'getKfList',
payload: { kb_id: payload.kb_id },
});
}
return retcode;
},
*document_rename({ payload = {} }, { call, put }) {
const { data } = yield call(
kbService.document_rename,
omit(payload, ['kb_id']),
);
const { retcode } = data;
if (retcode === 0) {
message.success(i18n.t('message.renamed'));
yield put({
type: 'getKfList',
payload: { kb_id: payload.kb_id },
});
}
return retcode;
},
*document_create({ payload = {} }, { call, put }) {
const { data } = yield call(kbService.document_create, payload);
const { retcode } = data;
if (retcode === 0) {
yield put({
type: 'getKfList',
payload: { kb_id: payload.kb_id },
});
message.success(i18n.t('message.created'));
}
return retcode;
},
*document_run({ payload = {} }, { call, put }) {
const { data } = yield call(
kbService.document_run,
omit(payload, ['knowledgeBaseId']),
);
const { retcode } = data;
if (retcode === 0) {
if (payload.knowledgeBaseId) {
yield put({
type: 'getKfList',
payload: { kb_id: payload.knowledgeBaseId },
});
}
message.success(i18n.t('message.operated'));
}
return retcode;
},
*document_change_parser({ payload = {} }, { call, put }) {
const { data } = yield call(
kbService.document_change_parser,
omit(payload, ['kb_id']),
);
const { retcode } = data;
if (retcode === 0) {
yield put({
type: 'getKfList',
payload: { kb_id: payload.kb_id },
});
message.success(i18n.t('message.modified'));
}
return retcode;
},
*fetch_document_thumbnails({ payload = {} }, { call, put }) {
const { data } = yield call(kbService.document_thumbnails, payload);
if (data.retcode === 0) {
yield put({ type: 'setFileThumbnails', payload: data.data });
}
},
*fetch_document_file({ payload = {} }, { call }) {
const documentId = payload;
try {
const ret = yield call(getDocumentFile, documentId);
return ret;
} catch (error) {
console.warn(error);
}
},
*upload_document({ payload = {} }, { call, put }) {
const fileList = payload.fileList;
const formData = new FormData();
formData.append('kb_id', payload.kb_id);
fileList.forEach((file: any) => {
formData.append('file', file);
});
const ret = yield call(kbService.document_upload, formData);
const succeed = ret?.data?.retcode === 0;
if (succeed) {
message.success(i18n.t('message.uploaded'));
}
if (succeed || ret?.data?.retcode === 500) {
yield put({
type: 'getKfList',
payload: { kb_id: payload.kb_id },
});
}
return ret?.data;
},
*web_crawl({ payload = {} }, { call, put }) {
const formData = new FormData();
formData.append('name', payload.name);
formData.append('url', payload.url);
formData.append('kb_id', payload.kb_id);
const { data } = yield call(kbService.web_crawl, formData);
const succeed = data.retcode === 0;
if (succeed) {
message.success(i18n.t('message.uploaded'));
}
if (succeed || data.retcode === 500) {
yield put({
type: 'getKfList',
payload: { kb_id: payload.kb_id },
});
}
return data.retcode;
},
},
subscriptions: {
setup({ dispatch, history }) {
history.listen(({ location }) => {
const state: { from: string } = (location.state ?? {
from: '',
}) as { from: string };
if (
state.from === '/knowledge' || // TODO: Just directly determine whether the current page is on the knowledge list page.
location.pathname === '/knowledge/dataset/upload'
) {
dispatch({
type: 'setPagination',
payload: { current: 1, pageSize: 10 },
});
}
});
},
},
};
export default model;

View File

@ -1,6 +1,6 @@
import { useShowDeleteConfirm, useTranslate } from '@/hooks/common-hooks'; import { useShowDeleteConfirm, useTranslate } from '@/hooks/common-hooks';
import { useRemoveDocument } from '@/hooks/document-hooks'; import { useRemoveNextDocument } from '@/hooks/document-hooks';
import { IKnowledgeFile } from '@/interfaces/database/knowledge'; import { IDocumentInfo } from '@/interfaces/database/document';
import { api_host } from '@/utils/api'; import { api_host } from '@/utils/api';
import { downloadFile } from '@/utils/file-util'; import { downloadFile } from '@/utils/file-util';
import { import {
@ -15,8 +15,8 @@ import { isParserRunning } from '../utils';
import styles from './index.less'; import styles from './index.less';
interface IProps { interface IProps {
record: IKnowledgeFile; record: IDocumentInfo;
setCurrentRecord: (record: IKnowledgeFile) => void; setCurrentRecord: (record: IDocumentInfo) => void;
showRenameModal: () => void; showRenameModal: () => void;
showChangeParserModal: () => void; showChangeParserModal: () => void;
} }
@ -30,7 +30,7 @@ const ParsingActionCell = ({
const documentId = record.id; const documentId = record.id;
const isRunning = isParserRunning(record.run); const isRunning = isParserRunning(record.run);
const { t } = useTranslate('knowledgeDetails'); const { t } = useTranslate('knowledgeDetails');
const removeDocument = useRemoveDocument(); const { removeDocument } = useRemoveNextDocument();
const showDeleteConfirm = useShowDeleteConfirm(); const showDeleteConfirm = useShowDeleteConfirm();
const onRmDocument = () => { const onRmDocument = () => {

View File

@ -2,7 +2,7 @@ import { ReactComponent as CancelIcon } from '@/assets/svg/cancel.svg';
import { ReactComponent as RefreshIcon } from '@/assets/svg/refresh.svg'; import { ReactComponent as RefreshIcon } from '@/assets/svg/refresh.svg';
import { ReactComponent as RunIcon } from '@/assets/svg/run.svg'; import { ReactComponent as RunIcon } from '@/assets/svg/run.svg';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { IKnowledgeFile } from '@/interfaces/database/knowledge'; import { IDocumentInfo } from '@/interfaces/database/document';
import { Badge, DescriptionsProps, Flex, Popover, Space, Tag } from 'antd'; import { Badge, DescriptionsProps, Flex, Popover, Space, Tag } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -21,7 +21,7 @@ const iconMap = {
}; };
interface IProps { interface IProps {
record: IKnowledgeFile; record: IDocumentInfo;
} }
const PopoverContent = ({ record }: IProps) => { const PopoverContent = ({ record }: IProps) => {
@ -93,7 +93,7 @@ export const ParsingStatusCell = ({ record }: IProps) => {
const label = t(`knowledgeDetails.runningStatus${text}`); const label = t(`knowledgeDetails.runningStatus${text}`);
const handleOperationIconClick = () => { const handleOperationIconClick = () => {
handleRunDocumentByIds(record.id, record.kb_id, isRunning); handleRunDocumentByIds(record.id, isRunning);
}; };
return ( return (

View File

@ -6,19 +6,17 @@ import {
} from '@/hooks/route-hook'; } from '@/hooks/route-hook';
import { Breadcrumb } from 'antd'; import { Breadcrumb } from 'antd';
import { ItemType } from 'antd/es/breadcrumb/Breadcrumb'; import { ItemType } from 'antd/es/breadcrumb/Breadcrumb';
import { useEffect, useMemo } from 'react'; import { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Link, Outlet, useDispatch, useLocation } from 'umi'; import { Link, Outlet } from 'umi';
import Siderbar from './components/knowledge-sidebar'; import Siderbar from './components/knowledge-sidebar';
import { KnowledgeDatasetRouteKey, KnowledgeRouteKey } from './constant'; import { KnowledgeDatasetRouteKey, KnowledgeRouteKey } from './constant';
import styles from './index.less'; import styles from './index.less';
const KnowledgeAdding = () => { const KnowledgeAdding = () => {
const dispatch = useDispatch();
const knowledgeBaseId = useKnowledgeBaseId(); const knowledgeBaseId = useKnowledgeBaseId();
const { t } = useTranslation(); const { t } = useTranslation();
const location = useLocation();
const activeKey: KnowledgeRouteKey = const activeKey: KnowledgeRouteKey =
(useSecondPathName() as KnowledgeRouteKey) || KnowledgeRouteKey.Dataset; (useSecondPathName() as KnowledgeRouteKey) || KnowledgeRouteKey.Dataset;
@ -58,23 +56,6 @@ const KnowledgeAdding = () => {
return items; return items;
}, [activeKey, datasetActiveKey, gotoList, knowledgeBaseId, t]); }, [activeKey, datasetActiveKey, gotoList, knowledgeBaseId, t]);
useEffect(() => {
const search: string = location.search.slice(1);
const map = search.split('&').reduce<Record<string, string>>((obj, cur) => {
const [key, value] = cur.split('=');
obj[key] = value;
return obj;
}, {});
dispatch({
type: 'kAModel/updateState',
payload: {
doc_id: undefined,
...map,
},
});
}, [location, dispatch]);
return ( return (
<> <>
<div className={styles.container}> <div className={styles.container}>

View File

@ -1,19 +1,19 @@
import Image from '@/components/image'; import Image from '@/components/image';
import SvgIcon from '@/components/svg-icon'; import SvgIcon from '@/components/svg-icon';
import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks';
import { IReference } from '@/interfaces/database/chat'; import { IReference } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge'; import { IChunk } from '@/interfaces/database/knowledge';
import { getExtension } from '@/utils/document-util'; import { getExtension } from '@/utils/document-util';
import { InfoCircleOutlined } from '@ant-design/icons'; import { InfoCircleOutlined } from '@ant-design/icons';
import { Button, Flex, Popover, Space } from 'antd'; import { Button, Flex, Popover, Space } from 'antd';
import DOMPurify from 'dompurify'; import DOMPurify from 'dompurify';
import { useCallback, useMemo } from 'react'; import { useCallback, useEffect, useMemo } from 'react';
import Markdown from 'react-markdown'; import Markdown from 'react-markdown';
import reactStringReplace from 'react-string-replace'; import reactStringReplace from 'react-string-replace';
import SyntaxHighlighter from 'react-syntax-highlighter'; import SyntaxHighlighter from 'react-syntax-highlighter';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
import { visitParents } from 'unist-util-visit-parents'; import { visitParents } from 'unist-util-visit-parents';
import { useFetchDocumentThumbnailsByIds } from '@/hooks/document-hooks';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import styles from './index.less'; import styles from './index.less';
@ -34,6 +34,8 @@ const MarkdownContent = ({
clickDocumentButton?: (documentId: string, chunk: IChunk) => void; clickDocumentButton?: (documentId: string, chunk: IChunk) => void;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { setDocumentIds, data: fileThumbnails } =
useFetchDocumentThumbnailsByIds();
const contentWithCursor = useMemo(() => { const contentWithCursor = useMemo(() => {
let text = content; let text = content;
if (text === '') { if (text === '') {
@ -42,7 +44,9 @@ const MarkdownContent = ({
return loading ? text?.concat('~~2$$') : text; return loading ? text?.concat('~~2$$') : text;
}, [content, loading, t]); }, [content, loading, t]);
const fileThumbnails = useSelectFileThumbnails(); useEffect(() => {
setDocumentIds(reference?.doc_aggs.map((x) => x.doc_id) ?? []);
}, [reference, setDocumentIds]);
const handleDocumentButtonClick = useCallback( const handleDocumentButtonClick = useCallback(
(documentId: string, chunk: IChunk, isPdf: boolean) => () => { (documentId: string, chunk: IChunk, isPdf: boolean) => () => {

View File

@ -1,5 +1,10 @@
import { ReactComponent as DeleteIcon } from '@/assets/svg/delete.svg'; import { ReactComponent as DeleteIcon } from '@/assets/svg/delete.svg';
import SvgIcon from '@/components/svg-icon';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import {
IListResult,
useFetchParentFolderList,
} from '@/hooks/file-manager-hooks';
import { import {
DownOutlined, DownOutlined,
FileTextOutlined, FileTextOutlined,
@ -24,11 +29,6 @@ import {
useSelectBreadcrumbItems, useSelectBreadcrumbItems,
} from './hooks'; } from './hooks';
import SvgIcon from '@/components/svg-icon';
import {
IListResult,
useFetchParentFolderList,
} from '@/hooks/file-manager-hooks';
import styles from './index.less'; import styles from './index.less';
interface IProps interface IProps

15
web/typings.d.ts vendored
View File

@ -1,20 +1,5 @@
import { KFModelState } from '@/pages/add-knowledge/components/knowledge-file/model';
declare module 'lodash'; declare module 'lodash';
function useSelector<TState = RootState, TSelected = unknown>(
selector: (state: TState) => TSelected,
equalityFn?: (left: TSelected, right: TSelected) => boolean,
): TSelected;
export interface RootState {
kFModel: KFModelState;
}
declare global { declare global {
type Nullable<T> = T | null; type Nullable<T> = T | null;
} }
declare module 'umi' {
export { useSelector };
}