Vue+element-UI implements right-click menu

Please indicate the original address for reprinting
. The general idea is similar. The specific implementation depends on whether the control provides a callback method for the right-click menu. The style of the right-click menu can also be changed according to your own needs.

new version

  • Based on vue3
  • Support automatic adjustment of offset to prevent the menu from exceeding the screen

Encapsulate the rightMenu component

<!--
 * @Description: 右键菜单
 * @Author: r_yuesheng
 * @Date: 2021-08-09 16:13:32
 * @LastEditTime: 2021-011-09 17:33:07
 * @LastEditors: r_yuesheng
-->
<template>
  <div>
    <ul :id="rightType" class="rightmenu" v-show="menuVisible">
      <li
        class="menu_item"
        :style="menu.customStyle"
        v-for="(menu, menuIndex) in menuOptions"
        :key="menuIndex"
        @click="menuClick(menu)"
      >
        <i :class="menu.icon"></i>
        <span>{
    
    {
    
     menu.label }}</span>
      </li>
    </ul>
  </div>
</template>

<script>
import {
    
     reactive, toRefs, watch } from "vue";
export default {
    
    
  emits: ["backEvent"],
  name: "rightMenu",
  props: {
    
    
    //右键类型,目前只有两种 table和tree
    rightType: String,
    //禁用按钮
    menuDisabled: {
    
    
      type: Array,
      default: () => {
    
    },
    },
    //是否显示
    menuVisible: Boolean,
    //鼠标事件
    menuEvent: Object,
    //自定义右键菜单内容
    menuOptions: {
    
    
      type: Array,
      default: () => {
    
    
        return [
          {
    
    
            label: "新建",
            icon: "el-icon-folder-add r_menu_icon",
            event: "create",
          },
        ];
      },
    },
  },
  setup(props, {
     
      emit }) {
    
    
    const state = reactive({
    
    
      listType: ["table", "tree"],
      currViewPortHeight: document.body.clientHeight,
      menuLenth: 100,
    });
    watch(
      () => props.menuEvent,
      (value) => {
    
    
        state.listType.forEach((item) => {
    
    
          const menu = document.querySelector(`#${
      
      item}`);
          if (menu) {
    
    
            menu.style.display = "none";
          }
        });
        initMenuDisable();
        state.currViewPortHeight = document.body.clientHeight;
        state.menuLenth = props.menuOptions.length * 38;
        // const tempMouseEvent = value.MouseEvent;
        treeRightClick();
      }
    );
    watch(
      () => props.menuVisible,
      (value) => {
    
    
        if (!value) {
    
    
          foo();
        }
      }
    );
    function menuClick(type) {
    
    
      emit("backEvent", type.event);
      foo();
    }
    function treeRightClick() {
    
    
      const {
    
     MouseEvent } = props.menuEvent;
      const menu = document.querySelector(`#${
      
      props.rightType}`);
      menu.style.display = "";
      window.event.returnValue = false;
      const tempCurrMenuHeight = MouseEvent.clientY + state.menuLenth;
      menu.style.left = `${
      
      MouseEvent.clientX + 10}px`;
      if (tempCurrMenuHeight > state.currViewPortHeight) {
    
    
        menu.style.top = `${
      
      state.currViewPortHeight - state.menuLenth - 50}px`;
      } else {
    
    
        menu.style.top = `${
      
      MouseEvent.clientY - 40}px`;
      }
      document.addEventListener("click", foo);
    }
    function foo() {
    
    
      emit("backEvent", null);
      document.removeEventListener("click", foo);
    }
    function initMenuDisable() {
    
    
      props.menuOptions.forEach((item) => {
    
    
        if (props.menuDisabled.includes(item.event)) {
    
    
          item.customStyle = "pointer-events:none;color:#c5cbd8;";
        } else {
    
    
          item.customStyle = "";
        }
      });
    }
    return {
    
    
      ...toRefs(state),
      menuClick,
      foo,
      treeRightClick,
    };
  },
};
</script>

