axios,request配置

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
import Cookies from 'js-cookie'
import { ElMessage, ElLoading } from 'element-plus'

// 响应数据接口
interface ApiResponse<T = any> {
  code: number
  message: string
  data: T
  success: boolean
}

// 请求配置接口
interface RequestConfig {
  baseURL?: string
  timeout?: number
  headers?: Record<string, string>
}

// 请求选项接口
interface RequestOptions {
  showLoading?: boolean
  showError?: boolean
  loadingText?: string
  noAuth?: boolean 
}

// Token相关常量
const TOKEN_KEY = 'access_token'
const REFRESH_TOKEN_KEY = 'refresh_token'

class Request {
  private instance: AxiosInstance
  private loadingInstance: any = null

  constructor(config: RequestConfig = {}) {
    // 创建axios实例
    this.instance = axios.create({
      baseURL: config.baseURL || import.meta.env.VITE_API_BASE_URL || '/api',
      timeout: config.timeout || 10000,
      headers: {
        'Content-Type': 'application/json',
        ...config.headers
      }
    })

    // 设置请求拦截器
    this.setupRequestInterceptor()
    
    // 设置响应拦截器
    this.setupResponseInterceptor()
  }

  // 获取token
  private getToken(): string | undefined {
    return Cookies.get(TOKEN_KEY)
  }

  // 设置token
  private setToken(token: string): void {
    Cookies.set(TOKEN_KEY, token, { expires: 7 }) // 7天过期
  }

  // 清除token
  private clearToken(): void {
    Cookies.remove(TOKEN_KEY)
    Cookies.remove(REFRESH_TOKEN_KEY)
  }

  // 刷新token
  private async refreshToken(): Promise<boolean> {
    try {
      const refreshToken = Cookies.get(REFRESH_TOKEN_KEY)
      if (!refreshToken) {
        return false
      }

      const response = await axios.post(`${this.instance.defaults.baseURL}/auth/refresh`, {
        refreshToken
      })

      if (response.data.success && response.data.data.token) {
        this.setToken(response.data.data.token)
        return true
      }
      return false
    } catch (error) {
      console.error('刷新token失败:', error)
      return false
    }
  }

  // 设置请求拦截器
  private setupRequestInterceptor(): void {
    this.instance.interceptors.request.use(
      (config) => {
        // 判断是否需要token
        // @ts-ignore
        if (!(config.noAuth)) {
          const token = this.getToken()
          if (token) {
            config.headers.Authorization = `Bearer ${token}`
          }
        }
        return config
      },
      (error) => Promise.reject(error)
    )
  }

  // 设置响应拦截器
  private setupResponseInterceptor(): void {
    this.instance.interceptors.response.use(
      (response: AxiosResponse<ApiResponse>) => {
        const { data } = response
        
        // 处理业务错误
        if (!data.success) {
          ElMessage.error(data.message || '请求失败')
          return Promise.reject(new Error(data.message || '请求失败'))
        }
        
        return response
      },
      async (error: AxiosError) => {
        const { response, config } = error
        
        // 处理401未授权
        if (response?.status === 401) {
          const refreshed = await this.refreshToken()
          if (!refreshed) {
            this.clearToken()
            // 跳转到登录页
            window.location.href = '/login'
            return Promise.reject(new Error('登录已过期,请重新登录'))
          }
          
          // 重新发起请求
          if (config) {
            const token = this.getToken()
            if (token) {
              config.headers.Authorization = `Bearer ${token}`
            }
            return this.instance.request(config)
          }
        }

        // 处理其他HTTP错误
        let errorMessage = '网络错误'
        if (response?.status) {
          switch (response.status) {
            case 400:
              errorMessage = '请求参数错误'
              break
            case 403:
              errorMessage = '没有权限访问'
              break
            case 404:
              errorMessage = '请求的资源不存在'
              break
            case 500:
              errorMessage = '服务器内部错误'
              break
            case 502:
              errorMessage = '网关错误'
              break
            case 503:
              errorMessage = '服务不可用'
              break
            case 504:
              errorMessage = '网关超时'
              break
            default:
              errorMessage = `请求失败 (${response.status})`
          }
        }
        ElMessage.error(errorMessage)
        return Promise.reject(error)
      }
    )
  }

