mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-10-08 02:46:39 +08:00
Compare commits
6 Commits
d6c8142bb4
...
936b834e62
Author | SHA1 | Date | |
---|---|---|---|
|
936b834e62 | ||
|
c965140b87 | ||
|
32b8f99071 | ||
|
abaaa4a068 | ||
|
b4e125300e | ||
|
50a5cba088 |
@ -13,11 +13,12 @@ import type {
|
|||||||
ResponseType
|
ResponseType
|
||||||
} from './type';
|
} from './type';
|
||||||
|
|
||||||
function createCommonRequest<ResponseData = any>(
|
function createCommonRequest<
|
||||||
axiosConfig?: CreateAxiosDefaults,
|
ResponseData,
|
||||||
options?: Partial<RequestOption<ResponseData>>
|
ApiData = ResponseData,
|
||||||
) {
|
State extends Record<string, unknown> = Record<string, unknown>
|
||||||
const opts = createDefaultOptions<ResponseData>(options);
|
>(axiosConfig?: CreateAxiosDefaults, options?: Partial<RequestOption<ResponseData, ApiData, State>>) {
|
||||||
|
const opts = createDefaultOptions<ResponseData, ApiData, State>(options);
|
||||||
|
|
||||||
const axiosConf = createAxiosConfig(axiosConfig);
|
const axiosConf = createAxiosConfig(axiosConfig);
|
||||||
const instance = axios.create(axiosConf);
|
const instance = axios.create(axiosConf);
|
||||||
@ -80,14 +81,6 @@ function createCommonRequest<ResponseData = any>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
function cancelRequest(requestId: string) {
|
|
||||||
const abortController = abortControllerMap.get(requestId);
|
|
||||||
if (abortController) {
|
|
||||||
abortController.abort();
|
|
||||||
abortControllerMap.delete(requestId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancelAllRequest() {
|
function cancelAllRequest() {
|
||||||
abortControllerMap.forEach(abortController => {
|
abortControllerMap.forEach(abortController => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
@ -98,7 +91,6 @@ function createCommonRequest<ResponseData = any>(
|
|||||||
return {
|
return {
|
||||||
instance,
|
instance,
|
||||||
opts,
|
opts,
|
||||||
cancelRequest,
|
|
||||||
cancelAllRequest
|
cancelAllRequest
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -109,15 +101,16 @@ function createCommonRequest<ResponseData = any>(
|
|||||||
* @param axiosConfig axios config
|
* @param axiosConfig axios config
|
||||||
* @param options request options
|
* @param options request options
|
||||||
*/
|
*/
|
||||||
export function createRequest<ResponseData = any, State = Record<string, unknown>>(
|
export function createRequest<ResponseData, ApiData, State extends Record<string, unknown>>(
|
||||||
axiosConfig?: CreateAxiosDefaults,
|
axiosConfig?: CreateAxiosDefaults,
|
||||||
options?: Partial<RequestOption<ResponseData>>
|
options?: Partial<RequestOption<ResponseData, ApiData, State>>
|
||||||
) {
|
) {
|
||||||
const { instance, opts, cancelRequest, cancelAllRequest } = createCommonRequest<ResponseData>(axiosConfig, options);
|
const { instance, opts, cancelAllRequest } = createCommonRequest<ResponseData, ApiData, State>(axiosConfig, options);
|
||||||
|
|
||||||
const request: RequestInstance<State> = async function request<T = any, R extends ResponseType = 'json'>(
|
const request: RequestInstance<ApiData, State> = async function request<
|
||||||
config: CustomAxiosRequestConfig
|
T extends ApiData = ApiData,
|
||||||
) {
|
R extends ResponseType = 'json'
|
||||||
|
>(config: CustomAxiosRequestConfig) {
|
||||||
const response: AxiosResponse<ResponseData> = await instance(config);
|
const response: AxiosResponse<ResponseData> = await instance(config);
|
||||||
|
|
||||||
const responseType = response.config?.responseType || 'json';
|
const responseType = response.config?.responseType || 'json';
|
||||||
@ -127,9 +120,8 @@ export function createRequest<ResponseData = any, State = Record<string, unknown
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response.data as MappedType<R, T>;
|
return response.data as MappedType<R, T>;
|
||||||
} as RequestInstance<State>;
|
} as RequestInstance<ApiData, State>;
|
||||||
|
|
||||||
request.cancelRequest = cancelRequest;
|
|
||||||
request.cancelAllRequest = cancelAllRequest;
|
request.cancelAllRequest = cancelAllRequest;
|
||||||
request.state = {} as State;
|
request.state = {} as State;
|
||||||
|
|
||||||
@ -144,14 +136,14 @@ export function createRequest<ResponseData = any, State = Record<string, unknown
|
|||||||
* @param axiosConfig axios config
|
* @param axiosConfig axios config
|
||||||
* @param options request options
|
* @param options request options
|
||||||
*/
|
*/
|
||||||
export function createFlatRequest<ResponseData = any, State = Record<string, unknown>>(
|
export function createFlatRequest<ResponseData, ApiData, State extends Record<string, unknown>>(
|
||||||
axiosConfig?: CreateAxiosDefaults,
|
axiosConfig?: CreateAxiosDefaults,
|
||||||
options?: Partial<RequestOption<ResponseData>>
|
options?: Partial<RequestOption<ResponseData, ApiData, State>>
|
||||||
) {
|
) {
|
||||||
const { instance, opts, cancelRequest, cancelAllRequest } = createCommonRequest<ResponseData>(axiosConfig, options);
|
const { instance, opts, cancelAllRequest } = createCommonRequest<ResponseData, ApiData, State>(axiosConfig, options);
|
||||||
|
|
||||||
const flatRequest: FlatRequestInstance<State, ResponseData> = async function flatRequest<
|
const flatRequest: FlatRequestInstance<ResponseData, ApiData, State> = async function flatRequest<
|
||||||
T = any,
|
T extends ApiData = ApiData,
|
||||||
R extends ResponseType = 'json'
|
R extends ResponseType = 'json'
|
||||||
>(config: CustomAxiosRequestConfig) {
|
>(config: CustomAxiosRequestConfig) {
|
||||||
try {
|
try {
|
||||||
@ -160,20 +152,21 @@ export function createFlatRequest<ResponseData = any, State = Record<string, unk
|
|||||||
const responseType = response.config?.responseType || 'json';
|
const responseType = response.config?.responseType || 'json';
|
||||||
|
|
||||||
if (responseType === 'json') {
|
if (responseType === 'json') {
|
||||||
const data = opts.transformBackendResponse(response);
|
const data = await opts.transformBackendResponse(response);
|
||||||
|
|
||||||
return { data, error: null, response };
|
return { data, error: null, response };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { data: response.data as MappedType<R, T>, error: null };
|
return { data: response.data as MappedType<R, T>, error: null, response };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { data: null, error, response: (error as AxiosError<ResponseData>).response };
|
return { data: null, error, response: (error as AxiosError<ResponseData>).response };
|
||||||
}
|
}
|
||||||
} as FlatRequestInstance<State, ResponseData>;
|
} as FlatRequestInstance<ResponseData, ApiData, State>;
|
||||||
|
|
||||||
flatRequest.cancelRequest = cancelRequest;
|
|
||||||
flatRequest.cancelAllRequest = cancelAllRequest;
|
flatRequest.cancelAllRequest = cancelAllRequest;
|
||||||
flatRequest.state = {} as State;
|
flatRequest.state = {
|
||||||
|
...opts.defaultState
|
||||||
|
} as State;
|
||||||
|
|
||||||
return flatRequest;
|
return flatRequest;
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,16 @@ import { stringify } from 'qs';
|
|||||||
import { isHttpSuccess } from './shared';
|
import { isHttpSuccess } from './shared';
|
||||||
import type { RequestOption } from './type';
|
import type { RequestOption } from './type';
|
||||||
|
|
||||||
export function createDefaultOptions<ResponseData = any>(options?: Partial<RequestOption<ResponseData>>) {
|
export function createDefaultOptions<
|
||||||
const opts: RequestOption<ResponseData> = {
|
ResponseData,
|
||||||
|
ApiData = ResponseData,
|
||||||
|
State extends Record<string, unknown> = Record<string, unknown>
|
||||||
|
>(options?: Partial<RequestOption<ResponseData, ApiData, State>>) {
|
||||||
|
const opts: RequestOption<ResponseData, ApiData, State> = {
|
||||||
onRequest: async config => config,
|
onRequest: async config => config,
|
||||||
isBackendSuccess: _response => true,
|
isBackendSuccess: _response => true,
|
||||||
onBackendFail: async () => {},
|
onBackendFail: async () => {},
|
||||||
transformBackendResponse: async response => response.data,
|
transformBackendResponse: async response => response.data as unknown as ApiData,
|
||||||
onError: async () => {}
|
onError: async () => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,7 +8,23 @@ export type ContentType =
|
|||||||
| 'application/x-www-form-urlencoded'
|
| 'application/x-www-form-urlencoded'
|
||||||
| 'application/octet-stream';
|
| 'application/octet-stream';
|
||||||
|
|
||||||
export interface RequestOption<ResponseData = any> {
|
export type ResponseTransform<Input = any, Output = any> = (input: Input) => Output | Promise<Output>;
|
||||||
|
|
||||||
|
export interface RequestOption<
|
||||||
|
ResponseData,
|
||||||
|
ApiData = ResponseData,
|
||||||
|
State extends Record<string, unknown> = Record<string, unknown>
|
||||||
|
> {
|
||||||
|
/**
|
||||||
|
* The default state
|
||||||
|
*/
|
||||||
|
defaultState?: State;
|
||||||
|
/**
|
||||||
|
* transform the response data to the api data
|
||||||
|
*
|
||||||
|
* @param response Axios response
|
||||||
|
*/
|
||||||
|
transformBackendResponse: ResponseTransform<AxiosResponse<ResponseData>, ApiData>;
|
||||||
/**
|
/**
|
||||||
* The hook before request
|
* The hook before request
|
||||||
*
|
*
|
||||||
@ -35,12 +51,6 @@ export interface RequestOption<ResponseData = any> {
|
|||||||
response: AxiosResponse<ResponseData>,
|
response: AxiosResponse<ResponseData>,
|
||||||
instance: AxiosInstance
|
instance: AxiosInstance
|
||||||
) => Promise<AxiosResponse | null> | Promise<void>;
|
) => Promise<AxiosResponse | null> | 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
|
* The hook to handle error
|
||||||
*
|
*
|
||||||
@ -68,15 +78,7 @@ export type CustomAxiosRequestConfig<R extends ResponseType = 'json'> = Omit<Axi
|
|||||||
responseType?: R;
|
responseType?: R;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface RequestInstanceCommon<T> {
|
export interface RequestInstanceCommon<State extends Record<string, unknown>> {
|
||||||
/**
|
|
||||||
* cancel the request by request id
|
|
||||||
*
|
|
||||||
* if the request provide abort controller sign from config, it will not collect in the abort controller map
|
|
||||||
*
|
|
||||||
* @param requestId
|
|
||||||
*/
|
|
||||||
cancelRequest: (requestId: string) => void;
|
|
||||||
/**
|
/**
|
||||||
* cancel all request
|
* cancel all request
|
||||||
*
|
*
|
||||||
@ -84,32 +86,35 @@ export interface RequestInstanceCommon<T> {
|
|||||||
*/
|
*/
|
||||||
cancelAllRequest: () => void;
|
cancelAllRequest: () => void;
|
||||||
/** you can set custom state in the request instance */
|
/** you can set custom state in the request instance */
|
||||||
state: T;
|
state: State;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The request instance */
|
/** The request instance */
|
||||||
export interface RequestInstance<S = Record<string, unknown>> extends RequestInstanceCommon<S> {
|
export interface RequestInstance<ApiData, State extends Record<string, unknown>> extends RequestInstanceCommon<State> {
|
||||||
<T = any, R extends ResponseType = 'json'>(config: CustomAxiosRequestConfig<R>): Promise<MappedType<R, T>>;
|
<T extends ApiData = ApiData, R extends ResponseType = 'json'>(
|
||||||
|
config: CustomAxiosRequestConfig<R>
|
||||||
|
): Promise<MappedType<R, T>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FlatResponseSuccessData<T = any, ResponseData = any> = {
|
export type FlatResponseSuccessData<ResponseData, ApiData> = {
|
||||||
data: T;
|
data: ApiData;
|
||||||
error: null;
|
error: null;
|
||||||
response: AxiosResponse<ResponseData>;
|
response: AxiosResponse<ResponseData>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FlatResponseFailData<ResponseData = any> = {
|
export type FlatResponseFailData<ResponseData> = {
|
||||||
data: null;
|
data: null;
|
||||||
error: AxiosError<ResponseData>;
|
error: AxiosError<ResponseData>;
|
||||||
response: AxiosResponse<ResponseData>;
|
response: AxiosResponse<ResponseData>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FlatResponseData<T = any, ResponseData = any> =
|
export type FlatResponseData<ResponseData, ApiData> =
|
||||||
| FlatResponseSuccessData<T, ResponseData>
|
| FlatResponseSuccessData<ResponseData, ApiData>
|
||||||
| FlatResponseFailData<ResponseData>;
|
| FlatResponseFailData<ResponseData>;
|
||||||
|
|
||||||
export interface FlatRequestInstance<S = Record<string, unknown>, ResponseData = any> extends RequestInstanceCommon<S> {
|
export interface FlatRequestInstance<ResponseData, ApiData, State extends Record<string, unknown>>
|
||||||
<T = any, R extends ResponseType = 'json'>(
|
extends RequestInstanceCommon<State> {
|
||||||
|
<T extends ApiData = ApiData, R extends ResponseType = 'json'>(
|
||||||
config: CustomAxiosRequestConfig<R>
|
config: CustomAxiosRequestConfig<R>
|
||||||
): Promise<FlatResponseData<MappedType<R, T>, ResponseData>>;
|
): Promise<FlatResponseData<ResponseData, MappedType<R, T>>>;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { inject, provide } from 'vue';
|
import { inject, provide } from 'vue';
|
||||||
import type { InjectionKey } from 'vue';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use context
|
* Use context
|
||||||
@ -12,7 +11,7 @@ import type { InjectionKey } from 'vue';
|
|||||||
* import { ref } from 'vue';
|
* import { ref } from 'vue';
|
||||||
* import { useContext } from '@sa/hooks';
|
* import { useContext } from '@sa/hooks';
|
||||||
*
|
*
|
||||||
* export const { setupStore, useStore } = useContext('demo', () => {
|
* export const [provideDemoContext, useDemoContext] = useContext('demo', () => {
|
||||||
* const count = ref(0);
|
* const count = ref(0);
|
||||||
*
|
*
|
||||||
* function increment() {
|
* function increment() {
|
||||||
@ -35,10 +34,10 @@ import type { InjectionKey } from 'vue';
|
|||||||
* <div>A</div>
|
* <div>A</div>
|
||||||
* </template>
|
* </template>
|
||||||
* <script setup lang="ts">
|
* <script setup lang="ts">
|
||||||
* import { setupStore } from './context';
|
* import { provideDemoContext } from './context';
|
||||||
*
|
*
|
||||||
* setupStore();
|
* provideDemoContext();
|
||||||
* // const { increment } = setupStore(); // also can control the store in the parent component
|
* // const { increment } = provideDemoContext(); // also can control the store in the parent component
|
||||||
* </script>
|
* </script>
|
||||||
* ``` // B.vue
|
* ``` // B.vue
|
||||||
* ```vue
|
* ```vue
|
||||||
@ -46,9 +45,9 @@ import type { InjectionKey } from 'vue';
|
|||||||
* <div>B</div>
|
* <div>B</div>
|
||||||
* </template>
|
* </template>
|
||||||
* <script setup lang="ts">
|
* <script setup lang="ts">
|
||||||
* import { useStore } from './context';
|
* import { useDemoContext } from './context';
|
||||||
*
|
*
|
||||||
* const { count, increment } = useStore();
|
* const { count, increment } = useDemoContext();
|
||||||
* </script>
|
* </script>
|
||||||
* ```;
|
* ```;
|
||||||
*
|
*
|
||||||
@ -57,40 +56,41 @@ import type { InjectionKey } from 'vue';
|
|||||||
* @param contextName Context name
|
* @param contextName Context name
|
||||||
* @param fn Context function
|
* @param fn Context function
|
||||||
*/
|
*/
|
||||||
export default function useContext<T extends (...args: any[]) => any>(contextName: string, fn: T) {
|
export default function useContext<Arguments extends Array<any>, T>(
|
||||||
type Context = ReturnType<T>;
|
contextName: string,
|
||||||
|
composable: (...args: Arguments) => T
|
||||||
|
) {
|
||||||
|
const key = Symbol(contextName);
|
||||||
|
|
||||||
const { useProvide, useInject: useStore } = createContext<Context>(contextName);
|
/**
|
||||||
|
* Injects the context value.
|
||||||
|
*
|
||||||
|
* @param consumerName - The name of the component that is consuming the context. If provided, the component must be
|
||||||
|
* used within the context provider.
|
||||||
|
* @param defaultValue - The default value to return if the context is not provided.
|
||||||
|
* @returns The context value.
|
||||||
|
*/
|
||||||
|
const useInject = <N extends string | null | undefined = undefined>(
|
||||||
|
consumerName?: N,
|
||||||
|
defaultValue?: T
|
||||||
|
): N extends null | undefined ? T | null : T => {
|
||||||
|
const value = inject(key, defaultValue);
|
||||||
|
|
||||||
function setupStore(...args: Parameters<T>) {
|
if (consumerName && !value) {
|
||||||
const context: Context = fn(...args);
|
throw new Error(`\`${consumerName}\` must be used within \`${contextName}\``);
|
||||||
return useProvide(context);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
// @ts-expect-error - we want to return null if the value is undefined or null
|
||||||
/** Setup store in the parent component */
|
return value || null;
|
||||||
setupStore,
|
|
||||||
/** Use store in the child component */
|
|
||||||
useStore
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/** Create context */
|
const useProvide = (...args: Arguments) => {
|
||||||
function createContext<T>(contextName: string) {
|
const value = composable(...args);
|
||||||
const injectKey: InjectionKey<T> = Symbol(contextName);
|
|
||||||
|
|
||||||
function useProvide(context: T) {
|
provide(key, value);
|
||||||
provide(injectKey, context);
|
|
||||||
|
|
||||||
return context;
|
return value;
|
||||||
}
|
|
||||||
|
|
||||||
function useInject() {
|
|
||||||
return inject(injectKey) as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
useProvide,
|
|
||||||
useInject
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return [useProvide, useInject] as const;
|
||||||
}
|
}
|
||||||
|
@ -6,31 +6,31 @@ import type {
|
|||||||
CreateAxiosDefaults,
|
CreateAxiosDefaults,
|
||||||
CustomAxiosRequestConfig,
|
CustomAxiosRequestConfig,
|
||||||
MappedType,
|
MappedType,
|
||||||
|
RequestInstanceCommon,
|
||||||
RequestOption,
|
RequestOption,
|
||||||
ResponseType
|
ResponseType
|
||||||
} from '@sa/axios';
|
} from '@sa/axios';
|
||||||
import useLoading from './use-loading';
|
import useLoading from './use-loading';
|
||||||
|
|
||||||
export type HookRequestInstanceResponseSuccessData<T = any> = {
|
export type HookRequestInstanceResponseSuccessData<ApiData> = {
|
||||||
data: Ref<T>;
|
data: Ref<ApiData>;
|
||||||
error: Ref<null>;
|
error: Ref<null>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HookRequestInstanceResponseFailData<ResponseData = any> = {
|
export type HookRequestInstanceResponseFailData<ResponseData> = {
|
||||||
data: Ref<null>;
|
data: Ref<null>;
|
||||||
error: Ref<AxiosError<ResponseData>>;
|
error: Ref<AxiosError<ResponseData>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HookRequestInstanceResponseData<T = any, ResponseData = any> = {
|
export type HookRequestInstanceResponseData<ResponseData, ApiData> = {
|
||||||
loading: Ref<boolean>;
|
loading: Ref<boolean>;
|
||||||
} & (HookRequestInstanceResponseSuccessData<T> | HookRequestInstanceResponseFailData<ResponseData>);
|
} & (HookRequestInstanceResponseSuccessData<ApiData> | HookRequestInstanceResponseFailData<ResponseData>);
|
||||||
|
|
||||||
export interface HookRequestInstance<ResponseData = any> {
|
export interface HookRequestInstance<ResponseData, ApiData, State extends Record<string, unknown>>
|
||||||
<T = any, R extends ResponseType = 'json'>(
|
extends RequestInstanceCommon<State> {
|
||||||
|
<T extends ApiData = ApiData, R extends ResponseType = 'json'>(
|
||||||
config: CustomAxiosRequestConfig
|
config: CustomAxiosRequestConfig
|
||||||
): HookRequestInstanceResponseData<MappedType<R, T>, ResponseData>;
|
): HookRequestInstanceResponseData<ResponseData, MappedType<R, T>>;
|
||||||
cancelRequest: (requestId: string) => void;
|
|
||||||
cancelAllRequest: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,25 +39,26 @@ export interface HookRequestInstance<ResponseData = any> {
|
|||||||
* @param axiosConfig
|
* @param axiosConfig
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
export default function createHookRequest<ResponseData = any>(
|
export default function createHookRequest<ResponseData, ApiData, State extends Record<string, unknown>>(
|
||||||
axiosConfig?: CreateAxiosDefaults,
|
axiosConfig?: CreateAxiosDefaults,
|
||||||
options?: Partial<RequestOption<ResponseData>>
|
options?: Partial<RequestOption<ResponseData, ApiData, State>>
|
||||||
) {
|
) {
|
||||||
const request = createFlatRequest<ResponseData>(axiosConfig, options);
|
const request = createFlatRequest<ResponseData, ApiData, State>(axiosConfig, options);
|
||||||
|
|
||||||
const hookRequest: HookRequestInstance<ResponseData> = function hookRequest<T = any, R extends ResponseType = 'json'>(
|
const hookRequest: HookRequestInstance<ResponseData, ApiData, State> = function hookRequest<
|
||||||
config: CustomAxiosRequestConfig
|
T extends ApiData = ApiData,
|
||||||
) {
|
R extends ResponseType = 'json'
|
||||||
|
>(config: CustomAxiosRequestConfig) {
|
||||||
const { loading, startLoading, endLoading } = useLoading();
|
const { loading, startLoading, endLoading } = useLoading();
|
||||||
|
|
||||||
const data = ref<MappedType<R, T> | null>(null) as Ref<MappedType<R, T>>;
|
const data = ref(null) as Ref<MappedType<R, T>>;
|
||||||
const error = ref<AxiosError<ResponseData> | null>(null) as Ref<AxiosError<ResponseData> | null>;
|
const error = ref(null) as Ref<AxiosError<ResponseData> | null>;
|
||||||
|
|
||||||
startLoading();
|
startLoading();
|
||||||
|
|
||||||
request(config).then(res => {
|
request(config).then(res => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
data.value = res.data;
|
data.value = res.data as MappedType<R, T>;
|
||||||
} else {
|
} else {
|
||||||
error.value = res.error;
|
error.value = res.error;
|
||||||
}
|
}
|
||||||
@ -70,9 +71,8 @@ export default function createHookRequest<ResponseData = any>(
|
|||||||
data,
|
data,
|
||||||
error
|
error
|
||||||
};
|
};
|
||||||
} as HookRequestInstance<ResponseData>;
|
} as HookRequestInstance<ResponseData, ApiData, State>;
|
||||||
|
|
||||||
hookRequest.cancelRequest = request.cancelRequest;
|
|
||||||
hookRequest.cancelAllRequest = request.cancelAllRequest;
|
hookRequest.cancelAllRequest = request.cancelAllRequest;
|
||||||
|
|
||||||
return hookRequest;
|
return hookRequest;
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@sa/fetch",
|
|
||||||
"version": "1.3.15",
|
|
||||||
"exports": {
|
|
||||||
".": "./src/index.ts"
|
|
||||||
},
|
|
||||||
"typesVersions": {
|
|
||||||
"*": {
|
|
||||||
"*": ["./src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ofetch": "1.4.1"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import { ofetch } from 'ofetch';
|
|
||||||
import type { FetchOptions } from 'ofetch';
|
|
||||||
|
|
||||||
export function createRequest(options: FetchOptions) {
|
|
||||||
const request = ofetch.create(options);
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default createRequest;
|
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"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,4 +1,4 @@
|
|||||||
import { computed, effectScope, nextTick, onScopeDispose, ref, watch } from 'vue';
|
import { computed, effectScope, nextTick, onScopeDispose, shallowRef, watch } from 'vue';
|
||||||
import { useElementSize } from '@vueuse/core';
|
import { useElementSize } from '@vueuse/core';
|
||||||
import * as echarts from 'echarts/core';
|
import * as echarts from 'echarts/core';
|
||||||
import { BarChart, GaugeChart, LineChart, PictorialBarChart, PieChart, RadarChart, ScatterChart } from 'echarts/charts';
|
import { BarChart, GaugeChart, LineChart, PictorialBarChart, PieChart, RadarChart, ScatterChart } from 'echarts/charts';
|
||||||
@ -86,11 +86,11 @@ export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: C
|
|||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const darkMode = computed(() => themeStore.darkMode);
|
const darkMode = computed(() => themeStore.darkMode);
|
||||||
|
|
||||||
const domRef = ref<HTMLElement | null>(null);
|
const domRef = shallowRef<HTMLElement | null>(null);
|
||||||
const initialSize = { width: 0, height: 0 };
|
const initialSize = { width: 0, height: 0 };
|
||||||
const { width, height } = useElementSize(domRef, initialSize);
|
const { width, height } = useElementSize(domRef, initialSize);
|
||||||
|
|
||||||
let chart: echarts.ECharts | null = null;
|
const chart = shallowRef<echarts.ECharts | null>(null);
|
||||||
const chartOptions: T = optionsFactory();
|
const chartOptions: T = optionsFactory();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -111,15 +111,6 @@ export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: C
|
|||||||
onDestroy
|
onDestroy
|
||||||
} = hooks;
|
} = hooks;
|
||||||
|
|
||||||
/**
|
|
||||||
* whether can render chart
|
|
||||||
*
|
|
||||||
* when domRef is ready and initialSize is valid
|
|
||||||
*/
|
|
||||||
function canRender() {
|
|
||||||
return domRef.value && initialSize.width > 0 && initialSize.height > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** is chart rendered */
|
/** is chart rendered */
|
||||||
function isRendered() {
|
function isRendered() {
|
||||||
return Boolean(domRef.value && chart);
|
return Boolean(domRef.value && chart);
|
||||||
@ -138,52 +129,52 @@ export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: C
|
|||||||
Object.assign(chartOptions, updatedOpts);
|
Object.assign(chartOptions, updatedOpts);
|
||||||
|
|
||||||
if (isRendered()) {
|
if (isRendered()) {
|
||||||
chart?.clear();
|
chart.value?.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
chart?.setOption({ ...updatedOpts, backgroundColor: 'transparent' });
|
chart.value?.setOption({ ...updatedOpts, backgroundColor: 'transparent' });
|
||||||
|
|
||||||
await onUpdated?.(chart!);
|
await onUpdated?.(chart.value!);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setOptions(options: T) {
|
function setOptions(options: T) {
|
||||||
chart?.setOption(options);
|
chart.value?.setOption(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** render chart */
|
/** render chart */
|
||||||
async function render() {
|
async function render() {
|
||||||
if (!isRendered()) {
|
if (isRendered()) return;
|
||||||
const chartTheme = darkMode.value ? 'dark' : 'light';
|
|
||||||
|
|
||||||
await nextTick();
|
const chartTheme = darkMode.value ? 'dark' : 'light';
|
||||||
|
|
||||||
chart = echarts.init(domRef.value, chartTheme);
|
await nextTick();
|
||||||
|
|
||||||
chart.setOption({ ...chartOptions, backgroundColor: 'transparent' });
|
chart.value = echarts.init(domRef.value, chartTheme);
|
||||||
|
|
||||||
await onRender?.(chart);
|
chart.value?.setOption({ ...chartOptions, backgroundColor: 'transparent' });
|
||||||
}
|
|
||||||
|
await onRender?.(chart.value!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** resize chart */
|
/** resize chart */
|
||||||
function resize() {
|
function resize() {
|
||||||
chart?.resize();
|
chart.value?.resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** destroy chart */
|
/** destroy chart */
|
||||||
async function destroy() {
|
async function destroy() {
|
||||||
if (!chart) return;
|
if (!chart.value) return;
|
||||||
|
|
||||||
await onDestroy?.(chart);
|
await onDestroy?.(chart.value);
|
||||||
chart?.dispose();
|
chart.value?.dispose();
|
||||||
chart = null;
|
chart.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** change chart theme */
|
/** change chart theme */
|
||||||
async function changeTheme() {
|
async function changeTheme() {
|
||||||
await destroy();
|
await destroy();
|
||||||
await render();
|
await render();
|
||||||
await onUpdated?.(chart!);
|
await onUpdated?.(chart.value!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,13 +187,6 @@ export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: C
|
|||||||
initialSize.width = w;
|
initialSize.width = w;
|
||||||
initialSize.height = h;
|
initialSize.height = h;
|
||||||
|
|
||||||
// size is abnormal, destroy chart
|
|
||||||
if (!canRender()) {
|
|
||||||
await destroy();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// resize chart
|
// resize chart
|
||||||
if (isRendered()) {
|
if (isRendered()) {
|
||||||
resize();
|
resize();
|
||||||
@ -211,15 +195,17 @@ export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: C
|
|||||||
// render chart
|
// render chart
|
||||||
await render();
|
await render();
|
||||||
|
|
||||||
if (chart) {
|
await onUpdated?.(chart.value!);
|
||||||
await onUpdated?.(chart);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.run(() => {
|
scope.run(() => {
|
||||||
watch([width, height], ([newWidth, newHeight]) => {
|
watch(
|
||||||
renderChartBySize(newWidth, newHeight);
|
[width, height],
|
||||||
});
|
([newWidth, newHeight]) => {
|
||||||
|
renderChartBySize(newWidth, newHeight);
|
||||||
|
},
|
||||||
|
{ flush: 'post' }
|
||||||
|
);
|
||||||
|
|
||||||
watch(darkMode, () => {
|
watch(darkMode, () => {
|
||||||
changeTheme();
|
changeTheme();
|
||||||
@ -233,6 +219,7 @@ export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: C
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
domRef,
|
domRef,
|
||||||
|
chart,
|
||||||
updateOptions,
|
updateOptions,
|
||||||
setOptions
|
setOptions
|
||||||
};
|
};
|
||||||
|
@ -43,7 +43,7 @@ export function useTable<A extends NaiveUI.TableApiFn>(config: NaiveUI.NaiveTabl
|
|||||||
// Ensure that the size is greater than 0, If it is less than 0, it will cause paging calculation errors.
|
// Ensure that the size is greater than 0, If it is less than 0, it will cause paging calculation errors.
|
||||||
const pageSize = size <= 0 ? 10 : size;
|
const pageSize = size <= 0 ? 10 : size;
|
||||||
|
|
||||||
const recordsWithIndex = records.map((item, index) => {
|
const recordsWithIndex = records.map((item: GetTableData<A>, index: number) => {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
index: (current - 1) * pageSize + index + 1
|
index: (current - 1) * pageSize + index + 1
|
||||||
|
@ -10,7 +10,7 @@ import GlobalTab from '../modules/global-tab/index.vue';
|
|||||||
import GlobalContent from '../modules/global-content/index.vue';
|
import GlobalContent from '../modules/global-content/index.vue';
|
||||||
import GlobalFooter from '../modules/global-footer/index.vue';
|
import GlobalFooter from '../modules/global-footer/index.vue';
|
||||||
import ThemeDrawer from '../modules/theme-drawer/index.vue';
|
import ThemeDrawer from '../modules/theme-drawer/index.vue';
|
||||||
import { setupMixMenuContext } from '../context';
|
import { provideMixMenuContext } from '../modules/global-menu/context';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'BaseLayout'
|
name: 'BaseLayout'
|
||||||
@ -18,7 +18,7 @@ defineOptions({
|
|||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const { childLevelMenus, isActiveFirstLevelMenuHasChildren } = setupMixMenuContext();
|
const { childLevelMenus, isActiveFirstLevelMenuHasChildren } = provideMixMenuContext();
|
||||||
|
|
||||||
const GlobalMenu = defineAsyncComponent(() => import('../modules/global-menu/index.vue'));
|
const GlobalMenu = defineAsyncComponent(() => import('../modules/global-menu/index.vue'));
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import type { RouteKey } from '@elegant-router/types';
|
|||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
|
|
||||||
export const { setupStore: setupMixMenuContext, useStore: useMixMenuContext } = useContext('mix-menu', useMixMenu);
|
export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu', useMixMenu);
|
||||||
|
|
||||||
function useMixMenu() {
|
function useMixMenu() {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
@ -2,7 +2,7 @@
|
|||||||
import { GLOBAL_HEADER_MENU_ID } from '@/constants/app';
|
import { GLOBAL_HEADER_MENU_ID } from '@/constants/app';
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { useMenu } from '../../../context';
|
import { useMenu } from '../context';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'HorizontalMenu'
|
name: 'HorizontalMenu'
|
||||||
|
@ -7,7 +7,7 @@ import { useAppStore } from '@/store/modules/app';
|
|||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { useMenu, useMixMenuContext } from '../../../context';
|
import { useMenu, useMixMenuContext } from '../context';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'TopHybridHeaderFirst'
|
name: 'TopHybridHeaderFirst'
|
||||||
@ -18,7 +18,8 @@ const appStore = useAppStore();
|
|||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const routeStore = useRouteStore();
|
const routeStore = useRouteStore();
|
||||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||||
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } = useMixMenuContext();
|
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
|
||||||
|
useMixMenuContext('TopHybridHeaderFirst');
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
|
|
||||||
const expandedKeys = ref<string[]>([]);
|
const expandedKeys = ref<string[]>([]);
|
||||||
|
@ -4,7 +4,7 @@ import { useAppStore } from '@/store/modules/app';
|
|||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import FirstLevelMenu from '../components/first-level-menu.vue';
|
import FirstLevelMenu from '../components/first-level-menu.vue';
|
||||||
import { useMenu, useMixMenuContext } from '../../../context';
|
import { useMenu, useMixMenuContext } from '../context';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'TopHybridSidebarFirst'
|
name: 'TopHybridSidebarFirst'
|
||||||
@ -13,7 +13,8 @@ defineOptions({
|
|||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||||
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } = useMixMenuContext();
|
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
|
||||||
|
useMixMenuContext('TopHybridSidebarFirst');
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import { useAppStore } from '@/store/modules/app';
|
|||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { useMenu, useMixMenuContext } from '../../../context';
|
import { useMenu, useMixMenuContext } from '../context';
|
||||||
import FirstLevelMenu from '../components/first-level-menu.vue';
|
import FirstLevelMenu from '../components/first-level-menu.vue';
|
||||||
import GlobalLogo from '../../global-logo/index.vue';
|
import GlobalLogo from '../../global-logo/index.vue';
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ const {
|
|||||||
handleSelectSecondLevelMenu,
|
handleSelectSecondLevelMenu,
|
||||||
getActiveSecondLevelMenuKey,
|
getActiveSecondLevelMenuKey,
|
||||||
childLevelMenus
|
childLevelMenus
|
||||||
} = useMixMenuContext();
|
} = useMixMenuContext('VerticalHybridHeaderFirst');
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
|
|
||||||
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
||||||
|
@ -7,7 +7,7 @@ import { useAppStore } from '@/store/modules/app';
|
|||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { useMenu } from '../../../context';
|
import { useMenu } from '../context';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'VerticalMenu'
|
name: 'VerticalMenu'
|
||||||
|
@ -10,7 +10,7 @@ import { useThemeStore } from '@/store/modules/theme';
|
|||||||
import { useRouteStore } from '@/store/modules/route';
|
import { useRouteStore } from '@/store/modules/route';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import { useMenu, useMixMenuContext } from '../../../context';
|
import { useMenu, useMixMenuContext } from '../context';
|
||||||
import FirstLevelMenu from '../components/first-level-menu.vue';
|
import FirstLevelMenu from '../components/first-level-menu.vue';
|
||||||
import GlobalLogo from '../../global-logo/index.vue';
|
import GlobalLogo from '../../global-logo/index.vue';
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ const {
|
|||||||
isActiveFirstLevelMenuHasChildren,
|
isActiveFirstLevelMenuHasChildren,
|
||||||
getActiveFirstLevelMenuKey,
|
getActiveFirstLevelMenuKey,
|
||||||
handleSelectFirstLevelMenu
|
handleSelectFirstLevelMenu
|
||||||
} = useMixMenuContext();
|
} = useMixMenuContext('VerticalMixMenu');
|
||||||
const { selectedKey } = useMenu();
|
const { selectedKey } = useMenu();
|
||||||
|
|
||||||
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
||||||
|
@ -10,7 +10,7 @@ import type { RequestInstanceState } from './type';
|
|||||||
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
|
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
|
||||||
const { baseURL, otherBaseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
|
const { baseURL, otherBaseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
|
||||||
|
|
||||||
export const request = createFlatRequest<App.Service.Response, RequestInstanceState>(
|
export const request = createFlatRequest(
|
||||||
{
|
{
|
||||||
baseURL,
|
baseURL,
|
||||||
headers: {
|
headers: {
|
||||||
@ -18,6 +18,13 @@ export const request = createFlatRequest<App.Service.Response, RequestInstanceSt
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
defaultState: {
|
||||||
|
errMsgStack: [],
|
||||||
|
refreshTokenPromise: null
|
||||||
|
} as RequestInstanceState,
|
||||||
|
transformBackendResponse(response: AxiosResponse<App.Service.Response<any>>) {
|
||||||
|
return response.data.data;
|
||||||
|
},
|
||||||
async onRequest(config) {
|
async onRequest(config) {
|
||||||
const Authorization = getAuthorization();
|
const Authorization = getAuthorization();
|
||||||
Object.assign(config.headers, { Authorization });
|
Object.assign(config.headers, { Authorization });
|
||||||
@ -91,9 +98,6 @@ export const request = createFlatRequest<App.Service.Response, RequestInstanceSt
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
transformBackendResponse(response) {
|
|
||||||
return response.data.data;
|
|
||||||
},
|
|
||||||
onError(error) {
|
onError(error) {
|
||||||
// when the request is fail, you can show error message
|
// when the request is fail, you can show error message
|
||||||
|
|
||||||
@ -123,11 +127,14 @@ export const request = createFlatRequest<App.Service.Response, RequestInstanceSt
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const demoRequest = createRequest<App.Service.DemoResponse>(
|
export const demoRequest = createRequest(
|
||||||
{
|
{
|
||||||
baseURL: otherBaseURL.demo
|
baseURL: otherBaseURL.demo
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
transformBackendResponse(response: AxiosResponse<App.Service.DemoResponse>) {
|
||||||
|
return response.data.result;
|
||||||
|
},
|
||||||
async onRequest(config) {
|
async onRequest(config) {
|
||||||
const { headers } = config;
|
const { headers } = config;
|
||||||
|
|
||||||
@ -147,9 +154,6 @@ export const demoRequest = createRequest<App.Service.DemoResponse>(
|
|||||||
// when the backend response code is not "200", it means the request is fail
|
// when the backend response code is not "200", it means the request is fail
|
||||||
// for example: the token is expired, refresh token and retry request
|
// for example: the token is expired, refresh token and retry request
|
||||||
},
|
},
|
||||||
transformBackendResponse(response) {
|
|
||||||
return response.data.result;
|
|
||||||
},
|
|
||||||
onError(error) {
|
onError(error) {
|
||||||
// when the request is fail, you can show error message
|
// when the request is fail, you can show error message
|
||||||
|
|
||||||
|
@ -28,14 +28,14 @@ async function handleRefreshToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function handleExpiredRequest(state: RequestInstanceState) {
|
export async function handleExpiredRequest(state: RequestInstanceState) {
|
||||||
if (!state.refreshTokenFn) {
|
if (!state.refreshTokenPromise) {
|
||||||
state.refreshTokenFn = handleRefreshToken();
|
state.refreshTokenPromise = handleRefreshToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
const success = await state.refreshTokenFn;
|
const success = await state.refreshTokenPromise;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
state.refreshTokenFn = null;
|
state.refreshTokenPromise = null;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
export interface RequestInstanceState {
|
export interface RequestInstanceState {
|
||||||
/** whether the request is refreshing token */
|
/** the promise of refreshing token */
|
||||||
refreshTokenFn: Promise<boolean> | null;
|
refreshTokenPromise: Promise<boolean> | null;
|
||||||
/** the request error message stack */
|
/** the request error message stack */
|
||||||
errMsgStack: string[];
|
errMsgStack: string[];
|
||||||
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user