@qlover/create-app 0.3.2 → 0.3.3

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 (66) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/package.json +3 -3
  3. package/templates/react-app/config/Identifier.I18n.ts +878 -0
  4. package/templates/react-app/config/app.router.json +7 -7
  5. package/templates/react-app/config/theme.json +7 -88
  6. package/templates/react-app/package.json +7 -3
  7. package/templates/react-app/postcss.config.js +1 -2
  8. package/templates/react-app/public/locales/en/common.json +118 -1
  9. package/templates/react-app/public/locales/zh/common.json +118 -1
  10. package/templates/react-app/src/App.tsx +14 -2
  11. package/templates/react-app/src/base/cases/RequestLogger.ts +1 -1
  12. package/templates/react-app/src/base/services/I18nService.ts +31 -3
  13. package/templates/react-app/src/base/services/ProcesserService.ts +0 -1
  14. package/templates/react-app/src/core/IOC.ts +12 -7
  15. package/templates/react-app/src/core/bootstrap.ts +42 -53
  16. package/templates/react-app/src/core/bootstraps/PrintBootstrap.ts +14 -0
  17. package/templates/react-app/src/core/bootstraps/index.ts +36 -7
  18. package/templates/react-app/src/core/registers/RegisterApi.ts +2 -5
  19. package/templates/react-app/src/core/registers/RegisterCommon.ts +38 -29
  20. package/templates/react-app/src/core/registers/RegisterControllers.ts +5 -10
  21. package/templates/react-app/src/core/registers/RegisterGlobals.ts +13 -13
  22. package/templates/react-app/src/core/registers/index.ts +27 -12
  23. package/templates/react-app/src/main.tsx +1 -1
  24. package/templates/react-app/src/pages/404.tsx +1 -1
  25. package/templates/react-app/src/pages/500.tsx +1 -1
  26. package/templates/react-app/src/pages/auth/Login.tsx +128 -36
  27. package/templates/react-app/src/pages/base/About.tsx +5 -2
  28. package/templates/react-app/src/pages/base/ErrorIdentifier.tsx +38 -19
  29. package/templates/react-app/src/pages/base/Executor.tsx +447 -29
  30. package/templates/react-app/src/pages/base/Home.tsx +99 -93
  31. package/templates/react-app/src/pages/base/JSONStorage.tsx +47 -38
  32. package/templates/react-app/src/pages/base/Layout.tsx +5 -2
  33. package/templates/react-app/src/pages/base/Request.tsx +90 -208
  34. package/templates/react-app/src/pages/base/components/BaseHeader.tsx +13 -5
  35. package/templates/react-app/src/styles/css/page.css +11 -0
  36. package/templates/react-app/src/styles/css/tailwind.css +5 -0
  37. package/templates/react-app/src/styles/css/themes/_default.css +200 -0
  38. package/templates/react-app/src/styles/css/themes/dark.css +154 -0
  39. package/templates/react-app/src/styles/css/themes/index.css +3 -0
  40. package/templates/react-app/src/styles/css/themes/pink.css +160 -0
  41. package/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +56 -0
  42. package/templates/react-app/src/uikit/components/Loading.tsx +27 -21
  43. package/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +63 -13
  44. package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +1 -1
  45. package/templates/react-app/src/uikit/controllers/UserController.ts +1 -1
  46. package/templates/react-app/tailwind.config.js +1 -15
  47. package/templates/react-app/vite.config.ts +7 -1
  48. package/templates/react-app/lib/tailwind/root10px.js +0 -178
  49. package/templates/react-app/lib/tailwind/theme-generator.js +0 -238
  50. package/templates/react-app/public/locales/en/about.json +0 -3
  51. package/templates/react-app/public/locales/en/executor.json +0 -6
  52. package/templates/react-app/public/locales/en/home.json +0 -10
  53. package/templates/react-app/public/locales/en/jsonStorage.json +0 -11
  54. package/templates/react-app/public/locales/en/login.json +0 -7
  55. package/templates/react-app/public/locales/en/request.json +0 -15
  56. package/templates/react-app/public/locales/zh/about.json +0 -3
  57. package/templates/react-app/public/locales/zh/executor.json +0 -6
  58. package/templates/react-app/public/locales/zh/home.json +0 -10
  59. package/templates/react-app/public/locales/zh/jsonStorage.json +0 -11
  60. package/templates/react-app/public/locales/zh/login.json +0 -7
  61. package/templates/react-app/public/locales/zh/request.json +0 -15
  62. package/templates/react-app/src/base/port/InversifyIocInterface.ts +0 -9
  63. package/templates/react-app/src/uikit/styles/css/page.css +0 -3
  64. package/templates/react-app/src/uikit/styles/css/tailwind.css +0 -3
  65. /package/templates/react-app/config/{ErrorIdentifier.ts → Identifier.Error.ts} +0 -0
  66. /package/templates/react-app/src/{uikit/styles → styles}/css/index.css +0 -0
