/** * 构造菜单树工具类 * @author dang * */ public class TreeUtil { protected TreeUtil() { } private final static Long TOP_NODE_ID = (long) 1; /** * 构造前端路由 * @param routes * @return */ public static ArrayList<MenuEntity> buildVueRouter(List<MenuEntity> routes) { if (routes == null) { return null; } List<MenuEntity> topRoutes = new ArrayList<>(); routes.forEach(route -> { Long parentId = route.getParentId(); if (TOP_NODE_ID.equals(parentId)) { topRoutes.add(route); return; } for (MenuEntity parent : routes) { Long id = parent.getId(); if (id != null && id.equals(parentId)) { if (parent.getChildren() == null) { parent.initChildren(); } parent.getChildren().add(route); return; } } }); ArrayList<MenuEntity> list = new ArrayList<>(); MenuEntity root = new MenuEntity(); root.setName("首页"); root.setComponent("BasicLayout"); root.setPath("https://www.jb51.net/"); root.setRedirect("/other/list/user-list"); root.setChildren(topRoutes); list.add(root); return list; } }
菜单实体 (使用了lombok插件)
/** * 菜单实体 * @author dang * */ public class MenuEntity extends CoreEntity { private static final long serialVersionUID = 1L; @TableField("FParentId") private Long parentId; @TableField("FNumber") private String number; @TableField("FName") private String name; @TableField("FPerms") private String perms; @TableField("FType") private int type; @TableField("FLongNumber") private String longNumber; @TableField("FPath") private String path; @TableField("FComponent") private String component; @TableField("FRedirect") private String redirect; @TableField(exist = false) private List<MenuEntity> children; @TableField(exist = false) private MenuMeta meta; @TableField(exist = false) private List<PermissionEntity> permissionList; @Override public int hashCode() { return number.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj(obj); } public void initChildren() { this.children = new ArrayList<>(); } }
路由菜单是根据用户的角色去获得的,一个用户具有多个角色,一个角色具有多个菜单
思路:
说下按钮权限控制的实现:前端vue主要用自定义指令实现控制按钮的显示与隐藏,后端我用的是SpringSecurity框架,所以使用的是 @PreAuthorize注解, 在菜单实体的 perms属性记录权限的标识,如:sys:user:add,记录有权限标识的菜单其 parentId 应为上级菜单,然后获取用户的perms集合,在用户登录的时候传给前端并用Vuex保存,在自定义指令中去比较用户是否含有按钮所需要的权限。
实现:
获取用户信息的时候,把权限存到Vuex中 commit('SET_PERMISSIONS', result.authorities)
// 获取用户信息 GetInfo ({ commit }) { return new Promise((resolve, reject) => { getInfo().then(response => { const result = response if (result.authorities) { commit('SET_PERMISSIONS', result.authorities) commit('SET_ROLES', result.principal.roles) commit('SET_INFO', result) } else { reject(new Error('getInfo: roles must be a non-null array !')) } commit('SET_NAME', { name: result.principal.displayName, welcome: welcome() }) commit('SET_AVATAR', result.principal.avatar) resolve(response) }).catch(error => { reject(error) }) }) }
前端自定义指令
// 定义一些和权限有关的 Vue指令 // 必须包含列出的所有权限,元素才显示 export const hasPermission = { install (Vue) { Vue.directive('hasPermission', { bind (el, binding, vnode) { const permissions = vnode.context.$store.state.user.permissions const per = [] for (const v of permissions) { per.push(v.authority) } const value = binding.value let flag = true for (const v of value) { if (!per.includes(v)) { flag = false } } if (!flag) { if (!el.parentNode) { el.style.display = 'none' } else { el.parentNode.removeChild(el) } } } }) } } // 当不包含列出的权限时,渲染该元素 export const hasNoPermission = { install (Vue) { Vue.directive('hasNoPermission', { bind (el, binding, vnode) { const permissions = vnode.context.$store.state.user.permissions const per = [] for (const v of permissions) { per.push(v.authority) } const value = binding.value let flag = true for (const v of value) { if (per.includes(v)) { flag = false } } if (!flag) { if (!el.parentNode) { el.style.display = 'none' } else { el.parentNode.removeChild(el) } } } }) } } // 只要包含列出的任意一个权限,元素就会显示 export const hasAnyPermission = { install (Vue) { Vue.directive('hasAnyPermission', { bind (el, binding, vnode) { const permissions = vnode.context.$store.state.user.permissions const per = [] for (const v of permissions) { per.push(v.authority) } const value = binding.value let flag = false for (const v of value) { if (per.includes(v)) { flag = true } } if (!flag) { if (!el.parentNode) { el.style.display = 'none' } else { el.parentNode.removeChild(el) } } } }) } } // 必须包含列出的所有角色,元素才显示 export const hasRole = { install (Vue) { Vue.directive('hasRole', { bind (el, binding, vnode) { const permissions = vnode.context.$store.state.user.roles const per = [] for (const v of permissions) { per.push(v.authority) } const value = binding.value let flag = true for (const v of value) { if (!per.includes(v)) { flag = false } } if (!flag) { if (!el.parentNode) { el.style.display = 'none' } else { el.parentNode.removeChild(el) } } } }) } } // 只要包含列出的任意一个角色,元素就会显示 export const hasAnyRole = { install (Vue) { Vue.directive('hasAnyRole', { bind (el, binding, vnode) { const permissions = vnode.context.$store.state.user.roles const per = [] for (const v of permissions) { per.push(v.authority) } const value = binding.value let flag = false for (const v of value) { if (per.includes(v)) { flag = true } } if (!flag) { if (!el.parentNode) { el.style.display = 'none' } else { el.parentNode.removeChild(el) } } } }) } }
在main.js中引入自定义指令