axios简易封装,适配H5开发

utils - request.ts

js 复制代码
import Axios, {
  type AxiosInstance,
  type AxiosError,
  type AxiosResponse,
  type AxiosRequestConfig
} from 'axios';
import { getStorage } from '@/utils/storage';
import { _closePage } from '@/utils/utils-app';
import { ContentTypeEnum, ResultEnum, RequestEnum } from '@/enums/requestEnum';
import { useAddRequestCount, useRemoveRequestCount } from '@/hooks/useRequestCounter';

import NProgress from './progress';
import qs from 'qs';

// 默认 axios 实例请求配置
const configDefault = {
  headers: {
    'Content-Type': ContentTypeEnum.JSON,
    'config-model': 'INTERNET'
  },
  timeout: RequestEnum.TIMEOUT,
  baseURL: import.meta.env.VITE_BASE_API,
  paramsSerializer: {
		serialize(params: any) {
			return qs.stringify(params, { allowDots: true });
		}
	},
  data: {}
};

class Http {
  // 当前实例
  private static axiosInstance: AxiosInstance;
  // 请求配置
  private static axiosConfigDefault: AxiosRequestConfig;

  // 请求拦截
  private httpInterceptorsRequest(): void {
    Http.axiosInstance.interceptors.request.use(
      config => {
        if (!config.hideLoading) {
          useAddRequestCount();
        };
        const { accessToken } = getStorage('accessToken');
        if (accessToken) {
         config.headers!['Authorization'] = accessToken;
        }
        return config;
      },
      (error: AxiosError) => {
        showFailToast(error.message);
        return Promise.reject(error);
      }
    );
  }

  // 响应拦截
  private httpInterceptorsResponse(): void {
    Http.axiosInstance.interceptors.response.use(
      (response: AxiosResponse) => {
        if (!response.config.hideLoading) {
          useRemoveRequestCount();
        };
        // 与后端协定的返回字段
        const { code, message } = response.data;
        // 判断请求是否成功
        const isSuccess = Reflect.has(response.data, 'code') && code === ResultEnum.SUCCESS;
        if (isSuccess) {
          return response.data;
        }
        if (code === ResultEnum.EXPIRED) {
          showDialog({
            title: "权限提示",
            message: "页面已失效,请重新进入页面!",
          })
            .then(() => {
              _closePage();
            })
        } else {
          // 处理请求错误
          showFailToast(message);
          return Promise.reject(response.data);
        }
      },
      (error: AxiosError) => {
        if (!error?.config?.hideLoading) {
          useRemoveRequestCount();
        };
        // HTTP 状态码
        const status = error.response?.status ?? 0;
        const handler = new Map([
          [400, '请求错误'],
          [401, '未授权,请登录'],
          [403, '拒绝访问'],
          [404, `请求地址出错: ${error.response?.config?.url}`],
          [408, '请求超时'],
          [500, '服务器内部错误'],
          [501, '服务未实现'],
          [502, '网关错误'],
          [503, '服务不可用'],
          [504, '网关超时'],
          [505, 'HTTP版本不受支持'],
          [0, '网络连接故障'],
        ])
        // 处理 HTTP 网络错误
        const message = handler.get(status) ?? handler.get(0) ?? '';

        showFailToast(message);
        return Promise.reject(error);
      }
    );
  }

  constructor(config: AxiosRequestConfig) {
    Http.axiosConfigDefault = config;
    Http.axiosInstance = Axios.create(config);
    this.httpInterceptorsRequest();
    this.httpInterceptorsResponse();
  }

  // 通用请求函数
  public request<T>(paramConfig: AxiosRequestConfig): Promise<T> {
    const config = { ...Http.axiosConfigDefault, ...paramConfig };
    return new Promise((resolve, reject) => {
      Http.axiosInstance
        .request(config)
        .then((response: any) => {
          resolve(response);
        })
        .catch(error => {
          reject(error);
        });
    });
  }
}

export const http = new Http(configDefault);

hooks - useReauestCounter.ts

js 复制代码
import { useRequestCounterStoreHook } from "@/store/modules/requestCounter";

const store = useRequestCounterStoreHook();
// 增加计数
export function useAddRequestCount() {
  store.addRequestCount();
}
// 减少计数
export function useRemoveRequestCount() {
  store.removeRequestCount();
}
// 清空计数
export function useClearRequestCount() {
  store.clearRequestCount();
}

store - reauestCounter.ts

js 复制代码
import { defineStore } from 'pinia';
import { store } from '@/store';

export const useRequestCounterStore = defineStore({
  id: 'request-counter',
  state: () => ({
    // 请求次数
    requestCount: 0,
  }),
  getters: {
    // 是否有请求正在进行
    isLoading(): boolean {
      return this.requestCount > 0;
    }
  },
  actions: {
    // 增加请求计数
    addRequestCount() {
      this.requestCount += 1;
    },
    // 减少请求计数
    removeRequestCount() {
      if (this.requestCount > 0) {
        this.requestCount -= 1;
      }
    },
    // 重置计数器(用于清理)
    clearRequestCount() {
      this.requestCount = 0;
    }
  }
});

export function useRequestCounterStoreHook() {
  return useRequestCounterStore(store);
}

根组件 - BasicLayout.vue

xml 复制代码
<template>
  <div class="flex-column-1 hidden-container">
    <!-- 适配顶部安全区域 -->
    <header class="van-safe-area-top" />
    <router-view v-slot="{ Component, route }">
      <keep-alive :include="cachedViews">
        <component :is="Component" :key="route.path" class="flex-column-1" />
      </keep-alive>
    </router-view>
    <!-- 适配底部安全区域 -->
    <footer class="van-safe-area-bottom" />
    <!-- 加载中遮罩 -->
    <van-overlay :show="isLoading" class="global-loading-overlay">
      <div class="flex-center loading-content">
        <van-loading type="circular" :size="$getSize(48)" />
      </div>
    </van-overlay>
  </div>
</template>

<script setup lang="ts" name="BasicLayout">
import { useCachedViewStoreHook } from "@/store/modules/cachedView";
import { useRequestCounterStoreHook } from "@/store/modules/requestCounter";

import $getSize from '@/utils/px2vw';

const router = useRouter();
const route = useRoute();

// 缓存页面
const cachedViews = computed(() => {
  return useCachedViewStoreHook().cachedViewList;
});
// 是否加载中
const isLoading = computed(() => {
  return useRequestCounterStoreHook().isLoading;
});
</script>

<style lang="less" scoped>
.global-loading-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 9999;
  background-color: transparent;
  // background-color: rgba(0, 0, 0, .6);
}
.loading-content {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 64px;
  height: 64px;
  background-color: var(--van-toast-background);
  border-radius: var(--van-toast-radius);
}
</style>
相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax