vue3实现一个通用的右键菜单组件
1、新建一个名为ContextMenu.vue的文件
<template> <div ref="containerRef"> <slot></slot> <Teleport to="body"> <Transition @beforeEnter="handleBeforeEnter" @enter="handleEnter" @afterEnter="handleAfterEnter"> <div v-if="showMenu" class="context-menu" :style="{ left: x + 'px', top: y + 'px' }"> <div class="menu-list"> <!-- 添加菜单的点击事件 --> <div @click="handleClick(item)" class="menu-item" v-for="(item, i) in menu" :key="item.label"> {{ item.label }} </div> </div> </div> </Transition> </Teleport> </div> </template> <script setup> import { ref } from 'vue'; import useContextMenu from './useContextMenu'; const props = defineProps({ menu: { type: Array, default: () => [], }, }); const containerRef = ref(null); const emit = defineEmits(['select']); const { x, y, showMenu } = useContextMenu(containerRef); // 菜单的点击事件 function handleClick(item) { // 选中菜单后关闭菜单 showMenu.value = false; // 并返回选中的菜单 emit('select', item); } function handleBeforeEnter(el) { el.style.height = 0; } function handleEnter(el) { el.style.height = 'auto'; const h = el.clientHeight; el.style.height = 0; requestAnimationFrame(() => { el.style.height = h + 'px'; el.style.transition = '.5s'; }); } function handleAfterEnter(el) { el.style.transition = 'none'; } </script> <style lang="scss" scoped> .context-menu{ position: absolute; .menu-list{ padding:0 10px; box-shadow: 0 0 2px 2px #f1f1f1; background: #fff; .menu-item{ padding: 4px 5px; text-align: center; cursor: pointer; &:hover{ color: #0073e5; } } } } </style>
2、新建useContextMenu.ts文件
import { onMounted, onUnmounted, ref } from "vue"; export default function (containerRef) { const showMenu = ref(false); const x = ref(0); const y = ref(0); const handleContextMenu = (e) => { e.preventDefault(); e.stopPropagation(); showMenu.value = true; x.value = e.clientX; y.value = e.clientY; }; function closeMenu() { showMenu.value = false; } onMounted(() => { const div = containerRef.value; div.addEventListener("contextmenu", handleContextMenu); window.addEventListener("click", closeMenu, true); window.addEventListener("contextmenu", closeMenu, true); }); onUnmounted(() => { const div = containerRef.value; div.removeEventListener("contextmenu", handleContextMenu); window.removeEventListener("click", closeMenu, true); window.removeEventListener("contextmenu", closeMenu, true); }); return { showMenu, x, y, }; }
3、使用方式如下:
<ContextMenu :menu="[
{ label: '添加' },
{ label: '编辑' },
{ label: '删除' },
{ label: '查看' },
{ label: '复制' },
]" @select="select($event.label)">
<div class="edit-mian-box">
<div class="edit-box">11111</div>
</div>
</ContextMenu>
效果图如下: