前言
最近得空,准备用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及衍生的生态插件的使用