mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
feat: add corresponding icons to files (#164)
This commit is contained in:
@ -3,12 +3,18 @@ import React from 'react';
|
|||||||
|
|
||||||
interface IProps extends React.PropsWithChildren {
|
interface IProps extends React.PropsWithChildren {
|
||||||
documentId: string;
|
documentId: string;
|
||||||
|
preventDefault?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NewDocumentLink = ({ children, documentId }: IProps) => {
|
const NewDocumentLink = ({
|
||||||
|
children,
|
||||||
|
documentId,
|
||||||
|
preventDefault = false,
|
||||||
|
}: IProps) => {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
onClick={!preventDefault ? undefined : (e) => e.preventDefault()}
|
||||||
href={`${api_host}/document/get/${documentId}`}
|
href={`${api_host}/document/get/${documentId}`}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -206,3 +206,26 @@ export const useSelectKnowledgeDetails = () => {
|
|||||||
return knowledgeDetails;
|
return knowledgeDetails;
|
||||||
};
|
};
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
//#region Retrieval testing
|
||||||
|
|
||||||
|
export const useTestChunkRetrieval = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const knowledgeBaseId = useKnowledgeBaseId();
|
||||||
|
|
||||||
|
const testChunk = useCallback(
|
||||||
|
(values: any) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'testingModel/testDocumentChunk',
|
||||||
|
payload: {
|
||||||
|
...values,
|
||||||
|
kb_id: knowledgeBaseId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dispatch, knowledgeBaseId],
|
||||||
|
);
|
||||||
|
|
||||||
|
return testChunk;
|
||||||
|
};
|
||||||
|
//#endregion
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chunkContainer {
|
.chunkContainer {
|
||||||
|
display: flex;
|
||||||
height: calc(100vh - 332px);
|
height: calc(100vh - 332px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,6 @@
|
|||||||
.img {
|
.img {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
margin-right: 10px;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
|
import ChunkMethodModal from '@/components/chunk-method-modal';
|
||||||
|
import SvgIcon from '@/components/svg-icon';
|
||||||
import {
|
import {
|
||||||
useSelectDocumentList,
|
useSelectDocumentList,
|
||||||
useSetDocumentStatus,
|
useSetDocumentStatus,
|
||||||
} from '@/hooks/documentHooks';
|
} from '@/hooks/documentHooks';
|
||||||
import { useSelectParserList } from '@/hooks/userSettingHook';
|
import { useSelectParserList } from '@/hooks/userSettingHook';
|
||||||
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
|
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||||
|
import { getExtension } from '@/utils/documentUtils';
|
||||||
import {
|
import {
|
||||||
FileOutlined,
|
FileOutlined,
|
||||||
FileTextOutlined,
|
FileTextOutlined,
|
||||||
@ -15,6 +18,7 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
Flex,
|
||||||
Input,
|
Input,
|
||||||
Space,
|
Space,
|
||||||
Switch,
|
Switch,
|
||||||
@ -38,8 +42,6 @@ import ParsingActionCell from './parsing-action-cell';
|
|||||||
import ParsingStatusCell from './parsing-status-cell';
|
import ParsingStatusCell from './parsing-status-cell';
|
||||||
import RenameModal from './rename-modal';
|
import RenameModal from './rename-modal';
|
||||||
|
|
||||||
import ChunkMethodModal from '@/components/chunk-method-modal';
|
|
||||||
import { getExtension } from '@/utils/documentUtils';
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const KnowledgeFile = () => {
|
const KnowledgeFile = () => {
|
||||||
@ -114,10 +116,19 @@ const KnowledgeFile = () => {
|
|||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
render: (text: any, { id, thumbnail }) => (
|
render: (text: any, { id, thumbnail, name }) => (
|
||||||
<div className={styles.tochunks} onClick={() => toChunk(id)}>
|
<div className={styles.tochunks} onClick={() => toChunk(id)}>
|
||||||
|
<Flex gap={10} align="center">
|
||||||
|
{thumbnail ? (
|
||||||
<img className={styles.img} src={thumbnail} alt="" />
|
<img className={styles.img} src={thumbnail} alt="" />
|
||||||
|
) : (
|
||||||
|
<SvgIcon
|
||||||
|
name={`file-icon/${getExtension(name)}`}
|
||||||
|
width={24}
|
||||||
|
></SvgIcon>
|
||||||
|
)}
|
||||||
{text}
|
{text}
|
||||||
|
</Flex>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,28 +1,21 @@
|
|||||||
|
import { useTestChunkRetrieval } from '@/hooks/knowledgeHook';
|
||||||
import { Flex, Form } from 'antd';
|
import { Flex, Form } from 'antd';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useDispatch } from 'umi';
|
||||||
import TestingControl from './testing-control';
|
import TestingControl from './testing-control';
|
||||||
import TestingResult from './testing-result';
|
import TestingResult from './testing-result';
|
||||||
|
|
||||||
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { useDispatch } from 'umi';
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const KnowledgeTesting = () => {
|
const KnowledgeTesting = () => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const testChunk = useTestChunkRetrieval();
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const knowledgeBaseId = useKnowledgeBaseId();
|
|
||||||
|
|
||||||
const handleTesting = async () => {
|
const handleTesting = async () => {
|
||||||
const values = await form.validateFields();
|
const values = await form.validateFields();
|
||||||
console.info(values);
|
testChunk(values);
|
||||||
dispatch({
|
|
||||||
type: 'testingModel/testDocumentChunk',
|
|
||||||
payload: {
|
|
||||||
...values,
|
|
||||||
kb_id: knowledgeBaseId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -2,10 +2,9 @@ import SimilaritySlider from '@/components/similarity-slider';
|
|||||||
import { Button, Card, Divider, Flex, Form, Input, Slider, Tag } from 'antd';
|
import { Button, Card, Divider, Flex, Form, Input, Slider, Tag } from 'antd';
|
||||||
import { FormInstance } from 'antd/lib';
|
import { FormInstance } from 'antd/lib';
|
||||||
|
|
||||||
|
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const list = [1, 2, 3];
|
|
||||||
|
|
||||||
type FieldType = {
|
type FieldType = {
|
||||||
similarity_threshold?: number;
|
similarity_threshold?: number;
|
||||||
vector_similarity_weight?: number;
|
vector_similarity_weight?: number;
|
||||||
@ -20,6 +19,9 @@ interface IProps {
|
|||||||
|
|
||||||
const TestingControl = ({ form, handleTesting }: IProps) => {
|
const TestingControl = ({ form, handleTesting }: IProps) => {
|
||||||
const question = Form.useWatch('question', { form, preserve: true });
|
const question = Form.useWatch('question', { form, preserve: true });
|
||||||
|
const loading = useOneNamespaceEffectsLoading('testingModel', [
|
||||||
|
'testDocumentChunk',
|
||||||
|
]);
|
||||||
|
|
||||||
const buttonDisabled =
|
const buttonDisabled =
|
||||||
!question || (typeof question === 'string' && question.trim() === '');
|
!question || (typeof question === 'string' && question.trim() === '');
|
||||||
@ -65,6 +67,7 @@ const TestingControl = ({ form, handleTesting }: IProps) => {
|
|||||||
size="small"
|
size="small"
|
||||||
onClick={handleTesting}
|
onClick={handleTesting}
|
||||||
disabled={buttonDisabled}
|
disabled={buttonDisabled}
|
||||||
|
loading={loading}
|
||||||
>
|
>
|
||||||
Testing
|
Testing
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -35,9 +35,11 @@
|
|||||||
}
|
}
|
||||||
.image {
|
.image {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.imagePreview {
|
.imagePreview {
|
||||||
display: block;
|
display: block;
|
||||||
width: 260px;
|
max-width: 45vw;
|
||||||
}
|
max-height: 40vh;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,7 +104,7 @@ const TestingResult = ({ handleTesting }: IProps) => {
|
|||||||
<Flex gap={'middle'}>
|
<Flex gap={'middle'}>
|
||||||
{x.img_id && (
|
{x.img_id && (
|
||||||
<Popover
|
<Popover
|
||||||
placement="topRight"
|
placement="left"
|
||||||
content={
|
content={
|
||||||
<Image
|
<Image
|
||||||
id={x.img_id}
|
id={x.img_id}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { ReactComponent as NavigationPointerIcon } from '@/assets/svg/navigation-pointer.svg';
|
import { ReactComponent as NavigationPointerIcon } from '@/assets/svg/navigation-pointer.svg';
|
||||||
import NewDocumentLink from '@/components/new-document-link';
|
import NewDocumentLink from '@/components/new-document-link';
|
||||||
import { ITestingDocument } from '@/interfaces/database/knowledge';
|
import { ITestingDocument } from '@/interfaces/database/knowledge';
|
||||||
|
import { isPdf } from '@/utils/documentUtils';
|
||||||
import { Table, TableProps } from 'antd';
|
import { Table, TableProps } from 'antd';
|
||||||
import { useDispatch, useSelector } from 'umi';
|
import { useDispatch, useSelector } from 'umi';
|
||||||
|
|
||||||
@ -33,8 +34,8 @@ const SelectFiles = ({ handleTesting }: IProps) => {
|
|||||||
title: 'View',
|
title: 'View',
|
||||||
key: 'view',
|
key: 'view',
|
||||||
width: 50,
|
width: 50,
|
||||||
render: (_, { doc_id }) => (
|
render: (_, { doc_id, doc_name }) => (
|
||||||
<NewDocumentLink documentId={doc_id}>
|
<NewDocumentLink documentId={doc_id} preventDefault={!isPdf(doc_name)}>
|
||||||
<NavigationPointerIcon />
|
<NavigationPointerIcon />
|
||||||
</NewDocumentLink>
|
</NewDocumentLink>
|
||||||
),
|
),
|
||||||
|
|||||||
@ -64,3 +64,6 @@
|
|||||||
max-height: 45vh;
|
max-height: 45vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
.documentLink {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|||||||
@ -36,6 +36,8 @@ import {
|
|||||||
useSendMessage,
|
useSendMessage,
|
||||||
} from '../hooks';
|
} from '../hooks';
|
||||||
|
|
||||||
|
import SvgIcon from '@/components/svg-icon';
|
||||||
|
import { getExtension, isPdf } from '@/utils/documentUtils';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const reg = /(#{2}\d+\${2})/g;
|
const reg = /(#{2}\d+\${2})/g;
|
||||||
@ -74,7 +76,10 @@ const MessageItem = ({
|
|||||||
const isAssistant = item.role === MessageType.Assistant;
|
const isAssistant = item.role === MessageType.Assistant;
|
||||||
|
|
||||||
const handleDocumentButtonClick = useCallback(
|
const handleDocumentButtonClick = useCallback(
|
||||||
(documentId: string, chunk: IChunk) => () => {
|
(documentId: string, chunk: IChunk, isPdf: boolean) => () => {
|
||||||
|
if (!isPdf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
clickDocumentButton(documentId, chunk);
|
clickDocumentButton(documentId, chunk);
|
||||||
},
|
},
|
||||||
[clickDocumentButton],
|
[clickDocumentButton],
|
||||||
@ -88,26 +93,31 @@ const MessageItem = ({
|
|||||||
(x) => x?.doc_id === chunkItem?.doc_id,
|
(x) => x?.doc_id === chunkItem?.doc_id,
|
||||||
);
|
);
|
||||||
const documentId = document?.doc_id;
|
const documentId = document?.doc_id;
|
||||||
|
const fileThumbnail = documentId ? fileThumbnails[documentId] : '';
|
||||||
|
const fileExtension = documentId ? getExtension(document?.doc_name) : '';
|
||||||
|
const imageId = chunkItem?.img_id;
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
key={chunkItem?.chunk_id}
|
key={chunkItem?.chunk_id}
|
||||||
gap={10}
|
gap={10}
|
||||||
className={styles.referencePopoverWrapper}
|
className={styles.referencePopoverWrapper}
|
||||||
>
|
>
|
||||||
|
{imageId && (
|
||||||
<Popover
|
<Popover
|
||||||
placement="left"
|
placement="left"
|
||||||
content={
|
content={
|
||||||
<Image
|
<Image
|
||||||
id={chunkItem?.img_id}
|
id={imageId}
|
||||||
className={styles.referenceImagePreview}
|
className={styles.referenceImagePreview}
|
||||||
></Image>
|
></Image>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
id={chunkItem?.img_id}
|
id={imageId}
|
||||||
className={styles.referenceChunkImage}
|
className={styles.referenceChunkImage}
|
||||||
></Image>
|
></Image>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
)}
|
||||||
<Space direction={'vertical'}>
|
<Space direction={'vertical'}>
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
@ -116,11 +126,23 @@ const MessageItem = ({
|
|||||||
className={styles.chunkContentText}
|
className={styles.chunkContentText}
|
||||||
></div>
|
></div>
|
||||||
{documentId && (
|
{documentId && (
|
||||||
<Flex gap={'middle'}>
|
<Flex gap={'small'}>
|
||||||
<img src={fileThumbnails[documentId]} alt="" />
|
{fileThumbnail ? (
|
||||||
|
<img src={fileThumbnail} alt="" />
|
||||||
|
) : (
|
||||||
|
<SvgIcon
|
||||||
|
name={`file-icon/${fileExtension}`}
|
||||||
|
width={24}
|
||||||
|
></SvgIcon>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
onClick={handleDocumentButtonClick(documentId, chunkItem)}
|
className={styles.documentLink}
|
||||||
|
onClick={handleDocumentButtonClick(
|
||||||
|
documentId,
|
||||||
|
chunkItem,
|
||||||
|
fileExtension === 'pdf',
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{document?.doc_name}
|
{document?.doc_name}
|
||||||
</Button>
|
</Button>
|
||||||
@ -224,17 +246,31 @@ const MessageItem = ({
|
|||||||
<List
|
<List
|
||||||
bordered
|
bordered
|
||||||
dataSource={referenceDocumentList}
|
dataSource={referenceDocumentList}
|
||||||
renderItem={(item) => (
|
renderItem={(item) => {
|
||||||
|
const fileThumbnail = fileThumbnails[item.doc_id];
|
||||||
|
const fileExtension = getExtension(item.doc_name);
|
||||||
|
return (
|
||||||
<List.Item>
|
<List.Item>
|
||||||
{/* <SvgIcon name={getFileIcon(item.doc_name)}></SvgIcon> */}
|
<Flex gap={'small'} align="center">
|
||||||
<Flex gap={'middle'}>
|
{fileThumbnail ? (
|
||||||
<img src={fileThumbnails[item.doc_id]}></img>
|
<img src={fileThumbnail}></img>
|
||||||
<NewDocumentLink documentId={item.doc_id}>
|
) : (
|
||||||
|
<SvgIcon
|
||||||
|
name={`file-icon/${fileExtension}`}
|
||||||
|
width={24}
|
||||||
|
></SvgIcon>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<NewDocumentLink
|
||||||
|
documentId={item.doc_id}
|
||||||
|
preventDefault={!isPdf(item.doc_name)}
|
||||||
|
>
|
||||||
{item.doc_name}
|
{item.doc_name}
|
||||||
</NewDocumentLink>
|
</NewDocumentLink>
|
||||||
</Flex>
|
</Flex>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
)}
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -38,3 +38,7 @@ export const isFileUploadDone = (file: UploadFile) => file.status === 'done';
|
|||||||
|
|
||||||
export const getExtension = (name: string) =>
|
export const getExtension = (name: string) =>
|
||||||
name?.slice(name.lastIndexOf('.') + 1).toLowerCase() ?? '';
|
name?.slice(name.lastIndexOf('.') + 1).toLowerCase() ?? '';
|
||||||
|
|
||||||
|
export const isPdf = (name: string) => {
|
||||||
|
return getExtension(name) === 'pdf';
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user