要求:鼠标右键出现右键菜单,并随鼠标点击的位置,菜单位置进行变化
效果图:
代码如下:
index.vue
<template>
<div>
<div class="father" @contextmenu.prevent="rightClick($event)"></div>
<context-menu
v-model="showContextMenu"
:position="showPosition"
@on-click-menu="clickContextMenu"
>
</context-menu>
</div>
</template>
<script>
import contextMenu from "./contextMenu.vue";
export default {
name: "father",
components: { contextMenu },
data() {
return {
showContextMenu: false,
showPosition: {},
};
},
methods: {
rightClick(event) {
this.showPosition = {
y: event.y,
x: event.x,
};
this.showContextMenu = true;
},
clickContextMenu(value) {
console.log(value);
},
},
};
</script>
<style>
.father {
width: 200px;
height: 200px;
border: 1px solid;
background: #fff;
}
</style>
contextMenu.vue
<template>
<div
class="theContextMenu"
id="theContextMenu"
v-clickoutside="closePicker"
:style="contextMenuStyle"
v-show="visible"
>
<div v-for="(list, index) in menuList" :key="index">
<ul :class="index < menuList.length - 1 ? 'splitLine' : ''">
<li
v-for="item in list"
:key="item.value"
@click="clickMenu(item.value)"
>
{{ item.label }}
</li>
</ul>
</div>
</div>
</template>
<script>
const clickoutside = {
// 初始化指令
bind(el, binding, vnode) {
function documentHandler(e) {
// 这里判断点击的元素是否是本身,是本身,则返回
if (el.contains(e.target)) {
return false;
}
// 判断指令中是否绑定了函数
if (binding.expression) {
// 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
binding.value(e);
}
}
// 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
el.__vueClickOutside__ = documentHandler;
document.addEventListener("click", documentHandler);
},
unbind(el, binding) {
// 解除事件监听
document.removeEventListener("click", el.__vueClickOutside__);
delete el.__vueClickOutside__;
},
};
export default {
name: "context-menu",
model: {
prop: "visible",
event: "change",
},
props: {
position: {
type: Object,
default: () => {
return {
y: 0,
x: 0,
};
},
},
visible: {
type: Boolean,
default: false,
},
},
directives: { clickoutside },
data() {
return {
menuList: [
[{ label: "menu1", value: "menu1" }],
[
{ label: "menu2-1", value: "menu21" },
{ label: "menu2-2", value: "menu22" },
],
[{ label: "menu3", value: "menu3" }],
],
width: 0,
height: 0,
contextMenuStyle: {},
};
},
watch: {
position() {
this.changePosition();
},
visible(value) {
if (value && !this.width) {
this.$nextTick(() => {
const ele = document.getElementById("theContextMenu");
this.width = ele.offsetWidth;
this.height = ele.offsetHeight;
this.changePosition();
});
}
},
},
methods: {
changePosition() {
if (!this.width) {
return;
}
let left = this.position.x;
let top = this.position.y;
if (left + this.width > document.documentElement.clientWidth) {
left = left - this.width;
}
if (top + this.height > document.documentElement.clientHeight) {
top = top - this.height;
}
this.contextMenuStyle = {
top: top + "px",
left: left + "px",
};
},
clickMenu(value) {
this.$emit("on-click-menu", value);
this.closePicker();
},
closePicker() {
this.$emit("change", false);
},
},
};
</script>
<style>
.theContextMenu {
position: fixed;
border: 1px solid #ddd;
border-radius: 0.3rem;
background: #fff;
z-index: 1;
}
ul {
padding: 5px 0;
list-style: none;
}
li {
padding: 5px 10px;
cursor: pointer;
}
li:hover {
background: #f3f3f3;
}
.splitLine {
border-bottom: 1px solid #e8eaec;
}
</style>