vue 动态路由实现

256 阅读1分钟

actions.js

// 动态路由后端返回的demo数据 提示:父路由必须在子路由的前面
const demoAsyncRoutes = [
  {
    id: 1,
    pid: 0,
    path: '/nested',
    name: 'Nested',
    hidden: false,
    alwaysShow: true,
    component: 'layout/Layout',
    redirect: '/nested/menu1/menu1-1',
    meta: '{ "title": "nested", "icon": "nested" }'
  },
  {
    id: 2,
    pid: 1,
    path: 'menu1',
    name: 'Menu1',
    component: 'nested/menu1/index',
    hidden: false,
    alwaysShow: true,
    meta: '{ "title": "menu1" }'
  },
  {
    id: 3,
    pid: 2,
    path: 'menu1-1',
    name: 'Menu1-1',
    component: 'nested/menu1/menu1-1',
    hidden: false,
    alwaysShow: true,
    meta: '{ "title": "menu1-1" }'
  },
  {
    id: 4,
    pid: 1,
    path: 'menu2',
    name: 'Menu2',
    component: 'nested/menu2/index',
    hidden: false,
    alwaysShow: true,
    meta: '{ "title": "menu2" }'
  }
]

// 404路由
const notFoundRoutes = [
  {
    path: "/404",
    component: () => import("@/views/404"),
    hidden: true
  },
]
const loadView = (view) => {
  // 路由懒加载
  // return (resolve) => require([`@/views/${view}`], resolve)
  return () => import(`@/views/${view}`);
}

/**
 * 格式化后端动态路由数据
 * @param {{
 * id:number,
 * pid:number,
 * path:string,
 * name:string,
 * component:string,
 * hidden:boolean,
 * alwaysShow:boolean,
 * meta:string
 * }[]} asyncRoutes 
 * @returns 
 */
const formatAsyncRoutes = (asyncRoutes) => {
  const menusMap = {};
  // 将数组转换为键值对
  asyncRoutes.forEach(item => {
    menusMap[item.id] = item;
  })
  const result = [];
  asyncRoutes.forEach(item => {
    item.hidden = item.hidden ? true : false;
    item.alwaysShow = item.alwaysShow ? true : false
    item.component && (item.component = loadView(item.component));
    item.meta.length !== 0 && (item.meta = JSON.parse(item.meta))

    let parent = menusMap[item.pid];
    if (parent) {
      // 下面reusult数组push进去对象实际是一次引用传递的过程,所以改变原对象可以影响到之后的数组
      (parent.children || (parent.children = [])).push(item);
    } else {
      result.push(item);
    }
  });
  return result;
}

import { constantRouterMap } from "@/router";

const permission = {
  state: {
    routers: constantRouterMap,
    addRouters: []
  },
  mutations: {
    SET_ROUTERS: (state, routers) => {
      state.addRouters = routers;
      state.routers = constantRouterMap.concat(routers);
    }
  },
  actions: {
    GenerateRoutes ({ commit }, data) {
      return new Promise(resolve => {
        //#region  动态路由
        // 将生成数组树结构的菜单并拼接404路由
        const resultAsyncRoutes = formatAsyncRoutes(demoAsyncRoutes).concat(notFoundRoutes)
        commit("SET_ROUTERS", resultAsyncRoutes);
        //#endregion
        resolve();
      });
    }
  }
};

permission.js

import store from "./store";
import router from "./router";

router.beforeEach((to, from, next) => {
    store.dispatch("GenerateRoutes").then(() => {
              router.addRoutes(store.getters.addRouters); // 动态添加可访问路由表
              next({ ...to, replace: true }); // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
            });
    })