Fix (search): Optimize the search page functionality and UI #3221 (#9525)

### What problem does this PR solve?

Fix (search): Optimize the search page functionality and UI #3221 

- Add a search list component
- Implement search settings
- Optimize search result display
- Add related search functionality
- Adjust the search input box style
- Unify internationalized text

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-08-18 14:50:29 +08:00
committed by GitHub
parent ca720bd811
commit 5808aef28c
14 changed files with 237 additions and 159 deletions

View File

@ -49,7 +49,7 @@ export const RAGFlowAvatar = memo(
const initials = getInitials(name);
const { from, to } = name
? getColorForName(name)
: { from: 'hsl(0, 0%, 80%)', to: 'hsl(0, 0%, 30%)' };
: { from: 'hsl(0, 0%, 30%)', to: 'hsl(0, 0%, 80%)' };
const fallbackRef = useRef<HTMLElement>(null);
const [fontSize, setFontSize] = useState('0.875rem');

View File

@ -1419,6 +1419,24 @@ This delimiter is used to split the input text into several text pieces echo of
search: {
createSearch: 'Create Search',
searchGreeting: 'How can I help you today ',
profile: 'Hide Profile',
locale: 'Locale',
embedCode: 'Embed code',
id: 'ID',
copySuccess: 'Copy Success',
welcomeBack: 'Welcome back',
searchSettings: 'Search Settings',
name: 'Name',
avatar: 'Avatar',
description: 'Description',
datasets: 'Datasets',
rerankModel: 'Rerank Model',
AISummary: 'AI Summary',
enableWebSearch: 'Enable Web Search',
enableRelatedSearch: 'Enable Related Search',
showQueryMindmap: 'Show Query Mindmap',
embedApp: 'Embed App',
relatedSearch: 'Related Search',
},
},
};

View File

@ -1192,6 +1192,12 @@ export default {
search: {
createSearch: '新建查詢',
searchGreeting: '今天我能為你做些什麽?',
profile: '隱藏個人資料',
locale: '語言',
embedCode: '嵌入代碼',
id: 'ID',
copySuccess: '複製成功',
welcomeBack: '歡迎回來',
},
},
};

View File

@ -1323,6 +1323,24 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
search: {
createSearch: '新建查询',
searchGreeting: '今天我能为你做些什么?',
profile: '隐藏个人资料',
locale: '语言',
embedCode: '嵌入代码',
id: 'ID',
copySuccess: '复制成功',
welcomeBack: '欢迎回来',
searchSettings: '搜索设置',
name: '姓名',
avatar: '头像',
description: '描述',
datasets: '数据集',
rerankModel: 'rerank 模型',
AISummary: 'AI 总结',
enableWebSearch: '启用网页搜索',
enableRelatedSearch: '启用相关搜索',
showQueryMindmap: '显示查询思维导图',
embedApp: '嵌入网站',
relatedSearch: '相关搜索',
},
},
};

View File

