Vue实战【调整Vue-element-admin中的菜单栏,并添加顶部模块菜单栏】

目录

  • 🌟前言
  • 🌟小伙伴们先看
  • 🌟实现思路
  • 🌟具体代码
  • 🌟最后

🌟前言

因为最近在整合公司的项目,需要把所有系统里的功能集成到一个项目里,这样就导致菜单栏目录会特别的多,不便于用户使用,体验效果极差。于是想到了一个方法,就是增加顶部导航栏,点击的时候让侧边菜单栏在显示相对应模块的所有菜单;这样的话就可以很大程度提升我们的用户体验啦。

🌟小伙伴们先看

在这里插入图片描述

🌟实现思路

嗯,干活前一定要先把思路理清楚,记在小本本上,画个图都行哈哈

  • 布局方面我需要在Navbar组件内添加一个导航组件以便我们去渲染顶部模块菜单;
  • 因为是动态路由所以我们可以:
    • 登录的时候让后端返回所有的当前用户下所有的菜单权限;
    • 登录时候只返回默认显示的菜单,每次点击的时候再去获取相应的模块菜单权限。

我这边用的是第一种方式,登陆的时候获取全部的存在vuex里,每次点击的时候再去处理相应的数据;小伙伴们也可以尝试一下第二种方式哦。

🌟具体代码

话不多说,直接开整。。。

