linyuanjie před 4 měsíci
rodič
revize
2de098ac7f

+ 1 - 0
package.json

@@ -49,6 +49,7 @@
     "highlight.js": "^11.9.0",
     "i18next": "^23.5.1",
     "i18next-browser-languagedetector": "^7.1.0",
+    "nanoid": "^5.1.0",
     "nprogress": "^0.2.0",
     "numeral": "^2.0.6",
     "qs": "^6.11.0",

+ 10 - 0
pnpm-lock.yaml

@@ -119,6 +119,9 @@ importers:
       i18next-browser-languagedetector:
         specifier: ^7.1.0
         version: 7.2.1
+      nanoid:
+        specifier: ^5.1.0
+        version: 5.1.0
       nprogress:
         specifier: ^0.2.0
         version: 0.2.0
@@ -3648,6 +3651,11 @@ packages:
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     hasBin: true
 
+  nanoid@5.1.0:
+    resolution: {integrity: sha512-zDAl/llz8Ue/EblwSYwdxGBYfj46IM1dhjVi8dyp9LQffoIGxJEAHj2oeZ4uNcgycSRcQ83CnfcZqEJzVDLcDw==}
+    engines: {node: ^18 || >=20}
+    hasBin: true
+
   nanomatch@1.2.13:
     resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
     engines: {node: '>=0.10.0'}
@@ -9420,6 +9428,8 @@ snapshots:
 
   nanoid@3.3.7: {}
 
+  nanoid@5.1.0: {}
+
   nanomatch@1.2.13:
     dependencies:
       arr-diff: 4.0.0

+ 35 - 8
src/api/request/index.ts

@@ -3,11 +3,18 @@ import { navigate } from '@/router/components/SetupNavigation'
 import { ResultEnum } from '#/enum'
 import { t } from '@/locales/i18n'
 import axios, { type AxiosError, type AxiosRequestConfig, type AxiosResponse } from 'axios'
+import { nanoid } from 'nanoid'
 import qs from 'qs'
 import { toast } from 'sonner'
 import { serialize } from '@/utils/util'
 import { BASE_URL, TIME_OUT } from './config'
 
+declare module 'axios' {
+  interface InternalAxiosRequestConfig {
+    nanoid?: string // 唯一id,用于中止请求
+  }
+}
+
 const handleNetworkError = (response: AxiosResponse) => {
   const { actions } = useUserSessionStore.getState()
   if (!response) return
@@ -20,10 +27,8 @@ const handleNetworkError = (response: AxiosResponse) => {
         break
       case 401:
         errMessage = '未授权,请重新登录'
-        // abortRequestFn()
-        // logoutFn()
-        actions.clearUserInfoAndToken()
-        navigate('/login')
+        abortRequestFn()
+        actions.logout()
         break
       case 403:
         errMessage = '拒绝访问'
@@ -39,10 +44,8 @@ const handleNetworkError = (response: AxiosResponse) => {
         break
       case 424:
         errMessage = 'token无效'
-        // abortRequestFn()
-        // logoutFn()
-        actions.clearUserInfoAndToken()
-        navigate('/login')
+        abortRequestFn()
+        actions.logout()
         break
       case 500:
         errMessage = '服务器端出错'
@@ -79,6 +82,17 @@ const handleNetworkError = (response: AxiosResponse) => {
   })
 }
 
+// 请求集合
+const requestMap = new Map<string, AbortController>()
+
+// 中止请求
+const abortRequestFn = () => {
+  for (const [mapKey, controller] of requestMap) {
+    controller.abort() // 中止
+    requestMap.delete(mapKey) // 清除
+  }
+}
+
 // 创建 axios 实例
 const axiosInstance = axios.create({
   baseURL: BASE_URL,
@@ -88,6 +102,13 @@ const axiosInstance = axios.create({
 // 请求拦截
 axiosInstance.interceptors.request.use(
   (config) => {
+    // 收集所有请求
+    const mapKey = nanoid()
+    const controller = new AbortController()
+    config.signal = controller.signal
+    config.nanoid = mapKey
+    requestMap.set(mapKey, controller)
+
     const { tenant_id, access_token } = useUserSessionStore.getState()
     const isToken = (config.headers || {}).isToken === false
     if (access_token && !isToken) {
@@ -120,6 +141,9 @@ axiosInstance.interceptors.request.use(
 // 响应拦截
 axiosInstance.interceptors.response.use(
   (res) => {
+    const mapKey = res.config.nanoid
+    requestMap.delete(mapKey as string)
+
     if (!res.data) throw new Error(t('sys.api.apiRequestFailed'))
     const { data, status, statusText } = res
 
@@ -133,6 +157,9 @@ axiosInstance.interceptors.response.use(
     throw new Error(statusText || t('sys.api.apiRequestFailed'))
   },
   (err) => {
+    // 清除当前请求
+    const mapKey = err.config.nanoid
+    requestMap.delete(mapKey as string)
     handleNetworkError(err.response)
     if (err.response) {
       // 请求已发出, 服务器用状态码响应

+ 4 - 4
src/layouts/components/account-dropdown.tsx

@@ -18,12 +18,12 @@ const randomIndex = Math.floor(Math.random() * 5)
 export default function AccountDropdown() {
   const { replace } = useRouter()
   const { username, phone } = useUserInfo()
-  const { clearUserInfoAndToken } = useUserActions()
+  const { logout } = useUserActions()
   // const { backToLogin } = useLoginStateContext()
   const { t } = useTranslation()
-  const logout = () => {
+  const logoutFn = () => {
     try {
-      clearUserInfoAndToken()
+      logout()
       // backToLogin();
     } catch (error) {
       console.log(error)
@@ -73,7 +73,7 @@ export default function AccountDropdown() {
         </button>
       ),
       key: '2',
-      onClick: logout
+      onClick: logoutFn
     }
   ]
 

+ 4 - 2
src/store/userStore.ts

@@ -1,6 +1,7 @@
 import { useNavigate } from 'react-router'
 import { create } from 'zustand'
 import { createJSONStorage, persist } from 'zustand/middleware'
+import { navigate } from '@/router/components/SetupNavigation'
 import type { UserToken } from '#/entity'
 import { StorageEnum } from '#/enum'
 import { toast } from 'sonner'
@@ -60,7 +61,7 @@ type UserStore = {
     setRefreshToken: (refresh_token: string) => void
     setMenu: (params: { menu?: UserStore['menu']; type?: boolean }) => void
     setPermissions: (permissions: string[]) => void
-    clearUserInfoAndToken: () => void
+    logout: () => void
     getUserDetail: () => Promise<void>
     getUserInfo: () => Promise<void>
   }
@@ -108,7 +109,7 @@ export const useUserSessionStore = create<UserStore>()(
           }
           set({ permissions: list })
         },
-        clearUserInfoAndToken() {
+        logout() {
           set({
             userInfo: {},
             tenantInfo: {},
@@ -119,6 +120,7 @@ export const useUserSessionStore = create<UserStore>()(
             access_token: '',
             refresh_token: ''
           })
+          navigate('/login')
         },
         getUserInfo() {
           const { actions } = get()