- 优化页面结构

- 修复「TOTP 输入界面,回车无法确定」close #101
This commit is contained in:
dushixiang 2021-03-17 20:00:36 +08:00
parent ea48bd3da2
commit d6ef8aa1db
17 changed files with 106 additions and 406 deletions

View File

@ -33,7 +33,7 @@
} }
.layout-header { .layout-header {
height: 48px; height: 60px;
align-items: center; align-items: center;
padding: 0 16px 0 0; padding: 0 16px 0 0;
background: #fff; background: #fff;
@ -45,23 +45,19 @@
padding: 0 12px; padding: 0 12px;
cursor: pointer; cursor: pointer;
transition: all .3s; transition: all .3s;
line-height: 48px; line-height: 60px;
height: 48px; height: 60px;
} }
.layout-header-right-item { .layout-header-right-item {
margin: 0 6px; margin: 0 6px;
display: inline; display: inline;
height: 48px; height: 60px;
}
.layout-header-right-item:hover {
background-color: #eeeeee;
} }
.nickname { .nickname {
line-height: 48px; line-height: 60px;
height: 48px; height: 60px;
width: 125px; width: 125px;
text-align: left; text-align: left;
padding: 0 5px; padding: 0 5px;

View File

@ -1,7 +1,7 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import 'antd/dist/antd.css'; import 'antd/dist/antd.css';
import './App.css'; import './App.css';
import {Divider, Layout, Menu} from "antd"; import {Col, Divider, Dropdown, Layout, Menu, Popconfirm, Row, Tooltip} from "antd";
import {Link, Route, Switch} from "react-router-dom"; import {Link, Route, Switch} from "react-router-dom";
import Dashboard from "./components/dashboard/Dashboard"; import Dashboard from "./components/dashboard/Dashboard";
import Asset from "./components/asset/Asset"; import Asset from "./components/asset/Asset";
@ -21,15 +21,21 @@ import {
DashboardOutlined, DashboardOutlined,
DesktopOutlined, DesktopOutlined,
DisconnectOutlined, DisconnectOutlined,
DownOutlined,
GithubOutlined,
IdcardOutlined, IdcardOutlined,
LinkOutlined, LinkOutlined,
LoginOutlined, LoginOutlined,
LogoutOutlined,
QuestionCircleOutlined,
SafetyCertificateOutlined, SafetyCertificateOutlined,
SettingOutlined, SettingOutlined,
SolutionOutlined, SolutionOutlined,
TeamOutlined, TeamOutlined,
UserOutlined, UserOutlined,
UserSwitchOutlined UserSwitchOutlined,
MenuUnfoldOutlined,
MenuFoldOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import Info from "./components/user/Info"; import Info from "./components/user/Info";
import request from "./common/request"; import request from "./common/request";
@ -37,18 +43,18 @@ import {message} from "antd/es";
import Setting from "./components/setting/Setting"; import Setting from "./components/setting/Setting";
import BatchCommand from "./components/command/BatchCommand"; import BatchCommand from "./components/command/BatchCommand";
import {isEmpty, NT_PACKAGE} from "./utils/utils"; import {isEmpty, NT_PACKAGE} from "./utils/utils";
import {isAdmin} from "./service/permission"; import {getCurrentUser, isAdmin} from "./service/permission";
import UserGroup from "./components/user/UserGroup"; import UserGroup from "./components/user/UserGroup";
import LoginLog from "./components/devops/LoginLog"; import LoginLog from "./components/devops/LoginLog";
import Term from "./components/access/Term"; import Term from "./components/access/Term";
import Job from "./components/devops/Job"; import Job from "./components/devops/Job";
import {Header} from "antd/es/layout/layout"; import {Header} from "antd/es/layout/layout";
import LayoutHeader from "./components/user/LayoutHeader";
import Security from "./components/devops/Security"; import Security from "./components/devops/Security";
const {Footer, Sider} = Layout; const {Footer, Sider} = Layout;
const {SubMenu} = Menu; const {SubMenu} = Menu;
const headerHeight = 60;
class App extends Component { class App extends Component {
@ -113,8 +119,54 @@ class App extends Component {
sessionStorage.setItem('openKeys', JSON.stringify(openKeys)); sessionStorage.setItem('openKeys', JSON.stringify(openKeys));
} }
confirm = async (e) => {
let result = await request.post('/logout');
if (result['code'] !== 1) {
message.error(result['message']);
} else {
message.success('退出登录成功,即将跳转至登录页面。');
window.location.reload();
}
}
render() { render() {
const menu = (
<Menu>
<Menu.Item>
<Link to={'/info'}>
<SolutionOutlined/>
个人中心
</Link>
</Menu.Item>
<Menu.Item>
<a target='_blank' rel="noreferrer" href='https://github.com/dushixiang/next-terminal'>
<GithubOutlined/>
点个Star
</a>
</Menu.Item>
<Menu.Divider/>
<Menu.Item>
<Popconfirm
key='login-btn-pop'
title="您确定要退出登录吗?"
onConfirm={this.confirm}
okText="确定"
cancelText="取消"
placement="left"
>
<LogoutOutlined/>
退出登录
</Popconfirm>
</Menu.Item>
</Menu>
);
return ( return (
@ -126,7 +178,7 @@ class App extends Component {
<Route path="/"> <Route path="/">
<Layout className="layout" style={{minHeight: '100vh'}}> <Layout className="layout" style={{minHeight: '100vh'}}>
<Sider collapsible collapsed={this.state.collapsed} onCollapse={this.onCollapse}> <Sider collapsible collapsed={this.state.collapsed} trigger={null}>
<div className="logo"> <div className="logo">
<img src='logo.svg' alt='logo'/> <img src='logo.svg' alt='logo'/>
{ {
@ -251,8 +303,38 @@ class App extends Component {
<Layout className="site-layout"> <Layout className="site-layout">
<Header className="site-layout-background" <Header className="site-layout-background"
style={{padding: 0, height: 48, zIndex: 20}}> style={{padding: 0, height: headerHeight, zIndex: 20}}>
<LayoutHeader key='layout-right-header'/> <div className='layout-header'>
<Row justify="space-around" align="middle" gutter={24} style={{height: headerHeight}}>
<Col span={4} key={1} style={{height: headerHeight}}>
{React.createElement(this.state.collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
className: 'trigger',
onClick: this.onCollapse,
})}
</Col>
<Col span={20} key={2} style={{textAlign: 'right'}}
className={'layout-header-right'}>
<div className={'layout-header-right-item'}>
<Tooltip placement="bottom" title={'使用帮助'}>
<a target='_blank' rel="noreferrer"
href='https://github.com/dushixiang/next-terminal/blob/master/docs/faq.md'>
<QuestionCircleOutlined/>
</a>
</Tooltip>
</div>
<Dropdown overlay={menu}>
<div className='nickname layout-header-right-item'>
{getCurrentUser()['nickname']} &nbsp;<DownOutlined/>
</div>
</Dropdown>
</Col>
</Row>
</div>
</Header> </Header>
<Route path="/" exact component={Dashboard}/> <Route path="/" exact component={Dashboard}/>

View File

@ -10,7 +10,8 @@ const {Title} = Typography;
class LoginForm extends Component { class LoginForm extends Component {
formRef = React.createRef() formRef = React.createRef();
totpInputRef = React.createRef();
state = { state = {
inLogin: false, inLogin: false,
@ -44,6 +45,8 @@ class LoginForm extends Component {
loginAccount: params, loginAccount: params,
totpModalVisible: true totpModalVisible: true
}) })
this.totpInputRef.current.focus();
return; return;
} }
if (result.code !== 1) { if (result.code !== 1) {
@ -74,8 +77,9 @@ class LoginForm extends Component {
try { try {
let result = await request.post('/loginWithTotp', loginAccount); let result = await request.post('/loginWithTotp', loginAccount);
if (result.code !== 1) { if (result['code'] !== 1) {
throw new Error(result.message); message.error(result['message']);
return;
} }
// 跳转登录 // 跳转登录
@ -129,12 +133,13 @@ class LoginForm extends Component {
<Modal title="双因素认证" visible={this.state.totpModalVisible} confirmLoading={this.state.confirmLoading} <Modal title="双因素认证" visible={this.state.totpModalVisible} confirmLoading={this.state.confirmLoading}
maskClosable={false} maskClosable={false}
centered={true} centered={true}
okButtonProps={{form:'totp-form', key: 'submit', htmlType: 'submit'}}
onOk={() => { onOk={() => {
this.formRef.current this.formRef.current
.validateFields() .validateFields()
.then(values => { .then(values => {
this.formRef.current.resetFields();
this.handleOk(values); this.handleOk(values);
// this.formRef.current.resetFields();
}) })
.catch(info => { .catch(info => {
@ -142,10 +147,10 @@ class LoginForm extends Component {
}} }}
onCancel={this.handleCancel}> onCancel={this.handleCancel}>
<Form ref={this.formRef}> <Form id='totp-form' ref={this.formRef}>
<Form.Item name='totp' rules={[{required: true, message: '请输入双因素认证APP中显示的授权码'}]}> <Form.Item name='totp' rules={[{required: true, message: '请输入双因素认证APP中显示的授权码'}]}>
<Input prefix={<OneToOneOutlined/>} placeholder="请输入双因素认证APP中显示的授权码"/> <Input ref={this.totpInputRef} prefix={<OneToOneOutlined/>} placeholder="请输入双因素认证APP中显示的授权码"/>
</Form.Item> </Form.Item>
</Form> </Form>
</Modal> </Modal>

View File

@ -51,19 +51,6 @@ const confirm = Modal.confirm;
const {Search} = Input; const {Search} = Input;
const {Content} = Layout; const {Content} = Layout;
const {Title, Text} = Typography; const {Title, Text} = Typography;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
breadcrumbName: '资源管理',
},
{
path: 'assets',
breadcrumbName: '资产管理',
}
];
class Asset extends Component { class Asset extends Component {
@ -660,18 +647,6 @@ class Asset extends Component {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="资产管理"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="资产"
>
</PageHeader>
<Content key='page-content' className="site-layout-background page-content"> <Content key='page-content' className="site-layout-background page-content">
<div style={{marginBottom: 20}}> <div style={{marginBottom: 20}}>
<Row justify="space-around" align="middle" gutter={24}> <Row justify="space-around" align="middle" gutter={24}>

View File

@ -43,16 +43,6 @@ const {Content} = Layout;
const {Title, Text} = Typography; const {Title, Text} = Typography;
const {Search} = Input; const {Search} = Input;
const CheckboxGroup = Checkbox.Group; const CheckboxGroup = Checkbox.Group;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'command',
breadcrumbName: '动态指令',
}
];
class DynamicCommand extends Component { class DynamicCommand extends Component {
@ -534,18 +524,6 @@ class DynamicCommand extends Component {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="动态指令"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="批量动态指令执行"
>
</PageHeader>
<Content className="site-layout-background page-content"> <Content className="site-layout-background page-content">
<div style={{marginBottom: 20}}> <div style={{marginBottom: 20}}>

View File

@ -10,7 +10,6 @@ import {
Layout, Layout,
Menu, Menu,
Modal, Modal,
PageHeader,
Row, Row,
Select, Select,
Space, Space,
@ -32,7 +31,6 @@ import {
SyncOutlined, SyncOutlined,
UndoOutlined UndoOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import {itemRender} from "../../utils/utils";
import {hasPermission, isAdmin} from "../../service/permission"; import {hasPermission, isAdmin} from "../../service/permission";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -41,16 +39,6 @@ const confirm = Modal.confirm;
const {Search} = Input; const {Search} = Input;
const {Title, Text} = Typography; const {Title, Text} = Typography;
const {Content} = Layout; const {Content} = Layout;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'credentials',
breadcrumbName: '授权凭证',
}
];
class Credential extends Component { class Credential extends Component {
@ -483,18 +471,6 @@ class Credential extends Component {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="授权凭证"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="访问资产的账户、密钥等"
>
</PageHeader>
<Content className="site-layout-background page-content"> <Content className="site-layout-background page-content">
<div style={{marginBottom: 20}}> <div style={{marginBottom: 20}}>

View File

@ -9,18 +9,6 @@ import {Area} from '@ant-design/charts';
import {isAdmin} from "../../service/permission"; import {isAdmin} from "../../service/permission";
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'dashboard',
breadcrumbName: '仪表盘',
}
];
class Dashboard extends Component { class Dashboard extends Component {
state = { state = {
@ -79,17 +67,6 @@ class Dashboard extends Component {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="dashboard"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="仪表盘"
extra={[]}
>
</PageHeader>
<div className="page-card"> <div className="page-card">

View File

@ -40,16 +40,6 @@ const confirm = Modal.confirm;
const {Content} = Layout; const {Content} = Layout;
const {Title, Text} = Typography; const {Title, Text} = Typography;
const {Search} = Input; const {Search} = Input;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'job',
breadcrumbName: '计划任务',
}
];
class Job extends Component { class Job extends Component {
@ -441,18 +431,6 @@ class Job extends Component {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="计划任务"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="计划任务"
>
</PageHeader>
<Content className="site-layout-background page-content"> <Content className="site-layout-background page-content">
<div style={{marginBottom: 20}}> <div style={{marginBottom: 20}}>

View File

@ -27,16 +27,6 @@ const confirm = Modal.confirm;
const {Content} = Layout; const {Content} = Layout;
const {Search} = Input; const {Search} = Input;
const {Title, Text} = Typography; const {Title, Text} = Typography;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'loginLog',
breadcrumbName: '登录日志',
}
];
class LoginLog extends Component { class LoginLog extends Component {
@ -280,18 +270,6 @@ class LoginLog extends Component {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="登录日志"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="只有登录成功的才会保存日志"
>
</PageHeader>
<Content className="site-layout-background page-content"> <Content className="site-layout-background page-content">
<div style={{marginBottom: 20}}> <div style={{marginBottom: 20}}>
<Row justify="space-around" align="middle" gutter={24}> <Row justify="space-around" align="middle" gutter={24}>

View File

@ -27,16 +27,6 @@ const confirm = Modal.confirm;
const {Content} = Layout; const {Content} = Layout;
const {Title, Text} = Typography; const {Title, Text} = Typography;
const {Search} = Input; const {Search} = Input;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'security',
breadcrumbName: '访问安全',
}
];
class Security extends Component { class Security extends Component {
@ -296,18 +286,6 @@ class Security extends Component {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="访问安全"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="IP访问规则限制"
>
</PageHeader>
<Content className="site-layout-background page-content"> <Content className="site-layout-background page-content">
<div style={{marginBottom: 20}}> <div style={{marginBottom: 20}}>

View File

@ -30,16 +30,6 @@ const confirm = Modal.confirm;
const {Content} = Layout; const {Content} = Layout;
const {Search} = Input; const {Search} = Input;
const {Title, Text} = Typography; const {Title, Text} = Typography;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'offlineSession',
breadcrumbName: '离线会话',
}
];
class OfflineSession extends Component { class OfflineSession extends Component {
@ -365,18 +355,6 @@ class OfflineSession extends Component {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="离线会话"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="离线会话管理"
>
</PageHeader>
<Content className="site-layout-background page-content"> <Content className="site-layout-background page-content">
<div style={{marginBottom: 20}}> <div style={{marginBottom: 20}}>
<Row justify="space-around" align="middle" gutter={24}> <Row justify="space-around" align="middle" gutter={24}>

View File

@ -31,16 +31,6 @@ const confirm = Modal.confirm;
const {Content} = Layout; const {Content} = Layout;
const {Search} = Input; const {Search} = Input;
const {Title, Text} = Typography; const {Title, Text} = Typography;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'onlineSession',
breadcrumbName: '在线会话',
}
];
class OnlineSession extends Component { class OnlineSession extends Component {
@ -335,18 +325,6 @@ class OnlineSession extends Component {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="在线会话"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="查询实时在线会话"
>
</PageHeader>
<Content className="site-layout-background page-content"> <Content className="site-layout-background page-content">
<div style={{marginBottom: 20}}> <div style={{marginBottom: 20}}>

View File

@ -10,17 +10,6 @@ const {Option} = Select;
const {TabPane} = Tabs; const {TabPane} = Tabs;
const {Title} = Typography; const {Title} = Typography;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'setting',
breadcrumbName: '系统设置',
}
];
const formItemLayout = { const formItemLayout = {
labelCol: {span: 12}, labelCol: {span: 12},
wrapperCol: {span: 12}, wrapperCol: {span: 12},
@ -114,18 +103,6 @@ class Setting extends Component {
render() { render() {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="系统设置"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="系统设置"
>
</PageHeader>
<Content className="site-layout-background page-content"> <Content className="site-layout-background page-content">
<Tabs tabPosition={'left'} onChange={this.handleOnTabChange} tabBarStyle={{width: 150}}> <Tabs tabPosition={'left'} onChange={this.handleOnTabChange} tabBarStyle={{width: 150}}>

View File

@ -8,17 +8,6 @@ import {ExclamationCircleOutlined, ReloadOutlined} from "@ant-design/icons";
const {Content} = Layout; const {Content} = Layout;
const {Meta} = Card; const {Meta} = Card;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'info',
breadcrumbName: '个人中心',
}
];
const formItemLayout = { const formItemLayout = {
labelCol: {span: 3}, labelCol: {span: 3},
wrapperCol: {span: 6}, wrapperCol: {span: 6},
@ -121,17 +110,6 @@ class Info extends Component {
render() { render() {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="个人中心"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="个人中心"
/>
<Content className="site-layout-background page-content"> <Content className="site-layout-background page-content">
<h1>修改密码</h1> <h1>修改密码</h1>
<Form ref={this.passwordFormRef} name="password" onFinish={this.changePassword}> <Form ref={this.passwordFormRef} name="password" onFinish={this.changePassword}>

View File

@ -1,87 +0,0 @@
import React, {Component} from 'react';
import {Col, Dropdown, Menu, message, Popconfirm, Row, Tooltip} from "antd";
import request from "../../common/request";
import {getCurrentUser} from "../../service/permission";
import {GithubOutlined, LogoutOutlined, QuestionCircleOutlined, SolutionOutlined} from "@ant-design/icons";
import {Link} from "react-router-dom";
class LayoutHeader extends Component {
confirm = async (e) => {
let result = await request.post('/logout');
if (result['code'] !== 1) {
message.error(result['message']);
} else {
message.success('退出登录成功,即将跳转至登录页面。');
window.location.reload();
}
}
render() {
const menu = (
<Menu>
<Menu.Item>
<Link to={'/info'}>
<SolutionOutlined/>
个人中心
</Link>
</Menu.Item>
<Menu.Item>
<a target='_blank' href='https://github.com/dushixiang/next-terminal'>
<GithubOutlined/>
点个Star
</a>
</Menu.Item>
<Menu.Divider/>
<Menu.Item>
<Popconfirm
key='login-btn-pop'
title="您确定要退出登录吗?"
onConfirm={this.confirm}
okText="确定"
cancelText="取消"
placement="left"
>
<LogoutOutlined/>
退出登录
</Popconfirm>
</Menu.Item>
</Menu>
);
return (
<div className='layout-header'>
<Row justify="space-around" align="middle" gutter={24} style={{height: 48}}>
<Col span={4} key={1} style={{height: 48}}> </Col>
<Col span={20} key={2} style={{textAlign: 'right'}} className={'layout-header-right'}>
<div className={'layout-header-right-item'}>
<Tooltip placement="bottom" title={'使用帮助'}>
<a target='_blank' href='https://github.com/dushixiang/next-terminal/blob/master/docs/faq.md'>
<QuestionCircleOutlined/>
</a>
</Tooltip>
</div>
<Dropdown overlay={menu}>
<div className='nickname layout-header-right-item'>{getCurrentUser()['nickname']}</div>
</Dropdown>
</Col>
</Row>
</div>
);
}
}
export default LayoutHeader;

View File

@ -34,7 +34,6 @@ import {
SyncOutlined, SyncOutlined,
UndoOutlined UndoOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import LayoutHeader from "./LayoutHeader";
import UserShareAsset from "./UserShareAsset"; import UserShareAsset from "./UserShareAsset";
import {hasPermission} from "../../service/permission"; import {hasPermission} from "../../service/permission";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -44,17 +43,6 @@ const {Search} = Input;
const {Title, Text} = Typography; const {Title, Text} = Typography;
const {Content} = Layout; const {Content} = Layout;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'user',
breadcrumbName: '用户',
}
];
class User extends Component { class User extends Component {
inputRefOfNickname = React.createRef(); inputRefOfNickname = React.createRef();
@ -473,18 +461,6 @@ class User extends Component {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="用户管理"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="平台用户管理"
>
</PageHeader>
<Content className="site-layout-background page-content"> <Content className="site-layout-background page-content">
<div style={{marginBottom: 20}}> <div style={{marginBottom: 20}}>
<Row justify="space-around" align="middle" gutter={24}> <Row justify="space-around" align="middle" gutter={24}>

View File

@ -15,17 +15,6 @@ const {Search} = Input;
const {Title, Text} = Typography; const {Title, Text} = Typography;
const {Content} = Layout; const {Content} = Layout;
const routes = [
{
path: '',
breadcrumbName: '首页',
},
{
path: 'user',
breadcrumbName: '用户组',
}
];
class UserGroup extends Component { class UserGroup extends Component {
inputRefOfName = React.createRef(); inputRefOfName = React.createRef();
@ -334,18 +323,6 @@ class UserGroup extends Component {
return ( return (
<> <>
<PageHeader
className="site-page-header-ghost-wrapper"
title="用户组管理"
breadcrumb={{
routes: routes,
itemRender: itemRender
}}
subTitle="平台用户管理"
>
</PageHeader>
<Content className="site-layout-background page-content"> <Content className="site-layout-background page-content">
<div style={{marginBottom: 20}}> <div style={{marginBottom: 20}}>
<Row justify="space-around" align="middle" gutter={24}> <Row justify="space-around" align="middle" gutter={24}>