设置路由守卫
登录后会根据不同角色跳转到前台或后台页面。也直接通过修改访问路径的方式访问后台,这显然是不安全的,因此需要设置路由守卫,在进行路由跳转前判断该用户是否可以跳转到该页面
在src\router\index.ts中加入下面的代码片段。路由白名单中是不需要验证直接放行的页面,在白名单之外的路径则获取当前用户的token和角色,已经登录且是管理员才能继续,否则进入登录页。
//路由白名单
const whiteRouters = ['/','/toLogin',]
//路由守卫
router.beforeEach((to, from, next) => {
//如果在白名单中直接放行
if (whiteRouters.indexOf(to.path) != -1){
return next();
}else{
//否则获取当前用户的token和角色
const userMsg = userStore();
const token = userMsg.userToken;
const role = userMsg.userRole;
//已经登录且是管理员才能继续,否则进入登录页
if (token == '' || role != 1){
return next('/toLogin')
}else{
next();
}
}
需要注意的是,const userMsg = userStore();如果写在全局守卫之外会报以下错误,
Uncaught Error: [🍍]: “getActivePinia()” was called but there was no
active Pinia. Did you forget to install pinia? const pinia =
createPinia()
这个错误好像是因为pinia没有激活引起的,应该是使用userStore时pinia为创建,如果想在全局守卫之外写,可以参考这篇文章的第二种解决方法,但是第二种方法会影响pinia持久化插件的使用,推荐使用第一种方法。
pinia持久化
将数据保存到pinia中后,发现一个问题,就是刷新之后数据会丢失。
像这样,保存在pinia中的token刷新后丢失了。
这里通过使用插件pinia-plugin-persistedstate完成数据持久化
- npm i pinia-plugin-persistedstate 下载安装
- 在main.ts中使用
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
export const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
createApp(App).use(router).use(pinia).use(ElementPlus).mount('#app')
- 在需要持久化的仓库中加入{ persist: true,}
例如
export const userStore = defineStore('user',()=>{
//token的存储和处理
const userToken = ref('');
const userRole = ref(0);
const setData = (token:string,role:number)=>{
userToken.value = token;
userRole.value = role;
}
return {userToken,setData,userRole}
},
{
persist: true,
}
)
至此,pinia的数据持久化就完成了,效果如下所示
通过以上方式,会将数据保存在localStorage中
也可以通过配置persist的属性storage,存到sessionStorage中。
export const userStore = defineStore('user',()=>{
//token的存储和处理
const userToken = ref('');
const userRole = ref(0);
const setData = (token:string,role:number)=>{
userToken.value = token;
userRole.value = role;
}
return {userToken,setData,userRole}
},
{
//存在sessionStorage
persist: {
storage: sessionStorage,
}
}
)
还可以通过配置其他属性达到存储指定数据的效果
详情见文章
也可以看视频
本文pina的持久化主要参考上面这两个链接。
全局请求加载动画
设置全局加载动画,发起请求时显示加载动画,请求结束关闭加载动画,并对同时进行的多个请求进行合并。
- 在utils文件夹添加loading.ts,存放展示加载动画和隐藏加载动画的相关方法。代码如下
/**
* 全局loading效果:合并多次loading请求,避免重复请求
* 当调用一次showLoading,则次数+1;当次数为0时,则显示loading
* 当调用一次hideLoading,则次数-1; 当次数为0时,则结束loading
*/
import { ElLoading } from 'element-plus';
// 定义一个请求次数的变量,用来记录当前页面总共请求的次数
let loadingRequestCount = 0;
// 初始化loading
let loadingInstance:any;
// 编写一个显示loading的函数 并且记录请求次数 ++
const showLoading = () => {
if (loadingRequestCount === 0) {
// element的服务方式 target 我这边取的是表格class
// 类似整个表格loading和在表格配置v-loading一样的效果,这么做是全局实现了,不用每个页面单独去v-loading
loadingInstance = ElLoading.service({
lock:true, //是否锁定
text:"拼命加载中...", //显示在加载图标下方的加载文案
background:'rgba(0,0,0,.7)', //遮罩背景色
});
}
loadingRequestCount++
}
// 编写一个隐藏loading的函数,并且记录请求次数 --
const hideLoading = () => {
if (loadingRequestCount <= 0) return
loadingRequestCount--
if (loadingRequestCount === 0) {
loadingInstance.close();
}
}
export {
showLoading,
hideLoading
}
- 在utils\request.ts中使用
import axios, {AxiosInstance,AxiosRequestConfig,AxiosResponse,AxiosError} from 'axios'
import { ElMessage } from 'element-plus'
import {showLoading,hideLoading} from './loading'
// 数据返回的接口
// 定义请求响应参数
interface Result<T> {
code: number;
msg: string;
data?: T;
}
const config = {
// 默认地址
baseURL: "http://localhost:8080",
// 设置超时时间
timeout: 3000,
// 跨域时候允许携带凭证
// withCredentials: true
}
class Request {
// 定义成员变量并指定类型
service: AxiosInstance;
public constructor(config: AxiosRequestConfig) {
// 实例化axios
this.service = axios.create(config);
/**请求拦截器
*/
this.service.interceptors.request.use(
//这里的config声明为AxiosRequestConfig类型会报错,很奇怪
(config: any) => {
// 发起请求时开始Loading
showLoading();
........
},
(error: AxiosError) => {
// 请求报错
Promise.reject(error)
}
)
/**
* 响应拦截器
*/
this.service.interceptors.response.use(
(response: AxiosResponse) => {
/*
获得响应后关闭Loading
采用延时处理是合并loading请求效果,避免多次请求loading关闭又开启
*/
setTimeout(() => {
hideLoading()
}, 200);
........
},
(error: AxiosError) => {
setTimeout(() => {
hideLoading()
}, 200);
.............
}
}
)
}
}
// 导出一个实例对象
export default new Request(config);
到此,全局请求加载动画配置完成,效果如下