??前言
因?yàn)樽罱谡瞎镜捻?xiàng)目,需要把所有系統(tǒng)里的功能集成到一個項(xiàng)目里,這樣就導(dǎo)致菜單欄目錄會特別的多,不便于用戶使用,體驗(yàn)效果極差。于是想到了一個方法,就是增加頂部導(dǎo)航欄,點(diǎn)擊的時(shí)候讓側(cè)邊菜單欄在顯示相對應(yīng)模塊的所有菜單;這樣的話就可以很大程度提升我們的用戶體驗(yàn)啦。
??小伙伴們先看
??實(shí)現(xiàn)思路
嗯,干活前一定要先把思路理清楚,記在小本本上,畫個圖都行哈哈
- 布局方面我需要在Navbar組件內(nèi)添加一個導(dǎo)航組件以便我們?nèi)ヤ秩卷敳磕K菜單;
- 因?yàn)槭莿討B(tài)路由所以我們可以:
- 登錄的時(shí)候讓后端返回所有的當(dāng)前用戶下所有的菜單權(quán)限;
- 登錄時(shí)候只返回默認(rèn)顯示的菜單,每次點(diǎn)擊的時(shí)候再去獲取相應(yīng)的模塊菜單權(quán)限。
我這邊用的是第一種方式,登陸的時(shí)候獲取全部的存在vuex里,每次點(diǎn)擊的時(shí)候再去處理相應(yīng)的數(shù)據(jù);小伙伴們也可以嘗試一下第二種方式哦。
??具體代碼
話不多說,直接開整。。。
<!--src/layout/components/Navbar.vue-->
<template>
<div class="navbar">
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<!--重點(diǎn)一:頂部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() { // 根據(jù)路徑綁定到對應(yīng)的一級菜單,防止頁面刷新重新跳回第一個
return '/' + this.$route.path.split('/')[1]
}
},
// eslint-disable-next-line vue/order-in-components
data() {
return {
menuList: [ // 水平一級菜單欄的菜單
]
}
},
mounted() {
// 初始化菜單數(shù)據(jù)
this.initMenuList()
},
methods: {
// 重點(diǎn)二:
// 因?yàn)檎麄€項(xiàng)目工程比較大,所以當(dāng)時(shí)搭建了一個demo,菜單數(shù)據(jù)我寫在了本地;
// 大家在實(shí)現(xiàn)的時(shí)候可以通過上邊第一種方法;
// 后臺獲取回來數(shù)據(jù)以后通過 router.addRoutes(獲取回來的菜單數(shù)組)方法;
// 動態(tài)的掛載到我們的router上。
initMenuList() {
const menuList = ['/login', '/404']
this.menuList = this.$router.options.routes.filter((v, i) => {
return v.path !== menuList[i]
})
},
// 重點(diǎn)三:
// 根據(jù)當(dāng)前惦記的頂部模塊菜單去切換左側(cè)菜單欄,把當(dāng)前點(diǎn)擊的菜單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: '',
// 定義以下兩個狀態(tài)
menuList: [], // 動態(tài)路由
path: '/' // 當(dāng)前點(diǎn)擊的菜單模塊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方法用來保存我們的動態(tài)路由
SET_MENULIST: (state, menuList) => {
state.menuList = menuList
},
// 定義SET_MENULIST方法用來保存我當(dāng)前點(diǎn)擊的頂部模塊菜單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 方法用來執(zhí)行我們上邊定義的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
}
動態(tài)菜單和path都存好了以后我們就可以根據(jù)當(dāng)前點(diǎn)擊的path去動態(tài)的渲染我們的側(cè)邊欄啦
<!--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: {
// 因?yàn)槊看吸c(diǎn)擊頂部菜單的時(shí)候path都會改變,所以我們要對它進(jìn)行監(jiān)聽;
// 通過數(shù)組的filter方法去過濾出來我們想要的菜單數(shù)組就可以啦。
'$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() {
// 頁面渲染時(shí)候獲取一下vuex里的menuList,因?yàn)閯偛旁趘uex里定義的path默認(rèn)給了'/';
// 所以第一次進(jìn)來的時(shí)候默認(rèn)顯示的首頁
console.log('當(dāng)前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>
??最后
當(dāng)我們接到新需求的時(shí)候,一定要仔細(xì)分析把邏輯梳理清楚了;復(fù)雜的話我們可以畫一下流程圖以便我們更好的去寫代碼;萬變不離其宗,思路最重要。小伙伴們?nèi)绻懈玫乃悸罚梢砸黄鸾涣?,共同進(jìn)步。文章來源:http://www.zghlxwxcb.cn/news/detail-794763.html
?原創(chuàng)不易,還希望各位大佬支持一下!
?? 點(diǎn)贊,你的認(rèn)可是我創(chuàng)作的動力!
?? 收藏,你的青睞是我努力的方向!
?? 評論,你的意見是我進(jìn)步的財(cái)富!文章來源地址http://www.zghlxwxcb.cn/news/detail-794763.html
到了這里,關(guān)于Vue實(shí)戰(zhàn)【調(diào)整Vue-element-admin中的菜單欄,并添加頂部模塊菜單欄】的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!