全部
常见问题
产品动态
精选推荐

vue2.0根据菜单信息动态添加路由

管理 管理 编辑 删除

在开发的过程中,一般都是后端去做权限的设置和配置前端的路由,前端根据后台编写相应的路由。这样就很做到权限的控制。在某些情况,需要添加的路由不确定,需要从后端获取数据,并且后端更新相关的路由时,页面也能够立即渲染出来,这时候就需要使用动态路。

原来的路由目录结构

例如OA管理系统中,菜单中的很多路由都是不确定的,即使你写了10个路由,但是后端那边新增了10个路由,那么这时候设置动态添加路由后,就可以自动在第一时间创建出所有的路由,而不需要你手动的写了。

1、设置默认路由

import Vue from 'vue';
import Router from 'vue-router';
import { roterPre } from '@/settings';
Vue.use(Router);
/* Layout */
import Layout from '@/layout';





import defaultRoutes from '@/router/routes';
import companyRouter from '@/router/company';
export const constantRoutes = [...defaultRoutes, companyRouter];



const createRouter = () =>
  new Router({
    mode: 'history', // require service support
    scrollBehavior: () => ({ y: 0 }),
    routes: constantRoutes,
  });

const router = createRouter();
// 解决刷新页面后路由重置
getRouterMenus();

export function resetRouter() {
  const newRouter = createRouter();
  router.matcher = newRouter.matcher; // reset router
}

export default router;

其中defaultRoutes ,companyRouter 为系统中的默认菜单。

2、获取系统模板以及处理后台返回的菜单

// 获取views中以.vue结尾的文件
const contextInfo = require.context('../views', true, /.vue$/);

/**
 * 过滤/components中的vue文件
 * @returns {{}}
 */
const filteredFileNames = () => {
  const files = {};
  contextInfo.keys().forEach((fileName) => {
    if (!fileName.includes('/components')) {
      const pathConfig = contextInfo(fileName);
      let path = '/' + fileName.substring(2, fileName.length - 4);
      files[path] = pathConfig.default;
    }
  });
  return files;
};
const files = filteredFileNames();

以上的处理就是为了的到views中以.vue结尾的组件,并且过滤掉components中的.vue文件。这一步主要是解决ES6 import()  中不能使用变量加载组件的问题。 files中的数据格式为

{
   "/uesr/calendar/index":components,
   "/uesr/duty/analyse":components,
   ...
}

3、处理后台返回的菜单及生成路由

/**
 * 删除不需要配置路由
 * @param items
 * @param paths
 */
const removeItems = (items, paths) => {
  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    for (let j = 0; j < paths.length; j++) {
      const path = paths[j];
      if (item.menu_path === path) {
        items.splice(i, 1);
        i--;
        break;
      } else if (item.children) {
        removeItems(item.children, paths);
        if (item.children.length === 0) {
          delete item.children;
        }
      }
    }
  }
};

/**
 * 处理平台菜单
 * @param menus
 * @param parentPath
 */
const replaceMenuPath = (menus, parentPath) => {
  menus.forEach((menu, index) => {
    if (menu.children) {
      replaceMenuPath(menu.children, menu.menu_path);
    }
    if (parentPath !== '/' && menu.menu_path.indexOf(parentPath) > -1) {
      menu.new_path = menu.menu_path.replace(parentPath + '/', '');
      menu.component = menu.menu_path.replace(roterPre, '');
    }
    if (menu.menu_path !== '/') {
      menu.component = menu.menu_path.replace(roterPre, '');
    }
  });
};

/**
 * 过滤路由所需要的数据
 * @param routes
 * @returns {*}
 */
const filterAsyncRoutes = (routes) => {
  return routes.map((route) => {
    const routeRecord = createRouteRecord(route);
    if (route.children != null && route.children && route.children.length) {
      routeRecord.children = filterAsyncRoutes(route.children);
    }
    return routeRecord;
  });
};

/**
 * 创建一条路由记录
 * @param route
 * @returns {{path: (*|string), meta: {noCache: boolean, icon, title: *}, name: *}}
 */
const createRouteRecord = (route) => {
  const routeRecord = {
    path: route.pid === 0 ? route.menu_path : route.new_path ? route.new_path : '/',
    name: route.unique_auth,
    meta: {
      title: route.menu_name,
      icon: route.icon,
      noCache: true,
    },
  };

  if (route.pid === 0) {
    routeRecord.component = Layout;
  } else if (route.pid > 0 && route.children && route.children.length > 0) {
    // 解决父级不写component 属性,子集的component也不会显示问题
    routeRecord.component = { render: (e) => e('router-view') };
  } else {
    routeRecord.component = files[route.component];
  }
  return routeRecord;
};

/**
 * 根据后台菜单动态生成路由
 */
export const getRouterMenus = () => {
  const entMenuList = JSON.parse(localStorage.getItem('entMenuList'));
  let entRouter = [];
  if (entMenuList && entMenuList.length > 0) {
    // 移除不需要处理路由
    removeItems(entMenuList, ['/admin/user/work']);
    // 处理后台返回的路由结构
    replaceMenuPath(entMenuList, '/');
    entRouter = filterAsyncRoutes(entMenuList);
    constantRoutes.push(...entRouter);
    // 路由重置加载
    resetRouter();
  }
};

其中constantRoutes为开始定义的数组,便于存储路由。

4、最终index.js逻辑源码

import Vue from 'vue';
import Router from 'vue-router';
import { roterPre } from '@/settings';
Vue.use(Router);
/* Layout */
import Layout from '@/layout';

// 获取views中以.vue结尾的文件
const contextInfo = require.context('../views', true, /.vue$/);

/**
 * 过滤/components中的vue文件
 * @returns {{}}
 */
