mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 12:32:30 +08:00
### What problem does this PR solve? feat(search): Optimized search functionality and user interface #3221 ### Type of change - Added similarity threshold adjustment function - Optimized mind map display logic - Adjusted search settings interface layout - Fixed related search and document viewing functions - Optimized time display and node selection logic - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -7,4 +7,5 @@ export interface IFeedbackRequestBody {
|
||||
export interface IAskRequestBody {
|
||||
question: string;
|
||||
kb_ids: string[];
|
||||
search_id?: string;
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { useTheme } from '@/components/theme-provider';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@ -163,9 +162,10 @@ export function Header() {
|
||||
className="size-8 cursor-pointer"
|
||||
onClick={navigateToProfile}
|
||||
></RAGFlowAvatar>
|
||||
<Badge className="h-5 w-8 absolute font-normal p-0 justify-center -right-8 -top-2 text-bg-base bg-gradient-to-l from-[#42D7E7] to-[#478AF5]">
|
||||
{/* Temporarily hidden */}
|
||||
{/* <Badge className="h-5 w-8 absolute font-normal p-0 justify-center -right-8 -top-2 text-bg-base bg-gradient-to-l from-[#42D7E7] to-[#478AF5]">
|
||||
Pro
|
||||
</Badge>
|
||||
</Badge> */}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -1437,6 +1437,8 @@ This delimiter is used to split the input text into several text pieces echo of
|
||||
showQueryMindmap: 'Show Query Mindmap',
|
||||
embedApp: 'Embed App',
|
||||
relatedSearch: 'Related Search',
|
||||
okText: 'Save',
|
||||
cancelText: 'Cancel',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -1341,6 +1341,8 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
||||
showQueryMindmap: '显示查询思维导图',
|
||||
embedApp: '嵌入网站',
|
||||
relatedSearch: '相关搜索',
|
||||
okText: '保存',
|
||||
cancelText: '返回',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -242,7 +242,7 @@ export function InnerNextStepDropdown({
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="w-[300px] font-semibold bg-white border border-border rounded-md shadow-lg">
|
||||
<div className="w-[300px] font-semibold bg-bg-base border border-border rounded-md shadow-lg">
|
||||
<div className="px-3 py-2 border-b border-border">
|
||||
<div className="text-sm font-medium">Next Step</div>
|
||||
</div>
|
||||
|
||||
@ -158,8 +158,9 @@ const ToolTimelineItem = ({
|
||||
</span>
|
||||
)}
|
||||
<span className="text-text-secondary text-xs">
|
||||
{/* 0:00
|
||||
{x.data.elapsed_time?.toString().slice(0, 6)} */}
|
||||
{/* 0:00*/}
|
||||
{tool.elapsed_time?.toString().slice(0, 6) || ''}
|
||||
{tool.elapsed_time ? 's' : ''}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
|
||||
@ -153,6 +153,22 @@ export const WorkFlowTimeline = ({
|
||||
}, []);
|
||||
}, [currentEventListWithoutMessage, sendLoading]);
|
||||
|
||||
const getElapsedTime = (nodeId: string) => {
|
||||
if (nodeId === 'begin') {
|
||||
return '';
|
||||
}
|
||||
const data = currentEventListWithoutMessage?.find((x) => {
|
||||
return (
|
||||
x.data.component_id === nodeId &&
|
||||
x.event === MessageEventType.NodeFinished
|
||||
);
|
||||
});
|
||||
if (!data || data?.data.elapsed_time < 0.000001) {
|
||||
return '';
|
||||
}
|
||||
return data?.data.elapsed_time || '';
|
||||
};
|
||||
|
||||
const hasTrace = useCallback(
|
||||
(componentId: string) => {
|
||||
if (Array.isArray(traceData)) {
|
||||
@ -272,7 +288,10 @@ export const WorkFlowTimeline = ({
|
||||
nodeLabel)}
|
||||
</span>
|
||||
<span className="text-text-secondary text-xs">
|
||||
{x.data.elapsed_time?.toString().slice(0, 6)}
|
||||
{getElapsedTime(x.data.component_id)
|
||||
.toString()
|
||||
.slice(0, 6)}
|
||||
{getElapsedTime(x.data.component_id) ? 's' : ''}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { useFetchTokenListBeforeOtherStep } from '@/components/embed-dialog/use-show-embed-dialog';
|
||||
import HightLightMarkdown from '@/components/highlight-markdown';
|
||||
import { Modal } from '@/components/ui/modal/modal';
|
||||
import { RAGFlowSelect } from '@/components/ui/select';
|
||||
@ -9,7 +8,7 @@ import {
|
||||
} from '@/constants/common';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { message } from 'antd';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
type IEmbedAppModalProps = {
|
||||
open: any;
|
||||
@ -18,17 +17,13 @@ type IEmbedAppModalProps = {
|
||||
from: string;
|
||||
setOpen: (e: any) => void;
|
||||
tenantId: string;
|
||||
beta?: string;
|
||||
};
|
||||
|
||||
const EmbedAppModal = (props: IEmbedAppModalProps) => {
|
||||
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 { open, setOpen, token = '', from, url, tenantId, beta = '' } = props;
|
||||
|
||||
const [hideAvatar, setHideAvatar] = useState(false);
|
||||
const [locale, setLocale] = useState('');
|
||||
|
||||
|
||||
@ -234,7 +234,10 @@ export const useTestRetrieval = (
|
||||
setSelectedDocumentIds,
|
||||
};
|
||||
};
|
||||
export const useFetchRelatedQuestions = (tenantId?: string) => {
|
||||
export const useFetchRelatedQuestions = (
|
||||
tenantId?: string,
|
||||
searchId?: string,
|
||||
) => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const shared_id = searchParams.get('shared_id');
|
||||
const retrievalTestFunc = shared_id
|
||||
@ -251,6 +254,7 @@ export const useFetchRelatedQuestions = (tenantId?: string) => {
|
||||
const { data } = await retrievalTestFunc({
|
||||
question,
|
||||
tenant_id: tenantId,
|
||||
search_id: searchId,
|
||||
});
|
||||
|
||||
return data?.data ?? [];
|
||||
@ -260,7 +264,12 @@ export const useFetchRelatedQuestions = (tenantId?: string) => {
|
||||
return { data, loading, fetchRelatedQuestions: mutateAsync };
|
||||
};
|
||||
|
||||
export const useSendQuestion = (kbIds: string[], tenantId?: string) => {
|
||||
export const useSendQuestion = (
|
||||
kbIds: string[],
|
||||
tenantId?: string,
|
||||
searchId: string = '',
|
||||
related_search: boolean = false,
|
||||
) => {
|
||||
const { sharedId } = useGetSharedSearchParams();
|
||||
const { send, answer, done, stopOutputMessage } = useSendMessageWithSse(
|
||||
sharedId ? api.askShare : api.ask,
|
||||
@ -271,7 +280,7 @@ export const useSendQuestion = (kbIds: string[], tenantId?: string) => {
|
||||
const [sendingLoading, setSendingLoading] = useState(false);
|
||||
const [currentAnswer, setCurrentAnswer] = useState({} as IAnswer);
|
||||
const { fetchRelatedQuestions, data: relatedQuestions } =
|
||||
useFetchRelatedQuestions(tenantId);
|
||||
useFetchRelatedQuestions(tenantId, searchId);
|
||||
const [searchStr, setSearchStr] = useState<string>('');
|
||||
const [isFirstRender, setIsFirstRender] = useState(true);
|
||||
const [selectedDocumentIds, setSelectedDocumentIds] = useState<string[]>([]);
|
||||
@ -286,7 +295,7 @@ export const useSendQuestion = (kbIds: string[], tenantId?: string) => {
|
||||
setIsFirstRender(false);
|
||||
setCurrentAnswer({} as IAnswer);
|
||||
setSendingLoading(true);
|
||||
send({ kb_ids: kbIds, question: q, tenantId });
|
||||
send({ kb_ids: kbIds, question: q, tenantId, search_id: searchId });
|
||||
testChunk({
|
||||
kb_id: kbIds,
|
||||
highlight: true,
|
||||
@ -295,7 +304,9 @@ export const useSendQuestion = (kbIds: string[], tenantId?: string) => {
|
||||
size: pagination.pageSize,
|
||||
});
|
||||
|
||||
fetchRelatedQuestions(q);
|
||||
if (related_search) {
|
||||
fetchRelatedQuestions(q);
|
||||
}
|
||||
},
|
||||
[
|
||||
send,
|
||||
@ -305,6 +316,8 @@ export const useSendQuestion = (kbIds: string[], tenantId?: string) => {
|
||||
setPagination,
|
||||
pagination.pageSize,
|
||||
tenantId,
|
||||
searchId,
|
||||
related_search,
|
||||
],
|
||||
);
|
||||
|
||||
@ -408,7 +421,12 @@ export const useSearching = ({
|
||||
isSearchStrEmpty,
|
||||
setSearchStr,
|
||||
stopOutputMessage,
|
||||
} = useSendQuestion(searchData.search_config.kb_ids, tenantId as string);
|
||||
} = useSendQuestion(
|
||||
searchData.search_config.kb_ids,
|
||||
tenantId as string,
|
||||
searchData.id,
|
||||
searchData.search_config.related_search,
|
||||
);
|
||||
|
||||
const handleSearchStrChange = useCallback(
|
||||
(value: string) => {
|
||||
@ -435,15 +453,20 @@ export const useSearching = ({
|
||||
showMindMapModal,
|
||||
mindMapLoading,
|
||||
mindMap,
|
||||
} = useShowMindMapDrawer(searchData.search_config.kb_ids, searchStr);
|
||||
} = useShowMindMapDrawer(
|
||||
searchData.search_config.kb_ids,
|
||||
searchStr,
|
||||
searchData.id,
|
||||
);
|
||||
const { chunks, total } = useSelectTestingResult();
|
||||
|
||||
const handleSearch = useCallback(
|
||||
(value: string) => {
|
||||
sendQuestion(value);
|
||||
setSearchStr?.(value);
|
||||
hideMindMapModal();
|
||||
},
|
||||
[setSearchStr, sendQuestion],
|
||||
[setSearchStr, sendQuestion, hideMindMapModal],
|
||||
);
|
||||
|
||||
const { pagination, setPagination } = useGetPaginationWithRouter();
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { useFetchTokenListBeforeOtherStep } from '@/components/embed-dialog/use-show-embed-dialog';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import {
|
||||
Breadcrumb,
|
||||
@ -10,7 +11,10 @@ import {
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { SharedFrom } from '@/constants/chat';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchTenantInfo } from '@/hooks/user-setting-hooks';
|
||||
import {
|
||||
useFetchTenantInfo,
|
||||
useFetchUserInfo,
|
||||
} from '@/hooks/user-setting-hooks';
|
||||
import { Send, Settings } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -29,11 +33,13 @@ 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 { data: userInfo } = useFetchUserInfo();
|
||||
const tenantId = tenantInfo.tenant_id;
|
||||
const { t } = useTranslation();
|
||||
const { openSetting: checkOpenSetting } = useCheckSettings(
|
||||
@ -75,6 +81,7 @@ export default function SearchPage() {
|
||||
isSearching={isSearching}
|
||||
searchText={searchText}
|
||||
setSearchText={setSearchText}
|
||||
userInfo={userInfo}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -105,6 +112,7 @@ export default function SearchPage() {
|
||||
token={SearchData?.id as string}
|
||||
from={SharedFrom.Search}
|
||||
tenantId={tenantId}
|
||||
beta={beta}
|
||||
/>
|
||||
}
|
||||
{
|
||||
@ -121,7 +129,14 @@ export default function SearchPage() {
|
||||
<div className="absolute right-5 top-12 ">
|
||||
<Button
|
||||
className="bg-text-primary text-bg-base border-b-[#00BEB4] border-b-2"
|
||||
onClick={() => setOpenEmbed(!openEmbed)}
|
||||
onClick={() => {
|
||||
handleOperate().then((res) => {
|
||||
console.log(res, 'res');
|
||||
if (res) {
|
||||
setOpenEmbed(!openEmbed);
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Send />
|
||||
<div>{t('search.embedApp')}</div>
|
||||
|
||||
@ -4,7 +4,7 @@ import { IReference, IReferenceChunk } from '@/interfaces/database/chat';
|
||||
import { getExtension } from '@/utils/document-util';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo } from 'react';
|
||||
import Markdown from 'react-markdown';
|
||||
import reactStringReplace from 'react-string-replace';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
@ -82,18 +82,18 @@ const MarkdownContent = ({
|
||||
(
|
||||
documentId: string,
|
||||
chunk: IReferenceChunk,
|
||||
isPdf: boolean,
|
||||
documentUrl?: string,
|
||||
// isPdf: boolean,
|
||||
// documentUrl?: string,
|
||||
) =>
|
||||
() => {
|
||||
if (!isPdf) {
|
||||
if (!documentUrl) {
|
||||
return;
|
||||
}
|
||||
window.open(documentUrl, '_blank');
|
||||
} else {
|
||||
clickDocumentButton?.(documentId, chunk);
|
||||
}
|
||||
// if (!isPdf) {
|
||||
// if (!documentUrl) {
|
||||
// return;
|
||||
// }
|
||||
// window.open(documentUrl, '_blank');
|
||||
// } else {
|
||||
clickDocumentButton?.(documentId, chunk);
|
||||
// }
|
||||
},
|
||||
[clickDocumentButton],
|
||||
);
|
||||
@ -144,7 +144,6 @@ const MarkdownContent = ({
|
||||
const getPopoverContent = useCallback(
|
||||
(chunkIndex: number) => {
|
||||
const {
|
||||
documentUrl,
|
||||
fileThumbnail,
|
||||
fileExtension,
|
||||
imageId,
|
||||
@ -198,8 +197,8 @@ const MarkdownContent = ({
|
||||
onClick={handleDocumentButtonClick(
|
||||
documentId,
|
||||
chunkItem,
|
||||
fileExtension === 'pdf',
|
||||
documentUrl,
|
||||
// fileExtension === 'pdf',
|
||||
// documentUrl,
|
||||
)}
|
||||
>
|
||||
{document?.doc_name}
|
||||
@ -218,8 +217,7 @@ const MarkdownContent = ({
|
||||
let replacedText = reactStringReplace(text, currentReg, (match, i) => {
|
||||
const chunkIndex = getChunkIndex(match);
|
||||
|
||||
const { documentUrl, fileExtension, imageId, chunkItem, documentId } =
|
||||
getReferenceInfo(chunkIndex);
|
||||
const { imageId, chunkItem, documentId } = getReferenceInfo(chunkIndex);
|
||||
|
||||
const docType = chunkItem?.doc_type;
|
||||
|
||||
@ -232,8 +230,8 @@ const MarkdownContent = ({
|
||||
? handleDocumentButtonClick(
|
||||
documentId,
|
||||
chunkItem,
|
||||
fileExtension === 'pdf',
|
||||
documentUrl,
|
||||
// fileExtension === 'pdf',
|
||||
// documentUrl,
|
||||
)
|
||||
: () => {}
|
||||
}
|
||||
@ -243,7 +241,9 @@ const MarkdownContent = ({
|
||||
<PopoverTrigger>
|
||||
<InfoCircleOutlined className={styles.referenceIcon} />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>{getPopoverContent(chunkIndex)}</PopoverContent>
|
||||
<PopoverContent className="!w-fit">
|
||||
{getPopoverContent(chunkIndex)}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
@ -292,4 +292,4 @@ const MarkdownContent = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownContent;
|
||||
export default memo(MarkdownContent);
|
||||
|
||||
@ -27,7 +27,7 @@ const MindMapDrawer = ({ data, hideModal, visible, loading }: IProps) => {
|
||||
/>
|
||||
</div>
|
||||
{loading && (
|
||||
<div className="absolute top-48">
|
||||
<div className=" rounded-lg p-4 w-full h-full">
|
||||
<Progress value={percent} className="h-1 flex-1 min-w-10" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Input } from '@/components/originui/input';
|
||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||
import { IUserInfo } from '@/interfaces/database/user-setting';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Search } from 'lucide-react';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
@ -12,13 +12,15 @@ export default function SearchPage({
|
||||
setIsSearching,
|
||||
searchText,
|
||||
setSearchText,
|
||||
userInfo,
|
||||
}: {
|
||||
isSearching: boolean;
|
||||
setIsSearching: Dispatch<SetStateAction<boolean>>;
|
||||
searchText: string;
|
||||
setSearchText: Dispatch<SetStateAction<string>>;
|
||||
userInfo?: IUserInfo;
|
||||
}) {
|
||||
const { data: userInfo } = useFetchUserInfo();
|
||||
// const { data: userInfo } = useFetchUserInfo();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<section className="relative w-full flex transition-all justify-center items-center mt-32">
|
||||
@ -38,7 +40,11 @@ export default function SearchPage({
|
||||
<>
|
||||
<p className="mb-4 transition-opacity">👋 Hi there</p>
|
||||
<p className="mb-10 transition-opacity">
|
||||
{t('search.welcomeBack')}, {userInfo?.nickname}
|
||||
{userInfo && (
|
||||
<>
|
||||
{t('search.welcomeBack')}, {userInfo.nickname}
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -18,6 +18,7 @@ import {
|
||||
} from '@/components/ui/multi-select';
|
||||
import { RAGFlowSelect } from '@/components/ui/select';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
|
||||
import {
|
||||
useComposeLlmOptionsByModelTypes,
|
||||
@ -64,7 +65,7 @@ const SearchSettingFormSchema = z
|
||||
description: z.string().optional(),
|
||||
search_config: z.object({
|
||||
kb_ids: z.array(z.string()).min(1, 'At least one dataset is required'),
|
||||
vector_similarity_weight: z.number().min(0).max(100),
|
||||
vector_similarity_weight: z.number().min(0).max(1),
|
||||
web_search: z.boolean(),
|
||||
similarity_threshold: z.number(),
|
||||
use_kg: z.boolean(),
|
||||
@ -128,7 +129,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
: 0.3) || 0.3,
|
||||
web_search: search_config?.web_search || false,
|
||||
doc_ids: [],
|
||||
similarity_threshold: 0.0,
|
||||
similarity_threshold: search_config?.similarity_threshold || 0.2,
|
||||
use_kg: false,
|
||||
rerank_id: search_config?.rerank_id || '',
|
||||
use_rerank: search_config?.rerank_id ? true : false,
|
||||
@ -417,7 +418,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
<FormItem>
|
||||
<FormLabel>{t('search.description')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
<Textarea
|
||||
placeholder="You are an intelligent assistant."
|
||||
{...field}
|
||||
onFocus={() => {
|
||||
@ -466,7 +467,41 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={formMethods.control}
|
||||
name="search_config.similarity_threshold"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Similarity Threshold</FormLabel>
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-4 justify-between',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<FormControl>
|
||||
<SingleFormSlider
|
||||
{...field}
|
||||
max={1}
|
||||
min={0}
|
||||
step={0.01}
|
||||
></SingleFormSlider>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<Input
|
||||
type={'number'}
|
||||
className="h-7 w-20 bg-bg-card"
|
||||
max={1}
|
||||
min={0}
|
||||
step={0.01}
|
||||
{...field}
|
||||
></Input>
|
||||
</FormControl>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{/* Keyword Similarity Weight */}
|
||||
<FormField
|
||||
control={formMethods.control}
|
||||
@ -474,7 +509,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<span className="text-destructive mr-1"> *</span>Keyword
|
||||
<span className="text-destructive mr-1"> *</span>Vector
|
||||
Similarity Weight
|
||||
</FormLabel>
|
||||
<div
|
||||
@ -608,7 +643,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
)}
|
||||
|
||||
{/* Feature Controls */}
|
||||
<FormField
|
||||
{/* <FormField
|
||||
control={formMethods.control}
|
||||
name="search_config.web_search"
|
||||
render={({ field }) => (
|
||||
@ -622,7 +657,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
<FormLabel>{t('search.enableWebSearch')}</FormLabel>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<FormField
|
||||
control={formMethods.control}
|
||||
@ -666,9 +701,9 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
{t('modal.cancelText')}
|
||||
{t('search.cancelText')}
|
||||
</Button>
|
||||
<Button type="submit">{t('modal.okText')}</Button>
|
||||
<Button type="submit">{t('search.okText')}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@ -276,7 +276,7 @@ export default function SearchingView({
|
||||
</div>
|
||||
|
||||
{mindMapVisible && (
|
||||
<div className="flex-1 h-[88dvh] z-30 ml-8 mt-5">
|
||||
<div className="flex-1 h-[88dvh] z-30 ml-32 mt-5">
|
||||
<MindMapDrawer
|
||||
visible={mindMapVisible}
|
||||
hideModal={hideMindMapModal}
|
||||
|
||||
@ -1,26 +1,28 @@
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import i18n from '@/locales/config';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
ISearchAppDetailProps,
|
||||
useFetchSearchDetail,
|
||||
} from '../../next-searches/hooks';
|
||||
import { useGetSharedSearchParams, useSearching } from '../hooks';
|
||||
import '../index.less';
|
||||
import SearchingView from '../search-view';
|
||||
export default function SearchingPage() {
|
||||
import SearchHome from '../search-home';
|
||||
import SearchingPage from '../searching';
|
||||
export default function ShareSeachPage() {
|
||||
const { tenantId, locale, visibleAvatar } = useGetSharedSearchParams();
|
||||
const {
|
||||
data: searchData = {
|
||||
search_config: { kb_ids: [] },
|
||||
} as unknown as ISearchAppDetailProps,
|
||||
} = useFetchSearchDetail(tenantId as string);
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const searchingParam = useSearching({
|
||||
data: searchData,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log('locale', locale, i18n.language);
|
||||
if (locale && i18n.language !== locale) {
|
||||
i18n.changeLanguage(locale);
|
||||
}
|
||||
@ -28,15 +30,36 @@ export default function SearchingPage() {
|
||||
return (
|
||||
<>
|
||||
{visibleAvatar && (
|
||||
<div className="flex justify-start items-center gap-1 mx-6 mt-6 text-text-primary">
|
||||
<div className="flex justify-start items-center gap-2 mx-6 mt-6 text-text-primary">
|
||||
<RAGFlowAvatar
|
||||
className="size-6"
|
||||
avatar={searchData.avatar}
|
||||
name={searchData.name}
|
||||
></RAGFlowAvatar>
|
||||
<div>{searchData.name}</div>
|
||||
</div>
|
||||
)}
|
||||
<SearchingView {...searchingParam} searchData={searchData} />;
|
||||
{/* <SearchingView {...searchingParam} searchData={searchData} />; */}
|
||||
{!isSearching && (
|
||||
<div className="animate-fade-in-down">
|
||||
<SearchHome
|
||||
setIsSearching={setIsSearching}
|
||||
isSearching={isSearching}
|
||||
searchText={searchText}
|
||||
setSearchText={setSearchText}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isSearching && (
|
||||
<div className="animate-fade-in-up">
|
||||
<SearchingPage
|
||||
setIsSearching={setIsSearching}
|
||||
searchText={searchText}
|
||||
setSearchText={setSearchText}
|
||||
data={searchData as ISearchAppDetailProps}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams, useSearchParams } from 'umi';
|
||||
|
||||
interface CreateSearchProps {
|
||||
name: string;
|
||||
description?: string;
|
||||
@ -122,40 +121,6 @@ interface DeleteSearchResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export const useDeleteSearch = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
data,
|
||||
isError,
|
||||
mutateAsync: deleteSearchMutation,
|
||||
} = useMutation<DeleteSearchResponse, Error, DeleteSearchProps>({
|
||||
mutationKey: ['deleteSearch'],
|
||||
mutationFn: async (props) => {
|
||||
const response = await searchService.deleteSearch(props);
|
||||
if (response.code !== 0) {
|
||||
throw new Error(response.message || 'Failed to delete search');
|
||||
}
|
||||
return response;
|
||||
},
|
||||
onSuccess: () => {
|
||||
message.success(t('message.deleted'));
|
||||
},
|
||||
onError: (error) => {
|
||||
message.error(t('message.error', { error: error.message }));
|
||||
},
|
||||
});
|
||||
|
||||
const deleteSearch = useCallback(
|
||||
(props: DeleteSearchProps) => {
|
||||
return deleteSearchMutation(props);
|
||||
},
|
||||
[deleteSearchMutation],
|
||||
);
|
||||
|
||||
return { data, isError, deleteSearch };
|
||||
};
|
||||
|
||||
export interface IllmSettingProps {
|
||||
llm_id: string;
|
||||
parameter: string;
|
||||
@ -237,6 +202,42 @@ export const useFetchSearchDetail = (tenantId?: string) => {
|
||||
return { data: data?.data, isLoading, isError };
|
||||
};
|
||||
|
||||
export const useDeleteSearch = () => {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
const {
|
||||
data,
|
||||
isError,
|
||||
mutateAsync: deleteSearchMutation,
|
||||
} = useMutation<DeleteSearchResponse, Error, DeleteSearchProps>({
|
||||
mutationKey: ['deleteSearch'],
|
||||
mutationFn: async (props) => {
|
||||
const { data: response } = await searchService.deleteSearch(props);
|
||||
if (response.code !== 0) {
|
||||
throw new Error(response.message || 'Failed to delete search');
|
||||
}
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: ['searchList'] });
|
||||
return response;
|
||||
},
|
||||
onSuccess: () => {
|
||||
message.success(t('message.deleted'));
|
||||
},
|
||||
onError: (error) => {
|
||||
message.error(t('message.error', { error: error.message }));
|
||||
},
|
||||
});
|
||||
|
||||
const deleteSearch = useCallback(
|
||||
(props: DeleteSearchProps) => {
|
||||
return deleteSearchMutation(props);
|
||||
},
|
||||
[deleteSearchMutation],
|
||||
);
|
||||
|
||||
return { data, isError, deleteSearch };
|
||||
};
|
||||
|
||||
export type IUpdateSearchProps = Omit<ISearchAppDetailProps, 'id'> & {
|
||||
search_id: string;
|
||||
};
|
||||
|
||||
@ -217,7 +217,11 @@ export const useTestRetrieval = (
|
||||
};
|
||||
};
|
||||
|
||||
export const useShowMindMapDrawer = (kbIds: string[], question: string) => {
|
||||
export const useShowMindMapDrawer = (
|
||||
kbIds: string[],
|
||||
question: string,
|
||||
searchId = '',
|
||||
) => {
|
||||
const { visible, showModal, hideModal } = useSetModalState();
|
||||
const ref = useRef<any>();
|
||||
|
||||
@ -228,7 +232,7 @@ export const useShowMindMapDrawer = (kbIds: string[], question: string) => {
|
||||
} = useSearchFetchMindMap();
|
||||
|
||||
const handleShowModal = useCallback(() => {
|
||||
const searchParams = { question: trim(question), kb_ids: kbIds };
|
||||
const searchParams = { question: trim(question), kb_ids: kbIds, searchId };
|
||||
if (
|
||||
!isEmpty(searchParams.question) &&
|
||||
!isEqual(searchParams, ref.current)
|
||||
@ -237,7 +241,7 @@ export const useShowMindMapDrawer = (kbIds: string[], question: string) => {
|
||||
fetchMindMap(searchParams);
|
||||
}
|
||||
showModal();
|
||||
}, [fetchMindMap, showModal, question, kbIds]);
|
||||
}, [fetchMindMap, showModal, question, kbIds, searchId]);
|
||||
|
||||
return {
|
||||
mindMap,
|
||||
|
||||
Reference in New Issue
Block a user