Vue3:动态路由+子页面(新增、详情页)动态路由配置(代码全注释)

发布于:2024-05-24 ⋅ 阅读:(45) ⋅ 点赞:(0)

实现思路

   emm,项目中使用动态路由实现根据后端返回的用户详情信息,动态将该用户能够访问的页面信息,动态生成并且绑定到路由对象中。但是后端返回的不包含像是页面详情页,新增页的权限路由,如果前端在路由对象中写死的话,那每次进入页面都得判断是否将进入一个用户没有权限的页面,但是路由中有存在的。所以就想着自己能不能根据后端现有返回的信息,前端自己生成类似详情之类的动态路由。

调用后端接口获取用户页面权限动态绑定到路由对象中

1.获取用户权限

async function getUserAuthority(ids:any) {
   let userAuthority = null
   let NewList = null
    //定义请求参数
   let params = {
    id:ids,
     permission_tree:1
   }
   //请求用户的信息
   await get('/system/user/detail',params).then(res=>{
      if(res.status_code == 200){
        //userAuthority  存入查询到该用户信息 
        userAuthority = res.data
        //模拟该页面是需要跳转其他项目的地址
        let list =[{
          id: 119,
          children:[{
             id:1191,
             children:[],
             parent_id:119,
             name :'审单管理',
             slug:'web-system-exchangegoods-management',
             web_path : `/gongdan`,
             links:'https://blog.csdn.net/qq_45061461?type=lately'
          }],
          slug:'web-system-examineadocument',
          web_icon:'el-icon-coin',
          name :'审单管理',
          web_path: null,
          },
          {
            id: 117,
            children:[{
              children:[],
              id:1171,
              parent_id:117,
              slug:'web-system-exchangegoods-management',
              name :'换货留言列表',
              web_path : `/gongdan`,
              links:'https://so.csdn.net/so/search?q=vue3%3Amian,ts%E4%B8%AD%E8%8E%B7%E5%8F%96import.meta.glob&t=&u=&urw='
           }],
           slug:'web-system-exchangegoods',
           web_icon:'el-icon-coin',
           name :'换货留言列表',
           web_path: null,
            }
        ]
      
        //将后端返回的用户信息中的权限 和 自己定义的权限对象 合并一起
        NewList = userAuthority.permissions.concat(list);
        //将用户的权限信息保存本地中
        sessionStorage.setItem('NavList',JSON.stringify(NewList))
      }
   })
   return NewList
}

 2.用户登录请求信息 方法我就不写了

//调用定义的获取权限方法
proxy.$PublicAPI.getUserAuthority(list.user_id).then((res) => {
            //res就是上个方法中返回的权限信息 然后调用将路由添加入路由对象的方法
            proxy.$PublicAPI.getRouteAddList();
            //获取默认第一菜单的一个页面的路径进行跳转
            proxy.$PublicAPI.getOnePagePath();
          });

3.调用动态追加路由对象的方法

async function getRouteAddList() {
   <!--定义存放路由信息对象-->
  let parentRoute = null;
    <!--判断本地中是否存在权限信息-->
  if (sessionStorage.getItem('NavList')) {
    //list是 forEachRout方法返回的数据,将本地权限信息传入,根据信息生成路由对象格式数据
    let list =  forEachRout() 
    <!--router.getRoutes().find(route => route.name === 'index') 查找路由对象中的路由信息中name名为index的路由对象,name = index的路由对象path指向 index.vue 有显示页面元素的路由视图 <router-view /> -->
    parentRoute = router.getRoutes().find(route => route.name === 'index');
     <!--想自己生成的路由对象格式数据 追加到 /index中的chilrend中 -->
    list.forEach(item => {
      parentRoute.children.push(item);
    });
    <!--将定义好的路由数据格式对象添加到路由对象中-->
    router.addRoute(parentRoute);
  }
}

4.设计路由对象格式数据 forEachRout方法

