如何自定义动态右键菜单组件
如果自定义动态右键菜单组件,模块与模块之间互不影, 细节样式可以后续补充

注意点:Teleport解决定位问题,Transition解决动画问题
contextMenu组件
<template>
<div ref="continerRef">
<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 class="menu-item" v-for="(item, i) in menu" :key="item.lable" @click="handleClick(item)">
{{item.lable}}
</div>
</div>
</div>
</Transition>
</Teleport>
</div>
</template>
<script setup>
import { ref, defineProps, Transition } from 'vue'
import {useContextMenu} from './useContextMenu'
const continerRef = ref(null)
const props = defineProps({
menu: {
type:Array,
default: () => []
}
})
const emit = defineEmits([ 'select' ])
const handleClick = (item) => {
showMenu.value = false
emit('select', item)
}
const handleBeforeEnter = (el) => {
el.style.height = 0
}
const handleEnter = (el) => {
el.style.height = 'auto';
const h = el.clientHeight;
el.style.height = 0;
requestAnimationFrame(() => {
el.style.height = h + 'px';
el.style.transition = 'height 0.3s';
})
}
const handleAfterEnter = (el) => {
el.style.transition = 'none';
}
const { x, y, showMenu } = useContextMenu(continerRef)
</script>
<style lang="scss" scoped>
.context-menu{
position: fixed;
background: #eee;
box-shadow: 1px 1px 2px #000;
z-index: 999;
font-size: .08rem;
padding: .04px .02rem;
box-sizing: border-box;
text-align: center;
line-height: .2rem;
width: 100px;
.menu-item{
cursor: pointer;
&:hover{
background: #3477d9;
color: #fff;
}
}
}
// .v-enter-from {
// opacity: 0;
// }
// .v-enter-to {
// transition: 0.5s;
// opacity: 1;
// }
</style>
useContextMenu.js 方法函数
import { onMounted, onUnmounted, ref } from 'vue'
export const useContextMenu = (continerRef) => {
const showMenu = ref(false)
const x = ref(0)
const y = ref(0)
const handleContextMenu = (e) => {
e.preventDefault() //阻止默认事件
e.stopPropagation() //阻止冒泡
console.log(e.clientX, e.clientY)
showMenu.value = true
x.value = e.clientX
y.value = e.clientY
}
const closeMenu = () => {
showMenu.value = false
}
onMounted(() => {
const div = continerRef.value
div.addEventListener('contextmenu', handleContextMenu)
window.addEventListener('click', closeMenu, true) // true:保证捕获阶段处理该事件,不被外部默认事件捕获
window.addEventListener('contextmenu', closeMenu, true)
})
onUnmounted(() => {
const div = continerRef.value
div?.removeEventListener('contextmenu', handleContextMenu)
window.removeEventListener('click', closeMenu, true)
window.removeEventListener('contextmenu', closeMenu, true)
})
return {
x,
y,
showMenu
}
}
页面使用:
import ContextMenu from '@/components/contextMenu' <ContextMenu :menu="[ {lable: '添加'}, {lable: '编辑'}, {lable: '删除'}, ]" @select='choose = $event.lable' > <div class="textCont"> <span class="text">这是一条测试文案 {{choose}}</span> <ContextMenu :menu="[ {lable: '菜单1'}, {lable: '菜单2'}, ]" class="block" style="width:400px;height:200px;background:#f00;display: inline-block;" > </ContextMenu> </div> </ContextMenu>

浙公网安备 33010602011771号