mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-05-22 04:28:52 +08:00
### What problem does this PR solve? feat: The search box is displayed globally when the page is loaded for the first time #2247 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
884dcbcb7e
commit
ceae4df889
@ -3,7 +3,7 @@ import { useTestChunkRetrieval } from '@/hooks/knowledge-hooks';
|
||||
import { useSendMessageWithSse } from '@/hooks/logic-hooks';
|
||||
import { IAnswer } from '@/interfaces/database/chat';
|
||||
import api from '@/utils/api';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { isEmpty, trim } from 'lodash';
|
||||
import { ChangeEventHandler, useCallback, useEffect, useState } from 'react';
|
||||
|
||||
export const useSendQuestion = (kbIds: string[]) => {
|
||||
@ -19,18 +19,22 @@ export const useSendQuestion = (kbIds: string[]) => {
|
||||
loading: mindMapLoading,
|
||||
} = useFetchMindMap();
|
||||
const [searchStr, setSearchStr] = useState<string>('');
|
||||
const [isFirstRender, setIsFirstRender] = useState(true);
|
||||
|
||||
const sendQuestion = useCallback(
|
||||
(question: string) => {
|
||||
const q = trim(question);
|
||||
if (isEmpty(q)) return;
|
||||
setIsFirstRender(false);
|
||||
setCurrentAnswer({} as IAnswer);
|
||||
setSendingLoading(true);
|
||||
send({ kb_ids: kbIds, question });
|
||||
testChunk({ kb_id: kbIds, highlight: true, question });
|
||||
send({ kb_ids: kbIds, question: q });
|
||||
testChunk({ kb_id: kbIds, highlight: true, question: q });
|
||||
fetchMindMap({
|
||||
question,
|
||||
question: q,
|
||||
kb_ids: kbIds,
|
||||
});
|
||||
fetchRelatedQuestions(question);
|
||||
fetchRelatedQuestions(q);
|
||||
},
|
||||
[send, testChunk, kbIds, fetchRelatedQuestions, fetchMindMap],
|
||||
);
|
||||
@ -65,14 +69,15 @@ export const useSendQuestion = (kbIds: string[]) => {
|
||||
|
||||
return {
|
||||
sendQuestion,
|
||||
handleSearchStrChange,
|
||||
handleClickRelatedQuestion,
|
||||
loading,
|
||||
sendingLoading,
|
||||
answer: currentAnswer,
|
||||
relatedQuestions: relatedQuestions?.slice(0, 5) ?? [],
|
||||
mindMap,
|
||||
mindMapLoading,
|
||||
handleClickRelatedQuestion,
|
||||
searchStr,
|
||||
handleSearchStrChange,
|
||||
isFirstRender,
|
||||
};
|
||||
};
|
||||
|
@ -49,18 +49,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
.firstRenderContent {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
.main {
|
||||
width: 60%;
|
||||
// background-color: aqua;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
padding: 20px 10px 10px;
|
||||
}
|
||||
|
||||
.graph {
|
||||
width: 40%;
|
||||
padding-right: 10px;
|
||||
padding: 20px 10px 10px;
|
||||
}
|
||||
}
|
||||
.answerWrapper {
|
||||
@ -72,3 +76,28 @@
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.input() {
|
||||
:global(.ant-input-affix-wrapper) {
|
||||
padding: 4px 8px;
|
||||
border-start-start-radius: 30px !important;
|
||||
border-end-start-radius: 30px !important;
|
||||
}
|
||||
input {
|
||||
height: 40px;
|
||||
}
|
||||
button {
|
||||
height: 50px !important;
|
||||
border-start-end-radius: 30px !important;
|
||||
border-end-end-radius: 30px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.globalInput {
|
||||
width: 600px;
|
||||
.input();
|
||||
}
|
||||
.partialInput {
|
||||
width: 100%;
|
||||
.input();
|
||||
}
|
||||
|
@ -1,14 +1,24 @@
|
||||
import HightLightMarkdown from '@/components/highlight-markdown';
|
||||
import { ImageWithPopover } from '@/components/image';
|
||||
import IndentedTree from '@/components/indented-tree/indented-tree';
|
||||
import { useSelectTestingResult } from '@/hooks/knowledge-hooks';
|
||||
import { IReference } from '@/interfaces/database/chat';
|
||||
import { Card, Flex, Input, Layout, List, Skeleton, Space, Tag } from 'antd';
|
||||
import {
|
||||
Card,
|
||||
Divider,
|
||||
Flex,
|
||||
Input,
|
||||
Layout,
|
||||
List,
|
||||
Skeleton,
|
||||
Space,
|
||||
Tag,
|
||||
} from 'antd';
|
||||
import { useState } from 'react';
|
||||
import MarkdownContent from '../chat/markdown-content';
|
||||
import { useSendQuestion } from './hooks';
|
||||
import SearchSidebar from './sidebar';
|
||||
|
||||
import IndentedTree from '@/components/indented-tree/indented-tree';
|
||||
import styles from './index.less';
|
||||
|
||||
const { Content } = Layout;
|
||||
@ -27,8 +37,25 @@ const SearchPage = () => {
|
||||
mindMap,
|
||||
mindMapLoading,
|
||||
searchStr,
|
||||
loading,
|
||||
isFirstRender,
|
||||
} = useSendQuestion(checkedList);
|
||||
|
||||
const InputSearch = (
|
||||
<Search
|
||||
value={searchStr}
|
||||
onChange={handleSearchStrChange}
|
||||
placeholder="input search text"
|
||||
allowClear
|
||||
enterButton
|
||||
onSearch={sendQuestion}
|
||||
size="large"
|
||||
loading={sendingLoading}
|
||||
disabled={checkedList.length === 0}
|
||||
className={isFirstRender ? styles.globalInput : styles.partialInput}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Layout className={styles.searchPage}>
|
||||
<SearchSidebar
|
||||
@ -37,70 +64,78 @@ const SearchPage = () => {
|
||||
></SearchSidebar>
|
||||
<Layout>
|
||||
<Content>
|
||||
<Flex className={styles.content}>
|
||||
<section className={styles.main}>
|
||||
<Search
|
||||
value={searchStr}
|
||||
onChange={handleSearchStrChange}
|
||||
placeholder="input search text"
|
||||
onSearch={sendQuestion}
|
||||
size="large"
|
||||
loading={sendingLoading}
|
||||
disabled={checkedList.length === 0}
|
||||
/>
|
||||
{answer.answer && (
|
||||
<div className={styles.answerWrapper}>
|
||||
<MarkdownContent
|
||||
loading={sendingLoading}
|
||||
content={answer.answer}
|
||||
reference={answer.reference ?? ({} as IReference)}
|
||||
clickDocumentButton={() => {}}
|
||||
></MarkdownContent>
|
||||
</div>
|
||||
)}
|
||||
<List
|
||||
dataSource={list.chunks}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<Card className={styles.card}>
|
||||
<Space>
|
||||
<ImageWithPopover id={item.img_id}></ImageWithPopover>
|
||||
<HightLightMarkdown>
|
||||
{item.highlight}
|
||||
</HightLightMarkdown>
|
||||
</Space>
|
||||
</Card>
|
||||
</List.Item>
|
||||
{isFirstRender ? (
|
||||
<Flex
|
||||
justify="center"
|
||||
align="center"
|
||||
className={styles.firstRenderContent}
|
||||
>
|
||||
{InputSearch}
|
||||
</Flex>
|
||||
) : (
|
||||
<Flex className={styles.content}>
|
||||
<section className={styles.main}>
|
||||
{InputSearch}
|
||||
{answer.answer && (
|
||||
<div className={styles.answerWrapper}>
|
||||
<MarkdownContent
|
||||
loading={sendingLoading}
|
||||
content={answer.answer}
|
||||
reference={answer.reference ?? ({} as IReference)}
|
||||
clickDocumentButton={() => {}}
|
||||
></MarkdownContent>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
{relatedQuestions?.length > 0 && (
|
||||
<Card>
|
||||
<Flex wrap="wrap" gap={'10px 0'}>
|
||||
{relatedQuestions?.map((x, idx) => (
|
||||
<Tag
|
||||
key={idx}
|
||||
className={styles.tag}
|
||||
onClick={handleClickRelatedQuestion(x)}
|
||||
>
|
||||
{x}
|
||||
</Tag>
|
||||
))}
|
||||
</Flex>
|
||||
</Card>
|
||||
)}
|
||||
</section>
|
||||
<section className={styles.graph}>
|
||||
{mindMapLoading ? (
|
||||
<Skeleton active />
|
||||
) : (
|
||||
<IndentedTree
|
||||
data={mindMap}
|
||||
show
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
></IndentedTree>
|
||||
)}
|
||||
</section>
|
||||
</Flex>
|
||||
<Divider></Divider>
|
||||
{list.chunks.length > 0 && (
|
||||
<List
|
||||
dataSource={list.chunks}
|
||||
loading={loading}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<Card className={styles.card}>
|
||||
<Space>
|
||||
<ImageWithPopover
|
||||
id={item.img_id}
|
||||
></ImageWithPopover>
|
||||
<HightLightMarkdown>
|
||||
{item.highlight}
|
||||
</HightLightMarkdown>
|
||||
</Space>
|
||||
</Card>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{relatedQuestions?.length > 0 && (
|
||||
<Card>
|
||||
<Flex wrap="wrap" gap={'10px 0'}>
|
||||
{relatedQuestions?.map((x, idx) => (
|
||||
<Tag
|
||||
key={idx}
|
||||
className={styles.tag}
|
||||
onClick={handleClickRelatedQuestion(x)}
|
||||
>
|
||||
{x}
|
||||
</Tag>
|
||||
))}
|
||||
</Flex>
|
||||
</Card>
|
||||
)}
|
||||
</section>
|
||||
<section className={styles.graph}>
|
||||
{mindMapLoading ? (
|
||||
<Skeleton active />
|
||||
) : (
|
||||
<IndentedTree
|
||||
data={mindMap}
|
||||
show
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
></IndentedTree>
|
||||
)}
|
||||
</section>
|
||||
</Flex>
|
||||
)}
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
|
@ -22,7 +22,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => {
|
||||
const { list } = useNextFetchKnowledgeList();
|
||||
const { list, loading } = useNextFetchKnowledgeList();
|
||||
const ids = useMemo(() => list.map((x) => x.id), [list]);
|
||||
|
||||
const checkAll = list.length === checkedList.length;
|
||||
@ -67,6 +67,7 @@ const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => {
|
||||
bordered
|
||||
dataSource={list}
|
||||
className={styles.list}
|
||||
loading={loading}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<Checkbox value={item.id} className={styles.checkbox}>
|
||||
|
Loading…
x
Reference in New Issue
Block a user