Vue 路由权限[菜单权限/按钮权限控制]
前言
刚刚完工了做了半年的保险后台管理系统,系统整体业务比较复杂,这也是我在公司从 0 到 1 的 一个完整系统实践,做这个系统过程中踩了不少坑,也学到了很多,趁着空挡不忙,特意做一个总结。
其实我们在做的后台管理系统大多数基础框架都一样,后台管理系统主要的是角色权限管理,按钮权限管理和菜单管理,其它的业务主要围绕在这个基础之上进行扩展,最终 构成了符合业务的后台管理系统。
由于我们公司的项目都是采用 Vue 技术栈,所以文章也是围绕 Vue 如何进行权限管理 进行讲解。
权限授权登录
任何一个后台管理系统都是首先从登录开始,登录后返回用户基本信息,以及token。
token:存入sessionStronge/localStronge中,然后加入到封装好的Axios的请求头中,每次请求携带token。- 用户基本信息
登录成功后同时要做很多事情,具体业务具体对待。后台管理系统登录成功后会请求当前用户的菜单权限接口,来获取用户的可访问的路由(动态路由),获取成功后, Vue Router 是不能直接使用的,必须得解析成符合 Vue Router 可识别的格式。
登录
handleLogin() {
this.$refs.loginForm.validate(valid = >{
if (valid) {
this.loading = true;
login(this.loginForm).then(res = >{
if (res.code === 200) {
// 存放 token
sessionStorage.setItem("tokens", res.data.token);
// 触发 Vuex 来加载获取当前用户的菜单,并解析路由
store.dispatch("setMenuList");
this.$message({
message: "登录成功",
type: "success",
duration: 1000
});
this.$router.replace({
path: "/dashboard"
});
}
}).
catch(() = >{
this.loading = false;
});
} else {
console.log("error submit!!");
return false;
}
});
}
获取当前用户菜单,解析路由
登录成功后,本文通过 Vuex 来获取当前用户菜单和解析路由的。
store.dispatch("setMenuList");
// getMenu 解析后台路由
import { getMenu } from '../../utils/getMenu'
// 引入路由 和 静态路由
import router, { constantRoutes } from '../../router/index'
const state = {
routerType: '',
// 菜单路由
meunList: []
}
const mutations = {
SET_ROUTER_TYPE(state, type) {
state.routerType = type
},
SET_ROUTER_MENULIST(state, list) {
// 静态路由 + 动态路由 合并 完整路由
const array = constantRoutes.concat(list)
state.meunList = array
router.options.routes = array
router.addRoutes([...array])
}
}
const actions = {
setMenuList({ commit, state }) {
// 接收返回来的 路由数组
return new Promise((resolve, reject) => {
getMenu().then(res => {
commit('SET_ROUTER_TYPE', '')
commit('SET_ROUTER_MENULIST', res)
resolve(res)
})
})
}
}
export default {
state,
mutations,
actions
}
解析后端返回来路由(重点)
封装好的解析后端返回来的路由,这块主要是为了在 Vuex 中使用。
import Layout from '@/layout'
import {getUserAuthMenu} from '@/api/user'
/**
* @description: 解析后端返回来的菜单树
* @param {*} data 后端返回来的路由树
* @param {*} arr 菜单
* @return {*}
*/
function tree(data, arr) {
data.forEach((datas, index) => {
arr.push({
path: datas.path,
name: datas.name,
types: datas.types,
hidden: datas.hidden == 'true' ? true : false,
// 当时这块踩坑了
component: datas.component === 'Layout' ? Layout : resolve => require([`@/views/${datas.component}.vue`], resolve),
meta: {
title: datas.meta.title,
icon: datas.meta.icon,
// 用来存放按钮权限
button: datas.meta.button
},
// redirect: datas.redirect,
id: datas.id,
// 子路由
children: []
})
if (datas.children) {
const childArr = tree(datas.children, [])
arr[index].children = childArr
}
})
return arr
}
/**
* @description: 获取当前登录用户的菜单
* @param {*}
* @return {*}
*/
export function getMenu() {
return new Promise(function (resolve, reject) {
getUserAuthMenu().then(res => {
if(res.code === 200){
const datas = res.data
// 调用 tree 来解析后端返回来的树
resolve(tree(datas, []))
}
})
})
}
后端接收路由格式
const data =[
{
name:'组件名',
path:'根路径',
types:'顶部菜单 type',
component: Layout,
hidden:true, // 是否在右侧栏显示
redirect:'重定向',
meta:{
icon:'图标',
title:'菜单名',
button:''
},
children:[
{
name:'组件名',
path:'根路径',
types:'顶部菜单 type',
component: Layout,
hidden: true,
redirect:'重定向',
meta:{
icon:'图标',
title:'菜单名',
button:''
}
},
{
name:'组件名',
path:'根路径',
types:'顶部菜单 type',
component: Layout,
hidden: true,
redirect:'重定向',
meta:{
icon:'图标',
title:'菜单名',
button:''
}
}
]
}
]
前端接收到的真实菜单树
[
{
"id": 1,
"name": "系统管理",
"path": "/sys",
"types": "systemcontrol",
"component": "Layout",
"hidden": true,
"redirect": null,
"icon": "eye",
"title": "系统管理",
"pid": 0,
"meta": {
"icon": "eye",
"title": "系统管理",
"button": []
},
"children": [
{
"id": 4,
"name": "菜单管理",
"path": "/MenuManage",
"types": "systemcontrol",
"component": "MenuManage/index",
"hidden": true,
"redirect": null,
"icon": "eyes",
"title": "菜单管理",
"pid": 1,
"meta": {
"icon": "eyes",
"title": "菜单管理",
//存放按钮权限
"button": [
"getAuthority",
"menu_show",
"menu_upd",
"menu.del",
"menu_bind",
"menu add"
]
},
"children": null,
"operations": null
},
{
"id": 3,
"name": "角色管理",
"path": "/RoleManage",
"types": "systemcontrol",
"component": "RoleManage/index",
"hidden": true,
"redirect": null,
"icon": "eye",
"title": "角色管理",
"pid": 1,
"meta": {
"icon": "eye",
"title": "角色管理",
"button": [
"role_getAll",
"role.add",
"role-get",
"role.upd",
"role.del",
"role bind"
]
},
"children": null,
"operations": null
},
{
"id": 2,
"name": "用户管理",
"path": "/UserManage",
"types": "systemcontrol",
"component": "UserManage/index",
"hidden": true,
"redirect": null,
"icon": "eye",
"title": "用户管理",
"pid": 1,
"meta": {
"icon": "eye",
"title": "用户管理",
"button": [
"user_add",
"user_get",
"user_upd",
"user_get",
"user.del",
"upd_password",
"reset_password"
]
},
"children": null,
"operations": null
}
],
"operations": null
}
]
页面刷新,路由丢失
到此为止,已经实现了 Vue 动态权限控制,别高兴的太早,哈哈,一刷新页面,页面就进入了 404 页面。
这是为什么呢?
因为存入 Vuex 中的数据,一刷新页面,就会清空,那么当然找不到当前路由,就进入 404 页面了.
如何处理呢?
一、可以将静态和动态构成的完整路由存放在sessionStronge/localStronge中,然后页面刷新时,通过在全局入口文件 App.vue 的生命周期created中,将router=sessionStronge/localStronge存入的完整的路由,页面在刷新时,它会重新加载完整的路由。
二、如果是使用 Vuex 来获取和解析用户菜单的话,那么你可以在全局入口文件 App.vue 的生命周期created中,再次执行 Vuex Action 来重新加载用户菜单
我这块直接在 App.vue 的生命周期created中,再次执行了Vuex来进行加载和解析,没有做其它操作。当然了,具体业务具体对待。
<template>
<div id="app">
<router-view v-if="isRouterAlive" />
</div>
</template>
<script>
import store from "@/store";
export default {
name: "App",
provide() {
return {
reload: this.reload
};
},
data() {
return {
isRouterAlive: true
};
},
methods: {
reload() {
this.isRouterAlive = false;
this.$nextTick(() => (this.isRouterAlive = true));
}
},
created() {
//只要刷新页面,就会重新加载路由树,保证了路由不会丢失数据
store.dispatch("setMenuList");
}
};
</script>
总结
核心思想
1.定义符合 当前项目业务路由格式,前后端按这个接收传递。
2.前端解析后端返回的动态路由,生成 Vue Router 可识别格式,最后拼接完整路由。
3.刷新路由丢失处理。
按钮权限控制
1.当前组件 路由 携带可使用的 按钮权限,存入数组中,通过v-if 来判断是否显示。
2.登录时,单独获取整个系统的按钮权限,将获取到的所有按钮 存入一个数组中,放入全局中,然后,通过 v-if 来判断是否显示。
以上关于Vue 路由权限[菜单权限/按钮权限控制]的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » Vue 路由权限[菜单权限/按钮权限控制]
微信
支付宝