mirror of https://github.com/ElemeFE/element
parent
daa4f83e4f
commit
c73eeed291
|
@ -3,7 +3,7 @@
|
|||
.el-menu-demo {
|
||||
padding-left: 55px;
|
||||
}
|
||||
.el-menu-vertical-demo {
|
||||
.el-menu-vertical-demo:not(.el-menu--collapse) {
|
||||
width: 200px;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
@ -33,7 +33,8 @@
|
|||
data() {
|
||||
return {
|
||||
activeIndex: '1',
|
||||
activeIndex2: '1'
|
||||
activeIndex2: '1',
|
||||
isCollapse: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -179,10 +180,70 @@ Vertical NavMenu with sub-menus.
|
|||
```
|
||||
:::
|
||||
|
||||
### Collapse
|
||||
|
||||
Vertical NavMenu could be collapsed.
|
||||
|
||||
::: demo
|
||||
```html
|
||||
<el-radio-group v-model="isCollapse" style="margin-bottom: 20px;">
|
||||
<el-radio-button :label="false">expand</el-radio-button>
|
||||
<el-radio-button :label="true">collapse</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" :collapse="isCollapse">
|
||||
<el-submenu index="1">
|
||||
<template slot="title">
|
||||
<i class="el-icon-message"></i>
|
||||
<span slot="title">Navigator One</span>
|
||||
</template>
|
||||
<el-menu-item-group>
|
||||
<span slot="title">Group One</span>
|
||||
<el-menu-item index="1-1">item one</el-menu-item>
|
||||
<el-menu-item index="1-2">item two</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
<el-menu-item-group title="Group Two">
|
||||
<el-menu-item index="1-3">item three</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
<el-submenu index="1-4">
|
||||
<span slot="title">item four</span>
|
||||
<el-menu-item index="1-4-1">item one</el-menu-item>
|
||||
</el-submenu>
|
||||
</el-submenu>
|
||||
<el-menu-item index="2">
|
||||
<i class="el-icon-menu"></i>
|
||||
<span slot="title">Navigator Two</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<i class="el-icon-setting"></i>
|
||||
<span slot="title">Navigator Three</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isCollapse: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleOpen(key, keyPath) {
|
||||
console.log(key, keyPath);
|
||||
},
|
||||
handleClose(key, keyPath) {
|
||||
console.log(key, keyPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Menu Attribute
|
||||
| Attribute | Description | Type | Accepted Values | Default |
|
||||
|---------- |-------- |---------- |------------- |-------- |
|
||||
| mode | menu display mode | string | horizontal/vertical | vertical |
|
||||
| collapse | whether the menu is collapsed (available only in vertical mode) | boolean | — | false |
|
||||
| theme | theme color | string | light/dark | light |
|
||||
| default-active | index of currently active menu | string | — | — |
|
||||
| default-openeds | array that contains keys of currently active sub-menus | Array | — | — |
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
.el-menu-demo {
|
||||
padding-left: 55px;
|
||||
}
|
||||
.el-menu-vertical-demo {
|
||||
.el-menu-vertical-demo:not(.el-menu--collapse) {
|
||||
width: 200px;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
@ -33,7 +33,8 @@
|
|||
data() {
|
||||
return {
|
||||
activeIndex: '1',
|
||||
activeIndex2: '1'
|
||||
activeIndex2: '1',
|
||||
isCollapse: true
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -181,10 +182,68 @@
|
|||
```
|
||||
:::
|
||||
|
||||
### 折叠
|
||||
|
||||
::: demo
|
||||
```html
|
||||
<el-radio-group v-model="isCollapse" style="margin-bottom: 20px;">
|
||||
<el-radio-button :label="false">展开</el-radio-button>
|
||||
<el-radio-button :label="true">收起</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" :collapse="isCollapse">
|
||||
<el-submenu index="1">
|
||||
<template slot="title">
|
||||
<i class="el-icon-message"></i>
|
||||
<span slot="title">导航一</span>
|
||||
</template>
|
||||
<el-menu-item-group>
|
||||
<span slot="title">分组一</span>
|
||||
<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 index="1-4">
|
||||
<span slot="title">选项4</span>
|
||||
<el-menu-item index="1-4-1">选项1</el-menu-item>
|
||||
</el-submenu>
|
||||
</el-submenu>
|
||||
<el-menu-item index="2">
|
||||
<i class="el-icon-menu"></i>
|
||||
<span slot="title">导航二</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<i class="el-icon-setting"></i>
|
||||
<span slot="title">导航三</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isCollapse: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleOpen(key, keyPath) {
|
||||
console.log(key, keyPath);
|
||||
},
|
||||
handleClose(key, keyPath) {
|
||||
console.log(key, keyPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Menu Attribute
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
|---------- |-------- |---------- |------------- |-------- |
|
||||
| mode | 模式 | string | horizontal,vertical | vertical |
|
||||
| collapse | 是否水平折叠收起菜单(仅在 mode 为 vertical 时可用)| boolean | — | false |
|
||||
| theme | 主题色 | string | light,dark | light |
|
||||
| default-active | 当前激活菜单的 index | string | — | — |
|
||||
| default-openeds | 当前打开的submenu的 key 数组 | Array | — | — |
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
componentName: 'ElMenuItemGroup',
|
||||
|
||||
inject: ['rootMenu'],
|
||||
props: {
|
||||
title: {
|
||||
type: String
|
||||
|
@ -29,6 +30,7 @@
|
|||
levelPadding() {
|
||||
let padding = 10;
|
||||
let parent = this.$parent;
|
||||
if (this.rootMenu.collapse) return 20;
|
||||
while (parent && parent.$options.componentName !== 'ElMenu') {
|
||||
if (parent.$options.componentName === 'ElSubmenu') {
|
||||
padding += 20;
|
||||
|
|
|
@ -6,7 +6,19 @@
|
|||
'is-active': active,
|
||||
'is-disabled': disabled
|
||||
}">
|
||||
<el-tooltip
|
||||
v-if="$parent === rootMenu && rootMenu.collapse"
|
||||
effect="dark"
|
||||
placement="right">
|
||||
<div slot="content"><slot name="title"></slot></div>
|
||||
<div style="position: absolute;left: 0;top: 0;height: 100%;width: 100%;display: inline-block;box-sizing: border-box;padding: 0 20px;">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<template v-else>
|
||||
<slot></slot>
|
||||
<slot name="title"></slot>
|
||||
</template>
|
||||
</li>
|
||||
</template>
|
||||
<script>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export default {
|
||||
inject: ['rootMenu'],
|
||||
computed: {
|
||||
indexPath() {
|
||||
var path = [this.index];
|
||||
|
@ -11,16 +12,6 @@ export default {
|
|||
}
|
||||
return path;
|
||||
},
|
||||
rootMenu() {
|
||||
var parent = this.$parent;
|
||||
while (
|
||||
parent &&
|
||||
parent.$options.componentName !== 'ElMenu'
|
||||
) {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
return parent;
|
||||
},
|
||||
parentMenu() {
|
||||
let parent = this.$parent;
|
||||
while (
|
||||
|
@ -36,12 +27,17 @@ export default {
|
|||
|
||||
let padding = 20;
|
||||
let parent = this.$parent;
|
||||
|
||||
if (this.rootMenu.collapse) {
|
||||
padding = 20;
|
||||
} else {
|
||||
while (parent && parent.$options.componentName !== 'ElMenu') {
|
||||
if (parent.$options.componentName === 'ElSubmenu') {
|
||||
padding += 20;
|
||||
}
|
||||
parent = parent.$parent;
|
||||
}
|
||||
}
|
||||
return {paddingLeft: padding + 'px'};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,82 @@
|
|||
<template>
|
||||
<el-menu-collapse-transition>
|
||||
<ul class="el-menu"
|
||||
:key="collapse"
|
||||
:class="{
|
||||
'el-menu--horizontal': mode === 'horizontal',
|
||||
'el-menu--dark': theme === 'dark'
|
||||
'el-menu--dark': theme === 'dark',
|
||||
'el-menu--collapse': collapse
|
||||
}"
|
||||
>
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</el-menu-collapse-transition>
|
||||
</template>
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
import emitter from 'element-ui/src/mixins/emitter';
|
||||
import { addClass, removeClass, hasClass } from 'element-ui/src/utils/dom';
|
||||
|
||||
Vue.component('el-menu-collapse-transition', {
|
||||
functional: true,
|
||||
render(createElement, context) {
|
||||
const data = {
|
||||
props: {
|
||||
mode: 'out-in'
|
||||
},
|
||||
on: {
|
||||
beforeEnter(el) {
|
||||
el.style.opacity = 0.2;
|
||||
},
|
||||
|
||||
enter(el) {
|
||||
addClass(el, 'el-opacity-transition');
|
||||
el.style.opacity = 1;
|
||||
},
|
||||
|
||||
afterEnter(el) {
|
||||
removeClass(el, 'el-opacity-transition');
|
||||
el.style.opacity = '';
|
||||
},
|
||||
|
||||
beforeLeave(el) {
|
||||
if (!el.dataset) el.dataset = {};
|
||||
|
||||
if (hasClass(el, 'el-menu--collapse')) {
|
||||
removeClass(el, 'el-menu--collapse');
|
||||
el.dataset.oldOverflow = el.style.overflow;
|
||||
el.dataset.scrollWidth = el.scrollWidth;
|
||||
addClass(el, 'el-menu--collapse');
|
||||
}
|
||||
|
||||
el.style.width = el.scrollWidth + 'px';
|
||||
el.style.overflow = 'hidden';
|
||||
},
|
||||
|
||||
leave(el) {
|
||||
if (!hasClass(el, 'el-menu--collapse')) {
|
||||
addClass(el, 'horizontal-collapse-transition');
|
||||
el.style.width = '64px';
|
||||
} else {
|
||||
addClass(el, 'horizontal-collapse-transition');
|
||||
el.style.width = el.dataset.scrollWidth + 'px';
|
||||
}
|
||||
},
|
||||
|
||||
afterLeave(el) {
|
||||
removeClass(el, 'horizontal-collapse-transition');
|
||||
if (hasClass(el, 'el-menu--collapse')) {
|
||||
el.style.width = el.dataset.scrollWidth + 'px';
|
||||
} else {
|
||||
el.style.width = '64px';
|
||||
}
|
||||
el.style.overflow = el.dataset.oldOverflow;
|
||||
}
|
||||
}
|
||||
};
|
||||
return createElement('transition', data, context.children);
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
name: 'ElMenu',
|
||||
|
@ -18,6 +85,12 @@
|
|||
|
||||
mixins: [emitter],
|
||||
|
||||
provide() {
|
||||
return {
|
||||
rootMenu: this
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
|
@ -37,7 +110,8 @@
|
|||
menuTrigger: {
|
||||
type: String,
|
||||
default: 'hover'
|
||||
}
|
||||
},
|
||||
collapse: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -106,7 +180,7 @@
|
|||
this.activedIndex = item.index;
|
||||
this.$emit('select', index, indexPath, item);
|
||||
|
||||
if (this.mode === 'horizontal') {
|
||||
if (this.mode === 'horizontal' || this.collapse) {
|
||||
this.openedMenus = [];
|
||||
}
|
||||
|
||||
|
|
|
@ -5,18 +5,21 @@
|
|||
'is-active': active,
|
||||
'is-opened': opened
|
||||
}"
|
||||
@mouseenter="handleMouseenter"
|
||||
@mouseleave="handleMouseleave"
|
||||
>
|
||||
<div class="el-submenu__title" ref="submenu-title" :style="paddingStyle">
|
||||
<div class="el-submenu__title" ref="submenu-title" @click="handleClick" :style="paddingStyle">
|
||||
<slot name="title"></slot>
|
||||
<i :class="{
|
||||
'el-submenu__icon-arrow': true,
|
||||
'el-icon-arrow-down': rootMenu.mode === 'vertical',
|
||||
'el-icon-caret-bottom': rootMenu.mode === 'horizontal'
|
||||
'el-icon-caret-bottom': rootMenu.mode === 'horizontal',
|
||||
'el-icon-arrow-down': rootMenu.mode === 'vertical' && !rootMenu.collapse,
|
||||
'el-icon-caret-right': rootMenu.mode === 'vertical' && rootMenu.collapse
|
||||
}">
|
||||
</i>
|
||||
</div>
|
||||
<template v-if="rootMenu.mode === 'horizontal'">
|
||||
<transition name="el-zoom-in-top">
|
||||
<template v-if="rootMenu.mode === 'horizontal' || (rootMenu.mode === 'vertical' && rootMenu.collapse)">
|
||||
<transition :name="menuTransitionName">
|
||||
<ul class="el-menu" v-show="opened"><slot></slot></ul>
|
||||
</transition>
|
||||
</template>
|
||||
|
@ -45,6 +48,7 @@
|
|||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
timeout: null,
|
||||
|
@ -53,6 +57,9 @@
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
menuTransitionName() {
|
||||
return this.rootMenu.collapse ? 'el-zoom-in-left' : 'el-zoom-in-top';
|
||||
},
|
||||
opened() {
|
||||
return this.rootMenu.openedMenus.indexOf(this.index) > -1;
|
||||
},
|
||||
|
@ -93,37 +100,40 @@
|
|||
delete this.submenus[item.index];
|
||||
},
|
||||
handleClick() {
|
||||
const {rootMenu} = this;
|
||||
if (
|
||||
(rootMenu.menuTrigger === 'hover' && rootMenu.mode === 'horizontal') ||
|
||||
(rootMenu.collapse && rootMenu.mode === 'vertical')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.dispatch('ElMenu', 'submenu-click', this);
|
||||
},
|
||||
handleMouseenter() {
|
||||
const {rootMenu} = this;
|
||||
if (
|
||||
(rootMenu.menuTrigger === 'click' && rootMenu.mode === 'horizontal') ||
|
||||
(!rootMenu.collapse && rootMenu.mode === 'vertical')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(() => {
|
||||
this.rootMenu.openMenu(this.index, this.indexPath);
|
||||
}, 300);
|
||||
},
|
||||
handleMouseleave() {
|
||||
const {rootMenu} = this;
|
||||
if (
|
||||
(rootMenu.menuTrigger === 'click' && rootMenu.mode === 'horizontal') ||
|
||||
(!rootMenu.collapse && rootMenu.mode === 'vertical')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(() => {
|
||||
this.rootMenu.closeMenu(this.index, this.indexPath);
|
||||
}, 300);
|
||||
},
|
||||
initEvents() {
|
||||
let {
|
||||
rootMenu,
|
||||
handleMouseenter,
|
||||
handleMouseleave,
|
||||
handleClick
|
||||
} = this;
|
||||
let triggerElm;
|
||||
|
||||
if (rootMenu.mode === 'horizontal' && rootMenu.menuTrigger === 'hover') {
|
||||
triggerElm = this.$el;
|
||||
triggerElm.addEventListener('mouseenter', handleMouseenter);
|
||||
triggerElm.addEventListener('mouseleave', handleMouseleave);
|
||||
} else {
|
||||
triggerElm = this.$refs['submenu-title'];
|
||||
triggerElm.addEventListener('click', handleClick);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -133,9 +143,6 @@
|
|||
beforeDestroy() {
|
||||
this.parentMenu.removeSubmenu(this);
|
||||
this.rootMenu.removeSubmenu(this);
|
||||
},
|
||||
mounted() {
|
||||
this.initEvents();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -68,9 +68,25 @@
|
|||
transform: scaleY(0);
|
||||
}
|
||||
|
||||
.el-zoom-in-left-enter-active,
|
||||
.el-zoom-in-left-leave-active {
|
||||
opacity: 1;
|
||||
transform: scale(1, 1);
|
||||
transition: var(--md-fade-transition);
|
||||
transform-origin: top left;
|
||||
}
|
||||
.el-zoom-in-left-enter,
|
||||
.el-zoom-in-left-leave-active {
|
||||
opacity: 0;
|
||||
transform: scale(.45, .45);
|
||||
}
|
||||
|
||||
.collapse-transition {
|
||||
transition: 0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out;
|
||||
}
|
||||
.horizontal-collapse-transition {
|
||||
transition: 0.3s width ease-in-out, 0.3s padding-left ease-in-out, 0.3s padding-right ease-in-out;
|
||||
}
|
||||
|
||||
.el-list-enter-active,
|
||||
.el-list-leave-active {
|
||||
|
@ -80,3 +96,7 @@
|
|||
opacity: 0;
|
||||
transform: translateY(-30px);
|
||||
}
|
||||
|
||||
.el-opacity-transition {
|
||||
transition: opacity .3s cubic-bezier(.55,0,.1,1);
|
||||
}
|
|
@ -137,6 +137,45 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
@m collapse {
|
||||
width: 64px;
|
||||
|
||||
> .el-menu-item,
|
||||
> .el-submenu > .el-submenu__title {
|
||||
text-align: center;
|
||||
[class^="el-icon-"] {
|
||||
margin: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.el-submenu__icon-arrow {
|
||||
display: none;
|
||||
}
|
||||
span {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.el-submenu {
|
||||
position: relative;
|
||||
& .el-menu {
|
||||
position: absolute;
|
||||
margin-left: 5px;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&.is-opened {
|
||||
> .el-submenu__title .el-submenu__icon-arrow {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@b menu-item {
|
||||
@extend menu-item;
|
||||
|
@ -175,6 +214,7 @@
|
|||
height: 50px;
|
||||
line-height: 50px;
|
||||
padding: 0 45px;
|
||||
min-width: 200px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-base-gray);
|
||||
|
|
Loading…
Reference in New Issue