Fix: Optimized the test results page layout and internationalization #3221 (#9974)

### What problem does this PR solve?

Fix: Optimized the test results page layout and internationalization

- Added an empty data component for when test results are empty
- Optimized internationalization support for the paging component
- Updated the layout and style of the test results page
- Added a tooltip for when test results are empty

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-09-08 12:49:12 +08:00
committed by GitHub
parent f48aed6d4a
commit cf18231713
10 changed files with 149 additions and 20 deletions

View File

@ -0,0 +1,77 @@
import { cn } from '@/lib/utils';
import { t } from 'i18next';
type EmptyProps = {
className?: string;
children?: React.ReactNode;
};
const EmptyIcon = () => (
<svg
width="184"
height="152"
viewBox="0 0 184 152"
xmlns="http://www.w3.org/2000/svg"
>
<title>{t('common.noData')}</title>
<g fill="none" fillRule="evenodd">
<g transform="translate(24 31.67)">
<ellipse
fillOpacity=".8"
fill="#F5F5F7"
cx="67.797"
cy="106.89"
rx="67.797"
ry="12.668"
></ellipse>
<path
d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z"
fill="#AEB8C2"
></path>
<path
d="M101.537 86.214L80.63 61.102c-1.001-1.207-2.507-1.867-4.048-1.867H31.724c-1.54 0-3.047.66-4.048 1.867L6.769 86.214v13.792h94.768V86.214z"
fill="url(#linearGradient-1)"
transform="translate(13.56)"
></path>
<path
d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"
fill="#F5F5F7"
></path>
<path
d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z"
fill="#DCE0E6"
></path>
</g>
<path
d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z"
fill="#DCE0E6"
></path>
<g transform="translate(149.65 15.383)" fill="#FFF">
<ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815"></ellipse>
<path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z"></path>
</g>
</g>
</svg>
);
const Empty = (props: EmptyProps) => {
const { className, children } = props;
return (
<div
className={cn(
'flex flex-col justify-center items-center text-center gap-3',
className,
)}
>
<EmptyIcon />
{!children && (
<div className="empty-text mt-4 text-text-secondary">
{t('common.noData')}
</div>
)}
{children}
</div>
);
};
export default Empty;

View File

@ -9,6 +9,7 @@ import {
} from '@/components/ui/pagination'; } from '@/components/ui/pagination';
import { RAGFlowSelect, RAGFlowSelectOptionType } from '@/components/ui/select'; import { RAGFlowSelect, RAGFlowSelectOptionType } from '@/components/ui/select';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { t } from 'i18next';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
export type RAGFlowPaginationType = { export type RAGFlowPaginationType = {
@ -32,7 +33,11 @@ export function RAGFlowPagination({
const sizeChangerOptions: RAGFlowSelectOptionType[] = useMemo(() => { const sizeChangerOptions: RAGFlowSelectOptionType[] = useMemo(() => {
return [10, 20, 50, 100].map((x) => ({ return [10, 20, 50, 100].map((x) => ({
label: <span>{x} / page</span>, label: (
<span>
{x} / {t('pagination.page')}
</span>
),
value: x.toString(), value: x.toString(),
})); }));
}, []); }, []);
@ -134,7 +139,7 @@ export function RAGFlowPagination({
return ( return (
<section className="flex items-center justify-end text-text-sub-title-invert"> <section className="flex items-center justify-end text-text-sub-title-invert">
<span className="mr-4">Total {total}</span> <span className="mr-4">{t('pagination.total', { total: total })}</span>
<Pagination className="w-auto mx-0 mr-4"> <Pagination className="w-auto mx-0 mr-4">
<PaginationContent> <PaginationContent>
<PaginationItem> <PaginationItem>

View File

@ -72,12 +72,14 @@ export const useTestRetrieval = () => {
chunks: [], chunks: [],
doc_aggs: [], doc_aggs: [],
total: 0, total: 0,
isRuned: false,
}, },
enabled: false, enabled: false,
gcTime: 0, gcTime: 0,
queryFn: async () => { queryFn: async () => {
const { data } = await kbService.retrieval_test(queryParams); const { data } = await kbService.retrieval_test(queryParams);
return data?.data ?? {}; const result = data?.data ?? {};
return { ...result, isRuned: true };
}, },
}); });

View File

@ -150,6 +150,7 @@ export interface INextTestingResult {
doc_aggs: ITestingDocument[]; doc_aggs: ITestingDocument[];
total: number; total: number;
labels?: Record<string, number>; labels?: Record<string, number>;
isRuned?: boolean;
} }
export type IRenameTag = { fromTag: string; toTag: string }; export type IRenameTag = { fromTag: string; toTag: string };

View File

@ -46,6 +46,7 @@ export default {
remove: 'Remove', remove: 'Remove',
search: 'Search', search: 'Search',
noDataFound: 'No data found.', noDataFound: 'No data found.',
noData: 'No data',
promptPlaceholder: `Please input or use / to quickly insert variables.`, promptPlaceholder: `Please input or use / to quickly insert variables.`,
mcp: { mcp: {
namePlaceholder: 'My MCP Server', namePlaceholder: 'My MCP Server',
@ -138,6 +139,10 @@ export default {
processBeginAt: 'Begin at', processBeginAt: 'Begin at',
processDuration: 'Duration', processDuration: 'Duration',
progressMsg: 'Progress', progressMsg: 'Progress',
noTestResultsForRuned:
'No relevant results found. Try adjusting your query or parameters.',
noTestResultsForNotRuned:
'No test has been run yet. Results will appear here.',
testingDescription: testingDescription:
'Conduct a retrieval test to check if RAGFlow can recover the intended content for the LLM. If you have adjusted the default settings, such as keyword similarity weight or similarity threshold, to achieve the optimal results, be aware that these changes will not be automatically saved. You must apply them to your chat assistant settings or the Retrieval agent component settings.', 'Conduct a retrieval test to check if RAGFlow can recover the intended content for the LLM. If you have adjusted the default settings, such as keyword similarity weight or similarity threshold, to achieve the optimal results, be aware that these changes will not be automatically saved. You must apply them to your chat assistant settings or the Retrieval agent component settings.',
similarityThreshold: 'Similarity threshold', similarityThreshold: 'Similarity threshold',
@ -1579,5 +1584,9 @@ This delimiter is used to split the input text into several text pieces echo of
korean: 'Korean', korean: 'Korean',
vietnamese: 'Vietnamese', vietnamese: 'Vietnamese',
}, },
pagination: {
total: 'Total {{total}}',
page: 'Page',
},
}, },
}; };

