feat: 添加i18n,美化界面
This commit is contained in:
@@ -1,42 +1,18 @@
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useLayoutStore } from './store';
|
||||
import clsx from 'clsx';
|
||||
import { Button, Dropdown } from 'antd';
|
||||
import { Menu, MenuItem, Tooltip } from '@mui/material';
|
||||
import { Button } from '@mui/material';
|
||||
import { IconButton } from '@kevisual/center-components/button/index.tsx';
|
||||
import { message } from '@/modules/message';
|
||||
import {
|
||||
CloseOutlined,
|
||||
CodeOutlined,
|
||||
DashboardOutlined,
|
||||
HomeOutlined,
|
||||
LogoutOutlined,
|
||||
MessageOutlined,
|
||||
ReadOutlined,
|
||||
RocketOutlined,
|
||||
SmileOutlined,
|
||||
SwapOutlined,
|
||||
SwitcherOutlined,
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { DashboardOutlined, HomeOutlined, LogoutOutlined, SmileOutlined } from '@ant-design/icons';
|
||||
import { useMemo } from 'react';
|
||||
import { query } from '../query';
|
||||
import { useNewNavigate } from '../navicate';
|
||||
const meun = [
|
||||
{
|
||||
title: 'Your profile',
|
||||
icon: <HomeOutlined />,
|
||||
link: '/user/profile',
|
||||
},
|
||||
{
|
||||
title: 'Your orgs',
|
||||
icon: <DashboardOutlined />,
|
||||
link: '/org/edit/list',
|
||||
},
|
||||
{
|
||||
title: 'Site Map',
|
||||
icon: <HomeOutlined />,
|
||||
link: '/map',
|
||||
},
|
||||
];
|
||||
import { Users, X } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import React from 'react';
|
||||
|
||||
export const LayoutUser = () => {
|
||||
const { open, setOpen, ...store } = useLayoutStore(
|
||||
useShallow((state) => ({
|
||||
@@ -47,6 +23,24 @@ export const LayoutUser = () => {
|
||||
})),
|
||||
);
|
||||
const navigate = useNewNavigate();
|
||||
const { t } = useTranslation();
|
||||
const meun = [
|
||||
{
|
||||
title: t('Your profile'),
|
||||
icon: <HomeOutlined />,
|
||||
link: '/user/profile',
|
||||
},
|
||||
{
|
||||
title: t('Your orgs'),
|
||||
icon: <DashboardOutlined />,
|
||||
link: '/org/edit/list',
|
||||
},
|
||||
{
|
||||
title: t('Site Map'),
|
||||
icon: <HomeOutlined />,
|
||||
link: '/map',
|
||||
},
|
||||
];
|
||||
const items = useMemo(() => {
|
||||
const orgs = store.me?.orgs || [];
|
||||
return orgs.map((item) => {
|
||||
@@ -57,30 +51,41 @@ export const LayoutUser = () => {
|
||||
};
|
||||
});
|
||||
}, [store.me]);
|
||||
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx('w-full h-full absolute z-20 no-drag', !open && 'hidden')}>
|
||||
<div className={clsx('w-full h-full absolute z-20 no-drag ', !open && 'hidden')}>
|
||||
<div
|
||||
className='bg-white w-full absolute h-full opacity-60 z-0'
|
||||
className='w-full absolute h-full opacity-60 z-0'
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
}}></div>
|
||||
<div className='w-[400px] h-full absolute top-0 right-0 bg-white rounded-l-lg'>
|
||||
<div className='w-[400px] bg-amber-900 text-primary transition-all duration-300 h-full absolute top-0 right-0 rounded-l-lg'>
|
||||
<div className='flex justify-between p-6 mt-4 font-bold items-center border-b'>
|
||||
User: {store.me?.username}
|
||||
<div className='flex items-center gap-2'>
|
||||
{t('User')}: <span className='text-primary'>{store.me?.username}</span>
|
||||
</div>
|
||||
<div className='flex gap-4'>
|
||||
{items.length > 0 && (
|
||||
<Dropdown
|
||||
placement='bottomRight'
|
||||
menu={{
|
||||
items: items,
|
||||
onClick: (item) => {
|
||||
store.switchOrg(item.key, 'org');
|
||||
},
|
||||
}}>
|
||||
<Button icon={<SwapOutlined />} onClick={() => {}}></Button>
|
||||
</Dropdown>
|
||||
<Tooltip title={t('Switch Org')}>
|
||||
<Button aria-controls='switch-org-menu' aria-haspopup='true' onClick={handleClick}>
|
||||
<Users />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Button icon={<CloseOutlined />} onClick={() => setOpen(false)}></Button>
|
||||
|
||||
<Button onClick={() => setOpen(false)}>
|
||||
<X />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-3 font-medium'>
|
||||
@@ -88,7 +93,7 @@ export const LayoutUser = () => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className='flex items-center p-4 hover:bg-gray-100 cursor-pointer'
|
||||
className='flex items-center p-4 hover:bg-secondary hover:text-white cursor-pointer'
|
||||
onClick={() => {
|
||||
if (item.link) {
|
||||
navigate(item.link);
|
||||
@@ -104,7 +109,7 @@ export const LayoutUser = () => {
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className='flex items-center p-4 hover:bg-gray-100 cursor-pointer'
|
||||
className='flex items-center p-4 hover:bg-secondary hover:text-white cursor-pointer'
|
||||
onClick={() => {
|
||||
query.removeToken();
|
||||
window.open('/user/login', '_self');
|
||||
@@ -112,9 +117,24 @@ export const LayoutUser = () => {
|
||||
<div className='mr-4'>
|
||||
<LogoutOutlined />
|
||||
</div>
|
||||
<div>Login Out</div>
|
||||
<div>{t('Login Out')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Menu id='simple-menu' anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
|
||||
{items.map((item, index) => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={index}
|
||||
onClick={() => {
|
||||
store.switchOrg(item.key, 'org');
|
||||
handleClose();
|
||||
}}>
|
||||
<div className='mr-4'>{item.icon}</div>
|
||||
<div>{item.label}</div>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,53 +3,44 @@ import { useLayoutStore } from './store';
|
||||
import clsx from 'clsx';
|
||||
import { Button } from '@mui/material';
|
||||
import { message } from '@/modules/message';
|
||||
import {
|
||||
AppstoreOutlined,
|
||||
CloseOutlined,
|
||||
CodeOutlined,
|
||||
DashboardOutlined,
|
||||
FolderOutlined,
|
||||
HomeOutlined,
|
||||
MessageOutlined,
|
||||
ReadOutlined,
|
||||
RocketOutlined,
|
||||
SmileOutlined,
|
||||
SwitcherOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { AppstoreOutlined, CodeOutlined, FolderOutlined, HomeOutlined, SmileOutlined, SwitcherOutlined } from '@ant-design/icons';
|
||||
import { X } from 'lucide-react';
|
||||
import { useNewNavigate } from '../navicate';
|
||||
const meun = [
|
||||
{
|
||||
title: 'Home',
|
||||
icon: <HomeOutlined />,
|
||||
link: '/map',
|
||||
},
|
||||
{
|
||||
title: 'User App',
|
||||
icon: <AppstoreOutlined />,
|
||||
link: '/app/edit/list',
|
||||
},
|
||||
{
|
||||
title: 'File App',
|
||||
icon: <FolderOutlined />,
|
||||
link: '/file/edit/list',
|
||||
},
|
||||
{
|
||||
title: 'Container',
|
||||
icon: <CodeOutlined />,
|
||||
link: '/container/edit/list',
|
||||
},
|
||||
{
|
||||
title: 'Org',
|
||||
icon: <SwitcherOutlined />,
|
||||
link: '/org/edit/list',
|
||||
},
|
||||
{
|
||||
title: 'About',
|
||||
icon: <SmileOutlined />,
|
||||
},
|
||||
];
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const LayoutMenu = () => {
|
||||
const { t } = useTranslation();
|
||||
const meun = [
|
||||
{
|
||||
title: t('Home'),
|
||||
icon: <HomeOutlined />,
|
||||
link: '/map',
|
||||
},
|
||||
{
|
||||
title: t('User App'),
|
||||
icon: <AppstoreOutlined />,
|
||||
link: '/app/edit/list',
|
||||
},
|
||||
{
|
||||
title: t('File App'),
|
||||
icon: <FolderOutlined />,
|
||||
link: '/file/edit/list',
|
||||
},
|
||||
{
|
||||
title: t('Container'),
|
||||
icon: <CodeOutlined />,
|
||||
link: '/container/edit/list',
|
||||
},
|
||||
{
|
||||
title: t('Org'),
|
||||
icon: <SwitcherOutlined />,
|
||||
link: '/org/edit/list',
|
||||
},
|
||||
{
|
||||
title: t('About'),
|
||||
icon: <SmileOutlined />,
|
||||
},
|
||||
];
|
||||
const { open, setOpen } = useLayoutStore(useShallow((state) => ({ open: state.open, setOpen: state.setOpen })));
|
||||
const navigate = useNewNavigate();
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MenuOutlined, SwapOutlined } from '@ant-design/icons';
|
||||
import { Tooltip } from 'antd';
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { LayoutMenu } from './Menu';
|
||||
import { useLayoutStore, usePlatformStore } from './store';
|
||||
@@ -7,9 +7,14 @@ import { useShallow } from 'zustand/react/shallow';
|
||||
import { useEffect, useLayoutEffect, useState } from 'react';
|
||||
import { LayoutUser } from './LayoutUser';
|
||||
import PandaPNG from '@/assets/panda.png';
|
||||
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
||||
import { Panel, PanelGroup } from 'react-resizable-panels';
|
||||
import clsx from 'clsx';
|
||||
import { IconButton as Button } from '@mui/material';
|
||||
import { Button, Menu, MenuItem } from '@mui/material';
|
||||
import i18n from 'i18next';
|
||||
|
||||
import { IconButton } from '@kevisual/center-components/button/index.tsx';
|
||||
import { Languages } from 'lucide-react';
|
||||
|
||||
type LayoutMainProps = {
|
||||
title?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
@@ -46,6 +51,23 @@ export const LayoutMain = (props: LayoutMainProps) => {
|
||||
useEffect(() => {
|
||||
menuStore.getMe();
|
||||
}, []);
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const changeLanguage = (lng: string) => {
|
||||
i18n.changeLanguage(lng);
|
||||
handleClose();
|
||||
};
|
||||
const currentLanguage = i18n.language;
|
||||
|
||||
return (
|
||||
<div className='flex w-full h-full flex-col relative'>
|
||||
<LayoutMenu />
|
||||
@@ -54,16 +76,31 @@ export const LayoutMain = (props: LayoutMainProps) => {
|
||||
style={{
|
||||
cursor: isElectron ? 'move' : 'default',
|
||||
}}>
|
||||
<Button
|
||||
<IconButton
|
||||
className={clsx('mr-4 cursor-pointer no-drag', isMac && 'ml-16')}
|
||||
onClick={() => {
|
||||
menuStore.setOpen(true);
|
||||
}}>
|
||||
<MenuOutlined />
|
||||
</Button>
|
||||
</IconButton>
|
||||
<div className='flex grow justify-between pl-4 items-center'>
|
||||
{props.title}
|
||||
<div className='mr-4 flex gap-4 items-center no-drag'>
|
||||
<div>
|
||||
<Tooltip title={currentLanguage === 'en' ? 'English' : 'Chinese'}>
|
||||
<IconButton onClick={handleClick} variant='contained'>
|
||||
<Languages />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleClose}>
|
||||
<MenuItem selected={currentLanguage === 'en'} onClick={() => changeLanguage('en')}>
|
||||
English
|
||||
</MenuItem>
|
||||
<MenuItem selected={currentLanguage === 'zh'} onClick={() => changeLanguage('zh')}>
|
||||
中文
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
{menuStore.me?.type === 'org' && (
|
||||
<div>
|
||||
<Tooltip title='Switch To User'>
|
||||
@@ -97,16 +134,11 @@ export const LayoutMain = (props: LayoutMainProps) => {
|
||||
<PanelGroup className='w-full h-full panel-layout' autoSaveId='editor-layout-main' direction='horizontal'>
|
||||
<Panel style={{ height: '100%' }}>
|
||||
<div className='h-full overflow-hidden'>
|
||||
<div className='w-full h-full rounded-lg'>
|
||||
<div className='w-full h-full rounded-lg text-primary'>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
{/* <PanelResizeHandle />
|
||||
<Panel style={{ height: '100%' }} defaultSize={25} className={clsx('bg-gray-100')}>
|
||||
侧边栏
|
||||
</Panel> */}
|
||||
</PanelGroup>
|
||||
</div>
|
||||
<LayoutUser />
|
||||
|
||||
Reference in New Issue
Block a user