| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 | <%const { apiConfig, generateResponses, config } = it;%>import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, HeadersDefaults, ResponseType, RawAxiosRequestHeaders } from 'axios'import { ElLoading, ElMessage, LoadingOptions } from 'element-plus'import { storeToRefs } from 'pinia'import { useUserInfo } from '/@/stores/userInfo'export type QueryParamsType = Record<string | number, any>;export interface FullRequestParams extends Omit<AxiosRequestConfig, "data" | "params" | "url" | "responseType"> {    secure?: boolean;    path: string;    type?: ContentType;    query?: QueryParamsType;    format?: ResponseType;    body?: unknown;    showErrorMessage?: boolean    showSuccessMessage?: boolean    login?: boolean    loading?: boolean    loadingOptions?: LoadingOptions    cancelRepeatRequest?: boolean}export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">;export interface ApiConfig<SecurityDataType = unknown> extends Omit<AxiosRequestConfig, "data" | "cancelToken"> {  securityWorker?: (securityData: SecurityDataType | null) => Promise<AxiosRequestConfig | void> | AxiosRequestConfig | void;  secure?: boolean;  format?: ResponseType;}export enum ContentType {  Json = "application/json",  FormData = "multipart/form-data",  UrlEncoded = "application/x-www-form-urlencoded",  Text = "text/plain",}export interface LoadingInstance {  target: any  count: number}const pendingMap = new Map()const loadingInstance: LoadingInstance = {  target: null,  count: 0,}export class HttpClient<SecurityDataType = unknown> {    public instance: AxiosInstance;    private securityData: SecurityDataType | null = null;    private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];    private secure?: boolean;    private format?: ResponseType;    constructor({ securityWorker, secure, format, ...axiosConfig }: ApiConfig<SecurityDataType> = {}) {        this.instance = axios.create({ ...axiosConfig, timeout: 60000, baseURL: axiosConfig.baseURL || import.meta.env.VITE_API_URL })        this.secure = secure;        this.format = format;        this.securityWorker = securityWorker;    }    public setSecurityData = (data: SecurityDataType | null) => {        this.securityData = data    }    protected mergeRequestParams(params1: AxiosRequestConfig, params2?: AxiosRequestConfig): AxiosRequestConfig {      const method = params1.method || (params2 && params2.method)      return {        ...this.instance.defaults,        ...params1,        ...(params2 || {}),        headers: {          ...((method && this.instance.defaults.headers[method.toLowerCase() as keyof HeadersDefaults]) || {}),          ...(params1.headers || {}),          ...((params2 && params2.headers) || {}),        } as RawAxiosRequestHeaders,      };    }    protected stringifyFormItem(formItem: unknown) {      if (typeof formItem === "object" && formItem !== null) {        return JSON.stringify(formItem);      } else {        return `${formItem}`;      }    }    protected createFormData(input: Record<string, unknown>): FormData {      return Object.keys(input || {}).reduce((formData, key) => {        const property = input[key];        const propertyContent: any[] = (property instanceof Array) ? property : [property]        for (const formItem of propertyContent) {          const isFileType = formItem instanceof Blob || formItem instanceof File;          formData.append(            key,            isFileType ? formItem : this.stringifyFormItem(formItem)            );        }        return formData;      }, new FormData());    }       protected errorHandle(error: any) {     if (!error) {       return     }     if (axios.isCancel(error)) return console.error('请求重复已被自动取消:' + error.message)     let message = ''     if (error.response) {       switch (error.response.status) {         case 302:           message = '接口重定向'           break         case 400:           message = '参数不正确'           break         case 401:           message = '您还没有登录'           break         case 403:           message = '您没有权限操作'           break         case 404:           message = '请求地址出错:' + error.response.config.url           break         case 408:           message = '请求超时'           break         case 409:           message = '系统已存在相同数据'           break         case 500:           message = '服务器内部错误'           break         case 501:           message = '服务未实现'           break         case 502:           message = '网关错误'           break         case 503:           message = '服务不可用'           break         case 504:           message = '服务暂时无法访问,请稍后再试'           break         case 505:           message = 'HTTP版本不受支持'           break         default:           message = '异常问题,请联系网站管理员'           break       }     }     if (error.message.includes('timeout')) message = '请求超时'     if (error.message.includes('Network')) message = window.navigator.onLine ? '服务端异常' : '您已断网'      if (message) {       ElMessage.error({ message, grouping: true })     }   }         protected async refreshToken(config: any) {      const storesUseUserInfo = useUserInfo()      const { userInfos } = storeToRefs(storesUseUserInfo)      const token = userInfos.value.token      if (!token) {        storesUseUserInfo.clear()        return Promise.reject(config)      }      if (window.tokenRefreshing) {        window.requests = window.requests ? window.requests : []        return new Promise((resolve) => {          window.requests.push(() => {            resolve(this.instance(config))          })        })      }      window.tokenRefreshing = true      return this.request<AxiosResponse, any>({        path: `/api/admin/auth/refresh`,        method: 'GET',        secure: true,        format: 'json',        login: false,        query: {          token: token,        },      })      .then((res) => {        if (res?.success) {          const token = res.data.token          storesUseUserInfo.setToken(token)          if (window.requests?.length > 0) {            window.requests.forEach((apiRequest) => apiRequest())            window.requests = []          }          return this.instance(config)        } else {          storesUseUserInfo.clear()          return Promise.reject(res)        }      })      .catch((error) => {        storesUseUserInfo.clear()        return Promise.reject(error)      })      .finally(() => {        window.tokenRefreshing = false      })    }        protected addPending(config: AxiosRequestConfig) {      const pendingKey = this.getPendingKey(config)      config.cancelToken =        config.cancelToken ||        new axios.CancelToken((cancel) => {          if (!pendingMap.has(pendingKey)) {            pendingMap.set(pendingKey, cancel)          }        })    }        protected removePending(config: AxiosRequestConfig) {      const pendingKey = this.getPendingKey(config)      if (pendingMap.has(pendingKey)) {        const cancelToken = pendingMap.get(pendingKey)        cancelToken(pendingKey)        pendingMap.delete(pendingKey)      }    }        protected getPendingKey(config: AxiosRequestConfig) {      let { data, headers } = config      headers = headers as RawAxiosRequestHeaders      const { url, method, params } = config      if (typeof data === 'string') data = JSON.parse(data)      return [url, method, headers && headers.Authorization ? headers.Authorization : '', JSON.stringify(params), JSON.stringify(data)].join('&')    }        protected closeLoading(loading: boolean = false) {      if (loading && loadingInstance.count > 0) loadingInstance.count--      if (loadingInstance.count === 0) {        loadingInstance.target.close()        loadingInstance.target = null      }    }    public request = async <T = any, _E = any>({        secure,        path,        type,        query,        format,        body,        showErrorMessage = true,        showSuccessMessage = false,        login = true,        loading = false,        loadingOptions = {},        cancelRepeatRequest = false,        ...params<% if (config.unwrapResponseData) { %>    }: FullRequestParams): Promise<T> => {<% } else { %>    }: FullRequestParams): Promise<AxiosResponse<T>> => {<% } %>        const secureParams = ((typeof secure === 'boolean' ? secure : this.secure) && this.securityWorker && (await this.securityWorker(this.securityData))) || {};        const requestParams = this.mergeRequestParams(params, secureParams);        const responseFormat = (format || this.format) || undefined;        if (type === ContentType.FormData && body && body !== null && typeof body === "object") {          body = this.createFormData(body as Record<string, unknown>);        }        if (type === ContentType.Text && body && body !== null && typeof body !== "string") {          body = JSON.stringify(body);        }                this.instance.interceptors.request.use(          (config) => {            this.removePending(config)            cancelRepeatRequest && this.addPending(config)            if (loading) {              loadingInstance.count++              if (loadingInstance.count === 1) {                loadingInstance.target = ElLoading.service(loadingOptions)              }            }            const { userInfos } = storeToRefs(useUserInfo())            const accessToken = userInfos.value.token            config.headers!['Authorization'] = `Bearer ${accessToken}`            return config          },          (error) => {            return Promise.reject(error)          }        )                this.instance.interceptors.response.use(          (res) => {            this.removePending(res.config)            loading && this.closeLoading(loading)            const data = res.data            if (data.success) {              if (showSuccessMessage) {                ElMessage.success({ message: data.msg ? data.msg : '操作成功', grouping: true })              }            } else {              if (showErrorMessage) {                ElMessage.error({ message: data.msg ? data.msg : '操作失败', grouping: true })              }                          }            return res          },          async (error) => {            error.config && this.removePending(error.config)            loading && this.closeLoading(loading)                        if (login && error?.response?.status === 401) {              return this.refreshToken(error.config)            }                        if (showErrorMessage) {              this.errorHandle(error)            }            return Promise.reject(error)          }        )        return this.instance.request({            ...requestParams,            headers: {                ...(requestParams.headers || {}),                ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),            } as RawAxiosRequestHeaders,            params: query,            responseType: responseFormat,            data: body,            url: path,<% if (config.unwrapResponseData) { %>        }).then(response => response.data);<% } else { %>        });<% } %>    };}
 |