diff --git a/src/hooks/common/useBoolean.ts b/src/hooks/common/useBoolean.ts index 674dc5f2..54af6770 100644 --- a/src/hooks/common/useBoolean.ts +++ b/src/hooks/common/useBoolean.ts @@ -3,20 +3,22 @@ import { ref } from 'vue'; export default function useBoolean(initValue: boolean = false) { const bool = ref(initValue); + function setBool(value: boolean) { + bool.value = value; + } function setTrue() { - bool.value = true; + setBool(true); } - function setFalse() { - bool.value = false; + setBool(false); } - function toggle() { - bool.value = !bool.value; + setBool(!bool.value); } return { bool, + setBool, setTrue, setFalse, toggle diff --git a/src/service/api/auth.ts b/src/service/api/auth.ts index e69de29b..cb0ff5c3 100644 --- a/src/service/api/auth.ts +++ b/src/service/api/auth.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/service/api/index.ts b/src/service/api/index.ts new file mode 100644 index 00000000..269586ee --- /dev/null +++ b/src/service/api/index.ts @@ -0,0 +1 @@ +export * from './auth'; diff --git a/src/service/index.ts b/src/service/index.ts index e69de29b..b1c13e73 100644 --- a/src/service/index.ts +++ b/src/service/index.ts @@ -0,0 +1 @@ +export * from './api'; diff --git a/src/service/middleware/auth.ts b/src/service/middleware/auth.ts index e69de29b..c5b9d568 100644 --- a/src/service/middleware/auth.ts +++ b/src/service/middleware/auth.ts @@ -0,0 +1 @@ +export function handleResponse() {} diff --git a/src/service/middleware/index.ts b/src/service/middleware/index.ts new file mode 100644 index 00000000..269586ee --- /dev/null +++ b/src/service/middleware/index.ts @@ -0,0 +1 @@ +export * from './auth'; diff --git a/src/service/request/axios/index.ts b/src/service/request/axios/index.ts new file mode 100644 index 00000000..82618924 --- /dev/null +++ b/src/service/request/axios/index.ts @@ -0,0 +1,8 @@ +import type { AxiosRequestConfig } from 'axios'; +import CustomAxiosInstance from './instance'; +import Request from './request'; + +export function createRequest(axiosConfig: AxiosRequestConfig) { + const customInstance = new CustomAxiosInstance(axiosConfig); + return new Request(customInstance.instance); +} diff --git a/src/service/request/instance.ts b/src/service/request/axios/instance.ts similarity index 55% rename from src/service/request/instance.ts rename to src/service/request/axios/instance.ts index 97fad8a7..6d4c797d 100644 --- a/src/service/request/instance.ts +++ b/src/service/request/axios/instance.ts @@ -1,9 +1,7 @@ import axios from 'axios'; -import qs from 'qs'; -import type { AxiosRequestConfig, AxiosInstance } from 'axios'; -import { ContentType } from '@/enum'; +import type { AxiosRequestConfig, AxiosInstance, CancelTokenStatic } from 'axios'; import { getToken } from '@/utils'; -import { transformFile, errorHandler } from '../utils'; +import { transformRequestData, handleResponseError } from '../helpers'; export interface StatusConfig { /** 表明请求状态的属性key */ @@ -16,54 +14,47 @@ export interface StatusConfig { /** * 封装axios请求类 - * @author Soybean(曹理斌) 2021-03-13 - * @class CustomAxiosInstance + * @author Soybean 2021-03-13 */ export default class CustomAxiosInstance { instance: AxiosInstance; - constructor( - axiosConfig: AxiosRequestConfig, - statusConfig: StatusConfig = { - statusKey: 'code', - msgKey: 'message', - successCode: 200 - } - ) { + cancelToken: CancelTokenStatic; + + private statusConfig: StatusConfig = { + statusKey: 'code', + msgKey: 'message', + successCode: 200 + }; + + constructor(axiosConfig: AxiosRequestConfig) { this.instance = axios.create(axiosConfig); - this.setInterceptor(statusConfig); + this.cancelToken = axios.CancelToken; + this.setInterceptor(); } /** 设置请求拦截器 */ - setInterceptor(statusConfig: StatusConfig): void { + setInterceptor(): void { this.instance.interceptors.request.use( async config => { const handleConfig = { ...config }; if (handleConfig.headers) { - // form类型转换 - if (handleConfig.headers['Content-Type'] === ContentType.formUrlencoded) { - handleConfig.data = qs.stringify(handleConfig.data); - } - // 文件类型转换 - if (handleConfig?.headers['Content-Type'] === ContentType.formData) { - const key = Object.keys(handleConfig.data)[0]; - const file = handleConfig.data[key]; - handleConfig.data = await transformFile(file, key); - } + // 数据转换 + handleConfig.data = await transformRequestData(handleConfig.data, handleConfig.headers['Content-Type']); // 设置token handleConfig.headers.Authorization = getToken(); } return handleConfig; }, error => { - errorHandler(error); + handleResponseError(error); return Promise.reject(error); } ); this.instance.interceptors.response.use( response => { const { status, data } = response; - const { statusKey, msgKey, successCode } = statusConfig; + const { statusKey, msgKey, successCode } = this.statusConfig; if (status === 200 || status < 300 || status === 304) { const responseData = data as any; if (responseData[statusKey] === successCode) { @@ -73,11 +64,11 @@ export default class CustomAxiosInstance { return Promise.reject(responseData[msgKey]); } const error = { response }; - errorHandler(error); + handleResponseError(error); return Promise.reject(error); }, error => { - errorHandler(error); + handleResponseError(error); return Promise.reject(error); } ); diff --git a/src/service/request/request.ts b/src/service/request/axios/request.ts similarity index 50% rename from src/service/request/request.ts rename to src/service/request/axios/request.ts index 0f9ad97f..4bd84fac 100644 --- a/src/service/request/request.ts +++ b/src/service/request/axios/request.ts @@ -1,6 +1,4 @@ import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios'; -import CustomAxiosInstance from './instance'; -import type { StatusConfig } from './instance'; type ResponseSuccess = [null, any]; type ResponseFail = [any, null]; @@ -10,7 +8,7 @@ type ResponseFail = [any, null]; * @author Soybean 2021-03-15 * @class Request */ -class Request { +export default class Request { instance: AxiosInstance; constructor(instance: AxiosInstance) { @@ -44,8 +42,37 @@ class Request { } } -export function createRequest(axiosConfig: AxiosRequestConfig, statusConfig?: StatusConfig) { - const customInstance = new CustomAxiosInstance(axiosConfig, statusConfig); - const request = new Request(customInstance.instance); - return request; -} +// import type { AxiosRequestConfig, AxiosInstance } from 'axios'; +// import { useBoolean } from '@/hooks'; + +// type RequestMethod = 'get' | 'post' | 'put' | 'delete'; + +// interface RequestParam { +// /** axios实例 */ +// instance: AxiosInstance; +// /** 请求地址 */ +// url: string; +// /** 请求方法 */ +// method?: RequestMethod; +// /** axios请求配置 */ +// axiosConfig?: AxiosRequestConfig; +// /** 请求结果的数据判断是否为空的函数 */ +// responseDataEmptyFunc?: (data: ResponseData) => boolean; +// /** 全局请求错误时是否弹出消息 */ +// showErrorMsg?: boolean; +// } + +// /** +// * 请求函数hooks +// * @param requestParam - 请求函数的参数 +// * @param url - 请求地址 +// * @param axiosConfig +// */ +// export default function useRequest(requestParam: RequestParam) { +// /** 网络状况 */ +// const { bool: networkStatus, setBool: setNetworkStatus } = useBoolean(window.navigator.onLine); +// /** 是否正在请求 */ +// const { bool: isFetching, setBool: setIsFetching } = useBoolean(); +// /** 响应的结果数据是否为空 */ +// const { bool: isEmpty, setBool: setIsEmpty } = useBoolean(); +// } diff --git a/src/service/request/helpers/error.ts b/src/service/request/helpers/error.ts new file mode 100644 index 00000000..a7fd8e28 --- /dev/null +++ b/src/service/request/helpers/error.ts @@ -0,0 +1,50 @@ +const ERROR_STATUS = { + 400: '400: 请求出现语法错误', + 401: '401: 用户未授权~', + 403: '403: 服务器拒绝访问~', + 404: '404: 请求的资源不存在~', + 405: '405: 请求方法未允许~', + 408: '408: 网络请求超时~', + 500: '500: 服务器内部错误~', + 501: '501: 服务器未实现请求功能~', + 502: '502: 错误网关~', + 503: '503: 服务不可用~', + 504: '504: 网关超时~', + 505: '505: http版本不支持该请求~' +}; + +type ErrorStatus = keyof typeof ERROR_STATUS; + +type ErrorMsg = [boolean, string]; +/** + * 获取请求失败的错误 + * @param error + */ +export function getFailRequestErrorMsg(error: any) { + const errorAction: ErrorMsg[] = [ + [!window.navigator.onLine || error.message === 'Network Error', '网络不可用~'], + [error.code === 'ECONNABORTED' && error.message.includes('timeout'), '网络连接超时~'], + [error.response, ERROR_STATUS[error.response.status as ErrorStatus]] + ]; + let errorMsg = '请求错误~'; + errorAction.some(item => { + const [flag, msg] = item; + if (flag) { + errorMsg = msg; + } + return flag; + }); + return errorMsg; +} + +/** + * 处理请求失败的错误 + * @param error - 错误 + */ +export function handleResponseError(error: any) { + const { $message: Message } = window; + + const msg = getFailRequestErrorMsg(error); + + Message?.error(msg); +} diff --git a/src/service/request/helpers/handler.ts b/src/service/request/helpers/handler.ts new file mode 100644 index 00000000..ee2c8bf9 --- /dev/null +++ b/src/service/request/helpers/handler.ts @@ -0,0 +1,16 @@ +// type ResultHandler = (...arg: any) => T; +// type SuccessRequest = { +// error: null; +// data: T; +// }; +// type FailRequest = { +// error: any; +// data: null; +// }; +// type RequestResult = SuccessRequest | FailRequest; +// /** +// * 对请求的结果数据进行格式化的处理 +// * @param handleFunc - 处理函数 +// * @param requests - 请求结果 +// */ +// export function handleResponse(resultHandler: ResultHandler, requests: RequestResult[]) {} diff --git a/src/service/request/helpers/index.ts b/src/service/request/helpers/index.ts new file mode 100644 index 00000000..67293c80 --- /dev/null +++ b/src/service/request/helpers/index.ts @@ -0,0 +1,2 @@ +export * from './transform'; +export * from './error'; diff --git a/src/service/request/helpers/transform.ts b/src/service/request/helpers/transform.ts new file mode 100644 index 00000000..bc7f7e3c --- /dev/null +++ b/src/service/request/helpers/transform.ts @@ -0,0 +1,40 @@ +import qs from 'qs'; +import FormData from 'form-data'; +import { isArray } from '@/utils'; +import { ContentType } from '@/enum'; + +export async function transformRequestData(requestData: any, contentType?: string) { + // application/json类型不处理 + let data = requestData; + // form类型转换 + if (contentType === ContentType.formUrlencoded) { + data = qs.stringify(requestData); + } + // form-data类型转换 + if (contentType === ContentType.formData) { + const key = Object.keys(requestData)[0]; + const file = requestData.data[key]; + data = await transformFile(file, key); + } + return data; +} + +/** + * 接口为上传文件的类型时数据转换 + * @param file - 单文件或多文件 + * @param key - 文件的属性名 + */ +async function transformFile(file: File[] | File, key: string) { + const formData = new FormData(); + if (isArray(file)) { + await Promise.all( + (file as File[]).map(item => { + formData.append(key, item); + return true; + }) + ); + } else { + await formData.append(key, file); + } + return formData; +} diff --git a/src/service/request/index.ts b/src/service/request/index.ts index 27f612a5..8a26bccc 100644 --- a/src/service/request/index.ts +++ b/src/service/request/index.ts @@ -1,4 +1,4 @@ -import { createRequest } from './request'; +import { createRequest } from './axios'; export const adminRequest = createRequest({ baseURL: import.meta.env.VITE_HTTP_URL_EMOSS_ADMIN as string diff --git a/src/service/utils/index.ts b/src/service/utils/index.ts deleted file mode 100644 index de67a933..00000000 --- a/src/service/utils/index.ts +++ /dev/null @@ -1,121 +0,0 @@ -import FormData from 'form-data'; -import { isArray } from '@/utils'; - -type HandleFunc = (...arg: any) => T; -type RequestError = any; -type RequestData = any; -type RequestResult = [RequestError, RequestData]; -/** - * 对请求的结果数据进行格式化的处理 - * @param handleFunc - 处理函数 - * @param requests - 请求结果 - */ -export function handleResponse(handleFunc: HandleFunc, ...requests: RequestResult[]) { - let handleData: any = null; - let error: any = null; - const hasError = requests.some(item => { - const isError = Boolean(item[0]); - if (isError) { - [error] = item; - } - return isError; - }); - if (!hasError) { - handleData = handleFunc(...requests.map(item => item[1])); - } - return [error, handleData] as [any, T]; -} - -/** - * 接口为上传文件的类型时数据转换 - * @param file - 单文件或多文件 - * @param key - 文件的属性名 - */ -export async function transformFile(file: File[] | File, key: string) { - const formData = new FormData(); - if (isArray(file)) { - await Promise.all( - (file as File[]).map(item => { - formData.append(key, item); - return true; - }) - ); - } else { - await formData.append(key, file); - } - return formData; -} - -const ERROR_STATUS = { - 400: '400: 请求出现语法错误', - 401: '401: 用户未授权~', - 403: '403: 服务器拒绝访问~', - 404: '404: 请求的资源不存在~', - 405: '405: 请求方法未允许~', - 408: '408: 网络请求超时~', - 500: '500: 服务器内部错误~', - 501: '501: 服务器未实现请求功能~', - 502: '502: 错误网关~', - 503: '503: 服务不可用~', - 504: '504: 网关超时~', - 505: '505: http版本不支持该请求~' -}; -type ErrorStatus = keyof typeof ERROR_STATUS; - -/** - * 网络请求错误状态处理 - * @param error - 错误 - */ -export function errorHandler(error: any): void { - const { $message: Message } = window; - if (error.response) { - const status = error.response.status as ErrorStatus; - Message?.error(ERROR_STATUS[status]); - return; - } - if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) { - Message?.error('网络连接超时~'); - return; - } - if (!window.navigator.onLine || error.message === 'Network Error') { - Message?.error('网络不可用~'); - return; - } - Message?.error('请求错误~'); -} - -/** - * 连续的请求错误依此显示 - * @param duration - 上一次弹出错误消息到下一次的时间(ms) - */ -export function continuousErrorHandler(duration: number) { - let errorStacks: string[] = []; - function pushError(id: string) { - errorStacks.push(id); - } - function removeError(id: string) { - errorStacks = errorStacks.filter(item => item !== id); - } - function handleError(id: string, callback: Function) { - callback(); - setTimeout(() => { - removeError(id); - }, duration); - } - - function handleContinuousError(callback: Function) { - const id = Date.now().toString(36); - const { length } = errorStacks; - if (length > 0) { - pushError(id); - setTimeout(() => { - handleError(id, callback); - }, duration * length); - } else { - pushError(id); - handleError(id, callback); - } - } - - return handleContinuousError; -}