vue-Router+Vuex实战:多角色页面权限控制+面包屑

  1. 介绍多角色的页面权限控制方法
  2. 面包屑的封装和使用

Vuex

Vuex入口

@/store/index.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Vue from 'vue';
import Vuex from 'vuex';
import app from './modules/app'; // 全局的store
import user from './modules/user'; // 用户信息
import permission from './modules/permission'; // 权限

import getters from './getters';

import { VUEX_DEFAULT_CONFIG } from '@/config'; // vuex 默认配置

Vue.use(Vuex);

export default new Vuex.Store({
...VUEX_DEFAULT_CONFIG,
modules: {
app,
user,
permission
},
getters
});

全局的store

@/store/modules/app.js:

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
import { getHomeRoute, getBreadCrumbList } from '@/utils';	// 获取主页、面包屑
import { HOME_NAME } from '../../config'; // 主页name,默认dashboard

const app = {
state: {
globalMenuList: [], // 全局菜单
breadCrumbList: [], // 面包屑
homeRoute: {} //主页,这里默认是dashboard
},
mutations: {
SET_GLOBAL_MENU: (state, menuList) => {
state.globalMenuList = menuList;
},
// 设置面包屑
SET_BREAD_CRUMB(state, route) {
state.breadCrumbList = getBreadCrumbList(route, state.homeRoute);
},
// 设置主页路由
SET_HOME_ROUTE(state, routes) {
state.homeRoute = getHomeRoute(routes, HOME_NAME);
}
}
};

export default app;

权限

@/store/modules/permission.js:

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
import { asyncRouterMap, constantRouterMap } from '@/router/modules/smart-assistant-jgpt';	// 引入所有的路由

/**
* @param urlList // 当前用户具备的路由表
* @param route // 待检查的route对象
*/
function hasPermission(urlList, route) {
if (route.meta.noControl) return true; //该页面不需要权限控制
return urlList.includes(route.meta.fullPath);
}

/**
* 递归过滤异步路由表,返回符合用户角色权限的路由表
* @param routes asyncRouterMap 所有有待权限认证的路由配置
* @param routeList 当前用户具备的路由表
*/
function filterAsyncRouter(routes, urlList) {
const res = [];

routes.forEach(route => {
const tmp = { ...route };
if (hasPermission(urlList, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRouter(tmp.children, urlList);
}
res.push(tmp);
}
});

return res;
}

const permission = {
state: {
routers: constantRouterMap,
addRouters: [],
addUrlList: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers;
state.routers = constantRouterMap.concat(routers);
},
SET_ADD_URLLIST: (state, urlList) => {
state.addUrlList = urlList;
}
},
actions: {
GenerateRoutes({ state, commit }, routeList) {
return new Promise(resolve => {
let urlList = [];
routeList.forEach(item => {
urlList.push(item.resourceUrl);
});
let accessedRouters = filterAsyncRouter(asyncRouterMap, urlList);
// 动态的控制左侧菜单的 显示隐藏
commit('SET_ROUTERS', accessedRouters);
commit('SET_ADD_URLLIST', urlList);
resolve();
});
}
}
};

export default permission;

用户信息

@/store/modules/user.js:

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import api from '@/plugins/api';
import { getToken, setToken, removeToken } from '@/utils/auth'; // token操作
import { treeToList, isEmpty } from '@/utils'; // 工具函数
import router from '@/router';
import bus from '@/plugins/eventbus';

import iView from 'iview';

