当前位置: 首页 > news >正文

ruoyi-vue(十五)——布局设置,导航栏,侧边栏,顶部栏

一、布局设置

在前端页面有一个布局设置,点开可以设置主题风格,颜色,是否显示标签页等功能。
在这里插入图片描述

1.1 路由

页面所有请求都是通过路由,先来看路由:src→router→index.js。
路由中引用了Layout,从项目中的 src/layout 路径导入一个默认导出的组件,并将其命名为 Layout

import Layout from '@/layout'

在vite.config.js中通过resolve.alias设置了路径别名,将@映射到项目的src目录。import Layout from ‘@/layout’ 实际上就是从 src/layout/index.vue 文件中导入默认导出的组件。

'@': path.resolve(__dirname, './src')

除了登录,注册,404等页面外基本都用了Layout

// 公共路由
export const constantRoutes = [{path: '/redirect',component: Layout,hidden: true,children: [{path: '/redirect/:path(.*)',component: () => import('@/views/redirect/index.vue')}]},{path: '/login',component: () => import('@/views/login'),hidden: true},{path: '/register',component: () => import('@/views/register'),hidden: true},{path: "/:pathMatch(.*)*",component: () => import('@/views/error/404'),hidden: true},{path: '/401',component: () => import('@/views/error/401'),hidden: true},{path: '',component: Layout,redirect: '/index',children: [{path: '/index',component: () => import('@/views/index'),name: 'Index',meta: { title: '首页', icon: 'dashboard', affix: true }}]},{path: '/user',component: Layout,hidden: true,redirect: 'noredirect',children: [{path: 'profile/:activeTab?',component: () => import('@/views/system/user/profile/index'),name: 'Profile',meta: { title: '个人中心', icon: 'user' }}]}
]// 动态路由,基于用户权限动态去加载
export const dynamicRoutes = [{path: '/system/user-auth',component: Layout,hidden: true,permissions: ['system:user:edit'],children: [{path: 'role/:userId(\\d+)',component: () => import('@/views/system/user/authRole'),name: 'AuthRole',meta: { title: '分配角色', activeMenu: '/system/user' }}]},{path: '/system/role-auth',component: Layout,hidden: true,permissions: ['system:role:edit'],children: [{path: 'user/:roleId(\\d+)',component: () => import('@/views/system/role/authUser'),name: 'AuthUser',meta: { title: '分配用户', activeMenu: '/system/role' }}]},{path: '/system/dict-data',component: Layout,hidden: true,permissions: ['system:dict:list'],children: [{path: 'index/:dictId(\\d+)',component: () => import('@/views/system/dict/data'),name: 'Data',meta: { title: '字典数据', activeMenu: '/system/dict' }}]},{path: '/monitor/job-log',component: Layout,hidden: true,permissions: ['monitor:job:list'],children: [{path: 'index/:jobId(\\d+)',component: () => import('@/views/monitor/job/log'),name: 'JobLog',meta: { title: '调度日志', activeMenu: '/monitor/job' }}]},{path: '/tool/gen-edit',component: Layout,hidden: true,permissions: ['tool:gen:edit'],children: [{path: 'index/:tableId(\\d+)',component: () => import('@/views/tool/gen/editTable'),name: 'GenEdit',meta: { title: '修改生成配置', activeMenu: '/tool/gen' }}]}
]

1.2 组件

1.2.1 Template模板部分

<template><div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }"><div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/><sidebar v-if="!sidebar.hide" class="sidebar-container" /><div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container"><div :class="{ 'fixed-header': fixedHeader }"><navbar @setLayout="setLayout" /><tags-view v-if="needTagsView" /></div><app-main /><settings ref="settingRef" /></div></div>
</template>
  • 根容器:
    • 使用动态class和style,根据主题设置背景色
    • 根据设备类型和侧边栏状态添加不同CSS类
  • 移动端遮罩层
    • 当在移动端且侧边栏打开时显示半透明黑色遮罩
    • 点击遮罩会触发handleClickOutside关闭侧边栏
  • 侧边栏组件:
    • 根据sidebar.hider状态决定是否显示侧边栏
  • 主内容区域:
    • 包含固定头部(导航栏和标签视图)和主要内容区域
    • 根据设置决定是否显示标签页视图
    • 集成设置组件

1.2.2 Script脚本部分

