jeecg-boot vue2版本项目菜单路由问题修复

1,618 阅读5分钟

问题描述(jeecg-boot vue2版本)

  1. 三级路由无法缓存

    解决:查看JEECG技术论坛,找到了遇到同问题的帖子

    permission.js文件,beforeEach中需要增加以下代码

        router.beforeEach((to,from,next)=>{ 
           if (to.matched && to.matched.length > 3) { 
            to.matched.splice(2, to.matched.length - 3) 
           } 
        })
    

    多级路由缓存修改后三级菜单点击展开问题

    以上的代码解决了三级菜单无法缓存的问题,但是会引发另一个问题出现:配置菜单权限时,可以配置聚合路由,聚合路由的界面会显示异常,比如: 个人页——个人设置

  2. 聚合路由 界面显示异常问题

    在代码中找到,是否为聚合路由是由 ‘alwaysShow’ 字段控制的,ture 是,false 否

    user.js文件

    获取用户信息接口中,拿到menuData数据,对menuData数据进行处理

    原代码处理逻辑:

    menuData.forEach((item, index) => { 
        if (item['children']) { 
            const hasChildrenMenu = item['children'].filter(i => { 
                return !i.hidden || i.hidden === false
            }) 
            if (hasChildrenMenu == null || hasChildrenMenu.length === 0) { 
                // item['hidden'] = true 
                item['children'].forEach(childrenItem => { 
                    menuData.push(childrenItem) 
                }) 
                item['children'] = null 
            } 
        } 
    })
    

    修改后的代码处理逻辑:

    
    // 递归调用方法 
    const menuHandle = function(menuData) { 
        menuData.forEach((item, index) => { 
            if (item['children']) { 
                // 返回子级菜单不是隐藏路由列表 
                const hasChildrenMenu = item['children'].filter(i => { 
                    return !i.hidden || i.hidden === false 
                }) 
                // 如果子级菜单全部是隐藏路由 
                if (hasChildrenMenu == null || hasChildrenMenu.length === 0) {
                    item['children'].forEach(childrenItem => { 
                        // 隐藏路由无法定位,将父级的path赋值 
                        childrenItem.selectedKeys = [item.path] 
                        
                        // 子级路由增加字段“isAlwaysShow” 
                        childrenItem.meta.isAlwaysShow = item.alwaysShow
                        menuData.push(childrenItem) 
                    }) 
                    item['children'] = null 
                } else { 
                    // 如果子级菜单中有不是隐藏的路由 
                    // 判断当前菜单是否是否为聚合路由,如果是给所有子级增加字段: ‘isAlwaysShow’ 
                    if (item.alwaysShow) { 
                        item['children'].forEach(childrenItem => { 
                            childrenItem.meta.isAlwaysShow = item.alwaysShow 
                        }) 
                    } 
                    
                    menuHandle(item['children']) 
                } 
            } 
        }) 
    } 
    
    menuHandle(menuData)
    
    

    这样处理之后,发现在 router.beforeEach 中无法 拿到 isAlwaysShow字段。

    在util.js 中 generateChildRouters() 方法中,生成路由

        const menu = { 
            path: item.path, 
            name: item.name, 
            redirect: item.redirect, 
            component: componentPath, 
            hidden: item.hidden, 
            // component:()=> import(`@/views/${item.component}.vue`), 
            meta: { 
                title: item.meta.title, 
                icon: item.meta.icon, 
                url: item.meta.url, 
                permissionList: item.meta.permissionList, 
                keepAlive: item.meta.keepAlive, 
                /* update_begin author:wuxianquan date:20190908 for:赋值 */ 
                internalOrExternal: item.meta.internalOrExternal, 
                /* update_end author:wuxianquan date:20190908 for:赋值 */ 
                // 是否为隐藏路由 
                hidden: item.hidden, 
                // 是否为聚合路由 
                isAlwaysShow: item.meta.isAlwaysShow, 
                // 定位path 
                selectedKeys: item.selectedKeys 
            } 
        }
    

    然后在permission.js 中继续修改

    // 截取的时候,过滤掉聚合路由信息 
    if (!to.meta.isAlwaysShow && to.matched && to.matched.length > 3) { 
        to.matched.splice(2, to.matched.length - 3) 
    }
    

    原以为这样处理之后就万事大吉了,结果: 三级菜单无法展开,查看代码,因为在上面把to.matched数据截掉了,导致,在每次更新路由时,找不到展开的项

  3. 三级无法展开

    在permission.js文件中,存下原有的 matched 数据

    router.beforeEach((to,from,next)={ 
        // 引用类型数据不能直接赋值 
        to.meta['matched'] = to.matched.filter(e => { 
            return e 
        }) 
        if (!to.meta.isAlwaysShow && to.matched && to.matched.length > 3) { 
            to.matched.splice(2, to.matched.length - 3) 
        } 
    })
    

    这样改了之后,就需要在更新时,获取值的地方改一下数据来源

    在 menu 文件夹下的index.js文件中

    updateMenu() { 
        // const routes = this.$route.matched.concat() // 原本获取展开的数据源 
        const routes = this.$route.meta['matched'] // 修改之后的数据来源 
        const { hidden, selectedKeys } = this.$route.meta 
        // 如果当前路由是隐藏路由,就需要使用之前处理的 ‘selectedKeys’ 
        if (hidden) { 
            routes.pop() this.selectedKeys = selectedKeys 
        } else { 
            this.selectedKeys = [routes.pop().path] 
        } 
        const openKeys = [] 
        if (this.mode === 'inline') { 
            routes.forEach(item => { 
                openKeys.push(item.path) 
            }) 
        } 
        // 其他代码 
    },
    
  4. 三级隐藏路由,父级显示异常

    与聚合问题一起处理的,原来代码逻辑是只处理了一层关系,没有处理多级,修改之后,使用递归处理更深层次。

    ‘selectedKeys’ 是处理定位关系。

    // 递归调用方法 
    const menuHandle = function(menuData) { 
        menuData.forEach((item, index) => { 
            if (item['children']) { 
                // 返回子级菜单不是隐藏路由列表 
                const hasChildrenMenu = item['children'].filter(i => { 
                    return !i.hidden || i.hidden === false 
                }) 
                // 如果子级菜单全部是隐藏路由 
                if (hasChildrenMenu == null || hasChildrenMenu.length === 0) {
                    item['children'].forEach(childrenItem => { 
                        // 隐藏路由无法定位,将父级的path赋值 
                        childrenItem.selectedKeys = [item.path] 
                        
                        // 子级路由增加字段“isAlwaysShow” 
                        childrenItem.meta.isAlwaysShow = item.alwaysShow
                        menuData.push(childrenItem) 
                    }) 
                    item['children'] = null 
                } else { 
                    // 如果子级菜单中有不是隐藏的路由 
                    // 判断当前菜单是否是否为聚合路由,如果是给所有子级增加字段: ‘isAlwaysShow’ 
                    if (item.alwaysShow) { 
                        item['children'].forEach(childrenItem => { 
                            childrenItem.meta.isAlwaysShow = item.alwaysShow 
                        }) 
                    } 
                    
                    menuHandle(item['children']) 
                } 
            } 
        }) 
    } 
    
    menuHandle(menuData)
    

    这样处理之后,目前测试正常可用的

