feat: The search box is displayed globally when the page is loaded for the first time #2247 (#2325)

### 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:
balibabu 2024-09-09 16:37:04 +08:00 committed by GitHub
parent 884dcbcb7e
commit ceae4df889
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 145 additions and 75 deletions

View File

@ -3,7 +3,7 @@ import { useTestChunkRetrieval } from '@/hooks/knowledge-hooks';
import { useSendMessageWithSse } from '@/hooks/logic-hooks'; import { useSendMessageWithSse } from '@/hooks/logic-hooks';
import { IAnswer } from '@/interfaces/database/chat'; import { IAnswer } from '@/interfaces/database/chat';
import api from '@/utils/api'; import api from '@/utils/api';
import { isEmpty } from 'lodash'; import { isEmpty, trim } from 'lodash';
import { ChangeEventHandler, useCallback, useEffect, useState } from 'react'; import { ChangeEventHandler, useCallback, useEffect, useState } from 'react';
export const useSendQuestion = (kbIds: string[]) => { export const useSendQuestion = (kbIds: string[]) => {
@ -19,18 +19,22 @@ export const useSendQuestion = (kbIds: string[]) => {
loading: mindMapLoading, loading: mindMapLoading,
} = useFetchMindMap(); } = useFetchMindMap();
const [searchStr, setSearchStr] = useState<string>(''); const [searchStr, setSearchStr] = useState<string>('');
const [isFirstRender, setIsFirstRender] = useState(true);
const sendQuestion = useCallback( const sendQuestion = useCallback(
(question: string) => { (question: string) => {
const q = trim(question);
if (isEmpty(q)) return;
setIsFirstRender(false);
setCurrentAnswer({} as IAnswer); setCurrentAnswer({} as IAnswer);
setSendingLoading(true); setSendingLoading(true);
send({ kb_ids: kbIds, question }); send({ kb_ids: kbIds, question: q });
testChunk({ kb_id: kbIds, highlight: true, question }); testChunk({ kb_id: kbIds, highlight: true, question: q });
fetchMindMap({ fetchMindMap({
question, question: q,
kb_ids: kbIds, kb_ids: kbIds,
}); });
fetchRelatedQuestions(question); fetchRelatedQuestions(q);
}, },
[send, testChunk, kbIds, fetchRelatedQuestions, fetchMindMap], [send, testChunk, kbIds, fetchRelatedQuestions, fetchMindMap],
); );
@ -65,14 +69,15 @@ export const useSendQuestion = (kbIds: string[]) => {
return { return {
sendQuestion, sendQuestion,
handleSearchStrChange,
handleClickRelatedQuestion,
loading, loading,
sendingLoading, sendingLoading,
answer: currentAnswer, answer: currentAnswer,
relatedQuestions: relatedQuestions?.slice(0, 5) ?? [], relatedQuestions: relatedQuestions?.slice(0, 5) ?? [],
mindMap, mindMap,
mindMapLoading, mindMapLoading,
handleClickRelatedQuestion,
searchStr, searchStr,
handleSearchStrChange, isFirstRender,
}; };
}; };

View File

