使用dva+umi+antd构建页面(二)

使用dva+umi+antd构建页面(二)

上次基于官网教程可以成功访问页面,并且做出列表页面,本次基于一个新的项目构建页面布局

布局

参考网址:https://ant.design/components/layout-cn/

在官网选择一种布局选项,覆盖自己src/layouts/index.js中的内容即可

layouts/index.js

import React from 'react'
import { Layout, Menu, Icon } from 'antd';
import styles from './index.css';

const { Header, Sider, Content } = Layout;

class BasicLayout extends React.Component {
  state = {
    collapsed: false,
  };

  toggle = () => {
    this.setState({
      collapsed: !this.state.collapsed,
    });
  }

  render() {
    return (
      <Layout>
        <Sider
          trigger={null}
          collapsible
          collapsed={this.state.collapsed}
        >
          <div className={styles.logo} />
          <Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
            <Menu.Item key="1">
              <Icon type="user" />
              <span>nav 1</span>
            </Menu.Item>
            <Menu.Item key="2">
              <Icon type="video-camera" />
              <span>nav 2</span>
            </Menu.Item>
            <Menu.Item key="3">
              <Icon type="upload" />
              <span>nav 3</span>
            </Menu.Item>
          </Menu>
        </Sider>
        <Layout>
          <Header style={{ background: '#fff', padding: 0 }}>
            <Icon
              className={styles.trigger}
              type={this.state.collapsed ? 'menu-unfold' : 'menu-fold'}
              onClick={this.toggle}
            />
          </Header>
          <Content style={{
            margin: '24px 16px', padding: 24, background: '#fff', minHeight: 280,
          }}
          >
            Content
          </Content>
        </Layout>
      </Layout>
    );
  }
}

export default BasicLayout

layouts/index.css

.trigger {
  font-size: 18px;
  line-height: 64px;
  padding: 0 24px;
  cursor: pointer;
  transition: color .3s;
}

.trigger:hover {
  color: #1890ff;
}

.logo {
  height: 32px;
  background: rgba(255,255,255,.2);
  margin: 16px;
}

效果

现在发现元素高度好像有问题,直接在src/global.css中加入以下代码

.ant-layout{
  height: 100%;
}

然后刷新发现高度已经可以占满啦,此时就可以看见我们基础的布局了

组件化

光这样布局是不行的,我们要将其组件化大概分为以下几部分:

接下来就进行组件化,在src目录新建components/laout目录并且新建以下几个文件Header.js、Sider.js、Menu.js、Content.js

./src/components
├── Content.js
├── Header.js
├── Menu.js
└── Sider.js

构建各个组件:

Header.js

import React, {Component} from "react";
import {Layout, Icon} from 'antd';

import styles from "@/layouts/index.css";

const Header = Layout.Header

class MyHeader extends Component {

  render() {
    const {
      collapsed,
      onCollapseChange
    } = this.props
    return (
      <Header style={{background: '#fff', padding: 0}}>
        <Icon
          className={styles.trigger}
          type={collapsed ? 'menu-unfold' : 'menu-fold'}
          onClick={onCollapseChange.bind(this, !collapsed)}
        />
      </Header>
    )
  }
}

export default MyHeader

Sider.js

import React, {Component} from "react";
import {Layout} from 'antd'
import styles from "@/layouts/index.css";

const {Sider} = Layout;

class MySider extends Component {
  render() {
    const {collapsed, children} = this.props
    return (
      <Sider
        trigger={null}
        collapsible
        collapsed={collapsed}
      >
        <div className={styles.logo}/>
        {children}
      </Sider>
    );
  }
}

export default MySider

Menu.js

import React, {Component, Fragment} from "react";
import {Icon, Menu} from "antd";
import withRouter from 'umi/withRouter'

const {SubMenu} = Menu

class MyMenu extends Component {

  constructor() {
    super()
    this.state = {
      defaultSelect: ['1']
    }
  }


