Vue权限管理
# Vue权限管理
# addRouter
router->index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = []
// 动态,添加的路由
const dynamicRouters = [
{
path: '/test',
name: 'test',
component: () => import('../views/Test.vue'),
},
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
// 循环遍历路由添加上去(不过一般不在这里添加)
dynamicRouters.forEach( (item) => {
router.addRoute(item)
})
export default router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
项目中使用一般不是在router->index.js中添加的,而是在登陆之后添加
dynamicRouters.forEach( (item) => {
this.$router.addRoute(item)
})
1
2
3
2
3
# 按钮级别控制
首先,我们这里会把权限直接放在 localStorage 中,如下:
# 定义自定义指令
完全移除没有权限的按钮的 DOM 元素
在使用 removeChild 删除的时候,我们必须加上 setTimeout。
因为,在指令的 bind 钩子函数执行的时候,按钮的 DOM 元素其实并没有渲染出来,所以还操作不了 DOM,我们通过 setTimeout 设置一个宏任务,让 setTimeout 里面的代码再按钮的 DOM 元素渲染出来以后再执行,这个时候就可以操作 DOM 了。
// 自定义指令
export default {
// big什么时候会被调用:
// 1.指令与元素成功绑定时,
// 2.指令所在的模板被重新解析时
bind(element, binding) {
// 获取所有权限
const permissions = JSON.parse(localStorage.getItem('permission'))
//需要的权限(元素身上绑定的权限)
const needPermission = binding.value
const hasPermission = permissions.includes(needPermission)
// 没有权限,就将对应的按钮不进行渲染
if (!hasPermission) {
el.style.display = 'none'
setTimeout(() => {
el.parentNode.removeChild(el)
},0)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 注册指令
import has from '../dorectives/has'
export default {
name: 'Permission',
directives: {
has
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 使用
<template>
<div>
<h1>根据权限进行按钮级别控制</h1>
<el-button v-has="'create'">增加</el-button>
<el-button v-has="'edit'">修改</el-button>
<el-button v-has="'delete'">删除</el-button>
</div>
</template>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
渲染出来也是只有两个按钮
# 补充:
当然也可以存储在vuex中
store->index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
buttonPermission: {
create: true,
edit: false,
delete: true
}
},
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
directives->has.js
export default {
// 指令所在元素被插入页面时执行
inserted(el,binding,vnode) {
// 获取指令身上绑定的权限
let btnPermissionValue = binding.value
// 查询vuex中对应的权限为true还是false
let boolean = vnode.context.$store.state.buttonPermission[btnPermissionValue]
// 没有该权限,就将该元素删除
!boolean && el.parentNode.removeChild(el)
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
其他用法和上述类似
相关知识点:vue自定义指令的用法
# 完整案例
# Mock
模拟后端返回的数据
vip_login.json (用于vip登陆返回的数据)
{
"code": 0,
"message": "登录成功",
"data": {
"token":"vip"
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
admin_login.json (用于管理员登录返回的数据)
{
"code": 0,
"message": "登录成功",
"data": {
"token":"admin"
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
vip_permission.json (vip拥有的权限)
{
"code": 0,
"message": "获取权限成功",
"data": [
{
"name": "订单管理",
"children": [
{
"name": "订单列表"
},
{
"name": "生产管理",
"children": [
{
"name": "生产列表"
}
]
},
{
"name": "退货管理"
}
]
},
{
"name": "产品管理",
"children": [
{
"name": "产品列表"
},
{
"name": "产品分类"
}
]
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
admin_permission.json (管理员拥有的权限)
{
"code": 0,
"message": "获取权限成功",
"data": [
{
"name": "订单管理",
"children": [
{
"name": "订单列表"
},
{
"name": "生产管理",
"children": [
{
"name": "生产列表"
},
{
"name": "审核管理"
}
]
},
{
"name": "退货管理"
}
]
},
{
"name": "产品管理",
"children": [
{
"name": "产品列表"
},
{
"name": "产品分类"
}
]
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
mock->index.js
const express = require("express")
const app = express();
const vipLogin = require("./data/vip_login.json")
const adminLogin = require("./data/admin_login.json")
const adminPermission = require("./data/admin_permission.json")
const vipPermission = require("./data/vip_permission.json")
const url = require("url")
// 正常应该是post请求,这里为了方便,用get
app.get("/login",(req,res) => {
const user = url.parse(req.url,true).query.user
if(user == 'admin') {
res.send(adminLogin)
}else {
res.send(vipLogin)
}
})
// 获取用户权限
app.get('/permission',(req,res) => {
const user = url.parse(req.url,true).query.user
if(user == 'admin') {
res.send(adminPermission)
}else {
res.send(vipPermission)
}
})
app.listen(3300,()=> {
console.log("服务器运行在3300")
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
封装http.js
import axios from 'axios'
// import store from '@/store/index.js'
import baseURL from './baseURL'
import { Message } from 'element-ui'
const http = {}
var instance = axios.create({
timeout: 5000
// baseURL
})
// 添加请求拦截器
instance.interceptors.request.use(
function(config) {
// 请求头添加token
// if (store.state.UserToken) {
// config.headers.Authorization = store.state.UserToken
// }
return config
},
function(error) {
return Promise.reject(error)
}
)
// 响应拦截器即异常处理
instance.interceptors.response.use(
response => {
return response.data
},
err => {
if (err && err.response) {
switch (err.response.status) {
case 400:
err.message = '请求出错'
break
case 401:
Message.warning({
message: '授权失败,请重新登录'
})
store.commit('LOGIN_OUT')
setTimeout(() => {
window.location.reload()
}, 1000)
return
case 403:
err.message = '拒绝访问'
break
case 404:
err.message = '请求错误,未找到该资源'
break
case 500:
err.message = '服务器端出错'
break
}
} else {
err.message = '连接服务器失败'
}
Message.error({
message: err.message
})
return Promise.reject(err.response)
}
)
http.get = function(url, options) {
return new Promise((resolve, reject) => {
instance
.get(url, options)
.then(response => {
if (response.code === 0) {
resolve(response.data)
} else {
Message.error({
message: response.message
})
reject(response.message)
}
})
.catch(e => {
console.log(e)
})
})
}
http.post = function(url, data, options) {
return new Promise((resolve, reject) => {
instance
.post(url, data, options)
.then(response => {
if (response.code === 0) {
resolve(response.data)
} else {
Message.error({
message: response.message
})
reject(response.message)
}
})
.catch(e => {
console.log(e)
})
})
}
export default http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# 路由
router->index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../pages/login'
import Home from '../pages/home'
import NotFound from '../pages/errorPage/404'
import Forbidden from '../pages/errorPage/403'
import Layout from "@/pages/layout";
Vue.use(VueRouter)
// 初始化路由
const routes = [
{
path: '/login',
name: 'Login',
component: Login,
}
]
/**
* 根据用户的权限不同,所能看到的页面和可操作性也不相同
* admin -> 所有页面都可以看到
* vip -> 属于vip的权限
* svip -> 更多vip 的权限
*
* addRouter()
*/
// 准备动态加载的路由
export const DynamicRoutes = [
{
path: "",
component: Layout,
name: 'container',
redirect: "home",
// 自定义路由元信息
meta: {
requiresAuth: true, // 需要登录才能进入
name: "首页"
},
children: [
{
path: "home",
component: Home,
name: "home",
meta: {
// 匹配规则
name: "首页",
icon: "icon-name"
}
}
]
},
{
path:"/403",
component: Forbidden
},
{
// 所有没匹配到的都是404
path: "*",
component: NotFound
}
]
const router = new VueRouter({
routes
})
export default router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
router-> dynamic-router.js (放置的是全部路由信息)
/* 订单管理 */
const Order = () => import('../pages/order-manage')
const OrderList = () => import('../pages/order-manage/order-list')
const ProductManage = () => import('../pages/order-manage/product-manage')
const ProductionList = () => import('../pages/order-manage/product-manage/production-list')
const ReviewManage = () => import('../pages/order-manage/product-manage/review-manage')
const ReturnGoods = () => import('../pages/order-manage/return-goods')
/* 产品管理 */
const Goods = () => import('../pages/goods-manage')
const GoodsList = () => import('../pages/goods-manage/goods-list')
const GoodsClassify = () => import('../pages/goods-manage/goods-classify')
/* 需要权限判断的路由 */
const dynamicRoutes = [
{
path: '/order',
component: Order,
name: 'order-manage',
meta: {
name: '订单管理',
icon: 'icon-email'
},
children: [
{
path: 'list',
name: 'order-list',
component: OrderList,
meta: {
name: '订单列表',
icon: 'icon-quit'
}
},
{
path: 'product',
name: 'product-manage',
component: ProductManage,
meta: {
name: '生产管理',
icon: 'icon-service'
},
children: [
{
path: 'list',
name: 'product-list',
component: ProductionList,
meta: {
name: '生产列表',
icon: 'icon-nav'
}
},
{
path: 'review',
name: 'review-manage',
component: ReviewManage,
meta: {
name: '审核管理',
icon: 'icon-finance-manage'
}
}
]
},
{
path: 'returnGoods',
name: 'return-goods',
component: ReturnGoods,
meta: {
name: '退货管理',
icon: 'icon-product-manage'
}
}
]
},
{
path: '/goods',
component: Goods,
name: 'goods',
meta: {
name: '产品管理',
icon: 'icon-order-manage'
},
children: [
{
path: 'list',
name: 'goods-list',
component: GoodsList,
meta: {
name: '产品列表',
icon: 'icon-home'
}
},
{
path: 'classify',
name: 'goods-classify',
component: GoodsClassify,
meta: {
name: '产品分类',
icon: 'icon-product-manage'
}
}
]
}
]
export default dynamicRoutes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
router -> permission.js (要在main.js中导入)
有些路由地址需要登录才能访问,所以可以在路由元信息上进行标记(meta)
import router from "./index"
import store from "../store/index"
router.beforeEach((to, from, next)=> {
// 没有token,说明没有登陆
if(!store.state.UserToken) {
// 未登录,先判断访问的页面是否需要登陆
if(to.matched.length > 0 && !to.matched.some(record => record.meta.requiresAuth)) { // 页面无需登陆也录也可以访问
next();
} else {
next( {
path:"/login"
})
}
}else {
// 用户已登录,接下来考虑路由的访问权限
if(!store.state.permission.permissionList) {
store.dispatch("permission/FETCH_PERMISSION").then(() => {
path: to.path
})
} else {
// store存在权限
if(to.path !== "/login") {
next()
}else {
next(from.fullPath)
}
}
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# vuex
此处不是完整代码,仅是权限处理相关的
permission.js
import {fetchPermission} from "@/api";
import router, {DynamicRoutes} from "@/router";
import dynamicRouter from "@/router/dynamic-router";
import {recursionRouter, setDefaultRoute} from "@/utils/recursion-router";
export default {
namespaced: true,
state : {
permissionList: null,
sidebarMenu: [], // 导航菜单
currentMenu: '' //高亮
},
getters:{},
mutations: {
SET_PERMISSION(state, routes) {
state.permissionList = routes;
},
CLEAR_PERMISSION(state) {
state.permisssionList = null;
},
SET_MENU(state, menu) {
state.sidebarMenu = menu;
},
CLEAR_MENU(state) {
state.sidebarMenu = []
}
},
// 异步访问
actions: {
async FETCH_PERMISSION({commit, state}) {
// 发送请求获取用户的权限
let permissionList = await fetchPermission();
// console.log(permissionList)
// 筛选
// 对比获取到的路由名称和已有的全部路由名称进行对比,返回用户有的路由
let routes = recursionRouter(permissionList,dynamicRouter)
// 这里是为了获取下图的路由
let MainContainer = DynamicRoutes.find(v => v.path === "")
let children = MainContainer.children
// 将用户本身的路由权限添加进来
children.push(...routes)
// 生成菜单
commit("SET_MENU", children)
// 配置默认路由
setDefaultRoute([MainContainer])
// 初始化路由
let initialRoutes = router.options.routes
// router.addRoutes(DynamicRoutes)
DynamicRoutes.forEach(res => {
router.addRoute(res)
})
commit("SET_PERMISSION",[...initialRoutes, ...DynamicRoutes])
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
utils->recursion-route.js
/***
* 方法一:比对路由权限
* 方法二:指定返回的默认路由
*/
/***
*
* @param userRouter 后台返回的路由权限json
* @param allRouter 前端配置好的路由权限数据
* @returns {*[]} realRoutes 过滤之后的符合条件的路由
*/
export function recursionRouter(userRouter =[], allRouter =[]) {
var realRoutes = []
allRouter.forEach((v,i)=>{
userRouter.forEach((item,index)=>{
if(item.name == v.meta.name) {
if(item.children && item.children.length > 0) {
v.children = recursionRouter(item.children, v.children)
}
realRoutes.push(v)
}
})
})
return realRoutes
}
export function setDefaultRoute(routes) {
routes.forEach((v,i) => {
if(v.children && v.children.length > 0){
v.redirect = {name: v.children[0].name}
setDefaultRoute(v.children)
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
编辑 (opens new window)
上次更新: 2024/12/19, 09:15:30