Vite+TS分用户动态显示目录

1,082 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

前言

今天讲一下动态显示路由的实现。如果想看如何实现建议向下滑动至最终实现处,前边是踩坑总结

踩坑记

状态管理器的坑

我在做分用户动态显示查看了很多的文章都是用Vuex记录用户所拥有的路由,然后在首页上通过v-for循环出来。读过我上一篇文章的xdm都知道我继承了pinia,所以我开始使用状态管理器记录当前用户路由。从而引发了三个坑、刷新页面和路由处理、动态路由问题。

pinia刷新页面

我们都知道,状态管理器没有存储数据到缓存中,所以我们在使用状态管理器的时候。往往会有这么一种处理:在路由跳转时获取不到state,在目录遍历时获取不到state。那好我们补充上在登录时设置路由缓存,在pinia获取不到的话就通过缓存获取,这样暂时解决了刷新页面丢失状态的情况。

路由处理

我们如果想要把路由存储在pinia中的话,需要设置路由分为动态路由和静态路由。因为需要一个入口然后在跳转到动态路由的时候addRouter,所以我们在设置路由缓存只设置动态路由到缓存中然后在router.beforeEach()中添加router就行了

动态路由

这个坑放到下边介绍

弃坑原因

补状态的代码太多了,尤其是在router.beforeEach()中,现在是需要补用户信息,加上路由的话同时又要补充路由信息。以后如果需要别的状态信息真的令人崩溃

记录路由到缓存的坑

动态路由

既然状态管理器需要补充状态,那我们就直接取缓存路由吧。想法很美好,现实很残忍。我们现在采用的是按需加载,在定义组件时,其实是没有加载的。现在就出现了一个问题,我设置完用户的路由缓存之后,跳转到指定路由就会发现在控制台中提示没有这个组件。芭比Q了。想要记录路由就不能动态加载,想要压缩加载速度就需要动态加载,这陷入了套娃纠结。

最终实现

就在我套娃纠结emo的时候,一个灵感突然出现在我脑袋中。既然缓存不了路由,那我缓存目录吧!,我了个去,我被我的机智深深的打败了。下边介绍一下我是如何实现的

修改原有的路由代码,定义一个所有的路由状态信息放在route中

这里我们创建一个config.ts文件在router路径下,然后定义所有需要的路由,这里放心,我们在前边的文章已经做好了路由鉴权,完全不用担心会访问到不属于当前人的页面。

import LayOut from '@/components/layout/index.vue'

export const allRouters = [
  {
    path: '/',
    redirect: '/index',
    component: LayOut,
    meta: { title: '首页' },
    children: [
      {
        hidden: true,
        path: '/index',
        component: () => import(/* webpackChunkName: "index" */ '@/views/index/index.vue')
      }
    ]
  },
  {
    hidden: true,
    path: '/login',
    component: () => import(/* webpackChunkName: "login" */ '@/views/login/index.vue')
  },
  {
    path: '/system',
    component: LayOut,
    meta: { roleCode: 'system', title: '系统管理' },
    hidden: false,
    children: [
      {
        path: '/system/user',
        meta: { roleCode: 'system-user', title: '用户管理' },
        component: () => import(/* webpackChunkName: "system" */ '@/views/system/user/index.vue')
      },
      {
        path: '/system/setting',
        meta: { roleCode: 'system-setting', title: '系统设置' },
        component: () => import(/* webpackChunkName: "system" */ '@/views/system/setting/index.vue')
      }
    ]
  }
]

登录时记录当前人的目录

继续添加config.ts的内容,定义一个根据当前人的权限取路由目录信息的方法,代码如下

