Merge pull request #6 from ElemeFE/menu-fixed

Menu fixed
pull/11/head
杨奕 2016-09-05 00:34:56 -05:00 committed by GitHub
commit 0080558807
8 changed files with 307 additions and 175 deletions

View File

@ -60,15 +60,25 @@
::: demo
```html
<el-menu theme="dark" default-active="1" class="el-menu-demo" @select="handleselect">
<el-menu theme="dark" default-active="1" class="el-menu-demo" mode="horizontal" @select="handleselect">
<el-menu-item index="1">处理中心</el-menu-item>
<el-menu-item index="2">我的工作台</el-menu-item>
<el-submenu index="2">
<template slot="title">我的工作台</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
<el-menu-item index="2-3">选项3</el-menu-item>
</el-submenu>
<el-menu-item index="3">订单管理</el-menu-item>
</el-menu>
<div class="line"></div>
<el-menu default-active="1" class="el-menu-demo" @select="handleselect">
<el-menu default-active="1" class="el-menu-demo" mode="horizontal" @select="handleselect">
<el-menu-item index="1">处理中心</el-menu-item>
<el-menu-item index="2">我的工作台</el-menu-item>
<el-submenu index="2">
<template slot="title">我的工作台</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
<el-menu-item index="2-3">选项3</el-menu-item>
</el-submenu>
<el-menu-item index="3">订单管理</el-menu-item>
</el-menu>
```
@ -83,12 +93,16 @@
<el-row class="tac">
<el-col :span="8">
<h5>带 icon</h5>
<el-menu mode="vertical" default-active="2" class="el-menu-vertical-demo" @open="handleopen" @close="handleclose">
<el-menu default-active="2" class="el-menu-vertical-demo" @open="handleopen" @close="handleclose">
<el-submenu index="1">
<template slot="title"><i class="el-icon-message"></i>导航一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
<el-menu-item index="1-3">选项3</el-menu-item>
<el-menu-item-group title="分组一">
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-menu-item index="2"><i class="el-icon-menu"></i>导航二</el-menu-item>
<el-menu-item index="3"><i class="el-icon-setting"></i>导航三</el-menu-item>
@ -96,12 +110,16 @@
</el-col>
<el-col :span="8">
<h5>不带 icon</h5>
<el-menu mode="vertical" default-active="2" class="el-menu-vertical-demo" @open="handleopen" @close="handleclose">
<el-menu default-active="2" class="el-menu-vertical-demo" @open="handleopen" @close="handleclose" theme="dark">
<el-submenu index="1">
<template slot="title">导航一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
<el-menu-item index="1-3">选项3</el-menu-item>
<el-menu-item-group title="分组一">
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-menu-item index="2">导航二</el-menu-item>
<el-menu-item index="3">导航三</el-menu-item>
@ -137,29 +155,29 @@
### Menu Attribute
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- |
| mode | 模式 | string | horizontal,vertical | horizontal |
| mode | 模式 | string | horizontal,vertical | vertical |
| theme | 主题色 | string | light,dark | light |
| default-active | 当前激活菜单的 key | string | — | — |
| default-active | 当前激活菜单的 index | string | — | — |
| default-openeds | 当前打开的submenu的 key 数组 | Array | — | — |
| unique-opend | 是否只保持一个子菜单的展开 | boolean | — | false |
| router | 是否使用 vue-router 的模式,启用该模式会在激活导航时以 key 作为 path 进行路由跳转 | boolean | — | false |
| router | 是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转 | boolean | — | false |
### Menu Events
| 事件名称 | 说明 | 回调参数 |
|---------- |-------- |---------- |
| select | 菜单激活回调 | key: 选中菜单项的 keyPath: 选中菜单项的 key path |
| open | SubMenu 展开的回调 | key: 打开的 subMenu 的 key keyPath: 打开的 subMenu 的 key path |
| close | SubMenu 收起的回调 | key: 收起的 subMenu 的 key keyPath: 收起的 subMenu 的 key path |
| select | 菜单激活回调 | index: 选中菜单项的 indexPath: 选中菜单项的 index path |
| open | SubMenu 展开的回调 | index: 打开的 subMenu 的 index indexPath: 打开的 subMenu 的 index path |
| close | SubMenu 收起的回调 | index: 收起的 subMenu 的 index indexPath: 收起的 subMenu 的 index path |
### SubMenu Attribute
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- |
| key | 唯一标志 | string | — | — |
| index | 唯一标志 | string | — | — |
### Menu-Item Attribute
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- |
| key | 唯一标志 | string | — | — |
| index | 唯一标志 | string | — | — |
### Menu-Group Attribute
| 参数 | 说明 | 类型 | 可选值 | 默认值 |

