@qlover/create-app 0.1.15 → 0.1.16
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 +33 -27
- package/bin/create-app.js +0 -0
- package/configs/_common/package.json.template +10 -0
- package/configs/react-app/eslint.config.js +70 -42
- package/dist/index.cjs +1 -3631
- package/dist/index.js +1 -3625
- package/package.json +7 -7
- package/templates/react-app/.env +1 -0
- package/templates/react-app/config/ErrorIdentifier.ts +27 -0
- package/templates/react-app/config/app.router.json +9 -0
- package/templates/react-app/config/common.ts +12 -0
- package/templates/react-app/config/feapi.mock.json +15 -2
- package/templates/react-app/config/i18n.ts +3 -11
- package/templates/react-app/package.json +7 -4
- package/templates/react-app/pnpm-lock.yaml +176 -25
- package/templates/react-app/public/locales/en/common.json +6 -2
- package/templates/react-app/public/locales/zh/common.json +6 -3
- package/templates/react-app/src/App.tsx +8 -8
- package/templates/react-app/src/base/apis/AiApi.ts +55 -0
- package/templates/react-app/src/base/apis/feApi/FeApi.ts +13 -44
- package/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +14 -0
- package/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +67 -0
- package/templates/react-app/src/base/apis/feApi/FeApiType.ts +2 -35
- package/templates/react-app/src/base/apis/userApi/UserApi.ts +64 -0
- package/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +14 -0
- package/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +75 -0
- package/templates/react-app/src/base/apis/userApi/UserApiType.ts +52 -0
- package/templates/react-app/src/base/cases/RequestLogger.ts +71 -0
- package/templates/react-app/src/base/cases/RequestStatusCatcher.ts +41 -0
- package/templates/react-app/src/base/cases/appError/AppError.ts +13 -0
- package/templates/react-app/{lib/router-loader/RouterLoader.ts → src/base/cases/router-loader/index.ts} +1 -1
- package/templates/react-app/src/base/port/ApiTransactionInterface.ts +7 -0
- package/templates/react-app/src/base/port/InversifyIocInterface.ts +1 -1
- package/templates/react-app/src/base/port/LoginInterface.ts +4 -0
- package/templates/react-app/src/base/port/RequestCatcherInterface.ts +12 -0
- package/templates/react-app/src/{services → base/services}/I18nService.ts +7 -2
- package/templates/react-app/src/{services/processer → base/services}/ProcesserService.ts +10 -11
- package/templates/react-app/src/base/types/Page.ts +1 -13
- package/templates/react-app/src/core/AppConfig.ts +14 -5
- package/templates/react-app/src/core/IOC.ts +77 -37
- package/templates/react-app/src/core/bootstrap.ts +38 -25
- package/templates/react-app/src/core/bootstraps/BootstrapApp.ts +7 -0
- package/templates/react-app/src/core/bootstraps/index.ts +12 -0
- package/templates/react-app/src/core/globals.ts +7 -10
- package/templates/react-app/src/core/registers/RegisterApi.ts +1 -52
- package/templates/react-app/src/core/registers/RegisterCommon.ts +44 -6
- package/templates/react-app/src/core/registers/RegisterControllers.ts +5 -34
- package/templates/react-app/src/core/registers/RegisterGlobals.ts +8 -2
- package/templates/react-app/src/core/registers/index.ts +2 -1
- package/templates/react-app/src/main.tsx +4 -1
- package/templates/react-app/src/pages/auth/Layout.tsx +4 -3
- package/templates/react-app/src/pages/auth/Login.tsx +5 -3
- package/templates/react-app/src/pages/base/ErrorIdentifier.tsx +39 -0
- package/templates/react-app/src/pages/base/Executor.tsx +31 -10
- package/templates/react-app/src/pages/base/Home.tsx +58 -30
- package/templates/react-app/src/pages/base/JSONStorage.tsx +2 -4
- package/templates/react-app/src/pages/base/Request.tsx +318 -73
- package/templates/react-app/src/pages/base/components/BaseHeader.tsx +2 -2
- package/templates/react-app/src/{components → uikit/components}/RouterRenderComponent.tsx +1 -1
- package/templates/react-app/src/{components → uikit/components}/ThemeSwitcher.tsx +6 -6
- package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +6 -3
- package/templates/react-app/src/uikit/controllers/ExecutorController.ts +52 -22
- package/templates/react-app/src/uikit/controllers/JSONStorageController.ts +7 -3
- package/templates/react-app/src/uikit/controllers/RequestController.ts +65 -11
- package/templates/react-app/src/uikit/controllers/RouterController.ts +8 -7
- package/templates/react-app/src/uikit/controllers/UserController.ts +48 -54
- package/templates/react-app/src/uikit/hooks/useLanguageGuard.ts +1 -2
- package/templates/react-app/src/uikit/providers/ProcessProvider.tsx +4 -4
- package/templates/react-app/src/uikit/styles/css/index.css +1 -1
- package/templates/react-app/src/uikit/styles/css/page.css +1 -1
- package/templates/react-app/tailwind.config.js +2 -2
- package/templates/react-app/tsconfig.json +0 -1
- package/templates/react-app/tsconfig.node.json +1 -2
- package/templates/react-app/vite.config.ts +21 -26
- package/templates/react-app/.env.local +0 -24
- package/templates/react-app/lib/bootstrap/Bootstrap.ts +0 -36
- package/templates/react-app/lib/bootstrap/BootstrapExecutorPlugin.ts +0 -20
- package/templates/react-app/lib/bootstrap/IOCContainerInterface.ts +0 -33
- package/templates/react-app/lib/bootstrap/IOCManagerInterface.ts +0 -12
- package/templates/react-app/lib/bootstrap/index.ts +0 -7
- package/templates/react-app/lib/bootstrap/plugins/InjectEnv.ts +0 -61
- package/templates/react-app/lib/bootstrap/plugins/InjectGlobal.ts +0 -36
- package/templates/react-app/lib/bootstrap/plugins/InjectIOC.ts +0 -24
- package/templates/react-app/lib/env-config/injectPkgConfig.ts +0 -11
- package/templates/react-app/lib/fe-react-controller/FeController.ts +0 -19
- package/templates/react-app/lib/fe-react-controller/index.ts +0 -2
- package/templates/react-app/lib/fe-react-controller/useController.ts +0 -71
- package/templates/react-app/lib/fe-react-theme/ThemeController.ts +0 -40
- package/templates/react-app/lib/fe-react-theme/ThemeStateGetter.ts +0 -53
- package/templates/react-app/lib/fe-react-theme/index.ts +0 -3
- package/templates/react-app/lib/fe-react-theme/type.ts +0 -21
- package/templates/react-app/lib/openAiApi/OpenAIAuthPlugin.ts +0 -29
- package/templates/react-app/lib/openAiApi/OpenAIClient.ts +0 -51
- package/templates/react-app/lib/openAiApi/StreamProcessor.ts +0 -81
- package/templates/react-app/lib/openAiApi/index.ts +0 -3
- package/templates/react-app/lib/request-common-plugin/index.ts +0 -169
- package/templates/react-app/lib/router-loader/Page.ts +0 -68
- package/templates/react-app/lib/tw-root10px/index.css +0 -4
- package/templates/react-app/src/base/apis/feApi/FeApiMockPlugin.ts +0 -44
- package/templates/react-app/src/base/apis/feApi/index.ts +0 -2
- package/templates/react-app/src/base/cases/UserToken.ts +0 -47
- package/templates/react-app/src/base/consts/IOCIdentifier.ts +0 -16
- package/templates/react-app/src/base/port/IOCFunctionInterface.ts +0 -39
- package/templates/react-app/src/base/port/StorageTokenInterface.ts +0 -27
- package/templates/react-app/src/core/AppIOCContainer.ts +0 -30
- package/templates/react-app/src/uikit/utils/RequestLogger.ts +0 -37
- package/templates/react-app/src/uikit/utils/datetime.ts +0 -30
- package/templates/react-app/src/uikit/utils/thread.ts +0 -3
- /package/templates/react-app/lib/{tw-root10px/index.js → tailwind/root10px.js} +0 -0
- /package/templates/react-app/lib/{fe-react-theme/tw-generator.js → tailwind/theme-generator.js} +0 -0
- /package/templates/react-app/src/{components → uikit/components}/Loading.tsx +0 -0
- /package/templates/react-app/src/{components → uikit/components}/LocaleLink.tsx +0 -0
|
@@ -1,94 +1,339 @@
|
|
|
1
1
|
import { IOC } from '@/core/IOC';
|
|
2
|
-
import { useControllerState } from '@lib/fe-react-controller';
|
|
3
2
|
import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
|
|
4
3
|
import { JSONStorageController } from '@/uikit/controllers/JSONStorageController';
|
|
5
4
|
import { RequestController } from '@/uikit/controllers/RequestController';
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
import { useSliceStore } from '@qlover/slice-store-react';
|
|
7
|
+
|
|
8
|
+
function JSONValue({ value }: { value: unknown }) {
|
|
9
|
+
const output = useMemo(() => {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.stringify(value, null, 2);
|
|
12
|
+
} catch {
|
|
13
|
+
return 'Invalid JSON';
|
|
14
|
+
}
|
|
15
|
+
}, [value]);
|
|
16
|
+
return (
|
|
17
|
+
<pre className="mt-1 text-sm text-gray-600 font-mono bg-gray-50 p-2 rounded overflow-x-auto">
|
|
18
|
+
{output}
|
|
19
|
+
</pre>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
6
22
|
|
|
7
23
|
export default function Request() {
|
|
8
24
|
const requestController = IOC(RequestController);
|
|
9
|
-
const requestControllerState =
|
|
10
|
-
const jsonStorageControllerState =
|
|
11
|
-
IOC(JSONStorageController)
|
|
12
|
-
);
|
|
25
|
+
const requestControllerState = useSliceStore(requestController);
|
|
26
|
+
const jsonStorageControllerState = useSliceStore(IOC(JSONStorageController));
|
|
13
27
|
const { t } = useBaseRoutePage();
|
|
14
28
|
|
|
15
29
|
return (
|
|
16
|
-
<div className="min-h-screen bg-gray-
|
|
17
|
-
<div className="
|
|
18
|
-
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
className="
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
{requestControllerState.helloState.loading ? 'Loading...' : 'Hello'}
|
|
27
|
-
</button>
|
|
28
|
-
<div className="mt-4">
|
|
29
|
-
{t('helloResult')}:{' '}
|
|
30
|
-
{JSON.stringify(requestControllerState.helloState.result)}
|
|
31
|
-
</div>
|
|
32
|
-
<div className="mt-4">
|
|
33
|
-
{t('helloError')}:{' '}
|
|
34
|
-
{JSON.stringify(requestControllerState.helloState.error)}
|
|
30
|
+
<div className="min-h-screen bg-gray-50 py-8 px-4 sm:px-6 lg:px-8">
|
|
31
|
+
<div className="max-w-4xl mx-auto space-y-6">
|
|
32
|
+
{/* 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
|
+
</h2>
|
|
37
|
+
<div className="text-sm text-gray-600 font-mono bg-gray-50 p-2 rounded">
|
|
38
|
+
{jsonStorageControllerState.requestTimeout}
|
|
39
|
+
</div>
|
|
35
40
|
</div>
|
|
36
|
-
</div>
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
<
|
|
40
|
-
className="
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
{/* 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
|
+
</h2>
|
|
47
|
+
|
|
48
|
+
<p className="text-sm text-gray-600 mb-4">
|
|
49
|
+
函数式 api, 使用了 FetchURLPlugin, RequestCommonPlugin,
|
|
50
|
+
ApiMockPlugin, RequestLogger 插件
|
|
51
|
+
</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
|
+
onClick={requestController.onHello}
|
|
55
|
+
>
|
|
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>
|
|
84
|
+
|
|
85
|
+
<div className="mt-4 space-y-2">
|
|
86
|
+
<div>
|
|
87
|
+
<p className="text-sm font-medium text-gray-700">
|
|
88
|
+
{t('helloResult')}:
|
|
89
|
+
</p>
|
|
90
|
+
<JSONValue value={requestControllerState.helloState.result} />
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div>
|
|
94
|
+
<p className="text-sm font-medium text-gray-700">
|
|
95
|
+
{t('helloError')}:
|
|
96
|
+
</p>
|
|
97
|
+
<JSONValue value={requestControllerState.helloState.error} />
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
50
100
|
</div>
|
|
51
|
-
</div>
|
|
52
101
|
|
|
53
|
-
|
|
54
|
-
<
|
|
55
|
-
className="
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
102
|
+
{/* 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
|
|
106
|
+
</h2>
|
|
107
|
+
|
|
108
|
+
<p className="text-sm text-gray-600 mb-4">
|
|
109
|
+
RequestScheduler 类式 api, 使用了 FetchURLPlugin,
|
|
110
|
+
RequestCommonPlugin, RequestLogger, ApiPickDataPlugin 插件, 其中
|
|
111
|
+
ApiPickDataPlugin 插件可以将返回类型统一扁平到 data 字段
|
|
112
|
+
</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"
|
|
115
|
+
onClick={requestController.onIpInfo}
|
|
116
|
+
>
|
|
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>
|
|
145
|
+
|
|
146
|
+
<div className="mt-4">
|
|
147
|
+
<p className="text-sm font-medium text-gray-700">
|
|
148
|
+
{t('ipInfoResult')}:
|
|
149
|
+
</p>
|
|
150
|
+
<JSONValue value={requestControllerState.ipInfoState.result} />
|
|
151
|
+
</div>
|
|
65
152
|
</div>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
153
|
+
|
|
154
|
+
{/* 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
|
|
158
|
+
</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 字段
|
|
164
|
+
</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"
|
|
167
|
+
onClick={requestController.onRandomUser}
|
|
168
|
+
>
|
|
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>
|
|
197
|
+
|
|
198
|
+
<div className="mt-4 space-y-2">
|
|
199
|
+
<div>
|
|
200
|
+
<p className="text-sm font-medium text-gray-700">
|
|
201
|
+
{t('randomUserResult')}:
|
|
202
|
+
</p>
|
|
203
|
+
<JSONValue
|
|
204
|
+
value={requestControllerState.randomUserState.result}
|
|
205
|
+
/>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<div>
|
|
209
|
+
<p className="text-sm font-medium text-gray-700">
|
|
210
|
+
{t('randomUserError')}:
|
|
211
|
+
</p>
|
|
212
|
+
<JSONValue value={requestControllerState.randomUserState.error} />
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
69
215
|
</div>
|
|
70
|
-
</div>
|
|
71
216
|
|
|
72
|
-
|
|
73
|
-
<
|
|
74
|
-
className="
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
217
|
+
{/* 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
|
|
221
|
+
</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 ${
|
|
224
|
+
requestControllerState.apiCatchResultState.loading
|
|
225
|
+
? 'bg-red-600 hover:bg-red-700'
|
|
226
|
+
: 'bg-blue-600 hover:bg-blue-700'
|
|
227
|
+
}`}
|
|
228
|
+
onClick={requestController.onTriggerApiCatchResult}
|
|
229
|
+
>
|
|
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>
|
|
258
|
+
|
|
259
|
+
<div className="mt-4 space-y-2">
|
|
260
|
+
<div>
|
|
261
|
+
<p className="text-sm font-medium text-gray-700">
|
|
262
|
+
{t('abortRequestResult')}:
|
|
263
|
+
</p>
|
|
264
|
+
<JSONValue
|
|
265
|
+
value={requestControllerState.apiCatchResultState.result}
|
|
266
|
+
/>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<div>
|
|
270
|
+
<p className="text-sm font-medium text-gray-700">
|
|
271
|
+
{t('abortRequestError')}:
|
|
272
|
+
</p>
|
|
273
|
+
<JSONValue
|
|
274
|
+
value={requestControllerState.apiCatchResultState.error}
|
|
275
|
+
/>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
88
278
|
</div>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
279
|
+
|
|
280
|
+
{/* 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
|
|
284
|
+
</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
|
+
}`}
|
|
291
|
+
onClick={requestController.onTriggerAbortRequest}
|
|
292
|
+
>
|
|
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>
|
|
321
|
+
|
|
322
|
+
<div className="mt-4 space-y-2">
|
|
323
|
+
<div>
|
|
324
|
+
<p className="text-sm font-medium text-gray-700">
|
|
325
|
+
{t('abortRequestResult')}:
|
|
326
|
+
</p>
|
|
327
|
+
<JSONValue value={requestControllerState.abortState.result} />
|
|
328
|
+
</div>
|
|
329
|
+
|
|
330
|
+
<div>
|
|
331
|
+
<p className="text-sm font-medium text-gray-700">
|
|
332
|
+
{t('abortRequestError')}:
|
|
333
|
+
</p>
|
|
334
|
+
<JSONValue value={requestControllerState.abortState.error} />
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
92
337
|
</div>
|
|
93
338
|
</div>
|
|
94
339
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import ThemeSwitcher from '@/components/ThemeSwitcher';
|
|
2
|
-
import LocaleLink from '@/components/LocaleLink';
|
|
1
|
+
import ThemeSwitcher from '@/uikit/components/ThemeSwitcher';
|
|
2
|
+
import LocaleLink from '@/uikit/components/LocaleLink';
|
|
3
3
|
|
|
4
4
|
export default function BaseHeader() {
|
|
5
5
|
return (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Suspense } from 'react';
|
|
2
2
|
import { Loading } from './Loading';
|
|
3
|
-
import { RouterLoaderRender } from '@lib/router-loader/RouterLoader';
|
|
4
3
|
import BaseRouteProvider from '@/uikit/providers/BaseRouteProvider';
|
|
4
|
+
import { RouterLoaderRender } from '@/base/cases/router-loader';
|
|
5
5
|
|
|
6
6
|
export const RouterRenderComponent: RouterLoaderRender = (route) => {
|
|
7
7
|
const Component = route.element();
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { IOC } from '@/core/IOC';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { ThemeService } from '@qlover/corekit-bridge';
|
|
3
|
+
import { useSliceStore } from '@qlover/slice-store-react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
export default function ThemeSwitcher() {
|
|
7
|
-
const
|
|
8
|
-
const { theme } =
|
|
9
|
-
const themes =
|
|
7
|
+
const themeService = IOC(ThemeService);
|
|
8
|
+
const { theme } = useSliceStore(themeService);
|
|
9
|
+
const themes = themeService.getSupportedThemes();
|
|
10
10
|
const { t } = useTranslation('common');
|
|
11
11
|
|
|
12
12
|
return (
|
|
@@ -17,7 +17,7 @@ export default function ThemeSwitcher() {
|
|
|
17
17
|
<select
|
|
18
18
|
id="theme-select"
|
|
19
19
|
value={theme}
|
|
20
|
-
onChange={(e) =>
|
|
20
|
+
onChange={(e) => themeService.changeTheme(e.target.value)}
|
|
21
21
|
>
|
|
22
22
|
{themes.map((theme) => (
|
|
23
23
|
<option key={theme} value={theme}>
|
|
@@ -4,10 +4,13 @@ import { BasePageProvider } from '@/base/types/Page';
|
|
|
4
4
|
import { RouteMeta } from '@/base/types/Page';
|
|
5
5
|
import { createContext } from 'react';
|
|
6
6
|
import merge from 'lodash/merge';
|
|
7
|
-
import
|
|
7
|
+
import i18nConfig from '@config/i18n';
|
|
8
|
+
import { WITHIN_PAGE_PROVIDER } from '@config/ErrorIdentifier';
|
|
9
|
+
|
|
10
|
+
const { defaultNS } = i18nConfig;
|
|
8
11
|
|
|
9
12
|
const defaultBaseRoutemeta = {
|
|
10
|
-
localNamespace:
|
|
13
|
+
localNamespace: defaultNS,
|
|
11
14
|
title: '',
|
|
12
15
|
icon: ''
|
|
13
16
|
};
|
|
@@ -18,7 +21,7 @@ export function useBaseRoutePage(): BasePageProvider {
|
|
|
18
21
|
const meta = useContext(BaseRoutePageContext);
|
|
19
22
|
|
|
20
23
|
if (!meta) {
|
|
21
|
-
throw new Error(
|
|
24
|
+
throw new Error(WITHIN_PAGE_PROVIDER);
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
const _meta = useMemo(() => merge({}, defaultBaseRoutemeta, meta), [meta]);
|
|
@@ -1,24 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { FeApi } from '@/base/apis/feApi';
|
|
1
|
+
import { FeApi } from '@/base/apis/feApi/FeApi';
|
|
3
2
|
import cloneDeep from 'lodash/cloneDeep';
|
|
4
3
|
import {
|
|
5
4
|
ExecutorPlugin,
|
|
6
5
|
RequestAdapterResponse,
|
|
7
6
|
RequestAdapterFetchConfig
|
|
8
|
-
} from '@qlover/fe-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
} from '@qlover/fe-corekit';
|
|
8
|
+
import { inject, injectable } from 'inversify';
|
|
9
|
+
import { SliceStore } from '@qlover/slice-store-react';
|
|
10
|
+
|
|
11
|
+
class ExecutorControllerState {
|
|
12
|
+
helloState = {
|
|
13
|
+
loading: false,
|
|
14
|
+
result: null as Record<string, unknown> | null,
|
|
15
|
+
error: null as unknown
|
|
17
16
|
};
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
export type ExecutorControllerState = ReturnType<typeof createDefaultState>;
|
|
21
|
-
|
|
22
19
|
const TestPlugin: ExecutorPlugin<RequestAdapterFetchConfig> = {
|
|
23
20
|
pluginName: 'test',
|
|
24
21
|
async onSuccess({ parameters, returnValue }) {
|
|
@@ -33,24 +30,57 @@ const TestPlugin: ExecutorPlugin<RequestAdapterFetchConfig> = {
|
|
|
33
30
|
}
|
|
34
31
|
};
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
@injectable()
|
|
34
|
+
export class ExecutorController extends SliceStore<ExecutorControllerState> {
|
|
35
|
+
selector = {
|
|
36
|
+
helloState: (state: ExecutorControllerState) => state.helloState
|
|
37
|
+
};
|
|
38
38
|
|
|
39
|
-
constructor(feApi: FeApi) {
|
|
40
|
-
super(
|
|
39
|
+
constructor(@inject(FeApi) private feApi: FeApi) {
|
|
40
|
+
super(ExecutorController.create);
|
|
41
41
|
|
|
42
|
+
// FIXME: not cloneDeep, create a new instance
|
|
42
43
|
this.feApi = cloneDeep(feApi);
|
|
43
44
|
|
|
44
45
|
this.feApi.usePlugin(TestPlugin);
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
static create(): ExecutorControllerState {
|
|
49
|
+
return new ExecutorControllerState();
|
|
50
|
+
}
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
onTestPlugins = async () => {
|
|
53
|
+
this.emit({
|
|
54
|
+
...this.state,
|
|
55
|
+
helloState: {
|
|
56
|
+
loading: true,
|
|
57
|
+
result: null,
|
|
58
|
+
error: null
|
|
59
|
+
}
|
|
52
60
|
});
|
|
53
61
|
|
|
54
|
-
|
|
62
|
+
try {
|
|
63
|
+
const res = await this.feApi.get('/api/v1/executor/test', {
|
|
64
|
+
responseType: 'text'
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
this.emit({
|
|
68
|
+
...this.state,
|
|
69
|
+
helloState: {
|
|
70
|
+
loading: false,
|
|
71
|
+
result: res,
|
|
72
|
+
error: null
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
} catch (error) {
|
|
76
|
+
this.emit({
|
|
77
|
+
...this.state,
|
|
78
|
+
helloState: {
|
|
79
|
+
loading: false,
|
|
80
|
+
result: null,
|
|
81
|
+
error
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
55
85
|
};
|
|
56
86
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { JSONStorage } from '@qlover/fe-corekit';
|
|
2
|
+
import { SliceStore } from '@qlover/slice-store-react';
|
|
3
3
|
import { random } from 'lodash';
|
|
4
4
|
|
|
5
5
|
interface JSONStoragePageState {
|
|
@@ -9,7 +9,11 @@ interface JSONStoragePageState {
|
|
|
9
9
|
requestTimeout: number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export class JSONStorageController extends
|
|
12
|
+
export class JSONStorageController extends SliceStore<JSONStoragePageState> {
|
|
13
|
+
selector = {
|
|
14
|
+
requestTimeout: (state: JSONStoragePageState) => state.requestTimeout
|
|
15
|
+
};
|
|
16
|
+
|
|
13
17
|
constructor(private storage: JSONStorage) {
|
|
14
18
|
super(() => ({
|
|
15
19
|
testKey1: storage.getItem('testKey1') ?? 0,
|