天天看點

使用Vue寫背景管理系統踩坑記-動态路由

本來很簡單的一件事,給背景差點弄得當場去世。

前言:動态路由就是根據一個json格式的數組來生成Vue中的路由表,實作動态注冊。可以前端自己實作,也可以使用背景傳回的路由表來生成。

坑1-----------------------------前背景字段不比對

坑2-----------------------------轉換使用異步思想,如果沒有添加路由資訊便開始跳轉,則會出現頁面跳轉空白和404

說起這個,弄得自己差點就懷疑人生(唉,忍住忍住,不是背景的鍋),先說明一下,路由裡面,最基礎的字段有path,name,component,meta。其中path為跳轉時浏覽器顯示的路徑,name為辨別,component為該path對應的元件路徑,meta為路由攜帶的資訊,裡面可以定義任意的字段。使用的最重要的方法為addRoutes()。

要實作一個動态路由有如下幾個步驟:

1.準備一個路由表

2.将路由表轉換為自己想要的資料格式

3.使用addRoutes添加

由于添加路由addRoutes是一個異步的方法,是以可以配個async和await來使用,效果更好。

1.準備路由表:

leftMenu: [{
        "title": "工作台",
        "path": "/home",
        "icon": "el-icon-house",
        "children": [],
        id: 1
      }, {
        "title": "借貸管理",
        "path": "/debitManage",
        "icon": "el-icon-money",
        id: 1,
        "children": [{
            "title": "送出借款初審",
            "path": "/debitManage/firstCon",
            id: 1,
            "children": [{
              "title": "新增借款",
              "path": "/debitManage/addDebitItem",
              name: "addDebitItem",
              id: 1,
              "children": []
            }, {
              "title": "新标維護",
              "path": "/debitManage/upholdNewBidItems",
              name: "addDebitItem",
              id: 1,
              "children": []
            }, {
              "title": "借款稽核",
              "path": "/debitManage/firstDebitCon",
              name: "addDebitItem",
              id: 1,
              "children": []
            }]
          }, {
            "title": "上架管理",
            id: 1,
            "path": "/debitManage/secondCon",
            "children": [{
              "title": "上架初審",
              "path": "/debitManage/firstLevelAuditItems",
              name: "addDebitItem",
              id: 1,
              "children": []
            }, {
              "title": "标的上架",
              "path": "/debitManage/secondBidsGroundItems",
              name: "addDebitItem",
              id: 1,
              "children": [

              ]
            }, {
              "title": "進行中的标管理",
              "path": "/debitManage/runningBidsItems",
              name: "addDebitItem",
              id: 1,
              "children": []
            }]
          },
          {
            "title": "複審管理",
            "path": "/debitManage/thridCon",
            id: 1,
            "children": [{
              "title": "滿标複審",
              "path": "/debitManage/reexAuditItems",
              name: "addDebitItem",
              id: 1,
              "children": []
            }]
          }, {
            "title": "記錄檢視",
            "path": "/debitManage/viewLogs",
            id: 1,
            "children": [{
              "title": "所有借款标",
              "path": "/debitManage/debitItems",
              name: "addDebitItem",
              id: 1,
              "children": []
            }, {
              "title": "投資記錄",
              "path": "/debitManage/investRecords",
              name: "addDebitItem",
              id: 1,
              "children": []
            }]
          }, {
            "title": "管理标類别",
            "path": "/debitManage/manageCategory",
            id: 1,
            "children": [{
              "title": "借款标類别",
              "path": "/debitManage/debitCategory",
              name: "addDebitItem",
              id: 1,
              "children": []
            }]
          }

        ]
      }, {
        "title": "還款管理",
        "path": "/repaymentManage",
        "icon": "el-icon-bank-card",
        id: 1,
        "children": [{
            "title": "管理還款項",
            "path": "/repaymentManage/repaymentItems",
            name: "addDebitItem",
            id: 1,
            "children": []
          },
          {
            "title": "還款記錄",
            "path": "/repaymentManage/repaymentRecords",
            name: "addDebitItem",
            "children": [],
            id: 1
          }
        ]
      }, {
        "title": "資金管理",
        "path": "/capitalManage",
        id: 1,
        "icon": "el-icon-wallet",
        "children": [{
          "title": "記錄檢視",
          "path": "/capitalManage/logsView",
          id: 1,
          "children": [{
            "title": "充值記錄",
            "path": "/capitalManage/reChargeLog",
            id: 1,
            name: "addDebitItem",
            "children": []
          }, {
            "title": "提現記錄",
            "path": "/capitalManage/withDrawMoneyLog",
            id: 1,
            name: "addDebitItem",
            "children": []
          }, {
            "title": "使用者資金",
            "path": "/capitalManage/userCapital",
            name: "addDebitItem",
            id: 1,
            "children": []
          }, {
            "title": "平台資金",
            "path": "/capitalManage/platformCapitalLog",
            name: "addDebitItem",
            id: 1,
            "children": []
          }]
        }, {
          "title": "相關稽核",
          "path": "/capitalManage/withDrawConfirm",
          id: 1,

          "children": [{
            "title": "提現稽核",
            "path": "/capitalManage/withDrawCon",
            id: 1,
            name: "addDebitItem",
            "children": []
          }]
        }]
      }, {
        "title": "會員管理",
        "path": "/memberManage",
        "icon": "el-icon-user-solid",
        id: 1,
        "children": [{
            "title": "新增借款使用者",
            "path": "/memberManage/addDebitUser",
            name: "addDebitItem",
            id: 1,
            "children": []
          },
          {
            "title": "投資使用者管理",
            "path": "/memberManage/investUserManage",
            name: "addDebitItem",
            id: 1,
            "children": []
          },
          {
            "title": "借款使用者管理",
            "path": "/memberManage/debitUserManage",
            name: "addDebitItem",
            id: 1,
            "children": []
          }
        ]
      }, {
        "title": "内容管理",
        "path": "/contentManage",
        "icon": "el-icon-s-grid",
        id: 1,
        "children": [{
            "title": "輪播管理",
            "path": "/contentManage/sliderManage",
            id: 1,
            "children": [{
              "title": "新增輪播",
              "path": "/contentManage/sliderManage/addSliderItem",
              "children": [],
              id: 1
            }]
          },
          {
            "title": "文章管理",
            "path": "/contentManage/articleManage",
            id: 1,
            "children": [{
              "title": "新增文章",
              "path": "/contentManage/articleManage/addArticleItem",
              "children": [],
              id: 1,
            }]
          },
          {
            "title": "意見回報",
            "path": "/contentManage/feedBack",
            id: 1,
            "children": [{
              "title": "回報處理",
              "path": "/contentManage/feedbackManage/addFeedbackItem",
              "children": [],
              id: 1,
            }, ]
          }
        ]
      }]
           