View File

@ -30,9 +30,9 @@
type="checkbox"
:disabled="disabled">
</span>
<span class="el-checkbox__label">
<span class="el-checkbox__label" v-if="$slots.default || label">
<slot></slot>
<template v-if="!$slots || !$slots.default">{{label}}</template>
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>

View File

@ -12,14 +12,9 @@
},
data() {
return {
paddingLeft: 15
paddingLeft: 20
};
},
computed: {
activeIndex() {
return this.$parent.activeIndex;
}
},
methods: {
initPadding() {
var parent = this.$parent;

View File

@ -1,13 +1,9 @@
<script>
import emitter from 'main/mixins/emitter';
module.exports = {
name: 'el-menu-item',
componentName: 'menu-item',
mixins: [emitter],
props: {
index: {
type: String,
@ -20,21 +16,33 @@
},
computed: {
indexPath() {
return this.$parent.indexPath ? this.$parent.indexPath.concat(this.index) : [this.index];
var path = [this.index];
var parent = this.$parent;
while (parent.$options._componentTag !== 'el-menu') {
if (parent.index) {
path.unshift(parent.index);
}
parent = parent.$parent;
}
return path;
},
activeIndex() {
return this.$parent.activeIndex;
rootMenu() {
var parent = this.$parent;
while (parent.$options._componentTag !== 'el-menu') {
parent = parent.$parent;
}
return parent;
},
active() {
return this.index === this.activeIndex;
return this.index === this.rootMenu.activeIndex;
}
},
methods: {
handleClick() {
if (!this.active) {
this.dispatch('menu', 'select-key', [this.index, this.indexPath]);
}
this.rootMenu.handleSelect(this.index, this.indexPath);
}
},
mounted() {
}
};
</script>
@ -47,8 +55,5 @@
'is-disabled': disabled
}">
<slot></slot>
<transition name="fade" mode="out-in">
<span class="el-menu-item__bar" v-if="active"></span>
</transition>
</li>
</template>

View File

@ -1,7 +1,7 @@
<template>
<ul class="el-menu"
:class="{
'el-menu--vertical': mode === 'vertical',
'el-menu--horizontal': mode === 'horizontal',
'el-menu--dark': theme === 'dark'
}"
>
@ -21,7 +21,7 @@
props: {
mode: {
type: String,
default: ''
default: 'vertical'
},
defaultActive: {
type: String,
@ -43,31 +43,30 @@
data() {
return {
activeIndex: this.defaultActive,
openedMenus: this.defaultOpeneds
openedMenus: this.defaultOpeneds.slice(0)
};
},
methods: {
handleMenuExpand(key, keyPath) {
this.openedMenus.push(key);
handleMenuExpand(index, indexPath) {
if (this.uniqueOpend) {
this.broadcast('submenu', 'close-menu', keyPath);
this.openedMenus = this.openedMenus.filter((key) => {
return keyPath.indexOf(key) !== -1;
this.broadcast('submenu', 'close-menu', indexPath);
this.openedMenus = this.openedMenus.filter((index) => {
return indexPath.indexOf(index) !== -1;
});
}
this.$emit('open', key, keyPath);
this.$emit('open', index, indexPath);
},
handleMenuCollapse(key, keyPath) {
this.openedMenus.splice(this.openedMenus.indexOf(key), 1);
this.$emit('close', key, keyPath);
handleMenuCollapse(index, indexPath) {
this.openedMenus.splice(this.openedMenus.indexOf(index), 1);
this.$emit('close', index, indexPath);
},
handleSelect(key, keyPath) {
this.activeIndex = key;
this.$emit('select', key, keyPath);
handleSelect(index, indexPath) {
this.activeIndex = index;
this.$emit('select', index, indexPath);
this.broadcast('submenu', 'select', [index, indexPath]);
if (this.router) {
this.$router.push(key);
this.$router.push(index);
}
}
},
@ -75,7 +74,6 @@
this.broadcast('submenu', 'open-menu', this.openedMenus);
this.$on('expand-menu', this.handleMenuExpand);
this.$on('collapse-menu', this.handleMenuCollapse);
this.$on('select-key', this.handleSelect);
}
};
</script>

View File

@ -16,25 +16,62 @@
},
data() {
return {
opened: false
opened: false,
timeout: null,
active: false
};
},
computed: {
indexPath() {
return this.$parent.indexPath ? this.$parent.indexPath.concat(this.index) : [this.index];
var path = [this.index];
var parent = this.$parent;
while (parent.$options._componentTag !== 'el-menu') {
if (parent.index) {
path.unshift(parent.index);
}
parent = parent.$parent;
}
return path;
},
activeIndex() {
return this.$parent.activeIndex;
rootMenu() {
var parent = this.$parent;
while (parent.$options._componentTag !== 'el-menu') {
parent = parent.$parent;
}
return parent;
},
mode() {
return this.rootMenu.mode;
}
},
methods: {
handleClick() {
if (!this.opened) {
this.dispatch('menu', 'expand-menu', [this.index, this.indexPath]);
this.opened = true;
} else {
this.dispatch('menu', 'collapse-menu', [this.index, this.indexPath]);
this.opened = false;
if (this.mode === 'vertical') {
if (!this.opened) {
this.dispatch('menu', 'expand-menu', [this.index, this.indexPath]);
this.opened = true;
} else {
this.dispatch('menu', 'collapse-menu', [this.index, this.indexPath]);
this.opened = false;
}
}
},
handleMouseenter() {
if (this.mode === 'horizontal') {
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.dispatch('menu', 'expand-menu', [this.index, this.indexPath]);
this.opened = true;
}, 300);
}
},
handleMouseleave() {
if (this.mode === 'horizontal') {
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.dispatch('menu', 'collapse-menu', [this.index, this.indexPath]);
this.opened = false;
}, 300);
}
}
},
@ -49,18 +86,47 @@
this.opened = true;
}
});
this.$on('select', (index, indexPath) => {
if (this.mode === 'horizontal') {
this.active = indexPath.indexOf(this.index) !== -1;
this.opened = false;
}
});
},
render(h) {
var submenu;
if (this.mode === 'horizontal') {
submenu = (
<transition name="md-fade-bottom">
{this.opened ? <ul class="el-menu">{this.$slots.default}</ul> : null }
</transition>
);
} else {
submenu = (
this.opened ? <ul class="el-menu">{this.$slots.default}</ul> : null
);
}
return (
<li
class={{ 'el-submenu': true, 'is-active': this.active, 'is-opened': this.opened}}
on-mouseenter={this.handleMouseenter}
on-mouseleave={this.handleMouseleave}
>
<div class="el-submenu__title" on-click={this.handleClick}>
{this.$slots.title}
<i class={{
'el-submenu__icon-arrow': true,
'el-icon-arrow-down': this.mode === 'vertical',
'el-icon-caret-bottom': this.mode === 'horizontal'
}}>
</i>
</div>
{submenu}
</li>
);
}
};
</script>
<template>
<li class="el-submenu" :class="{'is-opened': opened}">
<div class="el-menu-item el-submenu__title" @click="handleClick">
<slot name="title"></slot>
<i class="el-submenu__icon-arrow el-icon-arrow-up"></i>
</div>
<ul class="el-menu" v-show="opened">
<slot></slot>
</ul>
</li>
</template>

