vue3 鼠标右键自定义菜单组件

前言

在开发中,在点击单元行的时候,产品会想右键可以自定义菜单,可以覆盖系统自带的功能,想着写了一个组件,来循环列表单元行

 运行效果

HTML
<template>
  <div
    class="context-menu-wrapper"
    @contextmenu.prevent="showContextMenu"
    ref="contextMenuWrapper"
    :style="{
      width: width + 'px',
      height: height + 'px',
    }"
  >
    <!-- 上下文菜单 -->
    <transition name="context-menu-fade">
      <div
        v-if="isMenuVisible"
        class="context-menu"
        :style="{ top: `${menuPosition.y}px`, left: `${menuPosition.x}px` }"
        @mouseleave="hideContextMenu"
        ref="contextMenu"
      >
        <ul>
          <!-- 动态渲染菜单项 -->
          <li
            v-for="(item, index) in menuItems"
            :key="index"
            @click="handleAction(item)"
          >
            {{ item.label }}
          </li>
        </ul>
      </div>
    </transition>

    <!-- 父组件传入的内容 -->
    <span>{{ text }}</span>
  </div>
</template>
JS
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";

interface MenuItem {
  label: string;
  action?: () => void;
}

// 接收父组件传递的菜单项、宽度和高度
const props = defineProps({
  menuItems: {
    type: Array as PropType<MenuItem[]>,
    required: true,
    default: [],
  },
  width: {
    type: Number,
    default: 50,
  },
  height: {
    type: Number,
    default: 50,
  },
  text: {
    type: String,
    default: "",
  },
});

// 组件状态
const isMenuVisible = ref(false);
const menuPosition = ref({ x: 0, y: 0 });

// 显示菜单
const showContextMenu = (event: MouseEvent) => {
  const contextMenuWrapper = event.currentTarget as HTMLElement;
  const rect = contextMenuWrapper.getBoundingClientRect();
  const x = event.clientX - rect.left;
  const y = event.clientY - rect.top;
  menuPosition.value = { x, y };
  isMenuVisible.value = true;
};

// 隐藏菜单
const hideContextMenu = () => {
  isMenuVisible.value = false;
};

// 处理菜单项点击事件
const handleAction = (item: MenuItem) => {
  if (item.action) {
    item.action(); // 调用父组件传入的事件
  } else {
    console.log(item.label);
  }
  hideContextMenu(); // 点击后隐藏菜单
};

// 监听点击事件,关闭菜单
const handleClickOutside = (event: MouseEvent) => {
  const contextMenu = document.querySelector(".context-menu") as HTMLElement;
  const contextMenuWrapper = document.querySelector(
    ".context-menu-wrapper"
  ) as HTMLElement;

  // 判断点击是否发生在菜单外部
  if (
    contextMenuWrapper &&
    !contextMenuWrapper?.contains(event.target as Node) &&
    !contextMenu?.contains(event.target as Node)
  ) {
    hideContextMenu();
  }
};

onMounted(() => {
  // 在组件挂载时添加全局点击事件监听
  document.addEventListener("click", handleClickOutside);
});

onUnmounted(() => {
  // 在组件卸载时移除事件监听
  document.removeEventListener("click", handleClickOutside);
});
</script>
 CSS
<style>
.context-menu-wrapper {
  border: #ccc 1px solid;
  position: relative;
  display: flex;
  align-items: center;
}

.context-menu {
  position: absolute;
  background-color: white;
  border: 1px solid #ccc;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  width: 150px;
  z-index: 9999;
}

.context-menu ul {
  padding: 0;
  margin: 0;
  list-style: none;
}

.context-menu li {
  padding: 8px 12px;
  cursor: pointer;
}

.context-menu li:hover {
  background-color: #f0f0f0;
}

/* 定义上下文菜单的动画 */
.context-menu-fade-enter-active,
.context-menu-fade-leave-active {
  transition:
    opacity 0.3s,
    transform 0.3s;
}

.context-menu-fade-enter,
.context-menu-fade-leave-to {
  opacity: 0;
  transform: translateY(-10px);
}
</style>
组件使用
 <right-click
    :width= "400"  
    :height= "50"  
    :text='标题'   
    :menuItems=[
      {
        label: '按钮1',
        action: ()=>{ console.log('按钮1') }
      },
      {
        label: '按钮2',
        action: ()=>{ console.log('按钮2') }
      },
      {
        label: '按钮3',
        action: ()=>{ console.log('按钮3') }
      },
    ]
  />

这样就可以自定义右键弹出的菜单按钮和对应的功能了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值