提示:文章内容仅供参考
前言
提示:本文要记录的大概内容:
记录管理系统项目登录流程。
本章内容只是多种方案中的一种,希望可以帮助更多伙伴。
提示:以下是本篇文章正文内容,下面案例可供参考
一、本章环境
- VsCode开发工具。
- Vue3 + Vite + Ts 。
- 项目加密依赖包:aes
- 项目请求以来包:axios
安装以下依赖:
aes | axios |
---|---|
npm install aes | npm 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中写子路由,可以是同一级。