vue3rbac思路,动态路由

import { defineStore } from 'pinia'

export const usePermissionStore = defineStore('permission', {
  state: () => ({
    permissionData: null, // 存储原始权限数据
    authorizedRoutes: [], // 过滤后的前端路由
  }),

  actions: {
    setPermissionData(data) {
      this.permissionData = data
    },

    setAuthorizedRoutes(routes) {
      this.authorizedRoutes = routes
    },

    clearSessionData() {
      this.permissionData = null
      this.authorizedRoutes = []
    },
  },
})

export default usePermissionStore
router/index

import 'nprogress/nprogress.css'
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
import { baseRoutes } from './baseRoutes'
import PageLayout from '@/layout/page-layout.vue'
import { staticRoutes } from './staticRoutes'
import { dynamicRoutes } from './dynamicRoutes'
import usePermissionStore from '@/store/modules/permission'
import { toRaw } from 'vue'

// 需要 PageLayout 的路由
const layoutRoutes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'root',
    component: PageLayout,
    children: [
      ...staticRoutes, // 静态路由始终注册
      // 动态路由初始为空,后续通过 addRoute 添加
    ],
  },
]

const router = createRouter({
  history: createWebHistory(),
  routes: [...baseRoutes, ...layoutRoutes],
  scrollBehavior() {
    return { top: 0 }
  },
})

export function mergeDynamicRoutes() {
  const store = usePermissionStore()

  const authorizedRoutes = store.authorizedRoutes || []

  // 查找 root 路由
  const rootRoute = router.getRoutes().find((route) => route.name === 'root')
  
  if (!rootRoute) {
    console.error('未找到 root 路由')
    return
  }

  // 检查并合并动态路由到 rootRoute.children
  const existingPaths = new Set(rootRoute.children.map((child) => child.path))
  
  const newChildren = authorizedRoutes.filter((item) => !existingPaths.has(item.path))
  
  rootRoute.children = [...(rootRoute.children || []), ...newChildren]

  // 更新路由表
  newChildren.forEach((item) => {
    const routeIdentifier = item.path
    if (!router.hasRoute(routeIdentifier)) {
      console.log(`正在添加路由: ${item.path}`)
      router.addRoute('root', item)
    } else {
      console.log(`路由已存在: ${item.path}`)
    }
  })

  // 先移除可能已存在的404路由
  if (router.hasRoute('notFound')) {
    router.removeRoute('notFound')
  }

  router.addRoute({
    path: '/:pathMatch(.*)*',
    name: 'notFound',
    component: () => import('@/views/not-found/index.vue'),
  })

  console.log('合并后的 rootRoute.children:', rootRoute)
}

export default router
// src/router/guard/index.ts
import NProgress from 'nprogress'
import type { Router } from 'vue-router'
import { setupTokenGuard } from './token'
import { setupWhiteListGuard } from './whiteList'
import usePermissionStore from '@/store/modules/permission'
import { usePermissionRoutes } from '@/hooks/useAuthPermission'

export function setupRouterGuard(router: Router) {
  router.beforeEach(async (to, from, next) => {
    NProgress.start()

    const guards = [setupWhiteListGuard, setupTokenGuard]
    try {
      const results = await Promise.all(guards.map((guard) => guard(to, from, next)))

      if (results.includes(false)) {
        return
      }

      const store = usePermissionStore()
     
     // 这里很重要,登录成功后调用这个
      if (store.authorizedRoutes.length === 0) {
        const { fetchPermissionRoutes } = usePermissionRoutes()

        await fetchPermissionRoutes()

        next({ ...to }) // 避免刷新404

        return
      }

      next()
    } catch (error) {
      console.error('Router guard error:', error)
      next('/login')
    }
  })

  router.afterEach(() => {
    NProgress.done()
  })
}
// src/hooks/usePermissionRoutes.ts
import { getPermissionsUserApi } from '@/api/permission'
import usePermissionStore from '@/store/modules/permission'
import { extractPathsFromPermissionTree, filterAuthorizedRoutes } from '@/utils/routerUtils'
import { dynamicRoutes } from '@/router/dynamicRoutes'
import { mergeDynamicRoutes } from '@/router'

/**
 * 获取用户权限路由的Hook
 */
export function usePermissionRoutes() {
  const store = usePermissionStore()

  const fetchPermissionRoutes = async () => {
    try {
      const res = await getPermissionsUserApi()

      const data = res.data
    
      // 1. 存储原始权限数据
      store.setPermissionData(data)

      // 2. 提取有效路径,这里需要注意路由怎么返回的
      // 情况差不多的话,前端表如果有嵌套路由,那么详情页别带/
      const permissionPaths = extractPathsFromPermissionTree(data.permissionTree)

      // 3. 过滤出有权限的动态路由
      const authorizedRoutes = filterAuthorizedRoutes(dynamicRoutes, permissionPaths)

      // 4. 存储过滤后的路由
      store.setAuthorizedRoutes(authorizedRoutes)

      // 合并路由
      mergeDynamicRoutes()
    } catch (error) {
      console.error('获取权限失败:', error)
    }
  }

  return {
    fetchPermissionRoutes,
  }
}
// src/utils/routerUtils.ts
import type { RouteRecordRaw } from 'vue-router'


export function extractPathsFromPermissionTree(permissionTree: any[]): string[] {
  const paths: string[] = []

  const traverse = (nodes: any[]) => {
    nodes.forEach((node) => {
      // 处理MENU_DIR类型且webPath不为空的节点(对应路由父路径)
      if ((node.type === 'MENU_DIR' || node.type === 'MENU') && node.webPath) {
        paths.push(node.webPath)
      }
      
      // 递归处理子节点
      if (node.children && node.children.length > 0) {
        traverse(node.children)
      }
    })
  }

  traverse(permissionTree)
  return paths
}

/**
 * 过滤动态路由,只保留有权限的路由
 */
export function filterAuthorizedRoutes(allRoutes: RouteRecordRaw[], permissionPaths: string[]): RouteRecordRaw[] {
  return allRoutes.filter((route) => {
    // 去掉路径末尾的斜杠,确保匹配一致性
    const normalizedRoutePath = route.path.replace(/\/$/, '')
    return permissionPaths.some((path) => {
      const normalizedPermissionPath = path.replace(/\/$/, '')
      return normalizedRoutePath === normalizedPermissionPath
    })
  })
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值