其他尝试:

看到jeecg-boot vue2版本 演示系统中三级是显示正常的,前端Vue2源码 发现处理方式不同:

premission.js文件


router.beforeEach((to, from, next) => {
  //update-begin---author:scott ---date:2022-10-13  for:[jeecg-boot/issues/4091]多级路由缓存问题 #4091-----------
  //解决三级菜单无法缓存问题
  //参考: https://blog.csdn.net/qq_37322135/article/details/126013301
  //参考: https://blog.csdn.net/cwin8951/article/details/106644118
  if (to.matched && to.matched.length>3) {
    to.matched.splice(2, to.matched.length - 3)
  }
  //update-end---author:scott ---date::2022-10-13  for:[jeecg-boot/issues/4091]多级路由缓存问题 #4091--------------
  
  
  NProgress.start() // start progress bar

  if (Vue.ls.get(ACCESS_TOKEN)) {
    /* has token */
    if (to.path === '/user/login' || to.path === OAUTH2_LOGIN_PAGE_PATH) {
      next({ path: INDEX_MAIN_PAGE_PATH })
      NProgress.done()
    } else {
      if (store.getters.permissionList.length === 0) {
        store.dispatch('GetPermissionList').then(res => {
              const menuData = res.result.menu;
              //console.log(res.message)
              if (menuData === null || menuData === "" || menuData === undefined) {
                return;
              }
              let constRoutes = [];
              constRoutes = generateIndexRouter(menuData);
              // 添加主界面路由
              store.dispatch('UpdateAppRouter',  { constRoutes }).then(() => {
                // 根据roles权限生成可访问的路由表
                // 动态添加可访问路由表
                router.addRoutes(store.getters.addRouters)
                const redirect = decodeURIComponent(from.query.redirect || to.path)
                if (to.path === redirect) {
                  // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
                  next({ ...to, replace: true })
                } else {
                  // 跳转到目的路由
                  next({ path: redirect })
                }
              })
            })
          .catch(() => {
           /* notification.error({
              message: '系统提示',
              description: '请求用户信息失败,请重试!'
            })*/
            store.dispatch('Logout').then(() => {
              next({ path: '/user/login', query: { redirect: to.fullPath } })
            })
          })
      } else {
        next()
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免登录白名单,如果进入的页面是login页面并且当前是OAuth2app环境,就进入OAuth2登录页面
      if (to.path === '/user/login' && isOAuth2AppEnv()) {
        next({path: OAUTH2_LOGIN_PAGE_PATH})
      } else {
        // 在免登录白名单,直接进入
        next()
      }
      NProgress.done()
    } else {
      // 如果当前是在OAuth2APP环境,就跳转到OAuth2登录页面
      let path = isOAuth2AppEnv() ? OAUTH2_LOGIN_PAGE_PATH : '/user/login'
      next({ path: path, query: { redirect: to.fullPath } })
      NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
    }
  }
})

看代码可以看到,这相当于是三级无法缓存的问题修复。

user.js 文件

// 获取用户信息
    GetPermissionList({ commit }) {
      return new Promise((resolve, reject) => {
        queryPermissionsByUser().then(response => {
          
          // update-begin----author:scott---date:20221018------for: 判断是否是 vue3 版本的菜单,给予提示 ---
          let routeList = response.result.menu;
          var findVue3Menu = routeList.find(item => {
            return item.component === 'layouts/default/index';
          });
          if (findVue3Menu) {
            console.error("启动失败: 检查到当前菜单表是Vue3版本,导致菜单加载异常,请切换到Vue2版菜单!参考:http://doc.jeecg.com/3075165")
            Vue.prototype.$Jmessage.error('启动失败: 检查到当前菜单表是Vue3版本,导致菜单加载异常,请切换到Vue2版菜单!参考:http://doc.jeecg.com/3075165', 0)
          }
          // update-end----author:scott---date:20221018------for: 判断是否是 vue3 版本的菜单,给予提示 ---
          
          
          const menuData = response.result.menu;
          const authData = response.result.auth;
          const allAuthData = response.result.allAuth;
          //Vue.ls.set(USER_AUTH,authData);
          sessionStorage.setItem(USER_AUTH,JSON.stringify(authData));
          sessionStorage.setItem(SYS_BUTTON_AUTH,JSON.stringify(allAuthData));
          if (menuData && menuData.length > 0) {
            // //update--begin--autor:qinfeng-----date:20200109------for:JEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
            // menuData.forEach((item, index) => {
            //   if (item["children"]) {
            //     let hasChildrenMenu = item["children"].filter((i) => {
            //       return !i.hidden || i.hidden == false
            //     })
            //     if (hasChildrenMenu == null || hasChildrenMenu.length == 0) {
            //       item["hidden"] = true
            //     }
            //   }
            // })
            // //console.log(" menu show json ", menuData)
            // //update--end--autor:qinfeng-----date:20200109------for:JEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
            commit('SET_PERMISSIONLIST', menuData)
            // 设置系统安全模式
            commit('SET_SYS_SAFE_MODE', response.result.sysSafeMode)
          } else {
            reject('getPermissionList: permissions must be a non-null array !')
          }
          resolve(response)
        }).catch(error => {
          reject(error)
        })
      })
    },

上面的代码,没有对获取到的menu数据进行处理

menu文件夹下的index.js文件

updateMenu () {
      const routes = this.$route.matched.concat()
      const { hidden } = this.$route.meta
      if (routes.length >= 3 && hidden) {
        routes.pop()
        this.selectedKeys = [routes[routes.length - 1].path]
      } else {
        this.selectedKeys = [routes.pop().path]
      }
      let openKeys = []
      if (this.mode === 'inline') {
        routes.forEach(item => {
          openKeys.push(item.path)
        })
      }

      // update-begin-author:sunjianlei date:20210409 for: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题
      // 包含冒号的是动态菜单
      if (this.selectedKeys[0].includes(':')) {
        let selectedKey = this.$route.fullPath
        this.selectedKeys = [selectedKey]
        let newOpenKeys = []
        this.fullOpenKeys(this.menu, selectedKey, newOpenKeys)
        if (newOpenKeys.length > 0) {
          openKeys = newOpenKeys.reverse()
        }
      }
      // update-end-author:sunjianlei date:20210409 for: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题

      //update-begin-author:taoyan date:20190510 for:online表单菜单点击展开的一级目录不对
      if(!this.selectedKeys || this.selectedKeys[0].indexOf(":")<0){
        this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
      }
      //update-end-author:taoyan date:20190510 for:online表单菜单点击展开的一级目录不对
    },
    // update-begin-author:sunjianlei date:20210409 for: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题
    // 递归查找当前选中的菜单和父级菜单,填充openKeys
    fullOpenKeys(menus, selectedKey, openKeys) {
      for (let item of menus) {
        if (item.path === selectedKey) {
          openKeys.push(item.path)
          this.$emit('updateMenuTitle', item)
          return true
        } else if (Array.isArray(item.children)) {
          if (this.fullOpenKeys(item.children, selectedKey, openKeys)) {
            openKeys.push(item.path)
            return true
          }
        }
      }
    },

先拿到当前的路由信息,再去这是递归去处理,找展开项。

尝试过,这样处理之后,还是会有问题,二级隐藏路由会无法缓存,具体原因未查出,也无法在演示系统中增加隐藏路由。尝试之后还是无法满足我的需求,所以就果断放弃,使用自己方式。