index.ts 4.4 KB
Newer Older
ziwencao's avatar
ziwencao committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
import { ref, type Ref } from "vue";
import axios, {
  AxiosError,
  type AxiosResponse,
  type AxiosRequestConfig,
} from "axios";
import storage from "store";
// import { ElMessage } from "element-plus";
import { useUserStore, USER_INFO, type UserInfo } from "@/stores/user";

const { VITE_APP_BASE_URL } = import.meta.env;

const request = axios.create({
  // API 请求的默认前缀
  baseURL: VITE_APP_BASE_URL as string,
  timeout: 10000, // 请求超时时间
});

// 异常拦截处理器
const errorHandler = (error: AxiosError) => {
  const status = error.response?.status;
  const useStore = useUserStore();
  switch (status) {
    /* eslint-disable no-param-reassign */
    case 400:
      error.message = "请求错误";
      break;
    case 401:
      useStore.logout();
      error.message = "未授权,请登录";
      break;
    case 403:
      error.message = "拒绝访问";
      break;
    case 404:
      error.message = `请求地址出错: ${error.response?.config.url}`;
      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:
      break;
  }
  return Promise.reject(error);
};

request.interceptors.request.use((config) => {
  // 如果 token 存在
  // 让每个请求携带自定义 token 请根据实际情况自行修改
  // config.headers.Authorization = `Bearer ${storage.get(ACCESS_TOKEN)}`;
  const userInfo = (storage.get(USER_INFO) as UserInfo) || {};
  config.headers = {
    ...config.headers,
    Authorization: `Bearer ${userInfo?.accessToken}`,
    userId: userInfo?.userId,
    childId: userInfo?.childId,
    userMin: "vip",
    accessToken: userInfo?.accessToken,
  };
  return config;
}, errorHandler);

// response interceptor
request.interceptors.response.use((response: AxiosResponse) => {
  const dataAxios = response.data;
  const useStore = useUserStore();
  // 这个状态码是和后端约定的
  const { code, msg } = dataAxios;
  // 根据 code 进行判断
  if (code === undefined) {
    // 如果没有 code 代表这不是项目后端开发的接口
    return dataAxios;
  }
  // 有 code 代表这是一个后端接口 可以进行进一步的判断
  // if (code) ElMessage.error(msg);
  if (code) alert(msg);
  switch (code) {
    case 0:
      // code === 0 代表没有错误
      return dataAxios;
    case 1:
      // code === 1 代表请求错误
      throw Error(msg);
    case 401:
      useStore.logout();
      throw Error(msg);
    default:
      // 不是正确的 code
      return dataAxios;
  }
}, errorHandler);

export default request;

export interface RequestConfig {
  successMessage?: string;
  errorMessage?: string;
  /** 立即发送请求 */
  immediate?: boolean;
}

export function useRequest<T>(
  axiosConfig: AxiosRequestConfig,
  requestConfig?: RequestConfig
) {
  // 最终返回的数据
  const data = ref<T>();
  // 请求失败返回的 Error 对象
  const error = ref<Error>();
  // 请求状态
  const loading = ref(false);
  // 立即发送请求
  const immediate = requestConfig?.immediate !== false;
  // 终止请求
  const { CancelToken } = axios;
  const { token, cancel } = CancelToken.source();
  // 合并求情配置
  const config = { ...axiosConfig, cancelToken: token };
  // 请求 Promise

  function run() {
    loading.value = true;
    return new Promise<Ref<T | undefined>>((resolve, reject) => {
      request<T>(config)
        .then((res: T) => {
          data.value = res;
          resolve(data);
        })
        .catch((err: Error) => {
          error.value = err;
          reject(err);
          if (requestConfig?.errorMessage) {
            throw Error(requestConfig.errorMessage);
          }
        })
        .finally(() => {
          loading.value = false;
        });
    });
  }

  const content = new Promise<Ref<T | undefined>>((resolve, reject) => {
    if (immediate) {
      run()
        .then((res) => {
          resolve(res);
        })
        .catch((err) => {
          reject(err);
        });
    }
  });
  return { data, error, loading, content, run, cancel };
}