View File

@ -45,6 +45,7 @@ export default {
remove: '移除', remove: '移除',
search: '搜索', search: '搜索',
noDataFound: '没有找到数据。', noDataFound: '没有找到数据。',
noData: '暂无数据',
promptPlaceholder: '请输入或使用 / 快速插入变量。', promptPlaceholder: '请输入或使用 / 快速插入变量。',
}, },
login: { login: {
@ -129,6 +130,8 @@ export default {
processBeginAt: '开始于', processBeginAt: '开始于',
processDuration: '持续时间', processDuration: '持续时间',
progressMsg: '进度', progressMsg: '进度',
noTestResultsForRuned: '未找到相关结果,请尝试调整查询语句或参数',
noTestResultsForNotRuned: '尚未运行测试,结果会显示在这里',
testingDescription: testingDescription:
'请完成召回测试:确保你的配置可以从数据库召回正确的文本块。如果你调整了这里的默认设置,比如关键词相似度权重,请注意这里的改动不会被自动保存。请务必在聊天助手设置或者召回算子设置处同步更新相关设置。', '请完成召回测试:确保你的配置可以从数据库召回正确的文本块。如果你调整了这里的默认设置,比如关键词相似度权重,请注意这里的改动不会被自动保存。请务必在聊天助手设置或者召回算子设置处同步更新相关设置。',
similarityThreshold: '相似度阈值', similarityThreshold: '相似度阈值',
@ -1493,5 +1496,9 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
korean: '韩语', korean: '韩语',
vietnamese: '越南语', vietnamese: '越南语',
}, },
pagination: {
total: '总共 {{total}} 条',
page: '页',
},
}, },
}; };

View File

