Skip to content

Commit 8f3d862

Browse files
committed
支持动态菜单~
1 parent 384822b commit 8f3d862

File tree

10 files changed

+251
-37
lines changed

10 files changed

+251
-37
lines changed

src/App.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ body {
4545
margin-right: 10px;
4646
}
4747
48+
.svg-icon {
49+
width: 1em;
50+
height: 1em;
51+
vertical-align: -0.15em;
52+
fill: currentColor;
53+
overflow: hidden;
54+
}
55+
4856
.toolbar {
4957
/*background: #f2f2f2;*/
5058
padding: 10px;

src/assets/iconfont/iconfont.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<template>
2+
<svg class="svg-icon" aria-hidden="true">
3+
<use :xlink:href="iconName"></use>
4+
</svg>
5+
</template>
6+
7+
<script>
8+
export default {
9+
name: 'icon-svg',
10+
props: {
11+
iconClass: {
12+
type: String,
13+
required: true
14+
}
15+
},
16+
computed: {
17+
iconName() {
18+
return `#icon-${this.iconClass}`
19+
}
20+
}
21+
}
22+
</script>

src/main.js

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,83 @@ import Vue from 'vue'
33
import App from './App.vue'
44
import ElementUI from 'element-ui'
55
import 'element-ui/lib/theme-default/index.css'
6-
import VueRouter from 'vue-router'
6+
import NProgress from 'nprogress'; // Progress 进度条
7+
import 'nprogress/nprogress.css';// Progress 进度条 样式
8+
import './assets/iconfont/iconfont';
9+
import IconSvg from './components/common/Icon-svg';// svg 组件
710
import store from './store'
8-
import Vuex from 'vuex'
9-
import routes from './routes'
11+
import router from './routes'
1012
import {sync} from 'vuex-router-sync'
1113
import VueVideoPlayer from 'vue-video-player'
1214
import * as filters from './filters'; // 全局vue filter
1315

14-
import Mock from './mock';
15-
Mock.bootstrap();
16-
17-
16+
Vue.component('icon-svg', IconSvg);
1817
Vue.use(ElementUI);
19-
Vue.use(VueRouter);
20-
Vue.use(Vuex);
2118
Vue.use(VueVideoPlayer);
2219

2320
// register global utility filters.
2421
Object.keys(filters).forEach(key => {
2522
Vue.filter(key, filters[key])
2623
});
2724

28-
const router = new VueRouter({
29-
routes
30-
});
25+
// permissiom judge
26+
function hasPermission(roles, permissionRoles) {
27+
if (roles.indexOf('admin') >= 0) return true; // admin权限 直接通过
28+
if (!permissionRoles) return true;
29+
return roles.some(role => permissionRoles.indexOf(role) >= 0)
30+
}
3131

32-
// Some middleware to help us ensure the user is authenticated.
32+
// register global progress.
33+
const whiteList = ['/login'];// 不重定向白名单
3334
router.beforeEach((to, from, next) => {
34-
//NProgress.start();
35-
if (to.path === '/login') {
36-
localStorage.removeItem('account');
37-
localStorage.removeItem('token');
38-
localStorage.removeItem('expiration');
39-
localStorage.removeItem('nickname');
40-
}
41-
let token = localStorage.getItem('token');
42-
if (!token && to.path !== '/login') {
43-
next({path: '/login'})
44-
} else if (token && to.path === '/') {
45-
next({path: '/dashboard'})
46-
}
47-
else {
48-
next();
35+
NProgress.start(); // 开启Progress
36+
if (localStorage.getItem("token")) { // 判断是否有token
37+
if (to.path === '/login') {
38+
localStorage.removeItem('account');
39+
localStorage.removeItem('token');
40+
localStorage.removeItem('expiration');
41+
localStorage.removeItem('nickname');
42+
next({path: '/'});
43+
} else {
44+
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
45+
store.dispatch('GetUserInfo').then(() => { // 拉取user_info
46+
const roles = store.getters.roles;
47+
store.dispatch('GenerateRoutes', {roles}).then(() => { // 生成可访问的路由表
48+
router.addRoutes(store.getters.addRouters); // 动态添加可访问路由表
49+
next({...to}); // hack方法 确保addRoutes已完成
50+
})
51+
}).catch(() => {
52+
store.dispatch('LogOut').then(() => {
53+
next({path: '/login'});
54+
})
55+
})
56+
} else {
57+
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
58+
if (hasPermission(store.getters.roles, to.meta.role)) {
59+
next();//
60+
} else {
61+
next({path: '/401', query: {noGoBack: true}});
62+
}
63+
// 可删 ↑
64+
}
65+
}
66+
} else {
67+
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
68+
next()
69+
} else {
70+
next('/login'); // 否则全部重定向到登录页
71+
NProgress.done(); // 在hash模式下 改变手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 ps:history模式下无问题,可删除该行!
72+
}
4973
}
5074
});
5175

52-
sync(store, router);
76+
router.afterEach(() => {
77+
NProgress.done(); // 结束Progress
78+
});
5379

5480