  generateMenus = data => {
    return data.map(item => {
      if (item.children) {
        return (
          <SubMenu
            key={item.id}
            title={
              <Fragment>
                {item.icon && <Icon type={item.icon}/>}
                <span>{item.name}</span>
              </Fragment>
            }
          >
            {this.generateMenus(item.children)}
          </SubMenu>
        )
      } else {
        return (
          <Menu.Item key={item.id} url={item.url} name={item.name}>
            {item.icon && <Icon type={item.icon}/>}
            <span>{item.name}</span>
          </Menu.Item>
        )
      }
    })
  }

  render() {
    const {
      theme,
      menus,
      handelMenuClick
    } = this.props
    return (
      <Menu theme={theme} mode="inline" defaultSelectedKeys={this.state.defaultSelect}
            onClick={handelMenuClick.bind(this)}>
        {this.generateMenus(menus)}
      </Menu>
    );
  }

}

export default withRouter(MyMenu)

Content.js

import React,{Component} from "react";
import {Layout} from "antd/lib/index";
const {Content} = Layout;

class MyContent extends Component{
  render() {
    return (
      <Content>
        {this.props.children}
      </Content>
    );
  }
}
export default MyContent

组件全部构建完毕后将src/layouts/index.js的内容重新编写,并且connect到reducer

import React from 'react'
import {Layout} from 'antd';
import Header from '../components/layout/Header'
import MyContent from '../components/layout/Content'
import MySider from '../components/layout/Sider'
import MyMenu from '../components/layout/Menu'

import {connect} from 'dva';

class BasicLayout extends React.Component {
  onCollapseChange = collapsed => {
    this.props.dispatch({
      type: 'app/handleCollapseChange',
      payload: {collapsed},
    })
  }

  handelMenuClick(element) {
    //路由到相关页面
    this.props.history.push(element.item.props.url)
  }

  render() {
    const {app, children} = this.props
    const {menus, theme, collapsed} = app
    const {handelMenuClick, onCollapseChange} = this
    const menuProps = {
      theme,
      menus,
      children,
      handelMenuClick,
      onCollapseChange,
    }
    return (
      <Layout>
        <MySider collapsed={collapsed}>
          <MyMenu {...menuProps}/>
        </MySider>
        <Layout>
          <Header collapsed={collapsed} onCollapseChange={onCollapseChange}/>
          <MyContent>
            {this.props.children}
          </MyContent>
        </Layout>
      </Layout>
    );
  }
}

export default connect((({app}) => ({app})))(BasicLayout)

然后在src/models中新建app.js此文件相当于redux中的reducer,dva可以视作它的增强版,具体dva相关内容可以查阅dva文档:https://dvajs.com/guide/。文件中的数据暂时写死只是测试使用,后续会加入mock

export default {
  namespace: 'app',
  state: {
    collapsed: false,
    panes: [
      {title: `dashboard`, content: '', key: '1', closable: false, url: '/dashboard'},
    ],
    theme: 'dark',
    menus: [{
      id: '1',
      name: 'dashboard',
      icon: 'dashboard',
      url: '/dashboard',
    }, {
      id: '2',
      name: '用户管理',
      icon: 'user',
      url: '1',
      children: [{
        id: '3',
        name: '用户管理',
        icon: 'user',
        url: '/user',
      },]
    }, {
      id: '3',
      name: '用户管理',
      icon: 'user',
      url: '1',
      children: [{
        id: '4',
        name: '用户管理',
        icon: 'user',
        url: '/user',
      },]
    }],
  },
  subscriptions: {},
  effects: {},
  reducers: {
    handleCollapseChange(state, {payload}) {
      return {
        ...state,
        ...payload,
      }
    },
  },
}

此时我们点击菜单会404,是因为我们还没有定义测试菜单中的路由,只需要新建相关路由就可以访问了,前一篇提到可以使用命令行新建路由:

npx umi g page user

   create src/pages/user.js
   create src/pages/user.css
✔  success
npx umi g page dashboard

   create src/pages/dashboard.js
   create src/pages/dashboard.css
✔  success

路由新建后点击菜单如下:


本次代码参考GitHub

你可能感兴趣的:(react)