This commit is contained in:
kongduo
2026-06-11 18:45:44 +08:00
parent f006955a77
commit ca23283c6e
26 changed files with 1656 additions and 232 deletions

View File

@@ -2,7 +2,7 @@
<div class="h-full">
<div
v-if="mode === 'head'"
class="bg-white h-[calc(100%-4px)] text-slate-700 dark:text-slate-300 mx-2 dark:bg-slate-900 flex items-center w-[calc(100vw-600px)] overflow-auto"
class="gva-tech-aside-head h-[calc(100%-4px)] mx-2 flex items-center w-[calc(100vw-600px)] overflow-auto"
>
<el-menu
:default-active="routerStore.topActive"
@@ -23,7 +23,7 @@
</div>
<div
v-if="mode === 'normal'"
class="relative h-full bg-white text-slate-700 dark:text-slate-300 dark:bg-slate-900 border-r shadow dark:shadow-gray-700"
class="gva-tech-aside relative h-full border-r"
:class="isCollapse ? '' : ' px-2'"
:style="{
width: layoutSideWidth + 'px'
@@ -48,7 +48,7 @@
</el-menu>
</el-scrollbar>
<div
class="absolute bottom-8 right-2 w-8 h-8 bg-gray-50 dark:bg-slate-800 flex items-center justify-center rounded cursor-pointer"
class="gva-tech-collapse-btn absolute bottom-8 right-2 w-8 h-8 flex items-center justify-center rounded cursor-pointer"
:class="isCollapse ? 'right-0 left-0 mx-auto' : 'right-2'"
@click="toggleCollapse"
>

View File

@@ -1,6 +1,6 @@
<template>
<div
class="bg-white h-[calc(100%-4px)] text-slate-700 dark:text-slate-300 mx-2 dark:bg-slate-900 flex items-center w-[calc(100vw-600px)] overflow-auto"
class="gva-tech-aside-head h-[calc(100%-4px)] mx-2 flex items-center w-[calc(100vw-600px)] overflow-auto"
ref="menuContainer"
>
<el-menu

View File

@@ -1,6 +1,6 @@
<template>
<div
class="relative h-full bg-white text-slate-700 dark:text-slate-300 dark:bg-slate-900 border-r shadow dark:shadow-gray-700"
class="gva-tech-aside relative h-full border-r"
:class="isCollapse ? '' : ' px-2'"
:style="{
width: layoutSideWidth + 'px'
@@ -25,7 +25,7 @@
</el-menu>
</el-scrollbar>
<div
class="absolute bottom-8 right-2 w-8 h-8 bg-gray-50 dark:bg-slate-800 flex items-center justify-center rounded cursor-pointer"
class="gva-tech-collapse-btn absolute bottom-8 right-2 w-8 h-8 flex items-center justify-center rounded cursor-pointer"
:class="isCollapse ? 'right-0 left-0 mx-auto' : 'right-2'"
@click="toggleCollapse"
>

View File

@@ -2,7 +2,7 @@
<div class="flex h-full">
<!-- 一级菜单常驻侧边栏 -->
<div
class="relative !h-full bg-white text-slate-700 dark:text-slate-300 dark:bg-slate-900 border-r shadow dark:shadow-gray-700"
class="gva-tech-aside-rail relative !h-full border-r"
:style="{
width: config.layout_side_collapsed_width + 'px'
}"
@@ -64,7 +64,7 @@
<!-- 二级菜单并列显示 -->
<div
class="relative h-full bg-white text-slate-700 dark:text-slate-300 dark:bg-slate-900 border-r shadow dark:shadow-gray-700 px-2"
class="gva-tech-aside-secondary relative h-full border-r px-2"
:style="{
width: layoutSideWidth + 'px'
}"
@@ -88,7 +88,7 @@
</el-menu>
</el-scrollbar>
<div
class="absolute bottom-8 right-2 w-8 h-8 bg-gray-50 dark:bg-slate-800 flex items-center justify-center rounded cursor-pointer"
class="gva-tech-collapse-btn absolute bottom-8 right-2 w-8 h-8 flex items-center justify-center rounded cursor-pointer"
:class="isCollapse ? 'right-0 left-0 mx-auto' : 'right-2'"
@click="toggleCollapse"
>

View File

