Feat: Add a language switch drop-down box to the top navigation bar #3221 (#7416)

### What problem does this PR solve?

Feat: Add a language switch drop-down box to the top navigation bar
#3221
### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2025-04-29 18:20:46 +08:00 committed by GitHub
parent 942b94fc3c
commit de166d0ff2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 87 additions and 33 deletions

View File

@ -1,39 +1,87 @@
import { useTheme } from '@/components/theme-provider';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Container } from '@/components/ui/container'; import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Segmented, SegmentedValue } from '@/components/ui/segmented'; import { Segmented, SegmentedValue } from '@/components/ui/segmented';
import { useTranslate } from '@/hooks/common-hooks'; import { LanguageList, LanguageMap } from '@/constants/common';
import { useChangeLanguage } from '@/hooks/logic-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useNavigateWithFromState } from '@/hooks/route-hook'; import { useNavigateWithFromState } from '@/hooks/route-hook';
import { useFetchUserInfo, useListTenant } from '@/hooks/user-setting-hooks';
import { TenantRole } from '@/pages/user-setting/constants';
import { Routes } from '@/routes'; import { Routes } from '@/routes';
import { camelCase } from 'lodash';
import { import {
ChevronDown, ChevronDown,
CircleHelp,
Cpu, Cpu,
File, File,
Github, Github,
House, House,
Library, Library,
MessageSquareText, MessageSquareText,
Moon,
Search, Search,
Zap, Sun,
} from 'lucide-react'; } from 'lucide-react';
import { useCallback, useMemo } from 'react'; import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'umi'; import { useLocation } from 'umi';
const handleDocHelpCLick = () => {
window.open('https://ragflow.io/docs/dev/category/guides', 'target');
};
export function Header() { export function Header() {
const { t } = useTranslate('header'); const { t } = useTranslation();
const { pathname } = useLocation(); const { pathname } = useLocation();
const navigate = useNavigateWithFromState(); const navigate = useNavigateWithFromState();
const { navigateToProfile } = useNavigatePage(); const { navigateToProfile } = useNavigatePage();
const changeLanguage = useChangeLanguage();
const { setTheme, theme } = useTheme();
const {
data: { language = 'English' },
} = useFetchUserInfo();
const handleItemClick = (key: string) => () => {
changeLanguage(key);
};
const { data } = useListTenant();
const showBell = useMemo(() => {
return data.some((x) => x.role === TenantRole.Invite);
}, [data]);
const items = LanguageList.map((x) => ({
key: x,
label: <span>{LanguageMap[x as keyof typeof LanguageMap]}</span>,
}));
const onThemeClick = React.useCallback(() => {
setTheme(theme === 'dark' ? 'light' : 'dark');
}, [setTheme, theme]);
const handleBellClick = useCallback(() => {
navigate('/user-setting/team');
}, [navigate]);
const tagsData = useMemo( const tagsData = useMemo(
() => [ () => [
{ path: Routes.Home, name: t('home'), icon: House }, { path: Routes.Home, name: t('header.home'), icon: House },
{ path: Routes.Datasets, name: t('knowledgeBase'), icon: Library }, { path: Routes.Datasets, name: t('header.knowledgeBase'), icon: Library },
{ path: Routes.Chats, name: t('chat'), icon: MessageSquareText }, { path: Routes.Chats, name: t('header.chat'), icon: MessageSquareText },
{ path: Routes.Searches, name: t('search'), icon: Search }, { path: Routes.Searches, name: t('header.search'), icon: Search },
{ path: Routes.Agents, name: t('flow'), icon: Cpu }, { path: Routes.Agents, name: t('header.flow'), icon: Cpu },
{ path: Routes.Files, name: t('fileManager'), icon: File }, { path: Routes.Files, name: t('header.fileManager'), icon: File },
], ],
[t], [t],
); );
@ -87,30 +135,37 @@ export function Header() {
value={currentPath} value={currentPath}
onChange={handleChange} onChange={handleChange}
></Segmented> ></Segmented>
<div className="flex items-center gap-4"> <div className="flex items-center gap-5 text-text-badge">
<Container className="bg-colors-background-inverse-standard hidden xl:flex"> <DropdownMenu>
V 0.13.0 <DropdownMenuTrigger>
<Button variant="secondary" className="size-8"> <div className="flex items-center gap-1">
<ChevronDown /> {t(`common.${camelCase(language)}`)}
<ChevronDown className="size-4" />
</div>
</DropdownMenuTrigger>
<DropdownMenuContent>
{items.map((x) => (
<DropdownMenuItem key={x.key} onClick={handleItemClick(x.key)}>
{x.label}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<Button variant={'ghost'} onClick={handleDocHelpCLick}>
<CircleHelp />
</Button> </Button>
</Container> <Button variant={'ghost'} onClick={onThemeClick}>
<Container className="px-3 py-2 bg-colors-background-inverse-standard"> {theme === 'light' ? <Sun /> : <Moon />}
<Avatar </Button>
className="w-[30px] h-[30px] cursor-pointer" <div className="relative">
onClick={navigateToProfile} <Avatar className="size-8 cursor-pointer" onClick={navigateToProfile}>
>
<AvatarImage src="https://github.com/shadcn.png" /> <AvatarImage src="https://github.com/shadcn.png" />
<AvatarFallback>CN</AvatarFallback> <AvatarFallback>CN</AvatarFallback>
</Avatar> </Avatar>
<span className="max-w-14 truncate"> yifanwu92@gmail.com</span> <Badge className="h-5 w-8 absolute font-normal p-0 justify-center -right-8 -top-2 text-text-title-invert bg-gradient-to-l from-[#42D7E7] to-[#478AF5]">
<Button
variant="destructive"
className="py-[2px] px-[8px] h-[23px] rounded-[4px]"
>
<Zap />
Pro Pro
</Button> </Badge>
</Container> </div>
</div> </div>
</section> </section>
); );

View File

@ -164,8 +164,7 @@ const KnowledgeFile = () => {
text: t(`runningStatus${value}`), text: t(`runningStatus${value}`),
value: value, value: value,
})), })),
onFilter: (value: string | number | boolean, record: IDocumentInfo) => onFilter: (value, record: IDocumentInfo) => record.run === value,
record.run === value,
render: (text, record) => { render: (text, record) => {
return <ParsingStatusCell record={record}></ParsingStatusCell>; return <ParsingStatusCell record={record}></ParsingStatusCell>;
}, },