15、springboot3 vue3开发平台-前端-路由构建, 使用路由守卫,加载动态路由,pinia管理路由缓存数据

1. 创建菜单路由pinia管理

  • 通过searchSelfMenusService 从后端请求菜单树列表, 菜单树查询构建内容见前变后端部分菜单列表树部分
    src\stores\menu.ts
  • vue3 动态加载组件要使用 import.meta.glob(‘…/views/**/*.vue’)
  • 使用elementplus el-menu 所以要构建完整路由,以"/" 开头,不然会出现路由跳转时路由拼接问题,使用contactChildRouter 拼接完整路由,在后续递归菜单时使用
  • mountRouter 将所有菜单挂载在Layout子菜单下,Layout布局页面见下一章节
  • 数据说明
    • menuList: 构建菜单
    • routerList: 路由缓存,创建过程中会加载vue组件
    • menuArray: 构建完整路由后菜单的一维存储,便于后续菜单、路由搜索
import {defineStore} from 'pinia'
import {ref} from 'vue'
import { searchSelfMenusService } from '@/api/sys/menu'
import router from '@/router/index.js'
import Layout from '@/views/layout/Layout.vue'

let modules = import.meta.glob('../views/**/*.vue')

export const useMenuStore = defineStore("menu", () => {
    

    const menuList = ref<Array<any>>([])
    const routerList = ref<Array<any>>([])
    const menuArray = ref<Array<any>>([])                                           // 一维结构后菜单数组,便于菜单搜索

     // 重载路由列表
    const generateRouterAndMenu = async () => {
        let menuResult = await searchSelfMenusService()
        // menuList.value = menuResult.data
        routerList.value = []   // 清除缓存
        initMenuAndRouter(menuResult.data, routerList.value)
        menuList.value = []
        contactChildRouter(menuResult.data, '')
        menuList.value = menuResult.data
        menuArray.value = flattenMenuTree(menuList.value)
    }

    const initMenuAndRouter = (menuList: Array<any>, routerList: Array<any>) => {
        modules = import.meta.glob('../views/**/*.vue')
        initRouterMountComponent(menuList, routerList)
        mountRouter()
    }

    const initRouterMountComponent = (menuList: Array<any>, routerList: Array<any>) => {
        menuList.forEach(item => {
            // 定义数据结构
            let routerInfo: any = {}
            if (item.componentPath) {
                if (item.children && item.children.length > 0) {
                    routerInfo = {
                        name: item.menuName,
                        path: item.path,
                        meta: {title: item.menuName},
                        // 设置组件,
                        component: modules[`../views/${item.componentPath}.vue`],
                        children: []
                    }
                } else {
                    routerInfo = {
                        name: item.menuName,
                        path: item.path,
                        meta: {title: item.menuName},
                        // 设置组件,
                        component: modules[`../views/${item.componentPath}.vue`]
                    }
                }
                // 将路由信息添加到数组中
                routerList.push(routerInfo)
            }
            // 递归子菜单
            if (item.children && item.children.length > 0) {
                initRouterMountComponent(item.children, routerInfo.children)
            }
        })
    }

    // 拼接子菜单完整路由
    const contactChildRouter = (list: Array<any>, parentPath: string) => {
        list.forEach(item => {
            if (parentPath != '') {
                let path = parentPath + '/' + item.path
                item.path = path
            } else {
                let path = "/" + item.path
                item.path = path
            }
            // 递归子菜单
            if (item.children && item.children.length > 0) {
                contactChildRouter(item.children, item.path)
            }
        })
    }

    // 挂载路由, 添加为/ 子路由, 在右侧主区域显示
    const mountRouter = () => {
        // 所有的页面都是加载到Layout/Main组件的RouterView中, 相当于所有的路由都是Layout的子路由
        router.addRoute(
            {
                component: Layout,
                path: "/",
                redirect: 'index',
                children: routerList.value
            }
        )
    }

	// 降维列表树,转为一维数组
    const flattenMenuTree = (tree: Array<any>): any[] => {  
        let result: any[] = [] 
        // 递归函数来遍历树并收集节点  
        let traverse = (nodes: Array<any>) => {  
            nodes.forEach(node => {  
                // 将当前节点添加到结果数组中  
                result.push({ ...node})
                if (node.children && node.children.length > 0) {  
                    traverse(node.children);
                }  
            })
        }    
        // 开始遍历树  
        traverse(tree)  
        return result;  
    } 



    return {
        menuList, routerList, menuArray,
        generateRouterAndMenu, initMenuAndRouter
    }
},
{
    persist: {
        paths: ['activeTab']//指定要长久化的字段
      }
}
)

2. 创建路由守卫

src\router\permission.ts
检查白名单和pina中数据是否加载,没有缓存数据则重新发请求加载菜单路由

import router from "./index"

import { useMenuStore } from '@/stores/menu'
import { useTokenStore } from "@/stores/token"


// 设置路由白名单
const whiteRouter = ['/login','/error','/404']


// 全局前置路由守卫, 路由挂载, 名单过滤
router.beforeEach((to, from, next) =>{
     const menuStore = useMenuStore()
    // 判断 to 是否处于白名单
    if(whiteRouter.indexOf(to.path) == -1) {
        console.log("no white route to path", to.path)
        const tokenStore = useTokenStore()
        // 是否登录
        if (!tokenStore.isLogin()) {
            next('/login')
        } else {
            console.log("menuStore.routerList.length:", menuStore.routerList.length)
            // 判断routerList中是否有动态路由的数据
            if(menuStore.routerList.length == 0) {
                // 设置动态路由数据结构,并且添加到路由中
                console.log('loading router-----------------------------')
                menuStore.generateRouterAndMenu().then(() => {
                    // 跳转页面
                    next({...to,replace: true})
                })
            }else {
                // 情况1:路由的路径是合法的,正常的
                if(to.matched.length != 0) {
                    next();
                }else {
                    // 情况2:路由的页面并没有
                    next('/404');
                }
            }
        }
    }else {
        // 直接放行
        next()
    }

})

登录简单判断:

// 判断是否登录
    const isLogin = (): boolean => {
        if (tokenInfo.tokenName && tokenInfo.tokenValue) {
            return true
        } else {
            return false
        }
    }

记得return
在这里插入图片描述

3. 加载路由守卫

在main.ts 中:

import './router/permission'

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不知所云,

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值