分割線:

2.在cli搭建的項目的router檔案夾中替換index.js檔案,建立一個index.js檔案,在裡面寫預先定義好的靜态路由,并且寫上初始化路由的方法

router/index.js

import Vue from "vue";
import Router from "vue-router";
// import Layout from "@/Layout";
import Login from "@/views/Login";
Vue.use(Router);
const originalPush = Router.prototype.push;
Router.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err);
};

export const constRouters = [
  {
    path: "/login",
    component: Login,
    hidden: true,
    name: "login"
  }
];
const createRouter = () =>
  new Router({
    // mode: "history",
    routes: constRouters
  });

const index = createRouter();
export function resetRouter() {
  const newRouter = createRouter();
  index.matcher = newRouter.matcher;
}
export const errorRoute = [
  {
    path: "*",
    component: () => import("@/views/NotFound"),
    hidden: true,
    name: "pageNotFound",
    meta: { title: "頁面不見啦" }
  },
  {
    name: "login",
    path: "/login",
    component: () => import("../views/Login")
  }
];
export default index;
           

3.建立一個轉化路由格式的檔案:asyncRouter.js

//引入公共路由
export function getAsyncRoutes(routes) {
  const res = [];
  const keys = ["path", "name", "children"];
  if (Array.isArray(routes)) {
    routes.forEach(v => {
      const newItem = {};
      if (v.path) {
        //如果m有children,代表它是二級或者n級菜單,不引入元件
        if (v.children && v.children.length > 0) {
          newItem.component = () => "";
        } else if (v.path === "/") {
          newItem.component = newItem.component = resolve => {
            require([`@/views${v.component}`], resolve);
          };
        } else {
          //如果不是,就替換
          newItem.component = resolve => {
            require([`@/views${v.path}/index`], resolve);
          };
        }
      }
      for (const key in v) {
        if (keys.includes(key)) {
        //由于沒有name字段不合規範,隻能替換name字段為path後面的一截
          if (key === "name") {
            newItem["name"] = v["path"].split("/")[
              v["path"].split("/").length - 1
            ];
          } else {
            newItem[key] = v[key];
            //生成标題字段
            newItem.meta = { title: v.name };
          }
          // } else {
          //   newItem["name"] = v["path"].split("/")[
          //     v["path"].split("/").length - 1
          //   ]+v.id;
          // }
        }
      }
      if (v.path) { 
        if (
          (v.children == null || v.children.length === 0) &&
          v.path !== "/home"
        ) {
          newItem["name"] = v["path"].split("/")[
            v["path"].split("/").length - 1
          ];
        }
      }
//如果有children,使用遞歸繼續調用該方法,實作任意深度都可以轉換
      if (newItem.children && newItem.children.length) {
        newItem.children = getAsyncRoutes(v.children);
      }
      res.push(newItem);
    });
  }
  return res;
}
           

