@qlover/create-app 0.3.2 → 0.3.4
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.
- package/CHANGELOG.md +139 -0
- package/package.json +4 -4
- package/templates/react-app/README.md +311 -120
- package/templates/react-app/config/Identifier.I18n.ts +1048 -0
- package/templates/react-app/config/app.router.json +7 -7
- package/templates/react-app/config/common.ts +13 -0
- package/templates/react-app/config/theme.json +7 -88
- package/templates/react-app/package.json +11 -5
- package/templates/react-app/postcss.config.js +1 -2
- package/templates/react-app/public/locales/en/common.json +142 -1
- package/templates/react-app/public/locales/zh/common.json +142 -1
- package/templates/react-app/src/App.tsx +16 -3
- package/templates/react-app/src/base/apis/AiApi.ts +4 -4
- package/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +2 -2
- package/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +2 -2
- package/templates/react-app/src/base/cases/AppConfig.ts +103 -0
- package/templates/react-app/src/base/cases/{appError/AppError.ts → AppError.ts} +0 -3
- package/templates/react-app/src/base/cases/DialogHandler.ts +86 -0
- package/templates/react-app/src/base/cases/RequestLogger.ts +1 -1
- package/templates/react-app/src/base/cases/RouterLoader.ts +166 -0
- package/templates/react-app/src/base/port/InteractionHubInterface.ts +94 -0
- package/templates/react-app/src/base/services/I18nService.ts +50 -3
- package/templates/react-app/src/base/services/ProcesserService.ts +0 -1
- package/templates/react-app/src/base/types/deprecated-antd.d.ts +60 -0
- package/templates/react-app/src/core/IOC.ts +18 -8
- package/templates/react-app/src/core/bootstrap.ts +41 -62
- package/templates/react-app/src/core/bootstraps/PrintBootstrap.ts +14 -0
- package/templates/react-app/src/core/bootstraps/index.ts +36 -7
- package/templates/react-app/src/core/globals.ts +8 -1
- package/templates/react-app/src/core/registers/RegisterApi.ts +2 -5
- package/templates/react-app/src/core/registers/RegisterCommon.ts +38 -29
- package/templates/react-app/src/core/registers/RegisterControllers.ts +5 -10
- package/templates/react-app/src/core/registers/RegisterGlobals.ts +21 -17
- package/templates/react-app/src/core/registers/index.ts +27 -13
- package/templates/react-app/src/main.tsx +1 -1
- package/templates/react-app/src/pages/404.tsx +1 -1
- package/templates/react-app/src/pages/500.tsx +1 -1
- package/templates/react-app/src/pages/auth/Login.tsx +128 -36
- package/templates/react-app/src/pages/base/About.tsx +118 -2
- package/templates/react-app/src/pages/base/ErrorIdentifier.tsx +38 -19
- package/templates/react-app/src/pages/base/Executor.tsx +442 -29
- package/templates/react-app/src/pages/base/Home.tsx +99 -93
- package/templates/react-app/src/pages/base/JSONStorage.tsx +47 -38
- package/templates/react-app/src/pages/base/Layout.tsx +5 -2
- package/templates/react-app/src/pages/base/Request.tsx +90 -208
- package/templates/react-app/src/pages/base/components/BaseHeader.tsx +13 -5
- package/templates/react-app/src/styles/css/antd-themes/_default.css +239 -0
- package/templates/react-app/src/styles/css/antd-themes/dark.css +176 -0
- package/templates/react-app/src/styles/css/antd-themes/index.css +3 -0
- package/templates/react-app/src/styles/css/antd-themes/no-context.css +34 -0
- package/templates/react-app/src/styles/css/antd-themes/pink.css +199 -0
- package/templates/react-app/src/{uikit/styles → styles}/css/index.css +3 -0
- package/templates/react-app/src/styles/css/page.css +11 -0
- package/templates/react-app/src/styles/css/tailwind.css +5 -0
- package/templates/react-app/src/styles/css/themes/_default.css +29 -0
- package/templates/react-app/src/styles/css/themes/dark.css +29 -0
- package/templates/react-app/src/styles/css/themes/index.css +3 -0
- package/templates/react-app/src/styles/css/themes/pink.css +29 -0
- package/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +56 -0
- package/templates/react-app/src/uikit/components/Loading.tsx +27 -21
- package/templates/react-app/src/uikit/components/RouterRenderComponent.tsx +1 -1
- package/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +63 -13
- package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +1 -1
- package/templates/react-app/src/uikit/controllers/RouterController.ts +1 -1
- package/templates/react-app/src/uikit/controllers/UserController.ts +2 -2
- package/templates/react-app/tailwind.config.js +1 -15
- package/templates/react-app/tsconfig.json +3 -2
- package/templates/react-app/tsconfig.node.json +2 -1
- package/templates/react-app/vite.config.ts +15 -3
- package/templates/react-app/lib/tailwind/root10px.js +0 -178
- package/templates/react-app/lib/tailwind/theme-generator.js +0 -238
- package/templates/react-app/public/locales/en/about.json +0 -3
- package/templates/react-app/public/locales/en/executor.json +0 -6
- package/templates/react-app/public/locales/en/home.json +0 -10
- package/templates/react-app/public/locales/en/jsonStorage.json +0 -11
- package/templates/react-app/public/locales/en/login.json +0 -7
- package/templates/react-app/public/locales/en/request.json +0 -15
- package/templates/react-app/public/locales/zh/about.json +0 -3
- package/templates/react-app/public/locales/zh/executor.json +0 -6
- package/templates/react-app/public/locales/zh/home.json +0 -10
- package/templates/react-app/public/locales/zh/jsonStorage.json +0 -11
- package/templates/react-app/public/locales/zh/login.json +0 -7
- package/templates/react-app/public/locales/zh/request.json +0 -15
- package/templates/react-app/src/base/cases/router-loader/index.ts +0 -90
- package/templates/react-app/src/base/port/InversifyIocInterface.ts +0 -9
- package/templates/react-app/src/core/AppConfig.ts +0 -36
- package/templates/react-app/src/uikit/styles/css/page.css +0 -3
- package/templates/react-app/src/uikit/styles/css/tailwind.css +0 -3
- /package/templates/react-app/config/{ErrorIdentifier.ts → Identifier.Error.ts} +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { EnvConfigInterface } from '@qlover/corekit-bridge';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Application Configuration Management
|
|
5
|
+
*
|
|
6
|
+
* Significance: Centralized configuration management for the application
|
|
7
|
+
* Core idea: Single source of truth for all environment and application settings
|
|
8
|
+
* Main function: Provide typed access to application configuration values
|
|
9
|
+
* Main purpose: Maintain consistent configuration across the application
|
|
10
|
+
*
|
|
11
|
+
* Configuration values are automatically injected from the project's .env files:
|
|
12
|
+
* - .env: Default environment variables
|
|
13
|
+
* - .env.development: Development environment variables
|
|
14
|
+
* - .env.production: Production environment variables
|
|
15
|
+
* - .env.local: Local overrides (git ignored)
|
|
16
|
+
*
|
|
17
|
+
* Environment variables should be prefixed with VITE_ to be exposed to the client side.
|
|
18
|
+
* Example .env file:
|
|
19
|
+
* ```
|
|
20
|
+
* VITE_APP_NAME=MyApp
|
|
21
|
+
* VITE_API_BASE_URL=http://api.example.com
|
|
22
|
+
* VITE_USER_TOKEN_KEY=user_token
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const config = new AppConfig();
|
|
27
|
+
* console.log(config.appName); // Value from VITE_APP_NAME
|
|
28
|
+
* console.log(config.aiApiBaseUrl); // Value from VITE_AI_API_BASE_URL
|
|
29
|
+
*/
|
|
30
|
+
export class AppConfig implements EnvConfigInterface {
|
|
31
|
+
/**
|
|
32
|
+
* Application name identifier
|
|
33
|
+
* @description Injected from VITE_APP_NAME environment variable
|
|
34
|
+
*/
|
|
35
|
+
readonly appName = '';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Current version of the application
|
|
39
|
+
* @description Injected from VITE_APP_VERSION environment variable
|
|
40
|
+
*/
|
|
41
|
+
readonly appVersion = '';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Current environment mode for Vite
|
|
45
|
+
* @description Represents the running environment (development, production, etc.)
|
|
46
|
+
* Automatically set based on the current .env file being used
|
|
47
|
+
*/
|
|
48
|
+
readonly env: string = '';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Storage key for user authentication token
|
|
52
|
+
* @description Injected from VITE_USER_TOKEN_STORAGE_KEY environment variable
|
|
53
|
+
*/
|
|
54
|
+
readonly userTokenStorageKey = '';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Available OpenAI model configurations
|
|
58
|
+
* @description List of supported OpenAI models for the application
|
|
59
|
+
*/
|
|
60
|
+
readonly openAiModels = [
|
|
61
|
+
'gpt-4o-mini',
|
|
62
|
+
'gpt-3.5-turbo',
|
|
63
|
+
'gpt-3.5-turbo-2',
|
|
64
|
+
'gpt-4',
|
|
65
|
+
'gpt-4-32k'
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
/** Base URL for OpenAI API endpoints */
|
|
69
|
+
readonly openAiBaseUrl = '';
|
|
70
|
+
|
|
71
|
+
/** Authentication token for OpenAI API */
|
|
72
|
+
readonly openAiToken = '';
|
|
73
|
+
|
|
74
|
+
/** Prefix for OpenAI authentication token */
|
|
75
|
+
readonly openAiTokenPrefix = '';
|
|
76
|
+
|
|
77
|
+
/** Flag indicating if OpenAI token is required */
|
|
78
|
+
readonly openAiRequireToken = true;
|
|
79
|
+
|
|
80
|
+
/** Default login username */
|
|
81
|
+
readonly loginUser = '';
|
|
82
|
+
|
|
83
|
+
/** Default login password */
|
|
84
|
+
readonly loginPassword = '';
|
|
85
|
+
|
|
86
|
+
/** Base URL for frontend API endpoints */
|
|
87
|
+
readonly feApiBaseUrl = '';
|
|
88
|
+
|
|
89
|
+
/** Base URL for user-related API endpoints */
|
|
90
|
+
readonly userApiBaseUrl = '';
|
|
91
|
+
|
|
92
|
+
/** Base URL for AI service API endpoints */
|
|
93
|
+
readonly aiApiBaseUrl = 'https://api.openai.com/v1';
|
|
94
|
+
|
|
95
|
+
/** Authentication token for AI service API */
|
|
96
|
+
readonly aiApiToken = '';
|
|
97
|
+
|
|
98
|
+
/** Prefix for AI service authentication token */
|
|
99
|
+
readonly aiApiTokenPrefix = 'Bearer';
|
|
100
|
+
|
|
101
|
+
/** Flag indicating if AI service token is required */
|
|
102
|
+
readonly aiApiRequireToken = true;
|
|
103
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AntdStaticApiInterface,
|
|
3
|
+
MessageApi,
|
|
4
|
+
ModalApi,
|
|
5
|
+
NotificationApi
|
|
6
|
+
} from '@brain-toolkit/antd-theme-override/react';
|
|
7
|
+
import type {
|
|
8
|
+
InteractionHubInterface,
|
|
9
|
+
InteractionOptions,
|
|
10
|
+
ConfirmOptions
|
|
11
|
+
} from '../port/InteractionHubInterface';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Dialog Handler Implementation
|
|
15
|
+
*
|
|
16
|
+
* Implements the InteractionHubInterface using Ant Design components.
|
|
17
|
+
* Provides concrete implementations for displaying notifications and confirmation dialogs.
|
|
18
|
+
*
|
|
19
|
+
* Features:
|
|
20
|
+
* - Uses Ant Design's message component for notifications
|
|
21
|
+
* - Uses Ant Design's Modal component for confirmations
|
|
22
|
+
* - Supports customizable display durations
|
|
23
|
+
* - Handles error objects appropriately
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const dialog = new DialogHandler();
|
|
27
|
+
* dialog.success('Data saved successfully');
|
|
28
|
+
* dialog.confirm({
|
|
29
|
+
* title: 'Confirm Delete',
|
|
30
|
+
* content: 'Are you sure you want to delete this item?',
|
|
31
|
+
* onOk: () => handleDelete(),
|
|
32
|
+
* });
|
|
33
|
+
*/
|
|
34
|
+
export class DialogHandler
|
|
35
|
+
implements InteractionHubInterface, AntdStaticApiInterface
|
|
36
|
+
{
|
|
37
|
+
private antds: {
|
|
38
|
+
message?: MessageApi;
|
|
39
|
+
modal?: ModalApi;
|
|
40
|
+
notification?: NotificationApi;
|
|
41
|
+
} = {};
|
|
42
|
+
|
|
43
|
+
setMessage(message: MessageApi): void {
|
|
44
|
+
this.antds.message = message;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setModal(modal: ModalApi): void {
|
|
48
|
+
this.antds.modal = modal;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setNotification(notification: NotificationApi): void {
|
|
52
|
+
this.antds.notification = notification;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Formats error message from various error types
|
|
57
|
+
*/
|
|
58
|
+
private formatErrorMessage(error: unknown): string {
|
|
59
|
+
if (error instanceof Error) return error.message;
|
|
60
|
+
if (typeof error === 'string') return error;
|
|
61
|
+
return 'An unknown error occurred';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public success(msg: string, options?: InteractionOptions): void {
|
|
65
|
+
this.antds.message?.success({ content: msg, ...options });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public error(msg: string, options?: InteractionOptions): void {
|
|
69
|
+
this.antds.message?.error({
|
|
70
|
+
content: options?.error ? this.formatErrorMessage(options.error) : msg,
|
|
71
|
+
...options
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public info(msg: string, options?: InteractionOptions): void {
|
|
76
|
+
this.antds.message?.info({ content: msg, ...options });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public warn(msg: string, options?: InteractionOptions): void {
|
|
80
|
+
this.antds.message?.warning({ content: msg, ...options });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public confirm(options: ConfirmOptions): void {
|
|
84
|
+
this.antds.modal?.confirm(options);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { ComponentType, LazyExoticComponent, ReactNode } from 'react';
|
|
2
|
+
import { RouteObject } from 'react-router-dom';
|
|
3
|
+
import isString from 'lodash/isString';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Component mapping type for lazy-loaded components
|
|
7
|
+
* @description Maps component identifiers to their lazy-loaded implementations
|
|
8
|
+
*/
|
|
9
|
+
export type ComponentValue = Record<string, () => unknown>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Route component type definition supporting both regular and lazy-loaded components
|
|
13
|
+
*/
|
|
14
|
+
type RouteComponentType<T = unknown> =
|
|
15
|
+
| ComponentType<T>
|
|
16
|
+
| LazyExoticComponent<ComponentType<T>>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Extended route configuration interface
|
|
20
|
+
* @description Extends React Router's RouteObject with additional meta information
|
|
21
|
+
*/
|
|
22
|
+
export type RouteConfigValue = Omit<RouteObject, 'element' | 'children'> & {
|
|
23
|
+
/**
|
|
24
|
+
* Component identifier string that maps to a component in componentMaps
|
|
25
|
+
*
|
|
26
|
+
* TODO: support `ReactNode`
|
|
27
|
+
*
|
|
28
|
+
* @description Used to lookup the actual component implementation
|
|
29
|
+
*/
|
|
30
|
+
element?: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Nested route configurations
|
|
34
|
+
*/
|
|
35
|
+
children?: RouteConfigValue[];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Additional metadata for the route
|
|
39
|
+
* @description Can store any route-specific data like permissions, titles, etc.
|
|
40
|
+
*/
|
|
41
|
+
meta?: Record<string, unknown>;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Route rendering function type
|
|
46
|
+
* @description Function to customize how route components are rendered
|
|
47
|
+
*/
|
|
48
|
+
export type RouterLoaderRender = (
|
|
49
|
+
route: Omit<RouteConfigValue, 'element'> & {
|
|
50
|
+
element: () => RouteComponentType;
|
|
51
|
+
}
|
|
52
|
+
) => ReactNode;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Router loader configuration options
|
|
56
|
+
*/
|
|
57
|
+
export type RouterLoaderOptions = {
|
|
58
|
+
/**
|
|
59
|
+
* Route configuration array
|
|
60
|
+
* @description Defines the application's routing structure
|
|
61
|
+
*/
|
|
62
|
+
routes?: RouteConfigValue[];
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Component mapping object
|
|
66
|
+
* @description Maps component identifiers to their actual implementations
|
|
67
|
+
* @example
|
|
68
|
+
* {
|
|
69
|
+
* 'Home': () => import('./pages/Home'),
|
|
70
|
+
* 'About': () => import('./pages/About')
|
|
71
|
+
* }
|
|
72
|
+
*/
|
|
73
|
+
componentMaps?: ComponentValue;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Custom route rendering function
|
|
77
|
+
* @description Controls how route components are rendered
|
|
78
|
+
* @required
|
|
79
|
+
*/
|
|
80
|
+
render: RouterLoaderRender;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Router Configuration Loader
|
|
85
|
+
*
|
|
86
|
+
* Significance: Manages dynamic route configuration and component loading
|
|
87
|
+
* Core idea: Separate route definitions from component implementations
|
|
88
|
+
* Main function: Transform route configurations into React Router compatible routes
|
|
89
|
+
* Main purpose: Enable dynamic and code-split routing with lazy loading
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* const loader = new RouterLoader({
|
|
93
|
+
* routes: [{
|
|
94
|
+
* path: '/',
|
|
95
|
+
* element: 'Home',
|
|
96
|
+
* children: [{
|
|
97
|
+
* path: 'about',
|
|
98
|
+
* element: 'About'
|
|
99
|
+
* }]
|
|
100
|
+
* }],
|
|
101
|
+
* componentMaps: {
|
|
102
|
+
* 'Home': () => import('./pages/Home'),
|
|
103
|
+
* 'About': () => import('./pages/About')
|
|
104
|
+
* },
|
|
105
|
+
* render: (route) => <Suspense><route.element /></Suspense>
|
|
106
|
+
* });
|
|
107
|
+
*/
|
|
108
|
+
export class RouterLoader {
|
|
109
|
+
constructor(private readonly options: RouterLoaderOptions) {
|
|
110
|
+
if (!options.render) {
|
|
111
|
+
throw new Error('RouterLoader render is required');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get the component mapping object
|
|
117
|
+
* @returns Component mapping dictionary
|
|
118
|
+
*/
|
|
119
|
+
getComponentMaps(): ComponentValue {
|
|
120
|
+
const { componentMaps = {} } = this.options;
|
|
121
|
+
return componentMaps;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Retrieve a component implementation by its identifier
|
|
126
|
+
* @param element Component identifier string
|
|
127
|
+
* @returns Component loader function
|
|
128
|
+
* @throws Error if component is not found in componentMaps
|
|
129
|
+
*/
|
|
130
|
+
getComponent(element: string): () => RouteComponentType {
|
|
131
|
+
const maps = this.getComponentMaps();
|
|
132
|
+
const component = maps[element];
|
|
133
|
+
|
|
134
|
+
if (!component) {
|
|
135
|
+
throw new Error(`Component not found: ${element}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return component as () => RouteComponentType;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Transform a route configuration into a React Router compatible route
|
|
143
|
+
* @param route Route configuration object
|
|
144
|
+
* @returns React Router route object
|
|
145
|
+
*/
|
|
146
|
+
toRoute(route: RouteConfigValue): RouteObject {
|
|
147
|
+
const { render } = this.options;
|
|
148
|
+
const { element, children, ...rest } = route;
|
|
149
|
+
|
|
150
|
+
if (!element || !isString(element)) {
|
|
151
|
+
console.warn(
|
|
152
|
+
`Invalid route, path is: ${route.path}, element is: ${element}`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const componet = this.getComponent(element || '404');
|
|
157
|
+
const Element = render({ ...rest, element: componet });
|
|
158
|
+
|
|
159
|
+
// @ts-expect-error
|
|
160
|
+
return {
|
|
161
|
+
...rest,
|
|
162
|
+
element: Element,
|
|
163
|
+
children: children?.map((child) => this.toRoute(child))
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ModalFuncProps } from 'antd';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Confirmation dialog configuration options
|
|
5
|
+
* Extends Ant Design's ModalFuncProps and requires a title
|
|
6
|
+
*/
|
|
7
|
+
export interface ConfirmOptions extends ModalFuncProps {
|
|
8
|
+
content: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Basic dialog configuration options
|
|
13
|
+
*/
|
|
14
|
+
export interface InteractionOptions {
|
|
15
|
+
/** Display duration (milliseconds) */
|
|
16
|
+
duration?: number;
|
|
17
|
+
/** Close callback function */
|
|
18
|
+
onClose?: () => void;
|
|
19
|
+
/** Error object */
|
|
20
|
+
error?: unknown;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Interaction Hub Interface
|
|
25
|
+
*
|
|
26
|
+
* This interface serves as the central interaction layer for the application,
|
|
27
|
+
* managing all user interface interactions, notifications, and feedback functionality.
|
|
28
|
+
* Main responsibilities include:
|
|
29
|
+
* 1. Providing a unified message notification mechanism (success/error/info/warn)
|
|
30
|
+
* 2. Handling user interaction confirmation scenarios (confirm)
|
|
31
|
+
* 3. Unified error display and handling approach
|
|
32
|
+
*
|
|
33
|
+
* Design Goals:
|
|
34
|
+
* - Uniformity: Provide consistent user interaction experience
|
|
35
|
+
* - Extensibility: Easy to add new interaction types
|
|
36
|
+
* - Configurability: Support custom interaction behaviors
|
|
37
|
+
* - Decoupling: Separate UI interaction layer from business logic
|
|
38
|
+
*
|
|
39
|
+
* Use Cases:
|
|
40
|
+
* - Operation success/failure feedback
|
|
41
|
+
* - Important operation confirmation prompts
|
|
42
|
+
* - System message notifications
|
|
43
|
+
* - Warning message display
|
|
44
|
+
*
|
|
45
|
+
* Implementation Notes:
|
|
46
|
+
* - Currently implemented based on Ant Design component library
|
|
47
|
+
* - Supports other UI libraries through different adapters
|
|
48
|
+
* - Supports global configuration of default behaviors
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // Success notification
|
|
52
|
+
* interactionHub.success("Operation successful", { duration: 3000 });
|
|
53
|
+
*
|
|
54
|
+
* // Confirmation dialog
|
|
55
|
+
* interactionHub.confirm({
|
|
56
|
+
* content: "Are you sure you want to delete?",
|
|
57
|
+
* onOk: () => handleDelete()
|
|
58
|
+
* });
|
|
59
|
+
*/
|
|
60
|
+
export interface InteractionHubInterface {
|
|
61
|
+
/**
|
|
62
|
+
* Display success notification
|
|
63
|
+
* @param message Notification message
|
|
64
|
+
* @param options Configuration options
|
|
65
|
+
*/
|
|
66
|
+
success(message: string, options?: InteractionOptions): void;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Display error notification
|
|
70
|
+
* @param message Error message
|
|
71
|
+
* @param options Configuration options
|
|
72
|
+
*/
|
|
73
|
+
error(message: string, options?: InteractionOptions): void;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Display information notification
|
|
77
|
+
* @param message Information message
|
|
78
|
+
* @param options Configuration options
|
|
79
|
+
*/
|
|
80
|
+
info(message: string, options?: InteractionOptions): void;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Display warning notification
|
|
84
|
+
* @param message Warning message
|
|
85
|
+
* @param options Configuration options
|
|
86
|
+
*/
|
|
87
|
+
warn(message: string, options?: InteractionOptions): void;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Display confirmation dialog
|
|
91
|
+
* @param options Confirmation dialog configuration options
|
|
92
|
+
*/
|
|
93
|
+
confirm(options: ConfirmOptions): void;
|
|
94
|
+
}
|
|
@@ -4,17 +4,37 @@ import LanguageDetector from 'i18next-browser-languagedetector';
|
|
|
4
4
|
import HttpApi from 'i18next-http-backend';
|
|
5
5
|
import merge from 'lodash/merge';
|
|
6
6
|
import i18nConfig from '@config/i18n';
|
|
7
|
-
import
|
|
7
|
+
import {
|
|
8
|
+
SliceStore,
|
|
9
|
+
type BootstrapExecutorPlugin
|
|
10
|
+
} from '@qlover/corekit-bridge';
|
|
8
11
|
|
|
9
12
|
const { supportedLngs, fallbackLng } = i18nConfig;
|
|
10
13
|
|
|
11
14
|
export type I18nServiceLocale = (typeof supportedLngs)[number];
|
|
12
15
|
|
|
13
|
-
export class
|
|
16
|
+
export class I18nServiceState {
|
|
17
|
+
loading: boolean = false;
|
|
18
|
+
constructor(public language: I18nServiceLocale) {}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class I18nService
|
|
22
|
+
extends SliceStore<I18nServiceState>
|
|
23
|
+
implements BootstrapExecutorPlugin
|
|
24
|
+
{
|
|
14
25
|
readonly pluginName = 'I18nService';
|
|
15
26
|
|
|
16
|
-
|
|
27
|
+
selector = {
|
|
28
|
+
loading: (state: I18nServiceState) => state.loading
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
constructor(private pathname: string) {
|
|
32
|
+
super(() => new I18nServiceState(i18n.language as I18nServiceLocale));
|
|
33
|
+
}
|
|
17
34
|
|
|
35
|
+
/**
|
|
36
|
+
* @override
|
|
37
|
+
*/
|
|
18
38
|
onBefore(): void {
|
|
19
39
|
const debug = false;
|
|
20
40
|
|
|
@@ -48,6 +68,14 @@ export class I18nService implements BootstrapExecutorPlugin {
|
|
|
48
68
|
i18n.services.languageDetector.addDetector(pathLanguageDetector);
|
|
49
69
|
}
|
|
50
70
|
|
|
71
|
+
async changeLanguage(language: I18nServiceLocale): Promise<void> {
|
|
72
|
+
await i18n.changeLanguage(language);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
changeLoading(loading: boolean): void {
|
|
76
|
+
this.emit({ ...this.state, loading });
|
|
77
|
+
}
|
|
78
|
+
|
|
51
79
|
static getCurrentLanguage(): I18nServiceLocale {
|
|
52
80
|
return i18n.language as I18nServiceLocale;
|
|
53
81
|
}
|
|
@@ -60,4 +88,23 @@ export class I18nService implements BootstrapExecutorPlugin {
|
|
|
60
88
|
static isValidLanguage(language: string): language is I18nServiceLocale {
|
|
61
89
|
return supportedLngs.includes(language as I18nServiceLocale);
|
|
62
90
|
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* translate the key
|
|
94
|
+
* @param key - key to translate
|
|
95
|
+
* @param params - params to pass to the translation
|
|
96
|
+
* @returns translated value
|
|
97
|
+
*/
|
|
98
|
+
t(key: string, params?: Record<string, unknown>): string {
|
|
99
|
+
const i18nValue = i18n.t(key, {
|
|
100
|
+
lng: i18n.language,
|
|
101
|
+
...params
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (!i18nValue || i18nValue === key) {
|
|
105
|
+
return key;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return i18nValue;
|
|
109
|
+
}
|
|
63
110
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import AntdMessageStatic from 'antd/es/message';
|
|
2
|
+
import { TypeOpen } from 'antd/es/message/interface';
|
|
3
|
+
import AntdModalStatic from 'antd/es/modal';
|
|
4
|
+
|
|
5
|
+
import { ModalFunc } from 'antd/es/modal/confirm';
|
|
6
|
+
|
|
7
|
+
declare module 'antd' {
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated Please use alternative message implementation instead
|
|
10
|
+
*/
|
|
11
|
+
export const message: typeof AntdMessageStatic & {
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated Please use alternative message implementation instead
|
|
14
|
+
*/
|
|
15
|
+
info: TypeOpen;
|
|
16
|
+
/**
|
|
17
|
+
* @deprecated Please use alternative message implementation instead
|
|
18
|
+
*/
|
|
19
|
+
success: TypeOpen;
|
|
20
|
+
/**
|
|
21
|
+
* @deprecated Please use alternative message implementation instead
|
|
22
|
+
*/
|
|
23
|
+
error: TypeOpen;
|
|
24
|
+
/**
|
|
25
|
+
* @deprecated Please use alternative message implementation instead
|
|
26
|
+
*/
|
|
27
|
+
warning: TypeOpen;
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated Please use alternative message implementation instead
|
|
30
|
+
*/
|
|
31
|
+
loading: TypeOpen;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const Modal: typeof AntdModalStatic & {
|
|
35
|
+
/**
|
|
36
|
+
* @deprecated Please use alternative message implementation instead
|
|
37
|
+
*/
|
|
38
|
+
confirm: ModalFunc;
|
|
39
|
+
/**
|
|
40
|
+
* @deprecated Please use alternative message implementation instead
|
|
41
|
+
*/
|
|
42
|
+
info: ModalFunc;
|
|
43
|
+
/**
|
|
44
|
+
* @deprecated Please use alternative message implementation instead
|
|
45
|
+
*/
|
|
46
|
+
success: ModalFunc;
|
|
47
|
+
/**
|
|
48
|
+
* @deprecated Please use alternative message implementation instead
|
|
49
|
+
*/
|
|
50
|
+
error: ModalFunc;
|
|
51
|
+
/**
|
|
52
|
+
* @deprecated Please use alternative message implementation instead
|
|
53
|
+
*/
|
|
54
|
+
warn: ModalFunc;
|
|
55
|
+
/**
|
|
56
|
+
* @deprecated Please use alternative message implementation instead
|
|
57
|
+
*/
|
|
58
|
+
warning: ModalFunc;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -11,8 +11,22 @@ import {
|
|
|
11
11
|
} from '@qlover/corekit-bridge';
|
|
12
12
|
import type { JSONSerializer, JSONStorage } from '@qlover/fe-corekit';
|
|
13
13
|
import type { LoggerInterface } from '@qlover/logger';
|
|
14
|
+
import type { AppConfig } from '@/base/cases/AppConfig';
|
|
14
15
|
import { Container } from 'inversify';
|
|
15
|
-
import {
|
|
16
|
+
import { IOCRegisterInterface } from '@qlover/corekit-bridge';
|
|
17
|
+
import { DialogHandler } from '@/base/cases/DialogHandler';
|
|
18
|
+
|
|
19
|
+
export type IocRegisterOptions = {
|
|
20
|
+
pathname: string;
|
|
21
|
+
appConfig: AppConfig;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type InversifyRegisterContainer = Container;
|
|
25
|
+
/**
|
|
26
|
+
* Inversify register interface.
|
|
27
|
+
*/
|
|
28
|
+
export interface InversifyRegisterInterface
|
|
29
|
+
extends IOCRegisterInterface<InversifyContainer, IocRegisterOptions> {}
|
|
16
30
|
|
|
17
31
|
export class InversifyContainer implements IOCContainerInterface {
|
|
18
32
|
private container: Container;
|
|
@@ -26,12 +40,6 @@ export class InversifyContainer implements IOCContainerInterface {
|
|
|
26
40
|
});
|
|
27
41
|
}
|
|
28
42
|
|
|
29
|
-
configure(registers?: InversifyRegisterInterface[]): void {
|
|
30
|
-
if (registers) {
|
|
31
|
-
registers.forEach((register) => register.register(this.container, this));
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
43
|
bind<T>(key: ServiceIdentifier<T>, value: T): void {
|
|
36
44
|
this.container.bind<T>(key).toConstantValue(value);
|
|
37
45
|
}
|
|
@@ -67,7 +75,8 @@ export const IOCIdentifier = Object.freeze({
|
|
|
67
75
|
FeApiCommonPlugin: 'FeApiCommonPlugin',
|
|
68
76
|
AppConfig: 'AppConfig',
|
|
69
77
|
ApiMockPlugin: 'ApiMockPlugin',
|
|
70
|
-
ApiCatchPlugin: 'ApiCatchPlugin'
|
|
78
|
+
ApiCatchPlugin: 'ApiCatchPlugin',
|
|
79
|
+
DialogHandler: 'DialogHandler'
|
|
71
80
|
});
|
|
72
81
|
|
|
73
82
|
/**
|
|
@@ -82,6 +91,7 @@ export interface IOCIdentifierMap {
|
|
|
82
91
|
[IOCIdentifier.AppConfig]: EnvConfigInterface;
|
|
83
92
|
[IOCIdentifier.ApiMockPlugin]: ApiMockPlugin;
|
|
84
93
|
[IOCIdentifier.ApiCatchPlugin]: ApiCatchPlugin;
|
|
94
|
+
[IOCIdentifier.DialogHandler]: DialogHandler;
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
export const IOC = createIOCFunction<IOCIdentifierMap>(
|