使用dva+umi+antd构建页面(三)
使用dva+umi+antd构建页面(三)
布局做好以后就可以开始构建整个系统路由及其对应的路由组件,但是有时候我们的需求是要求路由的页面在Tabs标签页中的,然后点击各个标签页可以进行切换,接下来就来探讨一下这种布局的实现。
Tabs组件
-
添加Tabs组件,在src/components/layout中添加Tabs.js组件,首先在官网https://ant.design/components/tabs-cn/复制一个Tabs组件如下:
import React from 'react' import { Tabs, Button } from 'antd'; const TabPane = Tabs.TabPane; class Mytab extends React.Component { constructor(props) { super(props); this.newTabIndex = 0; const panes = [ { title: 'Tab 1', content: 'Content of Tab Pane 1', key: '1' }, { title: 'Tab 2', content: 'Content of Tab Pane 2', key: '2' }, ]; this.state = { activeKey: panes[0].key, panes, }; } onChange = (activeKey) => { this.setState({ activeKey }); } onEdit = (targetKey, action) => { this[action](targetKey); } add = () => { const panes = this.state.panes; const activeKey = `newTab${this.newTabIndex++}`; panes.push({ title: 'New Tab', content: 'New Tab Pane', key: activeKey }); this.setState({ panes, activeKey }); } remove = (targetKey) => { let activeKey = this.state.activeKey; let lastIndex; this.state.panes.forEach((pane, i) => { if (pane.key === targetKey) { lastIndex = i - 1; } }); const panes = this.state.panes.filter(pane => pane.key !== targetKey); if (panes.length && activeKey === targetKey) { if (lastIndex >= 0) { activeKey = panes[lastIndex].key; } else { activeKey = panes[0].key; } } this.setState({ panes, activeKey }); } render() { return ( <div> <div style={{ marginBottom: 16 }}> <Button onClick={this.add}>ADD</Button> </div> <Tabs hideAdd onChange={this.onChange} activeKey={this.state.activeKey} type="editable-card" onEdit={this.onEdit} > {this.state.panes.map(pane => <TabPane tab={pane.title} key={pane.key}>{pane.content}</TabPane>)} </Tabs> </div> ); } } export default Mytab
然后将其替换src/layouts/index.js中的Content
rander函数如下:
return ( <Layout> <MySider collapsed={collapsed}> <MyMenu {...menuProps}/> </MySider> <Layout> <Header collapsed={collapsed} onCollapseChange={onCollapseChange}/> <MyTab> {this.props.children} </MyTab> </Layout> </Layout> );
此时可以看到页面有了tab组件,并可以正常添加删除
-
状态提升
接下来需要将tab组件状态提升,由dva管理,那就先改造tab组件
import React from 'react' import {Tabs,Button} from 'antd'; import PropTypes from "prop-types"; const TabPane = Tabs.TabPane; class MyTab extends React.Component { onEdit = (targetKey, action) => { this[action](targetKey); } remove = (targetKey) => { const {onPaneRemove} = this.props onPaneRemove(targetKey) } render() { const { panes, activeKey, onPaneChange, onPaneAdd, } = this.props return ( <div> <div style={{ marginBottom: 16 }}> <Button onClick={onPaneAdd}>ADD</Button> </div> <Tabs hideAdd onChange={onPaneChange.bind(this)} activeKey={activeKey} type="editable-card" onEdit={this.onEdit} > {panes.map(pane => <TabPane tab={pane.title} key={pane.key} closable={pane.closable}> {pane.content} </TabPane>)} </Tabs> </div> ); } } MyTab.propTypes = { panes: PropTypes.array.isRequired, activeKey: PropTypes.string.isRequired, onPaneChange: PropTypes.func.isRequired, } export default MyTab
然后修改src/layouts/index.js
import React from 'react' import {Layout} from 'antd'; import Header from '../components/layout/Header' import MySider from '../components/layout/Sider' import MyMenu from '../components/layout/Menu' import MyTab from '../components/layout/Tabs' 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) } onPaneAdd = () => { //todo something } onPaneChange = () => { //todo something } onPaneRemove = () => { //todo something } render() { const {app, children} = this.props const {menus, theme, collapsed,panes,activeKey,} = app const {handelMenuClick, onCollapseChange,onPaneAdd,onPaneChange,onPaneRemove} = this const menuProps = { theme, menus, children, handelMenuClick, onCollapseChange, } const tapProps = { panes, children, activeKey, onPaneAdd, onPaneChange, onPaneRemove, } return ( <Layout> <MySider collapsed={collapsed}> <MyMenu {...menuProps}/> </MySider> <Layout> <Header collapsed={collapsed} onCollapseChange={onCollapseChange}/> <MyTab {...tapProps}> {this.props.children} </MyTab> </Layout> </Layout> ); } } export default connect((({app}) => ({app})))(BasicLayout)
然后修改src/models/app.js
export default { namespace: 'app', state: { collapsed: false, activeKey: '1', 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: { updateState(state, {payload}) { return { ...state, ...payload, } }, handleCollapseChange(state, {payload}) { return { ...state, ...payload, } }, }, }
最后完成src/layouts/index.js中几个控制状态的函数
onPaneAdd = () => { const {app, dispatch} = this.props const {panes,} = app let newPane = {title: 'new Tab', content: '', key: panes.length + '1',} let flag = false let newPanes = panes.map(item => { if (newPane.key === item.key) { flag = true return {...item, ...newPane} } return item }) dispatch({ type: 'app/updateState', payload: { panes: flag ? newPanes : [...newPanes, newPane], activeKey: newPane.key, }, }) } onPaneChange = targetKey => { const {dispatch,} = this.props dispatch({ type: 'app/updateState', payload: { activeKey: targetKey }, }) } onPaneRemove = targetKey => { const {dispatch,} = this.props const payload = this._removeTap(targetKey) dispatch({ type: 'app/updateState', payload: payload, }) } _removeTap = (targetKey) => { const {panes, activeKey} = this.props.app let newActiveKey = activeKey let lastIndex; panes.forEach((pane, i) => { if (pane.key === targetKey) { lastIndex = i - 1; } }); const newPanes = panes.filter(pane => pane.key !== targetKey); if (newPanes.length && activeKey === targetKey) { if (lastIndex >= 0) { newActiveKey = newPanes[lastIndex].key; } else { newActiveKey = newPanes[0].key; } } return { panes: newPanes, activeKey: newActiveKey } }
这样就可以添加与删除了,但是我们需要的是点击菜单添加,并且显示该页面,所以再稍微改造一下src/layouts/index.js中的函数,并且把Tab组件中的添加按钮删掉
index.js
//菜单点击事件 handelMenuClick = element => { let newPane = {title: element.item.props.name, content: '', key: element.key, url: element.item.props.url} this.onPaneAdd(newPane) } //打开新tab onPaneAdd = (newPane) => { const {app, dispatch, history,} = this.props const {panes,} = app let flag = false //路由到相关页面 history.push(newPane.url) setTimeout(()=>{ newPane.content = this.props.children let newPanes = panes.map(item => { if (newPane.key === item.key) { flag = true return {...item, ...newPane} } return item }) dispatch({ type: 'app/updateState', payload: { panes: flag ? newPanes : [...newPanes, newPane], activeKey: newPane.key, }, }) },100) }
Tabs.js
//初始化加载第一个tab componentWillMount(){ const {panes, onPaneAdd,} = this.props if (panes.length === 1) { const pane = panes[0] onPaneAdd(pane) } }
这样就可以实现tab的添加与删除了: