使用meta.glob实现路由权限【动态路由】

提示:文章内容仅供参考


前言

提示:本文要记录的大概内容:

记录管理系统项目登录流程。
本章内容只是多种方案中的一种,希望可以帮助更多伙伴。


提示:以下是本篇文章正文内容,下面案例可供参考

一、本章环境

  1. VsCode开发工具。
  2. Vue3 + Vite + Ts 。
  3. 项目加密依赖包:aes
  4. 项目请求以来包:axios

安装以下依赖:

aesaxios
npm install aesnpm install axios

二、管理系统登录

1.登录流程

本章流程只是其中一种方式,任何登录流程根据业务不同会有所不一样。
具体登录流程需要【前端】与【后端】共同商讨。

/**	
  *	1.账号登录
  *		a、服务器 返回 客户端{code、msg、token}
  *		b、加密后的账号通过登录接口 -->  服务器发送请求
  *		c、token进行持久化存储或cookie存储
  *	
  *			首先账号密码是需要加密的,禁止进行明文传输(不安全)本章使用aes加密
  *			aes加密需要和后端商量设置偏移量。前后端要一致才可以。
  *	
  *	2.在请求头中添加token
  *		a、headers中设置【Authorization】属性将token进行赋值。
  *		   Authorization:它作用是用来携带身份验证信息,发送给服务器。
  *	
  *	3.请求【个人信息】接口
  *		a、服务器返回userInfo、permission、units、roles。
  *		b、将返回的信息进行存储
  *	
  *	4.通过个人信息中的roles【角色信息】中的角色权限编码 获取路由
  *		a、获取的路由通过Vue-router中的 addRouter()API 动态添加路由信息。
  *	
  *			需要自己去查看api如何使用。
  *			返回路由信息的数据结构和【后端】沟通。
  *	
  *	5.通过个人信息中的permission【权限信息】来控制按钮的权限
  *		a、控制权限本章在【vue3】中使用【自定义指令】控制按钮权限。
  *	
  *	6.登录成功	进入	管理系统首页
*/

2.代码案例

2.1首先配置改造axios

在src目录中添加utils目录,创建request.js文件夹。
这样做是为了配置响应拦截器和请求拦截器。
axios文档:https://www.axios-http.cn/docs/interceptors

/**
 *	
 *	request.js	文件内容
 *
 */	
import axios from 'axios';
const request = axios.create({
    baseURL: '基本地址'
});
// 获取通过localStorage获取token
const token = localStorage.getItem('token');

// 添加请求拦截器
request.interceptors.request.use(function (config) {

	//在响应头中配置Authorization将token赋值给它
    config.headers['Authorization'] = token || '';
    return config;
}, function (error) {
    return Promise.reject(error);
});


// 添加响应拦截器
request.interceptors.response.use(function (response) {
    return response;
}, function (error) {
    return Promise.reject(error);
});
 

export default request;

2.2、账号登录代码vue文件(html + js)

/**
 *	
 *	vue	文件内容
 *
 */	
<template>
  <div>
    <input type="text" v-model="user.userName">
    <input type="password" v-model="user.password">
    <button @click="loginClick">登录</button>
  </div>
</template>

<script setup lang="ts">
import request from '../../../utils/request';
const user = {
  userName: '',
  password: ''
}
const loginClick = async() => {
  // 1.点击登录获取token
  let {data} = await request.post("loginUrl", user);
  localStorage.setItem('token', JSON.stringify(data));


  // 2.获取用户个人信息。里面有基本用户信息、路由权限、权限信息
  //  (直接获取用户信息,因为设置了拦截请求,将token赋值给Authorization请求头。)
  let {data:{userInfo, permissions, roles}} = await request.get('/personal/getInfo')
    /**
     * 
     *  2.1.存储用户信息userInfo
     *  2.2.存储权限信息permissions
     *  2.3.存储角色信息roles,因为需要用到角色权限
     */  
  localStorage.setItem('userInfo', JSON.stringify(userInfo))
  localStorage.setItem('roles', JSON.stringify(permissions))
  localStorage.setItem('roles', JSON.stringify(roles))


  // 3、获取路由列表,并存储
  const rolePerm = JSON.parse(localStorage.getItem('roles') || '')
  let {data:{menu}} = await request.get(`获取路由url+${rolePerm}`)
  localStorage.setItem('menu', JSON.stringify(menu))

  }
</script>

2.2、动态添加路由

