1.组件,分为Index.Vue,和Item.vue
1.此文件为index.Vue
<template> <transition name="fade"> <div class="elx-context-menu" v-show="visible" :style="{ width+'px', top: currentY+'px', left: currentX+'px'}"> <ul> <context-menu-item v-for="item in data" :key="item.label" :data="item" :tip-show="tipShow" @action="action"> </context-menu-item> </ul> </div> </transition> </template> <script> import ContextMenuItem from './item'; export default { name: 'ContextMenu', componentName: 'ContextMenu', components: { ContextMenuItem }, props: { {// 宽 type: Number, default: 80 }, x: {// X坐标 type: Number, default: 0 }, y: {// Y坐标 type: Number, default: 0 }, data: {// 显示右键菜单的数据 type: Array, default: function() { return []; } }, visible: {// 是否显示右键菜单 type: Boolean, default: false }, tipShow: {// 是否显示提示 type: Boolean, default: true } }, data() { return { currentX: 0, currentY: 0 }; }, methods: { action(data) { if (!data.disabled) { this.$emit('action', data); } }, changePos() { const gap = 5; const bodyClientHeight = document.body.clientHeight; const bodyClientTop = document.body.clientTop; const height = this.$el.clientHeight; const elBottom = height + this.currentY; const viewHeight = bodyClientHeight + bodyClientTop; if (viewHeight < elBottom) { this.currentY = viewHeight - height - gap; } }, handleDisplay() { this.contentMenuShow = false; } }, watch: { visible(val) { if (val) { const self = this; self.$nextTick(() => { self.changePos(); }); } }, x(val) { this.currentX = val; }, y(val) { this.currentY = val; this.changePos(); } }, created() { this.currentX = this.x; this.currentY = this.y; }, mounted() { this.changePos(); window.addEventListener('resize', this.handleDisplay); }, beforeDestroy() { window.removeEventListener('resize', this.handleDisplay); } }; </script>
2.item.vue
此文件为item.Vue
<template>
<li
:class="data.disabled?'disabled':''"
@click.stop.prevent="exec"
@mouseenter.stop.prevent="showChild"
@mouseleave.stop.prevent="hideChild">
<el-tooltip :content="data.label" placement="left" :hide-after="500" v-if="tipShow">
<div class="elx-context-menu-title">
<span :class="data.class">
<span v-if="data.icon" :class="data.icon"></span>
<span v-if="data.label" v-text="data.label"></span>
<!-- <node-content :node="data"></node-content> -->
</span>
<template v-if="'children' in data">
<span v-if="data.children.length>0" class="uex-icon-caret-right"></span>
</template>
</div>
</el-tooltip>
<div class="elx-context-menu-title" v-if="!tipShow" :title="data.label">
<span :class="data.class">
<span v-if="data.icon" :class="data.icon"></span>
<span v-if="data.label" v-text="data.label"></span>
<!-- <node-content :node="data"></node-content> -->
</span>
<template v-if="'children' in data">
<span v-if="data.children.length>0" class="uex-icon-caret-right"></span>
</template>
</div>
<ul
v-if="'children' in data"
v-show="visible"
:style="{top: pos.top, bottom: pos.bottom}">
<context-menu-item
v-for="(item,index) in data.children"
:key="index"
:data="item"
@action="action">
</context-menu-item>
</ul>
</li>
</template>
<script>
export default {
name: 'ContextMenuItems',
componentName: 'ContextMenuItems',
props: {
data: {
type: Object,
default() {
return {};
}
},
tipShow: {
type: Boolean,
default: false
}
},
components: {},
data() {
return {
pos: {
top: '0px',
bottom: 'auto'
},
visible: false
};
},
methods: {
getElementPosition(el) {
let x = 0;
let y = 0;
while (el != null) {
x += el.offsetLeft;
y += el.offsetTop;
// eslint-disable-next-line no-param-reassign
el = el.offsetParent;
}
return { x, y };
},
exec() {
if (!this.data.disabled) {
this.$emit('action', this.data);
}
},
action(data) {
if (!data.disabled) {
this.$emit('action', data);
}
},
changeStyle() {
const self = this;
if (self.$el.childNodes[1]) {
if (typeof self.$el.childNodes[1].tagName === 'string') {
if (self.$el.childNodes[1].tagName.toLowerCase() === 'ul') {
const bodyClientHeight = document.body.clientHeight;
const bodyClientTop = document.body.clientTop;
const viewHeight = bodyClientHeight + bodyClientTop;
const clientTop = this.getElementPosition(self.$el.childNodes[1]).y;
const height = self.$el.childNodes[1].clientHeight;
const elBottom = height + clientTop;
if (viewHeight < elBottom) {
this.pos.top = 'auto';
this.pos.bottom = '0px';
} else {
this.pos.top = '0px';
this.pos.bottom = 'auto';
}
}
}
}
},
showChild() {
this.visible = true;
},
hideChild() {
this.visible = false;
this.pos.top = '0px';
this.pos.bottom = 'auto';
}
},
watch: {
visible(val) {
if (val) {
const self = this;
this.$nextTick(() => {
self.changeStyle();
});
}
}
},
created() {
},
mounted() {
this.changeStyle();
}
};
</script>
3.调用组件:
import ContextMenu from './content-menu/index'; // 我这里是表格调用 @row-contextmenu="rowContextmenu1",在表格中放入改事件,放在el-table上, // 组件 //data数据 contextmenuData: [ { label: '新增', action: 'add', icon: 'ri-add-line' }, { label: '编辑', action: 'edit', icon: 'ri-edit-box-line', disabled: true }, { label: '删除', action: 'delete', icon: 'ri-delete-bin-7-line' } ], pos: { x: 0, y: 0 }, rowContextmenu: false, //html <ContextMenu @action="action" :tip-show="false" :data="contextmenuData" :width="120" :visible="rowContextmenu" :x="pos.x" :y="pos.y"> </ContextMenu> //js事件 getEventPos(e) { const x = e.clientX; const y = e.clientY; return { x, y }; }, rowContextmenu1(row) { console.log('row', row); const contextmenuData = [ { label: '新增', action: 'add', icon: 'ri-add-line' }, { label: '编辑', action: 'edit', icon: 'ri-edit-box-line', disabled: true }, { label: '删除', action: 'delete', icon: 'ri-delete-bin-7-line' } ]; const e = window.event; const pos = this.getEventPos(e); if (e.which === 3) { this.rowContextmenu = false; this.pos.x = pos.x; this.pos.y = pos.y; this.contextmenuData = contextmenuData; this.rowContextmenu = true; } this.preventDefault(e); e.returnValue = false; return false; }, preventDefault(el) { const e = el || window.event; if (e.preventDefault) { e.preventDefault(); } else { e.returnvalue = false; } return e; }, action(data) { console.log('data.action', data.action); this.rowContextmenu = false; },
这只是我项目中一个简单的demo,有问题,私信我