|
|
@@ -4,24 +4,65 @@ import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
|
|
import config from '@/config/defaultSetting'
|
|
|
import { useRouterStore } from '@/stores/router'
|
|
|
|
|
|
+interface Tab {
|
|
|
+ name: string
|
|
|
+ fullPath: string
|
|
|
+ params: object
|
|
|
+ query: object
|
|
|
+ title: string
|
|
|
+}
|
|
|
+
|
|
|
const router = useRouter()
|
|
|
const route = useRoute()
|
|
|
const routerStore = useRouterStore()
|
|
|
|
|
|
-const tabs = ref<any[]>([])
|
|
|
-const activeValue = ref(route.fullPath)
|
|
|
-const contextMenuVisible = ref(false)
|
|
|
-
|
|
|
+const routeToTab = (route: any) => {
|
|
|
+ return {
|
|
|
+ name: route.name,
|
|
|
+ fullPath: route.fullPath || route.path,
|
|
|
+ params: route.params,
|
|
|
+ query: route.query,
|
|
|
+ title: route.meta?.title
|
|
|
+ }
|
|
|
+}
|
|
|
const getHomeRoute = (route: []) => {
|
|
|
return route.find((item: any) => {
|
|
|
if (item.children) {
|
|
|
getHomeRoute(item.children)
|
|
|
} else {
|
|
|
- return item.name === config.homeRoute
|
|
|
+ return item.name === config.homeRouteName
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+const tabs = useStorage<any[]>('globalTabs', [routeToTab(getHomeRoute(routerStore.menuRouter))], sessionStorage)
|
|
|
+const activeValue = ref(route.fullPath)
|
|
|
+const activeIndex = ref(0)
|
|
|
+
|
|
|
+const findActiveIndex = (fullPath: string | number) => {
|
|
|
+ return tabs.value.findIndex(item => item.fullPath === fullPath)
|
|
|
+}
|
|
|
+
|
|
|
+const setTab = (tab: RouteLocationNormalizedLoaded) => {
|
|
|
+ activeValue.value = tab.fullPath
|
|
|
+ activeIndex.value = findActiveIndex(tab.fullPath)
|
|
|
+
|
|
|
+ if (tab.name === 'login' || tabs.value.find(item => item.fullPath === tab.fullPath)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ tabs.value.push(routeToTab(tab))
|
|
|
+}
|
|
|
+const changeTab = (name: TabPanelName) => {
|
|
|
+ activeIndex.value = findActiveIndex(name)
|
|
|
+ router.push(tabs.value.find(item => item.fullPath === name))
|
|
|
+}
|
|
|
+const removeTab = (name: TabPanelName) => {
|
|
|
+ const index = findActiveIndex(name)
|
|
|
+ activeIndex.value = index < tabs.value.length - 1 ? index + 1 : index - 1
|
|
|
+ router.push(tabs.value[activeIndex.value])
|
|
|
+ tabs.value.splice(index, 1)
|
|
|
+}
|
|
|
+
|
|
|
watch(
|
|
|
() => route,
|
|
|
to => {
|
|
|
@@ -31,47 +72,66 @@ watch(
|
|
|
deep: true
|
|
|
}
|
|
|
)
|
|
|
-watch(
|
|
|
- tabs,
|
|
|
- () => {
|
|
|
- sessionStorage.setItem('globalTabs', JSON.stringify(tabs.value))
|
|
|
- },
|
|
|
- {
|
|
|
- deep: true
|
|
|
- }
|
|
|
-)
|
|
|
-const setTab = (tab: RouteLocationNormalizedLoaded) => {
|
|
|
- activeValue.value = tab.fullPath
|
|
|
|
|
|
- if (tab.name === 'login' || tabs.value.find(item => item.fullPath === tab.fullPath)) {
|
|
|
- return
|
|
|
+const contextMenuVisible = ref(false)
|
|
|
+const left = ref(0)
|
|
|
+const top = ref(0)
|
|
|
+const contextMenuIndex = ref(0)
|
|
|
+const contextMenuItem = computed(() => tabs.value[contextMenuIndex.value]) as any
|
|
|
+
|
|
|
+const findContextMenuItemIndex = (fullPath: string) => tabs.value.findIndex(item => item.fullPath === fullPath)
|
|
|
+
|
|
|
+const openContextMenu = (e: any) => {
|
|
|
+ const id = e.target?.offsetParent?.id || e.target?.id
|
|
|
+ if (id) {
|
|
|
+ const fullPath = id.split('-')[1]
|
|
|
+ contextMenuIndex.value = findContextMenuItemIndex(fullPath)
|
|
|
+ left.value = e.clientX + 1
|
|
|
+ top.value = e.clientY + 1
|
|
|
+ contextMenuVisible.value = true
|
|
|
}
|
|
|
+}
|
|
|
+const closeContextMenu = () => {
|
|
|
+ contextMenuVisible.value = false
|
|
|
+}
|
|
|
|
|
|
- tabs.value.push(routeToTab(tab))
|
|
|
+const closeAll = () => {
|
|
|
+ router.push(tabs.value[0])
|
|
|
+ tabs.value.splice(1)
|
|
|
+ closeContextMenu()
|
|
|
}
|
|
|
-const routeToTab = (route: any) => {
|
|
|
- return {
|
|
|
- name: route.name,
|
|
|
- fullPath: route.fullPath,
|
|
|
- params: route.params,
|
|
|
- query: route.query,
|
|
|
- title: route.meta.title
|
|
|
+const closeLeft = () => {
|
|
|
+ if (activeIndex.value < contextMenuIndex.value) {
|
|
|
+ router.push(contextMenuItem)
|
|
|
}
|
|
|
+ tabs.value.splice(1, contextMenuIndex.value - 1)
|
|
|
+ closeContextMenu()
|
|
|
}
|
|
|
-tabs.value.push(routeToTab(getHomeRoute(routerStore.menuRouter)))
|
|
|
-console.log(routeToTab(getHomeRoute(routerStore.menuRouter)))
|
|
|
-const initTabs = () => {
|
|
|
- tabs.value = JSON.parse(sessionStorage.getItem('globalTabs') || '[]')
|
|
|
- activeValue.value = window.sessionStorage.getItem('activeValue') || route.fullPath
|
|
|
+const closeRight = () => {
|
|
|
+ if (activeIndex.value > contextMenuIndex.value) {
|
|
|
+ router.push(contextMenuItem)
|
|
|
+ }
|
|
|
+ tabs.value.splice(contextMenuIndex.value + 1)
|
|
|
+ closeContextMenu()
|
|
|
}
|
|
|
-initTabs()
|
|
|
|
|
|
-const changeTab = (name: TabPanelName) => {
|
|
|
- router.push(tabs.value.find(item => item.fullPath === name))
|
|
|
- sessionStorage.setItem('activeValue', name as string)
|
|
|
+const closeOther = () => {
|
|
|
+ router.push(contextMenuItem)
|
|
|
+ if (contextMenuIndex.value) {
|
|
|
+ tabs.value = [tabs.value[0], contextMenuItem]
|
|
|
+ } else {
|
|
|
+ tabs.value = [tabs.value[0]]
|
|
|
+ }
|
|
|
+ contextMenuVisible.value = false
|
|
|
}
|
|
|
-const removeTab = () => {}
|
|
|
-const openContextMenu = (event: any) => {}
|
|
|
+
|
|
|
+watch(contextMenuVisible, val => {
|
|
|
+ if (val) {
|
|
|
+ document.body.addEventListener('click', closeContextMenu)
|
|
|
+ } else {
|
|
|
+ document.body.removeEventListener('click', closeContextMenu)
|
|
|
+ }
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
@@ -80,26 +140,32 @@ const openContextMenu = (event: any) => {}
|
|
|
v-model="activeValue"
|
|
|
:closable="!(tabs.length === 1)"
|
|
|
type="card"
|
|
|
- @contextmenu.prevent="openContextMenu($event)"
|
|
|
@tab-change="changeTab"
|
|
|
@tab-remove="removeTab"
|
|
|
+ @contextmenu.prevent="openContextMenu($event)"
|
|
|
>
|
|
|
- <el-tab-pane v-for="item in tabs" :key="item.name" :label="item.title" :name="item.fullPath" :tab="item">
|
|
|
+ <el-tab-pane
|
|
|
+ v-for="item in tabs"
|
|
|
+ :key="item.name"
|
|
|
+ :label="item.title"
|
|
|
+ :name="item.fullPath"
|
|
|
+ :tab="item"
|
|
|
+ :closable="item.name !== config.homeRouteName"
|
|
|
+ >
|
|
|
+ <template #label>
|
|
|
+ <span>{{ item.title }}</span>
|
|
|
+ </template>
|
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
|
</div>
|
|
|
|
|
|
<!--自定义右键菜单html代码-->
|
|
|
- <!-- <ul
|
|
|
- v-show="contextMenuVisible"
|
|
|
- :style="{ left: left + 'px', top: top + 'px' }"
|
|
|
- class="contextmenu"
|
|
|
- >
|
|
|
- <li @click="closeAll">关闭所有</li>
|
|
|
- <li @click="closeLeft">关闭左侧</li>
|
|
|
- <li @click="closeRight">关闭右侧</li>
|
|
|
- <li @click="closeOther">关闭其他</li>
|
|
|
- </ul> -->
|
|
|
+ <ul v-show="contextMenuVisible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
|
|
|
+ <li @click="closeAll">关闭所有</li>
|
|
|
+ <li @click="closeLeft">关闭左侧</li>
|
|
|
+ <li @click="closeRight">关闭右侧</li>
|
|
|
+ <li @click="closeOther">关闭其他</li>
|
|
|
+ </ul>
|
|
|
</template>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
@@ -110,4 +176,30 @@ const openContextMenu = (event: any) => {}
|
|
|
border-top: none !important;
|
|
|
border-radius: 0 !important;
|
|
|
}
|
|
|
+:deep(.el-tabs__item.is-active) {
|
|
|
+ background-color: var(--el-color-primary-light-9);
|
|
|
+ border-bottom-color: var(--el-border-color) !important;
|
|
|
+}
|
|
|
+.contextmenu {
|
|
|
+ position: fixed;
|
|
|
+ width: 100px;
|
|
|
+ margin: 0;
|
|
|
+ border-radius: 4px;
|
|
|
+ background: var(--el-bg-color-overlay);
|
|
|
+ border: 1px solid var(--el-border-color-light);
|
|
|
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
+ z-index: 3000;
|
|
|
+ list-style-type: none;
|
|
|
+ padding: 0;
|
|
|
+
|
|
|
+ li {
|
|
|
+ margin: 0;
|
|
|
+ padding: 7px 16px;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: #f2f2f2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|