@4399ywkf/cli 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/dist/templates/HarmonyOS_Sans_Bold.woff2 +0 -0
  2. package/dist/templates/HarmonyOS_Sans_Medium.woff2 +0 -0
  3. package/dist/templates/HarmonyOS_Sans_Regular.woff2 +0 -0
  4. package/dist/templates/Locale.tsx +14 -18
  5. package/dist/templates/MainContentWrap.tsx +11 -15
  6. package/dist/templates/ThemeContext.tsx +27 -24
  7. package/dist/templates/app/config/env/.env.public.tpl +2 -19
  8. package/dist/templates/app/config/jwt/index.ts +4 -4
  9. package/dist/templates/app/config/request/error-handler.ts +67 -0
  10. package/dist/templates/app/config/request/index.ts +127 -129
  11. package/dist/templates/app/config/request/interceptors.ts +118 -0
  12. package/dist/templates/app/config/request/token-manager.ts +23 -0
  13. package/dist/templates/app/config/request/types.ts +63 -0
  14. package/dist/templates/app/config/rspack/rspack.config.mjs +62 -61
  15. package/dist/templates/app/config/rspack/rspack.prod.mjs +41 -62
  16. package/dist/templates/app/locales/zh-CN/common.json +3 -0
  17. package/dist/templates/app/package.json.tpl +1 -10
  18. package/dist/templates/app/public/fonts/HarmonyOS_Sans_Bold.woff2 +0 -0
  19. package/dist/templates/app/public/fonts/HarmonyOS_Sans_Medium.woff2 +0 -0
  20. package/dist/templates/app/public/fonts/HarmonyOS_Sans_Regular.woff2 +0 -0
  21. package/dist/templates/app/react-app-env.d.ts +13 -8
  22. package/dist/templates/app/src/bootstrap/index.ts +34 -0
  23. package/dist/templates/app/src/config/env.ts +84 -0
  24. package/dist/templates/app/src/index.tsx +17 -51
  25. package/dist/templates/app/src/layout/Locale.tsx +14 -18
  26. package/dist/templates/app/src/layout/MainContentWrap.tsx +11 -15
  27. package/dist/templates/app/src/layout/ThemeContext.tsx +27 -24
  28. package/dist/templates/app/src/locales/default/common.ts +3 -1
  29. package/dist/templates/app/src/micro/garfish.ts +53 -0
  30. package/dist/templates/app/src/pages/base/index.tsx +189 -25
  31. package/dist/templates/app/src/routes.tsx +21 -12
  32. package/dist/templates/app/src/types/global.d.ts +19 -0
  33. package/dist/templates/app/src/utils/index.ts +3 -1
  34. package/dist/templates/app/store/middleware/createDevtools.ts +7 -7
  35. package/dist/templates/base/index.tsx +189 -25
  36. package/dist/templates/bootstrap/index.ts +34 -0
  37. package/dist/templates/common.json +3 -0
  38. package/dist/templates/common.ts +3 -1
  39. package/dist/templates/config/env/.env.public.tpl +2 -19
  40. package/dist/templates/config/env.ts +84 -0
  41. package/dist/templates/config/jwt/index.ts +4 -4
  42. package/dist/templates/config/request/error-handler.ts +67 -0
  43. package/dist/templates/config/request/index.ts +127 -129
  44. package/dist/templates/config/request/interceptors.ts +118 -0
  45. package/dist/templates/config/request/token-manager.ts +23 -0
  46. package/dist/templates/config/request/types.ts +63 -0
  47. package/dist/templates/config/rspack/rspack.config.mjs +62 -61
  48. package/dist/templates/config/rspack/rspack.prod.mjs +41 -62
  49. package/dist/templates/createDevtools.ts +7 -7
  50. package/dist/templates/default/common.ts +3 -1
  51. package/dist/templates/env/.env.public.tpl +2 -19
  52. package/dist/templates/env.ts +83 -2
  53. package/dist/templates/error-handler.ts +67 -0
  54. package/dist/templates/fonts/HarmonyOS_Sans_Bold.woff2 +0 -0
  55. package/dist/templates/fonts/HarmonyOS_Sans_Medium.woff2 +0 -0
  56. package/dist/templates/fonts/HarmonyOS_Sans_Regular.woff2 +0 -0
  57. package/dist/templates/garfish.ts +53 -0
  58. package/dist/templates/global.d.ts +19 -0
  59. package/dist/templates/index.tsx +189 -25
  60. package/dist/templates/interceptors.ts +118 -0
  61. package/dist/templates/jwt/index.ts +4 -4
  62. package/dist/templates/layout/Locale.tsx +14 -18
  63. package/dist/templates/layout/MainContentWrap.tsx +11 -15
  64. package/dist/templates/layout/ThemeContext.tsx +27 -24
  65. package/dist/templates/locales/default/common.ts +3 -1
  66. package/dist/templates/locales/zh-CN/common.json +3 -0
  67. package/dist/templates/micro/garfish.ts +53 -0
  68. package/dist/templates/middleware/createDevtools.ts +7 -7
  69. package/dist/templates/package.json.tpl +1 -10
  70. package/dist/templates/page.tsx +21 -19
  71. package/dist/templates/pages/base/index.tsx +189 -25
  72. package/dist/templates/public/fonts/HarmonyOS_Sans_Bold.woff2 +0 -0
  73. package/dist/templates/public/fonts/HarmonyOS_Sans_Medium.woff2 +0 -0
  74. package/dist/templates/public/fonts/HarmonyOS_Sans_Regular.woff2 +0 -0
  75. package/dist/templates/react-app-env.d.ts +13 -8
  76. package/dist/templates/request/error-handler.ts +67 -0
  77. package/dist/templates/request/index.ts +127 -129
  78. package/dist/templates/request/interceptors.ts +118 -0
  79. package/dist/templates/request/token-manager.ts +23 -0
  80. package/dist/templates/request/types.ts +63 -0
  81. package/dist/templates/routes.tsx +21 -12
  82. package/dist/templates/rspack/rspack.config.mjs +62 -61
  83. package/dist/templates/rspack/rspack.prod.mjs +41 -62
  84. package/dist/templates/rspack.config.mjs +62 -61
  85. package/dist/templates/rspack.prod.mjs +41 -62
  86. package/dist/templates/src/bootstrap/index.ts +34 -0
  87. package/dist/templates/src/config/env.ts +84 -0
  88. package/dist/templates/src/index.tsx +17 -51
  89. package/dist/templates/src/layout/Locale.tsx +14 -18
  90. package/dist/templates/src/layout/MainContentWrap.tsx +11 -15
  91. package/dist/templates/src/layout/ThemeContext.tsx +27 -24
  92. package/dist/templates/src/locales/default/common.ts +3 -1
  93. package/dist/templates/src/micro/garfish.ts +53 -0
  94. package/dist/templates/src/pages/base/index.tsx +189 -25
  95. package/dist/templates/src/routes.tsx +21 -12
  96. package/dist/templates/src/types/global.d.ts +19 -0
  97. package/dist/templates/src/utils/index.ts +3 -1
  98. package/dist/templates/store/middleware/createDevtools.ts +7 -7
  99. package/dist/templates/token-manager.ts +23 -0
  100. package/dist/templates/types/global.d.ts +19 -0
  101. package/dist/templates/utils/index.ts +3 -1
  102. package/dist/templates/zh-CN/common.json +3 -0
  103. package/package.json +1 -1
  104. package/dist/templates/app/config/sentry/sentry.config.ts +0 -188
  105. package/dist/templates/app/src/hooks/useRouteTitle.tsx +0 -36
  106. package/dist/templates/app/src/hooks/useSentry.ts +0 -92
  107. package/dist/templates/app/src/pages/base/layout.tsx +0 -6
  108. package/dist/templates/app/src/pages/base/page.tsx +0 -25
  109. package/dist/templates/app/src/utils/env.ts +0 -3
  110. package/dist/templates/app/src/utils/format.ts +0 -21
  111. package/dist/templates/app/src/utils/getMicroApp.ts +0 -39
  112. package/dist/templates/app/src/utils/sentry.ts +0 -187
  113. package/dist/templates/app/src/utils/sentryDecorators.ts +0 -34
  114. package/dist/templates/app/src/utils/updateVersion.ts +0 -186
  115. package/dist/templates/base/layout.tsx +0 -6
  116. package/dist/templates/base/page.tsx +0 -25
  117. package/dist/templates/config/public/404.png +0 -0
  118. package/dist/templates/config/public/favicon.ico +0 -0
  119. package/dist/templates/config/public/images/banner_market_modal.webp +0 -0
  120. package/dist/templates/config/public/images/chatmode_chat_dark.webp +0 -0
  121. package/dist/templates/config/public/images/chatmode_chat_light.webp +0 -0
  122. package/dist/templates/config/public/images/chatmode_docs_dark.webp +0 -0
  123. package/dist/templates/config/public/images/chatmode_docs_light.webp +0 -0
  124. package/dist/templates/config/public/images/empty_topic_dark.webp +0 -0
  125. package/dist/templates/config/public/images/empty_topic_light.webp +0 -0
  126. package/dist/templates/config/public/images/screenshot_background.webp +0 -0
  127. package/dist/templates/config/public/images/theme_auto.webp +0 -0
  128. package/dist/templates/config/public/images/theme_dark.webp +0 -0
  129. package/dist/templates/config/public/images/theme_light.webp +0 -0
  130. package/dist/templates/config/public/index.html +0 -29
  131. package/dist/templates/config/sentry/sentry.config.ts +0 -188
  132. package/dist/templates/format.ts +0 -21
  133. package/dist/templates/getMicroApp.ts +0 -39
  134. package/dist/templates/hooks/useRouteTitle.tsx +0 -36
  135. package/dist/templates/hooks/useSentry.ts +0 -92
  136. package/dist/templates/layout.tsx +0 -6
  137. package/dist/templates/pages/base/layout.tsx +0 -6
  138. package/dist/templates/pages/base/page.tsx +0 -25
  139. package/dist/templates/sentry/sentry.config.ts +0 -188
  140. package/dist/templates/sentry.config.ts +0 -188
  141. package/dist/templates/sentry.ts +0 -187
  142. package/dist/templates/sentryDecorators.ts +0 -34
  143. package/dist/templates/src/hooks/useRouteTitle.tsx +0 -36
  144. package/dist/templates/src/hooks/useSentry.ts +0 -92
  145. package/dist/templates/src/pages/base/layout.tsx +0 -6
  146. package/dist/templates/src/pages/base/page.tsx +0 -25
  147. package/dist/templates/src/utils/env.ts +0 -3
  148. package/dist/templates/src/utils/format.ts +0 -21
  149. package/dist/templates/src/utils/getMicroApp.ts +0 -39
  150. package/dist/templates/src/utils/sentry.ts +0 -187
  151. package/dist/templates/src/utils/sentryDecorators.ts +0 -34
  152. package/dist/templates/src/utils/updateVersion.ts +0 -186
  153. package/dist/templates/updateVersion.ts +0 -186
  154. package/dist/templates/useRouteTitle.tsx +0 -36
  155. package/dist/templates/useSentry.ts +0 -92
  156. package/dist/templates/utils/env.ts +0 -3
  157. package/dist/templates/utils/format.ts +0 -21
  158. package/dist/templates/utils/getMicroApp.ts +0 -39
  159. package/dist/templates/utils/sentry.ts +0 -187
  160. package/dist/templates/utils/sentryDecorators.ts +0 -34
  161. package/dist/templates/utils/updateVersion.ts +0 -186
  162. /package/dist/templates/app/{config/public → public}/404.png +0 -0
  163. /package/dist/templates/app/{config/public → public}/favicon.ico +0 -0
  164. /package/dist/templates/app/{config/public → public}/images/banner_market_modal.webp +0 -0
  165. /package/dist/templates/app/{config/public → public}/images/chatmode_chat_dark.webp +0 -0
  166. /package/dist/templates/app/{config/public → public}/images/chatmode_chat_light.webp +0 -0
  167. /package/dist/templates/app/{config/public → public}/images/chatmode_docs_dark.webp +0 -0
  168. /package/dist/templates/app/{config/public → public}/images/chatmode_docs_light.webp +0 -0
  169. /package/dist/templates/app/{config/public → public}/images/empty_topic_dark.webp +0 -0
  170. /package/dist/templates/app/{config/public → public}/images/empty_topic_light.webp +0 -0
  171. /package/dist/templates/app/{config/public → public}/images/screenshot_background.webp +0 -0
  172. /package/dist/templates/app/{config/public → public}/images/theme_auto.webp +0 -0
  173. /package/dist/templates/app/{config/public → public}/images/theme_dark.webp +0 -0
  174. /package/dist/templates/app/{config/public → public}/images/theme_light.webp +0 -0
  175. /package/dist/templates/app/{config/public → public}/index.html +0 -0
