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 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰2 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万2 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝2 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
A黄俊辉A2 天前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
honkun63 天前
vue 表格组件 vxe-table 配置 ajax 请求自动加载数据与表单查询
vue.js·vxe-table
kybs19913 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端