• 全局loading
  • 单页面loading
  • 局部loading

首先,在Store中定义 loading.ts

import { ElLoading } from 'element-plus'
import * as _ from 'lodash-es'
import { defineStore } from 'pinia'
import { ref } from 'vue'
 
export const useLoading = defineStore('loading', () => {
  const loading = ref<boolean>(false)
  const loadingCount = ref<number>(0)
  const fullscreenLoading = ref()
 
  async function showLoading({ fullscreen = false, text = '' } = {}) {
    if (fullscreen) {
      if (fullscreenLoading.value) {
        fullscreenLoading.value.close()
      }
      fullscreenLoading.value = ElLoading.service({
        lock: true,
        text,
      })
    } else {
      loadingCount.value++
      if (loading.value) return
      loading.value = true
    }
  }
 
  const tryHideLoading = _.debounce(() => {
    if (loadingCount.value > 0) return
    loading.value = false
  }, 300)
 
  function hideLoading({ fullscreen = false } = {}) {
    if (fullscreen) {
      fullscreenLoading.value?.close()
    } else {
      loadingCount.value--
      tryHideLoading()
    }
  }
 
  return { loading, showLoading, hideLoading }
})

然后,再在 api.ts 的拦截器中使用。

import pinia from '@/stores'
import { useLoading } from '@/stores/loading'
import type {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from 'axios'
import axios from 'axios'
const { showLoading, hideLoading } = useLoading(pinia)
 
//...
class Api {
  // request 拦截器
  static setRequestInterceptor(instance: AxiosInstance) {
    instance.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        showLoading(config.loading)
        return config
      },
      (error: AxiosError) => {
        hideLoading(error.config?.loading)
        return Promise.reject(error) // 在调用的那边可以拿到(catch)你想返回的错误信息
      },
    )
  }
  
  // response 拦截器
  static setResponseInterceptor(instance: AxiosInstance) {
    instance.interceptors.response.use((response: AxiosResponse) => {
      hideLoading(response.config.loading)
      //...
    })
  }
  //...
}
export default Api

最后,在需要的地方添加全局 loading。

局部loading:

<div class="form" v-loading="props.attributes?.showLoading && loading"><div>
import { storeToRefs } from 'pinia'
const { loading } = storeToRefs(useLoading())

全屏loading:

const {
data: { aside, collect, group },
} = await Api.get('/release/index', {
	loading: {
	  text: '配置文件加载中...',
	  fullscreen: true,
	},
})

扩展阅读