动态路由
用户登录成功后根据用户名获取菜单数据,后台封装前端需要的路由格式
1.后端封装路由组件
1.1 RouterVo 实体类
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class RouterVo {
private String path;
private Boolean hidden;
private String name;
private String component;
private boolean alwaysShow;
private Meta meta;
@Data
@AllArgsConstructor
public class Meta {
private String title;
private String icon;
private Object[] roles;
}
private List<RouterVo> children = new ArrayList<>();
}
1.2 封装成树结构
@Override
public List<RouterVo> selectMenusByUsername(String username) {
Users one = userDao.selectOne(new QueryWrapper<Users>().eq("username", username));
List<Permissions> menuList = permissionsDao.selectPermitsByUser(one.getId());
List<Permissions> collect = menuList.stream().filter(item -> item != null && !item.getMenuType().equals(2)).collect(Collectors.toList());
return MenuUtils.makeRouter(collect, 0);
}
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class MenuUtils {
public static List<RouterVo> makeRouter(List<Permissions> menuList, Integer pid) {
List<RouterVo> list = new ArrayList<>();
Optional.ofNullable(menuList).orElse(new ArrayList<>())
.stream()
.filter(item -> item != null && item.getParentPermissId() == pid)
.forEach(item -> {
RouterVo router = new RouterVo();
router.setName(item.getName());
router.setPath(item.getUrl());
router.setHidden("1".equals(item.getHidden()));
if (item.getParentPermissId() == 0L) {
router.setComponent("Layout");
router.setAlwaysShow(true);
} else {
router.setComponent(item.getUrl());
router.setAlwaysShow(false);
}
router.setMeta(router.new Meta(
item.getPermissionName(),
item.getMenuIcon(),
item.getPerms().split(",")
));
List<RouterVo> children = makeRouter(menuList, item.getId());
router.setChildren(children);
list.add(router);
});
return list;
}
}
2.前端获取到后台返回的路由数据
2.1 router.beforeEach拦截路由进行封装
router.beforeEach(async(to, from, next) => {
NProgress.start()
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
next({ path: '/' })
NProgress.done()
} else {
const hasPermits = store.getters.permits && store.getters.permits.length > 0
if (hasPermits) {
next()
} else {
try {
const { permits } = await store.dispatch('user/getInfo')
const accessRoutes = await store.dispatch('menu/generateRoutes', permits)
let obj = { path: '*', redirect: '/404', hidden: true }
accessRoutes.push(obj)
router.addRoutes(accessRoutes);
next({ ...to, replace: true })
} catch (error) {
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
2.2新建menu.js中递归封装component
import { constantRoutes } from '@/router'
import { getMenus } from '@/api/user'
import Layout from '@/layout'
export function filterAsyncRoutes(routes, permits) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(permits, tmp)) {
const component = tmp.component
if (route.component) {
if (component == 'Layout') {
tmp.component = Layout
} else {
tmp.component = (resolve) => require([`@/views${component}`], resolve)
}
}
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, permits)
}
res.push(tmp)
}
})
return res
}
function hasPermission(permits, route) {
if (route.meta && route.meta.permits) {
return permits.some(permit => route.meta.permits.includes(permit))
} else {
return true
}
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, permits) {
return new Promise((resolve, reject) => {
getMenus().then(res => {
let accessedRoutes
if (res.code == 200) {
accessedRoutes = filterAsyncRoutes(res.data, permits)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
}).catch(error => {
reject(error)
})
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
sideBar中修改引用的路由,从vuex中获取
<template>
<div :class="{'has-logo':showLogo}">
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:unique-opened="true"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
</el-scrollbar>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'
export default {
components: { SidebarItem, Logo },
computed: {
...mapGetters([
'sidebar',
'permission_routes'
]),
routes() {
return this.$router.options.routes
},
activeMenu() {
const route = this.$route
const { meta, path } = route
if (meta.activeMenu) {
return meta.activeMenu
}
return path
},
showLogo() {
return this.$store.state.settings.sidebarLogo
},
variables() {
return variables
},
isCollapse() {
return !this.sidebar.opened
}
}
}
</script>
|