工作上需要给 element plus 的 tree 组件添加右键菜单,参考网上的示例简单实现了一个,开箱即用,简单方便。代码和使用示例如下:
ContextMenu.vue
<template>
<div
v-show="visible"
:style="{
left: position.left + 'px',
top: position.top + 'px',
display: visible ? 'block' : 'none',
}"
class="context-menu"
>
<div
v-for="(item, i) in menuItems"
:key="i"
class="menu-item"
@click="item.action(rightClickItem)"
>
{{ item.name }}
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
interface Props {
menuItems: ContextMenuItem[];
}
export interface ContextMenuItem {
name: string;
icon?: string;
action: (rightClickItem: any) => void;
}
const props = defineProps<Props>();
const visible = ref(false);
const rightClickItem = ref(null);
const position = ref({
top: 0,
left: 0,
});
const openMenu = (e: MouseEvent, item: any) => {
let menuCount = props.menuItems.length;
let windowHeight = window.innerHeight;
visible.value = true;
position.value.top = Math.min(e.pageY, windowHeight - 40 - menuCount * 32);
position.value.left = e.pageX;
rightClickItem.value = item;
};
const closeMenu = () => {
visible.value = false;
};
watch(visible, () => {
if (visible.value) {
document.body.addEventListener("click", closeMenu);
} else {
document.body.removeEventListener("click", closeMenu);
}
});
defineExpose({ openMenu, closeMenu });
</script>
<style scoped lang="less">
.context-menu {
margin: 0;
background: #fff;
z-index: 2;
position: absolute;
list-style-type: none;
padding: 4px;
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #333;
box-shadow: 2px 2px 2px 2px rgba(0, 0, 0, 0.3);
.menu-item {
padding: 0 15px;
height: 32px;
line-height: 32px;
color: rgb(29, 33, 41);
cursor: pointer;
}
.menu-item:hover {
background: var(--el-color-primary-light-9);
border-radius: 4px;
}
}
</style>
使用示例:
<template>
<el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick">
<template #default="{ node }">
<span @contextmenu.prevent="contextMenuRef!.openMenu($event, node)">
{{ node.label }}
</span>
</template>
</el-tree>
<context-menu :menu-items="menuItems" ref="contextMenuRef" />
</template>
<script lang="ts" setup>
import { ref } from "vue";
import ContextMenu from "../components/contextMenu/ContextMenu.vue";
import { ContextMenuItem } from "../components/contextMenu/ContextMenu.vue";
interface Tree {
label: string;
children?: Tree[];
}
const contextMenuRef = ref<InstanceType<typeof ContextMenu>>();
const menuItems = ref<ContextMenuItem[]>([
{
name: "新增",
action: (item: any) => {
console.log(item, "新增");
},
},
{
name: "修改",
action: (item: any) => {
console.log(item, "修改");
},
},
{
name: "删除",
action: (item: any) => {
console.log(item, "删除");
},
},
]);
const handleNodeClick = (data: Tree) => {
console.log(data);
};
const data: Tree[] = [
{
label: "Level one 1",
children: [
{
label: "Level two 1-1",
children: [
{
label: "Level three 1-1-1",
},
],
},
],
},
{
label: "Level one 2",
children: [
{
label: "Level two 2-1",
children: [
{
label: "Level three 2-1-1",
},
],
},
{
label: "Level two 2-2",
children: [
{
label: "Level three 2-2-1",
},
],
},
],
},
{
label: "Level one 3",
children: [
{
label: "Level two 3-1",
children: [
{
label: "Level three 3-1-1",
},
],
},
{
label: "Level two 3-2",
children: [
{
label: "Level three 3-2-1",
},
],
},
],
},
];
const defaultProps = {
children: "children",
label: "label",
};
</script>
参考文章:vue3实现右键菜单