vue實(shí)現(xiàn)自定義全局右鍵菜單
前段時(shí)間公司在做一個(gè)webIDE項(xiàng)目,其中有對(duì)文件樹(shù)的各種操作,主要通過(guò)右鍵菜單實(shí)現(xiàn),今天就來(lái)記錄一下怎么在vue項(xiàng)目中實(shí)現(xiàn)全局的自定義右鍵菜單。效果如圖所示:

注意:
需要在項(xiàng)目中找到頁(yè)面整體布局的組件,比如項(xiàng)目使用Home.vue作為整個(gè)項(xiàng)目的公共布局,將根元素定位設(shè)置成relative,使右鍵菜單相對(duì)于其進(jìn)行定位。
本文的右鍵菜單主要使用vuex實(shí)現(xiàn)
一、vuex中定義全局狀態(tài)用于管理右鍵菜單
export default {
? ? /**
? ? ?* menuContent格式:
? ? ?* [
? ? ?* ? ? ?{
? ? ?* ? ? ? ? ?name: '新建文件', ? // 操作名
? ? ?* ? ? ? ? ?method: 'createDirectory', ?// 需要執(zhí)行的對(duì)應(yīng)組件中的方法
? ? ?* ?? ??? ??? ?disabled: true?? ??? ?// 是否禁用,可以根據(jù)頁(yè)面具體邏輯進(jìn)行調(diào)整
? ? ?* ? ? ?}
? ? ?* ]
?? ? *?
?? ? *?
?? ? * 整體右鍵菜單采用絕對(duì)定位,所以clientX、clientY代表是left和top定位
? ? ?*/
? ? state: {
? ? ? ? menuContent: [], ? ?// 右鍵菜單內(nèi)容
? ? ? ? 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) => {
?? ?// 點(diǎn)擊右鍵菜單時(shí),應(yīng)觸發(fā)組件內(nèi)的同名方法,首先應(yīng)判斷該方法是否在本組件內(nèi)存在,存在則調(diào)用
?? ??? ?if(this[param]){
?? ??? ??? ?this[param]();
?? ??? ?}
?? ?});
},
// ...
methods: {
?? ?showContextMenu(event, data) {
? ? ? ? event.preventDefault();?? ??? ?// 阻止瀏覽器的默認(rèn)事件
? ? ? ? const menuContent = {
? ? ? ? ? ? menuContent: [
? ? ? ? ? ? {
? ? ? ? ? ? ? ? icon: "el-icon-upload2",
? ? ? ? ? ? ? ? name: "運(yùn)行",
? ? ? ? ? ? ? ? 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);
? ? ? ? // 再次點(diǎn)擊頁(yè)面,右鍵菜單消失
? ? ? ? document.onclick = (event) => {
? ? ? ? ? ? this.$store.commit("SET_CONTEXT_MENU", {
? ? ? ? ? ? ? ? displayContextMenu: false,
? ? ? ? ? ? });
? ? ? ? };
? ? },
? ? run(){
?? ??? ?// ...
?? ?},
?? ?editConfig(){
?? ??? ?// ...
?? ?},
?? ?addCommond(){
?? ??? ?// ...
?? ?},
?? ?deleteConfig(){
?? ??? ?// ...
?? ?}
}總結(jié)
這種可以實(shí)現(xiàn)全局右鍵菜單,并且支持自定義右鍵內(nèi)容,但是vue3.0對(duì)event bus的取消會(huì)導(dǎo)致不可用。
目前有一種優(yōu)化方法:項(xiàng)目中對(duì)應(yīng)會(huì)使用組件庫(kù),例如element ui,在定義contextMenu.vue的時(shí)候,使用dialog,將內(nèi)容定義在Popover 彈出框中,當(dāng)組件使用右鍵菜單時(shí),使用子組件的方式使用ContextMenu.vue,點(diǎn)擊右鍵菜單內(nèi)容時(shí)就不需要使用emit觸發(fā)了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Vuex實(shí)現(xiàn)數(shù)據(jù)增加和刪除功能
今天小編就為大家分享一篇Vuex實(shí)現(xiàn)數(shù)據(jù)增加和刪除功能,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11
Vue項(xiàng)目使用localStorage+Vuex保存用戶登錄信息
這篇文章主要為大家詳細(xì)介紹了Vue項(xiàng)目使用localStorage+Vuex保存用戶登錄信息,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
Vue3組合式API之getCurrentInstance詳解
我們可以通過(guò)?getCurrentInstance這個(gè)函數(shù)來(lái)返回當(dāng)前組件的實(shí)例對(duì)象,也就是當(dāng)前vue這個(gè)實(shí)例對(duì)象,下面這篇文章主要給大家介紹了關(guān)于Vue3組合式API之getCurrentInstance的相關(guān)資料,需要的朋友可以參考下2022-09-09
vue中axios處理http發(fā)送請(qǐng)求的示例(Post和get)
本篇文章主要介紹了vue中axios處理http請(qǐng)求的示例(Post和get),這里整理了詳細(xì)的代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10