@ -67,7 +67,7 @@ export function ParsingStatusCell({
return ( return (
<section className="flex gap-8 items-center"> <section className="flex gap-8 items-center">
<div className="w-fit flex items-center justify-between"> <div className="w-[100px] text-ellipsis overflow-hidden flex items-center justify-between">
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant={'transparent'} className="border-none" size={'sm'}> <Button variant={'transparent'} className="border-none" size={'sm'}>

View File

@ -25,7 +25,7 @@ export default function RetrievalTesting() {
<section className="flex justify-between items-center"> <section className="flex justify-between items-center">
<TopTitle <TopTitle
title={t('knowledgeDetails.retrievalTesting')} title={t('knowledgeDetails.retrievalTesting')}
description={t('knowledgeDetails.retrievalTestingDescription')} description={t('knowledgeDetails.testingDescription')}
></TopTitle> ></TopTitle>
{/* <Button>Save as Preset</Button> */} {/* <Button>Save as Preset</Button> */}
</section> </section>
@ -51,6 +51,7 @@ export default function RetrievalTesting() {
<TestingResult <TestingResult
data={data} data={data}
page={page} page={page}
loading={loading}
pageSize={pageSize} pageSize={pageSize}
filterValue={filterValue} filterValue={filterValue}
handleFilterSubmit={handleFilterSubmit} handleFilterSubmit={handleFilterSubmit}
@ -68,6 +69,7 @@ export default function RetrievalTesting() {
<TestingResult <TestingResult
data={data} data={data}
page={page} page={page}
loading={loading}
pageSize={pageSize} pageSize={pageSize}
filterValue={filterValue} filterValue={filterValue}
handleFilterSubmit={handleFilterSubmit} handleFilterSubmit={handleFilterSubmit}
@ -83,6 +85,7 @@ export default function RetrievalTesting() {
<TestingResult <TestingResult
data={data} data={data}
page={page} page={page}
loading={loading}
pageSize={pageSize} pageSize={pageSize}
filterValue={filterValue} filterValue={filterValue}
handleFilterSubmit={handleFilterSubmit} handleFilterSubmit={handleFilterSubmit}

View File

@ -1,3 +1,4 @@
import Empty from '@/components/empty/empty';
import { FormContainer } from '@/components/form-container'; import { FormContainer } from '@/components/form-container';
import { FilterButton } from '@/components/list-filter-bar'; import { FilterButton } from '@/components/list-filter-bar';
import { FilterPopover } from '@/components/list-filter-bar/filter-popover'; import { FilterPopover } from '@/components/list-filter-bar/filter-popover';
@ -38,6 +39,7 @@ type TestingResultProps = Pick<
| 'page' | 'page'
| 'pageSize' | 'pageSize'
| 'onPaginationChange' | 'onPaginationChange'
| 'loading'
>; >;
export function TestingResult({ export function TestingResult({
@ -45,6 +47,7 @@ export function TestingResult({
handleFilterSubmit, handleFilterSubmit,
page, page,
pageSize, pageSize,
loading,
onPaginationChange, onPaginationChange,
data, data,
}: TestingResultProps) { }: TestingResultProps) {
@ -77,6 +80,8 @@ export function TestingResult({
<FilterButton></FilterButton> <FilterButton></FilterButton>
</FilterPopover> </FilterPopover>
</div> </div>
{data.chunks?.length > 0 && !loading && (
<>
<section className="flex flex-col gap-5 overflow-auto h-[calc(100vh-241px)] scrollbar-thin mb-5"> <section className="flex flex-col gap-5 overflow-auto h-[calc(100vh-241px)] scrollbar-thin mb-5">
{data.chunks?.map((x) => ( {data.chunks?.map((x) => (
<FormContainer key={x.chunk_id} className="px-5 py-2.5"> <FormContainer key={x.chunk_id} className="px-5 py-2.5">
@ -91,6 +96,26 @@ export function TestingResult({
current={page} current={page}
pageSize={pageSize} pageSize={pageSize}
></RAGFlowPagination> ></RAGFlowPagination>
</>
)}
{!data.chunks?.length && !loading && (
<div className="flex justify-center items-center w-full h-[calc(100vh-241px)]">
<div>
<Empty>
{data.isRuned && (
<div className="text-text-secondary">
{t('knowledgeDetails.noTestResultsForRuned')}
</div>
)}
{!data.isRuned && (
<div className="text-text-secondary">
{t('knowledgeDetails.noTestResultsForNotRuned')}
</div>
)}
</Empty>
</div>
</div>
)}
</div> </div>
); );
} }

View File

@ -262,7 +262,7 @@ export default function SearchingView({
</div> </div>
{total > 0 && ( {total > 0 && (
<div className="mt-8 px-8 pb-8"> <div className="mt-8 px-8 pb-8 text-base">
<RAGFlowPagination <RAGFlowPagination
current={pagination.current} current={pagination.current}
pageSize={pagination.pageSize} pageSize={pagination.pageSize}