提交 v1.3.0 beta

This commit is contained in:
dushixiang
2022-10-23 20:05:13 +08:00
parent 4ff4d37442
commit 112435199a
329 changed files with 18340 additions and 58458 deletions

View 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;

View 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']} &nbsp;<DownOutlined/>
</div>
</Dropdown>
</div>
</div>
</Header>
<Suspense fallback={<Landing/>}>
<Outlet/>
</Suspense>
<FooterComponent/>
</Layout>
</Layout>
);
}
export default ManagerLayout;

View 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']} &nbsp;<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
View 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 />,
},
]