let errorCount = 0; // 登录超过3次输入验证码
const user = {
state: {
user: '',
status: '',
code: '',
token: getToken(),
userName: '',
avatar: '',
introduction: '',
roles: [], // role对象具备该用户下面的menuList
currentRole: ''
},

// mutation默认是全局的
mutations: {
SET_CODE: (state, code) => {
state.code = code;
},
SET_TOKEN: (state, token) => {
state.token = token;
},
SET_INTRODUCTION: (state, introduction) => {
state.introduction = introduction;
},
SET_STATUS: (state, status) => {
state.status = status;
},
SET_USER_NAME: (state, name) => {
state.userName = name;
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar;
},
SET_ROLES: (state, roles) => {
state.roles = roles;
},
SET_CURRENT_ROLE: (state, role) => {
state.currentRole = role; // 设置当前系统使用角色
sessionStorage.setItem('currentRoleId', role.id); // 角色的id 保存在当前会话
}
},

// actions中可以使用 async函数
// actions使用 大驼峰命名
actions: {
// 用户名登录
LoginByUsername({ commit }, userInfo) {
return new Promise((resolve, reject) => {
api['login/login']({ ...userInfo })
.then(data => {
commit('SET_TOKEN', data);
commit('SET_USER_NAME', userInfo.username); //保存登录的 [用户名]
setToken(data); // Cookie本地缓存token
errorCount = 0; // 登录成功清除错误次数
resolve();
})
.catch(err => {
errorCount++;
if (errorCount >= 3) {
bus.$emit('login-error-handler'); // 展示验证码
}
// 进行错误处理
reject(err);
});
});
},
// 获取用户信息 角色及对应的路由表 在路由的钩子中调用
async GetUserInfo({ commit, state, dispatch }) {
await api['user/getRolePermission']().then(data => {
if (isEmpty(data)) {
throw new Error('getRolePermission: getRolePermission must be a non-null array !');
} else {
// 注意这里的角色data 是数组 用户对应多个角色
commit('SET_ROLES', data);
let roleId = sessionStorage.getItem('currentRoleId');
if (roleId) {
let roleObj = data.find(item => {
return item.id == roleId;
});
if (roleObj) {
commit('SET_CURRENT_ROLE', roleObj);
} else {
throw new Error('用户的同名的角色id值前后不一致');
}
} else {
// 默认使用第一个权限 作为默认权限
commit('SET_CURRENT_ROLE', data[0]);
}
}
});
// 获取全局菜单
let allRouteList = await api['user/globalMenuResource']().then(data => {
if (isEmpty(data)) {
throw new Error('globalMenuResource: globalMenuResource must be a non-null array !');
} else {
return data;
}
});
commit('SET_GLOBAL_MENU', allRouteList); //设置全局菜单
return allRouteList;
},
// 后端 登出
async LogOut({ commit }) {
try {
await api['login/logout']();
commit('SET_TOKEN', '');
commit('SET_ROLES', []);
removeToken();
sessionStorage.removeItem('currentRoleId');
router.push({ name: 'login' });
} catch (e) {
iView.Message.error('Vuex-LogOut:退出服务接口报错');
}
},
// 前端 登出
FedLogOut({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '');
removeToken();
sessionStorage.removeItem('currentRoleId');
resolve();
router.push({ name: 'login' });
});
},
//动态的改变 用户权限 修改左侧的菜单
ChangeRoles({ state, commit, dispatch }, role) {
let routeList;
if (role) {
commit('SET_CURRENT_ROLE', role);
routeList = role.resourceVOS; //该角色能够访问的菜单
} else {
routeList = state.currentRole.resourceVOS;
}
let urlList = treeToList(routeList);
dispatch('GenerateRoutes', urlList);
}
}
};

export default user;

getRolePermission接口给的返回数据格式:

getters

@/store/getters.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const getters = {
globalMenuList: state => state.app.globalMenuList, // 全局的菜单列表
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.userName,
introduction: state => state.user.introduction,
status: state => state.user.status,
roles: state => state.user.roles, // 当前用户具备的权限角色
currentRole: state => state.user.currentRole, // 当使用的用户角色
permission_routers: state => state.permission.routers, // 完成的路由表
addRouters: state => state.permission.addRouters, // 动态添加的路由权限 const+动态获取
addUrlList: state => state.permission.addUrlList, // 动态获取
breadCrumbList: state => state.app.breadCrumbList //面包屑
};
export default getters;

router

入口

@/router/idnex.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Vue from 'vue';
import Router from 'vue-router';

