如何自定义动态右键菜单组件

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

 

注意点: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>
复制代码

 

 
 
posted @   10后程序员劝退师  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
历史上的今天:
2019-04-10 如何使用 mps 开发原生小程序
点击右上角即可分享
微信分享提示