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+iview分页组件的封装
Nov 17 Vue.js
VUE项目实现主题切换的多种方法
Nov 26 Vue.js
浅谈Vue使用Elementui修改默认的最快方法
Dec 05 Vue.js
vue组件是如何解析及渲染的?
Jan 13 Vue.js
vue实现禁止浏览器记住密码功能的示例代码
Feb 03 Vue.js
使用Vue.js和MJML创建响应式电子邮件
Mar 23 Vue.js
vue 中 get / delete 传递数组参数方法
Mar 23 Vue.js
Vue中foreach数组与js中遍历数组的写法说明
Jun 05 Vue.js
使用Vue3+Vant组件实现App搜索历史记录功能(示例代码)
Jun 09 Vue.js
vue-router中hash模式与history模式的区别
Jun 23 Vue.js
SSM VUE Axios详解
Oct 05 Vue.js
vue报错function () { [native code] },无法出现我们想要的内容 Unknown custom element
Apr 11 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入门学习笔记之一
2010/10/12 PHP
PHP fgetcsv 定义和用法(附windows与linux下兼容问题)
2012/05/29 PHP
PHP获取用户客户端真实IP的解决方案
2016/10/10 PHP
PHP微信分享开发详解
2017/01/14 PHP
理清PHP在Linxu下执行时的文件权限方法
2017/06/07 PHP
php微信开发之谷歌测距
2018/06/14 PHP
jquery struts 验证唯一标识(公用方法)
2013/03/27 Javascript
JS实现图片翻书效果示例代码
2013/09/09 Javascript
jQuery 无限级菜单的简单实例
2014/02/21 Javascript
实例讲解JS中数组Array的操作方法
2014/05/09 Javascript
jquery实现的一个简单进度条效果实例
2014/05/12 Javascript
使用jquery解析XML示例代码
2014/09/05 Javascript
js面向对象之静态方法和静态属性实例分析
2015/01/10 Javascript
jQuery简单实现QQ空间点赞已经取消点赞
2015/04/02 Javascript
轻量级jQuery插件slideBox实现带底栏轮播(焦点图)代码
2016/03/28 Javascript
Nodejs中 npm常用命令详解
2016/07/04 NodeJs
解决微信小程序中转换时间格式IOS不兼容的问题
2019/02/15 Javascript
bootstrap tooltips在 angularJS中的使用方法
2019/04/10 Javascript
Vue 实现分页与输入框关键字筛选功能
2020/01/02 Javascript
零基础写python爬虫之打包生成exe文件
2014/11/06 Python
Python中栈、队列与优先级队列的实现方法
2019/06/30 Python
python 进程的几种创建方式详解
2019/08/29 Python
Python3变量与基本数据类型用法实例分析
2020/02/14 Python
python爬取网易云音乐热歌榜实例代码
2020/08/07 Python
python安装cx_Oracle和wxPython的方法
2020/09/14 Python
python 基于wx实现音乐播放
2020/11/24 Python
如何用 Python 处理不平衡数据集
2021/01/04 Python
CSS3中:nth-child和:nth-of-type的区别深入理解
2014/03/10 HTML / CSS
HTML5 通过Vedio标签实现视频循环播放的示例代码
2020/08/05 HTML / CSS
芬兰攀岩、山地运动和户外活动用品购物网站:Bergfreunde
2016/10/06 全球购物
酒店大堂副理的职责范文
2014/02/13 职场文书
幼儿园教师教育感言
2014/02/28 职场文书
赔偿协议书怎么写
2015/01/28 职场文书
蜗居观后感
2015/06/11 职场文书
公司年会晚会开幕词
2019/04/02 职场文书
Vue Element plus使用方法梳理
2022/12/24 Vue.js