import { constantRouterMap } from './modules/smart-assistant-jgpt'; // 不需要验证用户的权限
import { ROUTER_DEFAULT_CONFIG } from '@/config'; // Router 默认配置
import { routerBeforeEachFunc, routerAfterEachFunc } from '@/config/interceptor/router'; // 路由拦截器

Vue.use(Router);

let routerInstance = new Router({
...ROUTER_DEFAULT_CONFIG,
routes: constantRouterMap
});

// 注入拦截器
routerInstance.beforeEach(routerBeforeEachFunc);
routerInstance.afterEach(routerAfterEachFunc);

export default routerInstance;

模块

@/router/modules/smart-assistant-jgpt/idnex.js:

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
import Main from '@/views/smart-assistant-jgpt/main';	// 进入平台后的默认首页
import DefaultPage from '@/views/smart-assistant-jgpt/default';
import Dashboard from '@/views/dashboard'; // 主页
import Login from '@/views/login';
import NoPermissionPage from '@/views/error-page/401';
import schoolCount from './school-count';

// 这些模块 不需要验证用户的权限
export const constantRouterMap = [
{
path: '/',
name: 'dashboard',
meta: {
title: '主页',
noControl: true,
headerConfig: { isStartFlag: true },
hideInMenu: true,
icon: 'ios-home',
by: 'author'
},
component: Dashboard
},
{
path: '/login',
name: 'login',
meta: {
title: '登录',
hideInMenu: true,
noControl: true,
by: 'author'
},
component: Login
},
{
path: '/401',
name: '401',
meta: {
title: '未授权',
noControl: true,
by: 'author',
hideInMenu: true
},
component: NoPermissionPage
}
];

/**
* meta name
* meta title 供左侧菜单展示
* meta hideInMenu 不在左侧菜单展示
* meta showAlways 就算只有1个子 也展示成下拉列表框
* meta noControl 不需要 验证角色的权限 直接保存到路由中
* meta icon 设置图标的名称
* meta headerConfig 供自定义的header组件使用
*/
//需要进行 权限验证的模块
export const asyncRouterMap = [
{
// 进入平台后的默认首页
path: '/jgpt',
name: 'jgpt',
meta: {
title: '监管平台',
noControl: true,
hideInMenu: true,
by: 'author'
},
redirect: '/jgpt/index',
component: Main,
children: [
{
path: '/jgpt/index',
name: 'index',
meta: {
title: '监管平台',
noControl: true,
hideInMenu: true,
by: 'author'
},
component: DefaultPage
}
]
},
schoolCount // 业务模块
];

列举其中一个模块写法:

@/router/modules/smart-assistant-jgpt/school-count.js:

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
import Main from '@/views/smart-assistant-jgpt/main';

/**
* /jgpt/schoolCount 学校统计
* -- /jgpt/schoolCount/schoolManage 学校管理
* -- /jgpt/schoolCount/schoolContact 学校联系人
*/

export default {
// 学校统计信息
path: '/jgpt/schoolCount',
name: 'schoolCount',
meta: {
fullPath: '/jgpt/schoolCount',
title: '学校统计',
icon: 'ios-paper',
showAlways: true
},
component: Main,
children: [
{
path: '/jgpt/schoolCount/schoolManage',
name: 'schoolManage',
meta: {
title: '学校管理',
fullPath: '/jgpt/schoolCount/schoolManage',
by: 'author'
},
component: resovle => require(['@/views/smart-assistant-jgpt/school-count/school-manage'], resovle)
},
{
path: '/jgpt/schoolCount/schoolManager/schoolDetail',
name: 'schoolDetail',
meta: {
title: '学校详情',
fullPath: '/jgpt/schoolCount/schoolManager/schoolDetail',
noControl: true,
hideInMenu: true,
by: 'author'
},
component: resovle =>
require(['@/views/smart-assistant-jgpt/school-count/school-manage/subpage/school-detail'], resovle)
},
{
path: '/jgpt/schoolCount/schoolContact',
name: 'schoolContact',
meta: {
title: '学校联系人',
fullPath: '/jgpt/schoolCount/schoolContact',
by: 'author'
},
component: resovle => require(['@/views/smart-assistant-jgpt/school-count/school-contact'], resovle)
},
{
path: '/jgpt/schoolCount/schoolContact/contactDetail',
name: 'contactDetail',
meta: {
title: '联系人详情',
fullPath: '/jgpt/schoolCount/schoolContact/contactDetail',
noControl: true,
hideInMenu: true,
by: 'author'
},
component: resovle =>
require(['@/views/smart-assistant-jgpt/school-count/school-contact/subpage/contact-detail'], resovle)
}
]
};