View File

@ -1,108 +1,141 @@
@charset "UTF-8";
@import "./common/var.css";
@define-extend menu-item {
height: 56px;
line-height: 56px;
font-size: 14px;
color: var(--menu-item-color);
padding: 0 20px;
cursor: pointer;
position: relative;
transition: border-color .3s, background-color .3s, color .3s;
box-sizing: border-box;
}
@component-namespace el {
@b menu {
height: 60px;
border-radius: 2px;
line-height: 60px;
list-style: none;
position: relative;
margin: 0;
padding-left: 0;
background-color: #fff;
background-color: var(--menu-item-fill);
@utils-clearfix;
& li {
list-style: none;
}
@m vertical {
height: auto;
& .el-menu-item {
float: none;
height: 56px;
line-height: 56px;
margin: 0;
padding-left: 20px;
cursor: pointer;
position: relative;
&:hover {
background-color: var(--menu-item-hover-fill);
}
& .el-menu-item__bar {
display: none;
}
}
& .el-menu-item.is-active {
color: var(--color-primary);
}
}
@m dark {
background-color: var(--dark-menu-item-fill);
& .el-menu-item,
& .el-submenu__title {
color: #c0ccda;
}
& .el-menu-item {
background-color: var(--dark-menu-item-fill);
}
&.el-menu--vertical {
& .el-menu-item,
& .el-submenu__title {
&:hover {
background-color: var(--dark-menu-item-hover-fill);
}
&.is-active {
background-color: #5e6d82;
color: #fff;
}
}
}
& .el-submenu .el-menu {
background-color: var(--dark-submenu-item-fill);
}
& .el-submenu .el-menu-item:not(.el-submenu__title) {
background-color: transparent;
color: #99a9bf;
&:hover {
background-color: var(--dark-menu-item-hover-fill);
background-color: #475669;
}
&.is-active {
background-color: #5e6d82;
color: #fff;
}
& .el-submenu .el-menu {
background-color: #1f2f3d;
& .el-menu-item:hover {
background-color: #475669;
}
}
}
@m horizontal {
& .el-menu-item {
float: left;
height: 60px;
line-height: 60px;
margin: 0;
cursor: pointer;
position: relative;
box-sizing: border-box;
border-bottom: 5px solid transparent;
&:hover {
background-color: var(--menu-item-hover-fill);
}
}
& .el-submenu {
float: left;
position: relative;
> .el-menu {
position: absolute;
top: 65px;
left: 0;
border:1px solid #d3dce6;
padding: 5px 0;
background-color: #fff;
z-index: 1;
width: 100%;
box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.12), 0px 0px 6px 0px rgba(0,0,0,0.04);
}
& .el-submenu__title {
height: 60px;
line-height: 60px;
border-bottom: 5px solid transparent;
}
& .el-menu-item {
background-color: #fff;
float: none;
height: 36px;
line-height: 36px;
padding: 0 10px;
}
& .el-submenu__icon-arrow {
position: static;
vertical-align: middle;
margin-left: 5px;
color: #99a9bf;
margin-top: -3px;
}
}
& .el-menu-item:hover,
& .el-submenu__title:hover {
background-color: var(--menu-item-fill);
}
& > .el-menu-item:hover,
& > .el-submenu:hover .el-submenu__title,
& > .el-submenu.is-active .el-submenu__title {
border-bottom: 5px solid var(--color-primary);
}
&.el-menu--dark {
& .el-menu-item:hover,
& .el-submenu__title:hover {
background-color: var(--dark-menu-item-fill);
}
& .el-submenu {
.el-menu-item,
.el-submenu-title {
color: #475669;
&:hover {
background-color: #d3dce6;
}
}
.el-menu-item.is-active {
color: var(--color-primary);
}
}
}
}
}
@b menu-item {
font-size: 14px;
color: var(--menu-item-color);
float: left;
height: 100%;
padding: 0 20px;
cursor: pointer;
position: relative;
transition: var(--md-fade-transition);
transform-origin: center center;
background-color: var(--menu-item-fill);
@extend menu-item;
@e bar {
content: '';
width: 100%;
height: 5px;
background-color: var(--color-primary);
bottom: 0;
left: 0;
position: absolute;
display: block;
}
& [class^="el-icon-"] {
vertical-align: baseline;
margin-right: 10px;
@ -113,40 +146,57 @@
&:last-child {
margin-right: 0;
}
&:hover {
background-color: #d3dce6;
}
@when active {
color: var(--color-primary);
}
}
@b submenu {
& .el-menu {
height: auto;
background-color: var(--submenu-item-fill);
}
& .el-menu-item:not(.el-submenu__title) {
padding-left: 46px;
background-color: transparent;
@e title {
position: relative;
@extend menu-item;
&:hover {
background-color: var(--menu-item-hover-fill);
background-color: #d3dce6;
}
}
@e title [class^="el-icon-"] {
vertical-align: baseline;
margin-right: 10px;
& .el-menu {
background-color: #e5e9f2;
}
& .el-menu-item {
height: 50px;
line-height: 50px;
padding: 0 45px;
&:hover {
background-color: #d3dce6;
}
}
@e icon-arrow {
position: absolute;
margin: 0;
top: 50%;
right: 20px;
margin-top: -8px;
transform: rotateZ(180deg);
font-size: 12px;
margin-top: -7px;
transition: transform .3s;
font-size: 12px;
}
@when active {
.el-submenu__title {
border-bottom-color: var(--color-primary);
}
}
@when opened {
& .el-submenu__icon-arrow {
transform: rotateZ(0);
.el-submenu__icon-arrow {
transform: rotateZ(180deg);
}
}
[class^="el-icon-"] {
vertical-align: baseline;
margin-right: 10px;
}
}
@b menu-item-group {
@ -157,7 +207,7 @@
padding-top: 15px;
line-height: normal;
font-size: 14px;
padding-left: 25px;
padding-left: 20px;
color: #99a9bf;
}
}

View File

@ -21,7 +21,7 @@
vertical-align: middle;
}
& > * {
margin-right: 8px;
margin-right: 3px;
}
&:hover {
background: #e5e9f2;