Browse Source
* Increase the sliding menu * Add a simple animation * update mobile menu * update * update * update * rebase master * recovery import/firstpull/516/head
committed by
ddcat1115
7 changed files with 272 additions and 165 deletions
@ -0,0 +1,162 @@ |
|||
import React, { PureComponent } from 'react'; |
|||
import { Layout, Menu, Icon } from 'antd'; |
|||
import { Link } from 'dva/router'; |
|||
import logo from '../../assets/logo.svg'; |
|||
import styles from './index.less'; |
|||
import { getMenuData } from '../../common/menu'; |
|||
|
|||
const { Sider } = Layout; |
|||
const { SubMenu } = Menu; |
|||
|
|||
export default class SiderMenu extends PureComponent { |
|||
constructor(props) { |
|||
super(props); |
|||
this.menus = getMenuData(); |
|||
this.state = { |
|||
openKeys: this.getDefaultCollapsedSubMenus(props), |
|||
}; |
|||
} |
|||
getDefaultCollapsedSubMenus(props) { |
|||
const { location: { pathname } } = props || this.props; |
|||
const snippets = pathname.split('/').slice(1, -1); |
|||
const currentPathSnippets = snippets.map((item, index) => { |
|||
const arr = snippets.filter((_, i) => i <= index); |
|||
return arr.join('/'); |
|||
}); |
|||
let currentMenuSelectedKeys = []; |
|||
currentPathSnippets.forEach((item) => { |
|||
currentMenuSelectedKeys = currentMenuSelectedKeys.concat(this.getSelectedMenuKeys(item)); |
|||
}); |
|||
if (currentMenuSelectedKeys.length === 0) { |
|||
return ['dashboard']; |
|||
} |
|||
return currentMenuSelectedKeys; |
|||
} |
|||
getFlatMenuKeys(menus) { |
|||
let keys = []; |
|||
menus.forEach((item) => { |
|||
if (item.children) { |
|||
keys.push(item.path); |
|||
keys = keys.concat(this.getFlatMenuKeys(item.children)); |
|||
} else { |
|||
keys.push(item.path); |
|||
} |
|||
}); |
|||
return keys; |
|||
} |
|||
getSelectedMenuKeys = (path) => { |
|||
const flatMenuKeys = this.getFlatMenuKeys(this.menus); |
|||
|
|||
if (flatMenuKeys.indexOf(path.replace(/^\//, '')) > -1) { |
|||
return [path.replace(/^\//, '')]; |
|||
} |
|||
if (flatMenuKeys.indexOf(path.replace(/^\//, '').replace(/\/$/, '')) > -1) { |
|||
return [path.replace(/^\//, '').replace(/\/$/, '')]; |
|||
} |
|||
return flatMenuKeys.filter((item) => { |
|||
const itemRegExpStr = `^${item.replace(/:[\w-]+/g, '[\\w-]+')}$`; |
|||
const itemRegExp = new RegExp(itemRegExpStr); |
|||
return itemRegExp.test(path.replace(/^\//, '')); |
|||
}); |
|||
} |
|||
getNavMenuItems(menusData) { |
|||
if (!menusData) { |
|||
return []; |
|||
} |
|||
return menusData.map((item) => { |
|||
if (!item.name) { |
|||
return null; |
|||
} |
|||
let itemPath; |
|||
if (item.path && item.path.indexOf('http') === 0) { |
|||
itemPath = item.path; |
|||
} else { |
|||
itemPath = `/${item.path || ''}`.replace(/\/+/g, '/'); |
|||
} |
|||
if (item.children && item.children.some(child => child.name)) { |
|||
return item.hideInMenu ? null : |
|||
( |
|||
<SubMenu |
|||
title={ |
|||
item.icon ? ( |
|||
<span> |
|||
<Icon type={item.icon} /> |
|||
<span>{item.name}</span> |
|||
</span> |
|||
) : item.name |
|||
} |
|||
key={item.key || item.path} |
|||
> |
|||
{this.getNavMenuItems(item.children)} |
|||
</SubMenu> |
|||
); |
|||
} |
|||
const icon = item.icon && <Icon type={item.icon} />; |
|||
return item.hideInMenu ? null : |
|||
( |
|||
<Menu.Item key={item.key || item.path}> |
|||
{ |
|||
/^https?:\/\//.test(itemPath) ? ( |
|||
<a href={itemPath} target={item.target}> |
|||
{icon}<span>{item.name}</span> |
|||
</a> |
|||
) : ( |
|||
<Link |
|||
to={itemPath} |
|||
target={item.target} |
|||
replace={itemPath === this.props.location.pathname} |
|||
onClick={this.props.isMobile && (() => { this.props.onCollapse(true); })} |
|||
> |
|||
{icon}<span>{item.name}</span> |
|||
</Link> |
|||
) |
|||
} |
|||
</Menu.Item> |
|||
); |
|||
}); |
|||
} |
|||
handleOpenChange = (openKeys) => { |
|||
const lastOpenKey = openKeys[openKeys.length - 1]; |
|||
const isMainMenu = this.menus.some( |
|||
item => lastOpenKey && (item.key === lastOpenKey || item.path === lastOpenKey) |
|||
); |
|||
this.setState({ |
|||
openKeys: isMainMenu ? [lastOpenKey] : [...openKeys], |
|||
}); |
|||
} |
|||
render() { |
|||
const { collapsed, location: { pathname }, onCollapse } = this.props; |
|||
// Don't show popup menu when it is been collapsed
|
|||
const menuProps = collapsed ? {} : { |
|||
openKeys: this.state.openKeys, |
|||
}; |
|||
return ( |
|||
<Sider |
|||
trigger={null} |
|||
collapsible |
|||
collapsed={collapsed} |
|||
breakpoint="md" |
|||
onCollapse={onCollapse} |
|||
width={256} |
|||
className={styles.sider} |
|||
> |
|||
<div className={styles.logo}> |
|||
<Link to="/"> |
|||
<img src={logo} alt="logo" /> |
|||
<h1>Ant Design Pro</h1> |
|||
</Link> |
|||
</div> |
|||
<Menu |
|||
theme="dark" |
|||
mode="inline" |
|||
{...menuProps} |
|||
onOpenChange={this.handleOpenChange} |
|||
selectedKeys={this.getSelectedMenuKeys(pathname)} |
|||
style={{ padding: '16px 0', width: '100%' }} |
|||
> |
|||
{this.getNavMenuItems(this.menus)} |
|||
</Menu> |
|||
</Sider> |
|||
); |
|||
} |
|||
} |
|||
@ -1,167 +1,40 @@ |
|||
import 'rc-drawer-menu/assets/index.css'; |
|||
import React, { PureComponent } from 'react'; |
|||
import { Layout, Menu, Icon } from 'antd'; |
|||
import { Link } from 'dva/router'; |
|||
import logo from '../../assets/logo.svg'; |
|||
import styles from './index.less'; |
|||
import { getMenuData } from '../../common/menu'; |
|||
import DrawerMenu from 'rc-drawer-menu'; |
|||
import SiderMenu from './SiderMenu'; |
|||
|
|||
const { Sider } = Layout; |
|||
const { SubMenu } = Menu; |
|||
|
|||
export default class SiderMenu extends PureComponent { |
|||
constructor(props) { |
|||
super(props); |
|||
this.menus = getMenuData(); |
|||
this.state = { |
|||
openKeys: this.getDefaultCollapsedSubMenus(props), |
|||
}; |
|||
} |
|||
export default class Index extends PureComponent { |
|||
onCollapse = (collapsed) => { |
|||
this.props.dispatch({ |
|||
type: 'global/changeLayoutCollapsed', |
|||
payload: collapsed, |
|||
}); |
|||
} |
|||
getDefaultCollapsedSubMenus(props) { |
|||
const { location: { pathname } } = props || this.props; |
|||
const snippets = pathname.split('/').slice(1, -1); |
|||
const currentPathSnippets = snippets.map((item, index) => { |
|||
const arr = snippets.filter((_, i) => i <= index); |
|||
return arr.join('/'); |
|||
}); |
|||
let currentMenuSelectedKeys = []; |
|||
currentPathSnippets.forEach((item) => { |
|||
currentMenuSelectedKeys = currentMenuSelectedKeys.concat(this.getSelectedMenuKeys(item)); |
|||
}); |
|||
if (currentMenuSelectedKeys.length === 0) { |
|||
return ['dashboard']; |
|||
} |
|||
return currentMenuSelectedKeys; |
|||
} |
|||
getFlatMenuKeys(menus) { |
|||
let keys = []; |
|||
menus.forEach((item) => { |
|||
if (item.children) { |
|||
keys.push(item.path); |
|||
keys = keys.concat(this.getFlatMenuKeys(item.children)); |
|||
} else { |
|||
keys.push(item.path); |
|||
} |
|||
}); |
|||
return keys; |
|||
} |
|||
getSelectedMenuKeys = (path) => { |
|||
const flatMenuKeys = this.getFlatMenuKeys(this.menus); |
|||
|
|||
if (flatMenuKeys.indexOf(path.replace(/^\//, '')) > -1) { |
|||
return [path.replace(/^\//, '')]; |
|||
} |
|||
if (flatMenuKeys.indexOf(path.replace(/^\//, '').replace(/\/$/, '')) > -1) { |
|||
return [path.replace(/^\//, '').replace(/\/$/, '')]; |
|||
} |
|||
return flatMenuKeys.filter((item) => { |
|||
const itemRegExpStr = `^${item.replace(/:[\w-]+/g, '[\\w-]+')}$`; |
|||
const itemRegExp = new RegExp(itemRegExpStr); |
|||
return itemRegExp.test(path.replace(/^\//, '')); |
|||
}); |
|||
} |
|||
getNavMenuItems(menusData) { |
|||
if (!menusData) { |
|||
return []; |
|||
} |
|||
return menusData.map((item) => { |
|||
if (!item.name) { |
|||
return null; |
|||
} |
|||
let itemPath; |
|||
if (item.path && item.path.indexOf('http') === 0) { |
|||
itemPath = item.path; |
|||
} else { |
|||
itemPath = `/${item.path || ''}`.replace(/\/+/g, '/'); |
|||
} |
|||
if (item.children && item.children.some(child => child.name)) { |
|||
return item.hideInMenu ? null : |
|||
( |
|||
<SubMenu |
|||
title={ |
|||
item.icon ? ( |
|||
<span> |
|||
<Icon type={item.icon} /> |
|||
<span>{item.name}</span> |
|||
</span> |
|||
) : item.name |
|||
} |
|||
key={item.key || item.path} |
|||
> |
|||
{this.getNavMenuItems(item.children)} |
|||
</SubMenu> |
|||
); |
|||
} |
|||
const icon = item.icon && <Icon type={item.icon} />; |
|||
return item.hideInMenu ? null : |
|||
( |
|||
<Menu.Item key={item.key || item.path}> |
|||
{ |
|||
/^https?:\/\//.test(itemPath) ? ( |
|||
<a href={itemPath} target={item.target}> |
|||
{icon}<span>{item.name}</span> |
|||
</a> |
|||
) : ( |
|||
<Link |
|||
to={itemPath} |
|||
target={item.target} |
|||
replace={itemPath === this.props.location.pathname} |
|||
> |
|||
{icon}<span>{item.name}</span> |
|||
</Link> |
|||
) |
|||
} |
|||
</Menu.Item> |
|||
); |
|||
}); |
|||
} |
|||
handleOpenChange = (openKeys) => { |
|||
const lastOpenKey = openKeys[openKeys.length - 1]; |
|||
const isMainMenu = this.menus.some( |
|||
item => lastOpenKey && (item.key === lastOpenKey || item.path === lastOpenKey) |
|||
); |
|||
this.setState({ |
|||
openKeys: isMainMenu ? [lastOpenKey] : [...openKeys], |
|||
}); |
|||
} |
|||
render() { |
|||
const { collapsed, location: { pathname } } = this.props; |
|||
// Don't show popup menu when it is been collapsed
|
|||
const menuProps = collapsed ? {} : { |
|||
openKeys: this.state.openKeys, |
|||
}; |
|||
return ( |
|||
<Sider |
|||
trigger={null} |
|||
collapsible |
|||
collapsed={collapsed} |
|||
breakpoint="md" |
|||
onCollapse={this.onCollapse} |
|||
width={256} |
|||
className={styles.sider} |
|||
const { collapsed, isMobile } = this.props; |
|||
return isMobile ? ( |
|||
<DrawerMenu |
|||
parent={null} |
|||
level={null} |
|||
iconChild={null} |
|||
open={!collapsed} |
|||
onMaskClick={() => { this.onCollapse(true); }} |
|||
width="256px" |
|||
> |
|||
<div className={styles.logo}> |
|||
<Link to="/"> |
|||
<img src={logo} alt="logo" /> |
|||
<h1>Ant Design Pro</h1> |
|||
</Link> |
|||
</div> |
|||
<Menu |
|||
theme="dark" |
|||
mode="inline" |
|||
{...menuProps} |
|||
onOpenChange={this.handleOpenChange} |
|||
selectedKeys={this.getSelectedMenuKeys(pathname)} |
|||
style={{ padding: '16px 0', width: '100%' }} |
|||
> |
|||
{this.getNavMenuItems(this.menus)} |
|||
</Menu> |
|||
</Sider> |
|||
<SiderMenu |
|||
{...this.props} |
|||
isMobile={isMobile} |
|||
onCollapse={this.onCollapse} |
|||
collapsed={isMobile ? false : collapsed} |
|||
/> |
|||
</DrawerMenu> |
|||
) : ( |
|||
<SiderMenu |
|||
{...this.props} |
|||
isMobile={isMobile} |
|||
onCollapse={this.onCollapse} |
|||
/> |
|||
); |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue