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及衍生的生态插件的使用

相关推荐
没有故事、有酒4 分钟前
Ajax介绍
前端·ajax·okhttp
朝新_7 分钟前
【SpringMVC】详解用户登录前后端交互流程:AJAX 异步通信与 Session 机制实战
前端·笔记·spring·ajax·交互·javaee
裴嘉靖10 分钟前
Vue 生成 PDF 完整教程
前端·vue.js·pdf
毕设小屋vx ylw28242612 分钟前
Java开发、Java Web应用、前端技术及Vue项目
java·前端·vue.js
冴羽1 小时前
今日苹果 App Store 前端源码泄露,赶紧 fork 一份看看
前端·javascript·typescript
蒜香拿铁1 小时前
Angular【router路由】
前端·javascript·angular.js
时间的情敌1 小时前
Vite 大型项目优化方案
vue.js
brzhang2 小时前
读懂 MiniMax Agent 的设计逻辑,然后我复刻了一个MiniMax Agent
前端·后端·架构
西洼工作室2 小时前
高效管理搜索历史:Vue持久化实践
前端·javascript·vue.js
广州华水科技2 小时前
北斗形变监测传感器在水库安全中的应用及技术优势分析
前端