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项目利用axios请求接口下载excel
Nov 17 Vue.js
Vue实现指令式动态追加小球动画组件的步骤
Dec 18 Vue.js
vue中使用echarts的示例
Jan 03 Vue.js
vue实现按钮切换图片
Jan 20 Vue.js
手动实现vue2.0的双向数据绑定原理详解
Feb 06 Vue.js
vue-router懒加载的3种方式汇总
Feb 28 Vue.js
vue引入Excel表格插件的方法
Apr 28 Vue.js
Vue实现跑马灯样式文字横向滚动
Nov 23 Vue.js
Vue的过滤器你真了解吗
Feb 24 Vue.js
vue3使用vuedraggable实现拖拽功能
Apr 06 Vue.js
vue 数字翻牌器动态加载数据
Apr 20 Vue.js
vue css 相对路径导入问题级踩坑记录
Jun 05 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
使用Apache的rewrite技术
2006/06/22 PHP
Gregarius中文日期格式问题解决办法
2008/04/22 PHP
php中大括号作用介绍
2012/03/22 PHP
PHP Directory 函数的详解
2013/03/07 PHP
PHP数据库链接类(PDO+Access)实例分享
2013/12/05 PHP
ThinkPHP3.1数据CURD操作快速入门
2014/06/19 PHP
php封装的验证码工具类完整实例
2016/10/19 PHP
PHP自定义序列化接口Serializable用法分析
2017/12/29 PHP
jQuery中scrollLeft()方法用法实例
2015/01/16 Javascript
利用js实现禁止复制文本信息
2015/06/03 Javascript
AngularJS中的过滤器使用详解
2015/06/16 Javascript
使用JQuery实现智能表单验证功能
2016/03/08 Javascript
最简单纯JavaScript实现Tab标签页切换的方式(推荐)
2016/07/25 Javascript
JS实现重新加载当前页面或者父页面的几种方法
2016/11/30 Javascript
JS中如何实现Laravel的route函数详解
2017/02/12 Javascript
js实现图片旋转 js滚动鼠标中间对图片放大缩小
2017/07/05 Javascript
es6 字符串String的扩展(实例讲解)
2017/08/03 Javascript
jQuery轻量级表单模型验证插件
2018/10/15 jQuery
关于Vue Router中路由守卫的应用及在全局导航守卫中检查元字段的方法
2018/12/09 Javascript
Vuex mutitons和actions初使用详解
2019/03/04 Javascript
layui layer select 选择被遮挡的解决方法
2019/09/21 Javascript
JS表单验证插件之数据与逻辑分离操作实例分析【策略模式】
2020/05/01 Javascript
图解JS原型和原型链实现原理
2020/09/15 Javascript
python操作redis的方法
2015/07/07 Python
Python基础学习之常见的内建函数整理
2017/09/06 Python
详解python异步编程之asyncio(百万并发)
2018/07/07 Python
python实现两个经纬度点之间的距离和方位角的方法
2019/07/05 Python
详解Django admin高级用法
2019/11/06 Python
python实现梯度下降法
2020/03/24 Python
Kneipp克奈圃美国官网:德国百年精油配方的传承
2018/02/07 全球购物
香港家用健身器材、运动器材及健康美容仪器专门店:FitBoxx
2019/12/05 全球购物
旅游管理专业生自荐信范文
2014/01/02 职场文书
史学专业毕业生求职信
2014/05/09 职场文书
2016形势与政策学习心得体会
2016/01/12 职场文书
golang 实现Location跳转方式
2021/05/02 Golang
Python 如何实现文件自动去重
2021/06/02 Python