流程

1.通过meta.glob获取的资源路径和接口获取的路由列表进行重组
2.根据modulesMap[key]返回对应的value值
3.把component 重构成 箭头函数的形式
4.路由扁平化

使用vite中的import.meta.glob()函数,它的作用是导入多个模块,返回的是对象。
因为项目使用的是vite构建工具,webpack也有类似导入多个模块的方法require.context()。
核心是第二步,通过meta.glob获取的资源路径和接口获取的路由列表进行重组。
前提是,需要和后端沟通。路由的层级关系以及关键字。

/**
 *	
 *	guards.js	文件内容
 *
 */	
import router from './index';
import clone from 'rfdc'
export const beforeEach = ( to:any )=>{

    if(to.path == '/login' ) return 
    if( !localStorage.getItem('token') ) return {name: 'Login'};
    //动态添加路由
    initRouter();

    // 当前路由没有匹配到任何路由记录
    if( to.matched.length == 0){
        router.push( to.fullPath );
    }
    return true;

}

//1. 动态添加路由 => 整个过程
const initRouter = ()=>{

    let menu = JSON.parse(localStorage.getItem('menu') || '{}')
    //1.2 把component 重构成 箭头函数的形式
    let menuRouter = filterRouter(menu);

    //1.3 路由扁平化
    menuRouter = flatRoutes(menuRouter);

    //1.4 使用addRoute动态添加路由
    menuRouter.forEach((item:any) => {
        if(item.path.startsWith('/') && item.parentView=='layout') {
          router.addRoute(item.parentView, item);
        }
    });
    console.log(router.getRoutes())
    
}

//2. 对于component的调整。(通过meta.glob获取的资源路径和接口获取的路由列表进行重组)
const modules= import.meta.glob('@renderer/views/**/*.vue');
const modulesMap = {};
Object.keys(modules).map((menuItem) => {
    let path = menuItem.replace('/src/views/', '').replace('/index', '').replace('.vue', '');

    if(menuItem.includes('index')){
        modulesMap[`${path}/index`] = modules[menuItem]
    }
    modulesMap[path] = modules[menuItem]

})

//3. 根据modulesMap[key]返回对应的value值
const loadComponent  = (component) => {
    if (component) {
        return modulesMap[component];
    }
    return;
};


//4. 把component 重构成 箭头函数的形式
const filterRouter = (menu) => {
    let arrRouter = [];
    menu.forEach((item: any) => {
        var route = {
            parentView: item.parentView,
            path: item.path,
            name: item.name,
            meta: item.meta,
            redirect: item.redirect,
            children: item.children ? filterRouter(item.children) : null,
            component: loadComponent(item.component) 
        };
        arrRouter.push(route);
    });
    return arrRouter;
};

//5. 路由扁平化 
const flatRoutes = (routes, breadcrumb = []) => {
    let res = [];
    //深度复制对象,并处理循环引用
    const cloneRoutes  = clone()(routes);
    cloneRoutes.forEach((route) => {
        const tmp = { ...route };
        if (tmp.children) {
            let childrenBreadcrumb = [...breadcrumb];
            childrenBreadcrumb.push(route);
            let tmpRoute = { ...route };
            tmpRoute.meta.breadcrumb = childrenBreadcrumb;
            delete tmpRoute.children;
            res.push(tmpRoute);
            let childrenRoutes = flatRoutes(tmp.children, childrenBreadcrumb);
            childrenRoutes.map((item) => {
                res.push(item);
            });
        } else {
            let tmpBreadcrumb = [...breadcrumb];
            tmp.meta.breadcrumb = tmpBreadcrumb;
            tmpBreadcrumb.push(tmp);
            res.push(tmp);
        }
    });
    return res;
};

//后置
export const afterEach = ()=>{
    console.log('后置');
}
/**
 *	
 *	router.js	文件内容
 *
 */	
import { createRouter, createWebHashHistory } from "vue-router";

//引入路由表
import { AppRoutes } from './routes'
//引入导航守卫
import {  beforeEach  , afterEach  } from './guards'

const router = createRouter({
    history: createWebHashHistory(),
    routes:AppRoutes
})


//全局前置导航守卫
router.beforeEach( beforeEach )

//全局后置导航守卫
router.afterEach( afterEach )


export default router;

总结

获取资源模块

vite:meta.glob()
webpack:require.context()。
路由其实不一定需要在children中写子路由,可以是同一级。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值