路由拦截器

@/config/interceptor/router.js:

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
import router from '@/router';
import store from '@/store';
import { getToken } from '@/utils/auth'; // getToken from cookie
import { treeToList } from '../../utils';
import iView from 'iview';

//设置加载条的高度
iView.LoadingBar.config({
height: 3
});

function hasPermission(urlList, to) {
if (to.meta.noControl) return true;
return urlList.includes(to.path || to.meta.fullPath);
}

const whiteList = ['/login']; // no redirect whitelist

export function routerBeforeEachFunc(to, from, next) {
iView.LoadingBar.start();
if (to.meta.title) {
document.title = to.meta.title;
}
window.currentRoute = to; //保存当前的路由,功能按钮权限访问
if (getToken()) {
// determine if there has token是否登录 使用本地缓存Cookie
/* has token 已经登录了访问login 直接跳转到 dashboard*/
if (to.path === '/login') {
next({ path: '/' });
iView.LoadingBar.finish(); //hack 技巧 if current page is dashboard will not trigger afterEach hook, so manually handle it
} else {
// 每次刷新 都重新的获取权限
if (store.getters.roles.length === 0) {
// 判断当前用户是否已拉取完user_info信息
store
.dispatch('GetUserInfo')
.then(res => {
// 获取全局的菜单
const routeTreeData = res;
let routeList = treeToList(routeTreeData);
// 对比代码配置的路由对象 与服务器点返回的路由 生成路由表
store.dispatch('GenerateRoutes', routeList).then(() => {
// 根据roles权限生成可访问的路由表
router.addRoutes(store.getters.addRouters); // 动态添加可访问路由表
// 以由当前用角色 改变[左侧渲染菜单]
store.dispatch('ChangeRoles');
// 下次再进入路由时候 直接走else进入到动态添加的路由中
next({ ...to, replace: true }); // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
});
})
.catch(err => {
store.dispatch('FedLogOut').then(() => {
iView.Message.error('获取角色信息失败,请重新登录');
next({ path: '/' });
});
});
} else {
// 没有动态的改变权限则直接进入
// next();
// 改变角色会改变vuex保存的路由,所以这里要判断
if (hasPermission(store.getters.addUrlList, to)) {
next();
} else {
// 进入未授权的页面
next({ path: '/401', replace: true, query: { noGoBack: true } });
}
}
}
} else {
/* has no token*/
// 白名单里面初始化
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next();
} else {
next(`/login?redirect=${to.path}`); // 否则全部重定向到登录页
iView.LoadingBar.finish(); // if current page is login will not trigger afterEach hook, so manually handle it
}
}
}

export function routerAfterEachFunc(to, from) {
iView.LoadingBar.finish();
}

Vuex和router配置

默认配置

@/config/idnex.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 面包屑的 主页是dashboard
export const HOME_NAME = 'dashboard';

// Router 默认配置
export const ROUTER_DEFAULT_CONFIG = {
mode: 'history',
//base: process.env.NODE_ENV !== 'production' ? '/' : '/etsapp/'
base: '/'
};

// vuex 默认配置 state 只能在mutation中改变
export const VUEX_DEFAULT_CONFIG = {
strict: process.env.NODE_ENV !== 'production'
// strict: false
};

注册

@/idnex.js:

1
2
3
4
5
6
7
8
9
10
11
12
import Vue from 'vue';
import App from './App';

import router from '@/router';
import store from '@/store';