function forEachRout  (){
  //存放已经设计好的路由对象
  let list = [] 
  //查找 ../../views/ 文件夹下所有的文件 views文件夹下是我存放的vue文件
  const module = import.meta.glob("../../views/**");
  <!-- 获取本地的权限信息 -->
  let params = JSON.parse(sessionStorage.getItem('NavList'));
  <!-- 循环遍历权限信息 -->
  params.forEach(item => {
  <!-- 判断权限信息中web_path 是否是为string,为string类型就是用户的页面权限 这是后端返回的 -->
    if (typeof item.web_path === 'string') {
    <!-- 设计路由对象格式信息 -->
      let routerChildrenOne = {
        path: item.web_path,
        name: `${item.web_path}`,
        component: module[`../../views${item.web_path}/index.vue`],
        meta: {
          title: item.name,
          buts: [],
          requireAuth: true,
          keepAlive: true,
          externalLink: item.links,
        }
      };
      <!-- 处理好的路由对象格式信息保存起来,后面存入路由对象中 -->
      list.push(routerChildrenOne);
      let butOne = item.slug;
      routerChildrenOne.meta.buts.push(butOne);
    } 
<!--  这个else 是判断是否需要跳转外部链接的对象,就是第一个截图中定义的两个权限数据对象 
-->
else {
      item.children.forEach(Citem => {
        let routerChildren = { 
           <!-- 判断对象中是否存在 links(跳转链接)属性,为存在的话,那么这个path将和其他同样存在links属性的对象公用一个页面(所有需要跳转外部的菜单都共同跳转这一个页面,所以这里使用了路由传参来确定点击他们不会只获取到最近的一个需要外部跳转的菜单,否则菜单中存在一个以上跳转外部菜单,无论点击哪一个都会跳转同一个外部链接。并菜单包含此path全部高亮),就会存在下方截图的错误-->
          path:Citem.links ? `${Citem.web_path}/:${Citem.id}` :  Citem.web_path ,
          name: `${Citem.web_path}`,
          component: module[`../../views${Citem.web_path}/index.vue`],
          meta: {
            title: Citem.name,
            buts: [],
            requireAuth: true,
            keepAlive: true,
            externallink:Citem.links
          }
        };
        list.push(routerChildren);
        Citem.children.forEach(C_item_C => {
          let but = C_item_C.slug;
          routerChildren.meta.buts.push(but);
        });
      });
    }
  });
  //返回定义好的路由格式对象
 return list
}

所以,在配置路由格式对象中外部跳转链接菜单通过动态路由传参来避免出现这个问题.到此菜单的动态路由已经配置完成。

动态添加子页面路由

但是我在想如果每个页面中都存在类似新增和详情的子页面,但是后端返回的信息中未包含,自己怎么也解决动态生成。于是就回到了,后端目前返回给我的权限信息,还有获取到veiws文件夹中的vue文件。

 1.后端返回的权限

2.veiws文件夹中已经存在的

3.实现,此方法就是 P4代码片段的方法 只不过增加了几个变量和调用了几个方法:

 新增变量名:Eligiblelimitsofauthority  , aa

 新增调用方法的方法名:UpdataRouterList 、GetPossibleDetails 

 已经底部retrun之前的判断对象是否存在component绑定的文件路由属性

function forEachRout  (){
  //存放已经设计好的路由对象
  let list = []
   <!-- 存放可能是新增页面,详情页面 和编辑页面的标识 -->
  let Eligiblelimitsofauthority = []
   <!-- 存放可能是新增页面,详情页面 和编辑页面的路由对象>
  let aa = [] 
  //查找 ../../views/ 文件夹下所有的文件 views文件夹下是我存放的vue文件
  const module = import.meta.glob("../../views/**");
  <!-- 获取本地的权限信息 -->
  let params = JSON.parse(sessionStorage.getItem('NavList'));
    
  <!-- 筛选出页面中出现可能是新增、详情或者编辑的信息 --> 
  UpdataRouterList(params,Eligiblelimitsofauthority)
  <!-- 根据传递过来的可能是详情或者编辑的信息,筛选出存在权限的数据,转换成路由格式对象 -->
  aa =  GetPossibleDetails(Eligiblelimitsofauthority,params,aa)


  <!-- 循环遍历权限信息 -->
  params.forEach(item => {
  <!-- 判断权限信息中web_path 是否是为string,为string类型就是用户的页面权限 这是后端返回的 -->
    if (typeof item.web_path === 'string') {
    <!-- 设计路由对象格式信息 -->
      let routerChildrenOne = {
        path: item.web_path,
        name: `${item.web_path}`,
        component: module[`../../views${item.web_path}/index.vue`],
        meta: {
          title: item.name,
          buts: [],
          requireAuth: true,
          keepAlive: true,
          externalLink: item.links,
        }
      };
      <!-- 处理好的路由对象格式信息保存起来,后面存入路由对象中 -- >
      list.push(routerChildrenOne);
      let butOne = item.slug;
      routerChildrenOne.meta.buts.push(butOne);
    } 
<!--  这个else 是判断是否需要跳转外部链接的对象,就是第一个截图中定义的两个权限数据对象 
-->
else {
      item.children.forEach(Citem => {
        let routerChildren = { 
           <!-- 判断对象中是否存在 links(跳转链接)属性,为存在的话,那么这个path将和其他同样存在links属性的对象公用一个页面(所有需要跳转外部的菜单都共同跳转这一个页面,所以这里使用了路由传参来确定点击他们不会只获取到最近的一个需要外部跳转的菜单,否则菜单中存在一个以上跳转外部菜单,无论点击哪一个都会跳转同一个外部链接。并菜单包含此path全部高亮),就会存在下方截图的错误-->
          path:Citem.links ? `${Citem.web_path}/:${Citem.id}` :  Citem.web_path ,
          name: `${Citem.web_path}`,
          component: module[`../../views${Citem.web_path}/index.vue`],
          meta: {
            title: Citem.name,
            buts: [],
            requireAuth: true,
            keepAlive: true,
            externallink:Citem.links
          }
        };
        list.push(routerChildren);
        Citem.children.forEach(C_item_C => {
          let but = C_item_C.slug;
          routerChildren.meta.buts.push(but);
        });
      });
    }
  });
   <!-- 判断转换成路由格式数据中component有数据的对象,component不为空的时候就是标识项目中已经创建了这个新增,详情 或编辑的页面-->
  if(aa){
     aa.map(item=>{
       if(item.component != undefined)
         list.push(item)
     })
  }
  //返回定义好的路由格式对象
 return list
}

