创建好vue3项目
Axios安装
npm install axios
Element Plus 安装
官网入口:https://element-plus.gitee.io/zh-CN/
npm install element-plus --save
Element 主要用到信息提示 与 全屏加载动画
api 文件夹下 封装 Axios封装 与 请求配置
utils 文件夹下 operate.ts 配置接口地址 与其他全局ts
旧版本地址:https://www.cnblogs.com/lovejielive/p/16363587.html
新版本:主要增加动态控制是否显示加载动画。
是否需要判断重复请求。
优化请求接口配置参数写法。
扩展AxiosRequestConfig 增加自定义参数
declare module 'axios' {
//请求自定义参数
interface AxiosRequestConfig {
// 是否显示加载框
ifLoading?: boolean
// 是否允许重复请求
repeatRequest?: boolean
// 登录 token
isToken?: any;
}
}
通过配置repeatRequest是否允许重复请求,来开启判断。主要在api.ts中配置。
每一次请求创建一个key,判断是否存在,如存在执行.abort()取消当前请求,
不存在pendingMap中新增一个key。
通过AbortController来进行手动取消。
主要代码
//格式化请求链接
function getRequestKey(config: AxiosRequestConfig) {
const { url, method, data, params } = config,
//字符串化参数
dataStr = JSON.stringify(data) || '',
paramsStr = JSON.stringify(params) || '',
//记得这里一定要处理 每次请求都掉会变化的参数(比如每个请求都携带了时间戳),否则二个请求的key不一样
key = [method, url, dataStr, paramsStr].join("&");
return key;
}
//创建存储 key 的 集合
const pendingMap = new Map()
//是否重复请求key
function setPendingMap(config: AxiosRequestConfig) {
//手动取消
const controller = new AbortController()
config.signal = controller.signal
const key = getRequestKey(config)
//判断是否存在key 存在取消请求 不存在添加
if (pendingMap.has(key)) {
// abort取消请求
pendingMap.get(key).abort()
//删除key
pendingMap.delete(key)
} else {
pendingMap.set(key, controller)
}
}
在接口消息提示时,通过 axios.isCancel(error) 过滤掉已取消的请求,
//拦截掉重复请求的错误,中断promise执行
if (axios.isCancel(error)) return []
/** @description: 请求封装
* @Author: Jay
* @Date: 2023-04-11 13:24:41
* @LastEditors: Jay
* @LastEditTime: 2023-09-04 15:04:28
*/
//导入axios
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
//使用element-ui ElMessage做消息提醒 ElLoading加载
import { ElMessage, ElLoading } from "element-plus";
//请求头
import operate from "@/utils/operate"
//加载配置
let loadingInstance: { close: () => void};
let requestNum = 0;
//加载动画
const addLoading = () =>{
//防止重复弹出
requestNum++;
if (requestNum === 1) {
loadingInstance = ElLoading.service({ fullscreen: true});
}
}
//关闭 加载动画
const cancelLoading = () =>{
requestNum--;
if (requestNum === 0) loadingInstance?.close();
}
//格式化请求链接
functiongetRequestKey(config: AxiosRequestConfig) {
const { url, method, data, params } =config,
//字符串化参数
dataStr = JSON.stringify(data) || '',
paramsStr = JSON.stringify(params) || '',
//记得这里一定要处理 每次请求都掉会变化的参数(比如每个请求都携带了时间戳),否则二个请求的key不一样
key = [method, url, dataStr, paramsStr].join("&");
returnkey;
}
//创建存储 key 的 集合
const pendingMap = newMap()
//是否重复请求key
functionsetPendingMap(config: AxiosRequestConfig) {
//手动取消请求
const controller = newAbortController()
config.signal =controller.signal
const key =getRequestKey(config)
//判断是否存在key 存在取消请求 不存在添加
if(pendingMap.has(key)) {
//abort取消请求
pendingMap.get(key).abort()
//删除key
pendingMap.delete(key)
} else{
pendingMap.set(key, controller)
}
}
//增加新的请求参数类型
declare module 'axios'{
//请求自定义参数
interface AxiosRequestConfig {
//是否显示加载框
ifLoading?: boolean
//是否允许重复请求
repeatRequest?: boolean
//登录 token
isToken?: any;
}
//解決 类型“AxiosResponse<any, any>”上不存在属性“code”
//interface AxiosResponse<T = any> {
//// 请求 data 里的一级参数
//code: number;
//time: string;
//msg: string;
//data: T;
//}
}
//创建axios的一个实例
const axiosInstance: AxiosInstance =axios.create({
//接口统一域名
baseURL: operate.baseUrl(),
//设置超时
timeout: 1000 * 30,
//跨域携带cookie
withCredentials: true,
})
//添加请求拦截器
axiosInstance.interceptors.request.use(
(config) =>{
//加载动画
if (config?.ifLoading) addLoading();
//是否判断重复请求
if (!config.repeatRequest) {
setPendingMap(config)
}
//判断是否有token 根据自己的需求判断
const token =config.isToken
//console.log("判断是否有token", token)
if (token !=undefined) {
//如果要求携带在参数中
//config.params = Object.assign({}, config.params, operate.uploadParameters())
//如果要求携带在请求头中
//config.headers = Object.assign({}, config.headers, operate.uploadParameters())
}
returnconfig
},
(error: AxiosError) =>{
returnPromise.reject(error)
}
)
//添加响应拦截器
axiosInstance.interceptors.response.use((response: AxiosResponse) =>{
const config =response.config
//关闭加载 动画
if (config?.ifLoading) cancelLoading();
//是否登录过期
if (response.data.code == 400 || response.data.code == 401) {
ElMessage.error("登录过期,请重新登录")
////清除登录缓存
//store.commit("LOGOUT")
////返回首页
//setTimeout(() => {
//router.push("/");
//}, 500);
return}
//返回参数
returnresponse.data
})
//错误处理
axiosInstance.interceptors.response.use(undefined, (error) =>{
const config =error.config
//关闭加载 动画
if (config?.ifLoading) cancelLoading();
//拦截掉重复请求的错误,中断promise执行
if (axios.isCancel(error)) return[]
/***** 接收到异常响应的处理开始 *****/
if (error &&error.response) {
//1.公共错误处理
//2.根据响应码具体处理
switch(error.response.status) {
case 400:
error.message = '错误请求'
break;
case 401:
error.message = '未授权,请重新登录'
break;
case 403:
error.message = '拒绝访问'
break;
case 404:
error.message = '请求错误,未找到该资源'
//window.location.href = "/NotFound"
break;
case 405:
error.message = '请求方法未允许'
break;
case 408:
error.message = '请求超时'
break;
case 500:
error.message = '服务器端出错'
break;
case 501:
error.message = '网络未实现'
break;
case 502:
error.message = '网络错误'
break;
case 503:
error.message = '服务不可用'
break;
case 504:
error.message = '网络超时'
break;
case 505:
error.message = 'http版本不支持该请求'
break;
default:
error.message =`连接错误${error.response.status}`
}
} else{
//超时处理
if (JSON.stringify(error).includes('timeout')) {
error.message = '服务器响应超时,请刷新当前页'} else{
error.message = '连接服务器失败'}
}
//提示
ElMessage.error(error.message)
/***** 处理结束 *****/
returnPromise.resolve(error)
})
export default axiosInstance
request-wrapper.ts
/** @description: 请求接口 配置
* @Author: Jay
* @Date: 2023-04-11 13:24:41
* @LastEditors: Jay
* @LastEditTime: 2023-09-04 13:30:09
*/
//导入 Axios 请求
import request from '@/utils/request'
//其他配置
import operate from '@/utils/operate';
//官网接口
export const homePost = (data?: any) =>{
returnrequest({
url: '/api/index',
method: 'post',
data,
//登录token
isToken: operate.isToken(),
//加载动画是否启动
ifLoading: true,
//是否允许重复请求
repeatRequest: false,
})
}
/*请求配置与使用
* 请求 方式
export const 名字 = (data: any) =>
request.post("接口", data, {
直接为空
注:只能配置 AxiosRequestConfig 里有的参数名 可不用配置
});
*使用 方法
*引入
import {
名字
} from "../api/api"
*生命周期中 请求
名字({请求参数}).then((res) => {
console.log(res)
})
*/
api.ts
<script lang="ts" setup>
import { onMounted } from "vue";
import { homePost } from "@/api/api";
//生命周期
onMounted(() => {
homePost().then((res) => {
console.log("第一次", res);
});
homePost().then((res) => {
console.log("第二次", res);
});
});
</script>
请求结果
第一次请求被拦截,只有第二次成功返回
主要放置一些 全局参数与方法。
在页面中可以通过 import operate from "@/utils/operate"
导入使用,也可以在main.ts中全局配置。
/** @description: 全局js
* @Author: Jay
* @Date: 2023-09-04 13:53:47
* @LastEditors: Jay
* @LastEditTime: 2023-09-04 13:55:44
*/
//vuex 数据
import store from '../store/index'
//接口地址
const baseUrl = () =>{
if (process.env.NODE_ENV == "development") {
//开发环境
return "";
} else{
//正式环境
return "";
}
}
//获取用户token
const isToken = () =>{
if (store.state.Authorization != '') {
returnstore.state.Authorization
}
return '';
}
//上传请求头 登录验证
const uploadParameters = () =>{
return { 'token': isToken() }
//return { 'Authori-zation': 'Bearer ' + isToken() }
}
/*eslint-disable */
/*格式化时间 加上时分秒
num: 后台时间格式
type: 'YY-MM-DD' 年月日 ,'HH-MM-SS' 时分秒 ,不传 年月日时分秒
*/const happenTime = (num: any, type: string) =>{
let date = new Date(num * 1000);
//时间戳为10位需*1000,时间戳为13位的话不需乘1000
let y: any =date.getFullYear();
let MM: any = date.getMonth() + 1;
MM = MM < 10 ? ('0' + MM) : MM; //月补0
let d: any =date.getDate();
d = d < 10 ? ('0' + d) : d; //天补0
let h: any =date.getHours();
h = h < 10 ? ('0' + h) : h; //小时补0
let m: any =date.getMinutes();
m = m < 10 ? ('0' + m) : m; //分钟补0
let s: any =date.getSeconds();
s = s < 10 ? ('0' + s) : s; //秒补0
if (type === 'YY-MM-DD') {
//年月日
return y + '-' + MM + '-' +d;
} else if (type === 'HH-MM-SS') {
//时分秒
return h + ':' + m + ':' +s;
} else{
//全部
return y + '-' + MM + '-' + d + ' ' + h + ':' + m + ':' +s;
}
}
/*eslint-enable */
//页面回到顶部(滚动效果)/*使用方法
//监听滚动事件
window.addEventListener("scroll", proxy.$operate.handleScroll, {
once: true,
});
*/const handleScroll = () =>{
let scrollTop = window.pageYOffset || document.documentElement.scrollTop ||document.body.scrollTop;
console.log(scrollTop, "scrollTop");
if (scrollTop > 0) {
const timeTop = setInterval(() =>{
document.documentElement.scrollTop = document.body.scrollTop = scrollTop -= 50; //一次减50往上滑动
if (scrollTop <= 0) {
clearInterval(timeTop);
}
}, 10); //定时调用函数使其更顺滑
}
};
export default{
baseUrl,
isToken,
uploadParameters,
happenTime,
handleScroll
}
operate.ts
本文来自博客园,作者:虚乄,转载请注明原文链接:https://www.cnblogs.com/lovejielive/p/17676856.html
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://www.cnblogs.com/lovejielive/p/17676856.html
内容来源于网络,如有侵权,请联系作者删除!