interface Menu {
  path: ''
  children: []
  title: ''
}
export const whiteNameRouters = ['/login']
const fillMenuData = (routers, roleCode) => {
  let menuArr: Menu[] = []
  let length = routers.length
  for (var index = 0; index < length; index++) {
    let router = routers[index]
    let needRoleCode = router.meta?.roleCode
    let hidden = router['hidden'] || false
    if (hidden) {
      continue
    }
    if (needRoleCode) {
      if (roleCode.indexOf(needRoleCode) > -1 && !hidden) {
        let childrenRouter: any = []
        if (router.children && router.children.length > 0) {
          childrenRouter = fillMenuData(router.children, roleCode)
        }
        menuArr.push({ path: router.path, children: childrenRouter, title: router.meta?.title })
      }
    } else {
      let childrenRouter: any = []
      if (router.children && router.children.length > 0) {
        childrenRouter = fillMenuData(router.children, roleCode)
      }
      menuArr.push({ path: router.path, children: childrenRouter, title: router.meta?.title })
    }
  }
  return menuArr
}
export const setMenuStoreByUserCode = (roleCode: string[]) => {
  if (!roleCode) {
    roleCode = []
  }
  //code:[add、del]等为按钮权限,此处略去
  roleCode = roleCode.filter((item) => item.indexOf(':') === -1)
  let menuData = fillMenuData(allRouters, roleCode)
  // Session记录异步缓存信息
  Session.set('menuInfo', menuData)
}

setMenuStoreByUserCode:根据传入的权限编码获取对应的路由封装成目录信息
fillMenuData:递归方法,如果当前router有子集,取当前的子集目录信息
whiteNameRouters:白名单

登录成功后,调用setMenuStoreByUserCode

const handleLogin = () => {
  loginRef.value.validate((vaild) => {
    if (vaild) {
      //设置加载中防止重复调用接口
      loginLoad.value = true
      Session.set('token', new Date().getTime())
      let username = loginModel.username ? loginModel.username : ''
      let userInfo = { userName: '', roleCode: [] as any[] }
      switch (username) {
        case 'admin':
          userInfo = getAdminInfo(username)
          break
        case 'dept':
          userInfo = getDeptAdminInfo(username)
          break
        default:
          userInfo = getCommonUserInfo(username)
          break
      }
      Session.set('userInfo', userInfo)
      userInfoStore.setUserInfo(userInfo)
      setMenuStoreByUserCode(userInfo.roleCode)
      loginLoad.value = false
      router.push('/')
    }
  })
}

目录循环

在进进行完以上几步之后,我们的会话缓存中已经有了目录信息。现在我们在layout左侧加载出来

...
   <el-menu class="el-menu-vertical-demo" @open="true" @close="false" :collapse="false" router>
        <template v-for="(item, index) in allMenu" :key="index">
          <template v-if="!item['hidden']">
            <el-sub-menu
              v-if="item.children && item.children.filter((item) => !item['hidden']).length > 0"
              :index="item.path"
            >
              <template #title>
                <span>{{ item.title }}</span>
              </template>
              <template v-for="(sub, childIndex) in item.children" :key="index + '-' + childIndex">
                <el-menu-item :index="sub.path">
                  <template #title>{{ sub.title ? sub.title : '' }}</template>
                </el-menu-item>
              </template>
            </el-sub-menu>
            <el-menu-item v-else :index="item.path">
              <template #title>{{ item.title }}</template>
            </el-menu-item>
          </template>
        </template>
      </el-menu>
      
...
import { onMounted, ref } from 'vue'
const allMenu = ref([] as any[])
...
onMounted(() => {
  allMenu.value = Session.get('menuInfo')
})
...

注意这里时在onMounted中设置目录信息
到这里我们就实现了在登录admin的时候会显示全部页面,在登录123时只显示首页

结语

真诚的说一句,可能是我的Vuex技术不太到家,状态管理器用的不是很6.建议各位前端同学有时间可以研究一下如何使用pinia实现动态显示
欢迎关注我的掘金账号:juejin.cn/user/261290…
欢迎star我的git项目:gitee.com/liangminghu…
下篇预告:终于搞完了路由动态显示,下期优化一下indexOf的路由判断存在的漏洞,按钮鉴权准备改为基于自定义指令。欢迎各位xdm提交基于pinia的动态目录显示issue。