This commit is contained in:
仲么了
2024-02-19 13:44:32 +08:00
commit 361cbb713d
238 changed files with 202544 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
<template>
<div class='nav-bar'>
<div v-if='config.layout.shrink && config.layout.menuCollapse' class='unfold'>
<Icon @click='onMenuCollapse' name='fa fa-indent' :color="config.getColorVal('menuActiveColor')"
size='18' />
</div>
<span class='nav-bar-title'>电能质量数据监测云平台</span>
<NavMenus />
</div>
</template>
<script setup lang='ts'>
import { useConfig } from '@/stores/config'
import NavTabs from '@/layouts/admin/components/navBar/tabs.vue'
import NavMenus from '../navMenus.vue'
import { showShade } from '@/utils/pageShade'
const config = useConfig()
const onMenuCollapse = () => {
showShade('ba-aside-menu-shade', () => {
config.setLayout('menuCollapse', true)
})
config.setLayout('menuCollapse', false)
}
</script>
<style scoped lang='scss'>
.nav-bar {
display: flex;
align-items: center;
height: 60px;
width: 100%;
background-color: v-bind('config.getColorVal("headerBarBackground")');
.nav-bar-title {
color: v-bind('config.getColorVal("headerBarTabColor")');
font-size: 24px;
margin-left: 10px;
font-weight: 700
}
:deep(.nav-tabs) {
display: flex;
height: 100%;
position: relative;
.ba-nav-tab {
display: flex;
align-items: center;
justify-content: center;
padding: 0 20px;
cursor: pointer;
z-index: 1;
height: 100%;
user-select: none;
color: v-bind('config.getColorVal("headerBarTabColor")');
transition: all 0.2s;
-webkit-transition: all 0.2s;
.close-icon {
padding: 2px;
margin: 2px 0 0 4px;
color: v-bind('config.getColorVal("headerBarTabColor")') !important;
}
&.active {
color: v-bind('config.getColorVal("headerBarTabActiveColor")');
.close-icon {
color: v-bind('config.getColorVal("headerBarTabActiveColor")') !important;;
}
}
&:hover {
color: v-bind('config.getColorVal("headerBarTabActiveColor")');
background-color: v-bind('config.getColorVal("headerBarHoverBackground")');
.close-icon {
color: v-bind('config.getColorVal("headerBarTabActiveColor")') !important;;
}
}
}
.nav-tabs-active-box {
position: absolute;
height: 50px;
background-color: v-bind('config.getColorVal("headerBarTabActiveBackground")');
transition: all 0.2s;
-webkit-transition: all 0.2s;
}
}
}
.unfold {
align-self: center;
padding-left: var(--ba-main-space);
}
</style>

View File

@@ -0,0 +1,65 @@
<template>
<div class="nav-bar">
<NavTabs />
<NavMenus />
</div>
</template>
<script setup lang="ts">
import { useConfig } from '@/stores/config'
import NavTabs from '@/layouts/admin/components/navBar/tabs.vue'
import NavMenus from '../navMenus.vue'
const config = useConfig()
</script>
<style lang="scss" scoped>
.nav-bar {
display: flex;
height: 40px;
margin: 10px var(--ba-main-space) 0 var(--ba-main-space);
:deep(.nav-tabs) {
display: flex;
height: 100%;
position: relative;
.ba-nav-tab {
display: flex;
align-items: center;
justify-content: center;
padding: 0 20px;
cursor: pointer;
z-index: 1;
user-select: none;
opacity: 0.7;
color: v-bind('config.getColorVal("headerBarTabColor")');
.close-icon {
padding: 2px;
margin: 2px 0 0 4px;
color: v-bind('config.getColorVal("headerBarTabColor")') !important;
}
&.active {
color: v-bind('config.getColorVal("headerBarTabActiveColor")');
.close-icon {
color: v-bind('config.getColorVal("headerBarTabActiveColor")') !important;;
}
}
&:hover {
color: v-bind('config.getColorVal("headerBarTabActiveColor")');
background-color: v-bind('config.getColorVal("headerBarHoverBackground")');
.close-icon {
color: v-bind('config.getColorVal("headerBarTabActiveColor")') !important;;
}
}
}
.nav-tabs-active-box {
position: absolute;
height: 40px;
border-radius: var(--el-border-radius-base);
background-color: v-bind('config.getColorVal("headerBarTabActiveBackground")');
box-shadow: var(--el-box-shadow-light);
transition: all 0.2s;
-webkit-transition: all 0.2s;
}
}
}
</style>

View File

