Feat: Adjust the page header to breadcrumbs #3221 (#8971)

### What problem does this PR solve?

Feat: Adjust the page header to breadcrumbs #3221
### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-07-22 18:27:01 +08:00
committed by GitHub
parent 131fc10af5
commit e992bc5307
11 changed files with 161 additions and 138 deletions

View File

@ -1,25 +1,8 @@
import { ArrowLeft } from 'lucide-react'; import { PropsWithChildren } from 'react';
import { PropsWithChildren, ReactNode } from 'react';
import { Button } from './ui/button';
interface IPageHeaderProps extends PropsWithChildren { export function PageHeader({ children }: PropsWithChildren) {
back(): void;
title: ReactNode;
}
export function PageHeader({ back, title, children }: IPageHeaderProps) {
return ( return (
<header className="flex justify-between items-center border-b pr-9"> <header className="flex justify-between items-center border-b bg-background-header-bar p-5">
<div className="flex items-center ">
<div className="flex items-center border-r p-1.5">
<Button variant="ghost" size="icon" onClick={back}>
<ArrowLeft className="w-5 h-5" />
</Button>
</div>
<div className="p-4">
<h1 className="text-2xl font-semibold tracking-tight">{title}</h1>
</div>
</div>
{children} {children}
</header> </header>
); );

View File

@ -33,7 +33,10 @@ const BreadcrumbItem = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<li <li
ref={ref} ref={ref}
className={cn('inline-flex items-center gap-1.5', className)} className={cn(
'inline-flex items-center gap-1.5 text-text-sub-title',
className,
)}
{...props} {...props}
/> />
)); ));

View File

