From e38c0656952f70552170fc4ee72420b0c741919e Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 18 六月 2026 10:04:09 +0800
Subject: [PATCH] 样式修改
---
src/components/TopNav/index.vue | 433 +++++++++++++++++++++++++++---------------------------
1 files changed, 216 insertions(+), 217 deletions(-)
diff --git a/src/components/TopNav/index.vue b/src/components/TopNav/index.vue
index 56e2803..27de555 100644
--- a/src/components/TopNav/index.vue
+++ b/src/components/TopNav/index.vue
@@ -1,217 +1,216 @@
-<template>
- <el-menu
- :default-active="activeMenu"
- mode="horizontal"
- @select="handleSelect"
- :ellipsis="false"
- >
- <template v-for="(item, index) in topMenus">
- <el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
- <svg-icon
- v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
- :icon-class="item.meta.icon"/>
- {{ item.meta.title }}
- </el-menu-item>
- </template>
-
- <!-- 椤堕儴鑿滃崟瓒呭嚭鏁伴噺鎶樺彔 -->
- <el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
- <template #title>鏇村鑿滃崟</template>
- <template v-for="(item, index) in topMenus">
- <el-menu-item
- :index="item.path"
- :key="index"
- v-if="index >= visibleNumber">
- <svg-icon
- v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
- :icon-class="item.meta.icon"/>
- {{ item.meta.title }}
- </el-menu-item>
- </template>
- </el-sub-menu>
- </el-menu>
-</template>
-
-<script setup>
-import { constantRoutes } from "@/router"
-import { isHttp } from '@/utils/validate'
-import useAppStore from '@/store/modules/app'
-import useSettingsStore from '@/store/modules/settings'
-import usePermissionStore from '@/store/modules/permission'
-
-// 椤堕儴鏍忓垵濮嬫暟
-const visibleNumber = ref(null)
-// 褰撳墠婵�娲昏彍鍗曠殑 index
-const currentIndex = ref(null)
-// 闅愯棌渚ц竟鏍忚矾鐢�
-const hideList = ['/index', '/user/profile']
-
-const appStore = useAppStore()
-const settingsStore = useSettingsStore()
-const permissionStore = usePermissionStore()
-const route = useRoute()
-const router = useRouter()
-
-// 涓婚棰滆壊
-const theme = computed(() => settingsStore.theme)
-// 鎵�鏈夌殑璺敱淇℃伅
-const routers = computed(() => permissionStore.topbarRouters)
-
-// 椤堕儴鏄剧ず鑿滃崟
-const topMenus = computed(() => {
- let topMenus = []
- routers.value.map((menu) => {
- if (menu.hidden !== true) {
- // 鍏煎椤堕儴鏍忎竴绾ц彍鍗曞唴閮ㄨ烦杞�
- if (menu.path === '/' && menu.children) {
- topMenus.push(menu.children[0])
- } else {
- topMenus.push(menu)
- }
- }
- })
- return topMenus
-})
-
-// 璁剧疆瀛愯矾鐢�
-const childrenMenus = computed(() => {
- let childrenMenus = []
- routers.value.map((router) => {
- for (let item in router.children) {
- if (router.children[item].parentPath === undefined) {
- if(router.path === "/") {
- router.children[item].path = "/" + router.children[item].path
- } else {
- if(!isHttp(router.children[item].path)) {
- router.children[item].path = router.path + "/" + router.children[item].path
- }
- }
- router.children[item].parentPath = router.path
- }
- childrenMenus.push(router.children[item])
- }
- })
- return constantRoutes.concat(childrenMenus)
-})
-
-// 榛樿婵�娲荤殑鑿滃崟
-const activeMenu = computed(() => {
- const path = route.path
- let activePath = path
- if (path !== undefined && path.lastIndexOf("/") > 0 && hideList.indexOf(path) === -1) {
- const tmpPath = path.substring(1, path.length)
- if (!route.meta.link) {
- activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"))
- appStore.toggleSideBarHide(false)
- }
- } else if(!route.children) {
- activePath = path
- appStore.toggleSideBarHide(true)
- }
- activeRoutes(activePath)
- return activePath
-})
-
-function setVisibleNumber() {
- const width = document.body.getBoundingClientRect().width / 3
- visibleNumber.value = parseInt(width / 85)
-}
-
-function handleSelect(key, keyPath) {
- currentIndex.value = key
- const route = routers.value.find(item => item.path === key)
- if (isHttp(key)) {
- // http(s):// 璺緞鏂扮獥鍙f墦寮�
- window.open(key, "_blank")
- } else if (!route || !route.children) {
- // 娌℃湁瀛愯矾鐢辫矾寰勫唴閮ㄦ墦寮�
- const routeMenu = childrenMenus.value.find(item => item.path === key)
- if (routeMenu && routeMenu.query) {
- let query = JSON.parse(routeMenu.query)
- router.push({ path: key, query: query })
- } else {
- router.push({ path: key })
- }
- appStore.toggleSideBarHide(true)
- } else {
- // 鏄剧ず宸︿晶鑱斿姩鑿滃崟
- activeRoutes(key)
- appStore.toggleSideBarHide(false)
- }
-}
-
-function activeRoutes(key) {
- let routes = []
- if (childrenMenus.value && childrenMenus.value.length > 0) {
- childrenMenus.value.map((item) => {
- if (key == item.parentPath || (key == "index" && "" == item.path)) {
- routes.push(item)
- }
- })
- }
- if(routes.length > 0) {
- permissionStore.setSidebarRouters(routes)
- } else {
- appStore.toggleSideBarHide(true)
- }
- return routes
-}
-
-onMounted(() => {
- window.addEventListener('resize', setVisibleNumber)
-})
-
-onBeforeUnmount(() => {
- window.removeEventListener('resize', setVisibleNumber)
-})
-
-onMounted(() => {
- setVisibleNumber()
-})
-</script>
-
-<style lang="scss">
-.topmenu-container.el-menu--horizontal > .el-menu-item {
- float: left;
- height: 50px !important;
- line-height: 50px !important;
- color: #999093 !important;
- padding: 0 5px !important;
- margin: 0 10px !important;
-}
-
-.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
- border-bottom: 2px solid #{'var(--theme)'} !important;
- color: #303133;
-}
-
-/* sub-menu item */
-.topmenu-container.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
- float: left;
- height: 50px !important;
- line-height: 50px !important;
- color: #999093 !important;
- padding: 0 5px !important;
- margin: 0 10px !important;
-}
-
-/* 鑳屾櫙鑹查殣钘� */
-.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
- background-color: #ffffff;
-}
-
-/* 鍥炬爣鍙抽棿璺� */
-.topmenu-container .svg-icon {
- margin-right: 4px;
-}
-
-/* topmenu more arrow */
-.topmenu-container .el-sub-menu .el-sub-menu__icon-arrow {
- position: static;
- vertical-align: middle;
- margin-left: 8px;
- margin-top: 0px;
-}
-
-
-</style>
+<template>
+ <div class="top-nav">
+ <button v-show="showArrows"
+ class="nav-arrow nav-arrow--left"
+ type="button"
+ :disabled="!canScrollLeft"
+ @click="scrollLeft">
+ <el-icon :size="18">
+ <ArrowLeft />
+ </el-icon>
+ </button>
+ <div ref="scrollWrapRef"
+ class="top-nav__scroll"
+ @scroll.passive="updateScrollState">
+ <el-menu class="top-nav-menu"
+ :default-active="activeMenu"
+ mode="horizontal"
+ :unique-opened="true"
+ :ellipsis="false"
+ :active-text-color="theme">
+ <sidebar-item v-for="(routeItem, index) in topbarRouters"
+ :key="routeItem.path + index"
+ :item="routeItem"
+ :base-path="routeItem.path" />
+ </el-menu>
+ </div>
+ <button v-show="showArrows"
+ class="nav-arrow nav-arrow--right"
+ type="button"
+ :disabled="!canScrollRight"
+ @click="scrollRight">
+ <el-icon :size="18">
+ <ArrowRight />
+ </el-icon>
+ </button>
+ </div>
+</template>
+
+<script setup>
+ import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
+ import useSettingsStore from "@/store/modules/settings";
+ import usePermissionStore from "@/store/modules/permission";
+ import SidebarItem from "@/layout/components/Sidebar/SidebarItem.vue";
+
+ const settingsStore = useSettingsStore();
+ const permissionStore = usePermissionStore();
+ const route = useRoute();
+
+ // 涓婚棰滆壊
+ const theme = computed(() => settingsStore.theme);
+ const topbarRouters = computed(() => permissionStore.topbarRouters);
+ const scrollWrapRef = ref(null);
+ const canScrollLeft = ref(false);
+ const canScrollRight = ref(false);
+ const showArrows = computed(() => canScrollLeft.value || canScrollRight.value);
+
+ // 榛樿婵�娲荤殑鑿滃崟
+ const activeMenu = computed(() => {
+ const { meta, path } = route;
+ if (meta?.activeMenu) return meta.activeMenu;
+ return path;
+ });
+
+ function updateScrollState() {
+ const el = scrollWrapRef.value;
+ if (!el) return;
+ const maxScrollLeft = el.scrollWidth - el.clientWidth;
+ canScrollLeft.value = el.scrollLeft > 0;
+ canScrollRight.value = el.scrollLeft < maxScrollLeft - 1;
+ }
+
+ function scrollByStep(direction) {
+ const el = scrollWrapRef.value;
+ if (!el) return;
+ const step = Math.max(240, Math.floor(el.clientWidth * 0.6));
+ el.scrollBy({ left: direction * step, behavior: "smooth" });
+ requestAnimationFrame(() => updateScrollState());
+ }
+
+ function scrollLeft() {
+ scrollByStep(-1);
+ }
+
+ function scrollRight() {
+ scrollByStep(1);
+ }
+
+ let resizeRaf = 0;
+ function handleResize() {
+ if (resizeRaf) cancelAnimationFrame(resizeRaf);
+ resizeRaf = requestAnimationFrame(() => {
+ updateScrollState();
+ });
+ }
+
+ onMounted(() => {
+ updateScrollState();
+ window.addEventListener("resize", handleResize, { passive: true });
+ });
+
+ onBeforeUnmount(() => {
+ window.removeEventListener("resize", handleResize);
+ if (resizeRaf) cancelAnimationFrame(resizeRaf);
+ });
+</script>
+
+<style lang="scss" scoped>
+ .top-nav {
+ position: relative;
+ height: var(--topbar-height);
+ display: flex;
+ align-items: center;
+ width: 100%;
+ min-width: 0;
+ }
+
+ .top-nav__scroll {
+ flex: 1;
+ min-width: 0;
+ height: 100%;
+ overflow-x: auto;
+ overflow-y: hidden;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ }
+
+ .nav-arrow {
+ width: 34px;
+ height: 34px;
+ flex: 0 0 34px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 999px;
+ border: 1px solid var(--surface-border);
+ background: var(--surface-base);
+ color: var(--text-secondary);
+ cursor: pointer;
+ transition: 0.2s ease;
+ margin: 0 8px;
+
+ &:hover:not(:disabled) {
+ color: var(--el-color-primary);
+ border-color: rgba(var(--el-color-primary-rgb), 0.35);
+ background: rgba(var(--el-color-primary-rgb), 0.06);
+ }
+
+ &:disabled {
+ opacity: 0.45;
+ cursor: not-allowed;
+ }
+ }
+
+ .top-nav-menu {
+ width: max-content;
+ min-width: 100%;
+ height: 100%;
+ border-bottom: none;
+ background: transparent;
+ }
+
+ :deep(.top-nav-menu.el-menu--horizontal) {
+ display: flex;
+ align-items: stretch;
+ flex-wrap: nowrap;
+ height: 100%;
+ width: max-content;
+ min-width: 100%;
+ }
+
+ :deep(.top-nav-menu.el-menu--horizontal > .el-menu-item),
+ :deep(.top-nav-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title) {
+ height: var(--topbar-height);
+ line-height: var(--topbar-height);
+ padding: 0 12px;
+ color: var(--text-secondary);
+ border-bottom: 2px solid transparent;
+ transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
+ }
+
+ :deep(
+ .top-nav-menu.el-menu--horizontal > .el-menu-item:not(.is-disabled):hover
+ ),
+ :deep(
+ .top-nav-menu.el-menu--horizontal > .el-sub-menu > .el-sub-menu__title:hover
+ ) {
+ background: rgba(var(--el-color-primary-rgb), 0.06);
+ color: var(--el-color-primary);
+ }
+
+ :deep(.top-nav-menu.el-menu--horizontal > .el-menu-item.is-active),
+ :deep(
+ .top-nav-menu.el-menu--horizontal
+ > .el-sub-menu.is-active
+ > .el-sub-menu__title
+ ) {
+ background: rgba(var(--el-color-primary-rgb), 0.06);
+ color: var(--el-color-primary);
+ font-weight: 600;
+ border-bottom-color: var(--el-color-primary);
+ }
+
+ :deep(.top-nav-menu.el-menu--horizontal > .el-menu-item .svg-icon),
+ :deep(
+ .top-nav-menu.el-menu--horizontal
+ > .el-sub-menu
+ > .el-sub-menu__title
+ .svg-icon
+ ) {
+ margin-right: 8px;
+ }
+</style>
--
Gitblit v1.9.3