上次基于官网教程可以成功访问页面,并且做出列表页面,本次基于一个新的项目构建页面布局
参考网址: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