赞
踩
最近做的开源项目有个需求就是在ElementUI
的NavMenu
侧边栏中,实现鼠标右击打开菜单功能,今天实现了一下,做个记录。
鼠标右击打开菜单
首先,该需求需要拦截浏览器的右击事件,更改为自定义监听事件,可使用@contextmenu.prevent
直接绑定一个监听函数,它的作用就是拦截右击事件并触发绑定的监听事件。
其次,为了在右击的位置打开菜单,我们需要获取鼠标点击的位置;一般来说,若绑定的函数没有自定义参数,则可以直接使用默认的参数e
来获取;若绑定的函数具有自定义参数,则需要定义$event
来获取位置,即@contextmenu.prevent="rightClick(index, indexMeasure, $event)"
。
最后,通过参数event
可获取鼠标点击的横向X轴值和竖直Y轴值,然后使用固定定位来对菜单进行偏移,因为固定定位是相对于浏览器窗口,且获取的X和Y轴值也是相对于浏览器窗口的。
该需求的实现原理其实就是通过监听全局的鼠标点击事件来进行控制,由于菜单使用v-if
来进行控制,因此只需要监听到全局的鼠标点击事件中将其置为false
即可。其实现方式即在App.vue
中绑定一个mousedown
函数,且通过vuex
来进行通信(全局与侧边栏组件关系复杂,使用它更为方便)。代码如下:
<template>
<div id="app" @mousedown="appMouseDown">
<!-- 这里是自己的代码 -->
</div>
</template>
<script>
export default {
methods: {
appMouseDown() {
this.$store.commit("changeShowRightMenu", false)
}
}
}
</script>
该需求是为了避免在点击菜单项时,触发了其他元素绑定的监听函数。这其实跟图层有关,我们只需要将菜单置到顶层即可。使用的是z-index
属性,代码如下:
.right-menu {
width: 130px;
position: fixed;
z-index: 1000;
background-color: white;
}
该需求是对菜单的样式进行定义,不同项目不同需求,博主简单改了一下样式,并实现一个鼠标进入高亮的效果(类似ElementUI)。代码如下(注意,该代码是将菜单里面的一项封装为组件):
<template> <div class="flex right-menu-item align-item-center pointer" @mouseover="handleOver" @mouseleave="handleLeave" :style="{ backgroundColor: bdColor, color: colorStr}"> <i :class="icon"></i> <div style="width: 70%;" class="text-overflow-hidden margin-left-small">{{ name }}</div> </div> </template> <script> export default { name: "RightMenuItemComponent", data() { return { bdColor: "white", colorStr: "var(--color-main-font)" } }, props: ['icon', 'name'], methods: { handleOver() { this.bdColor = "#ecf5ff" this.colorStr = "var(--color-main)" }, handleLeave() { this.bdColor = "white" this.colorStr = "var(--color-main-font)" } } } </script> <style lang="scss" scoped> .right-menu-item { padding: 3px 6px 3px 6px; width: calc(100% - 12px); height: 40px; text-align: start; } i { font-size: 25px; color: var(--color-main); } </style>
<!-- 侧边栏 AsideComponent.vue --> <template> <div style="width:100%; height:100%"> <el-menu default-active="2" active-text-color="#6E38F7" style="height: 100%;" :collapse="isCollapse" :collapse-transition="false"> <!-- 侧边导航栏,自己的代码 --> <!-- 注意需要在菜单项上面绑定右击事件的拦截函数,使用div绑定 --> <div @contextmenu.prevent="rightClick(index, indexMeasure, $event)"> <el-menu-item index="file1"> <template slot="title"> <i class="el-icon-document"></i> <span slot="title" >file1</span> </template> </el-menu-item> </div> </el-menu> <div v-if="showRightMenu" class="right-menu border-radius-little box-shadow" :style="{top: topNumber+'px', left: leftNumber+'px'}"> <right-menu-component></right-menu-component> </div> </div> </template> <script> import { mapState } from 'vuex' import RightMenuComponent from './RightMenuComponent.vue' export default { name: 'AsideComponent', data() { return { topNumber: 0, leftNumber: 0 } }, components: { RightMenuComponent }, computed: { ...mapState({ showRightMenu: (state) => state.menuState.showRightMenu }) }, methods: { rightClick(index, indexMeasure, e) { this.$store.commit("changeShowRightMenu", true) this.topNumber = e.pageY this.leftNumber = e.pageX } }, } </script> <style lang="scss" scoped> .el-menu { width: 100%; border-right: none; text-align: start; text-justify: middle; } .right-menu { width: 130px; position: fixed; z-index: 1000; background-color: white; } i { font-size: 25px; color: var(--color-main); } </style>
<!-- 菜单组件 RightMenuComponent.vue --> <template> <div style="width: 100%;" class="border-radius-little"> <div style="width: 100%;" v-for="(item, index) in rightMenuList" :key="item.name"> <right-menu-item :class="index == rightMenuList.length - 1 ? 'border-radius-bottom-little' : index == 0 ? 'border-radius-top-little bd-bottom' : 'bd-bottom'" :icon="item.icon" :name="item.name"></right-menu-item> </div> </div> </template> <script> import RightMenuItem from './RightMenuItemComponent.vue' export default { name: "RightMenuComponent", data() { return { rightMenuList: [{ icon: "el-icon-document", name: "new File", id: "create" }, { icon: "el-icon-refresh-right", name: "refresh", id: "refresh" }, { icon: "el-icon-delete", name: "delete", id: "delete" }] } }, components: { RightMenuItem } } </script> <style lang="scss" scoped> </style>
<!-- 菜单项组件 RightMenuItemComponet.vue --> <template> <div class="flex right-menu-item align-item-center pointer" @mouseover="handleOver" @mouseleave="handleLeave" :style="{ backgroundColor: bdColor, color: colorStr}"> <i :class="icon"></i> <div style="width: 70%;" class="text-overflow-hidden margin-left-small">{{ name }}</div> </div> </template> <script> export default { name: "RightMenuItemComponent", data() { return { bdColor: "white", colorStr: "var(--color-main-font)" } }, props: ['icon', 'name'], methods: { handleOver() { this.bdColor = "#ecf5ff" this.colorStr = "var(--color-main)" }, handleLeave() { this.bdColor = "white" this.colorStr = "var(--color-main-font)" } } } </script> <style lang="scss" scoped> .right-menu-item { padding: 3px 6px 3px 6px; width: calc(100% - 12px); height: 40px; text-align: start; } i { font-size: 25px; color: var(--color-main); } </style>
// vuex // 注意博主将其模块化 // menuState.js export default { state: { showRightMenu: false }, mutations: { changeShowRightMenu(state, val) { state.showRightMenu = val } } } // index.js import Vue from 'vue' import Vuex from 'vuex' import menuState from './menuState' Vue.use(Vuex) export default new Vuex.Store({ state: { }, mutations: { }, actions: { }, modules: { menuState } })
<!-- App.vue --> <template> <div id="app" @mousedown="appMouseDown"> <!-- 这里是自己的代码 --> </div> </template> <script> export default { methods: { appMouseDown() { this.$store.commit("changeShowRightMenu", false) } } } </script>
博主的代码中,标签属性class
里的内容是题主自定义的CSS
样式,若有需要可在题主的git仓库直接获取文件使用:
https://github.com/WuChuSheng1/skywalking-banyandb/tree/main/ui/src/assets
建议直接下载并在main.js
中导入,避免产生一些非必要的错误。
本文主要实现Web
端鼠标右键打开菜单的功能,目前并无bug
,后续考虑使用一些动画效果来提高使用体验度。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。