  // 显示加载状态
  private showLoading(text: string = '加载中...'): void {
    this.loadingInstance = ElLoading.service({
      lock: true,
      text,
      background: 'rgba(0, 0, 0, 0.7)'
    })
  }

  // 隐藏加载状态
  private hideLoading(): void {
    if (this.loadingInstance) {
      this.loadingInstance.close()
      this.loadingInstance = null
    }
  }

  // 核心请求方法
  async request<T = any>(config: AxiosRequestConfig & RequestOptions): Promise<ApiResponse<T>> {
    const { showLoading = false, showError = true, loadingText = '加载中...', ...axiosConfig } = config

    // 显示加载状态
    if (showLoading) {
      this.showLoading(loadingText)
    }

    try {
      const response = await this.instance.request<ApiResponse<T>>(axiosConfig)
      return response.data
    } catch (error) {
      // 如果showError为false,不显示错误消息
      if (!showError && error instanceof Error) {
        throw error
      }
      throw error
    } finally {
      // 隐藏加载状态
      if (showLoading) {
        this.hideLoading()
      }
    }
  }

  // GET请求
  async get<T = any>(url: string, params?: any, options?: RequestOptions): Promise<ApiResponse<T>> {
    return this.request<T>({
      method: 'GET',
      url,
      params,
      ...options
    })
  }

  // POST请求
  async post<T = any>(url: string, data?: any, options?: RequestOptions): Promise<ApiResponse<T>> {
    return this.request<T>({
      method: 'POST',
      url,
      data,
      ...options
    })
  }

  // PUT请求
  async put<T = any>(url: string, data?: any, options?: RequestOptions): Promise<ApiResponse<T>> {
    return this.request<T>({
      method: 'PUT',
      url,
      data,
      ...options
    })
  }

  // DELETE请求
  async delete<T = any>(url: string, options?: RequestOptions): Promise<ApiResponse<T>> {
    return this.request<T>({
      method: 'DELETE',
      url,
      ...options
    })
  }

  // PATCH请求
  async patch<T = any>(url: string, data?: any, options?: RequestOptions): Promise<ApiResponse<T>> {
    return this.request<T>({
      method: 'PATCH',
      url,
      data,
      ...options
    })
  }

  // 文件上传
  async upload<T = any>(url: string, file: File, options?: RequestOptions): Promise<ApiResponse<T>> {
    const formData = new FormData()
    formData.append('file', file)

    return this.request<T>({
      method: 'POST',
      url,
      data: formData,
      headers: {
        'Content-Type': 'multipart/form-data'
      },
      ...options
    })
  }

  // 批量文件上传
  async uploadMultiple<T = any>(url: string, files: File[], options?: RequestOptions): Promise<ApiResponse<T>> {
    const formData = new FormData()
    files.forEach((file, index) => {
      formData.append(`files[${index}]`, file)
    })

    return this.request<T>({
      method: 'POST',
      url,
      data: formData,
      headers: {
        'Content-Type': 'multipart/form-data'
      },
      ...options
    })
  }

  // 下载文件
  async download(url: string, filename?: string, options?: RequestOptions): Promise<void> {
    try {
      const response = await this.instance.request({
        method: 'GET',
        url,
        responseType: 'blob',
        ...options
      })

      // 创建blob链接并下载
      const blob = new Blob([response.data])
      const downloadUrl = window.URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = downloadUrl
      link.download = filename || 'download'
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
      window.URL.revokeObjectURL(downloadUrl)
    } catch (error) {
      ElMessage.error('下载失败')
      throw error
    }
  }

  // 获取axios实例(用于特殊需求)
  getInstance(): AxiosInstance {
    return this.instance
  }

  // 设置默认请求头
  setDefaultHeader(key: string, value: string): void {
    this.instance.defaults.headers.common[key] = value
  }

  // 移除默认请求头
  removeDefaultHeader(key: string): void {
    delete this.instance.defaults.headers.common[key]
  }
}

// 创建默认实例
const request = new Request()

// 导出实例和类
export default request
export { Request }

// 导出类型
export type { ApiResponse, RequestOptions, RequestConfig }

posted @ 2025-08-04 14:33  OvOGhostFace  阅读(8)  评论(0)    收藏  举报