@qlover/create-app 0.7.7 → 0.7.9

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 (58) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/build/generateLocales.ts +1 -1
  5. package/dist/templates/next-app/config/IOCIdentifier.ts +15 -2
  6. package/dist/templates/next-app/config/Identifier/common.error.ts +7 -0
  7. package/dist/templates/next-app/config/Identifier/page.home.ts +7 -0
  8. package/dist/templates/next-app/config/i18n/HomeI18n .ts +22 -0
  9. package/dist/templates/next-app/config/theme.ts +1 -0
  10. package/dist/templates/next-app/package.json +5 -5
  11. package/dist/templates/next-app/public/locales/{en/common.json → en.json} +3 -1
  12. package/dist/templates/next-app/public/locales/{zh/common.json → zh.json} +3 -1
  13. package/dist/templates/next-app/src/app/[locale]/layout.tsx +8 -26
  14. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +9 -18
  15. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +28 -24
  16. package/dist/templates/next-app/src/app/[locale]/page.tsx +105 -100
  17. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +92 -0
  18. package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +16 -0
  19. package/dist/templates/next-app/src/base/cases/PageParams.ts +74 -0
  20. package/dist/templates/next-app/src/base/cases/RouterService.ts +35 -0
  21. package/dist/templates/next-app/src/base/cases/ServerAuth.ts +17 -0
  22. package/dist/templates/next-app/src/base/cases/ServerErrorHandler.ts +27 -0
  23. package/dist/templates/next-app/src/base/port/IOCInterface.ts +24 -0
  24. package/dist/templates/next-app/src/base/port/ParamsHandlerInterface.ts +11 -0
  25. package/dist/templates/next-app/src/base/port/RouterInterface.ts +11 -0
  26. package/dist/templates/next-app/src/base/port/ServerAuthInterface.ts +3 -0
  27. package/dist/templates/next-app/src/base/port/ServerInterface.ts +12 -0
  28. package/dist/templates/next-app/src/base/types/PageProps.ts +9 -0
  29. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +2 -39
  30. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +78 -0
  31. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +37 -0
  32. package/dist/templates/next-app/src/core/{IocRegisterImpl.ts → clientIoc/ClientIOCRegister.ts} +20 -23
  33. package/dist/templates/next-app/src/core/globals.ts +3 -0
  34. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +52 -0
  35. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +63 -0
  36. package/dist/templates/next-app/src/i18n/request.ts +1 -2
  37. package/dist/templates/next-app/src/i18n/routing.ts +3 -3
  38. package/dist/templates/next-app/src/middleware.ts +6 -3
  39. package/dist/templates/next-app/src/styles/css/antd-themes/_default.css +12 -0
  40. package/dist/templates/next-app/src/styles/css/antd-themes/dark.css +26 -0
  41. package/dist/templates/next-app/src/styles/css/antd-themes/pink.css +16 -0
  42. package/dist/templates/next-app/src/styles/css/page.css +4 -3
  43. package/dist/templates/next-app/src/styles/css/themes/_default.css +1 -0
  44. package/dist/templates/next-app/src/styles/css/themes/dark.css +1 -0
  45. package/dist/templates/next-app/src/styles/css/themes/pink.css +1 -0
  46. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +14 -14
  47. package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +27 -0
  48. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +13 -1
  49. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +5 -0
  50. package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +49 -21
  51. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +34 -0
  52. package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +92 -35
  53. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +9 -2
  54. package/package.json +1 -1
  55. package/dist/templates/next-app/plugins/eslint-plugin-testid.mjs +0 -94
  56. package/dist/templates/next-app/plugins/generateLocalesPlugin.ts +0 -33
  57. package/dist/templates/next-app/src/core/IOC.ts +0 -58
  58. package/dist/templates/next-app/src/server/getServerI18n.ts +0 -26
