本篇是SpringBoot整合vue-admin-template的第二篇,需要了解SpringBoot如何整合vue-admin-template实现数据库登录认证的小伙伴,请参阅SpringBoot整合vue-admin-template实现登录,本文将主要介绍SpringBoot整合vue-admin-template实现从后台数据实时查询数据动态加载菜单,
2. 前端代码实现 2.1. router/index.js删除constantRoutes中多余路由配置,只保留必要的,新增asyncRoutes路由用于加载从后台数据库获取的动态数据,新增exceptionRoutes路由用于处理错误页面
export const asyncRoutes = [
]
export const exceptionRoutes = [
{
path: '*',
redirect: '/404',
hidden: true
}
]
2.2. api/menu.js
在api目录下新建menu.js文件,用于获取后台菜单数据
import request from '@/utils/request'
export function getRoutes() {
return request({
url: '/getRoutes',
method: 'get'
})
}
2.3. store/modules/permission.js
在store/modules目录下新建permission文件
import { asyncRoutes, constantRoutes, exceptionRoutes } from '@/router'
import { getRoutes } from '@/api/menu'
import Layout from '@/layout/index'
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
export function generateMenus(routes, data, is_children = false) {
data.forEach(item => {
const menu = {
path: item.path,
component: item.component === 'Layout' ? Layout : resolve => require([`@/views/${item.component}`], resolve),
name: item.name,
meta: item.meta
}
if (is_children === false) {
menu.children = []
menu.redirect = item.redirect
}
if (item.children && is_children === false) {
generateMenus(menu.children, item.children, true)
}
routes.push(menu)
})
}
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
const loadMenuData = []
getRoutes().then(res => {
Object.assign(loadMenuData, res.data)
const tmpAsyncRoutes = Object.assign([], asyncRoutes)
generateMenus(tmpAsyncRoutes, loadMenuData)
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = tmpAsyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(tmpAsyncRoutes, roles)
}
accessedRoutes = accessedRoutes.concat(exceptionRoutes)
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
})
}
}
export const loadView = (view) => { // 路由懒加载
return (resolve) => require([`@/views/${view}`], resolve)
}
export default {
namespaced: true,
state,
mutations,
actions
}
2.4. store/index.js
在store目录下index.js文件中添加permission引用
...
import permission from './modules/permission'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
...
permission
},
getters
})
export default store
2.5. src/permission.js
将动态路由挂载到src目录下permission文件中
// const hasGetUserInfo = store.getters.name
const hasGetUserInfo = store.getters.roles && store.getters.roles.length > 0
if (hasGetUserInfo) {
next()
} else {
try {
// get user info
// await store.dispatch('user/getInfo')
const { roles } = await store.dispatch('user/getInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
router.options.routes = constantRoutes.concat(accessRoutes)
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
// next()
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
2.6. src/modules/user.js
新增角色信息,完整代码如下
import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import router, { resetRouter } from '@/router'
const getDefaultState = () => {
return {
token: getToken(),
name: '',
avatar: '',
roles: []
}
}
const state = getDefaultState()
const mutations = {
RESET_STATE: (state) => {
Object.assign(state, getDefaultState())
},
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
}
}
const actions = {
// user login
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
const { data } = response
commit('SET_TOKEN', data.token)
setToken(data.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
// get user info
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const { data } = response
if (!data) {
return reject('Verification failed, please Login again.')
}
const { name, avatar, roles } = data
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
commit('SET_ROLES', roles)
resolve(data)
}).catch(error => {
reject(error)
})
})
},
// user logout
logout({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
removeToken() // must remove token first
resetRouter()
// commit('RESET_STATE')
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
resolve()
}).catch(error => {
reject(error)
})
})
},
// remove token
resetToken({ commit }) {
return new Promise(resolve => {
removeToken() // must remove token first
// commit('RESET_STATE')
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
resolve()
})
},
async changeRoles({ commit, dispatch }, role) {
const token = state.token
commit('SET_TOKEN', token)
setToken(token)
const { roles } = await dispatch('getInfo')
resetRouter()
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
router.addRoutes(accessRoutes)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
2.7. store/getters.js
增加角色配置
roles: state => state.user.roles3. 后端代码实现 3.1. 控制层代码
@GetMapping("/getRoutes")
public ResultData getRoutes() {
SysUserInfo userInfo = loginService.getUserInfo();
List menuInfos = menuInfoService.selectMenuTreeByUserId(userInfo.getId());
List routerMenus = menuInfoService.buildMenus(menuInfos);
return ResultData.success().data(routerMenus);
}
3.2. 业务层代码
@Override public List3.3. 路由数据selectMenuTreeByUserId(Long userId) { List menuInfoList = menuInfoMapper.selectMenuTreeByUserId(userId); return getChildPerms(menuInfoList, 0); } @Override public List buildMenus(List menuInfos) { List routerMenus = new linkedList<>(); for (SysMenuInfo menuInfo : menuInfos) { RouterMenu routerMenu = new RouterMenu(); //routerMenu.setHidden(menuInfo.getVisible().intValue() == 1); routerMenu.setName(getRouteName(menuInfo)); routerMenu.setPath(getRoutePath(menuInfo)); routerMenu.setComponent(getComponent(menuInfo)); routerMenu.setmeta(new metaMenu(menuInfo.getMenuName(), menuInfo.getIcon())); List childMenus = menuInfo.getChildren(); if (!childMenus.isEmpty() && childMenus.size() > 0 && "M".equals(menuInfo.getMenuType())) { routerMenu.setChildren(buildMenus(childMenus)); } routerMenus.add(routerMenu); } return routerMenus; } private String getRouteName(SysMenuInfo menuInfo) { String routeName = StringUtils.capitalize(menuInfo.getPath()); if (menuInfo.getParentId().longValue() == 0 && "C".equals(menuInfo.getMenuType())) { routeName = ""; } return routeName; } private String getRoutePath(SysMenuInfo menuInfo) { String routePath = menuInfo.getPath(); if (menuInfo.getParentId().longValue() == 0 && "M".equals(menuInfo.getMenuType())) { routePath = "/" + menuInfo.getPath(); } else if (menuInfo.getParentId().longValue() == 0 && "C".equals(menuInfo.getMenuType())) { routePath = "/"; } return routePath; } private String getComponent(SysMenuInfo menuInfo) { String component = "Layout"; if (StringUtils.hasLength(menuInfo.getComponent())) { component = menuInfo.getComponent(); } return component; } private List getChildPerms(List menuInfoList, int parentId) { List menuInfos = new ArrayList<>(); for (Iterator iterator = menuInfoList.iterator(); iterator.hasNext(); ) { SysMenuInfo menuInfo = iterator.next(); if (menuInfo.getParentId() == parentId) { recursionFn(menuInfoList, menuInfo); menuInfos.add(menuInfo); } } return menuInfos; } private void recursionFn(List menuInfoList, SysMenuInfo menuInfo) { List childList = getChildList(menuInfoList, menuInfo); menuInfo.setChildren(childList); for (SysMenuInfo childMenu : childList) { if (hasChild(menuInfoList, childMenu)) { recursionFn(menuInfoList, childMenu); } } } private boolean hasChild(List menuInfoList, SysMenuInfo childMenu) { return getChildList(menuInfoList, childMenu).size() > 0 ? true : false; } private List getChildList(List menuInfoList, SysMenuInfo menuInfo) { List childList = new ArrayList<>(); Iterator iterator = menuInfoList.iterator(); while (iterator.hasNext()) { SysMenuInfo nextMenu = iterator.next(); if (nextMenu.getParentId().longValue() == menuInfo.getId().longValue()) { childList.add(nextMenu); } } return childList; }
[
{
"name": "System",
"path": "/system",
"component": "Layout",
"meta": {
"title": "系统管理",
"icon": "system"
},
"children": [
{
"name": "User",
"path": "user",
"component": "system/user/index",
"meta": {
"title": "用户管理",
"icon": "user"
}
},
{
"name": "Role",
"path": "role",
"component": "system/role/index",
"meta": {
"title": "角色管理",
"icon": "peoples"
}
},
{
"name": "Menu",
"path": "menu",
"component": "system/menu/index",
"meta": {
"title": "菜单管理",
"icon": "tree-table"
}
},
{
"name": "Dict",
"path": "dict",
"component": "system/dict/index",
"meta": {
"title": "字典管理",
"icon": "dict"
}
}
]
}
]
4. 验证
分别使用用户admin和editor登录系统



