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,17 +64,18 @@ const SearchPage = () => {
></SearchSidebar> ></SearchSidebar>
<Layout> <Layout>
<Content> <Content>
{isFirstRender ? (
<Flex
justify="center"
align="center"
className={styles.firstRenderContent}
>
{InputSearch}
</Flex>
) : (
<Flex className={styles.content}> <Flex className={styles.content}>
<section className={styles.main}> <section className={styles.main}>
<Search {InputSearch}
value={searchStr}
onChange={handleSearchStrChange}
placeholder="input search text"
onSearch={sendQuestion}
size="large"
loading={sendingLoading}
disabled={checkedList.length === 0}
/>
{answer.answer && ( {answer.answer && (
<div className={styles.answerWrapper}> <div className={styles.answerWrapper}>
<MarkdownContent <MarkdownContent
@ -58,13 +86,18 @@ const SearchPage = () => {
></MarkdownContent> ></MarkdownContent>
</div> </div>
)} )}
<Divider></Divider>
{list.chunks.length > 0 && (
<List <List
dataSource={list.chunks} dataSource={list.chunks}
loading={loading}
renderItem={(item) => ( renderItem={(item) => (
<List.Item> <List.Item>
<Card className={styles.card}> <Card className={styles.card}>
<Space> <Space>
<ImageWithPopover id={item.img_id}></ImageWithPopover> <ImageWithPopover
id={item.img_id}
></ImageWithPopover>
<HightLightMarkdown> <HightLightMarkdown>
{item.highlight} {item.highlight}
</HightLightMarkdown> </HightLightMarkdown>
@ -73,6 +106,7 @@ const SearchPage = () => {
</List.Item> </List.Item>
)} )}
/> />
)}
{relatedQuestions?.length > 0 && ( {relatedQuestions?.length > 0 && (
<Card> <Card>
<Flex wrap="wrap" gap={'10px 0'}> <Flex wrap="wrap" gap={'10px 0'}>
@ -101,6 +135,7 @@ const SearchPage = () => {
)} )}
</section> </section>
</Flex> </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}>