feat(projects): @sa/axios: createRequest, createFlatRequest, createHookRequest

This commit is contained in:
Soybean 2024-01-16 01:50:12 +08:00
parent fbf4cc430d
commit bac1632457
26 changed files with 672 additions and 75 deletions

View File

@ -9,7 +9,8 @@ export default defineConfig(
{
ignores: ['index', 'App', '[id]']
}
]
],
'no-empty-function': 'off'
}
}
);

View File

@ -25,10 +25,10 @@
"dependencies": {
"@better-scroll/core": "2.5.1",
"@iconify/vue": "4.1.1",
"@sa/axios": "workspace:*",
"@sa/color-palette": "workspace:*",
"@sa/hooks": "workspace:*",
"@sa/materials": "workspace:*",
"@sa/request": "workspace:*",
"@sa/utils": "workspace:*",
"@vueuse/core": "10.7.2",
"clipboard": "2.0.11",

View 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"
}
}

View 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
View 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 };

View 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;
}

View 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;
}

View 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;
}

View File

@ -8,5 +8,8 @@
"*": {
"*": ["./src/*"]
}
},
"dependencies": {
"@sa/axios": "workspace:*"
}
}

View 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;
}

View File

@ -1,5 +1,5 @@
{
"name": "@sa/request",
"name": "@sa/fetch",
"version": "1.0.0",
"exports": {
".": "./src/index.ts"
@ -10,7 +10,6 @@
}
},
"dependencies": {
"axios": "1.6.5",
"ofetch": "1.3.3"
}
}

View File

@ -1,10 +1,10 @@
import { ofetch } from 'ofetch';
import type { FetchOptions } from 'ofetch';
export function createOfetch(options: FetchOptions) {
export function createRequest(options: FetchOptions) {
const request = ofetch.create(options);
return request;
}
export default createOfetch;
export default createRequest;

View 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"]
}

View File

@ -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;

View File

@ -1,4 +0,0 @@
import { createAxios } from './axios';
import { createOfetch } from './ofetch';
export { createAxios, createOfetch };

View File

@ -12,7 +12,8 @@
"dependencies": {
"colord": "2.9.3",
"crypto-js": "4.2.0",
"localforage": "1.10.0"
"localforage": "1.10.0",
"nanoid": "5.0.4"
},
"devDependencies": {
"@types/crypto-js": "4.2.1"

View File

@ -1,3 +1,4 @@
export * from './color';
export * from './crypto';
export * from './storage';
export * from './nanoid';

View File

@ -0,0 +1,3 @@
import { nanoid } from 'nanoid';
export { nanoid };

View File

@ -14,6 +14,9 @@ importers:
'@iconify/vue':
specifier: 4.1.1
version: 4.1.1(vue@3.4.14)
'@sa/axios':
specifier: workspace:*
version: link:packages/axios
'@sa/color-palette':
specifier: workspace:*
version: link:packages/color-palette
@ -23,9 +26,6 @@ importers:
'@sa/materials':
specifier: workspace:*
version: link:packages/materials
'@sa/request':
specifier: workspace:*
version: link:packages/request
'@sa/utils':
specifier: workspace:*
version: link:packages/utils
@ -160,6 +160,18 @@ importers:
specifier: 1.8.27
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:
dependencies:
colord:
@ -172,7 +184,11 @@ importers:
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)
packages/hooks: {}
packages/hooks:
dependencies:
'@sa/axios':
specifier: workspace:*
version: link:../axios
packages/materials:
dependencies:
@ -190,11 +206,8 @@ importers:
specifier: 0.8.1
version: 0.8.1
packages/request:
packages/ofetch:
dependencies:
axios:
specifier: 1.6.5
version: 1.6.5
ofetch:
specifier: 1.3.3
version: 1.3.3
@ -236,6 +249,9 @@ importers:
localforage:
specifier: 1.10.0
version: 1.10.0
nanoid:
specifier: 5.0.4
version: 5.0.4
devDependencies:
'@types/crypto-js':
specifier: 4.2.1
@ -2564,6 +2580,15 @@ packages:
engines: {node: '>= 0.4'}
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:
resolution: {integrity: sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==}
dependencies:
@ -5096,6 +5121,11 @@ packages:
has-tostringtag: 1.0.0
dev: true
/is-retry-allowed@2.2.0:
resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==}
engines: {node: '>=10'}
dev: false
/is-shared-array-buffer@1.0.2:
resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
dependencies:
@ -6079,6 +6109,12 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
/nanoid@5.0.4:
resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
engines: {node: ^18 || >=20}
hasBin: true
dev: false
/nanomatch@1.2.13:
resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
engines: {node: '>=0.10.0'}

View File

@ -7,9 +7,10 @@ import { request } from '../request';
* @param password Password
*/
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',
body: {
data: {
userName,
password
}
@ -18,7 +19,7 @@ export function fetchLogin(userName: string, password: string) {
/** Get user info */
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
*/
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',
body: {
data: {
refreshToken
}
});
}
export function fetchDebug() {
return request<string>({
url: '/debug-post',
method: 'post',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: {
a: '1'
}
});
}

View File

@ -6,7 +6,7 @@ import { request } from '../request';
* @param example Whether to use example data, default: 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
*/
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 } });
}

View File

@ -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 { 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';
export const request = createRequest({
baseURL: isHttpProxy ? createProxyPattern() : baseURL,
headers: {
apifoxToken: 'XL299LiMEDZ0H5h3A29PxwQXdMJqWyY2'
export const request = createFlatRequest<App.Service.Response>(
{
baseURL: isHttpProxy ? createProxyPattern() : baseURL,
headers: {
apifoxToken: 'XL299LiMEDZ0H5h3A29PxwQXdMJqWyY2'
}
},
onRequest({ options }) {
if (options.headers) {
{
async onRequest(config) {
const { headers } = config;
// set 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);
}
}
);

View File

@ -45,27 +45,28 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
async function login(userName: string, password: string) {
startLoading();
try {
const { data: loginToken } = await fetchLogin(userName, password);
const { data: loginToken, error } = 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) {
window.$notification?.success({
title: $t('page.login.common.loginSuccess'),
content: $t('page.login.common.welcomeBack', { userName: userInfo.userName }),
duration: 4500
});
if (routeStore.isInitAuthRoute) {
window.$notification?.success({
title: $t('page.login.common.loginSuccess'),
content: $t('page.login.common.welcomeBack', { userName: userInfo.userName })
});
}
}
} catch {
} else {
resetStore();
} finally {
endLoading();
}
endLoading();
}
async function loginByToken(loginToken: Api.Auth.LoginToken) {
@ -73,14 +74,20 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
localStg.set('token', loginToken.token);
localStg.set('refreshToken', loginToken.refreshToken);
const { data: info } = await fetchGetUserInfo();
const { data: info, error } = await fetchGetUserInfo();
// 2. store user info
localStg.set('userInfo', info);
if (!error) {
// 2. store user info
localStg.set('userInfo', info);
// 3. update auth route
token.value = loginToken.token;
Object.assign(userInfo, info);
// 3. update auth route
token.value = loginToken.token;
Object.assign(userInfo, info);
return true;
}
return false;
}
return {

View File

@ -167,17 +167,19 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
/** Init dynamic auth route */
async function initDynamicAuthRoute() {
const {
data: { routes, home }
} = await fetchGetUserRoutes();
const { data, error } = 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
View File

@ -426,9 +426,19 @@ declare namespace App {
/** The backend service response code */
code: string;
/** The backend service response message */
message: string;
msg: string;
/** The backend service response data */
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;
};
}
}