@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.
- package/CHANGELOG.md +88 -0
- package/package.json +3 -3
- package/templates/react-app/config/Identifier.I18n.ts +878 -0
- package/templates/react-app/config/app.router.json +7 -7
- package/templates/react-app/config/theme.json +7 -88
- package/templates/react-app/package.json +7 -3
- package/templates/react-app/postcss.config.js +1 -2
- package/templates/react-app/public/locales/en/common.json +118 -1
- package/templates/react-app/public/locales/zh/common.json +118 -1
- package/templates/react-app/src/App.tsx +14 -2
- package/templates/react-app/src/base/cases/RequestLogger.ts +1 -1
- package/templates/react-app/src/base/services/I18nService.ts +31 -3
- package/templates/react-app/src/base/services/ProcesserService.ts +0 -1
- package/templates/react-app/src/core/IOC.ts +12 -7
- package/templates/react-app/src/core/bootstrap.ts +42 -53
- 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/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 +13 -13
- package/templates/react-app/src/core/registers/index.ts +27 -12
- 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 +5 -2
- package/templates/react-app/src/pages/base/ErrorIdentifier.tsx +38 -19
- package/templates/react-app/src/pages/base/Executor.tsx +447 -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/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 +200 -0
- package/templates/react-app/src/styles/css/themes/dark.css +154 -0
- package/templates/react-app/src/styles/css/themes/index.css +3 -0
- package/templates/react-app/src/styles/css/themes/pink.css +160 -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/ThemeSwitcher.tsx +63 -13
- package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +1 -1
- package/templates/react-app/src/uikit/controllers/UserController.ts +1 -1
- package/templates/react-app/tailwind.config.js +1 -15
- package/templates/react-app/vite.config.ts +7 -1
- 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/port/InversifyIocInterface.ts +0 -9
- 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
- /package/templates/react-app/src/{uikit/styles → styles}/css/index.css +0 -0
|
@@ -1,38 +1,57 @@
|
|
|
1
|
+
import { Button } from 'antd';
|
|
1
2
|
import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
|
|
2
|
-
import * as ErrorIdentifierList from '@config/
|
|
3
|
+
import * as ErrorIdentifierList from '@config/Identifier.Error';
|
|
4
|
+
import * as i18nKeys from '@config/Identifier.I18n';
|
|
3
5
|
|
|
4
6
|
export default function ErrorIdentifier() {
|
|
5
7
|
const { t } = useBaseRoutePage();
|
|
6
8
|
|
|
7
9
|
return (
|
|
8
|
-
<div className="min-h-screen bg-
|
|
9
|
-
<div className="max-w-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
<div className="min-h-screen bg-primary py-8 px-4 sm:px-6 lg:px-8">
|
|
11
|
+
<div className="max-w-4xl mx-auto space-y-6">
|
|
12
|
+
{/* Header Section */}
|
|
13
|
+
<section className="py-8">
|
|
14
|
+
<div className="text-center">
|
|
15
|
+
<h1 className="text-4xl md:text-5xl font-bold mb-6 text-text">
|
|
16
|
+
{t(i18nKeys.PAGE_ERROR_IDENTIFIER_MAIN_TITLE)}
|
|
14
17
|
</h1>
|
|
15
|
-
<p className="text-
|
|
16
|
-
|
|
18
|
+
<p className="text-xl text-text-secondary mb-8">
|
|
19
|
+
{t(i18nKeys.PAGE_ERROR_IDENTIFIER_SOURCE_DESCRIPTION)}
|
|
17
20
|
</p>
|
|
18
21
|
</div>
|
|
22
|
+
</section>
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
{/* Error Identifier List */}
|
|
25
|
+
<div className="grid gap-4">
|
|
26
|
+
{Object.entries(ErrorIdentifierList).map(([key, value]) => (
|
|
27
|
+
<div
|
|
28
|
+
key={key}
|
|
29
|
+
className="bg-secondary shadow sm:rounded-lg p-6 border border-border hover:bg-elevated transition-colors duration-200"
|
|
30
|
+
>
|
|
31
|
+
<div className="flex flex-col sm:flex-row sm:items-center justify-between">
|
|
32
|
+
<span className="font-medium text-text mb-2 sm:mb-0">
|
|
27
33
|
{key}
|
|
28
34
|
</span>
|
|
29
|
-
<span className="
|
|
35
|
+
<span className="text-sm text-text-secondary bg-primary px-3 py-1 rounded-md">
|
|
30
36
|
{t(value)}
|
|
31
37
|
</span>
|
|
32
38
|
</div>
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
</div>
|
|
40
|
+
))}
|
|
35
41
|
</div>
|
|
42
|
+
|
|
43
|
+
{/* Call to Action Section */}
|
|
44
|
+
<section className="py-8 text-center">
|
|
45
|
+
<h2 className="text-2xl font-bold mb-4 text-text">
|
|
46
|
+
{t(i18nKeys.PAGE_ERROR_IDENTIFIER_HELP_TITLE)}
|
|
47
|
+
</h2>
|
|
48
|
+
<p className="text-lg text-text-secondary mb-6">
|
|
49
|
+
{t(i18nKeys.PAGE_ERROR_IDENTIFIER_HELP_DESCRIPTION)}
|
|
50
|
+
</p>
|
|
51
|
+
<Button type="primary" size="large">
|
|
52
|
+
{t(i18nKeys.PAGE_ERROR_IDENTIFIER_CONTACT_SUPPORT)}
|
|
53
|
+
</Button>
|
|
54
|
+
</section>
|
|
36
55
|
</div>
|
|
37
56
|
</div>
|
|
38
57
|
);
|
|
@@ -1,10 +1,36 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
Progress,
|
|
4
|
+
Tag,
|
|
5
|
+
Space,
|
|
6
|
+
Card,
|
|
7
|
+
message,
|
|
8
|
+
Input,
|
|
9
|
+
Select
|
|
10
|
+
} from 'antd';
|
|
2
11
|
import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
|
|
12
|
+
import { useState, useEffect } from 'react';
|
|
13
|
+
import { IOC } from '@/core/IOC';
|
|
3
14
|
import { JSONStorageController } from '@/uikit/controllers/JSONStorageController';
|
|
4
15
|
import { ExecutorController } from '@/uikit/controllers/ExecutorController';
|
|
5
16
|
import { useSliceStore } from '@qlover/slice-store-react';
|
|
17
|
+
import * as i18nKeys from '@config/Identifier.I18n';
|
|
18
|
+
|
|
19
|
+
interface Task {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
23
|
+
progress: number;
|
|
24
|
+
type: 'data-sync' | 'report-generation' | 'system-maintenance' | 'backup';
|
|
25
|
+
startTime?: Date;
|
|
26
|
+
endTime?: Date;
|
|
27
|
+
responseType?: 'text' | 'json' | 'blob';
|
|
28
|
+
url?: string;
|
|
29
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
30
|
+
}
|
|
6
31
|
|
|
7
32
|
export default function Executor() {
|
|
33
|
+
const { t } = useBaseRoutePage();
|
|
8
34
|
const executorController = IOC(ExecutorController);
|
|
9
35
|
const jSONStorageController = IOC(JSONStorageController);
|
|
10
36
|
const requestTimeout = useSliceStore(
|
|
@@ -17,46 +43,438 @@ export default function Executor() {
|
|
|
17
43
|
executorController.selector.helloState
|
|
18
44
|
);
|
|
19
45
|
|
|
20
|
-
const
|
|
46
|
+
const [tasks, setTasks] = useState<Task[]>([
|
|
47
|
+
{
|
|
48
|
+
id: '1',
|
|
49
|
+
name: t(i18nKeys.PAGE_EXECUTOR_TEST_PLUGIN_TITLE),
|
|
50
|
+
status: 'pending',
|
|
51
|
+
progress: 0,
|
|
52
|
+
type: 'data-sync',
|
|
53
|
+
responseType: 'text',
|
|
54
|
+
url: '/api/v1/executor/test',
|
|
55
|
+
method: 'GET'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: '2',
|
|
59
|
+
name: t(i18nKeys.PAGE_EXECUTOR_TASK_TYPE_DATA_SYNC),
|
|
60
|
+
status: 'pending',
|
|
61
|
+
progress: 0,
|
|
62
|
+
type: 'data-sync',
|
|
63
|
+
responseType: 'json',
|
|
64
|
+
url: '/api/v1/data/sync',
|
|
65
|
+
method: 'POST'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: '3',
|
|
69
|
+
name: t(i18nKeys.PAGE_EXECUTOR_TASK_TYPE_MAINTENANCE),
|
|
70
|
+
status: 'pending',
|
|
71
|
+
progress: 0,
|
|
72
|
+
type: 'system-maintenance',
|
|
73
|
+
responseType: 'json',
|
|
74
|
+
url: '/api/v1/system/maintenance',
|
|
75
|
+
method: 'PUT'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: '4',
|
|
79
|
+
name: t(i18nKeys.PAGE_EXECUTOR_TASK_TYPE_BACKUP),
|
|
80
|
+
status: 'pending',
|
|
81
|
+
progress: 0,
|
|
82
|
+
type: 'backup',
|
|
83
|
+
responseType: 'blob',
|
|
84
|
+
url: '/api/v1/data/backup',
|
|
85
|
+
method: 'GET'
|
|
86
|
+
}
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
const [customUrl, setCustomUrl] = useState('');
|
|
90
|
+
const [customMethod, setCustomMethod] = useState<
|
|
91
|
+
'GET' | 'POST' | 'PUT' | 'DELETE'
|
|
92
|
+
>('GET');
|
|
93
|
+
const [customResponseType, setCustomResponseType] = useState<
|
|
94
|
+
'text' | 'json' | 'blob'
|
|
95
|
+
>('text');
|
|
96
|
+
|
|
97
|
+
// 监听 helloState 变化,更新任务状态
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (helloState.result) {
|
|
100
|
+
message.success(t(i18nKeys.PAGE_EXECUTOR_PLUGIN_TEST_SUCCESS));
|
|
101
|
+
// 更新任务状态
|
|
102
|
+
setTasks((prevTasks) =>
|
|
103
|
+
prevTasks.map((task) =>
|
|
104
|
+
task.id === '1'
|
|
105
|
+
? {
|
|
106
|
+
...task,
|
|
107
|
+
status: 'completed',
|
|
108
|
+
progress: 100,
|
|
109
|
+
endTime: new Date()
|
|
110
|
+
}
|
|
111
|
+
: task
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
} else if (helloState.error) {
|
|
115
|
+
message.error(t(i18nKeys.PAGE_EXECUTOR_PLUGIN_TEST_FAILURE));
|
|
116
|
+
// 更新任务状态
|
|
117
|
+
setTasks((prevTasks) =>
|
|
118
|
+
prevTasks.map((task) =>
|
|
119
|
+
task.id === '1'
|
|
120
|
+
? { ...task, status: 'failed', endTime: new Date() }
|
|
121
|
+
: task
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}, [helloState.result, helloState.error, t]);
|
|
126
|
+
|
|
127
|
+
const getStatusColor = (status: Task['status']) => {
|
|
128
|
+
switch (status) {
|
|
129
|
+
case 'running':
|
|
130
|
+
return 'active';
|
|
131
|
+
case 'completed':
|
|
132
|
+
return 'success';
|
|
133
|
+
case 'failed':
|
|
134
|
+
return 'exception';
|
|
135
|
+
default:
|
|
136
|
+
return 'normal';
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const getTypeColor = (type: Task['type']) => {
|
|
141
|
+
switch (type) {
|
|
142
|
+
case 'data-sync':
|
|
143
|
+
return 'blue';
|
|
144
|
+
case 'report-generation':
|
|
145
|
+
return 'green';
|
|
146
|
+
case 'system-maintenance':
|
|
147
|
+
return 'orange';
|
|
148
|
+
case 'backup':
|
|
149
|
+
return 'purple';
|
|
150
|
+
default:
|
|
151
|
+
return 'default';
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const formatDuration = (startTime?: Date, endTime?: Date) => {
|
|
156
|
+
if (!startTime) return '-';
|
|
157
|
+
const end = endTime || new Date();
|
|
158
|
+
const duration = end.getTime() - startTime.getTime();
|
|
159
|
+
const minutes = Math.floor(duration / (1000 * 60));
|
|
160
|
+
return `${minutes} ${t(i18nKeys.PAGE_EXECUTOR_TASK_DURATION_UNIT)}`;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const handleStartTask = async (taskId: string) => {
|
|
164
|
+
const task = tasks.find((t) => t.id === taskId);
|
|
165
|
+
if (!task) return;
|
|
166
|
+
|
|
167
|
+
setTasks((prevTasks) =>
|
|
168
|
+
prevTasks.map((t) =>
|
|
169
|
+
t.id === taskId ? { ...t, status: 'running', startTime: new Date() } : t
|
|
170
|
+
)
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
await executorController.onTestPlugins();
|
|
175
|
+
|
|
176
|
+
setTasks((prevTasks) =>
|
|
177
|
+
prevTasks.map((t) =>
|
|
178
|
+
t.id === taskId
|
|
179
|
+
? { ...t, status: 'completed', progress: 100, endTime: new Date() }
|
|
180
|
+
: t
|
|
181
|
+
)
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
message.success(
|
|
185
|
+
t(i18nKeys.PAGE_EXECUTOR_TASK_SUCCESS, { name: task.name })
|
|
186
|
+
);
|
|
187
|
+
} catch {
|
|
188
|
+
setTasks((prevTasks) =>
|
|
189
|
+
prevTasks.map((t) =>
|
|
190
|
+
t.id === taskId ? { ...t, status: 'failed', endTime: new Date() } : t
|
|
191
|
+
)
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
message.error(
|
|
195
|
+
t(i18nKeys.PAGE_EXECUTOR_TASK_FAILURE, { name: task.name })
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const handleStopTask = (taskId: string) => {
|
|
201
|
+
setTasks((prevTasks) =>
|
|
202
|
+
prevTasks.map((task) =>
|
|
203
|
+
task.id === taskId
|
|
204
|
+
? { ...task, status: 'failed', endTime: new Date() }
|
|
205
|
+
: task
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const handleCreateTask = () => {
|
|
211
|
+
if (!customUrl) {
|
|
212
|
+
message.error(t(i18nKeys.PAGE_EXECUTOR_CUSTOM_TASK_URL_REQUIRED));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const newTask: Task = {
|
|
217
|
+
id: Date.now().toString(),
|
|
218
|
+
name: t(i18nKeys.PAGE_EXECUTOR_CUSTOM_TASK_NAME, {
|
|
219
|
+
method: customMethod,
|
|
220
|
+
url: customUrl
|
|
221
|
+
}),
|
|
222
|
+
status: 'pending',
|
|
223
|
+
progress: 0,
|
|
224
|
+
type: 'data-sync',
|
|
225
|
+
responseType: customResponseType,
|
|
226
|
+
url: customUrl,
|
|
227
|
+
method: customMethod
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
setTasks((prev) => [...prev, newTask]);
|
|
231
|
+
setCustomUrl('');
|
|
232
|
+
setCustomMethod('GET');
|
|
233
|
+
setCustomResponseType('text');
|
|
234
|
+
};
|
|
21
235
|
|
|
22
236
|
return (
|
|
23
|
-
<div className="min-h-screen bg-
|
|
24
|
-
<div className="
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
237
|
+
<div className="min-h-screen bg-primary py-8 px-4 sm:px-6 lg:px-8">
|
|
238
|
+
<div className="max-w-6xl mx-auto space-y-6">
|
|
239
|
+
{/* Header Section */}
|
|
240
|
+
<section className="py-8">
|
|
241
|
+
<div className="text-center">
|
|
242
|
+
<h1 className="text-4xl md:text-5xl font-bold mb-6 text-text">
|
|
243
|
+
{t(i18nKeys.PAGE_EXECUTOR_MAIN_TITLE)}
|
|
244
|
+
</h1>
|
|
245
|
+
<p className="text-xl text-text-secondary mb-8">
|
|
246
|
+
{t(i18nKeys.PAGE_EXECUTOR_DESCRIPTION)}
|
|
247
|
+
</p>
|
|
248
|
+
</div>
|
|
249
|
+
</section>
|
|
250
|
+
|
|
251
|
+
{/* Test Plugin Section */}
|
|
252
|
+
<section className="bg-secondary shadow sm:rounded-lg p-6 border border-border">
|
|
253
|
+
<h2 className="text-xl font-medium text-text mb-4">
|
|
254
|
+
{t(i18nKeys.PAGE_EXECUTOR_TEST_PLUGIN_TITLE)}
|
|
255
|
+
</h2>
|
|
256
|
+
<div className="space-y-4">
|
|
257
|
+
<div className="text-text-secondary">
|
|
258
|
+
{t(i18nKeys.PAGE_REQUEST_TIMEOUT)}: {requestTimeout}
|
|
259
|
+
</div>
|
|
260
|
+
<div>
|
|
41
261
|
{helloState.loading ? (
|
|
42
|
-
<div>Loading...</div>
|
|
262
|
+
<div className="text-text-secondary">Loading...</div>
|
|
43
263
|
) : (
|
|
44
|
-
<
|
|
45
|
-
|
|
264
|
+
<Button
|
|
265
|
+
type="primary"
|
|
46
266
|
onClick={executorController.onTestPlugins}
|
|
47
267
|
>
|
|
48
|
-
{t(
|
|
49
|
-
</
|
|
268
|
+
{t(i18nKeys.PAGE_EXECUTOR_TEST_PLUGIN_TITLE)}
|
|
269
|
+
</Button>
|
|
50
270
|
)}
|
|
51
|
-
|
|
271
|
+
</div>
|
|
272
|
+
<div className="bg-elevated p-4 rounded-lg">
|
|
52
273
|
{helloState.error instanceof Error ? (
|
|
53
|
-
<div>{helloState.error.message}</div>
|
|
274
|
+
<div className="text-red-500">{helloState.error.message}</div>
|
|
54
275
|
) : (
|
|
55
|
-
<
|
|
276
|
+
<pre className="text-text-secondary">
|
|
277
|
+
{IOC('JSON').stringify(helloState.result?.data)}
|
|
278
|
+
</pre>
|
|
56
279
|
)}
|
|
57
280
|
</div>
|
|
58
281
|
</div>
|
|
59
|
-
</
|
|
282
|
+
</section>
|
|
283
|
+
|
|
284
|
+
{/* Create Task Section */}
|
|
285
|
+
<section className="bg-secondary shadow sm:rounded-lg p-6 border border-border">
|
|
286
|
+
<h2 className="text-xl font-medium text-text mb-4">
|
|
287
|
+
{t(i18nKeys.PAGE_EXECUTOR_CREATE_TASK_TITLE)}
|
|
288
|
+
</h2>
|
|
289
|
+
<div className="space-y-4">
|
|
290
|
+
<div className="flex items-center space-x-4">
|
|
291
|
+
<Input
|
|
292
|
+
placeholder={t(i18nKeys.PAGE_EXECUTOR_ENTER_URL)}
|
|
293
|
+
value={customUrl}
|
|
294
|
+
onChange={(e) => setCustomUrl(e.target.value)}
|
|
295
|
+
className="flex-1"
|
|
296
|
+
/>
|
|
297
|
+
<Select
|
|
298
|
+
value={customMethod}
|
|
299
|
+
onChange={setCustomMethod}
|
|
300
|
+
style={{ width: 120 }}
|
|
301
|
+
>
|
|
302
|
+
<Select.Option value="GET">GET</Select.Option>
|
|
303
|
+
<Select.Option value="POST">POST</Select.Option>
|
|
304
|
+
<Select.Option value="PUT">PUT</Select.Option>
|
|
305
|
+
<Select.Option value="DELETE">DELETE</Select.Option>
|
|
306
|
+
</Select>
|
|
307
|
+
<Select
|
|
308
|
+
value={customResponseType}
|
|
309
|
+
onChange={setCustomResponseType}
|
|
310
|
+
style={{ width: 120 }}
|
|
311
|
+
>
|
|
312
|
+
<Select.Option value="text">Text</Select.Option>
|
|
313
|
+
<Select.Option value="json">JSON</Select.Option>
|
|
314
|
+
<Select.Option value="blob">Blob</Select.Option>
|
|
315
|
+
</Select>
|
|
316
|
+
<Button type="primary" onClick={handleCreateTask}>
|
|
317
|
+
{t(i18nKeys.PAGE_EXECUTOR_CREATE_BUTTON)}
|
|
318
|
+
</Button>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
</section>
|
|
322
|
+
|
|
323
|
+
{/* Task Statistics */}
|
|
324
|
+
<section className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
325
|
+
<Card className="bg-elevated border-border">
|
|
326
|
+
<div className="text-center">
|
|
327
|
+
<div className="text-2xl font-bold text-text">{tasks.length}</div>
|
|
328
|
+
<div className="text-sm text-text-secondary">
|
|
329
|
+
{t(i18nKeys.PAGE_EXECUTOR_TASK_STATS_TOTAL)}
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
</Card>
|
|
333
|
+
<Card className="bg-elevated border-border">
|
|
334
|
+
<div className="text-center">
|
|
335
|
+
<div className="text-2xl font-bold text-text">
|
|
336
|
+
{tasks.filter((t) => t.status === 'running').length}
|
|
337
|
+
</div>
|
|
338
|
+
<div className="text-sm text-text-secondary">
|
|
339
|
+
{t(i18nKeys.PAGE_EXECUTOR_TASK_STATS_RUNNING)}
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
</Card>
|
|
343
|
+
<Card className="bg-elevated border-border">
|
|
344
|
+
<div className="text-center">
|
|
345
|
+
<div className="text-2xl font-bold text-text">
|
|
346
|
+
{tasks.filter((t) => t.status === 'completed').length}
|
|
347
|
+
</div>
|
|
348
|
+
<div className="text-sm text-text-secondary">
|
|
349
|
+
{t(i18nKeys.PAGE_EXECUTOR_TASK_STATS_COMPLETED)}
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
</Card>
|
|
353
|
+
<Card className="bg-elevated border-border">
|
|
354
|
+
<div className="text-center">
|
|
355
|
+
<div className="text-2xl font-bold text-text">
|
|
356
|
+
{tasks.filter((t) => t.status === 'failed').length}
|
|
357
|
+
</div>
|
|
358
|
+
<div className="text-sm text-text-secondary">
|
|
359
|
+
{t(i18nKeys.PAGE_EXECUTOR_TASK_STATS_FAILED)}
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
</Card>
|
|
363
|
+
</section>
|
|
364
|
+
|
|
365
|
+
{/* Task List Section */}
|
|
366
|
+
<section className="bg-secondary shadow sm:rounded-lg p-6 border border-border">
|
|
367
|
+
<h2 className="text-xl font-medium text-text mb-4">
|
|
368
|
+
{t(i18nKeys.PAGE_EXECUTOR_TASK_LIST_TITLE)}
|
|
369
|
+
</h2>
|
|
370
|
+
<div className="space-y-4">
|
|
371
|
+
{tasks.map((task) => (
|
|
372
|
+
<Card
|
|
373
|
+
key={task.id}
|
|
374
|
+
className="bg-elevated border-border"
|
|
375
|
+
title={
|
|
376
|
+
<div className="flex items-center justify-between">
|
|
377
|
+
<span className="text-text">{task.name}</span>
|
|
378
|
+
<Space>
|
|
379
|
+
<Tag color={getTypeColor(task.type)}>{task.type}</Tag>
|
|
380
|
+
<Tag color={getStatusColor(task.status)}>
|
|
381
|
+
{task.status}
|
|
382
|
+
</Tag>
|
|
383
|
+
</Space>
|
|
384
|
+
</div>
|
|
385
|
+
}
|
|
386
|
+
>
|
|
387
|
+
<div className="space-y-4">
|
|
388
|
+
<div className="grid grid-cols-2 gap-4 text-text-secondary">
|
|
389
|
+
<div>
|
|
390
|
+
<span className="font-medium">URL:</span> {task.url}
|
|
391
|
+
</div>
|
|
392
|
+
<div>
|
|
393
|
+
<span className="font-medium">Method:</span> {task.method}
|
|
394
|
+
</div>
|
|
395
|
+
<div>
|
|
396
|
+
<span className="font-medium">Response Type:</span>{' '}
|
|
397
|
+
{task.responseType}
|
|
398
|
+
</div>
|
|
399
|
+
<div>
|
|
400
|
+
<span className="font-medium">Duration:</span>{' '}
|
|
401
|
+
{formatDuration(task.startTime, task.endTime)}
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
<Progress
|
|
405
|
+
percent={task.progress}
|
|
406
|
+
status={getStatusColor(task.status)}
|
|
407
|
+
/>
|
|
408
|
+
<div className="flex justify-end space-x-2">
|
|
409
|
+
{task.status === 'pending' && (
|
|
410
|
+
<Button
|
|
411
|
+
type="primary"
|
|
412
|
+
onClick={() => handleStartTask(task.id)}
|
|
413
|
+
>
|
|
414
|
+
{t(i18nKeys.PAGE_EXECUTOR_TASK_START)}
|
|
415
|
+
</Button>
|
|
416
|
+
)}
|
|
417
|
+
{task.status === 'running' && (
|
|
418
|
+
<Button
|
|
419
|
+
type="primary"
|
|
420
|
+
danger
|
|
421
|
+
onClick={() => handleStopTask(task.id)}
|
|
422
|
+
>
|
|
423
|
+
{t(i18nKeys.PAGE_EXECUTOR_TASK_STOP)}
|
|
424
|
+
</Button>
|
|
425
|
+
)}
|
|
426
|
+
</div>
|
|
427
|
+
</div>
|
|
428
|
+
</Card>
|
|
429
|
+
))}
|
|
430
|
+
</div>
|
|
431
|
+
</section>
|
|
432
|
+
|
|
433
|
+
{/* Task History */}
|
|
434
|
+
<section className="bg-secondary shadow sm:rounded-lg p-6 border border-border">
|
|
435
|
+
<h2 className="text-xl font-medium text-text mb-4">
|
|
436
|
+
{t(i18nKeys.PAGE_EXECUTOR_TASK_HISTORY_TITLE)}
|
|
437
|
+
</h2>
|
|
438
|
+
<div className="space-y-2">
|
|
439
|
+
{tasks
|
|
440
|
+
.filter(
|
|
441
|
+
(task) =>
|
|
442
|
+
task.status === 'completed' || task.status === 'failed'
|
|
443
|
+
)
|
|
444
|
+
.map((task) => (
|
|
445
|
+
<div
|
|
446
|
+
key={task.id}
|
|
447
|
+
className="flex items-center justify-between p-2 bg-elevated rounded"
|
|
448
|
+
>
|
|
449
|
+
<div className="flex items-center space-x-2">
|
|
450
|
+
<Tag color={getStatusColor(task.status)}>{task.status}</Tag>
|
|
451
|
+
<span className="text-text">{task.name}</span>
|
|
452
|
+
</div>
|
|
453
|
+
<span className="text-sm text-text-secondary">
|
|
454
|
+
{task.endTime?.toLocaleString()}
|
|
455
|
+
</span>
|
|
456
|
+
</div>
|
|
457
|
+
))}
|
|
458
|
+
</div>
|
|
459
|
+
</section>
|
|
460
|
+
|
|
461
|
+
{/* Call to Action Section */}
|
|
462
|
+
<section className="py-8 text-center">
|
|
463
|
+
<h2 className="text-2xl font-bold mb-4 text-text">
|
|
464
|
+
{t(i18nKeys.PAGE_EXECUTOR_HELP_TITLE)}
|
|
465
|
+
</h2>
|
|
466
|
+
<p className="text-lg text-text-secondary mb-6">
|
|
467
|
+
{t(i18nKeys.PAGE_EXECUTOR_HELP_DESCRIPTION)}
|
|
468
|
+
</p>
|
|
469
|
+
<Space>
|
|
470
|
+
<Button type="primary" size="large">
|
|
471
|
+
{t(i18nKeys.PAGE_EXECUTOR_VIEW_GUIDE)}
|
|
472
|
+
</Button>
|
|
473
|
+
<Button size="large">
|
|
474
|
+
{t(i18nKeys.PAGE_EXECUTOR_CONTACT_SUPPORT)}
|
|
475
|
+
</Button>
|
|
476
|
+
</Space>
|
|
477
|
+
</section>
|
|
60
478
|
</div>
|
|
61
479
|
</div>
|
|
62
480
|
);
|