From e3bd39724808b8b961b9c77261d491f6e14aa3a0 Mon Sep 17 00:00:00 2001 From: Soybean Date: Sat, 27 Apr 2024 02:29:39 +0800 Subject: [PATCH] feat(projects): support repeated request errors occur once in a short time. close #368, close #369 --- src/locales/langs/en-us.ts | 6 ++++++ src/locales/langs/zh-cn.ts | 6 ++++++ src/service/request/index.ts | 24 ++++++++++----------- src/service/request/shared.ts | 23 +++++++++++++++++++++ src/service/request/type.ts | 6 ++++++ src/typings/app.d.ts | 6 ++++++ src/views/function/request/index.vue | 31 ++++++++++++++++++++++++++++ 7 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 src/service/request/type.ts diff --git a/src/locales/langs/en-us.ts b/src/locales/langs/en-us.ts index 3d0a88a3d..d9ce62fbb 100644 --- a/src/locales/langs/en-us.ts +++ b/src/locales/langs/en-us.ts @@ -287,6 +287,12 @@ const local: App.I18n.Schema = { superAdminVisible: 'Super Admin Visible', adminVisible: 'Admin Visible', adminOrUserVisible: 'Admin and User Visible' + }, + request: { + repeatedErrorOccurOnce: 'Repeated Request Error Occurs Once', + repeatedError: 'Repeated Request Error', + repeatedErrorMsg1: 'Custom Request Error 1', + repeatedErrorMsg2: 'Custom Request Error 2' } }, manage: { diff --git a/src/locales/langs/zh-cn.ts b/src/locales/langs/zh-cn.ts index 14373c6f1..fbb463f56 100644 --- a/src/locales/langs/zh-cn.ts +++ b/src/locales/langs/zh-cn.ts @@ -287,6 +287,12 @@ const local: App.I18n.Schema = { superAdminVisible: '超级管理员可见', adminVisible: '管理员可见', adminOrUserVisible: '管理员和用户可见' + }, + request: { + repeatedErrorOccurOnce: '重复请求错误只出现一次', + repeatedError: '重复请求错误', + repeatedErrorMsg1: '自定义请求错误 1', + repeatedErrorMsg2: '自定义请求错误 2' } }, manage: { diff --git a/src/service/request/index.ts b/src/service/request/index.ts index 240707df3..092aacf35 100644 --- a/src/service/request/index.ts +++ b/src/service/request/index.ts @@ -1,20 +1,16 @@ import type { AxiosResponse } from 'axios'; import { BACKEND_ERROR_CODE, createFlatRequest, createRequest } from '@sa/axios'; import { useAuthStore } from '@/store/modules/auth'; +import { $t } from '@/locales'; import { localStg } from '@/utils/storage'; import { getServiceBaseURL } from '@/utils/service'; -import { $t } from '@/locales'; -import { handleRefreshToken } from './shared'; +import { handleRefreshToken, showErrorMsg } from './shared'; +import type { RequestInstanceState } from './type'; const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y'; const { baseURL, otherBaseURL } = getServiceBaseURL(import.meta.env, isHttpProxy); -interface InstanceState { - /** whether the request is refreshing token */ - isRefreshingToken: boolean; -} - -export const request = createFlatRequest( +export const request = createFlatRequest( { baseURL, headers: { @@ -35,7 +31,7 @@ export const request = createFlatRequest( isBackendSuccess(response) { // when the backend response code is "0000"(default), it means the request is success // to change this logic by yourself, you can modify the `VITE_SERVICE_SUCCESS_CODE` in `.env` file - return response.data.code === import.meta.env.VITE_SERVICE_SUCCESS_CODE; + return String(response.data.code) === import.meta.env.VITE_SERVICE_SUCCESS_CODE; }, async onBackendFail(response, instance) { const authStore = useAuthStore(); @@ -47,6 +43,8 @@ export const request = createFlatRequest( function logoutAndCleanup() { handleLogout(); window.removeEventListener('beforeunload', handleLogout); + + request.state.errMsgStack = request.state.errMsgStack.filter(msg => msg !== response.data.msg); } // when the backend response code is in `logoutCodes`, it means the user will be logged out and redirected to login page @@ -58,13 +56,15 @@ export const request = createFlatRequest( // when the backend response code is in `modalLogoutCodes`, it means the user will be logged out by displaying a modal const modalLogoutCodes = import.meta.env.VITE_SERVICE_MODAL_LOGOUT_CODES?.split(',') || []; - if (modalLogoutCodes.includes(response.data.code)) { + if (modalLogoutCodes.includes(response.data.code) && !request.state.errMsgStack?.includes(response.data.msg)) { + request.state.errMsgStack = [...(request.state.errMsgStack || []), response.data.msg]; + // prevent the user from refreshing the page window.addEventListener('beforeunload', handleLogout); window.$dialog?.error({ title: 'Error', - content: response.data.msg, + content: response.data.code, positiveText: $t('common.confirm'), maskClosable: false, onPositiveClick() { @@ -122,7 +122,7 @@ export const request = createFlatRequest( return; } - window.$message?.error?.(message); + showErrorMsg(request.state, message); } } ); diff --git a/src/service/request/shared.ts b/src/service/request/shared.ts index 151a85267..decd63d07 100644 --- a/src/service/request/shared.ts +++ b/src/service/request/shared.ts @@ -2,6 +2,7 @@ import type { AxiosRequestConfig } from 'axios'; import { useAuthStore } from '@/store/modules/auth'; import { localStg } from '@/utils/storage'; import { fetchRefreshToken } from '../api'; +import type { RequestInstanceState } from './type'; /** * refresh token @@ -29,3 +30,25 @@ export async function handleRefreshToken(axiosConfig: AxiosRequestConfig) { return null; } + +export function showErrorMsg(state: RequestInstanceState, message: string) { + if (!state.errMsgStack?.length) { + state.errMsgStack = []; + } + + const isExist = state.errMsgStack.includes(message); + + if (!isExist) { + state.errMsgStack.push(message); + + window.$message?.error(message, { + onLeave: () => { + state.errMsgStack = state.errMsgStack.filter(msg => msg !== message); + + setTimeout(() => { + state.errMsgStack = []; + }, 5000); + } + }); + } +} diff --git a/src/service/request/type.ts b/src/service/request/type.ts new file mode 100644 index 000000000..b2a5f9c4d --- /dev/null +++ b/src/service/request/type.ts @@ -0,0 +1,6 @@ +export interface RequestInstanceState { + /** whether the request is refreshing token */ + isRefreshingToken: boolean; + /** the request error message stack */ + errMsgStack: string[]; +} diff --git a/src/typings/app.d.ts b/src/typings/app.d.ts index 643d7eca3..031c347f9 100644 --- a/src/typings/app.d.ts +++ b/src/typings/app.d.ts @@ -472,6 +472,12 @@ declare namespace App { adminVisible: string; adminOrUserVisible: string; }; + request: { + repeatedErrorOccurOnce: string; + repeatedError: string; + repeatedErrorMsg1: string; + repeatedErrorMsg2: string; + }; }; manage: { common: { diff --git a/src/views/function/request/index.vue b/src/views/function/request/index.vue index be388c083..fb6df0b16 100644 --- a/src/views/function/request/index.vue +++ b/src/views/function/request/index.vue @@ -13,6 +13,25 @@ async function logoutWithModal() { async function refreshToken() { await fetchCustomBackendError('9999', $t('request.tokenExpired')); } + +async function handleRepeatedMessageError() { + await Promise.all([ + fetchCustomBackendError('2222', $t('page.function.request.repeatedErrorMsg1')), + fetchCustomBackendError('2222', $t('page.function.request.repeatedErrorMsg1')), + fetchCustomBackendError('2222', $t('page.function.request.repeatedErrorMsg1')), + fetchCustomBackendError('3333', $t('page.function.request.repeatedErrorMsg2')), + fetchCustomBackendError('3333', $t('page.function.request.repeatedErrorMsg2')), + fetchCustomBackendError('3333', $t('page.function.request.repeatedErrorMsg2')) + ]); +} + +async function handleRepeatedModalError() { + await Promise.all([ + fetchCustomBackendError('7777', $t('request.logoutWithModalMsg')), + fetchCustomBackendError('7777', $t('request.logoutWithModalMsg')), + fetchCustomBackendError('7777', $t('request.logoutWithModalMsg')) + ]); +}