<!--src/layout/components/Navbar.vue-->
<template><div class="navbar"><hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /><!--重点一:顶部menu--><el-menumode="horizontal"default-active="/"@select="handleSelect"><el-menu-item v-for="item in menuList" :key="item.path" class="menuItem" :index="item.path"><icon :class="item.meta?item.meta.icon:''" /><span slot="title">{{ item.name }}</span></el-menu-item></el-menu><div class="right-menu"><el-dropdown class="avatar-container" trigger="click"><div class="avatar-wrapper"><img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar"><i class="el-icon-caret-bottom" /></div><el-dropdown-menu slot="dropdown" class="user-dropdown"><router-link to="/"><el-dropdown-item>Home</el-dropdown-item></router-link><a target="_blank" href="https://github.com/PanJiaChen/vue-admin-template/"><el-dropdown-item>Github</el-dropdown-item></a><a target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/#/"><el-dropdown-item>Docs</el-dropdown-item></a><el-dropdown-item divided @click.native="logout"><span style="display:block;">Log Out</span></el-dropdown-item></el-dropdown-menu></el-dropdown></div></div>
</template><script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'export default {components: {Breadcrumb,Hamburger},computed: {...mapGetters(['sidebar','avatar']),toIndex() { // 根据路径绑定到对应的一级菜单,防止页面刷新重新跳回第一个return '/' + this.$route.path.split('/')[1]}},// eslint-disable-next-line vue/order-in-componentsdata() {return {menuList: [ // 水平一级菜单栏的菜单]}},mounted() {// 初始化菜单数据this.initMenuList()},methods: {// 重点二:// 因为整个项目工程比较大,所以当时搭建了一个demo,菜单数据我写在了本地;// 大家在实现的时候可以通过上边第一种方法;// 后台获取回来数据以后通过 router.addRoutes(获取回来的菜单数组)方法;// 动态的挂载到我们的router上。initMenuList() {const menuList = ['/login', '/404']this.menuList = this.$router.options.routes.filter((v, i) => {return v.path !== menuList[i]})},// 重点三:// 根据当前惦记的顶部模块菜单去切换左侧菜单栏,把当前点击的菜单path存在vuex里// 我这边是存在了store/modules/user里边,这个没有要求,小伙伴们随意handleSelect(path) {this.$store.dispatch('user/setPath', path)},toggleSideBar() {this.$store.dispatch('app/toggleSideBar')},async logout() {await this.$store.dispatch('user/logout')this.$router.push(`/login?redirect=${this.$route.fullPath}`)}}
}
</script><style lang="scss" scoped>
.navbar {display: flex;justify-content: space-between;height: 50px;overflow: hidden;position: relative;background: #fff;box-shadow: 0 1px 4px rgba(0,21,41,.08);.hamburger-container {line-height: 46px;height: 100%;float: left;cursor: pointer;transition: background .3s;-webkit-tap-highlight-color:transparent;&:hover {background: rgba(0, 0, 0, .025)}}.breadcrumb-container {float: left;}.right-menu {float: right;height: 100%;line-height: 50px;&: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 .3s;&:hover {background: rgba(0, 0, 0, .025)}}}.avatar-container {margin-right: 30px;.avatar-wrapper {margin-top: 5px;position: relative;.user-avatar {cursor: pointer;width: 40px;height: 40px;border-radius: 10px;}.el-icon-caret-bottom {cursor: pointer;position: absolute;right: -20px;top: 25px;font-size: 12px;}}}}
}.menuItem{height: 47px;}
</style>
// src/store/modules/user
import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { resetRouter } from '@/router'const getDefaultState = () => {return {token: getToken(),name: '',avatar: '',// 定义以下两个状态menuList: [], // 动态路由path: '/' // 当前点击的菜单模块path}
}const state = getDefaultState()const mutations = {RESET_STATE: (state) => {Object.assign(state, getDefaultState())},SET_TOKEN: (state, token) => {state.token = token},SET_NAME: (state, name) => {state.name = name},SET_AVATAR: (state, avatar) => {state.avatar = avatar},// 定义SET_MENULIST方法用来保存我们的动态路由SET_MENULIST: (state, menuList) => {state.menuList = menuList},// 定义SET_MENULIST方法用来保存我当前点击的顶部模块菜单pathSET_PATH: (state, path) => {state.path = path}
}const actions = {// user loginlogin({ commit }, userInfo) {const { username, password } = userInforeturn new Promise((resolve, reject) => {login({ username: username.trim(), password: password }).then(response => {const { data } = responsecommit('SET_TOKEN', data.token)setToken(data.token)resolve()}).catch(error => {reject(error)})})},//定义两个actions 方法用来执行我们上边定义的SET_MENULIST和SET_PATHsetMenuList({ commit }, menuList) {commit('SET_MENULIST', menuList)},setPath({ commit }, path) {commit('SET_PATH', path)},// get user infogetInfo({ commit, state }) {return new Promise((resolve, reject) => {getInfo(state.token).then(response => {const { data } = responseif (!data) {return reject('Verification failed, please Login again.')}const { name, avatar } = datacommit('SET_NAME', name)commit('SET_AVATAR', avatar)resolve(data)}).catch(error => {reject(error)})})},// user logoutlogout({ commit, state }) {return new Promise((resolve, reject) => {logout(state.token).then(() => {removeToken() // must remove  token  firstresetRouter()commit('RESET_STATE')resolve()}).catch(error => {reject(error)})})},// remove tokenresetToken({ commit }) {return new Promise(resolve => {removeToken() // must remove  token  firstcommit('RESET_STATE')resolve()})}
}export default {namespaced: true,state,mutations,actions
}

动态菜单和path都存好了以后我们就可以根据当前点击的path去动态的渲染我们的侧边栏啦

<!--src/layout/components/Sidebar/index.vue-->
<template><div :class="{'has-logo':showLogo}"><logo v-if="showLogo" :collapse="isCollapse" /><el-scrollbar wrap-class="scrollbar-wrapper"><el-menu:default-active="activeMenu":collapse="isCollapse":background-color="variables.menuBg":text-color="variables.menuText":unique-opened="false":active-text-color="variables.menuActiveText":collapse-transition="false"mode="vertical"><sidebar-item v-for="route in menuList" :key="route.path" :item="route" :base-path="route.path" /></el-menu></el-scrollbar></div>
</template><script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'export default {components: { SidebarItem, Logo },computed: {...mapGetters(['sidebar']),routes() {return this.$router.options.routes},activeMenu() {const route = this.$routeconst { meta, path } = route// if set path, the sidebar will highlight the path you setif (meta.activeMenu) {return meta.activeMenu}return path},showLogo() {return this.$store.state.settings.sidebarLogo},variables() {return variables},isCollapse() {return !this.sidebar.opened}},watch: {// 因为每次点击顶部菜单的时候path都会改变,所以我们要对它进行监听;// 通过数组的filter方法去过滤出来我们想要的菜单数组就可以啦。'$store.state.user.path': {handler: function(newVal, oldVal) {console.log('新值' + newVal, '旧值' + oldVal)console.log('vuex里存的菜单', this.$store.state.user.menuList)this.menuList = this.$store.state.user.menuList.filter(v => {return newVal === v.path})}}},mounted() {// 页面渲染时候获取一下vuex里的menuList,因为刚才在vuex里定义的path默认给了'/';// 所以第一次进来的时候默认显示的首页console.log('当前path', this.$store.state.user.path)this.$store.dispatch('user/setMenuList', this.$router.options.routes)this.menuList = this.$store.state.user.menuList.filter(v => {return this.$store.state.user.path === v.path})},// eslint-disable-next-line vue/order-in-componentsdata() {return {menuList: []}}
}
</script>

