vue element admin layout(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-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 export default { components: { Breadcrumb, Hamburger }, computed: { ...mapGetters([ sidebar, avatar ]), toIndex() { // 根据路径绑定到对应的一级菜单 ,防止页面刷新重新跳回第一个 return / + this.$route.path.split(/)[1] } }, // eslint-disable-next-line vue/order-in-components data() { 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方法用来保存我当前点击的顶部模块菜单path SET_PATH: (state, path) => { state.path = path } } const actions = { // user login login({ commit }, userInfo) { const { username, password } = userInfo return new Promise((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_PATH setMenuList({ commit }, menuList) { commit(SET_MENULIST, menuList) }, setPath({ commit }, path) { commit(SET_PATH, path) }, // get user info getInfo({ commit, state }) { return new Promise((resolve, reject) => { getInfo(state.token).then(response => { const { data } = response if (!data) { return reject(Verification failed, please Login again.) } const { name, avatar } = data commit(SET_NAME, name) commit(SET_AVATAR, avatar) resolve(data) }).catch(error => { reject(error) }) }) }, // user logout logout({ commit, state }) { return new Promise((resolve, reject) => { logout(state.token).then(() => { removeToken() // must remove token first resetRouter() commit(RESET_STATE) resolve() }).catch(error => { reject(error) }) }) }, // remove token resetToken({ commit }) { return new Promise(resolve => { removeToken() // must remove token first commit(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.$route const { meta, path } = route // if set path, the sidebar will highlight the path you set if (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-components data() { return { menuList: [] } } } </script>🌟最后
当我们接到新需求的时候 ,一定要仔细分析把逻辑梳理清楚了;复杂的话我们可以画一下流程图以便我们更好的去写代码;万变不离其宗 ,思路最重要 。小伙伴们如果有更好的思路 ,可以一起交流 ,共同进步 。
✨原创不易 ,还希望各位大佬支持一下!
👍 点赞 ,你的认可是我创作的动力!
⭐️ 收藏 ,你的青睐是我努力的方向!
✏️ 评论 ,你的意见是我进步的财富!创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!