目录
🌟前言
因为最近在整合公司的项目,需要把所有系统里的功能集成到一个项目里,这样就导致菜单栏目录会特别的多,不便于用户使用,体验效果极差。于是想到了一个方法,就是增加顶部导航栏,点击的时候让侧边菜单栏在显示相对应模块的所有菜单;这样的话就可以很大程度提升我们的用户体验啦。
🌟小伙伴们先看
🌟实现思路
嗯,干活前一定要先把思路理清楚,记在小本本上,画个图都行哈哈
- 布局方面我需要在Navbar组件内添加一个导航组件以便我们去渲染顶部模块菜单;
- 因为是动态路由所以我们可以: - 登录的时候让后端返回所有的当前用户下所有的菜单权限;- 登录时候只返回默认显示的菜单,每次点击的时候再去获取相应的模块菜单权限。
我这边用的是第一种方式,登陆的时候获取全部的存在vuex里,每次点击的时候再去处理相应的数据;小伙伴们也可以尝试一下第二种方式哦。
🌟具体代码
话不多说,直接开整。。。
<!--src/layout/components/Navbar.vue--><template><div class="navbar"><hamburger :is-active="sidebar.opened"class="hamburger-container" @toggleClick="toggleSideBar"/><!--重点一:顶部menu--><el-menu
mode="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'exportdefault{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')},asynclogout(){awaitthis.$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/userimport{ login, logout, getInfo }from'@/api/user'import{ getToken, setToken, removeToken }from'@/utils/auth'import{ resetRouter }from'@/router'constgetDefaultState=()=>{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 }= userInfo
returnnewPromise((resolve, reject)=>{login({username: username.trim(),password: password }).then(response=>{const{ data }= response
commit('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 }){returnnewPromise((resolve, reject)=>{getInfo(state.token).then(response=>{const{ data }= response
if(!data){returnreject('Verification failed, please Login again.')}const{ name, avatar }= data
commit('SET_NAME', name)commit('SET_AVATAR', avatar)resolve(data)}).catch(error=>{reject(error)})})},// user logoutlogout({ commit, state }){returnnewPromise((resolve, reject)=>{logout(state.token).then(()=>{removeToken()// must remove token firstresetRouter()commit('RESET_STATE')resolve()}).catch(error=>{reject(error)})})},// remove tokenresetToken({ commit }){returnnewPromise(resolve=>{removeToken()// must remove token firstcommit('RESET_STATE')resolve()})}}exportdefault{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'exportdefault{components:{ SidebarItem, Logo },computed:{...mapGetters(['sidebar']),routes(){returnthis.$router.options.routes
},activeMenu(){const route =this.$route
const{ meta, path }= route
// if set path, the sidebar will highlight the path you setif(meta.activeMenu){return meta.activeMenu
}return path
},showLogo(){returnthis.$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=>{returnthis.$store.state.user.path === v.path
})},// eslint-disable-next-line vue/order-in-componentsdata(){return{menuList:[]}}}</script>
🌟最后
当我们接到新需求的时候,一定要仔细分析把逻辑梳理清楚了;复杂的话我们可以画一下流程图以便我们更好的去写代码;万变不离其宗,思路最重要。小伙伴们如果有更好的思路,可以一起交流,共同进步。
✨原创不易,还希望各位大佬支持一下!
👍 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!
版权归原作者 忆凡_ 所有, 如有侵权,请联系我们删除。