@ -49,18 +49,22 @@
} }
} }
.firstRenderContent {
height: 100%;
}
.content { .content {
height: 100%; height: 100%;
.main { .main {
width: 60%; width: 60%;
// background-color: aqua; // background-color: aqua;
overflow: auto; overflow: auto;
padding: 10px; padding: 20px 10px 10px;
} }
.graph { .graph {
width: 40%; width: 40%;
padding-right: 10px; padding: 20px 10px 10px;
} }
} }
.answerWrapper { .answerWrapper {
@ -72,3 +76,28 @@
margin: 0; 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();
}

View File

@ -1,14 +1,24 @@
import HightLightMarkdown from '@/components/highlight-markdown'; import HightLightMarkdown from '@/components/highlight-markdown';
import { ImageWithPopover } from '@/components/image'; import { ImageWithPopover } from '@/components/image';
import IndentedTree from '@/components/indented-tree/indented-tree';
import { useSelectTestingResult } from '@/hooks/knowledge-hooks'; import { useSelectTestingResult } from '@/hooks/knowledge-hooks';
import { IReference } from '@/interfaces/database/chat'; 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 { useState } from 'react';
import MarkdownContent from '../chat/markdown-content'; import MarkdownContent from '../chat/markdown-content';
import { useSendQuestion } from './hooks'; import { useSendQuestion } from './hooks';
import SearchSidebar from './sidebar'; import SearchSidebar from './sidebar';
import IndentedTree from '@/components/indented-tree/indented-tree';
import styles from './index.less'; import styles from './index.less';
const { Content } = Layout; const { Content } = Layout;
@ -27,8 +37,25 @@ const SearchPage = () => {
mindMap, mindMap,
mindMapLoading, mindMapLoading,
searchStr, searchStr,
loading,
isFirstRender,
} = useSendQuestion(checkedList); } = 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 ( return (
<Layout className={styles.searchPage}> <Layout className={styles.searchPage}>
<SearchSidebar <SearchSidebar
@ -37,70 +64,78 @@ const SearchPage = () => {
></SearchSidebar> ></SearchSidebar>
<Layout> <Layout>
<Content> <Content>
<Flex className={styles.content}> {isFirstRender ? (
<section className={styles.main}> <Flex
<Search justify="center"
value={searchStr} align="center"
onChange={handleSearchStrChange} className={styles.firstRenderContent}
placeholder="input search text" >
onSearch={sendQuestion} {InputSearch}
size="large" </Flex>
loading={sendingLoading} ) : (
disabled={checkedList.length === 0} <Flex className={styles.content}>
/> <section className={styles.main}>
{answer.answer && ( {InputSearch}
<div className={styles.answerWrapper}> {answer.answer && (
<MarkdownContent <div className={styles.answerWrapper}>
loading={sendingLoading} <MarkdownContent
content={answer.answer} loading={sendingLoading}
reference={answer.reference ?? ({} as IReference)} content={answer.answer}
clickDocumentButton={() => {}} reference={answer.reference ?? ({} as IReference)}
></MarkdownContent> clickDocumentButton={() => {}}
</div> ></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>
)} )}
/> <Divider></Divider>
{relatedQuestions?.length > 0 && ( {list.chunks.length > 0 && (
<Card> <List
<Flex wrap="wrap" gap={'10px 0'}> dataSource={list.chunks}
{relatedQuestions?.map((x, idx) => ( loading={loading}
<Tag renderItem={(item) => (
key={idx} <List.Item>
className={styles.tag} <Card className={styles.card}>
onClick={handleClickRelatedQuestion(x)} <Space>
> <ImageWithPopover
{x} id={item.img_id}
</Tag> ></ImageWithPopover>
))} <HightLightMarkdown>
</Flex> {item.highlight}
</Card> </HightLightMarkdown>
)} </Space>
</section> </Card>
<section className={styles.graph}> </List.Item>
{mindMapLoading ? ( )}
<Skeleton active /> />
) : ( )}
<IndentedTree {relatedQuestions?.length > 0 && (
data={mindMap} <Card>
show <Flex wrap="wrap" gap={'10px 0'}>
style={{ width: '100%', height: '100%' }} {relatedQuestions?.map((x, idx) => (
></IndentedTree> <Tag
)} key={idx}
</section> className={styles.tag}
</Flex> 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> </Content>
</Layout> </Layout>
</Layout> </Layout>

View File

@ -22,7 +22,7 @@ interface IProps {
} }
const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => { const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => {
const { list } = useNextFetchKnowledgeList(); const { list, loading } = useNextFetchKnowledgeList();
const ids = useMemo(() => list.map((x) => x.id), [list]); const ids = useMemo(() => list.map((x) => x.id), [list]);
const checkAll = list.length === checkedList.length; const checkAll = list.length === checkedList.length;
@ -67,6 +67,7 @@ const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => {
bordered bordered
dataSource={list} dataSource={list}
className={styles.list} className={styles.list}
loading={loading}
renderItem={(item) => ( renderItem={(item) => (
<List.Item> <List.Item>
<Checkbox value={item.id} className={styles.checkbox}> <Checkbox value={item.id} className={styles.checkbox}>