zoukankan      html  css  js  c++  java
  • 【转】vue 动态路由实现

    方案一:
    运用场景:系统根据登录账号的权限,动态加载左侧菜单; 后端直接返回的路由菜单数据

    实现:

    1.获取的数据格式

    • 一级目录component: ”components/main”
    • 二级目录component: ”components/parentView”
    • 目录下菜单component: "view/organization/user/user1.vue", //实际指向而定
    •  path值不可有重复指向


    ------------

    2.将后端树转化为路由树( libs/util.js )

    •  递归树
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //动态路由
    /**
     * @description 将后端菜单树转换为路由树 递归树
     * @param {Array} menus
     * @returns {Array}
     */
    export const backendMenusToRouters = (menus) => {
      let routers = []
      forEach(menus, (menu) => {
        // 将后端数据转换成路由数据
        let route = backendMenuToRoute(menu)
        // 如果后端数据有下级,则递归处理下级
        if (menu.children && menu.children.length !== 0 && menu.name != "") {
          route.children = backendMenusToRouters(menu.children)
        }
        routers.push(route)
      })
      // console.log(routers)
      return routers
    }
     

    • 处理树
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    /**
     * @description 将后端菜单转换为路由 处理树
     * @param {Object} menu
     * @returns {Object}
     */
    const backendMenuToRoute = (menu) => {
      // 具体内容根据自己的数据结构来定,这里需要注意的一点是
      // 原先routers写法是component: () => import('@/view/error-page/404.vue')
      // 经过json数据转换,这里会丢失,所以需要按照上面提过的做转换,下面只写了核心点,其他自行处理
      let route = Object.assign({}, menu)
      // route.component = () => import(`/* webpackChunkName: ${menu.title} */'@/${menu.component}'`)
     
      // 菜单配置的时候都是矢量图标库里面的图标所以要加上iconfont,ivew-admin里面都是默认的font-family:'Ionicons'=>转成 font-family:'iconfont'
      if(menu.meta){
        route.meta.icon = `iconfont ${menu.meta.icon}`
      //矢量图标转划
       
      if(menu.component == "components/main"){
        route.component = Main;  //一级菜单判断
      }
      else if(menu.component == "components/parentView"){
        route.component = parentView; //二级菜单判断
      }
      else{
        route.component  = () =>  import(`@/${menu.component}`);
      }
      return route
    }
     


    • 左侧菜单数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    ///////////////动态路由
    /**
     * @param {Array} list 通过路由列表得到菜单列表
     * @param access
     * @returns {Array}
     */
    export const getUserMenuByRouter = (list) => {
      // console.log(list)
      let res = []
      forEach(list, item => {
     
        //meta没配置,或者配置了,但hideInMenu=false
        if (!item.meta || (item.meta && !item.meta.hideInMenu)) {
          let obj = {
            icon: (item.meta && item.meta.icon) || '',
            name: item.name,
            meta: item.meta
          }
          //有下级子元素或者showAlways=true并且还有权限访问,继续递归处理下级(item.meta && !item.meta.showAlways)
          if ((hasChild(item))) {
            obj.children = getUserMenuByRouter(item.children)
          }
          //如果配置了href,设置href
          if (item.meta && item.meta.href) obj.href = `${baseUrl}`+`${item.meta.href}`
          //加入
          res.push(obj)
        }
      })
      return res
    }
     

    • 后台管理踩过的坑
    2.1菜单配置使用的是矢量图标库里面的图标所以要加上和iview图标font-family属性不一致;ivew-admin里面都是默认的font-family:'Ionicons'=>转成 font-family:'iconfont'
      *解决* :这里不可以全局修改属性值,在个别使用到的地方做css的修改。

     2.2对一级菜单、二级菜单的component参数进行分别判断
     
     

    3.全局管理并缓存路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    // 获取用户相关信息
        getUserInfo ({ state, commit }) {
          let paramer ={
            token:state.token,
            refresData:false,
          }
          // console.log(state.token)
          return new Promise((resolve, reject) => {
            try {
              getUserInfo(paramer).then(res => {
                // console.log(res)
                const data = res.data.result
                // console.log("菜单",JSON.stringify(data.menuRoutes))
                // commit('setAvatar', data.avatar) //头像
     
                commit('setUserName', data.loginName)
                commit('setUserInfo', data)
                commit('setAccess', data.roleInfoDto.roleName) //权限 角色
                commit('setHasGetInfo'true)
                let routers = backendMenusToRouters(data.menuRoutes)
                console.log(routers)
                commit('setRouters', routers)
                commit('setHasGetRouter'true)
     
                localStorage.setItem("getInfo"true);
                localStorage.setItem("router", JSON.stringify(data.menuRoutes));
                resolve(data)
              }).catch(err => {
                reject(err)
              })
            catch (error) {
              reject(error)
            }
          })
        },
     
     

    4.重置路由,在最后添加404页面路径

    获取到缓存里的数据,在until.js中的方法转化后得到路由数据,最后加上404页面路径并重置路由
    *(InitRouters中的业务逻辑,根据实际情况进行修改)*

    • 除了考虑正常跳转,浏览器刷新,不存在的页面地址都需考虑
    • Tip:浏览器刷新后,路由中的name值可能会消失

    *根据缓存中的name值去循环判断(这里name和path在配置的时候是一致的)...实际情况判断...*

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    ```html
    // version2
    const initRouters = (to, next) => {
     
      // 在路由最后动态添加404, path:"*"
      let rou = JSON.parse(localStorage.getItem("router"));
      let routers = backendMenusToRouters(rou);
      routers.push({
        path: '*',
        name: 'error_404',
        meta: {
          hideInMenu: true
        },
        component: () => import('@/view/error-page/404.vue')
      })
      router.addRoutes(routers);
     
      let arr = [];
      let names2 = [];
      let names = getAllNames(rou,arr);
      names2= names.concat(["login","home","error_401","error_500"]);
      console.log(names2)
     
      let path = to.path;
      let path1 = path.substr(path.lastIndexOf('/') + 1);
      console.log(path1)
      console.log(names2.includes(path1))
     
     
      if (to.matched.length > 0 ) {
        next()
      }
      else if (to.name !== null && to.matched.length === 0) {
        next({
          name: 'error_404'
        })
        return
      }
      else {
        next({
          name: path1
        })
      }
    }
     

    Tip: 路由陷入无线循环的情况:


     
  • 相关阅读:
    mysql数据库——特殊sql语句整理之修改表结构
    mysql数据库引擎——MyISAM,InnoDB
    Golang程序性能分析
    Docker基本操作
    go module
    Docker基础原理
    基于zookeeper或redis实现分布式锁
    漫谈微服务
    快速排序
    设计模式泛谈
  • 原文地址:https://www.cnblogs.com/cyqdeshenluo/p/12176387.html
Copyright © 2011-2022 走看看