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 +WebSocket + WaveSurferJS 实现H5聊天对话交互的实例
Nov 18 Vue.js
用vue设计一个日历表
Dec 03 Vue.js
Vue解决移动端弹窗滚动穿透问题
Dec 15 Vue.js
vue 导航守卫和axios拦截器有哪些区别
Dec 19 Vue.js
Vue使用鼠标在Canvas上绘制矩形
Dec 24 Vue.js
Vue实现省市区三级联动
Dec 27 Vue.js
vue.js watch经常失效的场景与解决方案
Jan 07 Vue.js
Vue页面渲染中key的应用实例教程
Jan 12 Vue.js
vue实现可拖拽的dialog弹框
May 13 Vue.js
vue实现水波涟漪效果的点击反馈指令
May 31 Vue.js
vue3语法糖内的defineProps及defineEmits
Apr 14 Vue.js
vue实现input输入模糊查询的三种方式
Aug 14 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
Trying to clone an uncloneable object of class Imagic的解决方法
2012/01/11 PHP
PHP简单计算两个时间差的方法示例
2017/06/20 PHP
用脚本调用样式的几种方法
2006/12/09 Javascript
js实现九宫格图片半透明渐显特效的方法
2015/02/16 Javascript
jQuery实现表格颜色交替显示的方法
2015/03/09 Javascript
jquery悬浮提示框完整实例
2016/01/13 Javascript
JS上传组件FileUpload自定义模板的使用方法
2016/05/10 Javascript
基于cssSlidy.js插件实现响应式手机图片轮播效果
2016/08/30 Javascript
自己封装的一个原生JS拖动方法(推荐)
2016/11/22 Javascript
使用JavaScript实现链表的数据结构的代码
2017/08/02 Javascript
vue项目中应用ueditor自定义上传按钮功能
2018/04/27 Javascript
vue2.0中set添加属性后视图不能更新的解决办法
2019/02/22 Javascript
[49:43]VG vs FNATIC 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
Python  __getattr__与__setattr__使用方法
2008/09/06 Python
在Django的模板中使用认证数据的方法
2015/07/23 Python
解决python3中解压zip文件是文件名乱码的问题
2018/03/22 Python
Python实现html转换为pdf报告(生成pdf报告)功能示例
2019/05/04 Python
Python FTP文件定时自动下载实现过程解析
2019/11/12 Python
解决Keras 自定义层时遇到版本的问题
2020/06/16 Python
Python logging模块异步线程写日志实现过程解析
2020/06/30 Python
Python爬虫获取豆瓣电影并写入excel
2020/07/31 Python
OpenCV利用python来实现图像的直方图均衡化
2020/10/21 Python
CSS实现雨滴动画效果的实例代码
2019/10/08 HTML / CSS
与世界上最好的跑步专业品牌合作:Fleet Feet
2019/03/22 全球购物
医学院毕业生自荐信
2013/11/08 职场文书
生日宴会答谢词
2014/01/09 职场文书
婚纱摄影师求职信
2014/03/07 职场文书
学习十八大的心得体会
2014/09/12 职场文书
2014年班长个人工作总结
2014/11/14 职场文书
考试作弊检讨书范文
2015/01/27 职场文书
奖学金个人总结
2015/03/04 职场文书
2015年食品安全宣传周活动总结
2015/07/09 职场文书
MySQL 分组查询的优化方法
2021/05/12 MySQL
javascript canvas实现雨滴效果
2021/06/09 Javascript
MIME类型中application/xml与text/xml的区别介绍
2022/01/18 HTML / CSS
Golang 切片(Slice)实现增删改查
2022/04/22 Golang