vue-element-admin改为从后台拿动态路由(菜单)- 上

改为从后台拿动态路由,大概如下步骤:

1、后台增加接口,返回动态路由数据

2、前端增加请求动态路由接口请求

3、修改 src/route/index.js 去掉原有的动态路由,增加组件名和组件对象映射 map

4、修改 src/store/modules/permission.js 修改当前 权限判断处理方法 generateRoutes

一、后台增加接口

 1、后台随便添加一个 Controller 随便加一个接口,添加如下代码。正常 str 内容应该从数据库中查询得到,为了避免信息量过多这里直接写一个 json 返回

    @RequestMapping(value = "/getPermission", method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    @ApiOperation(value = "得到当前登录用户所有的权限(Route 菜单 + Permission 按钮)", notes = "得到当前登录用户所有的权限(Route 菜单 + Permission 按钮)")
    public Response<User> getPermission() throws Exception {
        String str = "[\n" +
                "  {\n" +
                "    component: 'layout',\n" +
                "    path: '/interface_test',\n" +
                "    name: 'interface_test',\n" +
                "    meta: { title: '接口测试', icon: 'el-icon-data-line' },\n" +
                "    redirect: '/interface_test/test_case',\n" +
                "    alwaysShow: true,\n" +
                "    children: [\n" +
                "      {\n" +
                "        path: 'public_step',\n" +
                "        name: 'public_step',\n" +
                "        meta: { title: '公共步骤' },\n" +
                "        component: 'public_step'\n" +
                "      }\n" +
                "    ]\n" +
                "  }]";

        JSONArray jsonObject = JSONArray.parseArray(str);

        Map<String, Object> retMap = new HashMap<>();
        retMap.put("routes", jsonObject);
        Response response = Response.success(retMap);
        return response;
    }

2、使用 postman 等工具请求接口,返回如下。其中 code、data 可能跟你的包装有所不同,关键就是返回 routes 下的内容

{
  "code": 0,
  "msg": "请求成功",
  "data": {
    "routes": [
      {
        "redirect": "/interface_test/test_case",
        "path": "/interface_test",
        "component": "layout",
        "children": [
          {
            "path": "public_step",
            "component": "public_step",
            "meta": {
              "title": "公共步骤"
            },
            "name": "public_step"
          }
        ],
        "meta": {
          "icon": "el-icon-data-line",
          "title": "接口测试"
        },
        "name": "interface_test",
        "alwaysShow": true
      }
    ]
  }
}

二、前端增加接口请求

1、前端打开 src/api/role.js 文件,插入刚增加的接口请求,代码如下

export function getPermission() {
  return request({
    url: '/manage/role/getPermission',
    method: 'get'
  })
}

 三、去掉原有的动态路由,增加组件名和组件对象映射 map

我这里后续的代码是,将从后台请求回来的路由和 route/index.js 中的动态路由组合后一起做为当前路由返回,所以这里要删除之前增加的动态路由,如果你是替换那就不用管。

1、打开 src/route/index.js 文件

去掉之前引入的 router 组件

从 asyncRoutes 动态路由中删除之前的定义,只保留 404。如果你是替换 asyncRoutes 动态路由,那一定要在最后添加一个 404,要不然输入一个不存在的 url 后不会显示 404,而是一个白屏。

 2、组件名和组件对象映射 map

由于后台请求回来的数据不是组件对象而是组件名称,所以要将名称转为 组件对象,这里定义的就是名称和对象的对应关系。代码如下

// 组件名 和 组件对象 映射表,用于后续根据后台传过来的 组件名 替换成 组件对象 使用
export const componentMap = {
  'layout': require('@/layout').default,
  'test_case': () => import('@/views/functional_test/test_case_manage/test_case').then(m => m.default),
  'public_step': () => import('@/views/functional_test/test_data_manage/public_step').then(m => m.default)
}  

 注:由于是通过组件名称对应,所以后台添加的组件名,一定要跟这里定义个名称一样。

 四、权限判断处理方法 generateRoutes

1、打开 src/store/modules/permission.js 修改如下

增加 api 引用

 修改 generateRoutes 权限判断

 代码如下

const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      // 从后台请求到的路由
      getPermission().then(response => {
        // 整理后台返回的路由
        let accessedRoutes = response.data.routes.filter(item => {
          return pruningRoutes(item)
        })

        accessedRoutes = accessedRoutes.concat(asyncRoutes) // 合并异步路由(例如将404页面合并入可访问路由)
        accessedRoutes = filterAsyncRoutes(accessedRoutes, roles) // 通过递归根据角色过滤异步路由

        commit('SET_ROUTES', accessedRoutes)
        resolve(accessedRoutes)
      })
    })
  }
}

