先上一下效果图:
登陆页:css
任意输入帐号密码(由于尚未后台,不想作啥校验了)
登陆后是这样子的react
很传统有没有,左边是菜单导航,右边是tabs区域。为了让系统显得高大上,这里还作了根据路由信息,判断是否全屏显示,来,看看咱们的全屏。git
嗯,点击右上角的家的图案,就能够返回主页,退出图案就是退出登陆啦啦啦。
啰嗦了这么多效果图,如今来说一下代码吧。
项目的入口文件是app.js,可能后续会用redux,因此先引进来再说。github
import React, { Component } from 'react'; import { Provider } from 'react-redux' import './App.css'; import store from './store' import {Router} from './routes/router' class App extends Component { render() { return ( <Provider store={store}> <Router/> </Provider> ); } } export default App;
接着就是路由的配置啦。这里主要用到了react router4进行路由的切换。redux
export const Router = () => ( <BrowserRouter> <div> <Switch> <Route path="/login" component={Login} /> <Redirect from="/" exact to="/login"/>{/*注意redirect转向的地址要先定义好路由*/} <AuthorizedRoute path="/layout" component={Layout} /> <Route component={NoFound}/> </Switch> </div> </BrowserRouter> )
经过这个路由配置,登陆后就会进入layout.js.这个类呢,AuthorizedRoute.js,就是作路由权限控制的。若是说页面须要登陆后才能查看,就用这个组件设置路由信息。里面的内容也很少,就是看一下sessionStorage里面有没有存储用户登陆信息,没有就重定向会登陆页呗。session
import React from 'react' import { Route, Redirect } from 'react-router-dom' class AuthorizedRoute extends React.Component { render() { const { component: Component, ...rest } = this.props const isLogged = sessionStorage.getItem("userName") != null ? true : false; return ( <Route {...rest} render={props => { return isLogged ? <Component {...props} /> : <Redirect to="/login" /> }} /> ) } } export default AuthorizedRoute
好了,那么如今就到了编写layout的内容了。左边是菜单导航栏,右边是tabs,这里都是用antd的组件。为了简化代码,我选择了在router.js里面写好菜单对象类,这里经过递归生成菜单组件。antd
class CreateMenuList extends React.Component { createMenu(data) { const childMenuData = data.child; let childMenu = <div></div>; if (childMenuData && childMenuData.length) { childMenu = childMenuData.map((item) => { return this.createMenu(item); }); return <SubMenu key={data.id} title={data.name}>{childMenu}</SubMenu> } else { return <Menu.Item key={data.id}><NavLink component={data.component} isfull={data.isFullScreen + ''}to={data.path} onClick={this.props.addTabs}>{data.name}</NavLink></Menu.Item> } } render() { return ( <Menu mode="vertical" theme="dark"> { menus.map((item) => { return this.createMenu(item); }) } </Menu> ); } }
Menu.Item里面那么多属性值,都是为了点击的时候去调用tabs的add函数。而后动态生成tabs。isfull是判断是否全屏显示,若是全屏显示就给layout组件最外层添加一个class,在这个class里面写一下样式,隐藏掉菜单导航栏,tabs区域等,是否是有点机智呢。
菜单导航和antd的tabs联系在一块儿,其实就是经过在tasb里面放route,这里能够先在外面判断一下是否是notFound。我又是在route.js里面弄了个变量,以key-value的形式保存指向的组件对象。react-router
export const menuObject = { 'home': Home, 'operation': Operation } const panes = [ { title: '主页', url: '/layout/home', key: 'newTab0', component: 'home', isFullScreen: false }, ]; //递归panes对象,生成tabs <div className="page-content"> <Tabs hideAdd onChange={this.onChange} activeKey={this.state.activeKey} type="editable-card" onEdit={this.onEdit} > {this.state.panes.map( pane => { let route = null; if(menuObject.hasOwnProperty(pane.component)) { route = <Route path={pane.url} exact component={menuObject[pane.component]} />; } else { route = <Route component={NoFound}/>; } return <TabPane tab={pane.title} key={pane.key}> {route} </TabPane> } )} </Tabs> </div>
点击左边导航的时候,调用父视图传过来的addTabs方法。在tabs的删除、修改方法里面,经过props.hsitory去修改路由。整个layout的代码贴出来吧。app
class Layout extends React.Component { constructor(props) { super(props); this.newTabIndex = 1; this.state = { collapsed: false, activeKey: panes[0].key, isFullScreen: false, panes }; } logout = () => { this.props.history.push('/login') } goHome = () => { this.setState({isFullScreen: false}); this.props.history.push('/layout/home') let matchArray = this.state.panes.filter((item) => item.url === '/layout/home'); if(matchArray.length === 0) { let activeKey = `newTab${this.newTabIndex++}`; let panes = this.state.panes; panes.push({title: '主页', url: '/layout/home', component: 'home' , key: activeKey }); this.setState({ panes, activeKey }); } else { this.setState({activeKey: matchArray[0].key}); } } add = (event) => { let url = event.currentTarget.getAttribute('href'); let isFullScreen = event.currentTarget.getAttribute('isfull'); if(isFullScreen === 'true') { this.setState({ isFullScreen: true }) } let matchArray = this.state.panes.filter((item) => item.url === url); if(matchArray.length === 0) { let activeKey = `newTab${this.newTabIndex++}`; let panes = this.state.panes; panes.push({ isFullScreen: isFullScreen, title: event.currentTarget.innerHTML, component: event.currentTarget.getAttribute('component'), url: url, key: activeKey }); this.setState({ panes, activeKey }); } else { this.setState({activeKey: matchArray[0].key}); } } onChange = (activeKey) => { let matchArray = this.state.panes.filter((item) => item.key === activeKey); this.setState({ isFullScreen: matchArray[0].isFullScreen }) this.props.history.push(matchArray[0].url); this.setState({ activeKey }); } onEdit = (targetKey, action) => { this[action](targetKey); } remove = (targetKey) => { let activeKey = this.state.activeKey; if (activeKey === targetKey) { let nextActiveIndex; this.state.panes.forEach((pane, i) => { if (pane.key === targetKey) { nextActiveIndex = i - 1; } }); if(nextActiveIndex > 0) { activeKey = this.state.panes[nextActiveIndex].key; this.props.history.push(this.state.panes[nextActiveIndex].url); } } const panes = this.state.panes.filter(pane => pane.key !== targetKey); this.setState({ panes, activeKey }); } render() { var fulllScreenClass = this.state.isFullScreen ? 'fullScreen' : ''; return <div className={"layout " + fulllScreenClass}> <div className="header"> <span>指标监控管理系统</span> <span> <span><Avatar icon="user" /> 欢迎您 {sessionStorage.getItem('userName')}</span> <Icon type="home" onClick={this.goHome.bind(this)}/> <Icon type="logout" onClick={this.logout.bind(this)}/> </span> </div> <div className={"content "}> <nav className="context-nav"> <CreateMenuList addTabs={this.add}/> </nav> <div className="page-content"> <Tabs hideAdd onChange={this.onChange} activeKey={this.state.activeKey} type="editable-card" onEdit={this.onEdit} > {this.state.panes.map( pane => { let route = null; if(menuObject.hasOwnProperty(pane.component)) { route = <Route path={pane.url} exact component={menuObject[pane.component]} />; } else { route = <Route component={NoFound}/>; } return <TabPane tab={pane.title} key={pane.key}> {route} </TabPane> } )} </Tabs> </div> </div> </div> } componentDidMount() { this.props.history.push('/layout/home') } }
总结一下,设置路由信息,先进入登陆页,登陆页后进去layout.js,在layout.js里面加载菜单,修改tabs的代码让它和路由相关,并把路由信息放在tabs下面从而实现点击左边菜单,tabs下面能显示路由渲染的组件。代码路径:https://github.com/supportlss...dom