🌟最后

当我们接到新需求的时候,一定要仔细分析把逻辑梳理清楚了;复杂的话我们可以画一下流程图以便我们更好的去写代码;万变不离其宗,思路最重要。小伙伴们如果有更好的思路,可以一起交流,共同进步。

✨原创不易,还希望各位大佬支持一下!
👍 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/118221.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL学习笔记】(七)内置函数

内置函数 日期函数示例案例-1案例-2 字符串函数示例 数学函数其他函数 日期函数 示例 获得当前年月日 mysql> select current_date(); ---------------- | current_date() | ---------------- | 2023-09-03 | ---------------- 1 row in set (0.00 sec)获得当前时分秒…

ARM 汇编基础知识

1.为什么学习汇编&#xff1f; 我们在进行嵌入式 Linux 开发的时候是绝对要掌握基本的 ARM 汇编&#xff0c;因为 Cortex-A 芯片一 上电 SP 指针还没初始化&#xff0c; C 环境还没准备好&#xff0c;所以肯定不能运行 C 代码&#xff0c;必须先用汇编语言设置好 C 环境…

HarmonyOS应用开发者高级认证练习题

系列文章目录 HarmonyOS应用开发者基础认证练习题 HarmonyOS应用开发者高级认证练习题 文章目录 系列文章目录前言一、判断二、单选三、多选 前言 本文所有内容来源于个人进行HarmonyOS应用开发者系列认证的学习过程中所做过的练习题&#xff0c;所有答案均是个人作答&#x…

手撕 视觉slam14讲 ch7 / pose_estimation_3d2d.cpp (1)

首先理清我们需要实现什么功能&#xff0c;怎么实现&#xff0c;提供一份整体逻辑&#xff1a;包括主函数和功能函数 主函数逻辑&#xff1a; 1. 读图,两张rgb&#xff08;cv::imread&#xff09; 2. 找到两张rgb图中的特征点匹配对 2.1定义所需要的参数&#xff1a;keypoints…

手机怎么剪视频?分享一些剪辑工具和注意事项

视频剪辑是一种将多个视频片段进行剪切、合并和编辑的技术&#xff0c;它可以帮助我们制作出精彩的视频作品。如今&#xff0c;随着智能手机的普及&#xff0c;我们可以随时随地使用手机进行视频剪辑。本文将为大家介绍一些手机剪辑工具和注意事项&#xff0c;帮助大家更好地进…

MATLAB实现AHP层次分析法——以情人节选取礼物为例

问题背景&#xff1a; 情人节来临之际&#xff0c;广大直男&#xff08;女&#xff09;同胞在给异性朋友选购礼物时会遇到难题——什么才是礼物好坏最重要的标准&#xff1f;基于层次分析法AHP进行计算&#xff0c;得出最高权重的指标&#xff0c;给出各位朋友选购礼物的一种思…

Vue框架--Vue中的数据绑定

Vue中有两种数据绑定的方式 1.单向数据绑定(v-band):数据只能够从data流向页面 2.双向数据绑定(v-model):数据不仅仅能够从data流向页面&#xff0c;也可以从页面流向data。 备注: 1.双向绑定一般都应用在表单类元素上。(如:input、select等有value属性值的标签上) 2.…

