loading的展示和取消可以说是每个前端对接口的时候都要关心的一个问题。比如说table加载数据的时候,我希望他得是数据拉取完成,spin自动消失,那么这种我可以在页面定义一个loading ref。
const loading = ref(false)
const data = ref(null)
axios.post(`/api/data`).then((res) => {
loading.value = false;
data.value = res;
}).catch(error => {
console.log("失败:", error);
}).finally(() => {
console.log("Promise 执行完毕,无论成功或失败。");
loading.value = false;
});
这样去处理loading,但是如果这样我再每个页面里面都需要这么去写loading,那么我就在想我能不能全局监听到axios是否正在调用api呢,想了下可以使用拦截器和结合pinia使用及时去处理当前访问网络的状态。
import { ref } from 'vue'
import { defineStore } from 'pinia'
//处理全局httploading
export const useHttpLoadingStore = defineStore('http-loading', () => {
const isLoading = ref(false)
function setLoading(loading: boolean) {
if (isLoading.value === loading) return
isLoading.value = loading
}
return { isLoading, setLoading }
})
在调用api的request里面
import axios from 'axios'
import type { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'
import { useLiveUserStore } from '@/stores/user'
import { useHttpLoadingStore } from '@/stores/http-loading'
// 通用响应列表接口
export interface ResponseList<T = unknown> {
count: number
rows: T[]
}
// 创建axios实例
const service: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 15000
})
// 请求拦截器
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const httpLoadingStore = useHttpLoadingStore()
httpLoadingStore.setLoading(true)
// 从 liveUser store 获取 token 并添加到请求头
const liveUser = useLiveUserStore()
if (liveUser.token && config.headers) {
config.headers['Authorization'] = `Bearer ${liveUser.token}`
}
return config
},
(error) => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
const res = response.data
const httpLoadingStore = useHttpLoadingStore()
httpLoadingStore.setLoading(false)
// 根据后端返回的状态码处理不同情况
if (response.status !== 200) {
ElMessage.error(res.message || '请求错误')
// 处理401未授权状态
if (response.status === 401) {
// 使用 liveUser store 的 logout 方法清除用户信息并跳转到登录页
const liveUser = useLiveUserStore()
liveUser.logout()
router.push('/login')
}
return Promise.reject(new Error(res.error || '请求错误'))
} else {
return res
}
},
(error) => {
console.log(error);
const httpLoadingStore = useHttpLoadingStore()
httpLoadingStore.setLoading(false)
// 处理HTTP错误状态
if (error.response) {
const { status } = error.response
let message = '请求错误'
if (error.response.data?.msg) {
message = `${message}: ${error.response.data.msg}`
}
const liveUser = useLiveUserStore()
// 处理常见的HTTP状态码
if (status === 401) {
message = '未授权,请重新登录'
liveUser.logout()
router.push('/login')
} else if (status === 403) {
message = '拒绝访问'
} else if (status === 404) {
message = '请求的资源不存在'
} else if (status === 500) {
message = '服务器内部错误'
}
ElMessage.error(message)
} else {
ElMessage.error('网络连接失败,请检查网络')
}
return Promise.reject(error)
}
)
// 封装GET请求
export function requestGet<T = unknown>(url: string, params?: Record<string, unknown>): Promise<T> {
return service.get(url, { params })
}
// 封装POST请求
export function requestPost<T = unknown>(url: string, data?: Record<string, unknown>): Promise<T> {
return service.post(url, data)
}
// 封装PUT请求
export function requestPut<T = unknown>(url: string, data?: Record<string, unknown>): Promise<T> {
return service.put(url, data)
}
// 封装DELETE请求
export function requestDel<T = unknown>(url: string, params?: Record<string, unknown>): Promise<T> {
return service.delete(url, { params })
}
export default service
在页面具体使用的时候 比如el-button,直接加上就可以使用了。
<template>
<el-button type="default" v-back v-loading="httpLoadingStore.isLoading">返回</el-button>
</template>
<script setup lang="ts">
import { useHttpLoadingStore } from '@/stores/http-loading';
const httpLoadingStore = useHttpLoadingStore()
</script>