forked from zhurui/management
350 lines
9.7 KiB
Vue
350 lines
9.7 KiB
Vue
|
<script>
|
||
|
import ElCollapseTransition from 'element-ui/src/transitions/collapse-transition';
|
||
|
import menuMixin from './menu-mixin';
|
||
|
import Emitter from 'element-ui/src/mixins/emitter';
|
||
|
import Popper from 'element-ui/src/utils/vue-popper';
|
||
|
|
||
|
const poperMixins = {
|
||
|
props: {
|
||
|
transformOrigin: {
|
||
|
type: [Boolean, String],
|
||
|
default: false
|
||
|
},
|
||
|
offset: Popper.props.offset,
|
||
|
boundariesPadding: Popper.props.boundariesPadding,
|
||
|
popperOptions: Popper.props.popperOptions
|
||
|
},
|
||
|
data: Popper.data,
|
||
|
methods: Popper.methods,
|
||
|
beforeDestroy: Popper.beforeDestroy,
|
||
|
deactivated: Popper.deactivated
|
||
|
};
|
||
|
|
||
|
export default {
|
||
|
name: 'ElSubmenu',
|
||
|
|
||
|
componentName: 'ElSubmenu',
|
||
|
|
||
|
mixins: [menuMixin, Emitter, poperMixins],
|
||
|
|
||
|
components: { ElCollapseTransition },
|
||
|
|
||
|
props: {
|
||
|
index: {
|
||
|
type: String,
|
||
|
required: true
|
||
|
},
|
||
|
showTimeout: {
|
||
|
type: Number,
|
||
|
default: 300
|
||
|
},
|
||
|
hideTimeout: {
|
||
|
type: Number,
|
||
|
default: 300
|
||
|
},
|
||
|
popperClass: String,
|
||
|
disabled: Boolean,
|
||
|
popperAppendToBody: {
|
||
|
type: Boolean,
|
||
|
default: undefined
|
||
|
}
|
||
|
},
|
||
|
|
||
|
data() {
|
||
|
return {
|
||
|
popperJS: null,
|
||
|
timeout: null,
|
||
|
items: {},
|
||
|
submenus: {},
|
||
|
mouseInChild: false
|
||
|
};
|
||
|
},
|
||
|
watch: {
|
||
|
opened(val) {
|
||
|
if (this.isMenuPopup) {
|
||
|
this.$nextTick(_ => {
|
||
|
this.updatePopper();
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
computed: {
|
||
|
// popper option
|
||
|
appendToBody() {
|
||
|
return this.popperAppendToBody === undefined
|
||
|
? this.isFirstLevel
|
||
|
: this.popperAppendToBody;
|
||
|
},
|
||
|
menuTransitionName() {
|
||
|
return this.rootMenu.collapse ? 'el-zoom-in-left' : 'el-zoom-in-top';
|
||
|
},
|
||
|
opened() {
|
||
|
return this.rootMenu.openedMenus.indexOf(this.index) > -1;
|
||
|
},
|
||
|
active() {
|
||
|
let isActive = false;
|
||
|
const submenus = this.submenus;
|
||
|
const items = this.items;
|
||
|
|
||
|
Object.keys(items).forEach(index => {
|
||
|
if (items[index].active) {
|
||
|
isActive = true;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
Object.keys(submenus).forEach(index => {
|
||
|
if (submenus[index].active) {
|
||
|
isActive = true;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return isActive;
|
||
|
},
|
||
|
hoverBackground() {
|
||
|
return this.rootMenu.hoverBackground;
|
||
|
},
|
||
|
backgroundColor() {
|
||
|
return this.rootMenu.backgroundColor || '';
|
||
|
},
|
||
|
activeTextColor() {
|
||
|
return this.rootMenu.activeTextColor || '';
|
||
|
},
|
||
|
textColor() {
|
||
|
return this.rootMenu.textColor || '';
|
||
|
},
|
||
|
mode() {
|
||
|
return this.rootMenu.mode;
|
||
|
},
|
||
|
isMenuPopup() {
|
||
|
return this.rootMenu.isMenuPopup;
|
||
|
},
|
||
|
titleStyle() {
|
||
|
if (this.mode !== 'horizontal') {
|
||
|
return {
|
||
|
color: this.textColor
|
||
|
};
|
||
|
}
|
||
|
return {
|
||
|
borderBottomColor: this.active
|
||
|
? (this.rootMenu.activeTextColor ? this.activeTextColor : '')
|
||
|
: 'transparent',
|
||
|
color: this.active
|
||
|
? this.activeTextColor
|
||
|
: this.textColor
|
||
|
};
|
||
|
},
|
||
|
isFirstLevel() {
|
||
|
let isFirstLevel = true;
|
||
|
let parent = this.$parent;
|
||
|
while (parent && parent !== this.rootMenu) {
|
||
|
if (['ElSubmenu', 'ElMenuItemGroup'].indexOf(parent.$options.componentName) > -1) {
|
||
|
isFirstLevel = false;
|
||
|
break;
|
||
|
} else {
|
||
|
parent = parent.$parent;
|
||
|
}
|
||
|
}
|
||
|
return isFirstLevel;
|
||
|
}
|
||
|
},
|
||
|
methods: {
|
||
|
handleCollapseToggle(value) {
|
||
|
if (value) {
|
||
|
this.initPopper();
|
||
|
} else {
|
||
|
this.doDestroy();
|
||
|
}
|
||
|
},
|
||
|
addItem(item) {
|
||
|
this.$set(this.items, item.index, item);
|
||
|
},
|
||
|
removeItem(item) {
|
||
|
delete this.items[item.index];
|
||
|
},
|
||
|
addSubmenu(item) {
|
||
|
this.$set(this.submenus, item.index, item);
|
||
|
},
|
||
|
removeSubmenu(item) {
|
||
|
delete this.submenus[item.index];
|
||
|
},
|
||
|
handleClick() {
|
||
|
const { rootMenu, disabled } = this;
|
||
|
if (
|
||
|
(rootMenu.menuTrigger === 'hover' && rootMenu.mode === 'horizontal') ||
|
||
|
(rootMenu.collapse && rootMenu.mode === 'vertical') ||
|
||
|
disabled
|
||
|
) {
|
||
|
return;
|
||
|
}
|
||
|
this.dispatch('ElMenu', 'submenu-click', this);
|
||
|
},
|
||
|
handleMouseenter(event, showTimeout = this.showTimeout) {
|
||
|
|
||
|
if (!('ActiveXObject' in window) && event.type === 'focus' && !event.relatedTarget) {
|
||
|
return;
|
||
|
}
|
||
|
const { rootMenu, disabled } = this;
|
||
|
if (
|
||
|
(rootMenu.menuTrigger === 'click' && rootMenu.mode === 'horizontal') ||
|
||
|
(!rootMenu.collapse && rootMenu.mode === 'vertical') ||
|
||
|
disabled
|
||
|
) {
|
||
|
return;
|
||
|
}
|
||
|
this.dispatch('ElSubmenu', 'mouse-enter-child');
|
||
|
clearTimeout(this.timeout);
|
||
|
this.timeout = setTimeout(() => {
|
||
|
this.rootMenu.openMenu(this.index, this.indexPath);
|
||
|
}, showTimeout);
|
||
|
|
||
|
if (this.appendToBody) {
|
||
|
this.$parent.$el.dispatchEvent(new MouseEvent('mouseenter'));
|
||
|
}
|
||
|
},
|
||
|
handleMouseleave(deepDispatch = false) {
|
||
|
const {rootMenu} = this;
|
||
|
if (
|
||
|
(rootMenu.menuTrigger === 'click' && rootMenu.mode === 'horizontal') ||
|
||
|
(!rootMenu.collapse && rootMenu.mode === 'vertical')
|
||
|
) {
|
||
|
return;
|
||
|
}
|
||
|
this.dispatch('ElSubmenu', 'mouse-leave-child');
|
||
|
clearTimeout(this.timeout);
|
||
|
this.timeout = setTimeout(() => {
|
||
|
!this.mouseInChild && this.rootMenu.closeMenu(this.index);
|
||
|
}, this.hideTimeout);
|
||
|
|
||
|
if (this.appendToBody && deepDispatch) {
|
||
|
if (this.$parent.$options.name === 'ElSubmenu') {
|
||
|
this.$parent.handleMouseleave(true);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
handleTitleMouseenter() {
|
||
|
if (this.mode === 'horizontal' && !this.rootMenu.backgroundColor) return;
|
||
|
const title = this.$refs['submenu-title'];
|
||
|
title && (title.style.backgroundColor = this.rootMenu.hoverBackground);
|
||
|
},
|
||
|
handleTitleMouseleave() {
|
||
|
if (this.mode === 'horizontal' && !this.rootMenu.backgroundColor) return;
|
||
|
const title = this.$refs['submenu-title'];
|
||
|
title && (title.style.backgroundColor = this.rootMenu.backgroundColor || '');
|
||
|
},
|
||
|
updatePlacement() {
|
||
|
this.currentPlacement = this.mode === 'horizontal' && this.isFirstLevel
|
||
|
? 'bottom-start'
|
||
|
: 'right-start';
|
||
|
},
|
||
|
initPopper() {
|
||
|
this.referenceElm = this.$el;
|
||
|
this.popperElm = this.$refs.menu;
|
||
|
this.updatePlacement();
|
||
|
}
|
||
|
},
|
||
|
created() {
|
||
|
this.$on('toggle-collapse', this.handleCollapseToggle);
|
||
|
this.$on('mouse-enter-child', () => {
|
||
|
this.mouseInChild = true;
|
||
|
clearTimeout(this.timeout);
|
||
|
});
|
||
|
this.$on('mouse-leave-child', () => {
|
||
|
this.mouseInChild = false;
|
||
|
clearTimeout(this.timeout);
|
||
|
});
|
||
|
},
|
||
|
mounted() {
|
||
|
this.parentMenu.addSubmenu(this);
|
||
|
this.rootMenu.addSubmenu(this);
|
||
|
this.initPopper();
|
||
|
},
|
||
|
beforeDestroy() {
|
||
|
this.parentMenu.removeSubmenu(this);
|
||
|
this.rootMenu.removeSubmenu(this);
|
||
|
},
|
||
|
render(h) {
|
||
|
const {
|
||
|
active,
|
||
|
opened,
|
||
|
paddingStyle,
|
||
|
titleStyle,
|
||
|
backgroundColor,
|
||
|
rootMenu,
|
||
|
currentPlacement,
|
||
|
menuTransitionName,
|
||
|
mode,
|
||
|
disabled,
|
||
|
popperClass,
|
||
|
$slots,
|
||
|
isFirstLevel
|
||
|
} = this;
|
||
|
|
||
|
const popupMenu = (
|
||
|
<transition name={menuTransitionName}>
|
||
|
<div
|
||
|
ref="menu"
|
||
|
v-show={opened}
|
||
|
class={[`el-menu--${mode}`, popperClass]}
|
||
|
on-mouseenter={($event) => this.handleMouseenter($event, 100)}
|
||
|
on-mouseleave={() => this.handleMouseleave(true)}
|
||
|
on-focus={($event) => this.handleMouseenter($event, 100)}>
|
||
|
<ul
|
||
|
role="menu"
|
||
|
class={['el-menu el-menu--popup', `el-menu--popup-${currentPlacement}`]}
|
||
|
style={{ backgroundColor: rootMenu.backgroundColor || '' }}>
|
||
|
{$slots.default}
|
||
|
</ul>
|
||
|
</div>
|
||
|
</transition>
|
||
|
);
|
||
|
|
||
|
const inlineMenu = (
|
||
|
<el-collapse-transition>
|
||
|
<ul
|
||
|
role="menu"
|
||
|
class="el-menu el-menu--inline"
|
||
|
v-show={opened}
|
||
|
style={{ backgroundColor: rootMenu.backgroundColor || '' }}>
|
||
|
{$slots.default}
|
||
|
</ul>
|
||
|
</el-collapse-transition>
|
||
|
);
|
||
|
|
||
|
const submenuTitleIcon = (
|
||
|
rootMenu.mode === 'horizontal' && isFirstLevel ||
|
||
|
rootMenu.mode === 'vertical' && !rootMenu.collapse
|
||
|
) ? 'el-icon-arrow-down' : 'el-icon-arrow-right';
|
||
|
|
||
|
return (
|
||
|
<li
|
||
|
class={{
|
||
|
'el-submenu': true,
|
||
|
'is-active': active,
|
||
|
'is-opened': opened,
|
||
|
'is-disabled': disabled
|
||
|
}}
|
||
|
role="menuitem"
|
||
|
aria-haspopup="true"
|
||
|
aria-expanded={opened}
|
||
|
on-mouseenter={this.handleMouseenter}
|
||
|
on-mouseleave={() => this.handleMouseleave(false)}
|
||
|
on-focus={this.handleMouseenter}
|
||
|
>
|
||
|
<div
|
||
|
class="el-submenu__title"
|
||
|
ref="submenu-title"
|
||
|
on-click={this.handleClick}
|
||
|
on-mouseenter={this.handleTitleMouseenter}
|
||
|
on-mouseleave={this.handleTitleMouseleave}
|
||
|
style={[paddingStyle, titleStyle, { backgroundColor }]}
|
||
|
>
|
||
|
{$slots.title}
|
||
|
<i class={[ 'el-submenu__icon-arrow', submenuTitleIcon ]}></i>
|
||
|
</div>
|
||
|
{this.isMenuPopup ? popupMenu : inlineMenu}
|
||
|
</li>
|
||
|
);
|
||
|
}
|
||
|
};
|
||
|
</script>
|