@@ -4,6 +4,9 @@ import { JSONStorageController } from '@/uikit/controllers/JSONStorageController
4
4
  import { RequestController } from '@/uikit/controllers/RequestController';
5
5
  import { useMemo } from 'react';
6
6
  import { useSliceStore } from '@qlover/slice-store-react';
7
+ import { Button } from 'antd';
8
+ import { LoadingOutlined } from '@ant-design/icons';
9
+ import * as i18nKeys from '@config/Identifier.I18n';
7
10
 
8
11
  function JSONValue({ value }: { value: unknown }) {
9
12
  const output = useMemo(() => {
@@ -14,7 +17,7 @@ function JSONValue({ value }: { value: unknown }) {
14
17
  }
15
18
  }, [value]);
16
19
  return (
17
- <pre className="mt-1 text-sm text-gray-600 font-mono bg-gray-50 p-2 rounded overflow-x-auto">
20
+ <pre className="mt-1 text-sm text-text-secondary font-mono bg-secondary p-2 rounded overflow-x-auto">
18
21
  {output}
19
22
  </pre>
20
23
  );
@@ -27,72 +30,48 @@ export default function Request() {
27
30
  const { t } = useBaseRoutePage();
28
31
 
29
32
  return (
30
- <div className="min-h-screen bg-gray-50 py-8 px-4 sm:px-6 lg:px-8">
33
+ <div className="min-h-screen bg-primary py-8 px-4 sm:px-6 lg:px-8">
31
34
  <div className="max-w-4xl mx-auto space-y-6">
32
35
  {/* Request Timeout Information */}
33
- <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
34
- <h2 className="text-lg font-medium text-gray-900 mb-2">
35
- {t('requestTimeout')}
36
+ <div className="bg-secondary shadow sm:rounded-lg p-6 border border-primary">
37
+ <h2 className="text-lg font-medium text-text mb-2">
38
+ {t(i18nKeys.PAGE_REQUEST_TIMEOUT)}
36
39
  </h2>
37
- <div className="text-sm text-gray-600 font-mono bg-gray-50 p-2 rounded">
40
+ <div className="text-sm text-text-secondary font-mono bg-base p-2 rounded">
38
41
  {jsonStorageControllerState.requestTimeout}
39
42
  </div>
40
43
  </div>
41
44
 
42
45
  {/* Hello Request Card */}
43
- <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
44
- <h2 className="text-lg font-medium text-gray-900 mb-4">
45
- AI API: Hello
46
+ <div className="bg-secondary shadow sm:rounded-lg p-6 border border-primary hover:bg-elevated transition-colors duration-200">
47
+ <h2 className="text-lg font-medium text-text mb-4">
48
+ {t(i18nKeys.PAGE_REQUEST_HELLO_TITLE)}
46
49
  </h2>
47
50
 
48
- <p className="text-sm text-gray-600 mb-4">
49
- 函数式 api, 使用了 FetchURLPlugin, RequestCommonPlugin,
50
- ApiMockPlugin, RequestLogger 插件
51
+ <p className="text-sm text-text-secondary mb-4">
52
+ {t(i18nKeys.PAGE_REQUEST_HELLO_DESCRIPTION)}
51
53
  </p>
52
- <button
53
- className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors"
54
+ <Button
55
+ type="primary"
54
56
  onClick={requestController.onHello}
57
+ loading={requestControllerState.helloState.loading}
55
58
  >
56
- {requestControllerState.helloState.loading ? (
57
- <>
58
- <svg
59
- className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
60
- xmlns="http://www.w3.org/2000/svg"
61
- fill="none"
62
- viewBox="0 0 24 24"
63
- >
64
- <circle
65
- className="opacity-25"
66
- cx="12"
67
- cy="12"
68
- r="10"
69
- stroke="currentColor"
70
- strokeWidth="4"
71
- ></circle>
72
- <path
73
- className="opacity-75"
74
- fill="currentColor"
75
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
76
- ></path>
77
- </svg>
78
- {t('loading')}
79
- </>
80
- ) : (
81
- 'Hello'
82
- )}
83
- </button>
59
+ {requestControllerState.helloState.loading
60
+ ? t(i18nKeys.REQUEST_LOADING)
61
+ : t(i18nKeys.PAGE_REQUEST_HELLO_BUTTON)}
62
+ </Button>
84
63
 
85
64
  <div className="mt-4 space-y-2">
86
65
  <div>
87
- <p className="text-sm font-medium text-gray-700">
88
- {t('helloResult')}:
66
+ <p className="text-sm font-medium text-text">
67
+ {t(i18nKeys.REQUEST_HELLO_RESULT)}:
89
68
  </p>
90
69
  <JSONValue value={requestControllerState.helloState.result} />
91
70
  </div>
92
71
 
93
72
  <div>
94
- <p className="text-sm font-medium text-gray-700">
95
- {t('helloError')}:
73
+ <p className="text-sm font-medium text-text">
74
+ {t(i18nKeys.REQUEST_HELLO_ERROR)}:
96
75
  </p>
97
76
  <JSONValue value={requestControllerState.helloState.error} />
98
77
  </div>
@@ -100,105 +79,54 @@ export default function Request() {
100
79
  </div>
101
80
 
102
81
  {/* IP Information Card */}
103
- <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
104
- <h2 className="text-lg font-medium text-gray-900 mb-4">
105
- FeApi: IP Information
82
+ <div className="bg-secondary shadow sm:rounded-lg p-6 border border-primary hover:bg-elevated transition-colors duration-200">
83
+ <h2 className="text-lg font-medium text-text mb-4">
84
+ {t(i18nKeys.PAGE_REQUEST_IP_INFO_TITLE)}
106
85
  </h2>
107
86
 
108
- <p className="text-sm text-gray-600 mb-4">
109
- RequestScheduler 类式 api, 使用了 FetchURLPlugin,
110
- RequestCommonPlugin, RequestLogger, ApiPickDataPlugin 插件, 其中
111
- ApiPickDataPlugin 插件可以将返回类型统一扁平到 data 字段
87
+ <p className="text-sm text-text-secondary mb-4">
88
+ {t(i18nKeys.PAGE_REQUEST_IP_INFO_DESCRIPTION)}
112
89
  </p>
113
- <button
114
- className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors"
90
+ <Button
91
+ type="primary"
115
92
  onClick={requestController.onIpInfo}
93
+ loading={requestControllerState.ipInfoState.loading}
116
94
  >
117
- {requestControllerState.ipInfoState.loading ? (
118
- <>
119
- <svg
120
- className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
121
- xmlns="http://www.w3.org/2000/svg"
122
- fill="none"
123
- viewBox="0 0 24 24"
124
- >
125
- <circle
126
- className="opacity-25"
127
- cx="12"
128
- cy="12"
129
- r="10"
130
- stroke="currentColor"
131
- strokeWidth="4"
132
- ></circle>
133
- <path
134
- className="opacity-75"
135
- fill="currentColor"
136
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
137
- ></path>
138
- </svg>
139
- {t('loading')}
140
- </>
141
- ) : (
142
- t('ipInfo')
143
- )}
144
- </button>
95
+ {requestControllerState.ipInfoState.loading
96
+ ? t(i18nKeys.REQUEST_LOADING)
97
+ : t(i18nKeys.REQUEST_IP_INFO)}
98
+ </Button>
145
99
 
146
100
  <div className="mt-4">
147
- <p className="text-sm font-medium text-gray-700">
148
- {t('ipInfoResult')}:
101
+ <p className="text-sm font-medium text-text">
102
+ {t(i18nKeys.REQUEST_IP_INFO_RESULT)}:
149
103
  </p>
150
104
  <JSONValue value={requestControllerState.ipInfoState.result} />
151
105
  </div>
152
106
  </div>
153
107
 
154
108
  {/* Random User Card */}
155
- <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
156
- <h2 className="text-lg font-medium text-gray-900 mb-4">
157
- UserApi:Random User
109
+ <div className="bg-secondary shadow sm:rounded-lg p-6 border border-primary hover:bg-elevated transition-colors duration-200">
110
+ <h2 className="text-lg font-medium text-text mb-4">
111
+ {t(i18nKeys.PAGE_REQUEST_RANDOM_USER_TITLE)}
158
112
  </h2>
159
- <p className="text-sm text-gray-600 mb-4">
160
- RequestTransaction 类式 api, 使用了 FetchURLPlugin,
161
- RequestCommonPlugin, ApiMockPlugin, FetchAbortPlugin,
162
- RequestLogger,ApiCatchPlugin 插件, 其中 FetchAbortPlugin 可以
163
- 中止请求, ApiCatchPlugin 可以将捕获的错误统一到 apiCatchResult 字段
113
+ <p className="text-sm text-text-secondary mb-4">
114
+ {t(i18nKeys.PAGE_REQUEST_RANDOM_USER_DESCRIPTION)}
164
115
  </p>
165
- <button
166
- className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors"
116
+ <Button
117
+ type="primary"
167
118
  onClick={requestController.onRandomUser}
119
+ loading={requestControllerState.randomUserState.loading}
168
120
  >
169
- {requestControllerState.randomUserState.loading ? (
170
- <>
171
- <svg
172
- className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
173
- xmlns="http://www.w3.org/2000/svg"
174
- fill="none"
175
- viewBox="0 0 24 24"
176
- >
177
- <circle
178
- className="opacity-25"
179
- cx="12"
180
- cy="12"
181
- r="10"
182
- stroke="currentColor"
183
- strokeWidth="4"
184
- ></circle>
185
- <path
186
- className="opacity-75"
187
- fill="currentColor"
188
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
189
- ></path>
190
- </svg>
191
- {t('loading')}
192
- </>
193
- ) : (
194
- t('randomUser')
195
- )}
196
- </button>
121
+ {requestControllerState.randomUserState.loading
122
+ ? t(i18nKeys.REQUEST_LOADING)
123
+ : t(i18nKeys.REQUEST_RANDOM_USER)}
124
+ </Button>
197
125
 
198
126
  <div className="mt-4 space-y-2">
199
127
  <div>
200
- <p className="text-sm font-medium text-gray-700">
201
- {t('randomUserResult')}:
128
+ <p className="text-sm font-medium text-text">
129
+ {t(i18nKeys.REQUEST_RANDOM_USER_RESULT)}:
202
130
  </p>
203
131
  <JSONValue
204
132
  value={requestControllerState.randomUserState.result}
@@ -206,8 +134,8 @@ export default function Request() {
206
134
  </div>
207
135
 
208
136
  <div>
209
- <p className="text-sm font-medium text-gray-700">
210
- {t('randomUserError')}:
137
+ <p className="text-sm font-medium text-text">
138
+ {t(i18nKeys.REQUEST_RANDOM_USER_ERROR)}:
211
139
  </p>
212
140
  <JSONValue value={requestControllerState.randomUserState.error} />
213
141
  </div>
@@ -215,51 +143,29 @@ export default function Request() {
215
143
  </div>
216
144
 
217
145
  {/* Api catch result */}
218
- <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
219
- <h2 className="text-lg font-medium text-gray-900 mb-4">
220
- UserApi: Api Catch Result
146
+ <div className="bg-secondary shadow sm:rounded-lg p-6 border border-primary hover:bg-elevated transition-colors duration-200">
147
+ <h2 className="text-lg font-medium text-text mb-4">
148
+ {t(i18nKeys.PAGE_REQUEST_API_CATCH_TITLE)}
221
149
  </h2>
222
- <button
223
- className={`inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors ${
150
+ <Button
151
+ type={
224
152
  requestControllerState.apiCatchResultState.loading
225
- ? 'bg-red-600 hover:bg-red-700'
226
- : 'bg-blue-600 hover:bg-blue-700'
227
- }`}
153
+ ? 'primary'
154
+ : 'primary'
155
+ }
156
+ danger={requestControllerState.apiCatchResultState.loading}
228
157
  onClick={requestController.onTriggerApiCatchResult}
158
+ loading={requestControllerState.apiCatchResultState.loading}
229
159
  >
230
- {requestControllerState.apiCatchResultState.loading ? (
231
- <>
232
- <svg
233
- className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
234
- xmlns="http://www.w3.org/2000/svg"
235
- fill="none"
236
- viewBox="0 0 24 24"
237
- >
238
- <circle
239
- className="opacity-25"
240
- cx="12"
241
- cy="12"
242
- r="10"
243
- stroke="currentColor"
244
- strokeWidth="4"
245
- ></circle>
246
- <path
247
- className="opacity-75"
248
- fill="currentColor"
249
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
250
- ></path>
251
- </svg>
252
- {t('stopApiCatchResult')}
253
- </>
254
- ) : (
255
- t('triggerApiCatchResult')
256
- )}
257
- </button>
160
+ {requestControllerState.apiCatchResultState.loading
161
+ ? t(i18nKeys.PAGE_REQUEST_STOP_API_CATCH)
162
+ : t(i18nKeys.PAGE_REQUEST_TRIGGER_API_CATCH)}
163
+ </Button>
258
164
 
259
165
  <div className="mt-4 space-y-2">
260
166
  <div>
261
- <p className="text-sm font-medium text-gray-700">
262
- {t('abortRequestResult')}:
167
+ <p className="text-sm font-medium text-text">
168
+ {t(i18nKeys.REQUEST_ABORT_RESULT)}:
263
169
  </p>
264
170
  <JSONValue
265
171
  value={requestControllerState.apiCatchResultState.result}
@@ -267,8 +173,8 @@ export default function Request() {
267
173
  </div>
268
174
 
269
175
  <div>
270
- <p className="text-sm font-medium text-gray-700">
271
- {t('abortRequestError')}:
176
+ <p className="text-sm font-medium text-text">
177
+ {t(i18nKeys.REQUEST_ABORT_ERROR)}:
272
178
  </p>
273
179
  <JSONValue
274
180
  value={requestControllerState.apiCatchResultState.error}
@@ -278,58 +184,34 @@ export default function Request() {
278
184
  </div>
279
185
 
280
186
  {/* Abort Request Card */}
281
- <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
282
- <h2 className="text-lg font-medium text-gray-900 mb-4">
283
- UserApi: Abort Request
187
+ <div className="bg-secondary shadow sm:rounded-lg p-6 border border-primary hover:bg-elevated transition-colors duration-200">
188
+ <h2 className="text-lg font-medium text-text mb-4">
189
+ {t(i18nKeys.PAGE_REQUEST_ABORT_TITLE)}
284
190
  </h2>
285
- <button
286
- className={`inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors ${
287
- requestControllerState.abortState.loading
288
- ? 'bg-red-600 hover:bg-red-700'
289
- : 'bg-blue-600 hover:bg-blue-700'
290
- }`}
191
+ <Button
192
+ type={
193
+ requestControllerState.abortState.loading ? 'primary' : 'primary'
194
+ }
195
+ danger={requestControllerState.abortState.loading}
291
196
  onClick={requestController.onTriggerAbortRequest}
292
197
  >
293
- {requestControllerState.abortState.loading ? (
294
- <>
295
- <svg
296
- className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
297
- xmlns="http://www.w3.org/2000/svg"
298
- fill="none"
299
- viewBox="0 0 24 24"
300
- >
301
- <circle
302
- className="opacity-25"
303
- cx="12"
304
- cy="12"
305
- r="10"
306
- stroke="currentColor"
307
- strokeWidth="4"
308
- ></circle>
309
- <path
310
- className="opacity-75"
311
- fill="currentColor"
312
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
313
- ></path>
314
- </svg>
315
- {t('stopAbortRequest')}
316
- </>
317
- ) : (
318
- t('triggerAbortRequest')
319
- )}
320
- </button>
198
+ {requestControllerState.abortState.loading && <LoadingOutlined />}
199
+ {requestControllerState.abortState.loading
200
+ ? t(i18nKeys.PAGE_REQUEST_STOP_ABORT)
201
+ : t(i18nKeys.PAGE_REQUEST_TRIGGER_ABORT)}
202
+ </Button>
321
203
 
322
204
  <div className="mt-4 space-y-2">
323
205
  <div>
324
- <p className="text-sm font-medium text-gray-700">
325
- {t('abortRequestResult')}:
206
+ <p className="text-sm font-medium text-text">
207
+ {t(i18nKeys.REQUEST_ABORT_RESULT)}:
326
208
  </p>
327
209
  <JSONValue value={requestControllerState.abortState.result} />
328
210
  </div>
329
211
 
330
212
  <div>
331
- <p className="text-sm font-medium text-gray-700">
332
- {t('abortRequestError')}:
213
+ <p className="text-sm font-medium text-text">
214
+ {t(i18nKeys.REQUEST_ABORT_ERROR)}:
333
215
  </p>
334
216
  <JSONValue value={requestControllerState.abortState.error} />
335
217
  </div>
@@ -1,16 +1,24 @@
1
1
  import ThemeSwitcher from '@/uikit/components/ThemeSwitcher';
2
2
  import LocaleLink from '@/uikit/components/LocaleLink';
3
+ import LanguageSwitcher from '@/uikit/components/LanguageSwitcher';
3
4
 
4
5
  export default function BaseHeader() {
5
6
  return (
6
- <header className="h-10 bg-white sticky top-0 z-50">
7
- <div className="flex items-center justify-between h-full px-2">
7
+ <header className="h-14 bg-secondary border-b border-border sticky top-0 z-50">
8
+ <div className="flex items-center justify-between h-full px-4 mx-auto max-w-7xl">
8
9
  <div className="flex items-center">
9
- <LocaleLink href="/">
10
- <img src="/logo.svg" alt="logo" />
10
+ <LocaleLink
11
+ href="/"
12
+ className="flex items-center hover:opacity-80 transition-opacity"
13
+ >
14
+ <img src="/logo.svg" alt="logo" className="h-8 w-auto" />
15
+ <span className="ml-2 text-lg font-semibold text-text">
16
+ {'appName'}
17
+ </span>
11
18
  </LocaleLink>
12
19
  </div>
13
- <div className="flex items-center">
20
+ <div className="flex items-center gap-4">
21
+ <LanguageSwitcher />
14
22
  <ThemeSwitcher />
15
23
  </div>
16
24
  </div>
@@ -0,0 +1,11 @@
1
+ @theme {
2
+ --color-primary: rgba(var(--color-bg-base));
3
+ --color-secondary: rgba(var(--color-bg-secondary));
4
+ --color-elevated: rgba(var(--color-bg-elevated));
5
+ --color-text: rgba(var(--color-text-primary));
6
+ --color-text-secondary: rgba(var(--color-text-secondary));
7
+ --color-text-tertiary: rgba(var(--color-text-tertiary));
8
+ --color-border: rgba(var(--color-border));
9
+ --color-brand: rgba(var(--color-brand));
10
+ --color-brand-hover: rgba(var(--color-brand-hover));
11
+ }
@@ -0,0 +1,5 @@
1
+ @layer theme, base, antd, components, utilities;
2
+
3
+ @import 'tailwindcss';
4
+
5
+ @import './themes/index.css';
@@ -0,0 +1,200 @@
1
+ /* theme variables - for tailwind */
2
+ @layer base {
3
+ :root {
4
+ /* 基础背景色 */
5
+ --color-bg-base: 255 255 255;
6
+ --color-bg-secondary: 241 245 249; /* slate-100 */
7
+ --color-bg-elevated: 248 250 252; /* slate-50 */
8
+
9
+ /* 文字颜色 */
10
+ --color-text-primary: 15 23 42; /* slate-900 */
11
+ --color-text-secondary: 71 85 105; /* slate-600 */
12
+ --color-text-tertiary: 100 116 139; /* slate-500 */
13
+
14
+ /* 边框颜色 */
15
+ --color-border: 226 232 240; /* slate-200 */
16
+
17
+ /* 品牌色 */
18
+ --color-brand: 37 99 235; /* blue-600 */
19
+ --color-brand-hover: 59 130 246; /* blue-500 */
20
+ }
21
+ }
22
+
23
+ /* custom variables - for antd and custom css */
24
+ html,
25
+ .fe-theme {
26
+ /* Antd 主色调相关变量 - 浅蓝色主题 */
27
+ --fe-color-primary: #60a5fa; /* blue-400 */
28
+ --fe-color-primary-hover: #3b82f6; /* blue-500 */
29
+ --fe-color-primary-active: #2563eb; /* blue-600 */
30
+ --fe-color-primary-bg: rgba(96, 165, 250, 0.1); /* blue-400 with 0.1 opacity */
31
+ --fe-color-primary-border: #60a5fa; /* blue-400 */
32
+ --fe-color-primary-text: #60a5fa; /* blue-400 */
33
+ --fe-color-primary-text-hover: #3b82f6; /* blue-500 */
34
+ --fe-color-primary-text-active: #2563eb; /* blue-600 */
35
+ --fe-color-primary-deprecated-bg: #60a5fa; /* blue-400 */
36
+ --fe-color-primary-deprecated-border: #60a5fa; /* blue-400 */
37
+
38
+ /* Antd 基础变量 */
39
+ --fe-color-bg-container: rgb(255 255 255);
40
+ --fe-color-bg-elevated: rgb(248 250 252);
41
+ --fe-color-text: rgba(15 23 42 / 0.85);
42
+ --fe-color-text-secondary: rgba(15 23 42 / 0.45);
43
+ --fe-color-text-tertiary: rgba(15 23 42 / 0.35);
44
+ --fe-color-text-quaternary: rgba(15 23 42 / 0.15);
45
+ --fe-color-text-placeholder: rgba(15 23 42 / 0.25);
46
+ --fe-color-border: rgb(226 232 240);
47
+
48
+ /* Antd 组件通用变量 */
49
+ --fe-line-width: 1px;
50
+ --fe-line-type: solid;
51
+ --fe-border-radius: 6px;
52
+ --fe-motion-duration-mid: 0.2s;
53
+ --fe-line-height: 1.5715;
54
+
55
+ /* Antd Input 组件变量 */
56
+ &.ant-input-css-var {
57
+ /* Input 内边距 */
58
+ --fe-input-padding-block: 4px;
59
+ --fe-input-padding-block-sm: 0px;
60
+ --fe-input-padding-block-lg: 7px;
61
+ --fe-input-padding-inline: 11px;
62
+ --fe-input-padding-inline-sm: 7px;
63
+ --fe-input-padding-inline-lg: 11px;
64
+
65
+ /* Input 字体大小 */
66
+ --fe-input-input-font-size: 14px;
67
+ --fe-input-input-font-size-lg: 16px;
68
+ --fe-input-input-font-size-sm: 14px;
69
+
70
+ /* Input 交互状态 */
71
+ --fe-input-hover-border-color: #4096ff;
72
+ --fe-input-active-border-color: #1677ff;
73
+ --fe-input-active-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1);
74
+ --fe-input-error-active-shadow: 0 0 0 2px rgba(255, 38, 5, 0.06);
75
+ --fe-input-warning-active-shadow: 0 0 0 2px rgba(255, 215, 5, 0.1);
76
+ --fe-input-hover-bg: #ffffff;
77
+ --fe-input-active-bg: #ffffff;
78
+ --fe-input-addon-bg: rgba(0, 0, 0, 0.02);
79
+ }
80
+
81
+ /* 登录页特定样式 */
82
+ --login-card-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
83
+ --login-input-bg: var(--fe-color-bg-container);
84
+ --login-input-border: var(--fe-color-border);
85
+ --login-button-text: rgb(255 255 255);
86
+ --login-social-button-bg: var(--fe-color-bg-container);
87
+ --login-social-button-border: var(--fe-color-border);
88
+
89
+ /* Antd Button 组件变量 */
90
+ &.ant-btn {
91
+ /* 基础样式 */
92
+ --fe-button-font-weight: 400;
93
+ --fe-button-padding-inline: 15px;
94
+ --fe-button-padding-inline-lg: 15px;
95
+ --fe-button-padding-inline-sm: 7px;
96
+ --fe-button-padding-block: 4px;
97
+ --fe-button-padding-block-sm: 0px;
98
+ --fe-button-padding-block-lg: 7px;
99
+
100
+ /* 字体大小 */
101
+ --fe-button-content-font-size: 14px;
102
+ --fe-button-content-font-size-sm: 14px;
103
+ --fe-button-content-font-size-lg: 16px;
104
+ --fe-button-content-line-height: 1.5714285714285714;
105
+ --fe-button-content-line-height-sm: 1.5714285714285714;
106
+ --fe-button-content-line-height-lg: 1.5;
107
+
108
+ /* 浅蓝色主题 */
109
+ --fe-button-primary-color: #fff;
110
+ --fe-button-primary-bg: #60a5fa; /* blue-400 */
111
+ --fe-button-primary-hover-bg: #3b82f6; /* blue-500 */
112
+ --fe-button-primary-active-bg: #2563eb; /* blue-600 */
113
+ --fe-button-primary-shadow: 0 2px 0 rgba(37, 99, 235, 0.1);
114
+
115
+ /* 默认按钮 */
116
+ --fe-button-default-color: rgba(0, 0, 0, 0.88);
117
+ --fe-button-default-bg: #ffffff;
118
+ --fe-button-default-border-color: #d9d9d9;
119
+ --fe-button-default-hover-bg: #ffffff;
120
+ --fe-button-default-hover-color: #60a5fa;
121
+ --fe-button-default-hover-border-color: #60a5fa;
122
+ --fe-button-default-active-bg: #ffffff;
123
+ --fe-button-default-active-color: #2563eb;
124
+ --fe-button-default-active-border-color: #2563eb;
125
+ --fe-button-default-shadow: 0 2px 0 rgba(0, 0, 0, 0.02);
126
+
127
+ /* 其他状态 */
128
+ --fe-button-danger-color: #fff;
129
+ --fe-button-danger-shadow: 0 2px 0 rgba(255, 38, 5, 0.06);
130
+ --fe-button-border-color-disabled: #d9d9d9;
131
+ --fe-button-text-hover-bg: rgba(0, 0, 0, 0.04);
132
+ }
133
+
134
+ /* Antd 图标相关变量 */
135
+ --fe-color-icon: rgba(0, 0, 0, 0.45); /* 默认图标颜色 */
136
+ --fe-color-icon-hover: rgba(0, 0, 0, 0.88); /* 图标悬停颜色 */
137
+ --fe-color-icon-active: var(--fe-color-primary); /* 图标激活颜色 */
138
+ --fe-color-icon-disabled: rgba(0, 0, 0, 0.25); /* 禁用状态图标颜色 */
139
+
140
+ /* Antd Select 组件变量 */
141
+ &.ant-select-css-var {
142
+ --fe-select-internal_fixed_item_margin: 2px;
143
+ --fe-select-z-index-popup: 1050;
144
+ --fe-select-option-selected-color: rgba(0, 0, 0, 0.88);
145
+ --fe-select-option-selected-font-weight: 600;
146
+ --fe-select-option-selected-bg: var(--fe-color-primary-bg);
147
+ --fe-select-option-active-bg: rgba(0, 0, 0, 0.04);
148
+ --fe-select-option-padding: 5px 12px;
149
+ --fe-select-option-font-size: 14px;
150
+ --fe-select-option-line-height: 1.5714285714285714;
151
+ --fe-select-option-height: 32px;
152
+ --fe-select-selector-bg: var(--fe-color-bg-container);
153
+ --fe-select-clear-bg: var(--fe-color-bg-container);
154
+ --fe-select-single-item-height-lg: 40px;
155
+ --fe-select-multiple-item-bg: rgba(0, 0, 0, 0.06);
156
+ --fe-select-multiple-item-border-color: transparent;
157
+ --fe-select-multiple-item-height: 24px;
158
+ --fe-select-multiple-item-height-sm: 16px;
159
+ --fe-select-multiple-item-height-lg: 32px;
160
+ --fe-select-multiple-selector-bg-disabled: rgba(0, 0, 0, 0.04);
161
+ --fe-select-multiple-item-color-disabled: rgba(0, 0, 0, 0.25);
162
+ --fe-select-multiple-item-border-color-disabled: transparent;
163
+ --fe-select-show-arrow-padding-inline-end: 18px;
164
+ --fe-select-hover-border-color: var(--fe-color-primary);
165
+ --fe-select-active-border-color: var(--fe-color-primary-active);
166
+ --fe-select-active-outline-color: var(--fe-color-primary-bg);
167
+ --fe-select-select-affix-padding: 4px;
168
+ }
169
+
170
+ /* Antd Tag 组件变量 */
171
+ &.ant-tag-css-var {
172
+ --fe-tag-default-bg: var(--fe-color-bg-container);
173
+ --fe-tag-default-color: var(--fe-color-text);
174
+ --fe-tag-default-border-color: var(--fe-color-border);
175
+ --fe-tag-font-size: 12px;
176
+ --fe-tag-line-height: 20px;
177
+ --fe-tag-height: 22px;
178
+ --fe-tag-padding-horizontal: 7px;
179
+ --fe-tag-margin: 0 8px 0 0;
180
+ --fe-tag-border-radius: 4px;
181
+ }
182
+
183
+ /* Antd Progress 组件变量 */
184
+ &.ant-progress-css-var {
185
+ --fe-progress-default-color: var(--fe-color-primary);
186
+ --fe-progress-remaining-color: var(--fe-color-bg-elevated);
187
+ --fe-progress-text-color: var(--fe-color-text);
188
+ --fe-progress-line-font-size: 14px;
189
+ --fe-progress-circle-font-size: 14px;
190
+ --fe-progress-circle-text-color: var(--fe-color-text);
191
+ --fe-progress-circle-trail-color: var(--fe-color-bg-elevated);
192
+ --fe-progress-circle-stroke-width: 6px;
193
+ --fe-progress-line-stroke-width: 8px;
194
+ }
195
+
196
+ /* Motion 动画变量 */
197
+ --fe-motion-duration-slow: 0.3s;
198
+ --fe-motion-duration-normal: 0.2s;
199
+ --fe-motion-duration-fast: 0.1s;
200
+ }