@ -1302,6 +1302,10 @@ This delimiter is used to split the input text into several text pieces echo of
tavilySearchDescription: 'Search results via Tavily service.', tavilySearchDescription: 'Search results via Tavily service.',
tavilyExtract: 'Tavily Extract', tavilyExtract: 'Tavily Extract',
tavilyExtractDescription: 'Tavily Extract', tavilyExtractDescription: 'Tavily Extract',
log: 'Log',
management: 'Management',
import: 'Import',
export: 'Export',
}, },
llmTools: { llmTools: {
bad_calculator: { bad_calculator: {

View File

@ -1255,6 +1255,10 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
tavilySearchDescription: '通过 Tavily 服务搜索结果', tavilySearchDescription: '通过 Tavily 服务搜索结果',
tavilyExtract: 'Tavily Extract', tavilyExtract: 'Tavily Extract',
tavilyExtractDescription: 'Tavily Extract', tavilyExtractDescription: 'Tavily Extract',
log: '日志',
management: '管理',
import: '导入',
export: '导出',
}, },
footer: { footer: {
profile: 'All rights reserved @ React', profile: 'All rights reserved @ React',

View File

@ -1,4 +1,12 @@
import { PageHeader } from '@/components/page-header'; import { PageHeader } from '@/components/page-header';
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import { Button, ButtonLoading } from '@/components/ui/button'; import { Button, ButtonLoading } from '@/components/ui/button';
import { import {
DropdownMenu, DropdownMenu,
@ -89,32 +97,45 @@ export default function Agent() {
return ( return (
<section className="h-full"> <section className="h-full">
<PageHeader back={navigateToAgentList} title={flowDetail.title}> <PageHeader>
<div className="flex items-center gap-2"> <Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink onClick={navigateToAgentList}>
Agent
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>{flowDetail.title}</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
<div className="flex items-center gap-5">
<ButtonLoading <ButtonLoading
variant={'secondary'} variant={'secondary'}
onClick={() => saveGraph()} onClick={() => saveGraph()}
loading={loading} loading={loading}
> >
<LaptopMinimalCheck /> Save <LaptopMinimalCheck /> {t('flow.save')}
</ButtonLoading> </ButtonLoading>
<Button variant={'secondary'} onClick={handleRunAgent}> <Button variant={'secondary'} onClick={handleRunAgent}>
<CirclePlay /> <CirclePlay />
Run app {t('flow.run')}
</Button> </Button>
<Button variant={'secondary'} onClick={showVersionDialog}> <Button variant={'secondary'} onClick={showVersionDialog}>
<History /> <History />
History version {t('flow.historyversion')}
</Button> </Button>
<Button variant={'secondary'}> <Button variant={'secondary'}>
<Logs /> <Logs />
Log {t('flow.log')}
</Button> </Button>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant={'secondary'}> <Button variant={'secondary'}>
<ChevronDown /> Management <ChevronDown /> {t('flow.management')}
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
@ -125,12 +146,12 @@ export default function Agent() {
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<AgentDropdownMenuItem onClick={handleImportJson}> <AgentDropdownMenuItem onClick={handleImportJson}>
<Download /> <Download />
Import {t('flow.import')}
</AgentDropdownMenuItem> </AgentDropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<AgentDropdownMenuItem onClick={handleExportJson}> <AgentDropdownMenuItem onClick={handleExportJson}>
<Upload /> <Upload />
Export {t('flow.export')}
</AgentDropdownMenuItem> </AgentDropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<AgentDropdownMenuItem onClick={showEmbedModal}> <AgentDropdownMenuItem onClick={showEmbedModal}>

View File

@ -1,68 +0,0 @@
import { PageHeader } from '@/components/page-header';
import { Button } from '@/components/ui/button';
import { Segmented, SegmentedValue } from '@/components/ui/segmented';
import {
QueryStringMap,
useNavigatePage,
} from '@/hooks/logic-hooks/navigate-hooks';
import { Routes } from '@/routes';
import { EllipsisVertical, Save } from 'lucide-react';
import { useMemo } from 'react';
import { Outlet, useLocation } from 'umi';
export default function ChunkPage() {
const { navigateToDataset, getQueryString, navigateToChunk } =
useNavigatePage();
const location = useLocation();
const options = useMemo(() => {
return [
{
label: 'Parsed results',
value: Routes.ParsedResult,
},
{
label: 'Chunk result',
value: Routes.ChunkResult,
},
{
label: 'Result view',
value: Routes.ResultView,
},
];
}, []);
const path = useMemo(() => {
return location.pathname.split('/').slice(0, 3).join('/');
}, [location.pathname]);
return (
<section>
<PageHeader
title="Editing block"
back={navigateToDataset(
getQueryString(QueryStringMap.KnowledgeId) as string,
)}
>
<div>
<Segmented
options={options}
value={path}
onChange={navigateToChunk as (val: SegmentedValue) => void}
className="bg-colors-background-inverse-standard text-colors-text-neutral-standard"
></Segmented>
</div>
<div className="flex items-center gap-2">
<Button variant={'icon'} size={'icon'}>
<EllipsisVertical />
</Button>
<Button variant={'tertiary'} size={'sm'}>
<Save />
Save
</Button>
</div>
</PageHeader>
<Outlet />
</section>
);
}

View File

@ -1,4 +1,12 @@
import { PageHeader } from '@/components/page-header'; import { PageHeader } from '@/components/page-header';
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Segmented, SegmentedValue } from '@/components/ui/segmented'; import { Segmented, SegmentedValue } from '@/components/ui/segmented';
import { import {
@ -38,12 +46,24 @@ export default function ChunkPage() {
return ( return (
<section> <section>
<PageHeader <PageHeader>
title="Editing block" <Breadcrumb>
back={navigateToDataset( <BreadcrumbList>
getQueryString(QueryStringMap.KnowledgeId) as string, <BreadcrumbItem>
)} <BreadcrumbLink
> onClick={navigateToDataset(
getQueryString(QueryStringMap.KnowledgeId) as string,
)}
>
Agent
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>xxx</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
<div> <div>
<Segmented <Segmented
options={options} options={options}

View File

@ -21,6 +21,14 @@ import CheckboxSets from './components/chunk-result-bar/checkbox-sets';
import DocumentHeader from './components/document-preview/document-header'; import DocumentHeader from './components/document-preview/document-header';
import { PageHeader } from '@/components/page-header'; import { PageHeader } from '@/components/page-header';
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import message from '@/components/ui/message'; import message from '@/components/ui/message';
import { import {
RAGFlowPagination, RAGFlowPagination,
@ -31,6 +39,7 @@ import {
QueryStringMap, QueryStringMap,
useNavigatePage, useNavigatePage,
} from '@/hooks/logic-hooks/navigate-hooks'; } from '@/hooks/logic-hooks/navigate-hooks';
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
import styles from './index.less'; import styles from './index.less';
const Chunk = () => { const Chunk = () => {
@ -47,6 +56,7 @@ const Chunk = () => {
} = useFetchNextChunkList(); } = useFetchNextChunkList();
const { handleChunkCardClick, selectedChunkId } = useHandleChunkCardClick(); const { handleChunkCardClick, selectedChunkId } = useHandleChunkCardClick();
const isPdf = documentInfo?.type === 'pdf'; const isPdf = documentInfo?.type === 'pdf';
const { data: dataset } = useFetchKnowledgeBaseConfiguration();
const { t } = useTranslation(); const { t } = useTranslation();
const { changeChunkTextMode, textMode } = useChangeChunkTextMode(); const { changeChunkTextMode, textMode } = useChangeChunkTextMode();
@ -61,7 +71,8 @@ const Chunk = () => {
chunkUpdatingVisible, chunkUpdatingVisible,
documentId, documentId,
} = useUpdateChunk(); } = useUpdateChunk();
const { navigateToDataset, getQueryString } = useNavigatePage(); const { navigateToDataset, getQueryString, navigateToDatasetList } =
useNavigatePage();
useEffect(() => { useEffect(() => {
setChunkList(data); setChunkList(data);
}, [data]); }, [data]);
@ -164,10 +175,31 @@ const Chunk = () => {
return ( return (
<> <>
<PageHeader <PageHeader>
title="Back" <Breadcrumb>
back={navigateToDataset(getQueryString(QueryStringMap.id) as string)} <BreadcrumbList>
></PageHeader> <BreadcrumbItem>
<BreadcrumbLink onClick={navigateToDatasetList}>
{t('knowledgeDetails.dataset')}
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink
onClick={navigateToDataset(
getQueryString(QueryStringMap.id) as string,
)}
>
{dataset.name}
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>{documentInfo.name}</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</PageHeader>
<div className={styles.chunkPage}> <div className={styles.chunkPage}>
<div className="flex flex-1 gap-8"> <div className="flex flex-1 gap-8">
<div className="w-2/5"> <div className="w-2/5">

View File

@ -1,10 +0,0 @@
import ParsedResultPanel from '../parsed-result-panel';
export default function ParsedResult() {
return (
<section className="flex">
<div className="flex-1"></div>
<ParsedResultPanel></ParsedResultPanel>
</section>
);
}

View File

@ -1,17 +1,40 @@
import { PageHeader } from '@/components/page-header'; import { PageHeader } from '@/components/page-header';
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
import { useTranslation } from 'react-i18next';
import { Outlet } from 'umi'; import { Outlet } from 'umi';
import { SideBar } from './sidebar'; import { SideBar } from './sidebar';
export default function DatasetWrapper() { export default function DatasetWrapper() {
const { navigateToDatasetList } = useNavigatePage(); const { navigateToDatasetList } = useNavigatePage();
const { t } = useTranslation();
const { data } = useFetchKnowledgeBaseConfiguration();
return ( return (
<section> <section>
<PageHeader <PageHeader>
title="Dataset details" <Breadcrumb>
back={navigateToDatasetList} <BreadcrumbList>
></PageHeader> <BreadcrumbItem>
<BreadcrumbLink onClick={navigateToDatasetList}>
{t('knowledgeDetails.dataset')}
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>{data.name}</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</PageHeader>
<div className="flex flex-1"> <div className="flex flex-1">
<SideBar></SideBar> <SideBar></SideBar>
<div className="flex-1"> <div className="flex-1">

View File

@ -1,34 +1,45 @@
import { PageHeader } from '@/components/page-header'; import { PageHeader } from '@/components/page-header';
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { House } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Outlet } from 'umi'; import { Outlet } from 'umi';
import { SideBar } from './sidebar'; import { SideBar } from './sidebar';
export default function ProfileSetting() { export default function ProfileSetting() {
const { navigateToHome } = useNavigatePage(); const { navigateToHome } = useNavigatePage();
const { t } = useTranslation();
return ( return (
<div className="flex flex-col w-full h-screen bg-background text-foreground"> <div className="flex flex-col w-full h-screen bg-background text-foreground">
{/* <header className="flex items-center border-b"> <PageHeader>
<div className="flex items-center border-r p-1.5"> <Breadcrumb>
<Button variant="ghost" size="icon" onClick={navigateToHome}> <BreadcrumbList>
<ArrowLeft className="w-5 h-5" /> <BreadcrumbItem>
</Button> <BreadcrumbLink onClick={navigateToHome}>
</div> <House className="size-4" />
<div className="p-4"> </BreadcrumbLink>
<h1 className="text-2xl font-semibold tracking-tight"> </BreadcrumbItem>
Profile & settings <BreadcrumbSeparator />
</h1> <BreadcrumbItem>
</div> <BreadcrumbPage>{t('setting.profile')}</BreadcrumbPage>
</header> */} </BreadcrumbItem>
</BreadcrumbList>
<PageHeader title="Profile & settings" back={navigateToHome}></PageHeader> </Breadcrumb>
</PageHeader>
<div className="flex flex-1 bg-muted/50"> <div className="flex flex-1 bg-muted/50">
<SideBar></SideBar> <SideBar></SideBar>
<main className="flex-1 "> <main className="flex-1 ">
<Outlet></Outlet> <Outlet></Outlet>
{/* <h1 className="text-3xl font-bold mb-6"> {title}</h1> */}
</main> </main>
</div> </div>
</div> </div>