vue自定义右键菜单之全局实现


Posted in Vue.js onApril 09, 2022

前段时间公司在做一个webIDE项目,其中有对文件树的各种操作,主要通过右键菜单实现,今天就来记录一下怎么在vue项目中实现全局的自定义右键菜单。效果如图所示:

vue自定义右键菜单之全局实现

注意:

需要在项目中找到页面整体布局的组件,比如项目使用Home.vue作为整个项目的公共布局,将根元素定位设置成relative,使右键菜单相对于其进行定位。

本文的右键菜单主要使用vuex实现

一、vuex中定义全局状态用于管理右键菜单

export default {
    /**
     * menuContent格式:
     * [
     *      {
     *          name: '新建文件',   // 操作名
     *          method: 'createDirectory',  // 需要执行的对应组件中的方法
     *             disabled: true        // 是否禁用,可以根据页面具体逻辑进行调整
     *      }
     * ]
     * 
     * 
     * 整体右键菜单采用绝对定位,所以clientX、clientY代表是left和top定位
     */
    state: {
        menuContent: [],    // 右键菜单内容
        clientX: '',    // left
        clientY: '',    // top
        displayContextMenu: false   // 是否展示右键菜单
    },
    mutations: {
        SET_CONTEXT_MENU: (state, payload) => {
            state.menuContent = payload.menuContent;
            state.clientX = payload.clientX;
            state.clientY = payload.clientY;
            state.displayContextMenu = payload.displayContextMenu;
        },
    }
}

二、定义公共组件ContextMenu.vue

<template>
    <div class="context-menu" v-show="contextMenu.displayContextMenu" 
        :style="{'left': contextMenu.clientX + 'px', 'top': contextMenu.clientY + 'px'}">
        <ul>
            <li v-for="(item, i) in contextMenu.menuContent" :key="i" :class="item.disabled ? 'disabled' : ''" 
                @click="emitEvent(item.method)">
                {{item.name}}
            </li>
        </ul>
    </div>
</template>

<script>
import { mapState } from 'vuex';
import bus from '@/views/common/bus';
export default {
    name: 'ContextMenu',
    data(){
        return {
        }
    },
    computed: {
        ...mapState(['contextMenu'])
    },
    methods: {
        emitEvent(type){
            bus.$emit('operateDirectory', type)
        }
    }
}
</script>

<style lang="scss" scoped>
    .context-menu {
        position: absolute;
        background: #FFF;
        color: #34495E;
        min-width: 100px;
        box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.2);
        border-radius: 2px;
        cursor: pointer;
        z-index: 1002;
        &>ul {
            text-align: left;
            padding: 5px 10px;
            &>li {
                padding: 3px 4px;
                font-size: 12px;
                list-style: none;
                height: 24px;
                line-height: 24px;
                &:hover {
                    background: #EDF6FF;
                }
            }
            .disabled {
                color: #888585;
                pointer-events: none;
            }
        }
    }
</style>

三、在组件中使用

import { mapState } from 'vuex';
// ...

computed: {
    ...mapState(['contextMenu'])
},
created(){
    bus.$on('operateDirectory', (param) => {
    // 点击右键菜单时,应触发组件内的同名方法,首先应判断该方法是否在本组件内存在,存在则调用
        if(this[param]){
            this[param]();
        }
    });
},
// ...

methods: {
    showContextMenu(event, data) {
        event.preventDefault();        // 阻止浏览器的默认事件
        const menuContent = {
            menuContent: [
            {
                icon: "el-icon-upload2",
                name: "运行",
                method: "run",
            },
            {
                icon: "el-icon-refresh",
                name: "编辑",
                method: "editConfig",
            },
            {
                icon: "el-icon-refresh",
                name: "添加",
                method: "addCommond",
            },
            {
                icon: "el-icon-refresh",
                name: "删除",
                method: "deleteConfig",
            },
            ],
            clientX: event.clientX,
            clientY: event.clientY,
            displayContextMenu: true,
        };
        this.$store.commit("SET_CONTEXT_MENU", menuContent);
        // 再次点击页面,右键菜单消失
        document.onclick = (event) => {
            this.$store.commit("SET_CONTEXT_MENU", {
                displayContextMenu: false,
            });
        };
    },
    run(){
        // ...
    },
    editConfig(){
        // ...
    },
    addCommond(){
        // ...
    },
    deleteConfig(){
        // ...
    }
}

总结

这种可以实现全局右键菜单,并且支持自定义右键内容,但是vue3.0对event bus的取消会导致不可用。