2.UpdataRouterList方法 筛选出页面中出现可能是详情或者编辑的信息

//筛选出页面中出现可能是详情或者编辑的信息
function UpdataRouterList(navlist:any,Eligiblelimitsofauthority:any) {
   <!-- 循环传过来的本地权限数据对象中包含了 'create','edit' 和 'detail'的标识 -->
   navlist.map(item=>{
      if(item.slug.indexOf('create') != -1 || item.slug.indexOf('edit') != -1 || item.slug.indexOf('detail') != -1){
        <!-- 存入到可能包含新增,详情,编辑的标识数组中 -->
        Eligiblelimitsofauthority.push(item.slug)
      }
         <!-- 判断是否还有子数据,第一次循环的是下拉菜单 第二次循环的是下拉菜单中的点击跳转页面的菜单,第三次循环点击跳转页面的菜单中的 按钮权限,主要是拿到按钮权限菜单  这里是只有二级菜单,所以循环了三次,如果有多级也会一直循环下去,知道children中没有数据-->>
      if(item.children){
        UpdataRouterList(item.children,Eligiblelimitsofauthority)
      }
   })
}

3.GetPossibleDetails 根据传递过来的信息,筛选出存在权限的数据, 转换成路由信息,因为如果在本地中已经定义了全部的页面新增,详情,编辑等页面,但是咱们还是需要根据后端返回的权限中,判断咱们是否注册该页面的路由。

//根据传递过来的信息,筛选出存在权限的数据, 转换成路由信息 
function GetPossibleDetails(data:object,navlist:object,aa:any,) {
  <!-- 获取项目中views文件夹下的所有vue文件 -->
  const module = import.meta.glob("../../views/**");
  <!-- 遍历本地中的权限数据-->
   navlist.map(item=>{
   <!-- 传递过来的可能是 新增,详情,编辑的权限标识 -->
     data.map(ditem =>{
      <!-- 判断 item.web_path是否不为空,如果为空的话表明菜单是下拉菜单就不做操作 item.slug :这个菜单的标识 如:web-users 就是表示user(用户)菜单 -->
       if(item.web_path != null && ditem == `${item.slug}-create` || ditem == `${item.slug}-edit` ||  ditem == `${item.slug}-detail`){
        <!-- 存放定义的路由数据格式对象 -->
        let list = {}
        <!-- 截取item.we_path 菜单路径处理后的值,如:/user 我拿取user 后面作为/veiws/user,匹配views下的user文件夹 所以这里获取的是该菜单存放的文件夹名称 -->
        let path  = ''
        if(item.web_path != null){
           <!-- 截取文件夹名称 -->
          path = item.web_path.slice(item.web_path.lastIndexOf('/') + 1 , item.web_path.length)
           <!-- ditem是方法接收传递过来可能是新增,编辑,详情的权限标识数组,判断最后的-符号的后面是 'create':新增 ,'edit' :编辑 'detail':详情 -->
          switch ( ditem.slice(ditem.lastIndexOf('-') + 1 , ditem.length)) {
            case 'create':
               //如果是新增
              list = {
                //定义新增页面跳转的路由路径,如: path是截取到的文件名:user  那就path最后就是 :/usercreate
                path: '/'+path + 'create',
                name: `${path}create`,
                <!-- 匹配本地vue文件中是否包含 module:views下所有的vue文件,path:文件夹名称,item.web_path:vue文件名称 -->
                component: module[`../../views/${path}${item.web_path}create.vue`],
                meta: {
                  title: '新建' + item.name ,
                  buts: [],
                  requireAuth: true,
                  keepAlive: true,
                  externalLink: item.links,
                }
              }
              break;
            case 'edit':
              console.log('item-edit',item);
              list = {
                path: '/'+path + 'edit',
                name: `${path}edit`,
                component: module[`../../views/${path}${item.web_path}edit.vue`],
                meta: {
                  title: item.name  + '编辑' ,
                  buts: [],
                  requireAuth: true,
                  keepAlive: true,
                  externalLink: item.links,
                }
              }
            break;
          case 'detail':
            list = {
              path: '/'+path + 'detail',
              name: `${path}detail`,
              component: module[`../../views/${path}${item.web_path}detail.vue`],
              meta: {
                title:  item.name + '详情'  ,
                buts: [],
                requireAuth: true,
                keepAlive: true,
                externalLink: item.links,
              }
            }
            break;
         }
        }
        aa.push(list)
       }
     })
     <!-- 判断菜单中的chidren是否存在数据,如果没有存在就表明是一级点击跳转菜单,parent_id :0 表示的是菜单 -->
    if(item.children && item.parent_id == 0){
      GetPossibleDetails( data,item.children,aa)
     }
   })
   return aa
}

小结

1.最后添加到路由对象中的数据,最后一个对象就是新增的动态子页面路由

项目文件