mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-12 20:59:00 +08:00
### 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:
parent
2c56d274d8
commit
a2f9c03a95
@ -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: {
|
||||||
|
@ -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'),
|
||||||
|
@ -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>
|
||||||
) : (
|
) : (
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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 = () => {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
};
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(() => {
|
||||||
|
@ -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) {
|
||||||
|
@ -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}
|
||||||
|
@ -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;
|
|
@ -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 = () => {
|
||||||
|
@ -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 (
|
||||||
|
@ -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}>
|
||||||
|
@ -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) => () => {
|
||||||
|
@ -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
15
web/typings.d.ts
vendored
@ -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 };
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user