[ZenTao]源码阅读:自定义任务类型

1、module/custom/control.php 2、module/custom/model.php

C#-单例模式

文章目录 单例模式的概述为什么会有单例模式如何创建单例模式1、首先要保证&#xff0c;该对象 有且仅有一个2、其次&#xff0c;需要让外部能够获取到这个对象 示例通过 属性 获取单例 单例模式的概述 总结来说&#xff1a; 单例 就是只有 一个实例对象。 模式 说的是设计模式…

多线程的五种“打开”方式

1 概念 1.1 线程是什么&#xff1f;&#xff1f; 线程&#xff08;Thread&#xff09;是计算机科学中的一个基本概念&#xff0c;它是进程&#xff08;Process&#xff09;中的一个执行单元&#xff0c;负责执行程序的指令序列。线程是操作系统能够进行调度和执行的最小单位。…

Linux中的基础IO

目录 1、关于C语言中的文件操作符 1.1 C语言中写文件 1.2 C语言读文件 1.3 往显示器上输出信息 1.4 stdin & stdout & stderr 1.5 打开文件的方式 2、系统文件IO 2.1 写操作文件 2.2 读操作文件、 2.3 open open函数的返回值 2.4 文件描述符 0 & 1 &a…

赴日IT 如何提高去日本做程序员的几率?

其实想去日本做IT工作只要满足学历、日语、技术三个必要条件&#xff0c;具备这些条件应聘就好&#xff0c;不具备条件你就想办法具备这些条件&#xff0c;在不具备条件之前不要轻易到日本去&#xff0c;日本IT行业虽然要求技术没有国内那么高&#xff0c;但也不是随便好进入的…

uniapp 微信小程序添加隐私保护指引

隐私弹窗&#xff1a; <uni-popup ref"popup"><view class"popupWrap"><view class"popupTxt">在你使用【最美万年历】之前&#xff0c;请仔细阅读<text class"blueColor" click"handleOpenPrivacyContract…

Spark【RDD编程(二)RDD编程基础】

前言 接上午的那一篇&#xff0c;下午我们学习剩下的RDD编程&#xff0c;RDD操作中的剩下的转换操作和行动操作&#xff0c;最好把剩下的RDD编程都学完。 Spark【RDD编程&#xff08;一&#xff09;RDD编程基础】 RDD 转换操作 6、distinct 对 RDD 集合内部的元素进行去重…

C语言:截断+整型提升+算数转换练习

详情关于整型提升、算数转换与截断见文章&#xff1a; 《C语言&#xff1a;整型提升》 《C语言&#xff1a;算数转换》 一、代码一 int main() { char a -1; signed char b -1; unsigned char c -1; printf("%d %d %d", a, b, c); return 0; } 求…

ToBeWritten之VSOC安全运营

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…

华为OD:敏感字段加密

题目描述&#xff1a; 给定一个由多个命令字组成的命令字符串&#xff1a; 1、字符串长度小于等于127字节,只包含大小写字母,数字,下划线和偶数个双引号&#xff1b; 2、命令字之间以一个或多个下划线_进行分割&#xff1b; 3、可以通过两个双引号”"来标识包含下划线…

【实战】十一、看板页面及任务组页面开发(六) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十八)

文章目录 一、项目起航&#xff1a;项目初始化与配置二、React 与 Hook 应用&#xff1a;实现项目列表三、TS 应用&#xff1a;JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…

pytest笔记: pytest单元测试框架

第一步&#xff1a;安装 和查看版本 pycharm settings 查看 第二步&#xff1a; 编写test_example.py def inc(x):return x1 def test_answer():assert inc(4) 5 第三步&#xff1a;在当前路径下执行pytest 命令 PS E:\data\web测试\Selenium3自动化测试实战——基于Pyth…

应用程序管理工具

应用程序管理是 DevOps 的重要组成部分。它可以定义为在所有阶段监控和管理软件应用程序的可用性、运行状况、性能和功能的过程&#xff0c;包括规划、设计、构建、测试、部署、维护和更新。这意味着应用程序从概念到停止都受到监控。 应用程序管理的重要性 管理应用程序可确…