@@ -0,0 +1,102 @@
<template>
<div class="layouts-menu-horizontal-double">
<el-scrollbar ref="horizontalMenusRef" class="double-menus-scrollbar">
<el-menu
class="menu-horizontal"
mode="horizontal"
:default-active="state.defaultActive"
:key="state.menuKey"
>
<MenuTree :extends="{ position: 'horizontal', level: 1 }" :menus="menus" />
</el-menu>
</el-scrollbar>
<NavMenus />
</div>
</template>
<script setup lang="ts">
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
import { useRoute, onBeforeRouteUpdate, type RouteLocationNormalizedLoaded } from 'vue-router'
import { currentRouteTopActivity } from '@/layouts/admin/components/menus/helper'
import MenuTree from '@/layouts/admin/components/menus/menuTree.vue'
import NavMenus from '@/layouts/admin/components/navMenus.vue'
import type { ScrollbarInstance } from 'element-plus'
import { useNavTabs } from '@/stores/navTabs'
import { useConfig } from '@/stores/config'
import { uuid } from '@/utils/random'
const horizontalMenusRef = ref<ScrollbarInstance>()
const config = useConfig()
const navTabs = useNavTabs()
const route = useRoute()
const state = reactive({
menuKey: uuid(),
defaultActive: ''
})
const menus = computed(() => {
state.menuKey = uuid() // eslint-disable-line
return navTabs.state.tabsViewRoutes
})
// 激活当前路由的菜单
const currentRouteActive = (currentRoute: RouteLocationNormalizedLoaded) => {
let routeChildren = currentRouteTopActivity(currentRoute.path, navTabs.state.tabsViewRoutes)
if (routeChildren) state.defaultActive = currentRoute.path
}
// 滚动条滚动到激活菜单所在位置
const verticalMenusScroll = () => {
nextTick(() => {
let activeMenu: HTMLElement | null = document.querySelector('.el-menu.menu-horizontal li.is-active')
if (!activeMenu) return false
horizontalMenusRef.value?.setScrollTop(activeMenu.offsetTop)
})
}
onMounted(() => {
currentRouteActive(route)
verticalMenusScroll()
})
onBeforeRouteUpdate(to => {
currentRouteActive(to)
})
</script>
<style scoped lang="scss">
.layouts-menu-horizontal-double {
display: flex;
align-items: center;
height: 50px;
background-color: var(--ba-bg-color-overlay);
border-bottom: solid 1px var(--el-color-info-light-8);
}
.double-menus-scrollbar {
width: 70vw;
}
.menu-horizontal {
border: none;
--el-menu-bg-color: v-bind('config.getColorVal("menuBackground")');
--el-menu-text-color: v-bind('config.getColorVal("menuColor")');
--el-menu-active-color: v-bind('config.getColorVal("menuActiveColor")');
--el-menu-hover-color: v-bind('config.getColorVal("menuActiveBackground")');
}
.el-sub-menu .icon,
.el-menu-item .icon {
vertical-align: middle;
margin-right: 5px;
width: 24px;
text-align: center;
flex-shrink: 0;
}
.is-active .icon {
color: var(--el-menu-active-color) !important;
}
.el-menu-item.is-active {
background-color: v-bind('config.getColorVal("menuActiveBackground")');
}
</style>

View File

