11

先上一下效果图:
登录页:

clipboard.png
任意输入账号密码(因为还没有后台,不想做啥校验了)
登录后是这样子的

clipboard.png
很传统有没有,左边是菜单导航,右边是tabs区域。为了让系统显得高大上,这里还做了根据路由信息,判断是否全屏显示,来,看看我们的全屏。

clipboard.png

嗯,点击右上角的家的图案,就可以返回主页,退出图案就是退出登录啦啦啦。
啰嗦了这么多效果图,现在来讲一下代码吧。
项目的入口文件是app.js,可能后续会用redux,所以先引进来再说。

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进行路由的切换。

    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里面有没有存储用户登录信息,没有就重定向会登录页呗。

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里面写好菜单对象类,这里通过递归生成菜单组件。

    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的形式保存指向的组件对象。

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的代码贴出来吧。

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" />&nbsp;&nbsp;欢迎您&nbsp;{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...


supportlss
230 声望16 粉丝

引用和评论

1 篇内容引用
0 条评论