一、router 获取系统路由菜单
1-后端生成菜单方法
getRouters 方法位于 类中,作用:根据角色获取菜单
-
@GetMapping("getRouters")
-
public R<List<RouterVo>> getRouters() {
-
Long userId = ();
-
List<SysMenu> menus = (userId);
-
return ((menus));
-
}
返回新构建,VO菜单集合逻辑
-
/**
-
* 构建前端路由所需要的菜单
-
*
-
* @param menus 菜单列表
-
* @return 路由列表
-
*/
-
@Override
-
public List<RouterVo> buildMenus(List<SysMenu> menus) {
-
List<RouterVo> routers = new LinkedList<>();
-
for (SysMenu menu : menus) {
-
RouterVo router = new RouterVo();
-
("1".equals(())); //是否显示
-
(getRouteName(menu));
-
(getRouterPath(menu));//设置路由地址
-
(getComponent(menu));//设置组件路径
-
(());//设置请求参数
-
(new MetaVo((), (), ("1", ()), ()));//设置其他信息
-
List<SysMenu> cMenus = ();
-
//2-菜单类型是目录 不跳转
-
if ((cMenus) && UserConstants.TYPE_DIR.equals(())) {
-
(true);//是否显示
-
("noRedirect");//不重定向
-
(buildMenus(cMenus));//递归子节点
-
} else if (isMenuFrame(menu)) {
-
//3-如果内部跳转
-
(null);
-
List<RouterVo> childrenList = new ArrayList<>();
-
RouterVo children = new RouterVo();
-
(());
-
(());
-
((()));
-
(new MetaVo((), (), ("1", ()), ()));
-
(());
-
(children);
-
(childrenList);
-
} else if (().intValue() == 0 && isInnerLink(menu)) {
-
//4- ,是否内链跳转,菜单目录为1级
-
(new MetaVo((), ()));
-
("/");
-
List<RouterVo> childrenList = new ArrayList<>();
-
RouterVo children = new RouterVo();
-
String routerPath = innerLinkReplaceEach(());
-
(routerPath);
-
(UserConstants.INNER_LINK);
-
((routerPath));
-
(new MetaVo((), (), ()));
-
(children);
-
(childrenList);
-
}
-
(router);
-
}
-
return routers;
-
}
2-前端调用getRouters 方法,生成路由:
src\api\中,定义了get请求
-
import request from '@/utils/request'
-
-
// 获取路由
-
export const getRouters = () => {
-
return request({
-
url: '/getRouters',
-
method: 'get'
-
})
-
}
vuex中发送请求,GenerateRoutes 是生成路由的方法
-
actions: {
-
// 生成路由
-
GenerateRoutes({ commit }) {
-
return new Promise(resolve => {
-
// 向后端请求路由数据
-
getRouters().then(res => {
-
const sdata = JSON.parse(JSON.stringify(res.data))
-
const rdata = JSON.parse(JSON.stringify(res.data))
-
//处理菜单类型,根据组件类型渲染菜单组件
-
const sidebarRoutes = filterAsyncRouter(sdata)
-
//比上面多了参数,此处是对子路由进行处理(额外多了,拼接路由路径)
-
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
-
-
//
-
const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
-
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
-
router.addRoutes(asyncRoutes);
-
// 设置到状态管理中去
-
commit('SET_ROUTES', rewriteRoutes)
-
// 设置侧边栏路由
-
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
-
// 设置默认路由
-
commit('SET_DEFAULT_ROUTES', sidebarRoutes)
-
// 顶部菜单路由
-
commit('SET_TOPBAR_ROUTES', sidebarRoutes)
-
resolve(rewriteRoutes)
-
})
-
})
-
}
-
}
而生成路由GenerateRoutes方法:src\ 前置路由守卫中(之前文章有写过路由守卫的逻辑)
最后在,rc\router\ 中导出路由
-
//2-导出路由的实例
-
export default new Router({
-
base: process.env.VUE_APP_CONTEXT_PATH,
-
mode: 'history', // 去掉url中的#
-
scrollBehavior: () => ({ y: 0 }),
-
routes: constantRoutes
-
})
二、侧边菜单和顶部菜单生成
src\layout\index.vue,主页面入口
-
<template>
-
<div :class="classObj" class="app-wrapper" :style="{'--current-color': theme}">
-
<div v-if="device==='mobile'&&" class="drawer-bg" @click="handleClickOutside"/>
-
-
<!-- 侧边 -->
-
<sidebar v-if="!" class="sidebar-container"/>
-
<div :class="{hasTagsView:needTagsView,sidebarHide:}" class="main-container">
-
-
<div :class="{'fixed-header':fixedHeader}">
-
<navbar/>
-
<tags-view v-if="needTagsView"/>
-
</div>
-
<!-- 主页面 -->
-
<app-main/>
-
<!-- 右边面板 -->
-
<right-panel>
-
<settings/>
-
</right-panel>
-
</div>
-
</div>
-
</template>
点击侧边组件,进入 src\layout\components\Sidebar\ 组件中,渲染侧边栏
-
<template>
-
<!--1- el-menu渲染菜单 log 、主题等-->
-
<div :class="{'has-logo':showLogo}" :style="{ backgroundColor: === 'theme-dark' ? : }">
-
<logo v-if="showLogo" :collapse="isCollapse" />
-
<el-scrollbar :class="" wrap-class="scrollbar-wrapper">
-
<el-menu
-
:default-active="activeMenu"
-
:collapse="isCollapse"
-
:background-color=" === 'theme-dark' ? : "
-
:text-color=" === 'theme-dark' ? : "
-
:unique-opened="true"
-
:active-text-color=""
-
:collapse-transition="false"
-
mode="vertical"
-
>
-
<!--2- 侧边栏 -->
-
<sidebar-item
-
v-for="(route, index) in sidebarRouters"
-
:key=" + index"
-
:item="route"
-
:base-path=""
-
/>
-
</el-menu>
-
</el-scrollbar>
-
</div>
-
</template>
点击注释2 ,进入侧边栏src\layout\components\Sidebar\,渲染菜单
-
<template>
-
<div v-if="!">
-
<!-- 1 - 只有一个菜单时候(不需要展开),页面跳转-->
-
<template v-if="hasOneShowingChild(,item) && (!||)&&!">
-
<app-link v-if="" :to="resolvePath(, )">
-
<el-menu-item :index="resolvePath()" :class="{'submenu-title-noDropdown':!isNest}">
-
<item :icon="||(&&)" :title="" />
-
</el-menu-item>
-
</app-link>
-
</template>
-
-
<!-- 2-有子菜单的时间,递归显示(展开子节点) -->
-
<el-submenu v-else ref="subMenu" :index="resolvePath()" popper-append-to-body>
-
<template slot="title">
-
<item v-if="" :icon=" && " :title="" />
-
</template>
-
<sidebar-item
-
v-for="child in "
-
:key=""
-
:is-nest="true"
-
:item="child"
-
:base-path="resolvePath()"
-
class="nest-menu"
-
/>
-
</el-submenu>
-
</div>
-
</template>
Navbar 导航栏 src\layout\components\
-
<template>
-
<div class="navbar">
-
<!-- 1- 是否水平折叠 -->
-
<hamburger id="hamburger-container" :is-active="" class="hamburger-container" @toggleClick="toggleSideBar" />
-
-
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav"/>
-
-
<!-- 2- 是否顶部导航 -->
-
<top-nav id="topmenu-container" class="topmenu-container" v-if="topNav"/>
-
-
-
。。。。。。。。。。。。。。
-
-
</div>
-
</template>
三、前端按钮控制、自定义权限角色组件
admin 和 普通用户的对比,权限方面的差距。
下面我以用户管理模块,来对比权限是怎么来控制的。
http://localhost/dev-api/getRouters 只是获取菜单。(用户关联角色--> 角色关联菜单)
-
{
-
"code": 200,
-
"msg": "操作成功",
-
"data": [
-
{
-
"name": "System",
-
"path": "/system",
-
"hidden": false,
-
"redirect": "noRedirect",
-
"component": "Layout",
-
"alwaysShow": true,
-
"meta": {
-
"title": "系统管理",
-
"icon": "system",
-
"noCache": false,
-
"link": null
-
},
-
"children": [
-
{
-
"name": "User",
-
"path": "user",
-
"hidden": false,
-
"component": "system/user/index",
-
"meta": {
-
"title": "用户管理",
-
"icon": "user",
-
"noCache": false,
-
"link": null
-
}
-
}
-
]
-
}
-
]
-
}
下面我们看下admin页面上的新增 修改 删除 导入 导出 几个按钮是怎么控制权限的。
看下用户管理的页面路径 : src\views\system\user\,中判断权限的代码: v-hasPermi="['system:user:add']"
-
<el-col :span="1.5">
-
<el-button
-
type="primary"
-
plain
-
icon="el-icon-plus"
-
size="mini"
-
@click="handleAdd"
-
v-hasPermi="['system:user:add']"
-
>新增</el-button>
-
</el-col>
其实就是在菜单管理中 权限表标识字段控制
v-hasPermi自定义指令的路径 src\directive\,自定义代码注释如下:
-
// 自定义指令
-
-
// 是否有角色
-
import hasRole from './permission/hasRole'
-
// 是否有权限
-
import hasPermi from './permission/hasPermi'
-
// 弹窗拖拽
-
import dialogDrag from './dialog/drag'
-
// 拖拽弹窗高度 宽度
-
import dialogDragWidth from './dialog/dragWidth'
-
import dialogDragHeight from './dialog/dragHeight'
-
// 剪辑板
-
import clipboard from './module/clipboard'
-
-
// 引入的模块声明为自定义指令
-
const install = function(Vue) {
-
Vue.directive('hasRole', hasRole)
-
Vue.directive('hasPermi', hasPermi)
-
Vue.directive('clipboard', clipboard)
-
Vue.directive('dialogDrag', dialogDrag)
-
Vue.directive('dialogDragWidth', dialogDragWidth)
-
Vue.directive('dialogDragHeight', dialogDragHeight)
-
}
-
-
if (window.Vue) {
-
window['hasRole'] = hasRole
-
window['hasPermi'] = hasPermi
-
Vue.use(install); // eslint-disable-line
-
}
-
-
// 导出
-
export default install
然后在src\中,导入组件并使用
-
// 引入自定组件
-
import directive from './directive' // directive
-
Vue.use(directive)
最后看下 v-hasPermi 里面的逻辑
// 1- 引入状态管理 import store from '@/store' // 2- 创建指令 export default { // 3- 监听权限变化, inserted自定义指令的钩子函数(el:指令操作的元素,binding:指令绑定的操作对象,vnode:vue生成虚拟节点用于对当前节点以及子节点操作) inserted(el, binding, vnode) { const { value } = binding const all_permission = "*:*:*";//超级管理员 const permissions = store.getters && store.getters.permissions // 获取权限,这边权限是在 中获取 if (value && value instanceof Array && value.length > 0) { const permissionFlag = value // 遇到符合的就不会继续往下执行,hasPermissions = true表示有权限,反正则没有 const hasPermissions = permissions.some(permission => { return all_permission === permission || permissionFlag.includes(permission) }) // 5-没有权限则移除节点 if (!hasPermissions) { el.parentNode && el.parentNode.removeChild(el) } } else { throw new Error(`请设置操作权限标签值`) } } }