@@ -1,17 +1,17 @@
1
- import { ConfigProvider } from 'antd';
2
- import type { Locale as AntdLocale } from 'antd/es/locale';
3
- import dayjs from 'dayjs';
1
+ import { ConfigProvider } from "antd";
2
+ import type { Locale as AntdLocale } from "antd/es/locale";
3
+ import dayjs from "dayjs";
4
4
  import React, {
5
5
  type PropsWithChildren,
6
6
  memo,
7
7
  useEffect,
8
8
  useState,
9
- } from 'react';
9
+ } from "react";
10
10
 
11
- import { isRtlLang } from 'rtl-detect';
11
+ import { isRtlLang } from "rtl-detect";
12
12
 
13
- import { createI18nNext } from '@/locales/create';
14
- import { getAntdLocale } from '@/utils/locale';
13
+ import { createI18nNext } from "@/locales/create";
14
+ import { getAntdLocale } from "@/utils/locale";
15
15
 
16
16
  const updateDayjs = async (lang: string) => {
17
17
  // load default lang
@@ -19,12 +19,12 @@ const updateDayjs = async (lang: string) => {
19
19
  try {
20
20
  // dayjs locale is using `en` instead of `en-US`
21
21
  // refs: https://github.com/lobehub/lobe-chat/issues/3396
22
- const locale = lang.toLowerCase() === 'en-us' ? 'en' : lang.toLowerCase();
22
+ const locale = lang.toLowerCase() === "en-us" ? "en" : lang.toLowerCase();
23
23
 
24
24
  dayJSLocale = await import(`dayjs/locale/${locale}.js`);
25
25
  } catch {
26
26
  console.warn(`dayjs locale for ${lang} not found, fallback to en`);
27
- dayJSLocale = await import('dayjs/locale/en.js');
27
+ dayJSLocale = await import("dayjs/locale/en.js");
28
28
  }
29
29
 
30
30
  dayjs.locale(dayJSLocale.default);
@@ -41,8 +41,6 @@ const Locale = memo<LocaleLayoutProps>(
41
41
  const [lang, setLang] = useState(defaultLang);
42
42
  const [locale, setLocale] = useState(antdLocale);
43
43
 
44
- console.log(antdLocale, defaultLang);
45
-
46
44
  // if on browser side, init i18n instance only once
47
45
  if (!i18n.instance.isInitialized)
48
46
  // console.debug('locale', lang);
@@ -65,25 +63,23 @@ const Locale = memo<LocaleLayoutProps>(
65
63
  await updateDayjs(lng);
66
64
  };
67
65
 
68
- i18n.instance.on('languageChanged', handleLang);
66
+ i18n.instance.on("languageChanged", handleLang);
69
67
  return () => {
70
- i18n.instance.off('languageChanged', handleLang);
68
+ i18n.instance.off("languageChanged", handleLang);
71
69
  };
72
70
  }, [i18n, lang]);
73
71
 
74
72
  // detect document direction
75
- const documentDir = isRtlLang(lang) ? 'rtl' : 'ltr';
76
-
77
- console.log(documentDir, locale);
73
+ const documentDir = isRtlLang(lang) ? "rtl" : "ltr";
78
74
 
79
75
  return (
80
76
  <ConfigProvider direction={documentDir} locale={locale as AntdLocale}>
81
77
  {children}
82
78
  </ConfigProvider>
83
79
  );
84
- },
80
+ }
85
81
  );