<style scoped>
.rightmenu {
    
    
  padding: 0 15px;
  position: absolute;
  border-radius: 4px;
  background-color: #fff;
  padding: 5px 0;
  text-align: left;
  box-shadow: 0 3px 6px 3px #dbdde5;
  z-index: 2;
  color: #4d556f;
}
.rightmenu .menu_item {
    
    
  display: block;
  line-height: 28px;
  text-align: left;
  padding: 4px 12px;
  font-size: 14px;
}
.rightmenu li:hover {
    
    
  background-color: #4671ff;
  color: #fff;
  cursor: pointer;
  transition: ease all 0.2s;
}
</style>

page use

Based on el-tree use

<template>
  <RightMenu
    rightType="tree" 
    :menuVisible="showTreeMenu"
    :menuEvent="objectTreeMenuEvent"
    :menuOptions="objMenuOptions"
    @backEvent="backEvent"
  ></RightMenu>
</template>
<script>
import RightMenu from './rightMenu.vue';//引入右键菜单组件
</script> 

Based on el-table use

  <TableRightMenu
    :menuVisible="tableRightMenu"
    :menuDisabled="listMenuDisabled"
    rightType="table"
    :menuEvent="objectTableMenuEvent"
    :menuOptions="objMenuOptions"
    @backEvent="backEvent"
  ></TableRightMenu>

old version

1. The style of the right-click menu

    <div v-show="menuVisible">
    		<ul id="menu" class="menu">
    				<li class="menu__item">新增</li>
    				<li class="menu__item">重命名</li>
    				<li class="menu__item">删除</li>
    		</ul>
    </div>
    
.menu__item {
    
    
	display: block;
	line-height: 20px;
	text-align: center;
	margin:10px;
	cursor: default;
}
.menu__item:hover{
    
    
	color: #FF0000;
}

.menu {
    
    
 	height: auto;
 	width: auto;
 	position: absolute;
 	font-size: 14px;
 	text-align: left;
 	border-radius: 10px;
 	border: 1px solid #c1c1c1;
 	background-color: #ffffff;
}

li:hover {
    
    
  background-color: #E0E0E2;
  color: white;
}

2. Public methods

method:{
    
    
    rightClick(row, event) {
    
    
      this.menuVisible = false; // 先把模态框关死,目的是 第二次或者第n次右键鼠标的时候 它默认的是true
      this.menuVisible = true; // 显示模态窗口,跳出自定义菜单栏
      var menu = document.querySelector('#menu');
      this.styleMenu(menu);
    },
    foo() {
    
    
      // 取消鼠标监听事件 菜单栏
      this.menuVisible = false;
      document.removeEventListener('click', this.foo); // 要及时关掉监听,不关掉的是一个坑,不信你试试,虽然前台显示的时候没有啥毛病,加一个alert你就知道了
    },
    styleMenu(menu) {
    
    
      if (event.clientX > 1800) {
    
    
        menu.style.left = event.clientX - 100 + 'px';
      } else {
    
    
        menu.style.left = event.clientX + 1 + 'px';
      }
      document.addEventListener('click', this.foo); // 给整个document新增监听鼠标事件,点击任何位置执行foo方法
      if (event.clientY > 700) {
    
    
        menu.style.top = event.clientY - 30 + 'px';
      } else {
    
    
        menu.style.top = event.clientY - 10 + 'px';
      }
    }
}

3. Choose a way to bind data by yourself

The first type: el-tree tree form
insert image description here

 <el-tree :data="templateData" :prop="templateData.tempName"  @node-contextmenu="rightClick"></el-tree>

The second el-table form
insert image description here

<el-table :data="Data"   @row-contextmenu="rightClick"></el-table>

I love you three thousand times❤

Guess you like

Origin blog.csdn.net/r657225738/article/details/90479983