4.與index.js同級建立一個permission.js檔案,攔截并處理跳轉加載

import router from "../router";
import store from "../store";
import NProgress from "nprogress";
// console.log(store);
import "nprogress/nprogress.css";
import { resetRouter } from "../router";
import { getAsyncRoutes } from "@/router/asyncRouter";
import { errorRoute } from "../router";
const whileList = ["/login"];
var update = true;
router.beforeEach(async (to, from, next) => {
  NProgress.start();
  document.title = to.meta.title;
  //擷取token
  const hasToken = (await store.getters.getToken) !== "";
  // console.log("token:" + hasToken);
  if (hasToken) {
    // console.log("有沒有token", hasToken);
    if (to.path === "/login" || to.path === "/") {
      next({ path: "/home" });
    } else {
      //擷取vuex中儲存的路由
      let hasRoutes = await store.getters.getState;
      //如果有路由則直接下一步,如果沒有則需要擷取路由
      //判斷是不是點選重新整理
      // console.log("是否有路由", hasRoutes);
      if (hasRoutes && !update) {
        next();
      } else {
        try {
          await resetRouter();
          const accessRoutes = getAsyncRoutes(await store.getters.getRoutes);
          let changedRoutes = accessRoutes.filter(v => {
            return v.path !== "/home/index";
          });
          //---------------------------------------------------===================================================
          //-----------------------------------------------------================================================
          //在這個下面添加自定義的路由,不要在index.js裡面添加
          let extraRoutes = [
            //在這裡寫額外的路由
            {
              path: "/sliderManage/sliderManage/addSlider",
              name: "addSlider",
              meta: { title: "新增輪播" },
              component: () =>
                import(
                  "../views/contentManage/sliderManage/sliderManage/addSlider"
                )
            },
            {
              path: "/articleManage/articleManage/addArticle",
              name: "addArticle",
              meta: { title: "新增文章" },
              component: () =>
                import(
                  "../views/contentManage/articleManage/articleManage/addArticle"
                )
            },
            {
              path: "/debitManage/Maintenance",
              name: "Maintenance",
              meta: { title: "上架複審" },
              component: () =>
                import("../views/debitManage/secondBidsGroundItems/Maintenance")
            },
            {
              path: "/debitManage/RecheckAction",
              name: "RecheckAction",
              meta: { title: "複審" },
              component: () =>
                import("../views/debitManage/reexAuditItems/RecheckAction")
            },
            {
              path: "/",
              component: () => import("@/views/home/index"),
              meta: { title: "工作台" },
              name: "index"
            },
            {
              path: "/Pwd",
              name: "Pwd",
              meta: { title: "密碼管理" },
              component: () =>
                import("@/components/rest/investUserManage/Pwd.vue")
            },
            {
              path: "/luser3",
              name: "luser3",
              component: () =>
                import("@/components/rest/debitUserManage/luser3")
            },
            {
              path: "/Modify",
              name: "Modify",
              component: () =>
                import("@/components/rest/investUserManage/Modify")
            },
            {
              path: "/luser4",
              name: "luser4",
              component: () =>
                import("@/components/rest/debitUserManage/luser4")
            }
          ];
          changedRoutes.push(...extraRoutes);
          //console.log("轉換後的路由:", changedRoutes);
          await router.addRoutes([
            {
              path: "/home",
              component: () => import("@/Layout/Home"),
              name: "home",
              children: changedRoutes
            }
          ]);
          await store.commit("setNoRefresh");
          update = false;
          next({ ...to, replace: true });
        } catch (error) {
          next(`/login?redirect=${to.path}`);
          console.log(error);
          NProgress.done();
        }
      }
    }
  } else {
    // console.log(to.path);
    if (whileList.indexOf(to.path) !== -1) {
      next();
    } else {
      next({ path: `/login` });
      NProgress.done();
    }
  }
});
router.afterEach(async () => {
  NProgress.done();
  // console.log("導航結束");
});
           

檔案中定義了路由的全局前置守衛,當有token并且目前已經生成路由表時才放行;如果沒有路由表則要調用getAsyncRoutes方法;

注意:1.檔案中引入nprogress插件,頁面加載條,啟動使用start方法,停止使用done方法2.頁面重新整理時,路由資訊會被清空,需要重新添加3.使用一個狀态值來說明是否已經存在路由資訊4.addRoutes隻接受數組,最好使用解構然後指派;

繼續閱讀