工作实战:Vue封装右键菜单组件

在这里插入图片描述
解决问题是,在点击色块区域的时候,显示不同的右键菜单。同时,希望右键菜单的高度,以动画的形式,慢慢变高。

组件封装

ContextMenu.vue

<template>
  <div ref="containRef">
    <slot></slot>
    <Teleport to="body">
      <Transition @beforeEnter="handleBeforEnter" @enter="handleEnter" @afterEnter="handleAfterEnter">
        <div class="context-menu" v-if="showMenu" :style="{
          left: x + 'px',
          top: y + 'px'
        }">
          <div class="menu-list">
            <div class="menu-item" @click="handleClcik(item)" v-for="(item, index) in menu" :key="item.label">
              {{ item.label }}
            </div>
          </div>
        </div>
      </Transition>
    </Teleport>

  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import useContextMenu from './useContextMenu'

defineProps({
  menu: {
    type: Array<any>,
    default: () => []
  }
})

const emits = defineEmits(['select'])

const containRef = ref(null)

const { x, y, showMenu } = useContextMenu(containRef)

const handleClcik = (item: any) => {
  emits('select', item)
  showMenu.value = false
}

const handleBeforEnter = (el: any) => {
  el.style.height = 0;
}

const handleEnter = (el: any) => {
  el.style.height = 'auto';
  const h = el.clientHeight;
  el.style.height = 0;
  requestAnimationFrame(() => {
    el.style.height = h + 'px';
    el.style.transition = '0.5s';
  })
}

const handleAfterEnter = (el: any) => {
  el.style.transition = 'none';
}

</script>

<style>
.context-menu {
  position: fixed;
  background: #eee;
  box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
  min-width: 100px;
  overflow: hidden;
  border-radius: 5px;
}

.menu-item {
  padding: 5px;
  color: #1d1d1f;
}

.menu-item:hover {
  background: #3477d9;
  color: #fff;
}

.v-enter-from {
  opacity: 0;
}

.v-enter-to {
  transition: 0.5s;
  opacity: 1;
}
</style>

useContextMenu.ts文件 这个钩子的作用就是 获取鼠标距离视口的x y坐标,然后显示右键菜单

import { onUnmounted } from "vue"
import { ref } from "vue"
import { onMounted } from "vue"

const useContextMenu = (containerRef: any) => {
  const showMenu = ref(false)
  const x = ref(0)
  const y = ref(0)

  const handleContextMenu = (e: MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    showMenu.value = true
    x.value = e.clientX
    y.value = e.clientY
  }

  const closeMenu = () => {
    showMenu.value = false
  }

  onMounted(() => {
    const div = containerRef.value as HTMLElement;
    div.addEventListener("contextmenu", handleContextMenu);
    window.addEventListener("contextmenu", closeMenu, true);
    window.addEventListener('click', closeMenu, true)
  })

  onUnmounted(() => {
    const div = containerRef.value as HTMLElement;
    div && div.removeEventListener('contextmenu', handleContextMenu)
    window.removeEventListener("contextmenu", closeMenu);
    window.removeEventListener('click', closeMenu)
  })

  return {
    x,
    y,
    showMenu
  }
}

export default useContextMenu

使用

App.vue文件

<template>
  <div class="container" @click.stop="">
    <ContextMenu class="block1" :menu="[
      { label: '添加' },
      { label: '删除' },
      { label: '编辑' },
      { label: '查看' },
      { label: '复制' },
    ]" @select="choose1">
      <h2>{{ choose1Text }}</h2>
    </ContextMenu>

    <ContextMenu class="block2" :menu="[
      { label: '员工2' },
      { label: '部门2' },
      { label: '角色2' },
    ]" @select="choose2">
      <h2>{{ choose2Text }}</h2>
      <ContextMenu class="block3" :menu="[
        { label: '员工3' },
        { label: '部门3' },
        { label: '角色3' },
      ]" @select="choose3">
        <h2>{{ choose3Text }}</h2>
      </ContextMenu>
    </ContextMenu>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import ContextMenu from '../ContextMenu/index.vue'

