feat: fetch file thumbnails

This commit is contained in:
billchen 2024-02-28 13:35:47 +08:00
parent eb517f3106
commit afeb7fddc3
10 changed files with 128 additions and 56 deletions

View file

@ -150,3 +150,34 @@ export const useFetchKnowledgeList = (
return list; return list;
}; };
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 };
};

View file

@ -20,9 +20,11 @@
} }
.img { .img {
height: 16px; height: 24px;
width: 16px; width: 24px;
margin-right: 6px; margin-right: 10px;
display: inline-block;
vertical-align: middle;
} }
.column { .column {

View file

@ -22,7 +22,7 @@ import {
} from 'antd'; } from 'antd';
import type { ColumnsType } from 'antd/es/table'; import type { ColumnsType } from 'antd/es/table';
import { PaginationProps } from 'antd/lib'; import { PaginationProps } from 'antd/lib';
import React, { useEffect, useMemo, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Link, useDispatch, useNavigate, useSelector } from 'umi'; import { Link, useDispatch, useNavigate, useSelector } from 'umi';
import CreateEPModal from './createEFileModal'; import CreateEPModal from './createEFileModal';
import styles from './index.less'; import styles from './index.less';
@ -46,7 +46,7 @@ const KnowledgeFile = () => {
const [parser_id, setParserId] = useState('0'); const [parser_id, setParserId] = useState('0');
let navigate = useNavigate(); let navigate = useNavigate();
const getKfList = () => { const getKfList = useCallback(() => {
const payload = { const payload = {
kb_id: knowledgeBaseId, kb_id: knowledgeBaseId,
}; };
@ -55,7 +55,7 @@ const KnowledgeFile = () => {
type: 'kFModel/getKfList', type: 'kFModel/getKfList',
payload, payload,
}); });
}; }, [dispatch, knowledgeBaseId]);
const throttledGetDocumentList = () => { const throttledGetDocumentList = () => {
dispatch({ dispatch({
@ -64,23 +64,29 @@ const KnowledgeFile = () => {
}); });
}; };
const setPagination = (pageNumber = 1, pageSize?: number) => { const setPagination = useCallback(
const pagination: Pagination = { (pageNumber = 1, pageSize?: number) => {
current: pageNumber, const pagination: Pagination = {
} as Pagination; current: pageNumber,
if (pageSize) { } as Pagination;
pagination.pageSize = pageSize; if (pageSize) {
} pagination.pageSize = pageSize;
dispatch({ }
type: 'kFModel/setPagination', dispatch({
payload: pagination, type: 'kFModel/setPagination',
}); payload: pagination,
}; });
},
[dispatch],
);
const onPageChange: PaginationProps['onChange'] = (pageNumber, pageSize) => { const onPageChange: PaginationProps['onChange'] = useCallback(
setPagination(pageNumber, pageSize); (pageNumber: number, pageSize: number) => {
getKfList(); setPagination(pageNumber, pageSize);
}; getKfList();
},
[getKfList, setPagination],
);
const pagination: PaginationProps = useMemo(() => { const pagination: PaginationProps = useMemo(() => {
return { return {
@ -92,7 +98,7 @@ const KnowledgeFile = () => {
pageSizeOptions: [1, 2, 10, 20, 50, 100], pageSizeOptions: [1, 2, 10, 20, 50, 100],
onChange: onPageChange, onChange: onPageChange,
}; };
}, [total, kFModel.pagination]); }, [total, kFModel.pagination, onPageChange]);
useEffect(() => { useEffect(() => {
if (knowledgeBaseId) { if (knowledgeBaseId) {
@ -107,7 +113,7 @@ const KnowledgeFile = () => {
type: 'kFModel/pollGetDocumentList-stop', type: 'kFModel/pollGetDocumentList-stop',
}); });
}; };
}, [knowledgeBaseId]); }, [knowledgeBaseId, dispatch, getKfList]);
const handleInputChange = ( const handleInputChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
@ -129,14 +135,14 @@ const KnowledgeFile = () => {
}); });
}; };
const showCEFModal = () => { const showCEFModal = useCallback(() => {
dispatch({ dispatch({
type: 'kFModel/updateState', type: 'kFModel/updateState',
payload: { payload: {
isShowCEFwModal: true, isShowCEFwModal: true,
}, },
}); });
}; }, [dispatch]);
const actionItems: MenuProps['items'] = useMemo(() => { const actionItems: MenuProps['items'] = useMemo(() => {
return [ return [
@ -169,7 +175,7 @@ const KnowledgeFile = () => {
// disabled: true, // disabled: true,
}, },
]; ];
}, []); }, [knowledgeBaseId, showCEFModal]);
const toChunk = (id: string) => { const toChunk = (id: string) => {
navigate( navigate(
@ -187,13 +193,9 @@ const KnowledgeFile = () => {
title: 'Name', title: 'Name',
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
render: (text: any, { id }) => ( render: (text: any, { id, thumbnail }) => (
<div className={styles.tochunks} onClick={() => toChunk(id)}> <div className={styles.tochunks} onClick={() => toChunk(id)}>
<img <img className={styles.img} src={thumbnail} alt="" />
className={styles.img}
src="https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg"
alt=""
/>
{text} {text}
</div> </div>
), ),

View file

@ -16,6 +16,7 @@ export interface KFModelState extends BaseState {
data: IKnowledgeFile[]; data: IKnowledgeFile[];
total: number; total: number;
currentRecord: Nullable<IKnowledgeFile>; currentRecord: Nullable<IKnowledgeFile>;
fileThumbnails: Record<string, string>;
} }
const model: DvaModel<KFModelState> = { const model: DvaModel<KFModelState> = {
@ -34,6 +35,7 @@ const model: DvaModel<KFModelState> = {
current: 1, current: 1,
pageSize: 10, pageSize: 10,
}, },
fileThumbnails: {} as Record<string, string>,
}, },
reducers: { reducers: {
updateState(state, { payload }) { updateState(state, { payload }) {
@ -54,6 +56,9 @@ const model: DvaModel<KFModelState> = {
setPagination(state, { payload }) { setPagination(state, { payload }) {
return { ...state, pagination: { ...state.pagination, ...payload } }; return { ...state, pagination: { ...state.pagination, ...payload } };
}, },
setFileThumbnails(state, { payload }) {
return { ...state, fileThumbnails: payload };
},
}, },
effects: { effects: {
*createKf({ payload = {} }, { call }) { *createKf({ payload = {} }, { call }) {
@ -201,6 +206,12 @@ const model: DvaModel<KFModelState> = {
} }
return retcode; 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 });
}
},
}, },
}; };
export default model; export default model;

View file

@ -3,16 +3,7 @@ import { MessageType } from '@/constants/chat';
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
import { useSelectUserInfo } from '@/hooks/userSettingHook'; import { useSelectUserInfo } from '@/hooks/userSettingHook';
import { IReference, Message } from '@/interfaces/database/chat'; import { IReference, Message } from '@/interfaces/database/chat';
import { import { Avatar, Button, Flex, Input, List, Popover, Space } from 'antd';
Avatar,
Button,
Flex,
Input,
List,
Popover,
Space,
Typography,
} from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import { ChangeEventHandler, useCallback, useMemo, useState } from 'react'; import { ChangeEventHandler, useCallback, useMemo, useState } from 'react';
import reactStringReplace from 'react-string-replace'; import reactStringReplace from 'react-string-replace';
@ -26,6 +17,7 @@ import { IClientConversation } from '../interface';
import Image from '@/components/image'; import Image from '@/components/image';
import NewDocumentLink from '@/components/new-document-link'; import NewDocumentLink from '@/components/new-document-link';
import { useSelectFileThumbnails } from '@/hooks/knowledgeHook';
import { InfoCircleOutlined } from '@ant-design/icons'; import { InfoCircleOutlined } from '@ant-design/icons';
import Markdown from 'react-markdown'; import Markdown from 'react-markdown';
import { visitParents } from 'unist-util-visit-parents'; import { visitParents } from 'unist-util-visit-parents';
@ -56,11 +48,10 @@ const MessageItem = ({
reference: IReference; reference: IReference;
}) => { }) => {
const userInfo = useSelectUserInfo(); const userInfo = useSelectUserInfo();
const fileThumbnails = useSelectFileThumbnails();
const isAssistant = item.role === MessageType.Assistant; const isAssistant = item.role === MessageType.Assistant;
const getFileIcon = useGetFileIcon();
const getPopoverContent = useCallback( const getPopoverContent = useCallback(
(chunkIndex: number) => { (chunkIndex: number) => {
const chunks = reference?.chunks ?? []; const chunks = reference?.chunks ?? [];
@ -82,15 +73,18 @@ const MessageItem = ({
<Space direction={'vertical'}> <Space direction={'vertical'}>
<div>{chunkItem?.content_with_weight}</div> <div>{chunkItem?.content_with_weight}</div>
{documentId && ( {documentId && (
<NewDocumentLink documentId={documentId}> <Flex gap={'middle'}>
{document?.doc_name} <img src={fileThumbnails[documentId]} alt="" />
</NewDocumentLink> <NewDocumentLink documentId={documentId}>
{document?.doc_name}
</NewDocumentLink>
</Flex>
)} )}
</Space> </Space>
</Flex> </Flex>
); );
}, },
[reference], [reference, fileThumbnails],
); );
const renderReference = useCallback( const renderReference = useCallback(
@ -163,12 +157,13 @@ const MessageItem = ({
dataSource={referenceDocumentList} dataSource={referenceDocumentList}
renderItem={(item) => ( renderItem={(item) => (
<List.Item> <List.Item>
<Typography.Text mark> {/* <SvgIcon name={getFileIcon(item.doc_name)}></SvgIcon> */}
{/* <SvgIcon name={getFileIcon(item.doc_name)}></SvgIcon> */} <Flex gap={'middle'}>
</Typography.Text> <img src={fileThumbnails[item.doc_id]}></img>
<NewDocumentLink documentId={item.doc_id}> <NewDocumentLink documentId={item.doc_id}>
{item.doc_name} {item.doc_name}
</NewDocumentLink> </NewDocumentLink>
</Flex>
</List.Item> </List.Item>
)} )}
/> />

View file

@ -384,7 +384,7 @@ export const useFetchConversation = () => {
const fetchConversation = useCallback(() => { const fetchConversation = useCallback(() => {
if (isConversationIdNotExist(conversationId)) { if (isConversationIdNotExist(conversationId)) {
dispatch<any>({ dispatch({
type: 'chatModel/getConversation', type: 'chatModel/getConversation',
payload: { payload: {
conversation_id: conversationId, conversation_id: conversationId,

View file

@ -4,6 +4,7 @@ import { message } from 'antd';
import { DvaModel } from 'umi'; import { DvaModel } from 'umi';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { IClientConversation, IMessage } from './interface'; import { IClientConversation, IMessage } from './interface';
import { getDocumentIdsFromConversionReference } from './utils';
export interface ChatModelState { export interface ChatModelState {
name: string; name: string;
@ -111,6 +112,12 @@ const model: DvaModel<ChatModelState> = {
*getConversation({ payload }, { call, put }) { *getConversation({ payload }, { call, put }) {
const { data } = yield call(chatService.getConversation, payload); const { data } = yield call(chatService.getConversation, payload);
if (data.retcode === 0) { if (data.retcode === 0) {
yield put({
type: 'kFModel/fetch_document_thumbnails',
payload: {
doc_ids: getDocumentIdsFromConversionReference(data.data),
},
});
yield put({ type: 'setCurrentConversation', payload: data.data }); yield put({ type: 'setCurrentConversation', payload: data.data });
} }
return data.retcode; return data.retcode;

View file

@ -1,3 +1,4 @@
import { IConversation, IReference } from '@/interfaces/database/chat';
import { EmptyConversationId, variableEnabledFieldMap } from './constants'; import { EmptyConversationId, variableEnabledFieldMap } from './constants';
export const excludeUnEnabledVariables = (values: any) => { export const excludeUnEnabledVariables = (values: any) => {
@ -14,3 +15,20 @@ export const excludeUnEnabledVariables = (values: any) => {
export const isConversationIdNotExist = (conversationId: string) => { export const isConversationIdNotExist = (conversationId: string) => {
return conversationId !== EmptyConversationId && conversationId !== ''; return conversationId !== EmptyConversationId && conversationId !== '';
}; };
export const getDocumentIdsFromConversionReference = (data: IConversation) => {
const documentIds = data.reference.reduce(
(pre: Array<string>, cur: IReference) => {
cur.doc_aggs
.map((x) => x.doc_id)
.forEach((x) => {
if (pre.every((y) => y !== x)) {
pre.push(x);
}
});
return pre;
},
[],
);
return documentIds.join(',');
};

View file

@ -13,6 +13,7 @@ const {
document_rm, document_rm,
document_create, document_create,
document_change_parser, document_change_parser,
document_thumbnails,
chunk_list, chunk_list,
create_chunk, create_chunk,
set_chunk, set_chunk,
@ -75,6 +76,10 @@ const methods = {
url: document_change_parser, url: document_change_parser,
method: 'post', method: 'post',
}, },
document_thumbnails: {
url: document_thumbnails,
method: 'get',
},
// chunk管理 // chunk管理
chunk_list: { chunk_list: {
url: chunk_list, url: chunk_list,

View file

@ -42,6 +42,7 @@ export default {
document_create: `${api_host}/document/create`, document_create: `${api_host}/document/create`,
document_run: `${api_host}/document/run`, document_run: `${api_host}/document/run`,
document_change_parser: `${api_host}/document/change_parser`, document_change_parser: `${api_host}/document/change_parser`,
document_thumbnails: `${api_host}/document/thumbnails`,
setDialog: `${api_host}/dialog/set`, setDialog: `${api_host}/dialog/set`,
getDialog: `${api_host}/dialog/get`, getDialog: `${api_host}/dialog/get`,