204 lines
6.9 KiB
TypeScript
204 lines
6.9 KiB
TypeScript
import { MenuOutlined, SwapOutlined } from '@ant-design/icons';
|
|
import { Tooltip } from '@mui/material';
|
|
import { Outlet, useNavigate } from 'react-router-dom';
|
|
import { LayoutMenu, useQuickMenu } from './Menu';
|
|
import { useLayoutStore, usePlatformStore } from './store';
|
|
import { useShallow } from 'zustand/react/shallow';
|
|
import { useEffect, useLayoutEffect, useState } from 'react';
|
|
import { LayoutUser } from './LayoutUser';
|
|
import PandaPNG from '@/assets/panda.png';
|
|
import QRCodePNG from '@/assets/qrcode-8x8.jpg';
|
|
import { Panel, PanelGroup } from 'react-resizable-panels';
|
|
import clsx from 'clsx';
|
|
import { Button, Menu, MenuItem } from '@mui/material';
|
|
import i18n from 'i18next';
|
|
|
|
import { IconButton } from '@kevisual/components/button/index.tsx';
|
|
import { Languages, QrCode } from 'lucide-react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { toast } from 'react-toastify';
|
|
import { useTheme } from '@kevisual/components/theme/index.tsx';
|
|
import { isSky } from '../basename';
|
|
|
|
type LayoutMainProps = {
|
|
title?: React.ReactNode;
|
|
children?: React.ReactNode;
|
|
};
|
|
export const LayoutMain = (props: LayoutMainProps) => {
|
|
const menuStore = useLayoutStore(
|
|
useShallow((state) => {
|
|
return {
|
|
open: state.open,
|
|
setOpen: state.setOpen, //
|
|
getMe: state.getMe,
|
|
me: state.me,
|
|
setOpenUser: state.setOpenUser,
|
|
switchOrg: state.switchOrg,
|
|
};
|
|
}),
|
|
);
|
|
const platformStore = usePlatformStore(
|
|
useShallow((state) => {
|
|
return {
|
|
isMac: state.isMac,
|
|
mount: state.mount,
|
|
isElectron: state.isElectron,
|
|
init: state.init,
|
|
};
|
|
}),
|
|
);
|
|
const { isMac, mount, isElectron } = platformStore;
|
|
const navigate = useNavigate();
|
|
const quickMenu = useQuickMenu();
|
|
|
|
useLayoutEffect(() => {
|
|
platformStore.init();
|
|
}, []);
|
|
|
|
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);
|
|
toast.success(t('Language changed to') + ' ' + t(lng));
|
|
handleClose();
|
|
};
|
|
const currentLanguage = i18n.language;
|
|
const { t } = useTranslation();
|
|
return (
|
|
<div className='flex w-full h-full flex-col relative'>
|
|
<LayoutMenu />
|
|
<div
|
|
className={clsx('layout-menu items-center ', !mount && '!invisible')}
|
|
style={{
|
|
cursor: isElectron ? 'move' : 'default',
|
|
}}>
|
|
<IconButton
|
|
className={clsx('mr-4 cursor-pointer no-drag', isMac && 'ml-16')}
|
|
onClick={() => {
|
|
menuStore.setOpen(true);
|
|
}}>
|
|
<MenuOutlined />
|
|
</IconButton>
|
|
<div className='flex grow justify-between pl-4 items-center'>
|
|
<div className='flex items-center gap-2'>
|
|
<div className='text-xl font-bold'>{props.title}</div>
|
|
<div className='ml-4 flex items-center gap-2 text-sm '>
|
|
{quickMenu.map((item, index) => {
|
|
const isActive = location.pathname.includes(item.link);
|
|
return (
|
|
<div
|
|
key={index}
|
|
className={clsx('flex items-center gap-2 px-1', isActive && 'border border-white')}
|
|
onClick={() => {
|
|
navigate(item.link);
|
|
}}>
|
|
<Tooltip title={item.title}>
|
|
<div className='cursor-pointer'>{item.icon}</div>
|
|
</Tooltip>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
<div className='mr-4 flex gap-4 items-center no-drag'>
|
|
{!isSky && (
|
|
<div className='group relative'>
|
|
<IconButton>
|
|
<QrCode size={16} />
|
|
</IconButton>
|
|
|
|
<div className='absolute hidden group-hover:flex bg-white p-2 border shadow-md top-10 -left-15 w-40 z-[9999] flex-col items-center justify-center rounded-md'>
|
|
<img src={QRCodePNG} alt='QR Code' />
|
|
<div className='text-sm text-black'>逸闻设计</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
<div>
|
|
<Tooltip title={currentLanguage === 'en' ? 'English' : 'Chinese'}>
|
|
<IconButton onClick={handleClick} variant='contained'>
|
|
<Languages size={16} />
|
|
</IconButton>
|
|
</Tooltip>
|
|
<Menu
|
|
anchorEl={anchorEl}
|
|
open={Boolean(anchorEl)}
|
|
onClose={handleClose}
|
|
anchorOrigin={{
|
|
vertical: 'bottom',
|
|
horizontal: 'left',
|
|
}}
|
|
transformOrigin={{
|
|
vertical: -2,
|
|
horizontal: 52,
|
|
}}
|
|
sx={{
|
|
'& .MuiButtonBase-root.Mui-selected': {
|
|
backgroundColor: 'primary.main',
|
|
color: 'white',
|
|
},
|
|
}}>
|
|
<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={t('Switch To User')}>
|
|
<IconButton
|
|
onClick={() => {
|
|
menuStore.switchOrg('', 'user');
|
|
}}>
|
|
<SwapOutlined />
|
|
</IconButton>
|
|
</Tooltip>
|
|
</div>
|
|
)}
|
|
<div className='w-8 h-8 rounded-full avatar cursor-pointer' onClick={() => menuStore.setOpenUser(true)}>
|
|
{menuStore.me?.avatar ? (
|
|
<img className='w-8 h-8 rounded-full' src={menuStore.me?.avatar} alt='avatar' />
|
|
) : (
|
|
<img className='w-8 h-8 rounded-full' src={PandaPNG} alt='avatar' />
|
|
)}
|
|
</div>
|
|
<div className='cursor-pointer' onClick={() => menuStore.setOpenUser(true)}>
|
|
{menuStore.me?.username}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
className='flex'
|
|
style={{
|
|
height: 'calc(100vh - 3rem)',
|
|
}}>
|
|
<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 text-text-primary'>
|
|
<Outlet />
|
|
</div>
|
|
</div>
|
|
</Panel>
|
|
</PanelGroup>
|
|
</div>
|
|
<LayoutUser />
|
|
</div>
|
|
);
|
|
};
|