<script setup>
// 导入模块
import { useWindowSize } from '@vueuse/core'
import Sidebar from './components/Sidebar/index.vue'
import { AppMain, Navbar, Settings, TagsView } from './components'
import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'// 初始化设置状态管理实例,用于管理应用的主题、标签页显示等设置
const settingsStore = useSettingsStore()
//计算属性
const theme = computed(() => settingsStore.theme)
const sideTheme = computed(() => settingsStore.sideTheme)
const sidebar = computed(() => useAppStore().sidebar)
const device = computed(() => useAppStore().device)
const needTagsView = computed(() => settingsStore.tagsView)
const fixedHeader = computed(() => settingsStore.fixedHeader)// 计算属性,判断是否需要固定头部
const classObj = computed(() => ({hideSidebar: !sidebar.value.opened,openSidebar: sidebar.value.opened,withoutAnimation: sidebar.value.withoutAnimation,mobile: device.value === 'mobile'
}))// 根据侧边栏和设备状态返回相应的CSS类名对象,用于动态设置布局样式
const { width, height } = useWindowSize()
// 使用VueUse库获取窗口尺寸的响应式数据
const WIDTH = 992 // refer to Bootstrap's responsive design// 定义断点宽度,小于该宽度视为移动设备
watch(() => device.value, () => {if (device.value === 'mobile' && sidebar.value.opened) {useAppStore().closeSideBar({ withoutAnimation: false })}
})// 监听设备类型变化,当切换到移动设备且侧边栏打开时,关闭侧边栏并带动画
watchEffect(() => {if (width.value - 1 < WIDTH) {useAppStore().toggleDevice('mobile')useAppStore().closeSideBar({ withoutAnimation: true })} else {useAppStore().toggleDevice('desktop')}
})// 响应式监听窗口宽度变化,当宽度小于断点时切换为移动设备模式并关闭侧边栏(无动画),
// 否则切换为桌面设备模式
function handleClickOutside() {useAppStore().closeSideBar({ withoutAnimation: false })
}// 处理点击遮罩层事件,关闭侧边栏并带动画。
const settingRef = ref(null)
function setLayout() {settingRef.value.openSetting()
}
</script>
  • 导入模块
    • 使用@vueuse/core的useWindowSize监听窗口大小
    • 导入各种子组件(侧边栏、主内容、导航栏等)
  • 状态管理
    • 获取状态管理实例,用于管理应用的主题、标签页显示等设置
    • 计算属性包括主题、侧边栏状态、设备类型等
  • 自适应处理
    • 监听窗口大小变化,当宽度小于992px时切换为移动端模式
    • 移动端模式自动关闭侧边栏
  • 交互方法
    • handleClickOutside点击遮罩关闭侧边栏
    • setLayout打开设置面板

1.2.3 Style样式部分

