提交 v1.3.0 beta
This commit is contained in:
30
web/src/layout/FooterComponent.js
Normal file
30
web/src/layout/FooterComponent.js
Normal file
@ -0,0 +1,30 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Layout} from "antd";
|
||||
import {NT_PACKAGE} from "../utils/utils";
|
||||
import brandingApi from "../api/branding";
|
||||
|
||||
const {Footer} = Layout;
|
||||
|
||||
let _package = NT_PACKAGE();
|
||||
|
||||
const FooterComponent = () => {
|
||||
|
||||
let [branding, setBranding] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
const x = async () => {
|
||||
let branding = await brandingApi.getBranding();
|
||||
document.title = branding['name'];
|
||||
setBranding(branding);
|
||||
}
|
||||
x();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Footer style={{textAlign: 'center'}}>
|
||||
{branding['copyright']} Version:{_package['version']}
|
||||
</Footer>
|
||||
);
|
||||
}
|
||||
|
||||
export default FooterComponent;
|
199
web/src/layout/ManagerLayout.js
Normal file
199
web/src/layout/ManagerLayout.js
Normal file
@ -0,0 +1,199 @@
|
||||
import React, {Suspense, useEffect, useState} from 'react';
|
||||
import {Breadcrumb, Dropdown, Layout, Menu, Popconfirm} from "antd";
|
||||
import {DesktopOutlined, DownOutlined, LogoutOutlined,} from "@ant-design/icons";
|
||||
import {Link, Outlet, useLocation, useNavigate} from "react-router-dom";
|
||||
import {getCurrentUser, isAdmin} from "../service/permission";
|
||||
import LogoWithName from "../images/logo-with-name.png";
|
||||
import Logo from "../images/logo.png";
|
||||
import FooterComponent from "./FooterComponent";
|
||||
import accountApi from "../api/account";
|
||||
import {routers} from "./router";
|
||||
import Landing from "../components/Landing";
|
||||
import {setTitle} from "../hook/title";
|
||||
|
||||
const {Sider, Header} = Layout;
|
||||
|
||||
const breadcrumbMatchMap = {
|
||||
'/asset/': '资产详情',
|
||||
'/user/': '用户详情',
|
||||
'/role/': '角色详情',
|
||||
'/user-group/': '用户组详情',
|
||||
'/login-policy/': '登录策略详情',
|
||||
'/strategy/': '授权策略详情',
|
||||
};
|
||||
const breadcrumbNameMap = {};
|
||||
|
||||
routers.forEach(r => {
|
||||
if (r.children) {
|
||||
r.children.forEach(c => {
|
||||
breadcrumbNameMap['/' + c.key] = c.label;
|
||||
})
|
||||
} else {
|
||||
breadcrumbNameMap['/' + r.key] = r.label;
|
||||
}
|
||||
});
|
||||
|
||||
const ManagerLayout = () => {
|
||||
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
let currentUser = getCurrentUser();
|
||||
|
||||
let userMenus = currentUser['menus'] || [];
|
||||
let menus = routers.filter(router => userMenus.includes(router.key)).map(router => {
|
||||
if (router.children) {
|
||||
router.children = router.children.filter(r => userMenus.includes(r.key));
|
||||
}
|
||||
return router;
|
||||
});
|
||||
|
||||
let [collapsed, setCollapsed] = useState(false);
|
||||
|
||||
let _current = location.pathname.split('/')[1];
|
||||
|
||||
|
||||
let [current, setCurrent] = useState(_current);
|
||||
let [logo, setLogo] = useState(LogoWithName);
|
||||
let [logoWidth, setLogoWidth] = useState(140);
|
||||
let [openKeys, setOpenKeys] = useState(JSON.parse(sessionStorage.getItem('openKeys')));
|
||||
|
||||
useEffect(() => {
|
||||
setCurrent(_current);
|
||||
setTitle(breadcrumbNameMap['/' + _current]);
|
||||
}, [_current]);
|
||||
|
||||
const pathSnippets = location.pathname.split('/').filter(i => i);
|
||||
|
||||
const extraBreadcrumbItems = pathSnippets.map((_, index) => {
|
||||
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
|
||||
let label = breadcrumbNameMap[url];
|
||||
if (!label) {
|
||||
for (let k in breadcrumbMatchMap) {
|
||||
if (url.includes(k)) {
|
||||
label = breadcrumbMatchMap[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Breadcrumb.Item key={url}>
|
||||
<Link to={url}>{label}</Link>
|
||||
</Breadcrumb.Item>
|
||||
);
|
||||
});
|
||||
|
||||
const breadcrumbItems = [
|
||||
<Breadcrumb.Item key="home">
|
||||
<Link to="/">首页</Link>
|
||||
</Breadcrumb.Item>,
|
||||
].concat(extraBreadcrumbItems);
|
||||
|
||||
const onCollapse = () => {
|
||||
let _collapsed = !collapsed;
|
||||
if (_collapsed) {
|
||||
setLogo(Logo);
|
||||
setLogoWidth(46);
|
||||
setCollapsed(_collapsed);
|
||||
} else {
|
||||
setLogo(LogoWithName);
|
||||
setLogoWidth(140);
|
||||
setCollapsed(false);
|
||||
}
|
||||
};
|
||||
|
||||
const subMenuChange = (openKeys) => {
|
||||
setOpenKeys(openKeys);
|
||||
sessionStorage.setItem('openKeys', JSON.stringify(openKeys));
|
||||
}
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item>
|
||||
<Link to={'/my-asset'}><DesktopOutlined/> 我的资产</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<Popconfirm
|
||||
key='login-btn-pop'
|
||||
title="您确定要退出登录吗?"
|
||||
onConfirm={accountApi.logout}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
placement="left"
|
||||
>
|
||||
<LogoutOutlined/> 退出登录
|
||||
</Popconfirm>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
if (!isAdmin()) {
|
||||
window.location.href = "#/my-asset";
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout className="layout" style={{minHeight: '100vh'}}>
|
||||
<Sider
|
||||
collapsible
|
||||
collapsed={collapsed}
|
||||
onCollapse={onCollapse}
|
||||
style={{
|
||||
overflow: 'auto',
|
||||
height: '100vh',
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<div className="logo">
|
||||
<img src={logo} alt='logo' width={logoWidth}/>
|
||||
</div>
|
||||
|
||||
<Menu
|
||||
onClick={(e) => {
|
||||
navigate(e.key);
|
||||
setCurrent(e.key);
|
||||
}}
|
||||
selectedKeys={[current]}
|
||||
onOpenChange={subMenuChange}
|
||||
defaultOpenKeys={openKeys}
|
||||
theme="dark"
|
||||
mode="inline"
|
||||
defaultSelectedKeys={['']}
|
||||
items={menus}
|
||||
>
|
||||
</Menu>
|
||||
</Sider>
|
||||
|
||||
<Layout className="site-layout" style={{marginLeft: collapsed ? 80 : 200}}>
|
||||
<Header style={{padding: 0, height: 60, zIndex: 20}}>
|
||||
<div className='layout-header'>
|
||||
<div className='layout-header-left'>
|
||||
<div style={{marginLeft: 16}}>
|
||||
<Breadcrumb>{breadcrumbItems}</Breadcrumb>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='layout-header-right'>
|
||||
<Dropdown overlay={menu}>
|
||||
<div className='nickname layout-header-right-item'>
|
||||
{getCurrentUser()['nickname']} <DownOutlined/>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</Header>
|
||||
|
||||
<Suspense fallback={<Landing/>}>
|
||||
<Outlet/>
|
||||
</Suspense>
|
||||
|
||||
<FooterComponent/>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default ManagerLayout;
|
132
web/src/layout/UserLayout.js
Normal file
132
web/src/layout/UserLayout.js
Normal file
@ -0,0 +1,132 @@
|
||||
import React, {Suspense, useEffect} from 'react';
|
||||
import {Link, Outlet, useLocation} from "react-router-dom";
|
||||
import {Breadcrumb, Button, Dropdown, Layout, Menu, Popconfirm} from "antd";
|
||||
import {
|
||||
CodeOutlined,
|
||||
DashboardOutlined,
|
||||
DesktopOutlined,
|
||||
DownOutlined,
|
||||
LogoutOutlined,
|
||||
UserOutlined
|
||||
} from "@ant-design/icons";
|
||||
import {getCurrentUser, isAdmin} from "../service/permission";
|
||||
import FooterComponent from "./FooterComponent";
|
||||
import accountApi from "../api/account";
|
||||
import LogoWithName from "../images/logo-with-name.png";
|
||||
import Landing from "../components/Landing";
|
||||
import {setTitle} from "../hook/title";
|
||||
|
||||
const {Header, Content} = Layout;
|
||||
|
||||
const breadcrumbNameMap = {
|
||||
'/my-asset': '我的资产',
|
||||
'/my-command': '我的指令',
|
||||
'/my-info': '个人中心',
|
||||
};
|
||||
|
||||
const UserLayout = () => {
|
||||
|
||||
const location = useLocation();
|
||||
let _current = location.pathname.split('/')[1];
|
||||
|
||||
useEffect(() => {
|
||||
setTitle(breadcrumbNameMap['/' + _current]);
|
||||
}, [_current]);
|
||||
|
||||
const pathSnippets = location.pathname.split('/').filter(i => i);
|
||||
|
||||
const extraBreadcrumbItems = pathSnippets.map((_, index) => {
|
||||
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
|
||||
return (
|
||||
<Breadcrumb.Item key={url}>
|
||||
<Link to={url}>{breadcrumbNameMap[url]}</Link>
|
||||
</Breadcrumb.Item>
|
||||
);
|
||||
});
|
||||
|
||||
const breadcrumbItems = [
|
||||
<Breadcrumb.Item key="home">
|
||||
<Link to="/my-asset">首页</Link>
|
||||
</Breadcrumb.Item>,
|
||||
].concat(extraBreadcrumbItems);
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
{
|
||||
isAdmin() &&
|
||||
<Menu.Item>
|
||||
<Link to={'/dashboard'}><DashboardOutlined/> 后台管理</Link>
|
||||
</Menu.Item>
|
||||
}
|
||||
|
||||
<Menu.Item>
|
||||
<Popconfirm
|
||||
key='login-btn-pop'
|
||||
title="您确定要退出登录吗?"
|
||||
onConfirm={accountApi.logout}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
placement="left"
|
||||
>
|
||||
<LogoutOutlined/> 退出登录
|
||||
</Popconfirm>
|
||||
</Menu.Item>
|
||||
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Layout className="layout" style={{minHeight: '100vh'}}>
|
||||
<Header style={{padding: 0}}>
|
||||
<div className='km-header'>
|
||||
<div style={{flex: '1 1 0%'}}>
|
||||
<Link to={'/my-asset'}>
|
||||
<img src={LogoWithName} alt='logo' width={120}/>
|
||||
</Link>
|
||||
|
||||
<Link to={'/my-asset'}>
|
||||
<Button type="text" style={{color: 'white'}}
|
||||
icon={<DesktopOutlined/>}>
|
||||
我的资产
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<Link to={'/my-command'}>
|
||||
<Button type="text" style={{color: 'white'}}
|
||||
icon={<CodeOutlined/>}>
|
||||
我的指令
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<Link to={'/my-info'}>
|
||||
<Button type="text" style={{color: 'white'}}
|
||||
icon={<UserOutlined/>}>
|
||||
个人中心
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
</div>
|
||||
<div className='km-header-right'>
|
||||
<Dropdown overlay={menu}>
|
||||
<div className={'nickname layout-header-right-item'}>
|
||||
{getCurrentUser()['nickname']} <DownOutlined/>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</Header>
|
||||
|
||||
<Content className='nt-container'>
|
||||
<div style={{marginBottom: 16}}>
|
||||
<Breadcrumb>{breadcrumbItems}</Breadcrumb>
|
||||
</div>
|
||||
<Suspense fallback={<Landing/>}>
|
||||
<Outlet/>
|
||||
</Suspense>
|
||||
</Content>
|
||||
<FooterComponent/>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default UserLayout;
|
181
web/src/layout/router.js
Normal file
181
web/src/layout/router.js
Normal file
@ -0,0 +1,181 @@
|
||||
import {
|
||||
ApiOutlined,
|
||||
AuditOutlined,
|
||||
BlockOutlined,
|
||||
CloudServerOutlined,
|
||||
CodeOutlined,
|
||||
ControlOutlined,
|
||||
DashboardOutlined,
|
||||
DesktopOutlined,
|
||||
DisconnectOutlined,
|
||||
HddOutlined,
|
||||
IdcardOutlined,
|
||||
InsuranceOutlined,
|
||||
LinkOutlined,
|
||||
LoginOutlined,
|
||||
MonitorOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
SettingOutlined,
|
||||
SolutionOutlined,
|
||||
TeamOutlined,
|
||||
UserOutlined,
|
||||
UserSwitchOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import React from "react";
|
||||
|
||||
export const routers = [
|
||||
{
|
||||
key: 'dashboard',
|
||||
label: '控制面板',
|
||||
icon: <DashboardOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'resource',
|
||||
label: '资源管理',
|
||||
icon: <CloudServerOutlined/>,
|
||||
children: [
|
||||
{
|
||||
key: 'asset',
|
||||
label: '资产管理',
|
||||
icon: <DesktopOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'credential',
|
||||
label: '授权凭证',
|
||||
icon: <IdcardOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'command',
|
||||
label: '动态指令',
|
||||
icon: <CodeOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'access-gateway',
|
||||
label: '接入网关',
|
||||
icon: <ApiOutlined/>,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'session-audit',
|
||||
label: '会话审计',
|
||||
icon: <AuditOutlined/>,
|
||||
children: [
|
||||
{
|
||||
key: 'online-session',
|
||||
label: '在线会话',
|
||||
icon: <LinkOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'offline-session',
|
||||
label: '历史会话',
|
||||
icon: <DisconnectOutlined/>,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'log-audit',
|
||||
label: '日志审计',
|
||||
icon: <AuditOutlined/>,
|
||||
children: [
|
||||
{
|
||||
key: 'login-log',
|
||||
label: '登录日志',
|
||||
icon: <LoginOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'storage-log',
|
||||
label: '文件日志',
|
||||
icon: <LoginOutlined/>,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'ops',
|
||||
label: '系统运维',
|
||||
icon: <ControlOutlined/>,
|
||||
children: [
|
||||
// {
|
||||
// key: 'batch-command',
|
||||
// label: '批量命令',
|
||||
// icon: <BlockOutlined/>,
|
||||
// },
|
||||
{
|
||||
key: 'job',
|
||||
label: '计划任务',
|
||||
icon: <BlockOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'storage',
|
||||
label: '磁盘空间',
|
||||
icon: <HddOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'monitoring',
|
||||
label: '系统监控',
|
||||
icon: <MonitorOutlined/>,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'security',
|
||||
label: '安全策略',
|
||||
icon: <SafetyCertificateOutlined/>,
|
||||
children: [
|
||||
{
|
||||
key: 'access-security',
|
||||
label: '访问安全',
|
||||
icon: <SafetyCertificateOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'login-policy',
|
||||
label: '登录策略',
|
||||
icon: <LoginOutlined/>,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'identity',
|
||||
label: '用户管理',
|
||||
icon: <UserSwitchOutlined/>,
|
||||
children: [
|
||||
{
|
||||
key: 'user',
|
||||
label: '用户管理',
|
||||
icon: <UserOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'role',
|
||||
label: '角色管理',
|
||||
icon: <SolutionOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'user-group',
|
||||
label: '用户组管理',
|
||||
icon: <TeamOutlined/>,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'authorised',
|
||||
label: '授权策略',
|
||||
icon: <UserSwitchOutlined/>,
|
||||
children: [
|
||||
{
|
||||
key: 'strategy',
|
||||
label: '授权策略',
|
||||
icon: <InsuranceOutlined/>,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'setting',
|
||||
label: '系统设置',
|
||||
icon: <SettingOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'info',
|
||||
label: '个人中心',
|
||||
icon: <UserOutlined />,
|
||||
},
|
||||
]
|
Reference in New Issue
Block a user