From 14368e8a09c4b5793d0975f85e36a4c1d410ca36 Mon Sep 17 00:00:00 2001
From: wupengfei <834520024@qq.com>
Date: 星期五, 16 五月 2025 17:27:24 +0800
Subject: [PATCH] feat: UI

---
 apps/h5/src/utils/request/index.ts |  303 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 303 insertions(+), 0 deletions(-)

diff --git a/apps/h5/src/utils/request/index.ts b/apps/h5/src/utils/request/index.ts
new file mode 100644
index 0000000..3ebb819
--- /dev/null
+++ b/apps/h5/src/utils/request/index.ts
@@ -0,0 +1,303 @@
+import { type IRequestOptions, BoleRequest, type RequestConfig } from '@bole-core/core';
+import { type AxiosRequestConfig, type AxiosError } from 'axios';
+import qs from 'qs';
+import NProgress from '../progress';
+import { loadEnv } from '@build/index';
+import { getToken, getUserInfo } from '../storage';
+import { useUserStoreHook } from '@/store/modules/user';
+import { ElLoading as Loading, ElNotification } from 'element-plus';
+import { router } from '@/router';
+import { Message, tokenIsExpired } from '@/utils';
+
+// 鍔犺浇鐜鍙橀噺 VITE_PROXY_DOMAIN锛堝紑鍙戠幆澧冿級  VITE_PROXY_DOMAIN_REAL锛堟墦鍖呭悗鐨勭嚎涓婄幆澧冿級
+const { VITE_PROXY_DOMAIN, VITE_PROXY_DOMAIN_REAL, DEV } = loadEnv();
+
+const RefreshTokenUrl = '/GetTokenByRefreshToken';
+
+/** 璇锋眰鐧藉悕鍗曪紝鏀剧疆涓�浜涗笉闇�瑕乼oken鐨勬帴鍙o紙閫氳繃璁剧疆璇锋眰鐧藉悕鍗曪紝闃叉token杩囨湡鍚庡啀璇锋眰閫犳垚鐨勬寰幆闂锛� */
+const whiteList = [RefreshTokenUrl, '/GetToken'];
+
+let loadingInstance: ReturnType<typeof Loading.service>;
+
+function startLoading() {
+  loadingInstance = Loading.service({
+    fullscreen: true,
+    lock: true,
+    background: 'transparent',
+  });
+}
+
+function endLoading() {
+  if (loadingInstance) {
+    loadingInstance.close();
+  }
+}
+
+// 閿欒澶勭悊鏂规锛� 閿欒绫诲瀷
+enum ErrorShowType {
+  /**
+   * 鍟ヤ篃涓嶅共
+   */
+  SILENT = 0,
+
+  WARN_MESSAGE = 1,
+  ERROR_MESSAGE = 2,
+  /**
+   * 閫氱煡鎶ラ敊
+   */
+  NOTIFICATION = 3,
+  /**
+   * 閲嶅畾鍚�
+   */
+  REDIRECT = 9,
+}
+// 涓庡悗绔害瀹氱殑鍝嶅簲鏁版嵁鏍煎紡
+interface ResponseStructure {
+  success: boolean;
+  data: any;
+  /**
+   * 閿欒鐮�
+   */
+  error?: number;
+  /**
+   * 閿欒淇℃伅
+   */
+  msg?: string;
+  showType?: ErrorShowType;
+}
+
+interface ErrorResponse {
+  error?: {
+    data: any;
+    code: number;
+    message: string;
+  };
+}
+
+interface ErrorInfo {
+  errorCode?: number;
+  errorMessage?: string;
+  showType?: ErrorShowType;
+  data: any;
+}
+
+const AxiosOptions: AxiosRequestConfig = {
+  baseURL: DEV ? VITE_PROXY_DOMAIN : VITE_PROXY_DOMAIN_REAL,
+  timeout: 30000,
+  headers: {
+    Accept: 'application/json, text/plain, */*',
+    'Content-Type': 'application/json',
+    'X-Requested-With': 'XMLHttpRequest',
+  },
+  // 鏁扮粍鏍煎紡鍙傛暟搴忓垪鍖�
+  paramsSerializer: {
+    //@ts-ignore
+    serialize: (params) => qs.stringify(params, { indices: false }),
+  },
+  withCredentials: true,
+};
+
+const config: RequestConfig<ResponseStructure, IRequestOptions> = {
+  ...AxiosOptions,
+
+  errorConfig: {
+    // 閿欒鎺ユ敹鍙婂鐞�
+    errorHandler: (error, opts) => {
+      console.log('error: ', error);
+      if (opts?.skipErrorHandler) throw error;
+
+      if (opts?.customErrorHandler) {
+        if (opts?.customErrorHandler(error)) {
+          return;
+        }
+      }
+
+      // 鎴戜滑鐨� errorThrower 鎶涘嚭鐨勯敊璇��
+      if (error.name === 'BizError') {
+        const errorInfo: ErrorInfo | undefined = (error as any).info;
+        if (errorInfo) {
+          const { errorMessage, errorCode } = errorInfo;
+          switch (errorInfo.showType) {
+            case ErrorShowType.SILENT:
+              // do nothing
+              break;
+            case ErrorShowType.WARN_MESSAGE:
+              Message.warnMessage(errorMessage);
+              break;
+            case ErrorShowType.ERROR_MESSAGE:
+              Message.errorMessage(errorMessage);
+              break;
+            case ErrorShowType.NOTIFICATION:
+              ElNotification.error({
+                message: `${errorCode}: ${errorMessage}`,
+              });
+              break;
+            case ErrorShowType.REDIRECT:
+              // TODO: redirect
+              break;
+            default:
+              Message.errorMessage(errorMessage);
+          }
+        }
+      } else if ((error as AxiosError<ResponseStructure, IRequestOptions>).response) {
+        // Axios 鐨勯敊璇�
+        // 璇锋眰鎴愬姛鍙戝嚭涓旀湇鍔″櫒涔熷搷搴斾簡鐘舵�佺爜锛屼絾鐘舵�佷唬鐮佽秴鍑轰簡 2xx 鐨勮寖鍥�
+        handleAxiosResponseError(error as AxiosError<ErrorResponse, IRequestOptions>);
+        // Message.errorMessage(`Response status:${(error as AxiosError).response.status}`);
+      } else if ((error as AxiosError).request) {
+        // 璇锋眰宸茬粡鎴愬姛鍙戣捣锛屼絾娌℃湁鏀跺埌鍝嶅簲
+        // \`error.request\` 鍦ㄦ祻瑙堝櫒涓槸 XMLHttpRequest 鐨勫疄渚嬶紝
+        // 鑰屽湪node.js涓槸 http.ClientRequest 鐨勫疄渚�
+        Message.errorMessage('鏈嶅姟鍣ㄦ棤鍝嶅簲锛岃閲嶈瘯');
+      } else {
+        // 鍙戦�佽姹傛椂鍑轰簡鐐归棶棰�
+        Message.errorMessage('鍙戦�佽姹傛椂鍑轰簡鐐归棶棰�');
+      }
+    },
+
+    // 閿欒鎶涘嚭
+    errorThrower: (res) => {
+      const { success, data, error: errorCode, msg, showType } = res;
+      if (!success) {
+        const error: any = new Error(msg);
+        error.name = 'BizError';
+        error.info = { errorCode, errorMessage: msg, showType, data };
+        throw error; // 鎶涘嚭鑷埗鐨勯敊璇�
+      }
+    },
+  },
+
+  requestInterceptors: [
+    [
+      (config) => {
+        const $config = config;
+        // 寮�鍚繘搴︽潯鍔ㄧ敾
+        if (config.needNProcess) {
+          NProgress.start();
+        }
+
+        const token = getToken();
+
+        const userInfo = getUserInfo();
+
+        const { showLoading = true, mock } = $config;
+
+        if (mock) {
+          $config.url = $config.url.replace(/^\/api/, '');
+        }
+
+        /**
+         * 濡傛灉鏄痳efreshToken杩欎釜鏂规硶 灏辩洿鎺ヨ繑鍥� 闃叉鍐呭瓨娉勬紡
+         */
+        if (whiteList.some((url) => $config.url.toLowerCase().includes(url.toLowerCase()))) {
+          return $config;
+        }
+
+        if (showLoading) {
+          startLoading();
+        }
+
+        return new Promise((resolve) => {
+          if (token && $config.withCredentials) {
+            if (tokenIsExpired(userInfo)) {
+              if (!BoleRequest.refreshTokenPending) {
+                const userStore = useUserStoreHook();
+                BoleRequest.refreshTokenPending = true;
+                // token杩囨湡鍒锋柊
+                userStore
+                  .refreshToken({
+                    refreshToken: userInfo.refreshToken,
+                    clientId: userStore.accountInfo.client_id,
+                  })
+                  .then((res) => {
+                    $config.headers['Authorization'] = 'Bearer ' + res.accessToken;
+                    BoleRequest.requests.forEach((cb) => cb(res.accessToken));
+                    BoleRequest.requests = [];
+                  })
+                  .finally(() => {
+                    BoleRequest.refreshTokenPending = false;
+                  });
+              }
+              resolve(BoleRequest.retryOriginalRequest($config));
+            } else {
+              $config.headers['Authorization'] = 'Bearer ' + token;
+              resolve($config);
+            }
+          } else {
+            resolve($config);
+          }
+        });
+      },
+      (error: AxiosError) => {
+        endLoading();
+        return Promise.reject(error);
+      },
+    ],
+  ],
+  responseInterceptors: [
+    [
+      (response) => {
+        const $config = response.config as IRequestOptions;
+
+        const { needNProcess, getResponse = false } = $config;
+
+        // 鍏抽棴杩涘害鏉″姩鐢�
+        if (needNProcess) {
+          NProgress.done();
+        }
+
+        endLoading();
+
+        return getResponse ? response : (response.data as any).result;
+      },
+      (error) => {
+        endLoading();
+        return Promise.reject(error);
+      },
+    ],
+  ],
+};
+
+const ErrorMessageMap = {
+  [400]: '璇锋眰閿欒',
+  [401]: '鏈巿鏉冿紝璇风櫥褰�',
+  [403]: '鎷掔粷璁块棶',
+  [404]: '璇锋眰鍦板潃鍑洪敊',
+  [408]: '璇锋眰瓒呮椂',
+  [500]: '鏈嶅姟鍣ㄥ唴閮ㄩ敊璇�',
+  [501]: '鏈嶅姟鏈疄鐜�',
+  [502]: '缃戝叧閿欒',
+  [503]: '鏈嶅姟涓嶅彲鐢�',
+  [504]: '缃戝叧瓒呮椂',
+  [505]: 'HTTP鐗堟湰涓嶅彈鏀寔',
+};
+
+function handleAxiosResponseError(error: AxiosError<ErrorResponse, IRequestOptions>) {
+  if (error.response.config.url.toLowerCase().includes(RefreshTokenUrl.toLowerCase())) {
+    handleLogout();
+    return;
+  }
+  if (error && error.response) {
+    let message = ErrorMessageMap[error.response?.status] ?? '璇锋眰閿欒';
+    if (error.response.data?.error?.message) {
+      message = error.response.data?.error?.message;
+    }
+
+    if (error.response?.status === 401) {
+      handleLogout();
+    }
+    Message.errorMessage(message);
+  }
+}
+
+function handleLogout() {
+  const userStore = useUserStoreHook();
+  let path = '/';
+  if (userStore.token) {
+    path = router.currentRoute.value.fullPath;
+  }
+  userStore.logout();
+  router.replace(`/login?redirect=${path}`);
+}
+
+export const request = BoleRequest.create(config);

--
Gitblit v1.9.1