<style lang="scss" scoped>
// 引入mixin和变量模块,分别命名为mix和vars
@use "@/assets/styles/mixin.scss" as mix;
@use "@/assets/styles/variables.module.scss" as vars;// 应用包装器样式,使用clearfix清除浮动,设置相对定位,占满全屏
.app-wrapper {@include mix.clearfix;position: relative;height: 100%;width: 100%;
// 当在移动设备上且侧边栏打开时,使用固定定位。&.mobile.openSidebar {position: fixed;top: 0;}
}// 移动端遮罩层样式,黑色半透明背景,覆盖全屏,层级为999。
.drawer-bg {background: #000;opacity: 0.3;width: 100%;top: 0;height: 100%;position: absolute;z-index: 999;
}// 固定头部样式,使用固定定位,宽度为100%减去侧边栏宽度(200px),宽度变化有0.28秒过渡动画
.fixed-header {position: fixed;top: 0;right: 0;z-index: 9;width: calc(100% - #{vars.$base-sidebar-width});transition: width 0.28s;
}// 当侧边栏隐藏时,固定头部宽度为100%减去54px(折叠后的侧边栏宽度)
.hideSidebar .fixed-header {width: calc(100% - 54px);
}.sidebarHide .fixed-header {width: 100%;
}// 当侧边栏完全隐藏时,固定头部占满全屏宽度。
.mobile .fixed-header {width: 100%;
}
</style>
  • 移动端打开侧边栏时使用固定定位
  • 不同状态下(隐藏侧边栏、移动端等)的样式适配

二、导航栏

导航栏在web页面上方,左侧是一个隐藏或展开侧边栏的按钮,紧挨着的是面包屑导航组件,右边是工具栏。
在这里插入图片描述

2.1 Navbar组件

src→layout→components→Navbar.vue
Navbar组件是系统的顶部导航栏,包含以下主要功能:

  • 侧边栏控制:通过汉堡菜单控制侧边栏展开/收起
  • 导航显示:根据设置显示面包屑或顶部菜单
  • 快捷功能:搜索、全屏、主题切换、布局大小调整
  • 用户信息:显示用户头像和昵称,提供个人中心和退出登录功能
  • 项目链接:提供源码和文档的快速访问
  • 响应式设计:移动端隐藏部分功能

这个组件整合了系统顶部的大部分常用功能,为用户提供了便捷的操作入口。

2.1.1 Template模板部分

<template>// 创建navbar容器,作为导航栏的根元素。<div class="navbar">// 汉堡菜单组件,用于控制侧边栏的展开/收起状态,点击时触发toggleSideBar方法<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />// 面包屑导航组件,当不使用顶部导航时显示,显示当前页面路径。<breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" />// 顶部导航菜单组件,当启用顶部导航时显示。<top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" />// 右侧菜单容器,包含各种功能按钮和用户信息 <div class="right-menu">// 非移动设备上显示以下内容<template v-if="appStore.device !== 'mobile'">// 头部搜索组件,用于快速搜索菜单项<header-search id="header-search" class="right-menu-item" />// 显示项目源码链接的组件,使用Element Plus的tooltip提示"源码地址"。<el-tooltip content="源码地址" effect="dark" placement="bottom"><ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /></el-tooltip>// 显示项目文档链接的组件,使用Element Plus的tooltip提示"文档地址"。<el-tooltip content="文档地址" effect="dark" placement="bottom"><ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" /></el-tooltip>// 全屏切换组件,用于切换页面全屏状态<screenfull id="screenfull" class="right-menu-item hover-effect" />// 主题切换组件<el-tooltip content="主题模式" effect="dark" placement="bottom"><div class="right-menu-item hover-effect theme-switch-wrapper" @click="toggleTheme"><svg-icon v-if="settingsStore.isDark" icon-class="sunny" /><svg-icon v-if="!settingsStore.isDark" icon-class="moon" /></div></el-tooltip>// 布局大小选择组件<el-tooltip content="布局大小" effect="dark" placement="bottom"><size-select id="size-select" class="right-menu-item hover-effect" /></el-tooltip>// 结束非移动端条件渲染</template>// 用户信息下拉菜单,显示用户头像和昵称,提供个人中心链接和退出登录功能<el-dropdown @command="handleCommand" class="avatar-container right-menu-item hover-effect" trigger="hover"><div class="avatar-wrapper"><img :src="userStore.avatar" class="user-avatar" /><span class="user-nickname"> {{ userStore.nickName }} </span></div><template #dropdown><el-dropdown-menu><router-link to="/user/profile"><el-dropdown-item>个人中心</el-dropdown-item></router-link><el-dropdown-item divided command="logout"><span>退出登录</span></el-dropdown-item></el-dropdown-menu></template></el-dropdown>// 设置按钮,当启用设置功能时显示,点击打开设置面板<div class="right-menu-item hover-effect setting" @click="setLayout" v-if="settingsStore.showSettings"><svg-icon icon-class="more-up" /></div></div></div>
// 结束navbar容器
</template>

2.1.2 Script脚本部分

<script setup>
// 导入Element Plus的消息框组件,用于确认对话框
import { ElMessageBox } from 'element-plus'
// 导入所需子组件
import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav'
import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
import HeaderSearch from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc'
// 导入状态管理模块
import useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings'// 初始化各状态管理实例
const appStore = useAppStore()
const userStore = useUserStore()
const settingsStore = useSettingsStore()// 切换侧边栏展开/收起状态的方法。
function toggleSideBar() {appStore.toggleSideBar()
}// 处理下拉菜单命令的通用方法,根据命令类型调用相应函数
function handleCommand(command) {switch (command) {case "setLayout":setLayout()breakcase "logout":logout()breakdefault:break}
}
// 退出登录方法
function logout() {ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {// 确认后调用用户登出方法并跳转到首页userStore.logOut().then(() => {location.href = '/index'})}).catch(() => { })
}// 定义并实现setLayout事件发射方法,用于通知父组件打开设置面板
const emits = defineEmits(['setLayout'])
function setLayout() {emits('setLayout')
}// 切换主题模式方法,调用设置存储中的切换主题功能
function toggleTheme() {settingsStore.toggleTheme()
}
</script>

2.1.3 Style样式部分

<style lang='scss' scoped>
// 导航栏基础样式
.navbar {height: 50px;overflow: hidden;position: relative;background: var(--navbar-bg);box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);// 汉堡菜单容器样式.hamburger-container {line-height: 46px;height: 100%;float: left;cursor: pointer;transition: background 0.3s;-webkit-tap-highlight-color: transparent;&:hover {background: rgba(0, 0, 0, 0.025);}}// 面包屑容器左浮动 .breadcrumb-container {float: left;}// 顶部菜单容器绝对定位.topmenu-container {position: absolute;left: 50px;}// 错误日志容器样式.errLog-container {display: inline-block;vertical-align: top;}// 右侧菜单样式容器.right-menu {float: right;height: 100%;line-height: 50px;display: flex;&:focus {outline: none;}// 右侧菜单项通用样式.right-menu-item {display: inline-block;padding: 0 8px;height: 100%;font-size: 18px;color: #5a5e66;vertical-align: text-bottom;&.hover-effect {cursor: pointer;transition: background 0.3s;&:hover {background: rgba(0, 0, 0, 0.025);}}// 主题切换按钮样式&.theme-switch-wrapper {display: flex;align-items: center;svg {transition: transform 0.3s;&:hover {transform: scale(1.15);}}}}// 用户头像容器样式.avatar-container {margin-right: 0px;padding-right: 0px;.avatar-wrapper {margin-top: 10px;right: 5px;position: relative;.user-avatar {cursor: pointer;width: 30px;height: 30px;border-radius: 50%;}.user-nickname{position: relative;left: 5px;bottom: 10px;font-size: 14px;font-weight: bold;}i {cursor: pointer;position: absolute;right: -20px;top: 25px;font-size: 12px;}}}}
}
</style>

三、侧边栏

web页面左侧的侧边栏
在这里插入图片描述

3.1 Sidebar组件

src→layout→Sidebar→index.vue
Sidebar组件实现了完整的侧边栏导航功能,具有以下特点:

  • 可配置性:支持显示/隐藏Logo、主题切换、折叠/展开等配置
  • 动态菜单:根据权限动态生成菜单项
  • 主题支持:支持多种主题和暗黑模式
  • 状态管理:与应用状态管理集成,保持状态同步
  • 自适应:根据屏幕大小和设置自动调整布局,颜色和样式根据主题动态调整
    通过组合Logo、SidebarItem等子组件构建完整的侧边栏界面

3.1.1 Template模板部分

<template>// 创建侧边栏容器,根据showLogo的值决定是否添加'has-logo'类<div :class="{ 'has-logo': showLogo }" class="sidebar-container">// 条件渲染Logo组件,当showLogo为true时显示,传递collapse属性控制Logo的折叠状态<logo v-if="showLogo" :collapse="isCollapse" />// 使用Element Plus的滚动条组件包装菜单内容,设置包装类名为scrollbar-wrapper<el-scrollbar wrap-class="scrollbar-wrapper">// 创建Element Plus菜单组件,并设置以下属性<el-menu// 当前激活的菜单项:default-active="activeMenu"// 控制菜单是否折叠:collapse="isCollapse"// 菜单背景色:background-color="getMenuBackground"// 菜单文字颜色:text-color="getMenuTextColor"// 只保持一个子菜单展开:unique-opened="true"// 激活菜单项的文字颜色:active-text-color="theme"// 是否使用折叠动画:collapse-transition="false"// 菜单模式为垂直mode="vertical"// 菜单主题类:class="sideTheme">// 遍历sidebarRouters,为每个路由创建SidebarItem组件,传递路由信息和基础路径<sidebar-itemv-for="(route, index) in sidebarRouters":key="route.path + index":item="route":base-path="route.path"/>// 结束菜单和滚动条组件</el-menu></el-scrollbar></div>
</template>

3.1.2 Script脚本部分

<script setup>
// 导入所需组件和模块
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/assets/styles/variables.module.scss'
import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'// 获取路由和各状态管理实例
const route = useRoute()
const appStore = useAppStore()
const settingsStore = useSettingsStore()
const permissionStore = usePermissionStore()// 计算属性,获取权限存储中的侧边栏路由
const sidebarRouters = computed(() => permissionStore.sidebarRouters)
// 根据设置决定是否显示logo
const showLogo = computed(() => settingsStore.sidebarLogo)
// 获取侧边栏主题设置
const sideTheme = computed(() => settingsStore.sideTheme)
// 获取当前主题颜色
const theme = computed(() => settingsStore.theme)
// 根据侧边栏是否打开决定菜单是否折叠
const isCollapse = computed(() => !appStore.sidebar.opened)// 根据暗黑模式和主题设置获取菜单背景色
const getMenuBackground = computed(() => {if (settingsStore.isDark) {return 'var(--sidebar-bg)'}return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBg
})// 根据暗黑模式和主题设置获取菜单文字颜色
const getMenuTextColor = computed(() => {if (settingsStore.isDark) {return 'var(--sidebar-text)'}return sideTheme.value === 'theme-dark' ? variables.menuText : variables.menuLightText
})// 根据路由元信息决定当前激活的菜单项
const activeMenu = computed(() => {const { meta, path } = routeif (meta.activeMenu) {return meta.activeMenu}return path
})
</script>

3.1.3 Style样式部分

// 设置侧边栏容器背景色,使用v-bind绑定计算属性
<style lang="scss" scoped>
.sidebar-container {background-color: v-bind(getMenuBackground);// 设置滚动条包装器背景色.scrollbar-wrapper {background-color: v-bind(getMenuBackground);}// 设置菜单样式.el-menu {border: none;height: 100%;width: 100% !important;// 设置菜单项和子菜单标题在悬停时的背景色.el-menu-item, .el-sub-menu__title {&:hover {background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;}}// 设置菜单项文字颜色,并为激活状态的菜单项设置特殊颜色和背景.el-menu-item {color: v-bind(getMenuTextColor);&.is-active {color: var(--menu-active-text, #409eff);background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;}}// 设置子菜单标题文字颜色.el-sub-menu__title {color: v-bind(getMenuTextColor);}}
}
</style>

四、顶部栏

页面上方的菜单栏,开启后会在页面上方显示一级菜单
关闭状态:
在这里插入图片描述

打开状态:
在这里插入图片描述

4.1 TopNav组件

src→components→TopNav→index.vue
TopNav组件是一个基于Element Plus的顶部水平导航菜单,主要功能包括:

  • 动态菜单显示:根据路由配置动态生成顶部菜单项
  • 菜单折叠:当菜单项过多时,自动将超出部分折叠到"更多菜单"下拉中
  • 自适应:根据屏幕宽度动态调整可见菜单项数量
  • 路由联动:与侧边栏菜单联动,点击顶部菜单项可切换侧边栏内容
  • 多类型链接支持:支持内部路由跳转和外部链接新窗口打开
  • 主题定制:支持主题颜色定制

该组件充分利用了Vue 3的响应式特性和组合式API,结合Element Plus组件库实现顶部导航菜单。

4.1.1 Template模板部分

<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图标<svg-iconv-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-iconv-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>

4.1.2 Script脚本部分

<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.pathlet 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 = keyconst route = routers.value.find(item => item.path === key)if (isHttp(key)) {// http(s):// 路径新窗口打开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>

4.1.3 Style样式部分

<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>
http://www.dtcms.com/a/390130.html

相关文章:

  • 第13章 线程池配置
  • 任天堂获得新专利:和《宝可梦传说:阿尔宙斯》相关
  • Redis MONITOR 命令详解
  • 七、Java-多线程、网络编程
  • 三轴云台之动态补偿机制篇
  • MySQL备份与恢复实战指南:从原理到落地,守护数据安全
  • 手机上记录todolist待办清单的工具选择用哪一个?
  • 仓颉编程语言青少年基础教程:Interface(接口)
  • 用 go-commons 打造一个轻量级内置监控系统,让服务开箱即用
  • PyQt6之QSpinBox计数器应用
  • 大模型应用开发4-MCP实战
  • Ruoyi-vue-plus-5.x第八篇文件管理与存储: 8.3 文件处理功能
  • 【51单片机】【protues仿真】基于51单片机PM2.5温湿度测量蓝牙系统
  • 病毒学原理
  • 怎样快速搭建一个高效的数据存储系统:Python实战指南
  • 音频驱动视频生成新突破:Wan2.2-S2V 模型全面体验与教程
  • 关于pc端分页+h5端加载更多的vue3简单钩子函数
  • MySQL 练习题
  • 推客小程序二级分销机制设计与实现:从0到1搭建裂变增长引擎
  • 【C++】多态(上)
  • uos中创建自定义Ip (192.168.137.1)的热点的方法
  • 【每日算法】搜索插入位置 LeetCode
  • vue+springboot+ngnix前后端分离项目部署
  • sward入门到实战(1) - 安装教程
  • 独立站的优势有哪些
  • Java学习历程18——哈希表的使用
  • 机械传动里的名词——传动比
  • qiankun 主子应用部署教程(Nginx 小白版)
  • 开启视觉奇旅:走进《计算机图形学》的世界
  • 光伏组件IV曲线测试仪的功能