如何自定义动态右键菜单组件
如果自定义动态右键菜单组件,模块与模块之间互不影, 细节样式可以后续补充
注意点: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>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
2019-04-10 如何使用 mps 开发原生小程序