window.app = new Vue({
el: '#app',
store,
router,
render: h => h(App)
});

面包屑使用方法

mixins

@/views/mixins/bread-crumb.js:

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
import { constantRouterMap } from '@/router/modules/smart-assistant-jgpt';	// 不需要验证用户权限的路由
import { mapMutations, mapGetters } from 'vuex';

/**
*
* 面包屑 子组件override watch $route 逻辑时拷贝已有的逻辑
*/
const mixin = {
mounted() {
this.setHomeRoute(constantRouterMap); // 设置主页
this.setBreadCrumb(this.$route); // 设置面包屑的内容
},
methods: {
...mapMutations({
setHomeRoute: 'SET_HOME_ROUTE',
setBreadCrumb: 'SET_BREAD_CRUMB'
})
},
computed: {
...mapGetters(['breadCrumbList'])
},
// 子组件可以 override这个方法
watch: {
$route(newRoute) {
this.setBreadCrumb(newRoute);
this.$refs.sideMenu && this.$refs.sideMenu.updateOpenName(newRoute.name);
}
}
};

export default mixin;

@/views/mixins/index.js:

1
2
import BreadCrumb from './bread-crumb';
export { BreadCrumb };

组件

@/components/custom-bread-crumb/custom-bread-crumb.vue:

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
<template>
<div class="custom-bread-crumb">
<Breadcrumb :style="{fontSize: `${fontSize}px`}">
<BreadcrumbItem v-for="item in list" :to="item.to" :key="`bread-crumb-${item.name}`">
<common-icon style="margin-right: 4px;" :type="item.icon || ''" :size="20"/>
{{ showTitle(item) }}
</BreadcrumbItem>

<template v-if="breadCrumbTitle instanceof Array && breadCrumbTitle.length>0">
<BreadcrumbItem v-for="item in breadCrumbTitle" :key="`bread-crumb-${item.name}`">
{{item.name}}
</BreadcrumbItem>
</template>

</Breadcrumb>
</div>
</template>
<script>
import { showTitle } from '@/utils';
import CommonIcon from '@/components/common-icon';
import './custom-bread-crumb.less';
export default {
name: 'customBreadCrumb',
components: {
CommonIcon
},
props: {
list: {
type: Array,
default: () => []
},
fontSize: {
type: Number,
default: 14
},
showIcon: {
type: Boolean,
default: false
},
breadCrumbTitle: {
//自定义面包屑
type: Array,
default: function() {
return [];
}
}
},
methods: {
showTitle(item) {
return showTitle(item, this);
}
}
};
</script>

@/components/custom-bread-crumb/custom-bread-crumb.less:

1
2
3
4
.custom-bread-crumb{
display: inline-block;
vertical-align: top;
}

@/components/custom-bread-crumb/index.js:

1
2
import customBreadCrumb from './custom-bread-crumb.vue';
export default customBreadCrumb;

@/components/common-icon/common-icon.vue:

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
<template>
<component :is="iconType" :type="iconName" :color="iconColor" :size="iconSize"/>
</template>

<script>
import Icons from '../icons';
export default {
name: 'CommonIcon',
components: { Icons },
props: {
type: {
type: String,
required: true
},
color: String,
size: Number
},
computed: {
iconType() {
return this.type.indexOf('_') === 0 ? 'Icons' : 'Icon';
},
iconName() {
return this.iconType === 'Icons' ? this.getCustomIconName(this.type) : this.type;
},
iconSize() {
return this.size || (this.iconType === 'Icons' ? 12 : undefined);
},
iconColor() {
return this.color || '';
}
},
methods: {
getCustomIconName(iconName) {
return iconName.slice(1);
}
}
};
</script>

@/components/common-icon/index.js:

1
2
import CommonIcon from './common-icon.vue';
export default CommonIcon;

无扩展项目时

1
2
3
<div class="navigation">
<CustomBreadCrumb :list="breadCrumbList"></CustomBreadCrumb>
</div>
1
2
3
4
5
6
7
8
import { BreadCrumb } from '@/views/mixins';
import CustomBreadCrumb from '@/components/custom-bread-crumb';