@@ -5,7 +5,7 @@
<template>
<div
class="flex justify-between fixed top-0 left-0 right-0 z-10 h-16 bg-white text-slate-700 dark:text-slate-300 dark:bg-slate-900 shadow dark:shadow-gray-700 items-center px-2"
class="gva-tech-header flex justify-between fixed top-0 left-0 right-0 z-10 h-16 items-center px-2"
>
<div class="flex items-center cursor-pointer flex-1">
<div
@@ -16,7 +16,7 @@
<Logo />
<div
v-if="!isMobile"
class="inline-flex font-bold text-2xl ml-2"
class="gva-tech-brand inline-flex font-bold text-2xl ml-2"
:class="
(config.side_mode === 'head' ||
config.side_mode === 'combination') &&
@@ -55,7 +55,7 @@
<el-dropdown>
<div class="flex justify-center items-center h-full w-full">
<span
class="cursor-pointer flex justify-center items-center text-black dark:text-gray-100"
class="cursor-pointer flex justify-center items-center text-cyan-50"
>
<CustomPic />
<span v-show="!isMobile" class="w-16">{{

View File

@@ -1,8 +1,8 @@
<template>
<div class="bg-gray-50 text-slate-700 dark:text-slate-500 dark:bg-slate-800 w-screen h-screen">
<div class="gva-tech-layout w-screen h-screen">
<el-watermark
v-if="config.show_watermark"
:font="font"
:font="[font]"
:z-index="9999"
:gap="[180, 150]"
class="!absolute !inset-0 !pointer-events-none"
@@ -14,18 +14,25 @@
v-if="
config.side_mode === 'normal' ||
config.side_mode === 'sidebar' ||
(device === 'mobile' && config.side_mode == 'head') ||
(device === 'mobile' && config.side_mode == 'combination')
(device === 'mobile' && config.side_mode === 'head') ||
(device === 'mobile' && config.side_mode === 'combination')
"
/>
<gva-aside v-if="config.side_mode === 'combination' && device !== 'mobile'" mode="normal" />
<div class="flex-1 w-0 h-full">
<div class="gva-tech-main flex-1 w-0 h-full">
<gva-tabs v-if="config.showTabs" />
<div class="overflow-auto px-2" :class="config.showTabs ? 'gva-container2' : 'gva-container pt-1'">
<div class="gva-tech-scroll overflow-auto px-2" :class="config.showTabs ? 'gva-container2' : 'gva-container pt-1'">
<router-view v-if="reloadFlag" v-slot="{ Component, route }">
<div id="gva-base-load-dom" class="gva-body-h bg-gray-50 dark:bg-slate-800">
<transition mode="out-in" :name="route.meta.transitionType || config.transition_type">
<keep-alive :include="routerStore.keepAliveRouters">
<div id="gva-base-load-dom" class="gva-body-h gva-tech-router-view">
<div v-if="showPageBanner(route)" class="gva-page-banner">
<div>
<div class="gva-page-kicker">INDUSTRIAL CONTROL</div>
<h1 class="gva-page-title">{{ fmtTitle(route.meta.title, route) }}</h1>
<p class="gva-page-subtitle">智慧用电安全监控 · 实时态势感知平台</p>
</div>
</div>
<transition mode="out-in" :name="route.meta['transitionType'] || config.transition_type">
<keep-alive :include="keepAliveRouterNames">
<component :is="Component" :key="route.fullPath" />
</keep-alive>
</transition>
@@ -45,12 +52,13 @@
import GvaTabs from './tabs/index.vue'
import BottomInfo from '@/components/bottomInfo/bottomInfo.vue'
import { emitter } from '@/utils/bus.js'
import { ref, onMounted, nextTick, reactive, watchEffect } from 'vue'
import { ref, onMounted, nextTick, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useRouterStore } from '@/pinia/modules/router'
import { useUserStore } from '@/pinia/modules/user'
import { useAppStore } from '@/pinia'
import { storeToRefs } from 'pinia'
import { fmtTitle } from '@/utils/fmtRouterTitle'
import '@/style/transition.scss'
const appStore = useAppStore()
const { config, isDark, device } = storeToRefs(appStore)
@@ -60,23 +68,20 @@
})
useResponsive(true)
const font = reactive({
color: 'rgba(0, 0, 0, .15)'
})
watchEffect(() => {
font.color = isDark.value ? 'rgba(255,255,255, .15)' : 'rgba(0, 0, 0, .15)'
})
const font = computed(() => ({
color: isDark.value ? 'rgba(255,255,255, .15)' : 'rgba(0, 0, 0, .15)'
}))
const router = useRouter()
const route = useRoute()
const routerStore = useRouterStore()
const keepAliveRouterNames = computed(() => routerStore.keepAliveRouters)
onMounted(() => {
// 挂载一些通用的事件
emitter.on('reload', reload)
if (userStore.loadingInstance) {
userStore.loadingInstance.close()
if (userStore['loadingInstance']) {
userStore['loadingInstance']['close']()
}
nextTick(() => {
document.getElementsByClassName(
@@ -88,6 +93,13 @@
const userStore = useUserStore()
const hideBannerRouteNames = ['Login', 'Reload', 'Init']
const showPageBanner = (currentRoute) => {
if (!currentRoute?.meta?.title) return false
if (hideBannerRouteNames.includes(currentRoute.name)) return false
return !currentRoute.meta.client && !currentRoute.meta['hidePageHeader']
}
const reloadFlag = ref(true)
let reloadTimer = null
const reload = async () => {
@@ -95,13 +107,13 @@
window.clearTimeout(reloadTimer)
}
reloadTimer = window.setTimeout(async () => {
if (route.meta.keepAlive) {
if (route.meta['keepAlive']) {
reloadFlag.value = false
await nextTick()
reloadFlag.value = true
} else {
const title = route.meta.title
router.push({ name: 'Reload', params: { title } })
const title = route.meta['title']
await router.push({ name: 'Reload', params: { title } })
}
}, 400)
}

View File

@@ -3,12 +3,12 @@
@date: 2024/5/7
!-->
<template>
<div class="gva-tabs">
<div class="gva-tabs gva-tech-tabs">
<el-tabs
v-model="activeValue"
:closable="!(historys.length === 1 && $route.name === defaultRouter)"
type="card"
class="bg-white text-slate-700 dark:text-slate-500 dark:bg-slate-900 pt-1"
class="pt-1"
@contextmenu.prevent="openContextMenu($event)"
@tab-click="changeTab"
@tab-remove="removeTab"
@@ -44,16 +44,19 @@
</el-tabs>
<!--自定义右键菜单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>
<teleport to="body">
<ul
v-show="contextMenuVisible"
:style="{ left: left + 'px', top: top + 'px' }"
ref="contextMenuRef"
class="contextmenu"
>
<li @click="closeAll">关闭所有</li>
<li @click="closeLeft">关闭左侧</li>
<li @click="closeRight">关闭右侧</li>
<li @click="closeOther">关闭其他</li>
</ul>
</teleport>
</div>
</template>
@@ -83,6 +86,7 @@
const left = ref(0)
const top = ref(0)
const contextMenuRef = ref(null)
const isCollapse = ref(false)
const isMobile = ref(false)
const rightActive = ref('')
@@ -93,18 +97,25 @@
if (historys.value.length === 1 && route.name === defaultRouter.value) {
return false
}
let id = ''
if (e.srcElement.nodeName === 'SPAN') {
id = e.srcElement.offsetParent.id
} else {
id = e.srcElement.id
}
const tabEl = e.target?.closest?.('[id^="tab-"]')
const id = tabEl?.id || ''
if (id) {
contextMenuVisible.value = true
left.value = e.clientX
top.value = e.clientY + 10
rightActive.value = id.substring(4)
const tabRect = tabEl.getBoundingClientRect()
left.value = tabRect.left
top.value = tabRect.bottom + 6
nextTick(() => {
const menu = contextMenuRef.value
if (!menu) return
const { offsetWidth, offsetHeight } = menu
const edgeSpace = 8
const maxLeft = window.innerWidth - offsetWidth - edgeSpace
const maxTop = window.innerHeight - offsetHeight - edgeSpace
left.value = Math.max(edgeSpace, Math.min(left.value, maxLeft))
top.value = Math.max(edgeSpace, Math.min(top.value, maxTop))
})
}
}
const closeAll = () => {
@@ -379,16 +390,46 @@
<style lang="scss" scoped>
.contextmenu {
@apply bg-white dark:bg-slate-900 w-28 m-0 py-2.5 px-0 border border-gray-200 text-sm shadow-md rounded absolute z-50 border-solid dark:border-slate-800;
min-width: 120px;
padding: 6px;
margin: 0;
position: fixed;
z-index: 3000;
overflow: hidden;
list-style: none;
border: 1px solid rgba(0, 212, 255, 0.26);
border-radius: 12px;
background:
linear-gradient(135deg, rgba(9, 24, 48, 0.98), rgba(5, 16, 32, 0.94)),
radial-gradient(circle at 100% 0%, rgba(0, 212, 255, 0.12), transparent 34%);
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.42), 0 0 22px rgba(0, 212, 255, 0.16);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
}
.contextmenu li {
@apply text-slate-700 dark:text-slate-200 text-base list-none px-4 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-600 cursor-pointer;
min-height: 32px;
padding: 6px 14px;
font-size: 14px;
line-height: 20px;
color: var(--tech-text-muted, #bfdbfe);
cursor: pointer;
border-radius: 8px;
transition: color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
&:hover {
color: #67e8f9;
background: rgba(14, 165, 233, 0.18);
box-shadow: inset 0 0 14px rgba(0, 212, 255, 0.08);
transform: translateX(2px);
}
}
$base-tag-item-height: 4rem;
.gva-tabs {
position: relative;
::v-deep(.el-tabs--card > .el-tabs__header) {
border: none;
}