/**
 * 整理后台返回的路由
 * @param route 路由
 * @returns {{children}|*}
 */
function pruningRoutes(route) {
  // 路由中有 component 属性,将组件字符串转换为组件对象
  if (route.component && typeof route.component === 'string') {
    route.component = componentMap[route.component]
  }
  // 路由中有 props 属性,将 props 字符串转换为 props 对象
  if (route.props && typeof route.props === 'string') {
    route.props = JSON.parse(route.props)
  }
  // 路由中有 children,循环递归处理 children
  if (route.children && route.children.length > 0) {
    for (let i = 0; i < route.children.length; i++) {
      pruningRoutes(route.children[i])
    }
    return route
  } else {
    delete route.children
  }
}

  

代码讲解  

改动如下:

 

 五、重启后效果如下

 六、后续 - 数据库设计

这次只定义了 role、role_route、route、route_meta 表,后续的用户角色对应表、资源路由对应表等还没创建。表结构如下

CREATE TABLE `role` (
  `name` varchar(50) NOT NULL COMMENT '角色名称',
  `desc` varchar(50) NOT NULL COMMENT '角色描述',
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `is_delete` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='角色信息表';


CREATE TABLE `role_route` (
  `role_id` bigint(20) NOT NULL COMMENT '角色 id',
  `route_id` bigint(20) NOT NULL COMMENT '路由信息',
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `is_delete` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=74 DEFAULT CHARSET=utf8 COMMENT='角色对应路由信息表';


CREATE TABLE `route` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `path` varchar(255) NOT NULL COMMENT '路由匹配的路径,注意根节点前要加/',
  `name` varchar(255) NOT NULL COMMENT '路由的名称',
  `component` varchar(255) NOT NULL COMMENT '对应加载的组件',
  `redirect` varchar(255) DEFAULT NULL COMMENT '重定向的路由路径',
  `hidden` bit(1) NOT NULL COMMENT '在菜单中是否隐藏。true:隐藏,默认 false',
  `always_show` bit(1) NOT NULL COMMENT '是否始终显示根菜单。true:无论有没有子节点都显示,false:必须有子节点才显示',
  `props` varchar(255) DEFAULT NULL COMMENT '传递数据的 props 属性',
  `route_meta_id` bigint(20) NOT NULL COMMENT '元信息 id',
  `parent_id` bigint(20) NOT NULL COMMENT '父分类的 id',
  `sort` int(11) NOT NULL COMMENT '排序',
  `is_delete` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8 COMMENT='路由信息表';


CREATE TABLE `route_meta` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `title` varchar(255) NOT NULL COMMENT '页面标题',
  `icon` varchar(255) DEFAULT NULL COMMENT '图标',
  `no_cache` bit(1) NOT NULL COMMENT '是否禁用缓存页面。true:禁止缓存页面(默认值为false)',
  `affix` bit(1) NOT NULL COMMENT '标签视图中是否可关闭。true:不允许关闭(默认 false)',
  `breadcrumb` bit(1) NOT NULL COMMENT '是否显示面包屑。false:不在面包屑中显示(默认值为 true)',
  `is_delete` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8 COMMENT='路由 Meta 信息表';

  

posted @ 2023-07-19 10:19  rslai  阅读(610)  评论(0编辑  收藏  举报