@@ -0,0 +1,239 @@
<template>
<div class='nav-tabs' ref='tabScrollbarRef'>
<div
v-for='(item, idx) in navTabs.state.tabsView'
@click='onTab(item)'
@contextmenu.prevent='onContextmenu(item, $event)'
class='ba-nav-tab'
:class="navTabs.state.activeIndex == idx ? 'active' : ''"
:ref='tabsRefs.set'
:key='idx'
>
{{ item.meta.title }}
<transition @after-leave='selectNavTab(tabsRefs[navTabs.state.activeIndex])' name='el-fade-in'>
<Icon
v-show='navTabs.state.tabsView.length > 1'
class='close-icon'
@click.stop='closeTab(item)'
size='15'
name='el-icon-Close'
/>
</transition>
</div>
<!-- <div :style='activeBoxStyle' class='nav-tabs-active-box'></div>-->
</div>
<Contextmenu ref='contextmenuRef' :items='state.contextmenuItems' @contextmenuItemClick='onContextmenuItem' />
</template>
<script setup lang='ts'>
import { nextTick, onMounted, reactive, ref } from 'vue'
import { useRoute, useRouter, onBeforeRouteUpdate, type RouteLocationNormalized } from 'vue-router'
import { useConfig } from '@/stores/config'
import { useNavTabs } from '@/stores/navTabs'
import { useTemplateRefsList } from '@vueuse/core'
import type { ContextMenuItem, ContextmenuItemClickEmitArg } from '@/components/contextmenu/interface'
import useCurrentInstance from '@/utils/useCurrentInstance'
import Contextmenu from '@/components/contextmenu/index.vue'
import horizontalScroll from '@/utils/horizontalScroll'
import { getFirstRoute, routePush } from '@/utils/router'
import { adminBaseRoutePath } from '@/router/static'
const route = useRoute()
const router = useRouter()
const config = useConfig()
const navTabs = useNavTabs()
const { proxy } = useCurrentInstance()
const tabScrollbarRef = ref()
const tabsRefs = useTemplateRefsList<HTMLDivElement>()
const contextmenuRef = ref()
const state: {
contextmenuItems: ContextMenuItem[]
} = reactive({
contextmenuItems: [
{ name: 'refresh', label: '重新加载', icon: 'fa fa-refresh' },
{ name: 'close', label: '关闭标签', icon: 'fa fa-times' },
{ name: 'fullScreen', label: '当前标签全屏', icon: 'el-icon-FullScreen' },
{ name: 'closeOther', label: '关闭其他标签', icon: 'fa fa-minus' },
{ name: 'closeAll', label: '关闭全部标签', icon: 'fa fa-stop' }
]
})
const activeBoxStyle = reactive({
width: '0',
transform: 'translateX(0px)'
})
const onTab = (menu: RouteLocationNormalized) => {
router.push(menu)
}
const onContextmenu = (menu: RouteLocationNormalized, el: MouseEvent) => {
// 禁用刷新
state.contextmenuItems[0].disabled = route.path !== menu.path
// 禁用关闭其他和关闭全部
state.contextmenuItems[4].disabled = state.contextmenuItems[3].disabled =
navTabs.state.tabsView.length == 1 ? true : false
const { clientX, clientY } = el
contextmenuRef.value.onShowContextmenu(menu, {
x: clientX,
y: clientY
})
}
// tab 激活状态切换
const selectNavTab = function(dom: HTMLDivElement) {
if (!dom) {
return false
}
activeBoxStyle.width = dom.clientWidth + 'px'
activeBoxStyle.transform = `translateX(${dom.offsetLeft}px)`
let scrollLeft = dom.offsetLeft + dom.clientWidth - tabScrollbarRef.value.clientWidth
if (dom.offsetLeft < tabScrollbarRef.value.scrollLeft) {
tabScrollbarRef.value.scrollTo(dom.offsetLeft, 0)
} else if (scrollLeft > tabScrollbarRef.value.scrollLeft) {
tabScrollbarRef.value.scrollTo(scrollLeft, 0)
}
}
const toLastTab = () => {
const lastTab = navTabs.state.tabsView.slice(-1)[0]
if (lastTab) {
router.push(lastTab)
} else {
router.push(adminBaseRoutePath)
}
}
const closeTab = (route: RouteLocationNormalized) => {
navTabs.closeTab(route)
proxy.eventBus.emit('onTabViewClose', route)
if (navTabs.state.activeRoute?.path === route.path) {
toLastTab()
} else {
navTabs.setActiveRoute(navTabs.state.activeRoute!)
nextTick(() => {
selectNavTab(tabsRefs.value[navTabs.state.activeIndex])
})
}
contextmenuRef.value.onHideContextmenu()
}
const closeOtherTab = (menu: RouteLocationNormalized) => {
navTabs.closeTabs(menu)
navTabs.setActiveRoute(menu)
if (navTabs.state.activeRoute?.path !== route.path) {
router.push(menu!.path)
}
}
const closeAllTab = (menu: RouteLocationNormalized) => {
let firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes)
if (firstRoute && firstRoute.path == menu.path) {
return closeOtherTab(menu)
}
if (firstRoute && firstRoute.path == navTabs.state.activeRoute?.path) {
return closeOtherTab(navTabs.state.activeRoute)
}
navTabs.closeTabs(false)
if (firstRoute) routePush(firstRoute.path)
}
const onContextmenuItem = async (item: ContextmenuItemClickEmitArg) => {
const { name, menu } = item
if (!menu) return
switch (name) {
case 'refresh':
proxy.eventBus.emit('onTabViewRefresh', menu)
break
case 'close':
closeTab(menu)
break
case 'closeOther':
closeOtherTab(menu)
break
case 'closeAll':
closeAllTab(menu)
break
case 'fullScreen':
if (route.path !== menu?.path) {
router.push(menu?.path as string)
}
navTabs.setFullScreen(true)
break
}
}
const updateTab = function(newRoute: RouteLocationNormalized) {
// 添加tab
navTabs.addTab(newRoute)
// 激活当前tab
navTabs.setActiveRoute(newRoute)
nextTick(() => {
selectNavTab(tabsRefs.value[navTabs.state.activeIndex])
})
}
onBeforeRouteUpdate(async to => {
updateTab(to)
})
onMounted(() => {
updateTab(router.currentRoute.value)
new horizontalScroll(tabScrollbarRef.value)
})
</script>
<style scoped lang='scss'>
.dark {
.close-icon {
color: v-bind('config.getColorVal("headerBarTabColor")') !important;
}
.ba-nav-tab.active {
.close-icon {
color: v-bind('config.getColorVal("headerBarTabActiveColor")') !important;
}
}
}
.nav-tabs {
overflow-x: auto;
overflow-y: hidden;
margin-right: var(--ba-main-space);
scrollbar-width: none;
&::-webkit-scrollbar {
height: 5px;
}
//
//&::-webkit-scrollbar-thumb {
// background: #eaeaea;
// border-radius: var(--el-border-radius-base);
// box-shadow: none;
// -webkit-box-shadow: none;
//}
//
//&::-webkit-scrollbar-track {
// background: v-bind('config.layout.layoutMode == "Default" ? "none":config.getColorVal("headerBarBackground")');
//}
//
//&:hover {
// &::-webkit-scrollbar-thumb:hover {
// background: #c8c9cc;
// }
//}
}
.ba-nav-tab {
white-space: nowrap;
height: 40px;
}
</style>