86
82
 
87
- Locale.displayName = 'Locale';
83
+ Locale.displayName = "Locale";
88
84
 
89
85
  export default Locale;
@@ -1,19 +1,15 @@
1
- import { ConfigProvider, theme } from 'antd';
2
- import type { Locale as AntdLocale } from 'antd/es/locale';
3
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState, useEffect } from "react";
4
2
 
5
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
6
- import { Outlet } from 'react-router';
7
- import '../index.css';
8
- import { DEFAULT_LANG } from '@/const/locale';
9
- import { useRouteTitle } from '@/hooks/useRouteTitle';
10
- import { getAntdLocale } from '@/utils/locale';
11
- import queryString from 'query-string';
12
- import Locale from './Locale';
13
- import AppTheme from './ThemeContext';
3
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
+ import { Outlet } from "react-router";
5
+ import "../index.css";
6
+ import { DEFAULT_LANG } from "@/const/locale";
7
+ import { getAntdLocale } from "@/utils";
8
+ import queryString from "query-string";
9
+ import Locale from "./Locale";
10
+ import AppTheme from "./ThemeContext";
14
11
 
15
12
  export const MainContentWrap = () => {
16
- useRouteTitle();
17
13
  const [antdLocale, setAntdLocale] = useState<unknown>(null);
18
14
  const [isLocaleLoaded, setIsLocaleLoaded] = useState(false);
19
15
 
@@ -32,11 +28,11 @@ export const MainContentWrap = () => {
32
28
  const loadLocale = async () => {
33
29
  try {
34
30
  const loadedLocale = await getAntdLocale(
35
- (lang as string) ?? DEFAULT_LANG,
31
+ (lang as string) ?? DEFAULT_LANG
36
32
  );
37
33
  setAntdLocale(loadedLocale);
38
34
  } catch (error) {
39
- console.error('Failed to load locale:', error);
35
+ console.error("Failed to load locale:", error);
40
36
  // 保持默认的中文 locale
41
37
  } finally {
42
38
  setIsLocaleLoaded(true);
@@ -1,14 +1,16 @@
1
- import ThemeProvider from '@/components/ThemeProvider';
2
- import { CLOUD_THEME_APPEARANCE } from '@/const/theme';
3
- import { setCookie } from '@/utils/cookie';
1
+ import ThemeProvider from "@/components/ThemeProvider";
2
+ import { CLOUD_THEME_APPEARANCE } from "@/const/theme";
3
+ import { setCookie } from "@/utils/cookie";
4
4
 
5
5
  // import { useGlobalStore } from "@store/global";
6
- import React, { memo, useEffect, type ReactNode } from 'react';
6
+ import React, { memo, useEffect, type ReactNode } from "react";
7
+
8
+ import { SYSTEM_PREFIX } from "@/const/system";
9
+ import { GlobalStyle } from "@/styles";
10
+ import { useUserStore } from "@store/user";
11
+ import { createStyles } from "antd-style";
12
+ import HarmonyOS_Sans_Regular from "@public/fonts/HarmonyOS_Sans_Regular.woff2";
7
13
 
8
- import { SYSTEM_PREFIX } from '@/const/system';
9
- import { GlobalStyle } from '@/styles';
10
- import { useUserStore } from '@store/user';
11
- import { createStyles } from 'antd-style';
12
14
  const useStyles = createStyles(({ css, token }) => ({
13
15
  app: css`
14
16
  position: relative;
@@ -70,55 +72,56 @@ interface AppThemeProps {
70
72
  }
71
73
 
72
74
  const AppTheme = memo(({ children }: AppThemeProps) => {
73
- const themeMode = useUserStore(s => s.themeMode);
74
- const animationMode = useUserStore(s => s.animationMode);
75
- const neutralColor = useUserStore(s => s.neutralColor);
76
- const primaryColor = useUserStore(s => s.primaryColor);
75
+ const themeMode = useUserStore((s) => s.themeMode);
76
+ const animationMode = useUserStore((s) => s.animationMode);
77
+ const neutralColor = useUserStore((s) => s.neutralColor);
78
+ const primaryColor = useUserStore((s) => s.primaryColor);
77
79
  const { styles, cx } = useStyles();
78
80
 
79
81
  useEffect(() => {
80
82
  // Update data-theme accordingly if user selects light or dark
81
- if (themeMode !== 'auto') {
83
+ if (themeMode !== "auto") {
82
84
  document.documentElement.dataset.theme = themeMode;
83
85
  return;
84
86
  }
85
87
 
86
88
  // For auto mode, we need to watch system preferences
87
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
89
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
88
90
 
89
91
  // Set initial theme based on system preference
90
92
  document.documentElement.dataset.theme = mediaQuery.matches
91
- ? 'dark'
92
- : 'light';
93
+ ? "dark"
94
+ : "light";
93
95
 
94
96
  // Update theme when system preference changes
95
97
  function handleChange(e) {
96
- document.documentElement.dataset.theme = e.matches ? 'dark' : 'light';
98
+ document.documentElement.dataset.theme = e.matches ? "dark" : "light";
97
99
  }
98
100
 
99
- mediaQuery.addEventListener('change', handleChange);
100
- return () => mediaQuery.removeEventListener('change', handleChange);
101
+ mediaQuery.addEventListener("change", handleChange);
102
+ return () => mediaQuery.removeEventListener("change", handleChange);
101
103
  }, [themeMode]);
102
104
 
103
105
  return (
104
106
  <ThemeProvider
105
107
  prefixCls={SYSTEM_PREFIX}
106
- appearance={themeMode !== 'auto' ? themeMode : undefined}
108
+ appearance={themeMode !== "auto" ? themeMode : undefined}
107
109
  className={cx(styles.app, styles.scrollbar, styles.scrollbarPolyfill)}
108
110
  customTheme={{
109
111
  neutralColor: neutralColor,
110
112
  primaryColor: primaryColor,
111
113
  }}
114
+ customFonts={[HarmonyOS_Sans_Regular]}
112
115
  theme={{
113
116
  cssVar: true,
114
117
  token: {
115
- motion: animationMode !== 'disabled',
116
- motionUnit: animationMode === 'agile' ? 0.05 : 0.1,
118
+ motion: animationMode !== "disabled",
119
+ motionUnit: animationMode === "agile" ? 0.05 : 0.1,
117
120
  },
118
121
  }}
119
122
  themeMode={themeMode}
120
- onAppearanceChange={appearance => {
121
- if (themeMode !== 'auto') return;
123
+ onAppearanceChange={(appearance) => {
124
+ if (themeMode !== "auto") return;
122
125
 
123
126
  setCookie(CLOUD_THEME_APPEARANCE, appearance);
124
127
  }}
@@ -1,5 +1,5 @@
1
1
  API_PREFIX="/api"
2
- API_TIMEOUT=60 * 1000
2
+ API_TIMEOUT=60000
3
3
 
4
4
  COOKIE_NAME='ywkf_jwt'
5
5
 
@@ -9,23 +9,6 @@ OUTPUT_PATH='dist'
9
9
  BASENAME='/'
10
10
 
11
11
  PUBLIC_PATH='/s{{{ name }}}/'
12
- VERSION_NOTIFY_TIME=30000
13
-
14
-
15
- # Sentry 配置 - (官网)
16
- # REACT_APP_SENTRY_DSN="https://95add4dc797c22aa2224494e4abd4d71@o4509466151878656.ingest.us.sentry.io/4509466153058304"
17
- # SENTRY_PROJECT="javascript-react"
18
- # SENTRY_ORG="test-x6m"
19
- # SENTRY_AUTH_TOKEN="d477b94cfe375f5b4e4e88ac4fc15a8be8557652cea424710b6f7f19be4f0b3d"
20
- # SENTRY_URL="https://test-x6m.sentry.io/"
21
12
 
22
13
  # 帮助中心
23
- HELP_CENTER_URL="https://im-support.gz4399.com/"
24
-
25
- # 是否开启Sentry
26
- SENTRY_ENABLED='{{SENTRY_ENABLED}}'
27
- REACT_APP_SENTRY_DSN='{{SENTRY_DSN}}'
28
- SENTRY_PROJECT='{{{ name }}}'
29
- SENTRY_ORG="sentry"
30
- SENTRY_AUTH_TOKEN="f13c66cd7a3629c8324cceb62fdb65aae1f337abbec4ebbadbe0a22447708994"
31
- SENTRY_URL="https://sentry.gz4399.com/"
14
+ HELP_CENTER_URL="https://im-support.gz4399.com/"
@@ -20,12 +20,12 @@ export default class jwt {
20
20
  }
21
21
 
22
22
  // 获取访问令牌
23
- static getAccessToken(accessTokenKey: string) {
24
- return Cookies.get(accessTokenKey);
23
+ static getAccessToken(accessTokenKey?: string) {
24
+ return Cookies.get(accessTokenKey ?? this.key);
25
25
  }
26
26
 
27
27
  // 清空访问令牌
28
- static clearAccessToken(accessTokenKey: string) {
29
- Cookies.remove(accessTokenKey);
28
+ static clearAccessToken(accessTokenKey?: string) {
29
+ Cookies.remove(accessTokenKey ?? this.key);
30
30
  }
31
31
  }
@@ -0,0 +1,67 @@
1
+ import { message as antdMessage } from "antd";
2
+ import type { AxiosError } from "axios";
3
+ import type { RequestConfig, ApiResponse } from "./types";
4
+
5
+ /**
6
+ * 错误处理器
7
+ */
8
+ export class ErrorHandler {
9
+ /**
10
+ * 处理请求错误
11
+ */
12
+ handle(error: any, config?: RequestConfig): void {
13
+ const messageConfig = config?.message;
14
+
15
+ // 如果明确禁用错误提示
16
+ if (messageConfig?.showError === false) {
17
+ return;
18
+ }
19
+
20
+ const axiosError = error as AxiosError<ApiResponse>;
21
+ let errorMessage = messageConfig?.errorMessage;
22
+
23
+ // 如果没有自定义错误消息,使用默认逻辑
24
+ if (!errorMessage) {
25
+ if (axiosError.response) {
26
+ const { status, data } = axiosError.response;
27
+
28
+ // 优先使用后端返回的消息
29
+ errorMessage = data?.message;
30
+
31
+ // 如果后端没有返回消息,使用默认消息
32
+ if (!errorMessage) {
33
+ errorMessage = this.getDefaultErrorMessage(status);
34
+ }
35
+ } else if (axiosError.request) {
36
+ errorMessage = "网络错误,请检查您的网络连接";
37
+ } else {
38
+ errorMessage = axiosError.message || "请求失败";
39
+ }
40
+ }
41
+
42
+ // 显示错误提示
43
+ antdMessage.error(errorMessage);
44
+ }
45
+
46
+ /**
47
+ * 获取默认错误消息
48
+ */
49
+ private getDefaultErrorMessage(status: number): string {
50
+ const errorMessages: Record<number, string> = {
51
+ 400: "请求参数错误",
52
+ 401: "未授权,请重新登录",
53
+ 403: "拒绝访问",
54
+ 404: "请求的资源不存在",
55
+ 405: "请求方法不允许",
56
+ 408: "请求超时",
57
+ 500: "服务器内部错误",
58
+ 502: "网关错误",
59
+ 503: "服务暂时不可用",
60
+ 504: "网关超时",
61
+ };
62
+
63
+ return errorMessages[status] || `请求失败 (${status})`;
64
+ }
65
+ }
66
+
67
+ export const errorHandler = new ErrorHandler();
@@ -1,174 +1,172 @@
1
- import jwt from "@config/jwt";
2
- import axios, {
3
- type AxiosInstance,
4
- type AxiosRequestConfig,
5
- type AxiosResponse,
6
- } from "axios";
1
+ import axios, { type AxiosInstance } from "axios";
7
2
  import qs from "qs";
8
-
9
- type Result<T> = {
10
- code: number;
11
- message: string;
12
- data: T;
13
- suppressErrorNotification?: boolean;
14
- };
15
-
16
- const baseURL = process.env.API_PREFIX;
17
- const timeout = process.env.API_TIMEOUT;
18
- const accessTokenKey = process.env.COOKIE_NAME;
19
-
20
- let flag = false; // 设置开关,保证一次只能请求一次刷新token,防止客户端多次操作,多次请求
21
- let subSequest: any[] = [];
22
-
23
- // 把过期请求添加在数组中
24
- function addRequest(request: () => any) {
25
- subSequest.push(request);
26
- }
27
-
28
- // 重新调用过期请求列表
29
- function retryRequest() {
30
- subSequest.forEach((request) => request());
31
- subSequest = [];
32
- }
33
-
34
- function getRefreshToken(token: string) {
35
- return fetch(`${baseURL}/dns/auth/login/token/refresh`, {
36
- headers: {
37
- authorization: token,
38
- },
39
- });
40
- }
41
-
42
- function redirectToLogin() {
43
- // 记录当前的url,并且实现跳转
44
- if (window.location.pathname === "/login") {
45
- return;
46
- }
47
- jwt.clearAccessToken(accessTokenKey as string);
48
- jwt.clearAccessToken("refresh_token");
49
- const href = encodeURIComponent(window.location.href);
50
- window.location.href = `${window.location.origin}/login?redirect=${href}`;
51
- }
52
-
53
- // 刷新token
54
- function refreshToken() {
55
- if (!flag) {
56
- flag = true;
57
- // 获取刷新token
58
- const r_tk = jwt.getAccessToken("refresh_token");
59
- if (r_tk) {
60
- // 判断刷新token是否过期
61
- getRefreshToken(r_tk)
62
- .then((v) => v.json())
63
- .then((v) => {
64
- flag = false;
65
- if (!v?.data?.accessToken) {
66
- redirectToLogin();
67
- return;
68
- }
69
- jwt.setAccessToken({
70
- token_type: v.data?.tokenType ?? "JWT",
71
- access_token: v.data?.accessToken,
72
- expires_at: v.data?.expires_at * 1000,
73
- });
74
- // 重新发送请求
75
- retryRequest();
76
- })
77
- .catch(() => {
78
- redirectToLogin();
79
- });
80
- } else {
81
- redirectToLogin();
3
+ import { env } from "@/config/env";
4
+ import type { RequestConfig, ApiResponse, TokenManager } from "./types";
5
+ import { tokenManager as defaultTokenManager } from "./token-manager";
6
+ import {
7
+ errorHandler as defaultErrorHandler,
8
+ type ErrorHandler,
9
+ } from "./error-handler";
10
+ import {
11
+ setupRequestInterceptor,
12
+ setupResponseInterceptor,
13
+ } from "./interceptors";
14
+
15
+ /**
16
+ * 参数序列化器
17
+ */
18
+ export function paramsSerializer(params: Record<string, any>): string {
19
+ const result: Record<string, any> = {};
20
+
21
+ Object.keys(params).forEach((key) => {
22
+ const value = params[key];
23
+ if (value === null || value === undefined) {
24
+ // 跳过 null 和 undefined
25
+ return;
82
26
  }
83
- }
84
- }
85
-
86
- export function paramsSerializer(params) {
87
- const result = {};
88
- Object.keys(params).forEach((p) => {
89
- const value = params[p];
90
27
  if (Array.isArray(value)) {
91
- result[p] = value;
92
- } else if (value === null || value === undefined) {
93
- result[p] = value;
28
+ result[key] = value;
94
29
  } else if (typeof value === "object") {
95
- result[p] = JSON.stringify(value);
30
+ result[key] = JSON.stringify(value);
96
31
  } else {
97
- result[p] = value;
32
+ result[key] = value;
98
33
  }
99
34
  });
35
+
100
36
  return qs.stringify(result, { arrayFormat: "repeat", skipNulls: true });
101
37
  }
102
38
 
103
- // 导出Request类,可以用来自定义传递配置来创建实例
39
+ /**
40
+ * Request 配置选项
41
+ */
42
+ export interface RequestOptions {
43
+ baseURL?: string;
44
+ timeout?: number;
45
+ tokenManager?: TokenManager;
46
+ errorHandler?: ErrorHandler;
47
+ onUnauthorized?: () => void;
48
+ }
49
+
50
+ /**
51
+ * HTTP 请求类
52
+ */
104
53
  export class Request {
105
- // axios 实例
106
54
  private instance: AxiosInstance;
107
- // 基础配置,url和超时时间
108
- private baseConfig: AxiosRequestConfig = {
109
- baseURL: baseURL,
110
- timeout: Number(timeout),
111
- withCredentials: true,
112
- };
113
-
114
- constructor(config: AxiosRequestConfig) {
115
- // 使用axios.create创建axios实例
116
- this.instance = axios.create(Object.assign(this.baseConfig, config));
117
-
118
- this.instance.interceptors.request.use(
119
- (config: any) => {
120
- // 一般会请求拦截里面加token,用于后端的验证
121
- config.headers = config.headers || {};
122
- config.headers.Authorization =
123
- jwt.getAccessToken(accessTokenKey as string) || "";
124
- return config;
125
- },
126
- (err: any) => {
127
- return Promise.reject(err);
128
- }
55
+ public tokenManager: TokenManager;
56
+ public errorHandler: ErrorHandler;
57
+
58
+ constructor(options: RequestOptions = {}) {
59
+ const {
60
+ baseURL = env.API_PREFIX,
61
+ timeout = env.API_TIMEOUT,
62
+ tokenManager = defaultTokenManager,
63
+ errorHandler = defaultErrorHandler,
64
+ onUnauthorized,
65
+ } = options;
66
+
67
+ this.tokenManager = tokenManager;
68
+ this.errorHandler = errorHandler;
69
+
70
+ // 创建 axios 实例
71
+ this.instance = axios.create({
72
+ baseURL,
73
+ timeout,
74
+ withCredentials: true,
75
+ });
76
+
77
+ // 设置拦截器
78
+ setupRequestInterceptor(this.instance, tokenManager);
79
+ setupResponseInterceptor(
80
+ this.instance,
81
+ tokenManager,
82
+ errorHandler,
83
+ onUnauthorized
129
84
  );
130
85
  }
131
86
 
132
- // 定义请求方法
133
- public request(config: AxiosRequestConfig): Promise<AxiosResponse> {
87
+ /**
88
+ * 通用请求方法
89
+ */
90
+ public request<T = any>(config: RequestConfig): Promise<ApiResponse<T>> {
134
91
  return this.instance.request(config);
135
92
  }
136
93
 
94
+ /**
95
+ * GET 请求
96
+ */
137
97
  public get<T = any>(
138
98
  url: string,
139
99
  params?: any,
140
- config?: AxiosRequestConfig & { suppressErrorNotification?: boolean }
141
- ): Promise<Result<T>> {
100
+ config?: RequestConfig
101
+ ): Promise<ApiResponse<T>> {
142
102
  return this.instance.get(url, {
143
103
  ...config,
144
- params: params,
104
+ params,
145
105
  paramsSerializer,
146
106
  });
147
107
  }
148
108
 
109
+ /**
110
+ * POST 请求
111
+ */
149
112
  public post<T = any>(
150
113
  url: string,
151
114
  data?: any,
152
- config?: AxiosRequestConfig & { suppressErrorNotification?: boolean }
153
- ): Promise<Result<T>> {
115
+ config?: RequestConfig
116
+ ): Promise<ApiResponse<T>> {
154
117
  return this.instance.post(url, data, config);
155
118
  }
156
119
 
120
+ /**
121
+ * PUT 请求
122
+ */
157
123
  public put<T = any>(
158
124
  url: string,
159
125
  data?: any,
160
- config?: AxiosRequestConfig & { suppressErrorNotification?: boolean }
161
- ): Promise<Result<T>> {
126
+ config?: RequestConfig
127
+ ): Promise<ApiResponse<T>> {
162
128
  return this.instance.put(url, data, config);
163
129
  }
164
130
 
131
+ /**
132
+ * DELETE 请求
133
+ */
165
134
  public delete<T = any>(
166
135
  url: string,
167
- config?: AxiosRequestConfig & { suppressErrorNotification?: boolean }
168
- ): Promise<Result<T>> {
136
+ config?: RequestConfig
137
+ ): Promise<ApiResponse<T>> {
169
138
  return this.instance.delete(url, config);
170
139
  }
140
+
141
+ /**
142
+ * PATCH 请求
143
+ */
144
+ public patch<T = any>(
145
+ url: string,
146
+ data?: any,
147
+ config?: RequestConfig
148
+ ): Promise<ApiResponse<T>> {
149
+ return this.instance.patch(url, data, config);
150
+ }
151
+
152
+ /**
153
+ * 获取 axios 实例(用于高级用法)
154
+ */
155
+ public getAxiosInstance(): AxiosInstance {
156
+ return this.instance;
157
+ }
171
158
  }
172
159
 
173
- // 默认导出Request实例
174
- export default new Request({});
160
+ // 导出默认实例
161
+ export const request = new Request();
162
+
163
+ // 导出类型
164
+ export type {
165
+ RequestConfig,
166
+ ApiResponse,
167
+ MessageConfig,
168
+ TokenManager,
169
+ } from "./types";
170
+
171
+ // 默认导出
172
+ export default request;