@ -7,6 +7,7 @@ import { useNavigate } from 'umi';
import { Agents } from './agent-list';
import { SeeAllAppCard } from './application-card';
import { ChatList } from './chat-list';
import { SearchList } from './search-list';
const IconMap = {
[Routes.Chats]: 'chat',
@ -56,6 +57,7 @@ export function Applications() {
<div className="flex flex-wrap gap-4">
{val === Routes.Agents && <Agents></Agents>}
{val === Routes.Chats && <ChatList></ChatList>}
{val === Routes.Searches && <SearchList></SearchList>}
{<SeeAllAppCard click={handleNavigate}></SeeAllAppCard>}
</div>
</section>

View File

@ -0,0 +1,15 @@
import { useFetchSearchList } from '../next-searches/hooks';
import { ApplicationCard } from './application-card';
export function SearchList() {
const { data } = useFetchSearchList();
return data?.data.search_apps
.slice(0, 10)
.map((x) => (
<ApplicationCard
key={x.id}
app={{ avatar: x.avatar, title: x.name, update_time: x.update_time }}
></ApplicationCard>
));
}

View File

@ -7,21 +7,28 @@ import {
LanguageAbbreviationMap,
} from '@/constants/common';
import { useTranslate } from '@/hooks/common-hooks';
import { useCallback, useMemo, useState } from 'react';
import { message } from 'antd';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useFetchTokenListBeforeOtherStep } from '../agent/hooks/use-show-dialog';
type IEmbedAppModalProps = {
open: any;
url: string;
token: string;
from: string;
beta: string;
setOpen: (e: any) => void;
tenantId: string;
};
const EmbedAppModal = (props: IEmbedAppModalProps) => {
const { t } = useTranslate('chat');
const { open, setOpen, token = '', from, beta = '', url, tenantId } = props;
const { t } = useTranslate('search');
const { open, setOpen, token = '', from, url, tenantId } = props;
const { beta, handleOperate } = useFetchTokenListBeforeOtherStep();
useEffect(() => {
if (open && !beta) {
handleOperate();
}
}, [handleOperate, open, beta]);
const [hideAvatar, setHideAvatar] = useState(false);
const [locale, setLocale] = useState('');
@ -69,7 +76,7 @@ const EmbedAppModal = (props: IEmbedAppModalProps) => {
{/* Hide Avatar Toggle */}
<div className="mb-6">
<label className="block text-sm font-medium mb-2">
{t('avatarHidden')}
{t('profile')}
</label>
<div className="flex items-center">
<Switch
@ -83,7 +90,9 @@ const EmbedAppModal = (props: IEmbedAppModalProps) => {
{/* Locale Select */}
<div className="mb-6">
<label className="block text-sm font-medium mb-2">Locale</label>
<label className="block text-sm font-medium mb-2">
{t('locale')}
</label>
<RAGFlowSelect
placeholder="Select a locale"
value={locale}
@ -93,7 +102,9 @@ const EmbedAppModal = (props: IEmbedAppModalProps) => {
</div>
{/* Embed Code */}
<div className="mb-6">
<label className="block text-sm font-medium mb-2">Embed code</label>
<label className="block text-sm font-medium mb-2">
{t('embedCode')}
</label>
{/* <div className=" border rounded-lg"> */}
{/* <pre className="text-sm whitespace-pre-wrap">{text}</pre> */}
<HightLightMarkdown>{text}</HightLightMarkdown>
@ -102,18 +113,21 @@ const EmbedAppModal = (props: IEmbedAppModalProps) => {
{/* ID Field */}
<div className="mb-4">
<label className="block text-sm font-medium mb-2">ID</label>
<div className="flex items-center">
<label className="block text-sm font-medium mb-2">{t('id')}</label>
<div className="flex items-center border border-border rounded-lg bg-bg-base">
<input
type="text"
value={token}
readOnly
className="flex-1 px-4 py-2 border border-gray-700 rounded-lg bg-bg-base focus:outline-none"
className="flex-1 px-4 py-2 focus:outline-none bg-bg-base rounded-lg"
/>
<button
type="button"
onClick={() => navigator.clipboard.writeText(token)}
className="ml-2 p-2 text-gray-400 hover:text-white transition-colors"
onClick={() => {
navigator.clipboard.writeText(token);
message.success(t('copySuccess'));
}}
className="ml-2 p-2 hover:text-white transition-colors"
title="Copy ID"
>
<svg

View File

@ -486,3 +486,16 @@ export const useSearching = ({
onChange,
};
};
export const useCheckSettings = (data: ISearchAppDetailProps) => {
if (!data) {
return {
openSetting: false,
};
}
const { search_config, name } = data;
const { kb_ids } = search_config;
return {
openSetting: kb_ids && kb_ids.length && name ? false : true,
};
};

View File

@ -13,12 +13,13 @@ import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useFetchTenantInfo } from '@/hooks/user-setting-hooks';
import { Send, Settings } from 'lucide-react';
import { useEffect, useState } from 'react';
import { useFetchTokenListBeforeOtherStep } from '../agent/hooks/use-show-dialog';
import { useTranslation } from 'react-i18next';
import {
ISearchAppDetailProps,
useFetchSearchDetail,
} from '../next-searches/hooks';
import EmbedAppModal from './embed-app-modal';
import { useCheckSettings } from './hooks';
import './index.less';
import SearchHome from './search-home';
import { SearchSetting } from './search-setting';
@ -28,15 +29,20 @@ export default function SearchPage() {
const { navigateToSearchList } = useNavigatePage();
const [isSearching, setIsSearching] = useState(false);
const { data: SearchData } = useFetchSearchDetail();
const { beta, handleOperate } = useFetchTokenListBeforeOtherStep();
const [openSetting, setOpenSetting] = useState(false);
const [openEmbed, setOpenEmbed] = useState(false);
const [searchText, setSearchText] = useState('');
const { data: tenantInfo } = useFetchTenantInfo();
const tenantId = tenantInfo.tenant_id;
const { t } = useTranslation();
const { openSetting: checkOpenSetting } = useCheckSettings(
SearchData as ISearchAppDetailProps,
);
useEffect(() => {
handleOperate();
}, [handleOperate]);
setOpenSetting(checkOpenSetting);
}, [checkOpenSetting]);
useEffect(() => {
if (isSearching) {
setOpenSetting(false);
@ -60,7 +66,7 @@ export default function SearchPage() {
</BreadcrumbList>
</Breadcrumb>
</PageHeader>
<div className="flex gap-3 w-full">
<div className="flex gap-3 w-full bg-bg-base">
<div className="flex-1">
{!isSearching && (
<div className="animate-fade-in-down">
@ -98,7 +104,6 @@ export default function SearchPage() {
url="/next-search/share"
token={SearchData?.id as string}
from={SharedFrom.Search}
beta={beta}
tenantId={tenantId}
/>
}
@ -119,7 +124,7 @@ export default function SearchPage() {
onClick={() => setOpenEmbed(!openEmbed)}
>
<Send />
<div>Embed App</div>
<div>{t('search.embedApp')}</div>
</Button>
</div>
{!isSearching && (
@ -130,7 +135,9 @@ export default function SearchPage() {
onClick={() => setOpenSetting(!openSetting)}
>
<Settings className="text-text-secondary" />
<div className="text-text-secondary">Search Settings</div>
<div className="text-text-secondary">
{t('search.searchSettings')}
</div>
</Button>
</div>
)}

View File

@ -3,6 +3,7 @@ import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
import { cn } from '@/lib/utils';
import { Search } from 'lucide-react';
import { Dispatch, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next';
import './index.less';
import Spotlight from './spotlight';
@ -18,6 +19,7 @@ export default function SearchPage({
setSearchText: Dispatch<SetStateAction<string>>;
}) {
const { data: userInfo } = useFetchUserInfo();
const { t } = useTranslation();
return (
<section className="relative w-full flex transition-all justify-center items-center mt-32">
<div className="relative z-10 px-8 pt-8 flex text-transparent flex-col justify-center items-center w-[780px]">
@ -36,14 +38,14 @@ export default function SearchPage({
<>
<p className="mb-4 transition-opacity">👋 Hi there</p>
<p className="mb-10 transition-opacity">
Welcome back, {userInfo?.nickname}
{t('search.welcomeBack')}, {userInfo?.nickname}
</p>
</>
)}
<div className="relative w-full ">
<Input
placeholder="How can I help you today?"
placeholder={t('search.searchGreeting')}
className="w-full rounded-full py-6 px-4 pr-10 text-text-primary text-lg bg-bg-base delay-700"
value={searchText}
onKeyUp={(e) => {
@ -57,7 +59,7 @@ export default function SearchPage({
/>
<button
type="button"
className="absolute right-2 top-1/2 -translate-y-1/2 transform rounded-full bg-white p-2 text-gray-800 shadow w-12"
className="absolute right-2 top-1/2 -translate-y-1/2 transform rounded-full bg-text-primary p-2 text-bg-base shadow w-12"
onClick={() => {
setIsSearching(!isSearching);
}}

View File

@ -28,10 +28,10 @@ import { IKnowledge } from '@/interfaces/database/knowledge';
import { cn } from '@/lib/utils';
import { transformFile2Base64 } from '@/utils/file-util';
import { zodResolver } from '@hookform/resolvers/zod';
import { t } from 'i18next';
import { Pencil, Upload, X } from 'lucide-react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import {
LlmModelType,
@ -113,6 +113,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
const [datasetList, setDatasetList] = useState<MultiSelectOptionType[]>([]);
const [datasetSelectEmbdId, setDatasetSelectEmbdId] = useState('');
const descriptionDefaultValue = 'You are an intelligent assistant.';
const { t } = useTranslation();
const resetForm = useCallback(() => {
formMethods.reset({
search_id: data?.id,
@ -305,7 +306,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
style={{ maxHeight: 'calc(100dvh - 170px)' }}
>
<div className="flex justify-between items-center text-base mb-8">
<div className="text-text-primary">Search Settings</div>
<div className="text-text-primary">{t('search.searchSettings')}</div>
<div onClick={() => setOpen(false)}>
<X size={16} className="text-text-primary cursor-pointer" />
</div>
@ -334,10 +335,11 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
render={({ field }) => (
<FormItem>
<FormLabel>
<span className="text-destructive mr-1"> *</span>Name
<span className="text-destructive mr-1"> *</span>
{t('search.name')}
</FormLabel>
<FormControl>
<Input placeholder="Name" {...field} />
<Input placeholder={t('search.name')} {...field} />
</FormControl>
<FormMessage />
</FormItem>
@ -350,7 +352,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
name="avatar"
render={() => (
<FormItem>
<FormLabel>Avatar</FormLabel>
<FormLabel>{t('search.avatar')}</FormLabel>
<FormControl>
<div className="relative group flex items-end gap-2">
<div>
@ -413,7 +415,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormLabel>{t('search.description')}</FormLabel>
<FormControl>
<Input
placeholder="You are an intelligent assistant."
@ -443,7 +445,8 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
render={({ field }) => (
<FormItem>
<FormLabel>
<span className="text-destructive mr-1"> *</span>Datasets
<span className="text-destructive mr-1"> *</span>
{t('search.datasets')}
</FormLabel>
<FormControl>
<MultiSelect
@ -501,26 +504,6 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
</div>
<FormMessage />
</FormItem>
// <FormItem className="flex flex-col">
// <FormLabel>
// <span className="text-destructive mr-1"> *</span>Keyword
// Similarity Weight
// </FormLabel>
// <FormControl>
// {/* <div className="flex justify-between items-center">
// <SingleFormSlider
// max={100}
// step={1}
// value={field.value as number}
// onChange={(values) => field.onChange(values)}
// ></SingleFormSlider>
// <Label className="w-10 h-6 bg-bg-card flex justify-center items-center rounded-lg ml-20">
// {field.value}
// </Label>
// </div> */}
// </FormControl>
// <FormMessage />
// </FormItem>
)}
/>
@ -536,7 +519,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
onCheckedChange={field.onChange}
/>
</FormControl>
<FormLabel>Rerank Model</FormLabel>
<FormLabel>{t('search.rerankModel')}</FormLabel>
</FormItem>
)}
/>
@ -612,7 +595,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
onCheckedChange={field.onChange}
/>
</FormControl>
<FormLabel>AI Summary</FormLabel>
<FormLabel>{t('search.AISummary')}</FormLabel>
</FormItem>
)}
/>
@ -636,7 +619,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
onCheckedChange={field.onChange}
/>
</FormControl>
<FormLabel>Enable Web Search</FormLabel>
<FormLabel>{t('search.enableWebSearch')}</FormLabel>
</FormItem>
)}
/>
@ -652,7 +635,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
onCheckedChange={field.onChange}
/>
</FormControl>
<FormLabel>Enable Related Search</FormLabel>
<FormLabel>{t('search.enableRelatedSearch')}</FormLabel>
</FormItem>
)}
/>
@ -668,7 +651,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
onCheckedChange={field.onChange}
/>
</FormControl>
<FormLabel>Show Query Mindmap</FormLabel>
<FormLabel>{t('search.showQueryMindmap')}</FormLabel>
</FormItem>
)}
/>
@ -683,9 +666,9 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
setOpen(false);
}}
>
Cancel
{t('modal.cancelText')}
</Button>
<Button type="submit">Confirm</Button>
<Button type="submit">{t('modal.okText')}</Button>
</div>
</form>
</Form>

View File

@ -9,13 +9,11 @@ import {
} from '@/components/ui/popover';
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
import { Skeleton } from '@/components/ui/skeleton';
import { Spin } from '@/components/ui/spin';
import { IReference } from '@/interfaces/database/chat';
import { cn } from '@/lib/utils';
import DOMPurify from 'dompurify';
import { TFunction } from 'i18next';
import { isEmpty } from 'lodash';
import { BrainCircuit, Search, Square, X } from 'lucide-react';
import { BrainCircuit, Search, X } from 'lucide-react';
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ISearchAppDetailProps } from '../next-searches/hooks';
@ -35,7 +33,6 @@ export default function SearchingView({
answer,
sendingLoading,
relatedQuestions,
loading,
isFirstRender,
selectedDocumentIds,
isSearchStrEmpty,
@ -56,19 +53,17 @@ export default function SearchingView({
handleSearch,
pagination,
onChange,
t,
}: ISearchReturnProps & {
setIsSearching?: Dispatch<SetStateAction<boolean>>;
searchData: ISearchAppDetailProps;
t: TFunction<'translation', undefined>;
}) {
const { t: tt, i18n } = useTranslation();
useEffect(() => {
const changeLanguage = async () => {
await i18n.changeLanguage('zh');
};
changeLanguage();
}, [i18n]);
const { t } = useTranslation();
// useEffect(() => {
// const changeLanguage = async () => {
// await i18n.changeLanguage('zh');
// };
// changeLanguage();
// }, [i18n]);
const [searchtext, setSearchtext] = useState<string>('');
useEffect(() => {
@ -105,9 +100,9 @@ export default function SearchingView({
<div className={cn('flex flex-col justify-start items-start w-full')}>
<div className="relative w-full text-primary">
<Input
placeholder={tt('search.searchGreeting')}
placeholder={t('search.searchGreeting')}
className={cn(
'w-full rounded-full py-6 pl-4 !pr-[8rem] text-primary text-lg bg-background',
'w-full rounded-full py-6 pl-4 !pr-[8rem] text-primary text-lg bg-bg-base',
)}
value={searchtext}
onChange={(e) => {
@ -122,16 +117,17 @@ export default function SearchingView({
/>
<div className="absolute right-2 top-1/2 -translate-y-1/2 transform flex items-center gap-1">
<X
className="text-text-secondary"
className="text-text-secondary cursor-pointer"
size={14}
onClick={() => {
setSearchtext('');
handleClickRelatedQuestion('');
}}
/>
<span className="text-text-secondary">|</span>
<span className="text-text-secondary ml-4">|</span>
<button
type="button"
className="rounded-full bg-white p-1 text-gray-800 shadow w-12 h-8 ml-4"
className="rounded-full bg-text-primary p-1 text-bg-base shadow w-12 h-8 ml-4"
onClick={() => {
if (sendingLoading) {
stopOutputMessage();
@ -141,7 +137,8 @@ export default function SearchingView({
}}
>
{sendingLoading ? (
<Square size={22} className="m-auto" />
// <Square size={22} className="m-auto" />
<div className="w-2 h-2 bg-bg-base m-auto"></div>
) : (
<Search size={22} className="m-auto" />
)}
@ -157,10 +154,10 @@ export default function SearchingView({
{searchData.search_config.summary && !isSearchStrEmpty && (
<>
<div className="flex justify-start items-start text-text-primary text-2xl">
AI Summary
{t('search.AISummary')}
</div>
{isEmpty(answer) && sendingLoading ? (
<div className="space-y-2">
<div className="space-y-2 mt-2">
<Skeleton className="h-4 w-full bg-bg-card" />
<Skeleton className="h-4 w-full bg-bg-card" />
<Skeleton className="h-4 w-2/3 bg-bg-card" />
@ -194,76 +191,75 @@ export default function SearchingView({
</>
)}
<div className="mt-3 ">
<Spin spinning={loading}>
{chunks?.length > 0 && (
<>
{chunks.map((chunk, index) => {
return (
<>
<div
key={chunk.chunk_id}
className="w-full flex flex-col"
>
<div className="w-full">
<ImageWithPopover
id={chunk.img_id}
></ImageWithPopover>
<Popover>
<PopoverTrigger asChild>
<div
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(
`${chunk.highlight}...`,
),
}}
className="text-sm text-text-primary mb-1"
></div>
</PopoverTrigger>
<PopoverContent className="text-text-primary">
<HightLightMarkdown>
{chunk.content_with_weight}
</HightLightMarkdown>
</PopoverContent>
</Popover>
</div>
<div
className="flex gap-2 items-center text-xs text-text-secondary border p-1 rounded-lg w-fit"
onClick={() =>
clickDocumentButton(chunk.doc_id, chunk as any)
}
>
<FileIcon name={chunk.docnm_kwd}></FileIcon>
{chunk.docnm_kwd}
</div>
{chunks?.length > 0 && (
<>
{chunks.map((chunk, index) => {
return (
<>
<div
key={chunk.chunk_id}
className="w-full flex flex-col"
>
<div className="w-full">
<ImageWithPopover
id={chunk.img_id}
></ImageWithPopover>
<Popover>
<PopoverTrigger asChild>
<div
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(
`${chunk.highlight}...`,
),
}}
className="text-sm text-text-primary mb-1"
></div>
</PopoverTrigger>
<PopoverContent className="text-text-primary">
<HightLightMarkdown>
{chunk.content_with_weight}
</HightLightMarkdown>
</PopoverContent>
</Popover>
</div>
{index < chunks.length - 1 && (
<div className="w-full border-b border-border-default/80 mt-6"></div>
)}
</>
);
})}
</>
)}
</Spin>
{relatedQuestions?.length > 0 && (
<div className="mt-14 w-full overflow-hidden opacity-100 max-h-96">
<p className="text-text-primary mb-2 text-xl">
Related Search
</p>
<div className="mt-2 flex flex-wrap justify-start gap-2">
{relatedQuestions?.map((x, idx) => (
<Button
key={idx}
variant="transparent"
className="bg-bg-card text-text-secondary"
onClick={handleClickRelatedQuestion(x)}
>
Related Search{x}
</Button>
))}
</div>
</div>
<div
className="flex gap-2 items-center text-xs text-text-secondary border p-1 rounded-lg w-fit"
onClick={() =>
clickDocumentButton(chunk.doc_id, chunk as any)
}
>
<FileIcon name={chunk.docnm_kwd}></FileIcon>
{chunk.docnm_kwd}
</div>
</div>
{index < chunks.length - 1 && (
<div className="w-full border-b border-border-default/80 mt-6"></div>
)}
</>
);
})}
</>
)}
{relatedQuestions?.length > 0 &&
searchData.search_config.related_search && (
<div className="mt-14 w-full overflow-hidden opacity-100 max-h-96">
<p className="text-text-primary mb-2 text-xl">
{t('relatedSearch')}
</p>
<div className="mt-2 flex flex-wrap justify-start gap-2">
{relatedQuestions?.map((x, idx) => (
<Button
key={idx}
variant="transparent"
className="bg-bg-card text-text-secondary"
onClick={handleClickRelatedQuestion(x)}
>
{x}
</Button>
))}
</div>
</div>
)}
</div>
</div>

View File

@ -1,5 +1,4 @@
import { Dispatch, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next';
import { ISearchAppDetailProps } from '../next-searches/hooks';
import { useSearching } from './hooks';
import './index.less';
@ -21,13 +20,11 @@ export default function SearchingPage({
setIsSearching,
setSearchText,
});
const { t } = useTranslation();
return (
<SearchingView
{...searchingParam}
searchData={searchData}
setIsSearching={setIsSearching}
t={t}
/>
);
}

View File

@ -1,6 +1,6 @@
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
import i18n from '@/locales/config';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import {
ISearchAppDetailProps,
useFetchSearchDetail,
@ -9,7 +9,7 @@ import { useGetSharedSearchParams, useSearching } from '../hooks';
import '../index.less';
import SearchingView from '../search-view';
export default function SearchingPage() {
const { tenantId, locale } = useGetSharedSearchParams();
const { tenantId, locale, visibleAvatar } = useGetSharedSearchParams();
const {
data: searchData = {
search_config: { kb_ids: [] },
@ -18,18 +18,25 @@ export default function SearchingPage() {
const searchingParam = useSearching({
data: searchData,
});
const { t } = useTranslation();
// useEffect(() => {
// if (locale) {
// i18n.changeLanguage(locale);
// }
// }, [locale, i18n]);
useEffect(() => {
console.log('locale', locale, i18n.language);
if (locale && i18n.language !== locale) {
i18n.changeLanguage(locale);
}
}, [locale]);
return <SearchingView {...searchingParam} searchData={searchData} t={t} />;
return (
<>
{visibleAvatar && (
<div className="flex justify-start items-center gap-1 mx-6 mt-6 text-text-primary">
<RAGFlowAvatar
avatar={searchData.avatar}
name={searchData.name}
></RAGFlowAvatar>
<div>{searchData.name}</div>
</div>
)}
<SearchingView {...searchingParam} searchData={searchData} />;
</>
);
}