| 比對新檔案 |
| | |
| | | import { AxiosError } from 'axios' |
| | | import { useAxiosStore } from '@/stores/axios' |
| | | import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' |
| | | |
| | | const baseUrl = import.meta.env.VITE_BASE_API |
| | | |
| | | export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' |
| | | |
| | | /** API 模式 */ |
| | | export enum API_MODE { |
| | | PROD = 'prod', |
| | | TEST = 'test', |
| | | } |
| | | |
| | | /** 請求成功後,後端 API 統一回傳的格式 */ |
| | | export type ResponseWrapper<D> = { |
| | | code: string |
| | | msg: string |
| | | data: D |
| | | sysDate: string |
| | | } |
| | | |
| | | /** 回傳給調用者的成功資料 */ |
| | | type SuccessResponse<Data> = { |
| | | success: true |
| | | response: AxiosResponse |
| | | data: Data |
| | | msg: string |
| | | code: string |
| | | sysDate: string |
| | | } |
| | | |
| | | /** 回傳給調用者的失敗資料格式 */ |
| | | type FailureResponse = { |
| | | success: false |
| | | response: AxiosResponse |
| | | msg: string |
| | | data: null |
| | | code: string |
| | | sysDate: string |
| | | } |
| | | |
| | | export type ApiResponse<Data = unknown> = SuccessResponse<Data> | FailureResponse |
| | | |
| | | interface RequestInfo<ResponseData> { |
| | | /** 執行請求的函式 */ |
| | | executor: (axiosInstance: AxiosInstance) => Promise<AxiosResponse<ResponseWrapper<ResponseData>>> |
| | | /** 請求位址 */ |
| | | url: string |
| | | /** HTTP 方法 */ |
| | | method: HttpMethod |
| | | /** 失敗時是否跳出彈窗 */ |
| | | showFailurePopup: boolean |
| | | } |
| | | |
| | | function getMode(apiModeEnum?: API_MODE) { |
| | | let modeValue: API_MODE[keyof API_MODE] |
| | | switch (true) { |
| | | case import.meta.env.PROD: |
| | | modeValue = API_MODE.PROD |
| | | break |
| | | case !!apiModeEnum: |
| | | modeValue = apiModeEnum |
| | | break |
| | | default: |
| | | modeValue = API_MODE.TEST |
| | | break |
| | | } |
| | | return modeValue === API_MODE.PROD ? null : API_MODE.TEST |
| | | } |
| | | |
| | | /** |
| | | * 方法: 回傳一個可以由 useRequest().apiRequest 使用,並進行非同步請求的函式 |
| | | * @param axiosRequestConfig 應至少包含: |
| | | * @ url: 路徑 |
| | | * @ method: http 方法 |
| | | * @ mode: 是否啟用 mock server worker, 應為空字串 (正式環境) 或 'test' (測試環境) |
| | | * @example |
| | | * function doMyRequest(data) { |
| | | * return defineRequest( |
| | | * { url: '/myRequestUrl', method: 'POST', mode: API_MODE.PROD, data })} |
| | | */ |
| | | export function defineRequest<ResponseData>( |
| | | axiosRequestConfig: AxiosRequestConfig & { |
| | | url: string |
| | | method: HttpMethod |
| | | mode?: API_MODE |
| | | }, |
| | | additionalConfig?: { |
| | | showFailurePopup?: boolean |
| | | }, |
| | | ): RequestInfo<ResponseData> { |
| | | const mode = getMode(axiosRequestConfig.mode) |
| | | return { |
| | | executor: (axiosInstance) => { |
| | | return axiosInstance.request({ |
| | | ...axiosRequestConfig, |
| | | url: `${baseUrl}${axiosRequestConfig.url}`, |
| | | data: axiosRequestConfig.data ?? {}, |
| | | params: { ...axiosRequestConfig.params, mode }, |
| | | }) |
| | | }, |
| | | url: axiosRequestConfig.url, |
| | | method: axiosRequestConfig.method, |
| | | showFailurePopup: additionalConfig?.showFailurePopup ?? true, |
| | | } |
| | | } |
| | | |
| | | export default function useRequest() { |
| | | const axiosStore = useAxiosStore() |
| | | |
| | | /** 方法: 執行已透過 defineRequest 定義好的非同步函式,並取得回傳結果 |
| | | * @ 成功時,可以解構出 success, data, msg, code, sysDate, response 等屬性,其中 data 為 response.data.data |
| | | * @example |
| | | * const { success, data, msg, code, response } = await apiRequest(doMyRequest(payload)) |
| | | * if (success) {...} |
| | | * else {...} |
| | | */ |
| | | |
| | | async function apiRequest<ResponseData>( |
| | | request: RequestInfo<ResponseData>, |
| | | ): Promise<ApiResponse<ResponseData>> { |
| | | return ( |
| | | request |
| | | // 執行請求 |
| | | .executor(axiosStore.axiosInstance) |
| | | .then((response) => { |
| | | const { data, msg, code, sysDate } = response.data |
| | | // 若 code 為 200 ,則回傳資料 |
| | | if (code === '200') { |
| | | return { |
| | | success: true, |
| | | data, |
| | | msg, |
| | | code, |
| | | sysDate, |
| | | response, |
| | | } as SuccessResponse<ResponseData> |
| | | } else { |
| | | // 否則回傳失敗 |
| | | return { success: false, data: null, msg, code, sysDate, response } as FailureResponse |
| | | } |
| | | }) |
| | | // 請求失敗時,回傳失敗 |
| | | .catch((err: AxiosError) => { |
| | | const sysDate = new Date().toISOString() |
| | | const msg = err.message |
| | | const code = err.code ?? '' |
| | | return { |
| | | success: false, |
| | | data: null, |
| | | msg, |
| | | code, |
| | | sysDate, |
| | | response: err.response, |
| | | } as FailureResponse |
| | | }) |
| | | ) |
| | | } |
| | | |
| | | return { apiRequest } |
| | | } |