81+
sync(store, router);
82+
5583
new Vue({
5684
router,
5785
store,

src/routes.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import Vue from 'vue';
2+
import Router from 'vue-router';
13
import Login from './components/page/Login.vue';
24
import NotFoundView from './components/common/error/404.vue';
35
import NotPermission from './components/common/error/401.vue';
@@ -15,8 +17,9 @@ import FrisNumCycleCharts from './components/page/friends/stat/FrisNumCycleChart
1517
import MsgNumCycleCharts from './components/page/msg/stat/MsgNumCycleCharts.vue';
1618
import GeoUserNumCityRanked from './components/page/userDataStat/GeoUserNumCityRanked.vue';
1719

20+
Vue.use(Router);
1821

19-
let routes = [
22+
export const defaultRouterMap = [
2023
{
2124
path: '/login',
2225
component: Login,
@@ -40,16 +43,27 @@ let routes = [
4043
path: '/',
4144
component: Home,
4245
name: '',
43-
iconCls: 'el-icon-star-on',
46+
// iconCls: 'el-icon-star-on',
47+
icon: 'favorfill',
4448
leaf: true,//只有一个节点
4549
children: [
4650
{path: '/dashboard', component: Dashboard, name: '应用概况'}
4751
]
48-
},
52+
}
53+
];
54+
55+
export default new Router({
56+
// mode: 'history', //后端支持可开
57+
scrollBehavior: () => ({y: 0}),
58+
routes: defaultRouterMap
59+
});
60+
61+
export const asyncRouterMap = [
4962
{
5063
path: '/',
5164
component: Home,
5265
name: '数据趋势',
66+
meta: {role: ['admin']},
5367
iconCls: 'el-icon-menu',
5468
children: [
5569
{path: '/cycleCharts', component: CycleCharts, name: '注册用户趋势'},
@@ -63,6 +77,7 @@ let routes = [
6377
path: '/',
6478
component: Home,
6579
name: '用户管理',
80+
meta: {role: ['admin']},
6681
iconCls: 'el-icon-star-off',
6782
children: [
6883
{path: '/userManage', component: UserManage, name: 'Sys用户管理'},
@@ -84,6 +99,7 @@ let routes = [
8499
path: '/',
85100
component: Home,
86101
name: '运营数据',
102+
meta: {role: ['admin']},
87103
iconCls: 'el-icon-date',
88104
children: [
89105
// {path: '/videoTest', component: VideoTest, name: '视频播放测试'},
@@ -104,4 +120,3 @@ let routes = [
104120
}
105121
];
106122

107-
export default routes;

src/store/getters.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
const getters = {
22
token: state => state.user.token,
3+
roles: state => state.user.roles,
34
avatar: state => state.user.avatar,
45
nickname: state => state.user.nickname,
56
uid: state => state.user.account,
67
ossSts: state => state.user.ossSts,
78
ossClient: state => state.user.ossClient,
9+
permission_routers: state => state.permission.routers,
10+
addRouters: state => state.permission.addRouters,
811
};
912
export default getters

src/store/index.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,20 @@ import state from './state'
44
import actions from './actions'
55
import mutations from './mutations'
66
import user from './modules/user';
7+
import permission from './modules/permission';
78
import getters from './getters'
89

9-
Vue.use(Vuex)
10+
Vue.use(Vuex);
1011

11-
export default new Vuex.Store({
12+
const store = new Vuex.Store({
1213
modules: {
1314
user,
15+
permission
1416
},
1517
getters,
1618
state,
1719
actions,
1820
mutations
19-
})
21+
});
22+
23+
export default store

src/store/modules/permission.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import {asyncRouterMap, defaultRouterMap} from '../../routes';
2+
import {deepClone} from '../../utils';
3+
4+
/**
5+
* 通过meta.role判断是否与当前用户权限匹配
6+
* @param roles
7+
* @param route
8+
*/
9+
function hasPermission(roles, route) {
10+
if (route.meta && route.meta.role) {
11+
return roles.some(role => route.meta.role.indexOf(role) >= 0)
12+
} else {
13+
return true
14+
}
15+
}
16+
17+
/**
18+
* 递归过滤异步路由表,返回符合用户角色权限的路由表
19+
* @param asyncRouterMap
20+
* @param roles
21+
*/
22+
function filterAsyncRouter(asyncRouterMap, roles) {
23+
const accessedRouters = asyncRouterMap.filter(route => {
24+
if (hasPermission(roles, route)) {
25+
if (route.children && route.children.length) {
26+
route.children = filterAsyncRouter(route.children, roles)
27+
}
28+
return true
29+
}
30+
return false
31+
});
32+
return accessedRouters
33+
}
34+
35+
const permission = {
36+
state: {
37+
routers: defaultRouterMap,
38+
addRouters: []
39+
},
40+
mutations: {
41+
SET_ROUTERS: (state, routers) => {
42+
state.addRouters = deepClone(routers);
43+
state.routers = deepClone(defaultRouterMap.concat(routers))
44+
}
45+
},
46+
actions: {
47+
GenerateRoutes({commit}, data) {
48+
return new Promise(resolve => {
49+
const {roles} = data;
50+
let accessedRouters;
51+
if (roles.indexOf('admin') >= 0) {
52+
53+
accessedRouters = asyncRouterMap
54+
} else {
55+
accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
56+
}
57+
commit('SET_ROUTERS', accessedRouters);
58+
resolve();
59+
})
60+
}
61+
}
62+
};
63+
64+
export default permission;

0 commit comments

Comments
 (0)