一个项目首先要解决的问题,同时也是核心的问题就是登陆和权限的问题,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自带的组件的使用!