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>
相关推荐
风止何安啊7 小时前
从 “翻页书” 到 “魔术盒”:React 路由凭啥如此丝滑?
前端·react.js·面试
徐小夕7 小时前
10k Star 的开源 AI 记忆引擎:6 行代码,用图谱+向量打造永不遗忘的 AI
前端·后端·github
前端不太难7 小时前
Vue 项目路由 + Layout 的最佳实践
前端·javascript·vue.js
Jolyne_7 小时前
个人积累的一些前端问题解决方案(理论或实践,持续更新....)
前端
程序员祥云7 小时前
港股证劵 社招 一面
前端·面试
qq_4783775157 小时前
python cut_merge video, convert video2gif, cut gif
java·前端·python
巴拉巴拉~~7 小时前
Flutter 通用列表刷新加载组件 CommonRefreshList:下拉刷新 + 上拉加载 + 状态适配
前端·javascript·flutter
Asus.Blogs7 小时前
golang格式化打印json
javascript·golang·json
梨子同志7 小时前
Express.js 基础
前端