feat: first commit
This commit is contained in:
21
admin/src/components/NavBar/IconButton.tsx
Normal file
21
admin/src/components/NavBar/IconButton.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React, { forwardRef } from 'react';
|
||||
import { Button } from '@arco-design/web-react';
|
||||
import styles from './style/icon-button.module.less';
|
||||
import cs from 'classnames';
|
||||
|
||||
function IconButton(props, ref) {
|
||||
const { icon, className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Button
|
||||
ref={ref}
|
||||
icon={icon}
|
||||
shape="circle"
|
||||
type="secondary"
|
||||
className={cs(styles['icon-button'], className)}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default forwardRef(IconButton);
|
||||
165
admin/src/components/NavBar/index.tsx
Normal file
165
admin/src/components/NavBar/index.tsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import React, { useContext } from 'react';
|
||||
import {
|
||||
Tooltip,
|
||||
Avatar,
|
||||
Select,
|
||||
Dropdown,
|
||||
Menu,
|
||||
Divider,
|
||||
Message,
|
||||
Button,
|
||||
} from '@arco-design/web-react';
|
||||
import {
|
||||
IconLanguage,
|
||||
IconSunFill,
|
||||
IconMoonFill,
|
||||
IconUser,
|
||||
IconSettings,
|
||||
IconPoweroff,
|
||||
IconTag,
|
||||
IconLoading,
|
||||
} from '@arco-design/web-react/icon';
|
||||
import { GlobalContext } from '@/context';
|
||||
import useLocale from '@/utils/useLocale';
|
||||
import IconButton from './IconButton';
|
||||
import Settings from '../Settings';
|
||||
import styles from './style/index.module.less';
|
||||
import defaultLocale from '@/locale';
|
||||
import useStorage from '@/utils/useStorage';
|
||||
import logoIcon from '@/assets/logo.png'
|
||||
import {useStore} from '@/store';
|
||||
|
||||
function Navbar({ show }: { show: boolean }) {
|
||||
const t = useLocale();
|
||||
const { userInfo, userLoading } = useStore()
|
||||
const [_, setUserStatus] = useStorage('userStatus');
|
||||
const [role, setRole] = useStorage('userRole', 'admin');
|
||||
|
||||
const { setLang, lang, theme, setTheme } = useContext(GlobalContext);
|
||||
|
||||
function logout() {
|
||||
setUserStatus('logout');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
function onMenuItemClick(key) {
|
||||
if (key === 'logout') {
|
||||
logout();
|
||||
} else {
|
||||
Message.info(`You clicked ${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!show) {
|
||||
return (
|
||||
<div className={styles['fixed-settings']}>
|
||||
<Settings
|
||||
trigger={
|
||||
<Button icon={<IconSettings />} type="primary" size="large" />
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const handleChangeRole = () => {
|
||||
const newRole = role === 'admin' ? 'user' : 'admin';
|
||||
setRole(newRole);
|
||||
};
|
||||
|
||||
const droplist = (
|
||||
<Menu onClickMenuItem={onMenuItemClick}>
|
||||
<Menu.SubMenu
|
||||
key="role"
|
||||
title={
|
||||
<>
|
||||
<IconUser className={styles['dropdown-icon']} />
|
||||
<span className={styles['user-role']}>
|
||||
{role === 'admin'
|
||||
? t['menu.user.role.admin']
|
||||
: t['menu.user.role.user']}
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Menu.Item onClick={handleChangeRole} key="switch role">
|
||||
<IconTag className={styles['dropdown-icon']} />
|
||||
{t['menu.user.switchRoles']}
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.Item key="setting">
|
||||
<IconSettings className={styles['dropdown-icon']} />
|
||||
{t['menu.user.setting']}
|
||||
</Menu.Item>
|
||||
<Divider style={{ margin: '4px 0' }} />
|
||||
<Menu.Item key="logout">
|
||||
<IconPoweroff className={styles['dropdown-icon']} />
|
||||
{t['navbar.logout']}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.navbar}>
|
||||
<div className={styles.left}>
|
||||
<div className={styles.logo}>
|
||||
<img className={styles.logo} src={logoIcon} alt=""/>
|
||||
<div className={styles['logo-name']}>{import.meta.env.VITE_APP_TITLE}</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul className={styles.right}>
|
||||
<li>
|
||||
<Select
|
||||
triggerElement={<IconButton icon={<IconLanguage />} />}
|
||||
options={[
|
||||
{ label: '中文', value: 'zh-CN' },
|
||||
{ label: 'English', value: 'en-US' },
|
||||
]}
|
||||
value={lang}
|
||||
triggerProps={{
|
||||
autoAlignPopupWidth: false,
|
||||
autoAlignPopupMinWidth: true,
|
||||
position: 'br',
|
||||
}}
|
||||
trigger="hover"
|
||||
onChange={(value) => {
|
||||
setLang(value);
|
||||
const nextLang = defaultLocale[value];
|
||||
Message.info(`${nextLang['message.lang.tips']}${value}`);
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<Tooltip
|
||||
content={
|
||||
theme === 'light'
|
||||
? t['settings.navbar.theme.toDark']
|
||||
: t['settings.navbar.theme.toLight']
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
icon={theme !== 'dark' ? <IconMoonFill /> : <IconSunFill />}
|
||||
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</li>
|
||||
<Settings />
|
||||
{userInfo && (
|
||||
<li>
|
||||
<Dropdown droplist={droplist} position="br" disabled={userLoading}>
|
||||
<Avatar size={32} style={{ cursor: 'pointer' }}>
|
||||
{userLoading ? (
|
||||
<IconLoading />
|
||||
) : (
|
||||
<img alt="avatar" src={userInfo?.avatar?.path} />
|
||||
)}
|
||||
</Avatar>
|
||||
</Dropdown>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Navbar;
|
||||
@@ -0,0 +1,8 @@
|
||||
.icon-button {
|
||||
font-size: 16px;
|
||||
border: 1px solid var(--color-border-2);
|
||||
|
||||
> svg {
|
||||
vertical-align: -3px;
|
||||
}
|
||||
}
|
||||
83
admin/src/components/NavBar/style/index.module.less
Normal file
83
admin/src/components/NavBar/style/index.module.less
Normal file
@@ -0,0 +1,83 @@
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
box-sizing: border-box;
|
||||
background-color: var(--color-bg-2);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 44px;
|
||||
padding-left: 20px;
|
||||
box-sizing: border-box;
|
||||
|
||||
svg{
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.logo-name {
|
||||
color: var(--color-text-1);
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
margin-left: 10px;
|
||||
font-family: 'PingFang SC';
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
padding-right: 20px;
|
||||
|
||||
li {
|
||||
padding: 0 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
}
|
||||
|
||||
.username {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.round {
|
||||
:global(.arco-input-inner-wrapper) {
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-icon {
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.fixed-settings {
|
||||
position: fixed;
|
||||
top: 280px;
|
||||
right: 0px;
|
||||
|
||||
svg {
|
||||
font-size: 18px;
|
||||
vertical-align: -4px;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user