@yuku123/z-frontend-common 0.2.8 → 1.0.0
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/LICENSE +25 -0
- package/README.md +55 -42
- package/dist/index.js +1 -0
- package/package.json +49 -44
- package/src/components/ErrorBoundary/index.jsx +24 -77
- package/src/components/Layout/index.jsx +141 -38
- package/src/components/LoginPage/index.jsx +47 -21
- package/src/components/StatusTag/index.jsx +14 -12
- package/src/index.js +32 -38
- package/src/services/request.js +74 -4
- package/dist/z-frontend-common.css +0 -1
- package/dist/z-frontend-common.es.js +0 -51275
- package/dist/z-frontend-common.umd.js +0 -459
- package/src/components/Ctc/Layout.jsx +0 -330
- package/src/components/Ctc/Layout.module.css +0 -145
- package/src/components/Ctc/agentTeam/index.tsx +0 -308
- package/src/components/Ctc/login/AuthPage.module.css +0 -26
- package/src/components/Ctc/login/AuthPage.tsx +0 -235
- package/src/components/Ctc/login/index.less +0 -49
- package/src/components/Ctc/login/index.tsx +0 -142
- package/src/components/Ctc/uc/UCLogin.tsx +0 -31
- package/src/components/Ctc/uc/UCPhoneLogin.tsx +0 -62
- package/src/components/Ctc/uc/UCRegister.tsx +0 -37
- package/src/components/Ctc/uc/UCResetPassword.tsx +0 -69
- package/src/components/Ctc/uc/index.tsx +0 -15
- package/src/components/Ctc/userPanel/index.tsx +0 -999
- package/src/components/Ctc/webide/index.tsx +0 -272
- package/src/components/LowCode/LowCodeModel.jsx +0 -962
- package/src/components/LowCode/LowCodePage.jsx +0 -31
- package/src/components/LowCode/LowCodeRuntime.jsx +0 -335
- package/src/components/LowCode/MaterializePage.jsx +0 -235
- package/src/components/LowCode/index.js +0 -1
- package/src/components/MockPlatform/CurlImportModal.jsx +0 -362
- package/src/components/MockPlatform/EndpointsTab.jsx +0 -509
- package/src/components/MockPlatform/EnvironmentsTab.jsx +0 -212
- package/src/components/MockPlatform/MockTemplateHelper.jsx +0 -200
- package/src/components/MockPlatform/OpenApiImportModal.jsx +0 -305
- package/src/components/MockPlatform/RecordingsTab.jsx +0 -397
- package/src/components/MockPlatform/RequestLogsTab.jsx +0 -239
- package/src/components/MockPlatform/ScenariosTab.jsx +0 -236
- package/src/components/MockPlatform/TestCasesTab.jsx +0 -462
- package/src/components/MockPlatform/index.jsx +0 -127
- package/src/components/Overview.jsx +0 -74
- package/src/dev.jsx +0 -28
- package/src/services/agentTeam.js +0 -7
- package/src/services/api.js +0 -84
- package/src/services/ctcAc.js +0 -7
- package/src/services/ctcAcDomain.js +0 -7
- package/src/services/ctcAuthorization.js +0 -7
- package/src/services/ctcSurl.js +0 -7
- package/src/services/ctcUser.js +0 -7
- package/src/services/job.js +0 -7
- package/src/services/metaApp.js +0 -7
- package/src/services/privateConfig.js +0 -7
- package/src/services/webide.js +0 -6
- package/src/services/workspace.js +0 -21
- package/src/utils/request.ts +0 -102
|
@@ -1,509 +0,0 @@
|
|
|
1
|
-
import {useEffect, useState} from 'react'
|
|
2
|
-
import {
|
|
3
|
-
Alert,
|
|
4
|
-
Button,
|
|
5
|
-
Card,
|
|
6
|
-
Col,
|
|
7
|
-
Descriptions,
|
|
8
|
-
Drawer,
|
|
9
|
-
Form,
|
|
10
|
-
Input,
|
|
11
|
-
InputNumber,
|
|
12
|
-
message,
|
|
13
|
-
Modal,
|
|
14
|
-
Popconfirm,
|
|
15
|
-
Row,
|
|
16
|
-
Select,
|
|
17
|
-
Space,
|
|
18
|
-
Switch,
|
|
19
|
-
Table,
|
|
20
|
-
Tabs,
|
|
21
|
-
Tag,
|
|
22
|
-
Tooltip,
|
|
23
|
-
Typography
|
|
24
|
-
} from 'antd'
|
|
25
|
-
import {
|
|
26
|
-
CloudUploadOutlined,
|
|
27
|
-
CopyOutlined,
|
|
28
|
-
DeleteOutlined,
|
|
29
|
-
EditOutlined,
|
|
30
|
-
ExperimentOutlined,
|
|
31
|
-
PlayCircleOutlined,
|
|
32
|
-
PlusOutlined
|
|
33
|
-
} from '@ant-design/icons'
|
|
34
|
-
import {mockPlatformApi} from '@/services/api'
|
|
35
|
-
import OpenApiImportModal from './OpenApiImportModal'
|
|
36
|
-
import MockTemplateHelper from './MockTemplateHelper'
|
|
37
|
-
|
|
38
|
-
const {TextArea} = Input
|
|
39
|
-
const {Text, Paragraph} = Typography
|
|
40
|
-
|
|
41
|
-
const METHOD_OPTIONS = [
|
|
42
|
-
{value: 'GET', label: 'GET'},
|
|
43
|
-
{value: 'POST', label: 'POST'},
|
|
44
|
-
{value: 'PUT', label: 'PUT'},
|
|
45
|
-
{value: 'DELETE', label: 'DELETE'},
|
|
46
|
-
{value: 'PATCH', label: 'PATCH'},
|
|
47
|
-
]
|
|
48
|
-
const METHOD_COLORS = {GET: 'green', POST: 'blue', PUT: 'orange', DELETE: 'red', PATCH: 'purple'}
|
|
49
|
-
|
|
50
|
-
const FAULT_OPTIONS = [
|
|
51
|
-
{value: 'NONE', label: '无故障'},
|
|
52
|
-
{value: 'TIMEOUT', label: '超时 (TIMEOUT)'},
|
|
53
|
-
{value: 'EMPTY_RESPONSE', label: '空响应 (EMPTY)'},
|
|
54
|
-
{value: 'RANDOM_DROP', label: '随机丢弃 (RANDOM_DROP)'},
|
|
55
|
-
{value: 'MALFORMED', label: '畸形响应 (MALFORMED)'},
|
|
56
|
-
{value: '500_ERROR', label: '500 错误'},
|
|
57
|
-
{value: 'RATE_LIMIT', label: '限流 (RATE_LIMIT)'},
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
const BODY_MATCH_TYPES = [
|
|
61
|
-
{value: 'JSON', label: 'JSON'},
|
|
62
|
-
{value: 'JSONPATH', label: 'JSONPath'},
|
|
63
|
-
{value: 'REGEX', label: '正则'},
|
|
64
|
-
{value: 'TEXT', label: '纯文本'},
|
|
65
|
-
]
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
export default function EndpointsTab({activeEnvCode}) {
|
|
69
|
-
const [endpoints, setEndpoints] = useState([])
|
|
70
|
-
const [loading, setLoading] = useState(false)
|
|
71
|
-
const [mockError, setMockError] = useState(null)
|
|
72
|
-
const [modalOpen, setModalOpen] = useState(false)
|
|
73
|
-
const [editing, setEditing] = useState(null)
|
|
74
|
-
const [form] = Form.useForm()
|
|
75
|
-
const [previewOpen, setPreviewOpen] = useState(false)
|
|
76
|
-
const [previewEndpoint, setPreviewEndpoint] = useState(null)
|
|
77
|
-
const [openApiImportOpen, setOpenApiImportOpen] = useState(false)
|
|
78
|
-
|
|
79
|
-
useEffect(() => {
|
|
80
|
-
loadEndpoints()
|
|
81
|
-
}, [activeEnvCode])
|
|
82
|
-
|
|
83
|
-
const loadEndpoints = async () => {
|
|
84
|
-
setLoading(true)
|
|
85
|
-
setMockError(null)
|
|
86
|
-
try {
|
|
87
|
-
const res = await mockPlatformApi.endpoints.list(activeEnvCode)
|
|
88
|
-
setEndpoints(Array.isArray(res) ? res : [])
|
|
89
|
-
} catch (e) {
|
|
90
|
-
setMockError(e?.message || 'API unavailable')
|
|
91
|
-
setEndpoints([])
|
|
92
|
-
} finally {
|
|
93
|
-
setLoading(false)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const handleCreate = () => {
|
|
98
|
-
setEditing(null)
|
|
99
|
-
form.resetFields()
|
|
100
|
-
form.setFieldsValue({
|
|
101
|
-
method: 'GET', statusCode: 200, delayMs: 0, priority: 5,
|
|
102
|
-
envCode: activeEnvCode || 'default',
|
|
103
|
-
matchBodyType: 'JSON', faultType: 'NONE', status: 1,
|
|
104
|
-
})
|
|
105
|
-
setModalOpen(true)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const handleEdit = (record) => {
|
|
109
|
-
setEditing(record)
|
|
110
|
-
form.setFieldsValue(record)
|
|
111
|
-
setModalOpen(true)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const handleSave = async () => {
|
|
115
|
-
try {
|
|
116
|
-
const values = await form.validateFields()
|
|
117
|
-
if (editing) {
|
|
118
|
-
await mockPlatformApi.endpoints.update(editing.mockCode, values)
|
|
119
|
-
message.success('更新成功')
|
|
120
|
-
} else {
|
|
121
|
-
await mockPlatformApi.endpoints.create(values)
|
|
122
|
-
message.success('创建成功')
|
|
123
|
-
}
|
|
124
|
-
setModalOpen(false)
|
|
125
|
-
loadEndpoints()
|
|
126
|
-
} catch (e) {
|
|
127
|
-
if (e?.errorFields) return
|
|
128
|
-
message.error('保存失败: ' + (e?.message || ''))
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const handleDelete = async (mockCode) => {
|
|
133
|
-
try {
|
|
134
|
-
await mockPlatformApi.endpoints.delete(mockCode)
|
|
135
|
-
message.success('删除成功')
|
|
136
|
-
loadEndpoints()
|
|
137
|
-
} catch (e) {
|
|
138
|
-
message.error('删除失败')
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const handleToggle = async (record) => {
|
|
143
|
-
const newStatus = record.status === 1 ? 0 : 1
|
|
144
|
-
try {
|
|
145
|
-
await mockPlatformApi.endpoints.toggle(record.mockCode, newStatus)
|
|
146
|
-
message.success(newStatus === 1 ? '已启用' : '已禁用')
|
|
147
|
-
loadEndpoints()
|
|
148
|
-
} catch (e) {
|
|
149
|
-
message.error('操作失败')
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const getEndpointUrl = (record) => {
|
|
154
|
-
return `${window.location.origin}/api/mock/${record.envCode || activeEnvCode || 'default'}${record.path || ''}`
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const columns = [
|
|
158
|
-
{
|
|
159
|
-
title: '路径', dataIndex: 'path', key: 'path', width: 280,
|
|
160
|
-
render: (text, r) => (
|
|
161
|
-
<Space>
|
|
162
|
-
<Tag color={METHOD_COLORS[r.method] || 'default'}>{r.method}</Tag>
|
|
163
|
-
<Text code copyable={{text}}>{text}</Text>
|
|
164
|
-
</Space>
|
|
165
|
-
)
|
|
166
|
-
},
|
|
167
|
-
{title: '编码', dataIndex: 'mockCode', key: 'mockCode', width: 160},
|
|
168
|
-
{
|
|
169
|
-
title: '优先级', dataIndex: 'priority', key: 'priority', width: 80, align: 'center',
|
|
170
|
-
render: (v) => <Tag color="blue">P{v || 5}</Tag>
|
|
171
|
-
},
|
|
172
|
-
{title: '状态码', dataIndex: 'statusCode', key: 'statusCode', width: 80, align: 'center'},
|
|
173
|
-
{
|
|
174
|
-
title: '延迟', dataIndex: 'delayMs', key: 'delayMs', width: 80, align: 'center',
|
|
175
|
-
render: (v) => v > 0 ? <Text type="warning">{v}ms</Text> : <Text type="secondary">-</Text>
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
title: '故障', dataIndex: 'faultType', key: 'faultType', width: 110,
|
|
179
|
-
render: (v) => v && v !== 'NONE' ? <Tag color="red">{v}</Tag> : <Text type="secondary">-</Text>
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
title: '场景', dataIndex: 'scenarioCode', key: 'scenarioCode', width: 130,
|
|
183
|
-
render: (v) => v ? <Tag color="purple">{v}</Tag> : <Text type="secondary">-</Text>
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
title: '命中', dataIndex: 'hitCount', key: 'hitCount', width: 80, align: 'center',
|
|
187
|
-
render: (v) => <Text strong>{v || 0}</Text>
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
title: '状态', key: 'status', width: 100, align: 'center',
|
|
191
|
-
render: (_, record) => (
|
|
192
|
-
<Switch checked={record.status === 1} onChange={() => handleToggle(record)}
|
|
193
|
-
checkedChildren="启用" unCheckedChildren="禁用" size="small"/>
|
|
194
|
-
)
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
title: '操作', key: 'action', width: 220, fixed: 'right',
|
|
198
|
-
render: (_, record) => (
|
|
199
|
-
<Space size="small">
|
|
200
|
-
<Tooltip title="预览响应">
|
|
201
|
-
<Button size="small" type="link" icon={<PlayCircleOutlined/>}
|
|
202
|
-
onClick={() => {
|
|
203
|
-
setPreviewEndpoint(record);
|
|
204
|
-
setPreviewOpen(true)
|
|
205
|
-
}}>预览</Button>
|
|
206
|
-
</Tooltip>
|
|
207
|
-
<Tooltip title="复制URL">
|
|
208
|
-
<Button size="small" type="link" icon={<CopyOutlined/>}
|
|
209
|
-
onClick={() => {
|
|
210
|
-
navigator.clipboard.writeText(getEndpointUrl(record));
|
|
211
|
-
message.success('已复制')
|
|
212
|
-
}}/>
|
|
213
|
-
</Tooltip>
|
|
214
|
-
<Tooltip title="编辑">
|
|
215
|
-
<Button size="small" type="link" icon={<EditOutlined/>} onClick={() => handleEdit(record)}/>
|
|
216
|
-
</Tooltip>
|
|
217
|
-
<Popconfirm title="确认删除?" onConfirm={() => handleDelete(record.mockCode)}>
|
|
218
|
-
<Button size="small" type="link" danger icon={<DeleteOutlined/>}/>
|
|
219
|
-
</Popconfirm>
|
|
220
|
-
</Space>
|
|
221
|
-
)
|
|
222
|
-
},
|
|
223
|
-
]
|
|
224
|
-
|
|
225
|
-
return (
|
|
226
|
-
<div>
|
|
227
|
-
{mockError && (
|
|
228
|
-
<Alert type="warning" showIcon style={{marginBottom: 16}}
|
|
229
|
-
title="后端 API 暂未连接" description={mockError}/>
|
|
230
|
-
)}
|
|
231
|
-
|
|
232
|
-
<Row gutter={16} style={{marginBottom: 16}}>
|
|
233
|
-
<Col span={6}><Card><strong>总端点</strong>
|
|
234
|
-
<div style={{fontSize: 22}}>{endpoints.length}</div>
|
|
235
|
-
</Card></Col>
|
|
236
|
-
<Col span={6}><Card><strong>已启用</strong>
|
|
237
|
-
<div style={{fontSize: 22, color: '#52c41a'}}>{endpoints.filter(e => e.status === 1).length}</div>
|
|
238
|
-
</Card></Col>
|
|
239
|
-
<Col span={6}><Card><strong>已禁用</strong>
|
|
240
|
-
<div style={{fontSize: 22, color: '#ff4d4f'}}>{endpoints.filter(e => e.status === 0).length}</div>
|
|
241
|
-
</Card></Col>
|
|
242
|
-
<Col span={6}><Card><strong>总命中</strong>
|
|
243
|
-
<div style={{
|
|
244
|
-
fontSize: 22,
|
|
245
|
-
color: '#1890ff'
|
|
246
|
-
}}>{endpoints.reduce((s, e) => s + (e.hitCount || 0), 0)}</div>
|
|
247
|
-
</Card></Col>
|
|
248
|
-
</Row>
|
|
249
|
-
|
|
250
|
-
<Space style={{marginBottom: 16}}>
|
|
251
|
-
<Button icon={<PlayCircleOutlined/>} onClick={loadEndpoints}>刷新</Button>
|
|
252
|
-
<Button type="primary" icon={<PlusOutlined/>} onClick={handleCreate}>新建 Mock 端点</Button>
|
|
253
|
-
<Button icon={<CloudUploadOutlined/>} onClick={() => setOpenApiImportOpen(true)}>
|
|
254
|
-
批量导入 (OpenAPI / Swagger / Postman)
|
|
255
|
-
</Button>
|
|
256
|
-
</Space>
|
|
257
|
-
|
|
258
|
-
<Table
|
|
259
|
-
dataSource={endpoints}
|
|
260
|
-
columns={columns}
|
|
261
|
-
rowKey="id"
|
|
262
|
-
loading={loading}
|
|
263
|
-
pagination={{pageSize: 10}}
|
|
264
|
-
scroll={{x: 1300}}
|
|
265
|
-
size="small"
|
|
266
|
-
/>
|
|
267
|
-
|
|
268
|
-
<Modal
|
|
269
|
-
title={editing ? `编辑 Mock 端点: ${editing.mockCode}` : '新建 Mock 端点'}
|
|
270
|
-
open={modalOpen}
|
|
271
|
-
onCancel={() => setModalOpen(false)}
|
|
272
|
-
onOk={handleSave}
|
|
273
|
-
width={900}
|
|
274
|
-
okText="保存" cancelText="取消"
|
|
275
|
-
>
|
|
276
|
-
<Form form={form} layout="vertical" style={{marginTop: 16}}>
|
|
277
|
-
<Tabs
|
|
278
|
-
items={[
|
|
279
|
-
{
|
|
280
|
-
key: 'basic', label: '基本信息',
|
|
281
|
-
children: (
|
|
282
|
-
<>
|
|
283
|
-
<Row gutter={16}>
|
|
284
|
-
<Col span={8}>
|
|
285
|
-
<Form.Item name="mockCode" label="Mock 编码" rules={[{required: true}]}>
|
|
286
|
-
<Input placeholder="mock-user-list" disabled={!!editing}/>
|
|
287
|
-
</Form.Item>
|
|
288
|
-
</Col>
|
|
289
|
-
<Col span={8}>
|
|
290
|
-
<Form.Item name="mockName" label="名称">
|
|
291
|
-
<Input placeholder="User List Endpoint"/>
|
|
292
|
-
</Form.Item>
|
|
293
|
-
</Col>
|
|
294
|
-
<Col span={8}>
|
|
295
|
-
<Form.Item name="envCode" label="环境编码">
|
|
296
|
-
<Input placeholder="default"/>
|
|
297
|
-
</Form.Item>
|
|
298
|
-
</Col>
|
|
299
|
-
</Row>
|
|
300
|
-
<Row gutter={16}>
|
|
301
|
-
<Col span={12}>
|
|
302
|
-
<Form.Item name="path" label="路径" rules={[{required: true}]}>
|
|
303
|
-
<Input placeholder="/api/user/list"/>
|
|
304
|
-
</Form.Item>
|
|
305
|
-
</Col>
|
|
306
|
-
<Col span={4}>
|
|
307
|
-
<Form.Item name="method" label="方法">
|
|
308
|
-
<Select options={METHOD_OPTIONS}/>
|
|
309
|
-
</Form.Item>
|
|
310
|
-
</Col>
|
|
311
|
-
<Col span={4}>
|
|
312
|
-
<Form.Item name="priority" label="优先级">
|
|
313
|
-
<InputNumber min={0} max={99} style={{width: '100%'}}/>
|
|
314
|
-
</Form.Item>
|
|
315
|
-
</Col>
|
|
316
|
-
<Col span={4}>
|
|
317
|
-
<Form.Item name="statusCode" label="状态码">
|
|
318
|
-
<InputNumber min={100} max={599} style={{width: '100%'}}/>
|
|
319
|
-
</Form.Item>
|
|
320
|
-
</Col>
|
|
321
|
-
</Row>
|
|
322
|
-
<Row gutter={16}>
|
|
323
|
-
<Col span={8}>
|
|
324
|
-
<Form.Item name="delayMs" label="延迟(ms)">
|
|
325
|
-
<InputNumber min={0} max={60000} style={{width: '100%'}}/>
|
|
326
|
-
</Form.Item>
|
|
327
|
-
</Col>
|
|
328
|
-
<Col span={8}>
|
|
329
|
-
<Form.Item name="projectCode" label="项目">
|
|
330
|
-
<Input placeholder="default"/>
|
|
331
|
-
</Form.Item>
|
|
332
|
-
</Col>
|
|
333
|
-
<Col span={8}>
|
|
334
|
-
<Form.Item name="moduleCode" label="模块">
|
|
335
|
-
<Input placeholder="user"/>
|
|
336
|
-
</Form.Item>
|
|
337
|
-
</Col>
|
|
338
|
-
</Row>
|
|
339
|
-
<Form.Item name="description" label="描述">
|
|
340
|
-
<TextArea rows={2}/>
|
|
341
|
-
</Form.Item>
|
|
342
|
-
</>
|
|
343
|
-
)
|
|
344
|
-
},
|
|
345
|
-
{
|
|
346
|
-
key: 'match', label: '请求匹配',
|
|
347
|
-
children: (
|
|
348
|
-
<>
|
|
349
|
-
<Form.Item name="matchUrlPattern" label="URL 匹配模式 (Ant风格)"
|
|
350
|
-
extra="支持 /api/users/{id} 通配符,留空则按 path 精确匹配">
|
|
351
|
-
<Input placeholder="/api/users/{id}"/>
|
|
352
|
-
</Form.Item>
|
|
353
|
-
<Row gutter={16}>
|
|
354
|
-
<Col span={12}>
|
|
355
|
-
<Form.Item name="matchHeaders" label="Header 匹配 (JSON)"
|
|
356
|
-
extra='例: {"Authorization": "Bearer *", "Content-Type": "application/json"}'>
|
|
357
|
-
<TextArea rows={3} placeholder='{"Authorization": "Bearer *"}'/>
|
|
358
|
-
</Form.Item>
|
|
359
|
-
</Col>
|
|
360
|
-
<Col span={12}>
|
|
361
|
-
<Form.Item name="matchQuery" label="Query 匹配 (JSON)"
|
|
362
|
-
extra='例: {"page": "1", "size": "10"}'>
|
|
363
|
-
<TextArea rows={3} placeholder='{"page": "1"}'/>
|
|
364
|
-
</Form.Item>
|
|
365
|
-
</Col>
|
|
366
|
-
</Row>
|
|
367
|
-
<Form.Item name="matchBodyType" label="Body 匹配类型">
|
|
368
|
-
<Select options={BODY_MATCH_TYPES}/>
|
|
369
|
-
</Form.Item>
|
|
370
|
-
<Form.Item name="matchBody" label="Body 匹配表达式"
|
|
371
|
-
extra="JSON 类型支持 JSONPath,REGEX 类型为正则表达式">
|
|
372
|
-
<TextArea rows={3} placeholder='$.user.id == "100"'/>
|
|
373
|
-
</Form.Item>
|
|
374
|
-
</>
|
|
375
|
-
)
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
key: 'scenario', label: '场景状态机',
|
|
379
|
-
children: (
|
|
380
|
-
<>
|
|
381
|
-
<Form.Item name="scenarioCode" label="场景编码"
|
|
382
|
-
extra="关联场景实现状态机驱动 (如 user-flow, payment-flow)">
|
|
383
|
-
<Input placeholder="user-flow"/>
|
|
384
|
-
</Form.Item>
|
|
385
|
-
<Row gutter={16}>
|
|
386
|
-
<Col span={12}>
|
|
387
|
-
<Form.Item name="requiredState" label="匹配所需状态"
|
|
388
|
-
extra="仅当实例处于此状态时才匹配 (留空 = 不限制)">
|
|
389
|
-
<Input placeholder="started"/>
|
|
390
|
-
</Form.Item>
|
|
391
|
-
</Col>
|
|
392
|
-
<Col span={12}>
|
|
393
|
-
<Form.Item name="newState" label="匹配后新状态"
|
|
394
|
-
extra="匹配成功后自动切换">
|
|
395
|
-
<Input placeholder="logged-in"/>
|
|
396
|
-
</Form.Item>
|
|
397
|
-
</Col>
|
|
398
|
-
</Row>
|
|
399
|
-
</>
|
|
400
|
-
)
|
|
401
|
-
},
|
|
402
|
-
{
|
|
403
|
-
key: 'fault', label: '故障注入',
|
|
404
|
-
children: (
|
|
405
|
-
<>
|
|
406
|
-
<Form.Item name="faultType" label="故障类型"
|
|
407
|
-
extra="模拟网络异常,验证客户端容错">
|
|
408
|
-
<Select options={FAULT_OPTIONS}/>
|
|
409
|
-
</Form.Item>
|
|
410
|
-
<Form.Item name="faultConfig" label="故障配置 (JSON)"
|
|
411
|
-
extra='例: {"errorRate": 0.5, "rateLimitQps": 10, "timeoutMs": 30000}'>
|
|
412
|
-
<TextArea rows={4} placeholder='{"errorRate": 0.5}'/>
|
|
413
|
-
</Form.Item>
|
|
414
|
-
<Form.Item name="proxyTo" label="代理目标 URL"
|
|
415
|
-
extra="未匹配时转发到真实服务 (MockEnvironment 模式)">
|
|
416
|
-
<Input placeholder="http://real-service:8080"/>
|
|
417
|
-
</Form.Item>
|
|
418
|
-
</>
|
|
419
|
-
)
|
|
420
|
-
},
|
|
421
|
-
{
|
|
422
|
-
key: 'response', label: '响应',
|
|
423
|
-
children: (
|
|
424
|
-
<>
|
|
425
|
-
<Form.Item name="responseHeaders" label="响应头 (JSON)"
|
|
426
|
-
extra='例: {"Content-Type": "application/json", "X-Trace-Id": "..."}'>
|
|
427
|
-
<TextArea rows={2} placeholder='{"Content-Type": "application/json"}'/>
|
|
428
|
-
</Form.Item>
|
|
429
|
-
<Form.Item name="responseTemplate" label="响应模板 (JSON)"
|
|
430
|
-
rules={[{required: true}]}
|
|
431
|
-
extra={
|
|
432
|
-
<div>
|
|
433
|
-
<Text type="secondary" style={{display: 'block', marginBottom: 4}}>
|
|
434
|
-
支持 <code>@int(1,100)</code> / <code>@uuid</code> / <code>@datetime</code> / <code>#path.id</code> 等动态表达式
|
|
435
|
-
</Text>
|
|
436
|
-
<MockTemplateHelper form={form} formField="responseTemplate"/>
|
|
437
|
-
</div>
|
|
438
|
-
}>
|
|
439
|
-
<TextArea
|
|
440
|
-
rows={14}
|
|
441
|
-
placeholder={'{\n "success": true,\n "data": [],\n "code": 200\n}'}
|
|
442
|
-
style={{fontFamily: 'monospace', fontSize: 13}}
|
|
443
|
-
/>
|
|
444
|
-
</Form.Item>
|
|
445
|
-
</>
|
|
446
|
-
)
|
|
447
|
-
},
|
|
448
|
-
]}
|
|
449
|
-
/>
|
|
450
|
-
</Form>
|
|
451
|
-
</Modal>
|
|
452
|
-
|
|
453
|
-
<Drawer
|
|
454
|
-
title={<Space><ExperimentOutlined/>端点详情: {previewEndpoint?.mockCode}</Space>}
|
|
455
|
-
open={previewOpen}
|
|
456
|
-
onClose={() => setPreviewOpen(false)}
|
|
457
|
-
width={650}
|
|
458
|
-
>
|
|
459
|
-
{previewEndpoint && (
|
|
460
|
-
<div>
|
|
461
|
-
<Descriptions column={2} size="small" bordered style={{marginBottom: 16}}>
|
|
462
|
-
<Descriptions.Item label="编码">{previewEndpoint.mockCode}</Descriptions.Item>
|
|
463
|
-
<Descriptions.Item label="名称">{previewEndpoint.mockName}</Descriptions.Item>
|
|
464
|
-
<Descriptions.Item label="方法">
|
|
465
|
-
<Tag color={METHOD_COLORS[previewEndpoint.method]}>{previewEndpoint.method}</Tag>
|
|
466
|
-
</Descriptions.Item>
|
|
467
|
-
<Descriptions.Item label="环境">{previewEndpoint.envCode}</Descriptions.Item>
|
|
468
|
-
<Descriptions.Item label="路径" span={2}>{previewEndpoint.path}</Descriptions.Item>
|
|
469
|
-
<Descriptions.Item label="状态码">{previewEndpoint.statusCode}</Descriptions.Item>
|
|
470
|
-
<Descriptions.Item label="延迟">{previewEndpoint.delayMs}ms</Descriptions.Item>
|
|
471
|
-
<Descriptions.Item label="优先级">P{previewEndpoint.priority}</Descriptions.Item>
|
|
472
|
-
<Descriptions.Item label="命中">{previewEndpoint.hitCount || 0}</Descriptions.Item>
|
|
473
|
-
<Descriptions.Item label="故障">
|
|
474
|
-
{previewEndpoint.faultType && previewEndpoint.faultType !== 'NONE' ?
|
|
475
|
-
<Tag color="red">{previewEndpoint.faultType}</Tag> :
|
|
476
|
-
<Text type="secondary">-</Text>}
|
|
477
|
-
</Descriptions.Item>
|
|
478
|
-
<Descriptions.Item label="场景">{previewEndpoint.scenarioCode || '-'}</Descriptions.Item>
|
|
479
|
-
<Descriptions.Item label="状态" span={2}>
|
|
480
|
-
{previewEndpoint.status === 1 ? <Tag color="green">启用</Tag> : <Tag>禁用</Tag>}
|
|
481
|
-
</Descriptions.Item>
|
|
482
|
-
<Descriptions.Item label="完整 URL" span={2}>
|
|
483
|
-
<Text copyable>{getEndpointUrl(previewEndpoint)}</Text>
|
|
484
|
-
</Descriptions.Item>
|
|
485
|
-
</Descriptions>
|
|
486
|
-
|
|
487
|
-
<Paragraph strong>响应模板:</Paragraph>
|
|
488
|
-
<pre style={{
|
|
489
|
-
background: '#f5f5f5', padding: 12, borderRadius: 4,
|
|
490
|
-
maxHeight: 350, overflow: 'auto', fontSize: 13, fontFamily: 'monospace',
|
|
491
|
-
whiteSpace: 'pre-wrap', wordBreak: 'break-all',
|
|
492
|
-
}}>
|
|
493
|
-
{previewEndpoint.responseTemplate}
|
|
494
|
-
</pre>
|
|
495
|
-
</div>
|
|
496
|
-
)}
|
|
497
|
-
</Drawer>
|
|
498
|
-
|
|
499
|
-
<OpenApiImportModal
|
|
500
|
-
open={openApiImportOpen}
|
|
501
|
-
onClose={() => setOpenApiImportOpen(false)}
|
|
502
|
-
onSuccess={() => {
|
|
503
|
-
message.success('已刷新列表');
|
|
504
|
-
loadEndpoints()
|
|
505
|
-
}}
|
|
506
|
-
/>
|
|
507
|
-
</div>
|
|
508
|
-
)
|
|
509
|
-
}
|