mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-07-31 00:52:01 +08:00

### What problem does this PR solve? feat: Search for the answers you want based on the selected knowledge base #2247 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
236 lines
7.9 KiB
TypeScript
236 lines
7.9 KiB
TypeScript
import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
|
|
import { MessageType } from '@/constants/chat';
|
|
import { useSetModalState } from '@/hooks/common-hooks';
|
|
import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks';
|
|
import { IReference } from '@/interfaces/database/chat';
|
|
import { IChunk } from '@/interfaces/database/knowledge';
|
|
import classNames from 'classnames';
|
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
|
import {
|
|
useFetchDocumentInfosByIds,
|
|
useFetchDocumentThumbnailsByIds,
|
|
} from '@/hooks/document-hooks';
|
|
import { IRegenerateMessage, IRemoveMessageById } from '@/hooks/logic-hooks';
|
|
import { IMessage } from '@/pages/chat/interface';
|
|
import MarkdownContent from '@/pages/chat/markdown-content';
|
|
import { getExtension, isImage } from '@/utils/document-util';
|
|
import { Avatar, Button, Flex, List, Space, Typography } from 'antd';
|
|
import FileIcon from '../file-icon';
|
|
import IndentedTreeModal from '../indented-tree/modal';
|
|
import NewDocumentLink from '../new-document-link';
|
|
import { AssistantGroupButton, UserGroupButton } from './group-button';
|
|
import styles from './index.less';
|
|
|
|
const { Text } = Typography;
|
|
|
|
interface IProps extends Partial<IRemoveMessageById>, IRegenerateMessage {
|
|
item: IMessage;
|
|
reference: IReference;
|
|
loading?: boolean;
|
|
sendLoading?: boolean;
|
|
nickname?: string;
|
|
avatar?: string;
|
|
clickDocumentButton?: (documentId: string, chunk: IChunk) => void;
|
|
index: number;
|
|
showLikeButton?: boolean;
|
|
}
|
|
|
|
const MessageItem = ({
|
|
item,
|
|
reference,
|
|
loading = false,
|
|
avatar = '',
|
|
sendLoading = false,
|
|
clickDocumentButton,
|
|
index,
|
|
removeMessageById,
|
|
regenerateMessage,
|
|
showLikeButton = true,
|
|
}: IProps) => {
|
|
const isAssistant = item.role === MessageType.Assistant;
|
|
const isUser = item.role === MessageType.User;
|
|
const fileThumbnails = useSelectFileThumbnails();
|
|
const { data: documentList, setDocumentIds } = useFetchDocumentInfosByIds();
|
|
const { data: documentThumbnails, setDocumentIds: setIds } =
|
|
useFetchDocumentThumbnailsByIds();
|
|
const { visible, hideModal, showModal } = useSetModalState();
|
|
const [clickedDocumentId, setClickedDocumentId] = useState('');
|
|
|
|
const referenceDocumentList = useMemo(() => {
|
|
return reference?.doc_aggs ?? [];
|
|
}, [reference?.doc_aggs]);
|
|
|
|
const handleUserDocumentClick = useCallback(
|
|
(id: string) => () => {
|
|
setClickedDocumentId(id);
|
|
showModal();
|
|
},
|
|
[showModal],
|
|
);
|
|
|
|
const handleRegenerateMessage = useCallback(() => {
|
|
regenerateMessage?.(item);
|
|
}, [regenerateMessage, item]);
|
|
|
|
useEffect(() => {
|
|
const ids = item?.doc_ids ?? [];
|
|
if (ids.length) {
|
|
setDocumentIds(ids);
|
|
const documentIds = ids.filter((x) => !(x in fileThumbnails));
|
|
if (documentIds.length) {
|
|
setIds(documentIds);
|
|
}
|
|
}
|
|
}, [item.doc_ids, setDocumentIds, setIds, fileThumbnails]);
|
|
|
|
return (
|
|
<div
|
|
className={classNames(styles.messageItem, {
|
|
[styles.messageItemLeft]: item.role === MessageType.Assistant,
|
|
[styles.messageItemRight]: item.role === MessageType.User,
|
|
})}
|
|
>
|
|
<section
|
|
className={classNames(styles.messageItemSection, {
|
|
[styles.messageItemSectionLeft]: item.role === MessageType.Assistant,
|
|
[styles.messageItemSectionRight]: item.role === MessageType.User,
|
|
})}
|
|
>
|
|
<div
|
|
className={classNames(styles.messageItemContent, {
|
|
[styles.messageItemContentReverse]: item.role === MessageType.User,
|
|
})}
|
|
>
|
|
{item.role === MessageType.User ? (
|
|
<Avatar
|
|
size={40}
|
|
src={
|
|
avatar ??
|
|
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
|
|
}
|
|
/>
|
|
) : (
|
|
<AssistantIcon></AssistantIcon>
|
|
)}
|
|
<Flex vertical gap={8} flex={1}>
|
|
<Space>
|
|
{isAssistant ? (
|
|
index !== 0 && (
|
|
<AssistantGroupButton
|
|
messageId={item.id}
|
|
content={item.content}
|
|
prompt={item.prompt}
|
|
showLikeButton={showLikeButton}
|
|
audioBinary={item.audio_binary}
|
|
></AssistantGroupButton>
|
|
)
|
|
) : (
|
|
<UserGroupButton
|
|
content={item.content}
|
|
messageId={item.id}
|
|
removeMessageById={removeMessageById}
|
|
regenerateMessage={
|
|
regenerateMessage && handleRegenerateMessage
|
|
}
|
|
sendLoading={sendLoading}
|
|
></UserGroupButton>
|
|
)}
|
|
|
|
{/* <b>{isAssistant ? '' : nickname}</b> */}
|
|
</Space>
|
|
<div
|
|
className={
|
|
isAssistant ? styles.messageText : styles.messageUserText
|
|
}
|
|
>
|
|
<MarkdownContent
|
|
loading={loading}
|
|
content={item.content}
|
|
reference={reference}
|
|
clickDocumentButton={clickDocumentButton}
|
|
></MarkdownContent>
|
|
</div>
|
|
{isAssistant && referenceDocumentList.length > 0 && (
|
|
<List
|
|
bordered
|
|
dataSource={referenceDocumentList}
|
|
renderItem={(item) => {
|
|
return (
|
|
<List.Item>
|
|
<Flex gap={'small'} align="center">
|
|
<FileIcon
|
|
id={item.doc_id}
|
|
name={item.doc_name}
|
|
></FileIcon>
|
|
|
|
<NewDocumentLink
|
|
documentId={item.doc_id}
|
|
documentName={item.doc_name}
|
|
prefix="document"
|
|
>
|
|
{item.doc_name}
|
|
</NewDocumentLink>
|
|
</Flex>
|
|
</List.Item>
|
|
);
|
|
}}
|
|
/>
|
|
)}
|
|
{isUser && documentList.length > 0 && (
|
|
<List
|
|
bordered
|
|
dataSource={documentList}
|
|
renderItem={(item) => {
|
|
// TODO:
|
|
const fileThumbnail =
|
|
documentThumbnails[item.id] || fileThumbnails[item.id];
|
|
const fileExtension = getExtension(item.name);
|
|
return (
|
|
<List.Item>
|
|
<Flex gap={'small'} align="center">
|
|
<FileIcon id={item.id} name={item.name}></FileIcon>
|
|
|
|
{isImage(fileExtension) ? (
|
|
<NewDocumentLink
|
|
documentId={item.id}
|
|
documentName={item.name}
|
|
prefix="document"
|
|
>
|
|
{item.name}
|
|
</NewDocumentLink>
|
|
) : (
|
|
<Button
|
|
type={'text'}
|
|
onClick={handleUserDocumentClick(item.id)}
|
|
>
|
|
<Text
|
|
style={{ maxWidth: '40vw' }}
|
|
ellipsis={{ tooltip: item.name }}
|
|
>
|
|
{item.name}
|
|
</Text>
|
|
</Button>
|
|
)}
|
|
</Flex>
|
|
</List.Item>
|
|
);
|
|
}}
|
|
/>
|
|
)}
|
|
</Flex>
|
|
</div>
|
|
</section>
|
|
{visible && (
|
|
<IndentedTreeModal
|
|
visible={visible}
|
|
hideModal={hideModal}
|
|
documentId={clickedDocumentId}
|
|
></IndentedTreeModal>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default memo(MessageItem);
|