vue3 实现一个鼠标右击菜单栏

前言

最近得空,准备用vite+vue3搭建一个后台管理系统.

技术栈

vue3 实现一个鼠标右击菜单栏

效果图

代码

多的不说直接上代码

下面是tabs的代码片段

js 复制代码
<template>
  <div>
    <el-menu :default-active="store.defaultActive" router mode="horizontal" @select="handleSelect">
      <el-menu-item
        v-for="(item, index) in store.tabList"
        :key="index"
        class="tabMenu"
        :data-key="index"
        :index="item.path"
      >
        {{ item.title }}
      </el-menu-item>
    </el-menu>
    <closePanel :path="path" ref="closePanelRef" />
  </div>
</template>

<script setup lang="ts">
import { useGlobalStore } from "@/store/index"
import closePanel from "./closePanel.vue"
const closePanelRef = ref()
const store = useGlobalStore()

const handleSelect = (key: string, keyPath: string[]) => {
  console.log(key, keyPath)
  store.setDefaultActive(key)
}

const path = reactive({
  x: 0,
  y: 0
})
const init = () => {
  const dom = document.getElementsByClassName("tabMenu")
  Array.from(dom).forEach((element) => {
    element.addEventListener("contextmenu", (e: Event) => {
      path.x = (e as MouseEvent).clientX
      path.y = (e as MouseEvent).clientY
      const key = (e.target as HTMLElement)?.getAttribute("data-key")
      closePanelRef.value.toggele(key)
      e.preventDefault()
    })
  })
}

watch(
  () => store.tabList.length,
  () => {
    nextTick(() => {
      removeEvent()
      init()
    })
  }
)

const removeEvent = () => {
  const dom = document.getElementsByClassName("tabMenu")
  Array.from(dom).forEach((element) => {
    element.removeEventListener("contextmenu", () => {})
  })
}

onMounted(() => {
  init()
})
</script>

<style></style>
<template>
  <div>
    <el-menu :default-active="store.defaultActive" router mode="horizontal" @select="handleSelect">
      <el-menu-item
        v-for="(item, index) in store.tabList"
        :key="index"
        class="tabMenu"
        :data-key="index"
        :index="item.path"
      >
        {{ item.title }}
      </el-menu-item>
    </el-menu>
    <closePanel :path="path" ref="closePanelRef" />
  </div>
</template>

<script setup lang="ts">
import { useGlobalStore } from "@/store/index"
import closePanel from "./closePanel.vue"
const closePanelRef = ref()
const store = useGlobalStore()

const handleSelect = (key: string, keyPath: string[]) => {
  console.log(key, keyPath)
  store.setDefaultActive(key)
}

const path = reactive({
  x: 0,
  y: 0
})
const init = () => {
  const dom = document.getElementsByClassName("tabMenu")
  Array.from(dom).forEach((element) => {
    element.addEventListener("contextmenu", (e: Event) => {
      path.x = (e as MouseEvent).clientX
      path.y = (e as MouseEvent).clientY
      const key = (e.target as HTMLElement)?.getAttribute("data-key")
      closePanelRef.value.toggele(key)
      e.preventDefault()
    })
  })
}

watch(
  () => store.tabList.length,
  () => {
    nextTick(() => {
      removeEvent()
      init()
    })
  }
)

const removeEvent = () => {
  const dom = document.getElementsByClassName("tabMenu")
  Array.from(dom).forEach((element) => {
    element.removeEventListener("contextmenu", () => {})
  })
}

onMounted(() => {
  init()
})
</script>

<style></style>
  • 主要是给el-menu-item加上contextmenu事件并用e.preventDefault()阻止默认菜单得展示。
  • closePanel组件是右击展示的弹层

closePanel组件的相关代码

js 复制代码
<template>
  <div
    v-click-outside="() => toggele('', false)"
    v-if="show"
    :class="['absolute']"
    :key="path.x + path.y"
    :style="{ left: path.x + 'px', top: path.y + 'px' }"
  >
    <el-card class="relative z-10">
      <template #header>
        <div class="card-header">
          <span>操作菜单栏</span>
        </div>
      </template>
      <div class="text item" @click="closeAll">关闭所有</div>
      <div class="text item" @click="closeOther">关闭其他</div>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { useGlobalStore } from "@/store/index"
import { ClickOutside as vClickOutside } from "element-plus"
const store = useGlobalStore()
const show = ref(false)
const index = ref("")
defineProps({
  path: {
    type: Object,
    default: () => {
      return {
        x: 0,
        y: 0
      }
    }
  }
})
const closeAll = () => {
  console.log("关闭所有")
  store.clearTab()
  toggele("", false)
}
const closeOther = () => {
  console.log("关闭其他")
  store.closeOtherTab(index.value)
  toggele("", false)
}

const toggele = (key: string, visible = true) => {
  if (key) {
    index.value = key
  }
  show.value = visible
  console.log(show.value)
}
defineExpose({
  toggele
})
</script>

<style></style>
  • defineExpose将父组件触发展示的函数抛出
  • v-click-outside点击组件外的地方触发关闭函数
    • element-plus导出的指令能直接使用,下次好好看看还有那些指令

下面是关于全局store的代码

js 复制代码
export const useGlobalStore = defineStore("globalStore", {
  persist: true,
  state: () => {
    return {
      tabList: [
        {
          path: "/index",
          name: "index",
          title: "首页"
        }
      ],
      defaultActive: "/index"
    }
  },
  actions: {
    setDefaultActive(name: string) {
      this.defaultActive = name
    },
    clearTab() {
      this.tabList = [
        {
          path: "/index",
          name: "index",
          title: "首页"
        }
      ]
      this.defaultActive = "/index"
    },
    closeOtherTab(i: string) {
      this.tabList = this.tabList.filter((item: any, index: any) => index == 0 || i == index)
    },
    addTab(tab: any) {
      if (this.tabList.some((item: any) => item.path === tab.path)) return
      this.tabList.push(tab)
    }
  }
})

最后

关于这个vue3+vite的管理系统我是打算有一下功能

  • 权限
  • 主题切换
  • 多语言

目前想到这么多,主要是记录一下vite和vue3及衍生的生态插件的使用

相关推荐
少年姜太公2 小时前
什么?还不知道git cherry pick?
前端·javascript·git
白兰地空瓶3 小时前
🏒 前端 AI 应用实战:用 Vue3 + Coze,把宠物一键变成冰球运动员!
前端·vue.js·coze
Liu.7744 小时前
vue3使用vue3-print-nb打印
前端·javascript·vue.js
松涛和鸣5 小时前
Linux Makefile : From Basic Syntax to Multi-File Project Compilation
linux·运维·服务器·前端·windows·哈希算法
dly_blog5 小时前
Vue 逻辑复用的多种方案对比!
前端·javascript·vue.js
万少5 小时前
HarmonyOS6 接入分享,原来也是三分钟的事情
前端·harmonyos
烛阴5 小时前
C# 正则表达式:量词与锚点——从“.*”到精确匹配
前端·正则表达式·c#
wyzqhhhh6 小时前
京东啊啊啊啊啊
开发语言·前端·javascript
JIngJaneIL6 小时前
基于java+ vue助农电商系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
q_19132846956 小时前
基于Springboot+MySQL+RuoYi的会议室预约管理系统
java·vue.js·spring boot·后端·mysql·若依·计算机毕业设计