@@ -0,0 +1,78 @@
1
+ import {
2
+ type BootstrapContextValue,
3
+ type BootstrapExecutorPlugin,
4
+ type IOCContainerInterface,
5
+ type IOCFunctionInterface,
6
+ type LoggerInterface
7
+ } from '@qlover/corekit-bridge';
8
+ import { AsyncExecutor, type ExecutorPlugin } from '@qlover/fe-corekit';
9
+ import { I, type IOCIdentifierMapServer } from '@config/IOCIdentifier';
10
+ import type { ServerInterface } from '@/base/port/ServerInterface';
11
+ import { ServerIOC } from '../serverIoc/ServerIOC';
12
+
13
+ export interface BootstrapServerResult {
14
+ locale: string;
15
+ messages: Record<string, string>;
16
+ }
17
+
18
+ export interface BootstrapServerContextValue extends BootstrapContextValue {
19
+ locale: string;
20
+ messages: Record<string, string>;
21
+ }
22
+
23
+ export class BootstrapServer implements ServerInterface {
24
+ protected executor: AsyncExecutor;
25
+ protected root: Record<string, unknown> = {};
26
+ protected IOC: IOCFunctionInterface<
27
+ IOCIdentifierMapServer,
28
+ IOCContainerInterface
29
+ >;
30
+ readonly logger: LoggerInterface;
31
+
32
+ constructor() {
33
+ const serverIOC = ServerIOC.create();
34
+ const ioc = serverIOC.create();
35
+ const logger = ioc(I.Logger);
36
+
37
+ this.executor = new AsyncExecutor();
38
+ this.IOC = ioc;
39
+ this.logger = logger;
40
+ }
41
+
42
+ getIOC(): IOCFunctionInterface<IOCIdentifierMapServer, IOCContainerInterface>;
43
+ getIOC<T extends keyof IOCIdentifierMapServer>(
44
+ identifier: T
45
+ ): IOCIdentifierMapServer[T];
46
+ getIOC(
47
+ identifier?: keyof IOCIdentifierMapServer
48
+ ):
49
+ | IOCFunctionInterface<IOCIdentifierMapServer, IOCContainerInterface>
50
+ | IOCIdentifierMapServer[keyof IOCIdentifierMapServer] {
51
+ if (identifier === undefined) {
52
+ return this.IOC;
53
+ }
54
+ return this.IOC(identifier);
55
+ }
56
+
57
+ /**
58
+ * @override
59
+ */
60
+ use(
61
+ plugin:
62
+ | BootstrapExecutorPlugin
63
+ | BootstrapExecutorPlugin[]
64
+ | ((
65
+ ioc: IOCFunctionInterface<
66
+ IOCIdentifierMapServer,
67
+ IOCContainerInterface
68
+ >
69
+ ) => BootstrapExecutorPlugin)
70
+ ): this {
71
+ if (typeof plugin === 'function') {
72
+ plugin = plugin(this.IOC);
73
+ }
74
+
75
+ this.executor.use(plugin as ExecutorPlugin<unknown>);
76
+ return this;
77
+ }
78
+ }
@@ -0,0 +1,37 @@
1
+ import {
2
+ createIOCFunction,
3
+ type IOCContainerInterface,
4
+ type IOCFunctionInterface
5
+ } from '@qlover/corekit-bridge';
6
+ import { InversifyContainer } from '@/base/cases/InversifyContainer';
7
+ import type { IOCInterface } from '@/base/port/IOCInterface';
8
+ import { ClientIOCRegister } from './ClientIOCRegister';
9
+ import { appConfig } from '../globals';
10
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
11
+
12
+ export class ClientIOC
13
+ implements IOCInterface<IOCIdentifierMap, IOCContainerInterface>
14
+ {
15
+ protected ioc: IOCFunctionInterface<
16
+ IOCIdentifierMap,
17
+ IOCContainerInterface
18
+ > | null = null;
19
+
20
+ create(): IOCFunctionInterface<IOCIdentifierMap, IOCContainerInterface> {
21
+ if (this.ioc) {
22
+ return this.ioc;
23
+ }
24
+
25
+ this.ioc = createIOCFunction<IOCIdentifierMap>(new InversifyContainer());
26
+
27
+ const register = new ClientIOCRegister({
28
+ appConfig: appConfig
29
+ });
30
+
31
+ register.register(this.ioc.implemention!, this.ioc);
32
+
33
+ return this.ioc;
34
+ }
35
+ }
36
+
37
+ export const clientIOC = new ClientIOC();
@@ -1,10 +1,18 @@
1
1
  import { IOCIdentifier as I } from '@config/IOCIdentifier';