目前有一种优化方法:项目中对应会使用组件库,例如element ui,在定义contextMenu.vue的时候,使用dialog,将内容定义在Popover 弹出框中,当组件使用右键菜单时,使用子组件的方式使用ContextMenu.vue,点击右键菜单内容时就不需要使用emit触发了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Vue.js 相关文章推荐
Vue router传递参数并解决刷新页面参数丢失问题
Dec 02 Vue.js
Vue如何实现验证码输入交互
Dec 07 Vue.js
详解vue中使用transition和animation的实例代码
Dec 12 Vue.js
Vue3 实现双盒子定位Overlay的示例
Dec 22 Vue.js
基于Vue3.0开发轻量级手机端弹框组件V3Popup的场景分析
Dec 30 Vue.js
jenkins自动构建发布vue项目的方法步骤
Jan 04 Vue.js
详解实现vue的数据响应式原理
Jan 20 Vue.js
用vite搭建vue3应用的实现方法
Feb 22 Vue.js
vue3.0中使用element的完整步骤
Mar 04 Vue.js
详解Vue slot插槽
Nov 20 Vue.js
Vue.js中v-bind指令的用法介绍
Mar 13 Vue.js
vue中this.$http.post()跨域和请求参数丢失的解决
Apr 08 Vue.js
vue判断按钮是否可以点击
Apr 09 #Vue.js
VUE之图片Base64编码使用ElementUI组件上传
Apr 09 #Vue.js
vue如何实现关闭对话框后刷新列表
Apr 08 #Vue.js
vue实现列表垂直无缝滚动
Apr 08 #Vue.js
vue3引入highlight.js进行代码高亮的方法实例
vue中的可拖拽宽度div的实现示例
vue 实现弹窗关闭后刷新效果
Apr 08 #Vue.js
You might like
php网页标题中文乱码的有效解决方法
2014/03/05 PHP
thinkPHP3.x常量整理(预定义常量/路径常量/系统常量)
2016/05/20 PHP
Yii的Srbac插件用法详解
2016/07/14 PHP
PHP实现权限管理功能示例
2017/09/22 PHP
DEFER怎么用?
2006/07/01 Javascript
风吟的小型JavaScirpt库 (FY.JS).
2010/03/09 Javascript
基于Jquery的将DropDownlist的选中值赋给label的实现代码
2011/05/06 Javascript
关于IE中getElementsByClassName不能用的问题解决方法
2013/08/26 Javascript
jQuery EasyUI之DataGrid使用实例详解
2016/01/04 Javascript
深入理解jQuery中的事件冒泡
2016/05/24 Javascript
bootstrap输入框组代码分享
2016/06/07 Javascript
jquery表单插件form使用方法详解
2017/01/20 Javascript
使用openSpeDiv方法实现Ecshop登录弹窗框效果
2017/03/13 Javascript
angular $watch 一个变量的变化(实例讲解)
2017/08/02 Javascript
基于require.js的使用(实例讲解)
2017/09/07 Javascript
微信禁止下拉查看URL的处理方法
2017/09/28 Javascript
vue 组件使用中的一些细节点
2018/04/25 Javascript
Node.js 使用AngularJS的方法示例
2018/05/11 Javascript
小程序开发踩坑:页面窗口定位(相对于浏览器定位)(推荐)
2019/04/25 Javascript
有关vue 开发钉钉 H5 微应用 dd.ready() 不执行问题及快速解决方案
2020/05/09 Javascript
将Django框架和遗留的Web应用集成的方法
2015/07/24 Python
python列表操作之extend和append的区别实例分析
2015/07/28 Python
使用python实现tcp自动重连
2017/07/02 Python
对python requests的content和text方法的区别详解
2018/10/11 Python
python for循环输入一个矩阵的实例
2018/11/14 Python
Django集成CAS单点登录的方法示例
2019/06/10 Python
Python实现二叉树的最小深度的两种方法
2019/09/30 Python
CSS3实现内凹圆角的实例代码
2017/05/04 HTML / CSS
运动会入场词100字
2014/02/06 职场文书
小区物业门卫岗位职责
2014/04/10 职场文书
小学优秀辅导员事迹材料
2014/05/11 职场文书
好听的队名和口号
2014/06/09 职场文书
党支部遵守党的政治纪律情况对照检查材料
2014/09/26 职场文书
上班时间打瞌睡检讨书
2014/09/26 职场文书
私人房屋买卖协议书
2014/10/04 职场文书
Python绘制散点图之可视化神器pyecharts
2022/07/07 Python