mixins: [ BreadCrumb]

components: {
CustomBreadCrumb
}

有扩展项目时

1
<CustomBreadCrumb :list="breadCrumbList" :breadCrumbTitle="breadCrumbTitle"></CustomBreadCrumb>
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
import { BreadCrumb } from '@/views/mixins';
import CustomBreadCrumb from '@/components/custom-bread-crumb';

mixins: [ BreadCrumb]

components: {
CustomBreadCrumb
}

data() {
return {
breadCrumbTitle: [], // 面包屑扩展项目
breadTile: {
LOOK: [
{
id: 1,
name: '查看'
}
],
EDIT: [
{
id: 2,
name: '修改'
}
]
},
}
}

watch: {
$route: {
handler: function(value) {
this.breadCrumbTitle = []; // 清空面包屑扩展项目
if ('schoolDetail' === value.name) {
const { detailState } = value.query;
this.breadCrumbTitle = this.breadTile[detailState];
this.breadCrumbTitle = this.breadTile[detailState];
}
},
immediate: true
},
}

工具函数

@/utils/index.js:

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
/**
* @param {Array} routers 路由列表数组
* @description 用于找到路由列表中name为home的对象
*/
export const getHomeRoute = (routers, homeName = 'dashboard') => {
let i = -1;
let len = routers.length;
let homeRoute = {};
while (++i < len) {
let item = routers[i];
if (item.children && item.children.length) {
let res = getHomeRoute(item.children, homeName);
if (res.name) return res;
} else {
if (item.name === homeName) homeRoute = item;
}
}
return homeRoute;
};

/**
* @param {Array} routeMetched 当前路由metched
* @returns {Array}
*/
export const getBreadCrumbList = (route, homeRoute) => {
let homeItem = { ...homeRoute, icon: homeRoute.meta.icon };
let routeMetched = route.matched;
if (routeMetched.some(item => item.name === homeRoute.name)) return [homeItem];
let res = routeMetched
.filter(item => {
return item.meta === undefined || !item.meta.hideInBread; // 过滤掉面包屑隐藏的
})
.map(item => {
let meta = { ...item.meta };
if (meta.title && typeof meta.title === 'function') {
meta.__titleIsFunction__ = true;
meta.title = meta.title(route);
}
let obj = {
icon: (item.meta && item.meta.icon) || '',
name: item.name,
meta: meta
};
return obj;
});
//hide in menu的不过滤
// res = res.filter(item => {
// return !item.meta.hideInMenu;
// });
// home 可以跳转
return [{ ...homeItem, to: homeRoute.path }, ...res];
};

export function treeToList(routeList) {
let res = [];
function foo(nodeData) {
let { resourceName, resourceUrl, children, actionVOS } = nodeData;
// 过滤掉 resourceUrl是null的选项
if (resourceUrl) res.push({ resourceName, resourceUrl, actionVOS });
// children 挂载的是子节点
if (!isEmpty(children)) {
children.forEach(item => {
foo(item);
});
}
}
routeList.forEach(item => {
foo(item);
});

return res;
}

//判断空数组,null,undefined,空字符串,空对象
export function isEmpty(obj) {
//基础数据类型,是非空值
if (typeof obj === 'boolean' || typeof obj === 'number') {
return false;
}
if (
obj == '' ||
obj == null ||
obj == undefined ||
(obj && Array.isArray(obj) && !obj.length) ||
(obj && Object.keys(obj).length === 0)
) {
return true;
} else {
return false;
}
}

export const showTitle = (item, vm) => {
let { title, __titleIsFunction__ } = item.meta;
if (!title) return;
title = (item.meta && item.meta.title) || item.name;
return title;
};

@/utils/auth.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Cookies from 'js-cookie';

const TokenKey = 'Admin-Token';

export function getToken() {
return Cookies.get(TokenKey);
}

export function setToken(token) {
return Cookies.set(TokenKey, token);
}

export function removeToken() {
return Cookies.remove(TokenKey);
}