2
+ import { RouterService } from '@/base/cases/RouterService';
3
+ import type { IocRegisterOptions } from '@/base/port/IOCInterface';
2
4
  import { I18nService } from '@/base/services/I18nService';
3
- import { logger, JSON } from './globals';
4
- import type { IOCContainer, IOCRegister, IocRegisterOptions } from './IOC';
5
- import type { IOCManagerInterface } from '@qlover/corekit-bridge';
5
+ import { UserService } from '@/base/services/UserService';
6
+ import { dialogHandler, logger, JSON } from '../globals';
7
+ import type {
8
+ IOCContainerInterface,
9
+ IOCManagerInterface,
10
+ IOCRegisterInterface
11
+ } from '@qlover/corekit-bridge';
6
12
 
7
- export class IocRegisterImpl implements IOCRegister {
13
+ export class ClientIOCRegister
14
+ implements IOCRegisterInterface<IOCContainerInterface, IocRegisterOptions>
15
+ {
8
16
  constructor(protected options: IocRegisterOptions) {}
9
17
 
10
18
  /**
@@ -14,13 +22,13 @@ export class IocRegisterImpl implements IOCRegister {
14
22
  *
15
23
  * @param ioc - IOC container
16
24
  */
17
- protected registerGlobals(ioc: IOCContainer): void {
25
+ protected registerGlobals(ioc: IOCContainerInterface): void {
18
26
  const { appConfig } = this.options;
19
27
  ioc.bind(I.JSONSerializer, JSON);
20
28
  ioc.bind(I.Logger, logger);
21
29
  ioc.bind(I.AppConfig, appConfig);
22
30
  // ioc.bind(I.EnvConfigInterface, appConfig);
23
- // ioc.bind(I.DialogHandler, dialogHandler);
31
+ ioc.bind(I.DialogHandler, dialogHandler);
24
32
  // ioc.bind(I.UIDialogInterface, dialogHandler);
25
33
  // ioc.bind(I.AntdStaticApiInterface, dialogHandler);
26
34
  // ioc.bind(I.LocalStorage, globals.localStorage);
@@ -38,20 +46,9 @@ export class IocRegisterImpl implements IOCRegister {
38
46
  *
39
47
  * @param ioc
40
48
  */
41
- protected registerImplement(ioc: IOCContainer): void {
49
+ protected registerImplement(ioc: IOCContainerInterface): void {
42
50
  ioc.bind(I.I18nServiceInterface, new I18nService());
43
- // ioc.bind(
44
- // I.RouteServiceInterface,
45
- // new RouteService(
46
- // ioc.get(NavigateBridge),
47
- // ioc.get(I.I18nServiceInterface),
48
- // {
49
- // routes: useLocaleRoutes ? baseRoutes : baseNoLocaleRoutes,
50
- // logger: ioc.get(I.Logger),
51
- // hasLocalRoutes: useLocaleRoutes
52
- // }
53
- // )
54
- // );
51
+ ioc.bind(I.RouterServiceInterface, ioc.get(RouterService));
55
52
  // ioc.bind(
56
53
  // I.ThemeService,
57
54
  // new ThemeService({
@@ -61,14 +58,14 @@ export class IocRegisterImpl implements IOCRegister {
61
58
  // );
62
59
  // ioc.bind(I.I18nKeyErrorPlugin, ioc.get(I18nKeyErrorPlugin));
63
60
  // ioc.bind(I.ProcesserExecutorInterface, ioc.get(ProcesserExecutor));
64
- // ioc.bind(I.UserServiceInterface, ioc.get(UserService));
61
+ ioc.bind(I.UserServiceInterface, ioc.get(UserService));
65
62
  // ioc.bind(I.RequestCatcherInterface, ioc.get(RequestStatusCatcher));
66
63
  // ioc.bind(I.ExecutorPageBridgeInterface, ioc.get(ExecutorPageBridge));
67
64
  // ioc.bind(I.JSONStoragePageInterface, ioc.get(JSONStoragePageBridge));
68
65
  // ioc.bind(I.RequestPageBridgeInterface, ioc.get(RequestPageBridge));
69
66
  }
70
67
 
71
- protected registerCommon(_ioc: IOCContainer): void {
68
+ protected registerCommon(_ioc: IOCContainerInterface): void {
72
69
  // const { appConfig } = this.options;
73
70
  // const logger = ioc.get(I.Logger);
74
71
  // const feApiRequestCommonPlugin = new RequestCommonPlugin({
@@ -90,8 +87,8 @@ export class IocRegisterImpl implements IOCRegister {
90
87
  * @override
91
88
  */
92
89
  register(
93
- ioc: IOCContainer,
94
- _manager: IOCManagerInterface<IOCContainer>
90
+ ioc: IOCContainerInterface,
91
+ _manager: IOCManagerInterface<IOCContainerInterface>
95
92
  ): void {
96
93
  this.registerGlobals(ioc);
97
94
  this.registerCommon(ioc);
@@ -3,9 +3,12 @@ import { ColorFormatter, ConsoleHandler, Logger } from '@qlover/corekit-bridge';
3
3
  import { JSONSerializer } from '@qlover/fe-corekit';
4
4
  import { loggerStyles } from '@config/common';
5
5
  import { AppConfig } from '@/base/cases/AppConfig';
6
+ import { DialogHandler } from '@/base/cases/DialogHandler';
6
7
 
7
8
  export const appConfig = new AppConfig();
8
9
 
10
+ export const dialogHandler = new DialogHandler();
11
+
9
12
  /**
10
13
  * Global logger
11
14
  */
@@ -0,0 +1,52 @@
1
+ import {
2
+ createIOCFunction,
3
+ type IOCContainerInterface,
4
+ type IOCFunctionInterface
5
+ } from '@qlover/corekit-bridge';
6
+ import { AppConfig } from '@/base/cases/AppConfig';
7
+ import { InversifyContainer } from '@/base/cases/InversifyContainer';
8
+ import type { IOCInterface } from '@/base/port/IOCInterface';
9
+ import { ServerIOCRegister } from './ServerIOCRegister';
10
+ import type { IOCIdentifierMapServer } from '@config/IOCIdentifier';
11
+
12
+ export class ServerIOC
13
+ implements IOCInterface<IOCIdentifierMapServer, IOCContainerInterface>
14
+ {
15
+ static instance: ServerIOC | null = null;
16
+
17
+ protected ioc: IOCFunctionInterface<
18
+ IOCIdentifierMapServer,
19
+ IOCContainerInterface
20
+ > | null = null;
21
+
22
+ static create(): ServerIOC {
23
+ if (this.instance) {
24
+ return this.instance;
25
+ }
26
+
27
+ this.instance = new ServerIOC();
28
+
29
+ return this.instance;
30
+ }
31
+
32
+ create(): IOCFunctionInterface<
33
+ IOCIdentifierMapServer,
34
+ IOCContainerInterface
35
+ > {
36
+ if (this.ioc) {
37
+ return this.ioc;
38
+ }
39
+
40
+ this.ioc = createIOCFunction<IOCIdentifierMapServer>(
41
+ new InversifyContainer()
42
+ );
43
+
44
+ const register = new ServerIOCRegister({
45
+ appConfig: new AppConfig()
46
+ });
47
+
48
+ register.register(this.ioc.implemention!, this.ioc);
49
+
50
+ return this.ioc;
51
+ }
52
+ }
@@ -0,0 +1,63 @@
1
+ import {
2
+ ConsoleHandler,
3
+ Logger,
4
+ TimestampFormatter,
5
+ type IOCContainerInterface,
6
+ type IOCManagerInterface,
7
+ type IOCRegisterInterface
8
+ } from '@qlover/corekit-bridge';
9
+ import { IOCIdentifier as I } from '@config/IOCIdentifier';
10
+ import type { IocRegisterOptions } from '@/base/port/IOCInterface';
11
+
12
+ export class ServerIOCRegister
13
+ implements IOCRegisterInterface<IOCContainerInterface, IocRegisterOptions>
14
+ {
15
+ constructor(protected options: IocRegisterOptions) {}
16
+
17
+ /**
18
+ * Register globals
19
+ *
20
+ * 一般用于注册全局
21
+ *
22
+ * @param ioc - IOC container
23
+ */
24
+ protected registerGlobals(ioc: IOCContainerInterface): void {
25
+ const { appConfig } = this.options;
26
+ ioc.bind(I.AppConfig, appConfig);
27
+ ioc.bind(
28
+ I.Logger,
29
+ new Logger({
30
+ handlers: new ConsoleHandler(
31
+ new TimestampFormatter({
32
+ localeOptions: {
33
+ year: '2-digit',
34
+ month: '2-digit',
35
+ day: '2-digit',
36
+ hour: '2-digit',
37
+ minute: '2-digit',
38
+ second: '2-digit'
39
+ }
40
+ })
41
+ ),
42
+ silent: false,
43
+ level: appConfig.env === 'development' ? 'debug' : 'info'
44
+ })
45
+ );
46
+ }
47
+
48
+ protected registerImplement(_ioc: IOCContainerInterface): void {}
49
+
50
+ protected registerCommon(_ioc: IOCContainerInterface): void {}
51
+
52
+ /**
53
+ * @override
54
+ */
55
+ register(
56
+ ioc: IOCContainerInterface,
57
+ _manager: IOCManagerInterface<IOCContainerInterface>
58
+ ): void {
59
+ this.registerGlobals(ioc);
60
+ this.registerCommon(ioc);
61
+ this.registerImplement(ioc);
62
+ }
63
+ }
@@ -16,7 +16,6 @@ export default getRequestConfig(async ({ requestLocale }) => {
16
16
  // Dynamically import the translation messages for the selected locale
17
17
  return {
18
18
  locale,
19
- messages: (await import(`../../public/locales/${locale}/common.json`))
20
- .default
19
+ messages: (await import(`../../public/locales/${locale}.json`)).default
21
20
  };
22
21
  });
@@ -19,9 +19,9 @@ export const routing = defineRouting({
19
19
  en: '/',
20
20
  zh: '/'
21
21
  },
22
- '/about': {
23
- en: '/about',
24
- zh: '/about'
22
+ '/login': {
23
+ en: '/login',
24
+ zh: '/login'
25
25
  }
26
26
  }
27
27
  });
@@ -15,8 +15,11 @@ export const config = {
15
15
  matcher: [
16
16
  '/', // Match the root path explicitly
17
17
 
18
- // Match all paths except for API routes, Next.js internals, static assets, favicon, and sitemap.xml
19
- // This prevents middleware from rewriting or interfering with these special paths
20
- '/((?!api|_next/static|_next/image|icon|favicon.ico|sitemap.xml|sitemap-0.xml).*)'
18
+ // Match all paths except for:
19
+ // - API routes
20
+ // - Next.js internals (_next/*)
21
+ // - Static files (*.svg, *.png, *.jpg, *.jpeg, *.gif, *.ico)
22
+ // - Other static assets and special files
23
+ '/((?!api|_next|.*\\.(?:svg|png|jpg|jpeg|gif|ico)|favicon.ico|sitemap.xml|sitemap-0.xml).*)'
21
24
  ]
22
25
  };
@@ -236,4 +236,16 @@ html,
236
236
  --fe-message-info-color: var(--fe-color-primary);
237
237
  --fe-message-loading-color: var(--fe-color-primary);
238
238
  }
239
+
240
+ .ant-dropdown-css-var {
241
+ /* Control Variables - Default Theme */
242
+ --fe-control-outline-width: 2px;
243
+ --fe-control-interactive-size: 16px;
244
+ --fe-control-item-bg-hover: rgba(0, 0, 0, 0.04);
245
+ --fe-control-item-bg-active: #e6f4ff;
246
+ --fe-control-item-bg-active-hover: #bae0ff;
247
+ --fe-control-item-bg-active-disabled: rgba(0, 0, 0, 0.15);
248
+ --fe-control-tmp-outline: rgba(0, 0, 0, 0.02);
249
+ --fe-control-outline: rgba(96, 165, 250, 0.1); /* blue-400 with 0.1 opacity */
250
+ }
239
251
  }
@@ -175,4 +175,30 @@
175
175
  0.85
176
176
  ); /* 确保文字在深色背景上清晰可见 */
177
177
  }
178
+ .ant-dropdown-css-var {
179
+ /* Control Variables - Dark Theme */
180
+ --fe-control-outline-width: 2px;
181
+ --fe-control-interactive-size: 16px;
182
+ --fe-control-item-bg-hover: rgba(255, 255, 255, 0.08);
183
+ --fe-control-item-bg-active: rgba(
184
+ 96,
185
+ 165,
186
+ 250,
187
+ 0.2
188
+ ); /* blue-400 with 0.2 opacity */
189
+ --fe-control-item-bg-active-hover: rgba(
190
+ 96,
191
+ 165,
192
+ 250,
193
+ 0.3
194
+ ); /* blue-400 with 0.3 opacity */
195
+ --fe-control-item-bg-active-disabled: rgba(255, 255, 255, 0.15);
196
+ --fe-control-tmp-outline: rgba(255, 255, 255, 0.02);
197
+ --fe-control-outline: rgba(
198
+ 96,
199
+ 165,
200
+ 250,
201
+ 0.1
202
+ ); /* blue-400 with 0.1 opacity */
203
+ }
178
204
  }
@@ -201,4 +201,20 @@
201
201
  0 9px 28px 8px rgba(244, 114, 182, 0.05);
202
202
  --fe-modal-mask-bg: rgba(244, 114, 182, 0.45);
203
203
  }
204
+ .ant-dropdown-css-var {
205
+ /* Control Variables - Pink Theme */
206
+ --fe-control-outline-width: 2px;
207
+ --fe-control-interactive-size: 16px;
208
+ --fe-control-item-bg-hover: rgba(0, 0, 0, 0.04);
209
+ --fe-control-item-bg-active: #fce7f3; /* pink-100 */
210
+ --fe-control-item-bg-active-hover: #fbcfe8; /* pink-200 */
211
+ --fe-control-item-bg-active-disabled: rgba(0, 0, 0, 0.15);
212
+ --fe-control-tmp-outline: rgba(0, 0, 0, 0.02);
213
+ --fe-control-outline: rgba(
214
+ 244,
215
+ 114,
216
+ 182,
217
+ 0.1
218
+ ); /* pink-400 with 0.1 opacity */
219
+ }
204
220
  }
@@ -11,9 +11,10 @@
11
11
  --color-secondary: rgba(var(--color-bg-secondary));
12
12
  --color-elevated: rgba(var(--color-bg-elevated));
13
13
  --color-text: rgba(var(--text-primary));
14
+ --color-text-hover: rgba(var(--text-primary-hover));
14
15
  --color-text-secondary: rgba(var(--text-secondary));
15
16
  --color-text-tertiary: rgba(var(--text-tertiary));
16
- --color-border: rgba(var(--color-border));
17
- --color-brand: rgba(var(--color-brand));
18
- --color-brand-hover: rgba(var(--color-brand-hover));
17
+ --color-c-border: rgba(var(--color-border));
18
+ --color-c-brand: rgba(var(--color-brand));
19
+ --color-c-brand-hover: rgba(var(--color-brand-hover));
19
20
  }
@@ -8,6 +8,7 @@
8
8
 
9
9
  /* 文字颜色 */
10
10
  --text-primary: 15 23 42; /* slate-900 */
11
+ --text-primary-hover: 100 116 139; /* slate-500 */
11
12
  --text-secondary: 71 85 105; /* slate-600 */
12
13
  --text-tertiary: 100 116 139; /* slate-500 */
13
14
 
@@ -8,6 +8,7 @@
8
8
 
9
9
  /* 文字颜色 */
10
10
  --text-primary: 255 255 255;
11
+ --text-primary-hover: 148 163 184; /* slate-400 */
11
12
  --text-secondary: 148 163 184; /* slate-400 */
12
13
  --text-tertiary: 100 116 139; /* slate-500 */
13
14
 
@@ -8,6 +8,7 @@
8
8
 
9
9
  /* 文字颜色 */
10
10
  --text-primary: 190 18 60; /* rose-700 */
11
+ --text-primary-hover: 244 63 94; /* rose-500 */
11
12
  --text-secondary: 225 29 72; /* rose-600 */
12
13
  --text-tertiary: 244 63 94; /* rose-500 */
13
14
 
@@ -1,16 +1,21 @@
1
1
  'use client';
2
2
 
3
3
  import Link from 'next/link';
4
- import { AppConfig } from '@/base/cases/AppConfig';
5
- import { IOC } from '@/core/IOC';
4
+ import { IOCIdentifier } from '@config/IOCIdentifier';
5
+ import { useIOC } from '@/uikit/hook/useIOC';
6
6
  import { LanguageSwitcher } from './LanguageSwitcher';
7
+ import { LogoutButton } from './LogoutButton';
7
8
  import { ThemeSwitcher } from './ThemeSwitcher';
8
9
 
9
- export function BaseHeader() {
10
+ export function BaseHeader(props: { showLogoutButton?: boolean }) {
11
+ const { showLogoutButton } = props;
12
+ const appConfig = useIOC(IOCIdentifier.AppConfig);
13
+ const i18nService = useIOC(IOCIdentifier.I18nServiceInterface);
14
+
10
15
  return (
11
16
  <header
12
- data-testid="base-header"
13
- className="h-14 bg-secondary border-b border-border sticky top-0 z-50"
17
+ data-testid="BaseHeader"
18
+ className="h-14 bg-secondary border-b border-c-border sticky top-0 z-50"
14
19
  >
15
20
  <div className="flex items-center justify-between h-full px-4 mx-auto max-w-7xl">
16
21
  <div className="flex items-center">
@@ -18,23 +23,18 @@ export function BaseHeader() {
18
23
  href="/"
19
24
  className="flex items-center hover:opacity-80 transition-opacity"
20
25
  >
21
- {/* <img
22
- data-testid="base-header-logo"
23
- src={IOC(PublicAssetsPath).getPath('/logo.svg')}
24
- alt="logo"
25
- className="h-8 w-auto"
26
- /> */}
27
26
  <span
28
27
  data-testid="base-header-app-name"
29
28
  className="ml-2 text-lg font-semibold text-text"
30
29
  >
31
- {IOC(AppConfig).appName}
30
+ {appConfig.appName}
32
31
  </span>
33
32
  </Link>
34
33
  </div>
35
- <div className="flex items-center gap-4">
36
- <LanguageSwitcher />
34
+ <div className="flex items-center gap-2">
35
+ <LanguageSwitcher i18nService={i18nService} />
37
36
  <ThemeSwitcher />
37
+ {showLogoutButton && <LogoutButton />}
38
38
  </div>
39
39
  </div>
40
40
  </header>
@@ -0,0 +1,27 @@
1
+ import { BaseHeader } from './BaseHeader';
2
+ import type { HTMLAttributes } from 'react';
3
+
4
+ export interface BaseLayoutProps extends HTMLAttributes<HTMLDivElement> {
5
+ showLogoutButton?: boolean;
6
+ mainProps?: HTMLAttributes<HTMLElement>;
7
+ }
8
+
9
+ export function BaseLayout({
10
+ children,
11
+ showLogoutButton,
12
+ mainProps,
13
+ ...props
14
+ }: BaseLayoutProps) {
15
+ return (
16
+ <div
17
+ data-testid="BaseLayout"
18
+ className="flex flex-col min-h-screen"
19
+ {...props}
20
+ >
21
+ <BaseHeader showLogoutButton={showLogoutButton} />
22
+ <main className="flex flex-1 flex-col bg-primary" {...mainProps}>
23
+ {children}
24
+ </main>
25
+ </div>
26
+ );
27
+ }
@@ -1,11 +1,23 @@
1
1
  'use client';
2
2
  import '@ant-design/v5-patch-for-react-19';
3
+ import { useRouter } from 'next/navigation';
4
+ import { useLocale } from 'next-intl';
3
5
  import { useEffect } from 'react';
6
+ import { I } from '@config/IOCIdentifier';
7
+ import { NavigateBridge } from '@/base/cases/NavigateBridge';
4
8
  import { BootstrapClient } from '@/core/bootstraps/BootstrapClient';
9
+ import { clientIOC } from '@/core/clientIoc/ClientIOC';
5
10
  import { IOCContext } from '../context/IOCContext';
6
11
 
7
12
  export function BootstrapsProvider(props: { children: React.ReactNode }) {
8
- const IOC = BootstrapClient.createSingletonIOC();
13
+ const IOC = clientIOC.create();
14
+ const locale = useLocale();
15
+ const router = useRouter();
16
+
17
+ useEffect(() => {
18
+ IOC(I.RouterServiceInterface).setLocale(locale);
19
+ IOC(NavigateBridge).setUIBridge(router);
20
+ }, [locale, router, IOC]);
9
21
 
10
22
  useEffect(() => {
11
23
  if (typeof window !== 'undefined') {
@@ -3,6 +3,8 @@ import '@ant-design/v5-patch-for-react-19';
3
3
  import { AntdRegistry } from '@ant-design/nextjs-registry';
4
4
  import { AntdThemeProvider } from '@brain-toolkit/antd-theme-override/react';
5
5
  import { ThemeProvider } from 'next-themes';
6
+ import { IOCIdentifier } from '@config/IOCIdentifier';
7
+ import { clientIOC } from '@/core/clientIoc/ClientIOC';
6
8
  import { BootstrapsProvider } from './BootstrapsProvider';
7
9
  import type { CommonThemeConfig } from '@config/theme';
8
10
 
@@ -23,10 +25,13 @@ export function ComboProvider(props: {
23
25
  }) {
24
26
  const { themeConfig, children } = props;
25
27
 
28
+ const IOC = clientIOC.create();
29
+
26
30
  return (
27
31
  <AntdThemeProvider
28
32
  data-testid="ComboProvider"
29
33
  theme={themeConfig.antdTheme}
34
+ staticApi={IOC(IOCIdentifier.DialogHandler)}
30
35
  >
31
36
  <ThemeProvider
32
37
  themes={themeConfig.supportedThemes as unknown as string[]}