一個項目首先要解決的問題,同時也是核心的問題就是登陸和權限的問題,vue-element-admin將路由和左邊側欄進行了綁定,所以我們要解決的就是根據不同的role,生成不同的路由,然後動態渲染不同的側邊欄,同時還要解決在後臺可以通過tree控件動態設置權限的效果,所以,先擼一下整體實現思路:
首先要說一下,vue-element-admin是怎麼與後端進行交互的:用戶端有交互提交---->>調用api下的.js請求函數(有些請求函數我單獨放到service裏了)--->>調用untils下的request.js(封裝好的請求axiso)--->>獲取到後端返回的response--->>進行客戶端data渲染(後面我會細說一下request.js裏封裝的axiso)
1.登陸:當用戶完成登陸後,要實現兩個功能:一是:會向後端進行user_info的獲取,並把相關信息寫入cookie中,以實現整個系統的一個角色綁定。二是進行頁面的跳轉
2.權限:登陸後在permission.js中router.beforeEach方法中利用寫在請求頭裏的cookie向後端獲取menuList,然後利用vuex進行路由的拼接,最後通過router.addRouter掛載所計算好的路由並實現側邊欄的動態渲染
ok,下面擼一下具體的實現操作:
1,login頁面的click事件中寫:
handleLogin() {
this.$refs.loginForm.validate(async valid => { //進行登陸驗證
if (valid) { //驗證通過
this.loading = true
this.logins='登錄中...'
const { username, password } = this.loginForm
try { //像後端獲取user_info並存儲到cookie中
const res = await login({ user_name: username, passwd: password })
setUserInfo(res.data) //user_info存儲到cookie中
this.loading = false
this.logins='登錄'
this.$router.push({ path: '/' }) //跳轉到首頁
} catch (e) {
this.loading = false
this.logins='登錄'
}
} else {
console.log('error submit!!')
return false
}
})
}
其中setUserInfo()方法在utils下的auth.js,其中這裏面可以寫一些cookie的相關函數,並export出去,以供複用
// 設置Role_id
export function setRoleId(roleId) {
return Cookies.set(RoleKey, roleId)
} //這裏只寫一個,其他的set方法都類似於此
// 保存用戶信息
export function setUserInfo(user) {
const { role_id, agent_id, user_name,
agent_name, province_id, level, price, city_name,
city_ip
} = user
setRoleId(role_id)
setAgentId(agent_id)
setUserName(user_name)
setAgentName(agent_name)
setProvinceId(province_id)
setLevel(level)
setPrice(price / 100)
setCityName(city_name)
setCityIp(city_ip)
}
// 清除用戶信息
export function removeUserInfo() {
removeRoleId()
removeAgentId()
removeUserName()
removeAgentName()
removeProvinceId()
removeLevel()
removePrice()
removeCityName()
removeCityIp()
}
2.然後在在permission.js中router.beforeEach,一個是實現渲染側邊欄之前的一個token攔截,二是實現從後端獲取menuList以供拼接動態路由
router.beforeEach(async(to, from, next) => {
NProgress.start() // start progress bar
if (getToken()) { // determine if there has token
if (to.path === '/login') {
next({ path: '/' })
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
} else {
if (!menuList) { //根據之前綁定的角色,通過cookie中的role向後端獲取menulist
const res = await fetchMenuList()
menuList = res.data
store.dispatch('GenerateRoutes', menuList) //利用vuex,在store中的premission.js中進行路由的拼接
}
next()
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) { // 在免登錄白名單,直接進入
next()
} else {
next('/login') // 否則全部重定向到登錄頁
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}
})
3.在store中的premission.js中進行路由的拼接,思路是在後端根據role獲取到id,然後通過獲取到的id與寫在route.index裏的動態路由表進行比對拼接,如果包含獲取到的id的話就將路由表中的hidden設置爲false,否則設爲true來控制側邊欄是否顯示這個menu
const checkedId = [] //設置一個數組,用來儲存權限獲取到的route的id
function getCheckedId(menulist) { //將根據角色從後端獲取到的id進行foreach後存儲到checkedId中
menulist.forEach(item => {
checkedId.push(item.id)
if (item.childs.length) {
getCheckedId(item.childs)
}
})
}
function findIdRoute(asyncRouterMap, id) { //通過比對checkedId和寫在route中的路由表的id來進行hidden參數的true/false編寫
asyncRouterMap.forEach(item => {
if (id === item.id) {
item.hidden = false
}
if (item.children) {
findIdRoute(item.children, id)
}
})
}
function getRoutes(asyncRouterMap) { //調用findidroute函數
checkedId.forEach(id => {
findIdRoute(asyncRouterMap, +id)
})
}
const permission = { //vuex
state: {
routers: constantRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => { //將動態路由與靜態路由進行concat拼接,形成完整路由表
state.addRouters = routers
state.routers = constantRouterMap.concat(routers)
}
},
//路由拼接
actions: {
GenerateRoutes({ commit }, menulist) {
getCheckedId(menulist)
getRoutes(asyncRouterMap)
return new Promise(resolve => {
const accessedRouters = asyncRouterMap
commit('SET_ROUTERS', accessedRouters)
resolve()
})
}
}
ok至此,路由已經根據role拼接完畢,下面介紹下側邊欄如何去渲染出來,其原理就是遍歷之前算出來的permission_routers,然後通過vuex拿到之後動態v-for渲染而已
1:在sliderba的index中書寫如下
<sidebar-item v-for="route in permission_routers" :key="route.name" :item="route" :base-path="route.path"/>
<script>
import { mapGetters } from 'vuex'
import SidebarItem from './SidebarItem'
export default {
components: { SidebarItem },
computed: {
...mapGetters([
'permission_routers',
'sidebar'
]),
isCollapse() {
return !this.sidebar.opened
}
}
}
</script>
2.在sliderba的sidebaritem中書寫如下
hasOneShowingChild(children) {
const showingChildren = children.filter(item => {
if (item.hidden) {
return false
} else {
// temp set(will be used if only has one showing child )
this.onlyOneChild = item
return true
}
})
if (showingChildren.length === 1) {
return true
}
return false
},
resolvePath(...paths) {
return path.resolve(this.basePath, ...paths)
}
下面講一下前端登出:登出需要做兩件事,一是清除token,一是將vuex管理的用戶信息state清空
1.在view層面設置點擊函數
//登出
logout() {
this.$store.dispatch('FedLogOut').then(() => {
location.reload()// In order to re-instantiate the vue-router object to avoid bugs
})
}
2.在store的user.js中配置user相關的state和書寫登出actions
import { getToken, removeToken, removeUserInfo } from '@/utils/auth' //用於獲取token和userinfo
import { logout } from '../../service/common' //用於後端登出請求
const user = {
state: {
token: getToken(),
agent_id: '',
agent_name: '',
role_id: '', // 角色id
role_name: '',
user_name: '',
user_id: ''
},
mutations: {
SET_USERINFO: (state, user) => {
state = {
...state,
...user
}
console.log(state)
},
CLEAR_USERINFO: (state) => {
state.agent_id = ''
state.agent_name = ''
state.role_id = ''
state.role_name = ''
state.user_id = ''
state.user_name = ''
}
},
actions: {
saveUserinfo({ commit, state }, user) {
commit('SET_USERINFO', user)
},
// 後端登出
LogOut({ commit, state }) {
return new Promise((resolve, reject) => {
logout().then(() => {
commit('CLEAR_USERINFO')
removeToken()
resolve()
}).catch(error => {
reject(error)
})
})
},
// 前端 登出
FedLogOut({ commit }) {
return new Promise(resolve => {
commit('CLEAR_USERINFO') //請求mutations
removeToken() //清除token
removeUserInfo() //清除cookes
resolve()
})
}
}
}
export default user
好了,這樣登陸,退出和權限,以及動態路由已經實現,會繼續下更,如tree組件時間動態更改權限,以及其他一些vue-element-admind自帶的組件的使用!