组件使用方法:

import ContextMenu from "./ContextMenu";

// 为需要触发的容器绑定右键点击事件
<div class="container" @contextmenu.prevent="onContextMenu">
    <context-menu 
        :context-menu-show.sync="contextShow" 
        :config="contextConfig" 
        @edit="onEdit">
    </context-menu>
</div>


onContextMenu({ clientX,clientY }) {
    Object.assign(this,{
        contextConfig: {
            offsetLeft: clientX,
            offsetTop: clientY,
        },
    contextShow: true
  })
}

config参数示例:

contextConfig: {
    // 右键点击距左位置
    offsetLeft: 0,
    // 右键点击距上位置
    offsetTop: 0,
    menuList: [
        // 无需按键监听可以不传keyCode
        {label: '编辑', id: 1,des: 'E',keyCode: 69,emitType: 'edit' },
        {label: '删除', id: 2,des: 'D',keyCode: 68,emitType: 'del' },
        {label: '撤回', id: 3,des: 'R',keyCode: 82,emitType: 'return' }
   ]
}

组件完整代码:


由于click事件与全局的mousedown和mousewheel有冲突,需要额外绑定@mousedown.stop

<template>
    <ul class="context-menu" :style="{ left: `${config.offsetLeft}px`,top: `${config.offsetTop}px` }" v-show="contextMenuShow">
        <li v-for="item in menuList" :key="item.id" @mousedown.stop @click.passive="handleClick(item)">
            <a href="javascript:void(0)">{{ item.label }}</a>
            <span v-if="item.des">{{ item.des }}</span>
        </li>
    </ul>
</template>

<script>
    export default {
        name: 'context-menu',
        data() {
            // 传入watchKeyEvent= false,则不再监听按键点击
            const { menuList = [],watchKeyEvent = true } = this.config || {}
            return {
                menuList,
                watchKeyEvent,
            }
        },
        props: {
            config: Object,
            contextMenuShow: Boolean
        },
        mounted() {
            const { menuList,watchKeyEvent } = this

            document.addEventListener('mousedown', this.triggerHide)
            document.addEventListener('mousewheel', this.triggerHide)

            if (watchKeyEvent) {
                document.addEventListener('keydown', ({ keyCode }) => {
                    const res = menuList.find(item => {
                        return item.keyCode === keyCode
                    })
                    if(res !== undefined && res.emitType) {
                        this.$emit(res.emitType)
                    }
                })
            }
        },
        destroyed() {
            // 组件销毁时删除监听
            document.removeEventListener('mousedown', this.triggerHide)
            document.removeEventListener('mousewheel', this.triggerHide)
        },
        methods: {
            triggerHide() {
                this.$emit('update:contextMenuShow', false)
            },
            handleClick(item) {
                item.emitType && this.$emit(item.emitType)
            }
        }
    }
</script>

<style lang="scss" scoped>
    .context-menu {
        position: fixed;
        border: 1px solid #eee;
        box-shadow: 0 0.5em 1em 0 rgba(0, 0, 0, .1);
        border-radius: 1px;
        background: #fff;
        z-index: 999;
        font-size: 14px;
        display: block;
        width: 200px;
        li {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 2px 10px 2px 30px;
            cursor: pointer;
            &:hover {
                background: #42b983;
                color: #fff;
            }
        }
        a {
            height: 25px;
            line-height: 25px;
            display: block;
            color: #1a1a1a;
            text-decoration: none;
        }
        span {
            font-size: 12px;
            color: gray;
        }
    }
</style>

参考链接:

https://github.com/xunleif2e/vue-context-menu 

Logo

前往低代码交流专区

更多推荐