const filteredFileNames = () => {
  const files = {};
  contextInfo.keys().forEach((fileName) => {
    if (!fileName.includes('/components')) {
      const pathConfig = contextInfo(fileName);
      let path = '/' + fileName.substring(2, fileName.length - 4);
      files[path] = pathConfig.default;
    }
  });
  return files;
};
const files = filteredFileNames();

/**
 * 删除不需要配置路由
 * @param items
 * @param paths
 */
const removeItems = (items, paths) => {
  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    for (let j = 0; j < paths.length; j++) {
      const path = paths[j];
      if (item.menu_path === path) {
        items.splice(i, 1);
        i--;
        break;
      } else if (item.children) {
        removeItems(item.children, paths);
        if (item.children.length === 0) {
          delete item.children;
        }
      }
    }
  }
};

/**
 * 处理平台菜单
 * @param menus
 * @param parentPath
 */
const replaceMenuPath = (menus, parentPath) => {
  menus.forEach((menu, index) => {
    if (menu.children) {
      replaceMenuPath(menu.children, menu.menu_path);
    }
    if (parentPath !== '/' && menu.menu_path.indexOf(parentPath) > -1) {
      menu.new_path = menu.menu_path.replace(parentPath + '/', '');
      menu.component = menu.menu_path.replace(roterPre, '');
    }
    if (menu.menu_path !== '/') {
      menu.component = menu.menu_path.replace(roterPre, '');
    }
  });
};

/**
 * 过滤路由所需要的数据
 * @param routes
 * @returns {*}
 */
const filterAsyncRoutes = (routes) => {
  return routes.map((route) => {
    const routeRecord = createRouteRecord(route);
    if (route.children != null && route.children && route.children.length) {
      routeRecord.children = filterAsyncRoutes(route.children);
    }
    return routeRecord;
  });
};

/**
 * 创建一条路由记录
 * @param route
 * @returns {{path: (*|string), meta: {noCache: boolean, icon, title: *}, name: *}}
 */
const createRouteRecord = (route) => {
  const routeRecord = {
    path: route.pid === 0 ? route.menu_path : route.new_path ? route.new_path : '/',
    name: route.unique_auth,
    meta: {
      title: route.menu_name,
      icon: route.icon,
      noCache: true,
    },
  };

  if (route.pid === 0) {
    routeRecord.component = Layout;
  } else if (route.pid > 0 && route.children && route.children.length > 0) {
    // 解决父级不写component 属性,子集的component也不会显示问题
    routeRecord.component = { render: (e) => e('router-view') };
  } else {
    routeRecord.component = files[route.component];
  }
  return routeRecord;
};

import defaultRoutes from '@/router/routes';
import companyRouter from '@/router/company';
export const constantRoutes = [...defaultRoutes, companyRouter];

/**
 * 根据后台菜单动态生成路由
 */
export const getRouterMenus = () => {
  const entMenuList = JSON.parse(localStorage.getItem('entMenuList'));
  let entRouter = [];
  if (entMenuList && entMenuList.length > 0) {
    // 移除不需要处理路由
    removeItems(entMenuList, ['/admin/user/work']);
    // 处理后台返回的路由结构
    replaceMenuPath(entMenuList, '/');
    entRouter = filterAsyncRoutes(entMenuList);
    constantRoutes.push(...entRouter);
    // 路由重置加载
    resetRouter();
  }
};

const createRouter = () =>
  new Router({
    mode: 'history', // require service support
    scrollBehavior: () => ({ y: 0 }),
    routes: constantRoutes,
  });

const router = createRouter();
// 解决刷新页面后路由重置
getRouterMenus();

export function resetRouter() {
  const newRouter = createRouter();
  router.matcher = newRouter.matcher; // reset router
}

export default router;

最终的目录结构。简化了好多需要自己手动创建的路由。


请登录后查看

武湘君 最后编辑于2023-05-22 10:21:39

快捷回复
回复
回复
回复({{post_count}}) {{!is_user ? '我的回复' :'全部回复'}}
排序 默认正序 回复倒序 点赞倒序

{{item.user_info.nickname ? item.user_info.nickname : item.user_name}} LV.{{ item.user_info.bbs_level }}

作者 管理员 企业

{{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest == 1? '取消推荐': '推荐'}}
{{item.is_suggest == 1? '取消推荐': '推荐'}}
沙发 板凳 地板 {{item.floor}}#
{{item.user_info.title || '暂无简介'}}
附件

{{itemf.name}}

{{item.created_at}}  {{item.ip_address}}
{{item.like_count}}
{{item.showReply ? '取消回复' : '回复'}}
删除
回复
回复

{{itemc.user_info.nickname}}

{{itemc.user_name}}

回复 {{itemc.comment_user_info.nickname}}

附件

{{itemf.name}}

{{itemc.created_at}}
{{itemc.like_count}}
{{itemc.showReply ? '取消回复' : '回复'}}
删除
回复
回复
查看更多
3188
{{like_count}}
{{collect_count}}
添加回复 ({{post_count}})

相关推荐

快速安全登录

使用微信扫码登录
{{item.label}} 加精
{{item.label}} {{item.label}} 板块推荐 常见问题 产品动态 精选推荐 首页头条 首页动态 首页推荐
取 消 确 定
回复
回复
问题:
问题自动获取的帖子内容,不准确时需要手动修改. [获取答案]
答案:
提交
bug 需求 取 消 确 定

微信登录/注册

切换手机号登录

{{ bind_phone ? '绑定手机' : '手机登录'}}

{{codeText}}
切换微信登录/注册
暂不绑定
CRMEB客服

CRMEB咨询热线 咨询热线

400-8888-794

微信扫码咨询

CRMEB开源商城下载 源码下载 CRMEB帮助文档 帮助文档
返回顶部 返回顶部
CRMEB客服