mirror of
				https://github.com/soybeanjs/soybean-admin.git
				synced 2025-11-04 15:53:43 +08:00 
			
		
		
		
	feat(projects): @sa/axios: createRequest, createFlatRequest, createHookRequest
This commit is contained in:
		@@ -9,7 +9,8 @@ export default defineConfig(
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
          ignores: ['index', 'App', '[id]']
 | 
					          ignores: ['index', 'App', '[id]']
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      ]
 | 
					      ],
 | 
				
			||||||
 | 
					      'no-empty-function': 'off'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,10 +25,10 @@
 | 
				
			|||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@better-scroll/core": "2.5.1",
 | 
					    "@better-scroll/core": "2.5.1",
 | 
				
			||||||
    "@iconify/vue": "4.1.1",
 | 
					    "@iconify/vue": "4.1.1",
 | 
				
			||||||
 | 
					    "@sa/axios": "workspace:*",
 | 
				
			||||||
    "@sa/color-palette": "workspace:*",
 | 
					    "@sa/color-palette": "workspace:*",
 | 
				
			||||||
    "@sa/hooks": "workspace:*",
 | 
					    "@sa/hooks": "workspace:*",
 | 
				
			||||||
    "@sa/materials": "workspace:*",
 | 
					    "@sa/materials": "workspace:*",
 | 
				
			||||||
    "@sa/request": "workspace:*",
 | 
					 | 
				
			||||||
    "@sa/utils": "workspace:*",
 | 
					    "@sa/utils": "workspace:*",
 | 
				
			||||||
    "@vueuse/core": "10.7.2",
 | 
					    "@vueuse/core": "10.7.2",
 | 
				
			||||||
    "clipboard": "2.0.11",
 | 
					    "clipboard": "2.0.11",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								packages/axios/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								packages/axios/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "@sa/axios",
 | 
				
			||||||
 | 
					  "version": "1.0.0",
 | 
				
			||||||
 | 
					  "exports": {
 | 
				
			||||||
 | 
					    ".": "./src/index.ts"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "typesVersions": {
 | 
				
			||||||
 | 
					    "*": {
 | 
				
			||||||
 | 
					      "*": ["./src/*"]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "@sa/utils": "workspace:*",
 | 
				
			||||||
 | 
					    "axios": "1.6.5",
 | 
				
			||||||
 | 
					    "axios-retry": "^4.0.0"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								packages/axios/src/constant.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								packages/axios/src/constant.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					/** request id key */
 | 
				
			||||||
 | 
					export const REQUEST_ID_KEY = 'X-Request-Id';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** the backend error code key */
 | 
				
			||||||
 | 
					export const BACKEND_ERROR_CODE = 'BACKEND_ERROR';
 | 
				
			||||||
							
								
								
									
										176
									
								
								packages/axios/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								packages/axios/src/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
				
			|||||||
 | 
					import axios, { AxiosError } from 'axios';
 | 
				
			||||||
 | 
					import type { AxiosResponse, CancelTokenSource, CreateAxiosDefaults, InternalAxiosRequestConfig } from 'axios';
 | 
				
			||||||
 | 
					import axiosRetry from 'axios-retry';
 | 
				
			||||||
 | 
					import { nanoid } from '@sa/utils';
 | 
				
			||||||
 | 
					import { createAxiosConfig, createDefaultOptions, createRetryOptions } from './options';
 | 
				
			||||||
 | 
					import { BACKEND_ERROR_CODE, REQUEST_ID_KEY } from './constant';
 | 
				
			||||||
 | 
					import type {
 | 
				
			||||||
 | 
					  CustomAxiosRequestConfig,
 | 
				
			||||||
 | 
					  FlatRequestInstance,
 | 
				
			||||||
 | 
					  MappedType,
 | 
				
			||||||
 | 
					  RequestInstance,
 | 
				
			||||||
 | 
					  RequestOption,
 | 
				
			||||||
 | 
					  ResponseType
 | 
				
			||||||
 | 
					} from './type';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function createCommonRequest<ResponseData = any>(
 | 
				
			||||||
 | 
					  axiosConfig?: CreateAxiosDefaults,
 | 
				
			||||||
 | 
					  options?: Partial<RequestOption<ResponseData>>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const opts = createDefaultOptions<ResponseData>(options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const axiosConf = createAxiosConfig(axiosConfig);
 | 
				
			||||||
 | 
					  const instance = axios.create(axiosConf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const cancelTokenSourceMap = new Map<string, CancelTokenSource>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // config axios retry
 | 
				
			||||||
 | 
					  const retryOptions = createRetryOptions(axiosConf);
 | 
				
			||||||
 | 
					  axiosRetry(instance, retryOptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  instance.interceptors.request.use(conf => {
 | 
				
			||||||
 | 
					    const config: InternalAxiosRequestConfig = { ...conf };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // set request id
 | 
				
			||||||
 | 
					    const requestId = nanoid();
 | 
				
			||||||
 | 
					    config.headers.set(REQUEST_ID_KEY, requestId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // config cancel token
 | 
				
			||||||
 | 
					    const cancelTokenSource = axios.CancelToken.source();
 | 
				
			||||||
 | 
					    config.cancelToken = cancelTokenSource.token;
 | 
				
			||||||
 | 
					    cancelTokenSourceMap.set(requestId, cancelTokenSource);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // handle config by hook
 | 
				
			||||||
 | 
					    const handledConfig = opts.onRequest?.(config) || config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return handledConfig;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  instance.interceptors.response.use(
 | 
				
			||||||
 | 
					    async response => {
 | 
				
			||||||
 | 
					      if (opts.isBackendSuccess(response)) {
 | 
				
			||||||
 | 
					        return Promise.resolve(response);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const fail = await opts.onBackendFail(response, instance);
 | 
				
			||||||
 | 
					      if (fail) {
 | 
				
			||||||
 | 
					        return fail;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const backendError = new AxiosError<ResponseData>(
 | 
				
			||||||
 | 
					        'the backend request error',
 | 
				
			||||||
 | 
					        BACKEND_ERROR_CODE,
 | 
				
			||||||
 | 
					        response.config,
 | 
				
			||||||
 | 
					        response,
 | 
				
			||||||
 | 
					        response.request
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await opts.onError(backendError);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Promise.reject(backendError);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async (error: AxiosError<ResponseData>) => {
 | 
				
			||||||
 | 
					      await opts.onError(error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Promise.reject(error);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function cancelRequest(requestId: string) {
 | 
				
			||||||
 | 
					    const cancelTokenSource = cancelTokenSourceMap.get(requestId);
 | 
				
			||||||
 | 
					    if (cancelTokenSource) {
 | 
				
			||||||
 | 
					      cancelTokenSource.cancel();
 | 
				
			||||||
 | 
					      cancelTokenSourceMap.delete(requestId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function cancelAllRequest() {
 | 
				
			||||||
 | 
					    cancelTokenSourceMap.forEach(cancelTokenSource => {
 | 
				
			||||||
 | 
					      cancelTokenSource.cancel();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    cancelTokenSourceMap.clear();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    instance,
 | 
				
			||||||
 | 
					    opts,
 | 
				
			||||||
 | 
					    cancelRequest,
 | 
				
			||||||
 | 
					    cancelAllRequest
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * create a request instance
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param axiosConfig axios config
 | 
				
			||||||
 | 
					 * @param options request options
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function createRequest<ResponseData = any>(
 | 
				
			||||||
 | 
					  axiosConfig?: CreateAxiosDefaults,
 | 
				
			||||||
 | 
					  options?: Partial<RequestOption<ResponseData>>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const { instance, opts, cancelRequest, cancelAllRequest } = createCommonRequest<ResponseData>(axiosConfig, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const request: RequestInstance = async function request<T = any, R extends ResponseType = 'json'>(
 | 
				
			||||||
 | 
					    config: CustomAxiosRequestConfig
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    const response: AxiosResponse<ResponseData> = await instance(config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const responseType = response.config?.responseType || 'json';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (responseType === 'json') {
 | 
				
			||||||
 | 
					      return opts.transformBackendResponse(response);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return response.data as MappedType<R, T>;
 | 
				
			||||||
 | 
					  } as RequestInstance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  request.cancelRequest = cancelRequest;
 | 
				
			||||||
 | 
					  request.cancelAllRequest = cancelAllRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return request;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * create a flat request instance
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The response data is a flat object: { data: any, error: AxiosError }
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param axiosConfig axios config
 | 
				
			||||||
 | 
					 * @param options request options
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function createFlatRequest<ResponseData = any>(
 | 
				
			||||||
 | 
					  axiosConfig?: CreateAxiosDefaults,
 | 
				
			||||||
 | 
					  options?: Partial<RequestOption<ResponseData>>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const { instance, opts, cancelRequest, cancelAllRequest } = createCommonRequest<ResponseData>(axiosConfig, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const flatRequest: FlatRequestInstance = async function flatRequest<T = any, R extends ResponseType = 'json'>(
 | 
				
			||||||
 | 
					    config: CustomAxiosRequestConfig
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const response: AxiosResponse<ResponseData> = await instance(config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const responseType = response.config?.responseType || 'json';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (responseType === 'json') {
 | 
				
			||||||
 | 
					        const data = opts.transformBackendResponse(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return { data, error: null };
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return { data: response.data as MappedType<R, T>, error: null };
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      return { data: null, error };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } as FlatRequestInstance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  flatRequest.cancelRequest = cancelRequest;
 | 
				
			||||||
 | 
					  flatRequest.cancelAllRequest = cancelAllRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return flatRequest;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { BACKEND_ERROR_CODE, REQUEST_ID_KEY };
 | 
				
			||||||
 | 
					export type * from './type';
 | 
				
			||||||
 | 
					export type { CreateAxiosDefaults, AxiosError };
 | 
				
			||||||
							
								
								
									
										44
									
								
								packages/axios/src/options.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								packages/axios/src/options.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					import type { CreateAxiosDefaults } from 'axios';
 | 
				
			||||||
 | 
					import type { IAxiosRetryConfig } from 'axios-retry';
 | 
				
			||||||
 | 
					import { isHttpSuccess } from './shared';
 | 
				
			||||||
 | 
					import type { RequestOption } from './type';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function createDefaultOptions<ResponseData = any>(options?: Partial<RequestOption<ResponseData>>) {
 | 
				
			||||||
 | 
					  const opts: RequestOption<ResponseData> = {
 | 
				
			||||||
 | 
					    onRequest: async config => config,
 | 
				
			||||||
 | 
					    isBackendSuccess: _response => true,
 | 
				
			||||||
 | 
					    onBackendFail: async () => {},
 | 
				
			||||||
 | 
					    transformBackendResponse: async response => response.data,
 | 
				
			||||||
 | 
					    onError: async () => {}
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Object.assign(opts, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return opts;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function createRetryOptions(config?: Partial<CreateAxiosDefaults>) {
 | 
				
			||||||
 | 
					  const retryConfig: IAxiosRetryConfig = {
 | 
				
			||||||
 | 
					    retries: 3
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Object.assign(retryConfig, config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return retryConfig;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function createAxiosConfig(config?: Partial<CreateAxiosDefaults>) {
 | 
				
			||||||
 | 
					  const TEN_SECONDS = 10 * 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const axiosConfig: CreateAxiosDefaults = {
 | 
				
			||||||
 | 
					    timeout: TEN_SECONDS,
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      'Content-Type': 'application/json'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    validateStatus: isHttpSuccess
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Object.assign(axiosConfig, config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return axiosConfig;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								packages/axios/src/shared.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								packages/axios/src/shared.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import type { AxiosHeaderValue, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function getContentType(config: InternalAxiosRequestConfig) {
 | 
				
			||||||
 | 
					  const contentType: AxiosHeaderValue = config.headers?.['Content-Type'] || 'application/json';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return contentType;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * check if http status is success
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param status
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function isHttpSuccess(status: number) {
 | 
				
			||||||
 | 
					  const isSuccessCode = status >= 200 && status < 300;
 | 
				
			||||||
 | 
					  return isSuccessCode || status === 304;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * is response json
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param response axios response
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function isResponseJson(response: AxiosResponse) {
 | 
				
			||||||
 | 
					  const { responseType } = response.config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return responseType === 'json' || responseType === undefined;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										97
									
								
								packages/axios/src/type.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								packages/axios/src/type.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ContentType =
 | 
				
			||||||
 | 
					  | 'text/html'
 | 
				
			||||||
 | 
					  | 'text/plain'
 | 
				
			||||||
 | 
					  | 'multipart/form-data'
 | 
				
			||||||
 | 
					  | 'application/json'
 | 
				
			||||||
 | 
					  | 'application/x-www-form-urlencoded'
 | 
				
			||||||
 | 
					  | 'application/octet-stream';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface RequestOption<ResponseData = any> {
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * The hook before request
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * For example: You can add header token in this hook
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param config Axios config
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  onRequest: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * The hook to check backend response is success or not
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param response Axios response
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  isBackendSuccess: (response: AxiosResponse<ResponseData>) => boolean;
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * The hook after backend request fail
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * For example: You can handle the expired token in this hook
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param response Axios response
 | 
				
			||||||
 | 
					   * @param instance Axios instance
 | 
				
			||||||
 | 
					   * @returns
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  onBackendFail: (
 | 
				
			||||||
 | 
					    response: AxiosResponse<ResponseData>,
 | 
				
			||||||
 | 
					    instance: AxiosInstance
 | 
				
			||||||
 | 
					  ) => Promise<AxiosResponse> | Promise<void>;
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * transform backend response when the responseType is json
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param response Axios response
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  transformBackendResponse(response: AxiosResponse<ResponseData>): any | Promise<any>;
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * The hook to handle error
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * For example: You can show error message in this hook
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param error
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  onError: (error: AxiosError<ResponseData>) => void | Promise<void>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ResponseMap {
 | 
				
			||||||
 | 
					  blob: Blob;
 | 
				
			||||||
 | 
					  text: string;
 | 
				
			||||||
 | 
					  arrayBuffer: ArrayBuffer;
 | 
				
			||||||
 | 
					  stream: ReadableStream<Uint8Array>;
 | 
				
			||||||
 | 
					  document: Document;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export type ResponseType = keyof ResponseMap | 'json';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type MappedType<R extends ResponseType, JsonType = any> = R extends keyof ResponseMap
 | 
				
			||||||
 | 
					  ? ResponseMap[R]
 | 
				
			||||||
 | 
					  : JsonType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CustomAxiosRequestConfig<R extends ResponseType = 'json'> = Omit<AxiosRequestConfig, 'responseType'> & {
 | 
				
			||||||
 | 
					  responseType?: R;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** The request instance */
 | 
				
			||||||
 | 
					export interface RequestInstance {
 | 
				
			||||||
 | 
					  <T = any, R extends ResponseType = 'json'>(config: CustomAxiosRequestConfig<R>): Promise<MappedType<R, T>>;
 | 
				
			||||||
 | 
					  cancelRequest: (requestId: string) => void;
 | 
				
			||||||
 | 
					  cancelAllRequest: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type FlatResponseSuccessData<T = any> = {
 | 
				
			||||||
 | 
					  data: T;
 | 
				
			||||||
 | 
					  error: null;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type FlatResponseFailData<T = any> = {
 | 
				
			||||||
 | 
					  data: null;
 | 
				
			||||||
 | 
					  error: AxiosError<T>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type FlatResponseData<T = any> = FlatResponseSuccessData<T> | FlatResponseFailData<T>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface FlatRequestInstance {
 | 
				
			||||||
 | 
					  <T = any, R extends ResponseType = 'json'>(
 | 
				
			||||||
 | 
					    config: CustomAxiosRequestConfig<R>
 | 
				
			||||||
 | 
					  ): Promise<FlatResponseData<MappedType<R, T>>>;
 | 
				
			||||||
 | 
					  cancelRequest: (requestId: string) => void;
 | 
				
			||||||
 | 
					  cancelAllRequest: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -8,5 +8,8 @@
 | 
				
			|||||||
    "*": {
 | 
					    "*": {
 | 
				
			||||||
      "*": ["./src/*"]
 | 
					      "*": ["./src/*"]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "@sa/axios": "workspace:*"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										79
									
								
								packages/hooks/src/use-request.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								packages/hooks/src/use-request.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					import { ref } from 'vue';
 | 
				
			||||||
 | 
					import type { Ref } from 'vue';
 | 
				
			||||||
 | 
					import { createFlatRequest } from '@sa/axios';
 | 
				
			||||||
 | 
					import type {
 | 
				
			||||||
 | 
					  AxiosError,
 | 
				
			||||||
 | 
					  CreateAxiosDefaults,
 | 
				
			||||||
 | 
					  CustomAxiosRequestConfig,
 | 
				
			||||||
 | 
					  MappedType,
 | 
				
			||||||
 | 
					  RequestOption,
 | 
				
			||||||
 | 
					  ResponseType
 | 
				
			||||||
 | 
					} from '@sa/axios';
 | 
				
			||||||
 | 
					import useLoading from './use-loading';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type HookRequestInstanceResponseSuccessData<T = any> = {
 | 
				
			||||||
 | 
					  data: Ref<T>;
 | 
				
			||||||
 | 
					  error: Ref<null>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type HookRequestInstanceResponseFailData<T = any> = {
 | 
				
			||||||
 | 
					  data: Ref<null>;
 | 
				
			||||||
 | 
					  error: Ref<AxiosError<T>>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type HookRequestInstanceResponseData<T = any> = {
 | 
				
			||||||
 | 
					  loading: Ref<boolean>;
 | 
				
			||||||
 | 
					} & (HookRequestInstanceResponseSuccessData<T> | HookRequestInstanceResponseFailData<T>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface HookRequestInstance {
 | 
				
			||||||
 | 
					  <T = any, R extends ResponseType = 'json'>(
 | 
				
			||||||
 | 
					    config: CustomAxiosRequestConfig
 | 
				
			||||||
 | 
					  ): HookRequestInstanceResponseData<MappedType<R, T>>;
 | 
				
			||||||
 | 
					  cancelRequest: (requestId: string) => void;
 | 
				
			||||||
 | 
					  cancelAllRequest: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * create a hook request instance
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param axiosConfig
 | 
				
			||||||
 | 
					 * @param options
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export default function createHookRequest<ResponseData = any>(
 | 
				
			||||||
 | 
					  axiosConfig?: CreateAxiosDefaults,
 | 
				
			||||||
 | 
					  options?: Partial<RequestOption<ResponseData>>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const request = createFlatRequest<ResponseData>(axiosConfig, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const hookRequest: HookRequestInstance = function hookRequest<T = any, R extends ResponseType = 'json'>(
 | 
				
			||||||
 | 
					    config: CustomAxiosRequestConfig
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    const { loading, startLoading, endLoading } = useLoading();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const data = ref<MappedType<R, T> | null>(null);
 | 
				
			||||||
 | 
					    const error = ref<AxiosError<MappedType<R, T>> | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    startLoading();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    request(config).then(res => {
 | 
				
			||||||
 | 
					      if (res.data) {
 | 
				
			||||||
 | 
					        data.value = res.data;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        error.value = res.error;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      endLoading();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      loading,
 | 
				
			||||||
 | 
					      data,
 | 
				
			||||||
 | 
					      error
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  } as HookRequestInstance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hookRequest.cancelRequest = request.cancelRequest;
 | 
				
			||||||
 | 
					  hookRequest.cancelAllRequest = request.cancelAllRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return hookRequest;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "@sa/request",
 | 
					  "name": "@sa/fetch",
 | 
				
			||||||
  "version": "1.0.0",
 | 
					  "version": "1.0.0",
 | 
				
			||||||
  "exports": {
 | 
					  "exports": {
 | 
				
			||||||
    ".": "./src/index.ts"
 | 
					    ".": "./src/index.ts"
 | 
				
			||||||
@@ -10,7 +10,6 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "axios": "1.6.5",
 | 
					 | 
				
			||||||
    "ofetch": "1.3.3"
 | 
					    "ofetch": "1.3.3"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
import { ofetch } from 'ofetch';
 | 
					import { ofetch } from 'ofetch';
 | 
				
			||||||
import type { FetchOptions } from 'ofetch';
 | 
					import type { FetchOptions } from 'ofetch';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function createOfetch(options: FetchOptions) {
 | 
					export function createRequest(options: FetchOptions) {
 | 
				
			||||||
  const request = ofetch.create(options);
 | 
					  const request = ofetch.create(options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return request;
 | 
					  return request;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default createOfetch;
 | 
					export default createRequest;
 | 
				
			||||||
							
								
								
									
										20
									
								
								packages/ofetch/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								packages/ofetch/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "compilerOptions": {
 | 
				
			||||||
 | 
					    "target": "ESNext",
 | 
				
			||||||
 | 
					    "jsx": "preserve",
 | 
				
			||||||
 | 
					    "lib": ["DOM", "ESNext"],
 | 
				
			||||||
 | 
					    "baseUrl": ".",
 | 
				
			||||||
 | 
					    "module": "ESNext",
 | 
				
			||||||
 | 
					    "moduleResolution": "node",
 | 
				
			||||||
 | 
					    "resolveJsonModule": true,
 | 
				
			||||||
 | 
					    "types": ["node"],
 | 
				
			||||||
 | 
					    "strict": true,
 | 
				
			||||||
 | 
					    "strictNullChecks": true,
 | 
				
			||||||
 | 
					    "noUnusedLocals": true,
 | 
				
			||||||
 | 
					    "allowSyntheticDefaultImports": true,
 | 
				
			||||||
 | 
					    "esModuleInterop": true,
 | 
				
			||||||
 | 
					    "forceConsistentCasingInFileNames": true
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "include": ["src/**/*"],
 | 
				
			||||||
 | 
					  "exclude": ["node_modules", "dist"]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,10 +0,0 @@
 | 
				
			|||||||
import axios from 'axios';
 | 
					 | 
				
			||||||
import type { CreateAxiosDefaults } from 'axios';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function createAxios(config?: CreateAxiosDefaults) {
 | 
					 | 
				
			||||||
  const instance = axios.create(config);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return instance;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default createAxios;
 | 
					 | 
				
			||||||
@@ -1,4 +0,0 @@
 | 
				
			|||||||
import { createAxios } from './axios';
 | 
					 | 
				
			||||||
import { createOfetch } from './ofetch';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export { createAxios, createOfetch };
 | 
					 | 
				
			||||||
@@ -12,7 +12,8 @@
 | 
				
			|||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "colord": "2.9.3",
 | 
					    "colord": "2.9.3",
 | 
				
			||||||
    "crypto-js": "4.2.0",
 | 
					    "crypto-js": "4.2.0",
 | 
				
			||||||
    "localforage": "1.10.0"
 | 
					    "localforage": "1.10.0",
 | 
				
			||||||
 | 
					    "nanoid": "5.0.4"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@types/crypto-js": "4.2.1"
 | 
					    "@types/crypto-js": "4.2.1"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
export * from './color';
 | 
					export * from './color';
 | 
				
			||||||
export * from './crypto';
 | 
					export * from './crypto';
 | 
				
			||||||
export * from './storage';
 | 
					export * from './storage';
 | 
				
			||||||
 | 
					export * from './nanoid';
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								packages/utils/src/nanoid.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/utils/src/nanoid.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					import { nanoid } from 'nanoid';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { nanoid };
 | 
				
			||||||
							
								
								
									
										52
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										52
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -14,6 +14,9 @@ importers:
 | 
				
			|||||||
      '@iconify/vue':
 | 
					      '@iconify/vue':
 | 
				
			||||||
        specifier: 4.1.1
 | 
					        specifier: 4.1.1
 | 
				
			||||||
        version: 4.1.1(vue@3.4.14)
 | 
					        version: 4.1.1(vue@3.4.14)
 | 
				
			||||||
 | 
					      '@sa/axios':
 | 
				
			||||||
 | 
					        specifier: workspace:*
 | 
				
			||||||
 | 
					        version: link:packages/axios
 | 
				
			||||||
      '@sa/color-palette':
 | 
					      '@sa/color-palette':
 | 
				
			||||||
        specifier: workspace:*
 | 
					        specifier: workspace:*
 | 
				
			||||||
        version: link:packages/color-palette
 | 
					        version: link:packages/color-palette
 | 
				
			||||||
@@ -23,9 +26,6 @@ importers:
 | 
				
			|||||||
      '@sa/materials':
 | 
					      '@sa/materials':
 | 
				
			||||||
        specifier: workspace:*
 | 
					        specifier: workspace:*
 | 
				
			||||||
        version: link:packages/materials
 | 
					        version: link:packages/materials
 | 
				
			||||||
      '@sa/request':
 | 
					 | 
				
			||||||
        specifier: workspace:*
 | 
					 | 
				
			||||||
        version: link:packages/request
 | 
					 | 
				
			||||||
      '@sa/utils':
 | 
					      '@sa/utils':
 | 
				
			||||||
        specifier: workspace:*
 | 
					        specifier: workspace:*
 | 
				
			||||||
        version: link:packages/utils
 | 
					        version: link:packages/utils
 | 
				
			||||||
@@ -160,6 +160,18 @@ importers:
 | 
				
			|||||||
        specifier: 1.8.27
 | 
					        specifier: 1.8.27
 | 
				
			||||||
        version: 1.8.27(typescript@5.3.3)
 | 
					        version: 1.8.27(typescript@5.3.3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  packages/axios:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@sa/utils':
 | 
				
			||||||
 | 
					        specifier: workspace:*
 | 
				
			||||||
 | 
					        version: link:../utils
 | 
				
			||||||
 | 
					      axios:
 | 
				
			||||||
 | 
					        specifier: 1.6.5
 | 
				
			||||||
 | 
					        version: 1.6.5
 | 
				
			||||||
 | 
					      axios-retry:
 | 
				
			||||||
 | 
					        specifier: ^4.0.0
 | 
				
			||||||
 | 
					        version: 4.0.0(axios@1.6.5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  packages/color-palette:
 | 
					  packages/color-palette:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      colord:
 | 
					      colord:
 | 
				
			||||||
@@ -172,7 +184,11 @@ importers:
 | 
				
			|||||||
        specifier: 1.0.0-rc.36
 | 
					        specifier: 1.0.0-rc.36
 | 
				
			||||||
        version: 1.0.0-rc.36(@algolia/client-search@4.22.1)(@types/node@20.11.2)(nprogress@0.2.0)(postcss@5.2.18)(sass@1.69.7)(search-insights@2.13.0)(typescript@5.3.3)
 | 
					        version: 1.0.0-rc.36(@algolia/client-search@4.22.1)(@types/node@20.11.2)(nprogress@0.2.0)(postcss@5.2.18)(sass@1.69.7)(search-insights@2.13.0)(typescript@5.3.3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  packages/hooks: {}
 | 
					  packages/hooks:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@sa/axios':
 | 
				
			||||||
 | 
					        specifier: workspace:*
 | 
				
			||||||
 | 
					        version: link:../axios
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  packages/materials:
 | 
					  packages/materials:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
@@ -190,11 +206,8 @@ importers:
 | 
				
			|||||||
        specifier: 0.8.1
 | 
					        specifier: 0.8.1
 | 
				
			||||||
        version: 0.8.1
 | 
					        version: 0.8.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  packages/request:
 | 
					  packages/ofetch:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      axios:
 | 
					 | 
				
			||||||
        specifier: 1.6.5
 | 
					 | 
				
			||||||
        version: 1.6.5
 | 
					 | 
				
			||||||
      ofetch:
 | 
					      ofetch:
 | 
				
			||||||
        specifier: 1.3.3
 | 
					        specifier: 1.3.3
 | 
				
			||||||
        version: 1.3.3
 | 
					        version: 1.3.3
 | 
				
			||||||
@@ -236,6 +249,9 @@ importers:
 | 
				
			|||||||
      localforage:
 | 
					      localforage:
 | 
				
			||||||
        specifier: 1.10.0
 | 
					        specifier: 1.10.0
 | 
				
			||||||
        version: 1.10.0
 | 
					        version: 1.10.0
 | 
				
			||||||
 | 
					      nanoid:
 | 
				
			||||||
 | 
					        specifier: 5.0.4
 | 
				
			||||||
 | 
					        version: 5.0.4
 | 
				
			||||||
    devDependencies:
 | 
					    devDependencies:
 | 
				
			||||||
      '@types/crypto-js':
 | 
					      '@types/crypto-js':
 | 
				
			||||||
        specifier: 4.2.1
 | 
					        specifier: 4.2.1
 | 
				
			||||||
@@ -2564,6 +2580,15 @@ packages:
 | 
				
			|||||||
    engines: {node: '>= 0.4'}
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
    dev: true
 | 
					    dev: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /axios-retry@4.0.0(axios@1.6.5):
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-F6P4HVGITD/v4z9Lw2mIA24IabTajvpDZmKa6zq/gGwn57wN5j1P3uWrAV0+diqnW6kTM2fTqmWNfgYWGmMuiA==}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      axios: 0.x || 1.x
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      axios: 1.6.5
 | 
				
			||||||
 | 
					      is-retry-allowed: 2.2.0
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /axios@1.6.5:
 | 
					  /axios@1.6.5:
 | 
				
			||||||
    resolution: {integrity: sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==}
 | 
					    resolution: {integrity: sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==}
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
@@ -5096,6 +5121,11 @@ packages:
 | 
				
			|||||||
      has-tostringtag: 1.0.0
 | 
					      has-tostringtag: 1.0.0
 | 
				
			||||||
    dev: true
 | 
					    dev: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /is-retry-allowed@2.2.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==}
 | 
				
			||||||
 | 
					    engines: {node: '>=10'}
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /is-shared-array-buffer@1.0.2:
 | 
					  /is-shared-array-buffer@1.0.2:
 | 
				
			||||||
    resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
 | 
					    resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
@@ -6079,6 +6109,12 @@ packages:
 | 
				
			|||||||
    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
 | 
					    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
 | 
				
			||||||
    hasBin: true
 | 
					    hasBin: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /nanoid@5.0.4:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
 | 
				
			||||||
 | 
					    engines: {node: ^18 || >=20}
 | 
				
			||||||
 | 
					    hasBin: true
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /nanomatch@1.2.13:
 | 
					  /nanomatch@1.2.13:
 | 
				
			||||||
    resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
 | 
					    resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
 | 
				
			||||||
    engines: {node: '>=0.10.0'}
 | 
					    engines: {node: '>=0.10.0'}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,9 +7,10 @@ import { request } from '../request';
 | 
				
			|||||||
 * @param password Password
 | 
					 * @param password Password
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function fetchLogin(userName: string, password: string) {
 | 
					export function fetchLogin(userName: string, password: string) {
 | 
				
			||||||
  return request<App.Service.Response<Api.Auth.LoginToken>>('/auth/login', {
 | 
					  return request<Api.Auth.LoginToken>({
 | 
				
			||||||
 | 
					    url: '/auth/login',
 | 
				
			||||||
    method: 'post',
 | 
					    method: 'post',
 | 
				
			||||||
    body: {
 | 
					    data: {
 | 
				
			||||||
      userName,
 | 
					      userName,
 | 
				
			||||||
      password
 | 
					      password
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -18,7 +19,7 @@ export function fetchLogin(userName: string, password: string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/** Get user info */
 | 
					/** Get user info */
 | 
				
			||||||
export function fetchGetUserInfo() {
 | 
					export function fetchGetUserInfo() {
 | 
				
			||||||
  return request<App.Service.Response<Api.Auth.UserInfo>>('/auth/getUserInfo');
 | 
					  return request<Api.Auth.UserInfo>({ url: '/auth/getUserInfo' });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -27,10 +28,22 @@ export function fetchGetUserInfo() {
 | 
				
			|||||||
 * @param refreshToken Refresh token
 | 
					 * @param refreshToken Refresh token
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function fetchRefreshToken(refreshToken: string) {
 | 
					export function fetchRefreshToken(refreshToken: string) {
 | 
				
			||||||
  return request<App.Service.Response<Api.Auth.LoginToken>>('/auth/refreshToken', {
 | 
					  return request<Api.Auth.LoginToken>({
 | 
				
			||||||
 | 
					    url: '/auth/refreshToken',
 | 
				
			||||||
    method: 'post',
 | 
					    method: 'post',
 | 
				
			||||||
    body: {
 | 
					    data: {
 | 
				
			||||||
      refreshToken
 | 
					      refreshToken
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function fetchDebug() {
 | 
				
			||||||
 | 
					  return request<string>({
 | 
				
			||||||
 | 
					    url: '/debug-post',
 | 
				
			||||||
 | 
					    method: 'post',
 | 
				
			||||||
 | 
					    headers: { 'content-type': 'application/x-www-form-urlencoded' },
 | 
				
			||||||
 | 
					    data: {
 | 
				
			||||||
 | 
					      a: '1'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ import { request } from '../request';
 | 
				
			|||||||
 * @param example Whether to use example data, default: 0
 | 
					 * @param example Whether to use example data, default: 0
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function fetchGetUserRoutes(example: '0' | '1' = '0') {
 | 
					export function fetchGetUserRoutes(example: '0' | '1' = '0') {
 | 
				
			||||||
  return request<App.Service.Response<Api.Route.UserRoute>>('/route/getUserRoutes', { params: { example } });
 | 
					  return request<Api.Route.UserRoute>({ url: '/route/getUserRoutes', params: { example } });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -16,5 +16,5 @@ export function fetchGetUserRoutes(example: '0' | '1' = '0') {
 | 
				
			|||||||
 * @param example Whether to use example data, default: 0
 | 
					 * @param example Whether to use example data, default: 0
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function fetchIsRouteExist(routeName: string, example: '0' | '1' = '0') {
 | 
					export function fetchIsRouteExist(routeName: string, example: '0' | '1' = '0') {
 | 
				
			||||||
  return request<App.Service.Response<boolean>>('/route/isRouteExist', { params: { routeName, example } });
 | 
					  return request<boolean>({ url: '/route/isRouteExist', params: { routeName, example } });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import { createOfetch as createRequest } from '@sa/request';
 | 
					import { BACKEND_ERROR_CODE, createFlatRequest, createRequest } from '@sa/axios';
 | 
				
			||||||
import { localStg } from '@/utils/storage';
 | 
					import { localStg } from '@/utils/storage';
 | 
				
			||||||
import { createProxyPattern, createServiceConfig } from '~/env.config';
 | 
					import { createProxyPattern, createServiceConfig } from '~/env.config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -6,20 +6,89 @@ const { baseURL, otherBaseURL } = createServiceConfig(import.meta.env);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const isHttpProxy = import.meta.env.VITE_HTTP_PROXY === 'Y';
 | 
					const isHttpProxy = import.meta.env.VITE_HTTP_PROXY === 'Y';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const request = createRequest({
 | 
					export const request = createFlatRequest<App.Service.Response>(
 | 
				
			||||||
  baseURL: isHttpProxy ? createProxyPattern() : baseURL,
 | 
					  {
 | 
				
			||||||
  headers: {
 | 
					    baseURL: isHttpProxy ? createProxyPattern() : baseURL,
 | 
				
			||||||
    apifoxToken: 'XL299LiMEDZ0H5h3A29PxwQXdMJqWyY2'
 | 
					    headers: {
 | 
				
			||||||
 | 
					      apifoxToken: 'XL299LiMEDZ0H5h3A29PxwQXdMJqWyY2'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  onRequest({ options }) {
 | 
					  {
 | 
				
			||||||
    if (options.headers) {
 | 
					    async onRequest(config) {
 | 
				
			||||||
 | 
					      const { headers } = config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // set token
 | 
				
			||||||
      const token = localStg.get('token');
 | 
					      const token = localStg.get('token');
 | 
				
			||||||
 | 
					      const Authorization = token ? `Bearer ${token}` : null;
 | 
				
			||||||
 | 
					      Object.assign(headers, { Authorization });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const Authorization = token ? `Bearer ${token}` : '';
 | 
					      return config;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    isBackendSuccess(response) {
 | 
				
			||||||
 | 
					      // when the backend response code is "0000", it means the request is success
 | 
				
			||||||
 | 
					      // you can change this logic by yourself
 | 
				
			||||||
 | 
					      return response.data.code === '0000';
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async onBackendFail(_response) {
 | 
				
			||||||
 | 
					      // when the backend response code is not 200, it means the request is fail
 | 
				
			||||||
 | 
					      // for example: the token is expired, refetch token and retry request
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    transformBackendResponse(response) {
 | 
				
			||||||
 | 
					      return response.data.data;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    onError(error) {
 | 
				
			||||||
 | 
					      // when the request is fail, you can show error message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      Object.assign(options.headers, { Authorization });
 | 
					      let message = error.message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // show backend error message
 | 
				
			||||||
 | 
					      if (error.code === BACKEND_ERROR_CODE) {
 | 
				
			||||||
 | 
					        message = error.request?.data.msg || message;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      window.$message?.error(message);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const demoRequest = createRequest({ baseURL: isHttpProxy ? createProxyPattern('demo') : otherBaseURL.demo });
 | 
					export const demoRequest = createRequest<App.Service.DemoResponse>(
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    baseURL: isHttpProxy ? createProxyPattern('demo') : otherBaseURL.demo
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    async onRequest(config) {
 | 
				
			||||||
 | 
					      const { headers } = config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // set token
 | 
				
			||||||
 | 
					      const token = localStg.get('token');
 | 
				
			||||||
 | 
					      const Authorization = token ? `Bearer ${token}` : null;
 | 
				
			||||||
 | 
					      Object.assign(headers, { Authorization });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return config;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    isBackendSuccess(response) {
 | 
				
			||||||
 | 
					      // when the backend response code is 200, it means the request is success
 | 
				
			||||||
 | 
					      // you can change this logic by yourself
 | 
				
			||||||
 | 
					      return response.data.status === '200';
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async onBackendFail(_response) {
 | 
				
			||||||
 | 
					      // when the backend response code is not 200, it means the request is fail
 | 
				
			||||||
 | 
					      // for example: the token is expired, refetch token and retry request
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    transformBackendResponse(response) {
 | 
				
			||||||
 | 
					      return response.data.result;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    onError(error) {
 | 
				
			||||||
 | 
					      // when the request is fail, you can show error message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let message = error.message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // show backend error message
 | 
				
			||||||
 | 
					      if (error.code === BACKEND_ERROR_CODE) {
 | 
				
			||||||
 | 
					        message = error.request?.data.message || message;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      window.$message?.error(message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,27 +45,28 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
 | 
				
			|||||||
  async function login(userName: string, password: string) {
 | 
					  async function login(userName: string, password: string) {
 | 
				
			||||||
    startLoading();
 | 
					    startLoading();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    const { data: loginToken, error } = await fetchLogin(userName, password);
 | 
				
			||||||
      const { data: loginToken } = await fetchLogin(userName, password);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await loginByToken(loginToken);
 | 
					    if (!error) {
 | 
				
			||||||
 | 
					      const pass = await loginByToken(loginToken);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await routeStore.initAuthRoute();
 | 
					      if (pass) {
 | 
				
			||||||
 | 
					        await routeStore.initAuthRoute();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await redirectFromLogin();
 | 
					        await redirectFromLogin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (routeStore.isInitAuthRoute) {
 | 
					        if (routeStore.isInitAuthRoute) {
 | 
				
			||||||
        window.$notification?.success({
 | 
					          window.$notification?.success({
 | 
				
			||||||
          title: $t('page.login.common.loginSuccess'),
 | 
					            title: $t('page.login.common.loginSuccess'),
 | 
				
			||||||
          content: $t('page.login.common.welcomeBack', { userName: userInfo.userName }),
 | 
					            content: $t('page.login.common.welcomeBack', { userName: userInfo.userName })
 | 
				
			||||||
          duration: 4500
 | 
					          });
 | 
				
			||||||
        });
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch {
 | 
					    } else {
 | 
				
			||||||
      resetStore();
 | 
					      resetStore();
 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
      endLoading();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    endLoading();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async function loginByToken(loginToken: Api.Auth.LoginToken) {
 | 
					  async function loginByToken(loginToken: Api.Auth.LoginToken) {
 | 
				
			||||||
@@ -73,14 +74,20 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
 | 
				
			|||||||
    localStg.set('token', loginToken.token);
 | 
					    localStg.set('token', loginToken.token);
 | 
				
			||||||
    localStg.set('refreshToken', loginToken.refreshToken);
 | 
					    localStg.set('refreshToken', loginToken.refreshToken);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const { data: info } = await fetchGetUserInfo();
 | 
					    const { data: info, error } = await fetchGetUserInfo();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 2. store user info
 | 
					    if (!error) {
 | 
				
			||||||
    localStg.set('userInfo', info);
 | 
					      // 2. store user info
 | 
				
			||||||
 | 
					      localStg.set('userInfo', info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 3. update auth route
 | 
					      // 3. update auth route
 | 
				
			||||||
    token.value = loginToken.token;
 | 
					      token.value = loginToken.token;
 | 
				
			||||||
    Object.assign(userInfo, info);
 | 
					      Object.assign(userInfo, info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -167,17 +167,19 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /** Init dynamic auth route */
 | 
					  /** Init dynamic auth route */
 | 
				
			||||||
  async function initDynamicAuthRoute() {
 | 
					  async function initDynamicAuthRoute() {
 | 
				
			||||||
    const {
 | 
					    const { data, error } = await fetchGetUserRoutes();
 | 
				
			||||||
      data: { routes, home }
 | 
					 | 
				
			||||||
    } = await fetchGetUserRoutes();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    handleAuthRoutes(routes);
 | 
					    if (!error) {
 | 
				
			||||||
 | 
					      const { routes, home } = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setRouteHome(home);
 | 
					      handleAuthRoutes(routes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    handleUpdateRootRouteRedirect(home);
 | 
					      setRouteHome(home);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setIsInitAuthRoute(true);
 | 
					      handleUpdateRootRouteRedirect(home);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setIsInitAuthRoute(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								src/typings/app.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								src/typings/app.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -426,9 +426,19 @@ declare namespace App {
 | 
				
			|||||||
      /** The backend service response code */
 | 
					      /** The backend service response code */
 | 
				
			||||||
      code: string;
 | 
					      code: string;
 | 
				
			||||||
      /** The backend service response message */
 | 
					      /** The backend service response message */
 | 
				
			||||||
      message: string;
 | 
					      msg: string;
 | 
				
			||||||
      /** The backend service response data */
 | 
					      /** The backend service response data */
 | 
				
			||||||
      data: T;
 | 
					      data: T;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** The demo backend service response data */
 | 
				
			||||||
 | 
					    type DemoResponse<T = unknown> = {
 | 
				
			||||||
 | 
					      /** The backend service response code */
 | 
				
			||||||
 | 
					      status: string;
 | 
				
			||||||
 | 
					      /** The backend service response message */
 | 
				
			||||||
 | 
					      message: string;
 | 
				
			||||||
 | 
					      /** The backend service response data */
 | 
				
			||||||
 | 
					      result: T;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user