const choose1Text = ref('')
const choose2Text = ref('')
const choose3Text = ref('')

const choose1 = (event: any) => {
  choose1Text.value = event.label
}

const choose2 = (event: any) => {
  choose2Text.value = event.label
}

const choose3 = (event: any) => {
  choose3Text.value = event.label
}

</script>

<style>
.container {
  background: #ffffff;
  display: flex;
  justify-content: space-between;
}

.block1 {
  width: 30%;
  height: 300px;
  margin-right: 30px;
  background-color: aqua;
}

.block2 {
  flex: 1;
  height: 300px;
  padding: 20px;
  box-sizing: border-box;
  background-color: red;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.block3 {
  height: 100px;
  background-color: skyblue;
}
</style>

总结 :

1、之所使用Teleport组件,是因为fixed的定位一般情况下是相对于视口的,但是如果父组件的样式是transform时,这是fixed的定位就相对于父组件了

2、老板可能要求菜单高度需要动画,组件里无法获取菜单项的具体数量,只能使用js来动态的获取高度值了,如何获取请看 handleEnter 事件里的代码

3、如何在点击页面别的区域时,取消右键菜单,需要在windows上添加关闭弹出框,注意的是。可能用户在处于某些需要,给组件的父元素添加了阻止冒泡事件,需要在捕获阶段关闭弹出框

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将vue3右键菜单组件固定在表格内部,可以使用以下步骤: 1. 首先,在表格的外层包裹一个相对定位的 div 元素,这个 div 元素的高度和宽度要与表格相同。 2. 在这个 div 元素内部添加一个绝对定位的 div 元素,这个 div 元素的高度和宽度也要与表格相同。 3. 在这个绝对定位的 div 元素内部添加右键菜单组件。 4. 为这个绝对定位的 div 元素添加一个事件监听器,当鼠标右键点击时,显示右键菜单组件。 5. 在右键菜单组件的样式中,设置 position: fixed,这样右键菜单组件就可以固定在表格内部。 下面是一个示例代码: ```html <template> <div class="table-container"> <div class="table-wrapper"> <table> <!-- 表格内容 --> </table> <div class="context-menu" v-show="showMenu" @click="handleMenuClick"> <!-- 右键菜单内容 --> </div> </div> </div> </template> <script> export default { data() { return { showMenu: false, menuX: 0, menuY: 0 }; }, mounted() { document.addEventListener("contextmenu", this.handleContextMenu); document.addEventListener("click", this.handleMenuClose); }, beforeUnmount() { document.removeEventListener("contextmenu", this.handleContextMenu); document.removeEventListener("click", this.handleMenuClose); }, methods: { handleContextMenu(e) { e.preventDefault(); this.menuX = e.clientX; this.menuY = e.clientY; this.showMenu = true; }, handleMenuClose() { this.showMenu = false; }, handleMenuClick() { // 处理右键菜单的点击事件 } } }; </script> <style> .table-container { position: relative; height: 400px; /* 表格高度 */ width: 100%; /* 表格宽度 */ } .table-wrapper { position: absolute; top: 0; left: 0; height: 100%; width: 100%; } .context-menu { position: fixed; top: 0; left: 0; z-index: 999; /* 右键菜单的样式 */ } </style> ``` 在这个示例中,我们使用了一个外层的相对定位的 div 元素来包裹表格,然后在这个 div 元素内部添加了一个绝对定位的 div 元素,将右键菜单组件放在这个 div 元素内部。在右键菜单组件的样式中,设置了 position: fixed,这样右键菜单组件就可以固定在表格内部了。我们还为这个绝对定位的 div 元素添加了一个事件监听器,当鼠标右键点击时,显示右键菜单组件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值