@yuku123/z-agent-frontend-component 0.1.1
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/README.md +66 -0
- package/dist/z-agent-frontend-component.css +1 -0
- package/dist/z-agent-frontend-component.es.js +9956 -0
- package/dist/z-agent-frontend-component.umd.js +219 -0
- package/package.json +77 -0
- package/src/api/apiRouter.js +78 -0
- package/src/api/index.js +23 -0
- package/src/api/request.js +59 -0
- package/src/api/routes.js +140 -0
- package/src/dev.jsx +80 -0
- package/src/index.js +86 -0
- package/src/pages/agent/app/index.jsx +2 -0
- package/src/pages/agent/editor/AgentAppEditor.jsx +456 -0
- package/src/pages/agent/editor/WorkflowEditor.jsx +495 -0
- package/src/pages/agent/editor/nodes/index.ts +225 -0
- package/src/pages/agent/index.jsx +1379 -0
- package/src/pages/agent/share.jsx +512 -0
- package/src/pages/ak/AkUsageDrawer.jsx +208 -0
- package/src/pages/ak/index.jsx +496 -0
- package/src/pages/llm/index.jsx +736 -0
- package/src/pages/llm/model/index.jsx +220 -0
- package/src/pages/llm/provider/index.jsx +173 -0
- package/src/pages/mcp/index.jsx +359 -0
- package/src/pages/oss/BucketList.jsx +320 -0
- package/src/pages/oss/ObjectBrowser.jsx +409 -0
- package/src/pages/product/execute.jsx +608 -0
- package/src/pages/product/index.jsx +628 -0
- package/src/pages/product/scene.jsx +746 -0
- package/src/pages/script/ApiBridgeEditor.jsx +255 -0
- package/src/pages/script/CurlImportModal.jsx +263 -0
- package/src/pages/script/FieldMappingEditor.jsx +131 -0
- package/src/pages/script/OpenApiImportModal.jsx +212 -0
- package/src/pages/script/index.jsx +532 -0
- package/src/pages/skill/index.jsx +1595 -0
- package/src/pages/trace/DebugPlayground.jsx +357 -0
- package/src/pages/trace/components/MetricsDashboard.jsx +164 -0
- package/src/pages/trace/components/RagFragments.jsx +134 -0
- package/src/pages/trace/components/Timeline.jsx +142 -0
- package/src/pages/trace/components/ToolCallTree.jsx +116 -0
- package/src/pages/trace/index.jsx +13 -0
- package/src/pages/usage/index.jsx +352 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
import {useEffect, useState} from 'react'
|
|
2
|
+
import {
|
|
3
|
+
Button,
|
|
4
|
+
Card,
|
|
5
|
+
Col,
|
|
6
|
+
Descriptions,
|
|
7
|
+
Drawer,
|
|
8
|
+
Form,
|
|
9
|
+
Input,
|
|
10
|
+
message,
|
|
11
|
+
Modal,
|
|
12
|
+
Popconfirm,
|
|
13
|
+
Row,
|
|
14
|
+
Select,
|
|
15
|
+
Space,
|
|
16
|
+
Statistic,
|
|
17
|
+
Table,
|
|
18
|
+
Tag,
|
|
19
|
+
Tooltip,
|
|
20
|
+
Typography
|
|
21
|
+
} from 'antd'
|
|
22
|
+
import {
|
|
23
|
+
CloudDownloadOutlined,
|
|
24
|
+
CloudServerOutlined,
|
|
25
|
+
CloudUploadOutlined,
|
|
26
|
+
CodeOutlined,
|
|
27
|
+
DeleteOutlined,
|
|
28
|
+
EditOutlined,
|
|
29
|
+
ImportOutlined,
|
|
30
|
+
PlayCircleOutlined,
|
|
31
|
+
PlusOutlined
|
|
32
|
+
} from '@ant-design/icons'
|
|
33
|
+
import {scriptApi} from '../../api'
|
|
34
|
+
import CurlImportModal from './CurlImportModal'
|
|
35
|
+
import OpenApiImportModal from './OpenApiImportModal'
|
|
36
|
+
import ApiBridgeEditor from './ApiBridgeEditor'
|
|
37
|
+
|
|
38
|
+
const {TextArea} = Input
|
|
39
|
+
const {Text, Paragraph} = Typography
|
|
40
|
+
|
|
41
|
+
const DSL_TYPES = [
|
|
42
|
+
{value: 'EL', label: 'EL (SpEL)'},
|
|
43
|
+
{value: 'GROOVY', label: 'Groovy'},
|
|
44
|
+
{value: 'LUA', label: 'Lua'},
|
|
45
|
+
{value: 'SQL', label: 'SQL'},
|
|
46
|
+
{value: 'MOCK', label: 'Mock Template'},
|
|
47
|
+
{value: 'API_BRIDGE', label: 'API Bridge'},
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
const EXPOSE_OPTIONS = [
|
|
51
|
+
{value: 'NONE', label: 'None'},
|
|
52
|
+
{value: 'HTTP', label: 'HTTP'},
|
|
53
|
+
{value: 'MCP', label: 'MCP'},
|
|
54
|
+
{value: 'BOTH', label: 'HTTP + MCP'},
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
const STATUS_MAP = {
|
|
58
|
+
0: {text: 'Draft', color: 'default'},
|
|
59
|
+
1: {text: 'Active', color: 'green'},
|
|
60
|
+
2: {text: 'Disabled', color: 'red'},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const EXPOSE_COLOR = {
|
|
64
|
+
NONE: 'default',
|
|
65
|
+
HTTP: 'blue',
|
|
66
|
+
MCP: 'purple',
|
|
67
|
+
BOTH: 'cyan',
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Mock data for when backend is unavailable
|
|
71
|
+
const MOCK_SCRIPTS = [
|
|
72
|
+
{
|
|
73
|
+
id: 1,
|
|
74
|
+
scriptCode: 'hello-el',
|
|
75
|
+
scriptName: 'Hello EL表达式',
|
|
76
|
+
dslType: 'EL',
|
|
77
|
+
exposeAs: 'HTTP',
|
|
78
|
+
status: 1,
|
|
79
|
+
version: '1.0.0',
|
|
80
|
+
httpPath: '/run/hello-el',
|
|
81
|
+
sourceCode: "'Hello from ' + #name + '!'"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 2,
|
|
85
|
+
scriptCode: 'hello-groovy',
|
|
86
|
+
scriptName: 'Hello Groovy',
|
|
87
|
+
dslType: 'GROOVY',
|
|
88
|
+
exposeAs: 'NONE',
|
|
89
|
+
status: 1,
|
|
90
|
+
version: '1.0.0',
|
|
91
|
+
sourceCode: 'def result = "Hello from Groovy!"\nreturn result'
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 3,
|
|
95
|
+
scriptCode: 'mock-users',
|
|
96
|
+
scriptName: 'Mock Users API',
|
|
97
|
+
dslType: 'MOCK',
|
|
98
|
+
exposeAs: 'HTTP',
|
|
99
|
+
status: 1,
|
|
100
|
+
version: '1.0.0',
|
|
101
|
+
httpPath: '/run/mock-users',
|
|
102
|
+
sourceCode: '{"users": [{"name": "@name", "email": "@email"}]}'
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: 4,
|
|
106
|
+
scriptCode: 'parse-sql',
|
|
107
|
+
scriptName: 'SQL解析器',
|
|
108
|
+
dslType: 'SQL',
|
|
109
|
+
exposeAs: 'NONE',
|
|
110
|
+
status: 1,
|
|
111
|
+
version: '1.0.0',
|
|
112
|
+
sourceCode: 'SELECT id, name FROM users WHERE status = 1'
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: 5,
|
|
116
|
+
scriptCode: 'lua-greet',
|
|
117
|
+
scriptName: 'Lua问候',
|
|
118
|
+
dslType: 'LUA',
|
|
119
|
+
exposeAs: 'NONE',
|
|
120
|
+
status: 0,
|
|
121
|
+
version: '0.1.0',
|
|
122
|
+
sourceCode: 'return "Hello from Lua!"'
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: 6,
|
|
126
|
+
scriptCode: 'fetch-weather',
|
|
127
|
+
scriptName: 'Fetch Weather',
|
|
128
|
+
dslType: 'API_BRIDGE',
|
|
129
|
+
exposeAs: 'MCP',
|
|
130
|
+
status: 1,
|
|
131
|
+
version: '1.0.0',
|
|
132
|
+
mcpToolName: 'fetch_weather',
|
|
133
|
+
sourceCode: 'source: https://api.weather.com\nmethod: GET'
|
|
134
|
+
},
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
export default function ScriptCenterPage() {
|
|
138
|
+
const [scripts, setScripts] = useState([])
|
|
139
|
+
const [loading, setLoading] = useState(false)
|
|
140
|
+
const [isMock, setIsMock] = useState(false)
|
|
141
|
+
const [modalOpen, setModalOpen] = useState(false)
|
|
142
|
+
const [editing, setEditing] = useState(null)
|
|
143
|
+
const [form] = Form.useForm()
|
|
144
|
+
const [runDrawerOpen, setRunDrawerOpen] = useState(false)
|
|
145
|
+
const [runScript, setRunScript] = useState(null)
|
|
146
|
+
const [runParams, setRunParams] = useState('{}')
|
|
147
|
+
const [runResult, setRunResult] = useState('')
|
|
148
|
+
const [runLoading, setRunLoading] = useState(false)
|
|
149
|
+
const [curlModalOpen, setCurlModalOpen] = useState(false)
|
|
150
|
+
const [openApiModalOpen, setOpenApiModalOpen] = useState(false)
|
|
151
|
+
const [bridgeSourceCode, setBridgeSourceCode] = useState(null)
|
|
152
|
+
const watchedDslType = Form.useWatch('dslType', form)
|
|
153
|
+
const isApiBridge = watchedDslType === 'API_BRIDGE'
|
|
154
|
+
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
loadScripts()
|
|
157
|
+
}, [])
|
|
158
|
+
|
|
159
|
+
const loadScripts = async () => {
|
|
160
|
+
setLoading(true)
|
|
161
|
+
try {
|
|
162
|
+
const res = await scriptApi.list()
|
|
163
|
+
setScripts(Array.isArray(res) ? res : (res?.data || res || []))
|
|
164
|
+
setIsMock(false)
|
|
165
|
+
} catch (e) {
|
|
166
|
+
console.warn('Script API unavailable:', e?.message)
|
|
167
|
+
setScripts(MOCK_SCRIPTS)
|
|
168
|
+
setIsMock(true)
|
|
169
|
+
message.warning('后端未连接,显示模拟数据')
|
|
170
|
+
} finally {
|
|
171
|
+
setLoading(false)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const handleCreate = () => {
|
|
176
|
+
setEditing(null)
|
|
177
|
+
form.resetFields()
|
|
178
|
+
form.setFieldsValue({dslType: 'EL', exposeAs: 'NONE', status: 0})
|
|
179
|
+
setBridgeSourceCode(null)
|
|
180
|
+
setModalOpen(true)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const handleDslTypeChange = (val) => {
|
|
184
|
+
if (val === 'API_BRIDGE') {
|
|
185
|
+
// Initialize empty bridge definition
|
|
186
|
+
setBridgeSourceCode(JSON.stringify({
|
|
187
|
+
request: {
|
|
188
|
+
httpRequestLine: {url: '', requestMethod: 'GET'},
|
|
189
|
+
httpRequestHeader: {headers: {}},
|
|
190
|
+
httpRequestBody: {body: null}
|
|
191
|
+
},
|
|
192
|
+
inputParams: [],
|
|
193
|
+
outputMapping: [],
|
|
194
|
+
}))
|
|
195
|
+
} else {
|
|
196
|
+
setBridgeSourceCode(null)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const handleEdit = (record) => {
|
|
201
|
+
setEditing(record)
|
|
202
|
+
form.setFieldsValue(record)
|
|
203
|
+
if (record.dslType === 'API_BRIDGE') {
|
|
204
|
+
setBridgeSourceCode(record.sourceCode)
|
|
205
|
+
} else {
|
|
206
|
+
setBridgeSourceCode(null)
|
|
207
|
+
}
|
|
208
|
+
setModalOpen(true)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const handleSave = async () => {
|
|
212
|
+
try {
|
|
213
|
+
const values = await form.validateFields()
|
|
214
|
+
// For API_BRIDGE, serialize the structured editor back to sourceCode
|
|
215
|
+
if (values.dslType === 'API_BRIDGE' && bridgeSourceCode != null) {
|
|
216
|
+
values.sourceCode = bridgeSourceCode
|
|
217
|
+
}
|
|
218
|
+
if (editing) {
|
|
219
|
+
await scriptApi.update(editing.scriptCode, values)
|
|
220
|
+
message.success('更新成功')
|
|
221
|
+
} else {
|
|
222
|
+
await scriptApi.create(values)
|
|
223
|
+
message.success('创建成功')
|
|
224
|
+
}
|
|
225
|
+
setModalOpen(false)
|
|
226
|
+
setBridgeSourceCode(null)
|
|
227
|
+
loadScripts()
|
|
228
|
+
} catch (e) {
|
|
229
|
+
if (e?.errorFields) return
|
|
230
|
+
message.error('保存失败: ' + (e?.message || '未知错误'))
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const handleDelete = async (scriptCode) => {
|
|
235
|
+
try {
|
|
236
|
+
await scriptApi.delete(scriptCode)
|
|
237
|
+
message.success('删除成功')
|
|
238
|
+
loadScripts()
|
|
239
|
+
} catch (e) {
|
|
240
|
+
message.error('删除失败')
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const handleRun = (record) => {
|
|
245
|
+
setRunScript(record)
|
|
246
|
+
setRunParams('{}')
|
|
247
|
+
setRunResult('')
|
|
248
|
+
setRunDrawerOpen(true)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const executeRun = async () => {
|
|
252
|
+
if (!runScript) return
|
|
253
|
+
setRunLoading(true)
|
|
254
|
+
try {
|
|
255
|
+
const params = JSON.parse(runParams)
|
|
256
|
+
const res = await scriptApi.run(runScript.scriptCode, params)
|
|
257
|
+
setRunResult(JSON.stringify(res, null, 2))
|
|
258
|
+
} catch (e) {
|
|
259
|
+
setRunResult('Error: ' + (e?.message || '执行失败'))
|
|
260
|
+
} finally {
|
|
261
|
+
setRunLoading(false)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const handlePublish = async (record, exposeAs) => {
|
|
266
|
+
try {
|
|
267
|
+
await scriptApi.publish(record.scriptCode, exposeAs || 'HTTP')
|
|
268
|
+
message.success('发布成功')
|
|
269
|
+
loadScripts()
|
|
270
|
+
} catch (e) {
|
|
271
|
+
message.error('发布失败')
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const handleUnpublish = async (record) => {
|
|
276
|
+
try {
|
|
277
|
+
await scriptApi.unpublish(record.scriptCode)
|
|
278
|
+
message.success('已取消发布')
|
|
279
|
+
loadScripts()
|
|
280
|
+
} catch (e) {
|
|
281
|
+
message.error('操作失败')
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const columns = [
|
|
286
|
+
{
|
|
287
|
+
title: '名称', dataIndex: 'scriptName', key: 'scriptName', width: 180,
|
|
288
|
+
render: (text, r) => <Space><CodeOutlined/><Text strong>{text}</Text></Space>
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
title: '编码', dataIndex: 'scriptCode', key: 'scriptCode', width: 150,
|
|
292
|
+
render: (text) => <Text code copyable={{text}}>{text}</Text>
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
title: 'DSL类型', dataIndex: 'dslType', key: 'dslType', width: 120,
|
|
296
|
+
render: (v) => <Tag>{v}</Tag>
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
title: '暴露方式', dataIndex: 'exposeAs', key: 'exposeAs', width: 120,
|
|
300
|
+
render: (v) => <Tag color={EXPOSE_COLOR[v] || 'default'}>{v}</Tag>
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
title: '状态', dataIndex: 'status', key: 'status', width: 100,
|
|
304
|
+
render: (v) => {
|
|
305
|
+
const s = STATUS_MAP[v] || STATUS_MAP[0];
|
|
306
|
+
return <Tag color={s.color}>{s.text}</Tag>
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
{title: '版本', dataIndex: 'version', key: 'version', width: 80},
|
|
310
|
+
{
|
|
311
|
+
title: 'HTTP路径', dataIndex: 'httpPath', key: 'httpPath', width: 180,
|
|
312
|
+
render: (v) => v ? <Text type="secondary" copyable={{text: v}}>{v}</Text> : '-'
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
title: 'MCP工具名', dataIndex: 'mcpToolName', key: 'mcpToolName', width: 160,
|
|
316
|
+
render: (v) => v ? <Tag color="purple">{v}</Tag> : '-'
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
title: '操作', key: 'action', width: 260, fixed: 'right',
|
|
320
|
+
render: (_, record) => (
|
|
321
|
+
<Space size="small">
|
|
322
|
+
<Tooltip title="运行测试">
|
|
323
|
+
<Button size="small" type="link" icon={<PlayCircleOutlined/>}
|
|
324
|
+
onClick={() => handleRun(record)}>运行</Button>
|
|
325
|
+
</Tooltip>
|
|
326
|
+
<Tooltip title="编辑">
|
|
327
|
+
<Button size="small" type="link" icon={<EditOutlined/>} onClick={() => handleEdit(record)}/>
|
|
328
|
+
</Tooltip>
|
|
329
|
+
{record.exposeAs === 'NONE' ? (
|
|
330
|
+
<Tooltip title="发布为HTTP">
|
|
331
|
+
<Button size="small" type="link" icon={<CloudUploadOutlined/>}
|
|
332
|
+
onClick={() => handlePublish(record, 'HTTP')}>发布</Button>
|
|
333
|
+
</Tooltip>
|
|
334
|
+
) : (
|
|
335
|
+
<Tooltip title="取消发布">
|
|
336
|
+
<Button size="small" type="link" icon={<CloudDownloadOutlined/>}
|
|
337
|
+
onClick={() => handleUnpublish(record)}>取消</Button>
|
|
338
|
+
</Tooltip>
|
|
339
|
+
)}
|
|
340
|
+
<Popconfirm title="确认删除?" onConfirm={() => handleDelete(record.scriptCode)}>
|
|
341
|
+
<Button size="small" type="link" danger icon={<DeleteOutlined/>}/>
|
|
342
|
+
</Popconfirm>
|
|
343
|
+
</Space>
|
|
344
|
+
)
|
|
345
|
+
},
|
|
346
|
+
]
|
|
347
|
+
|
|
348
|
+
return (
|
|
349
|
+
<div>
|
|
350
|
+
{isMock && (
|
|
351
|
+
<div style={{
|
|
352
|
+
padding: '8px 16px',
|
|
353
|
+
marginBottom: 16,
|
|
354
|
+
background: '#fffbe6',
|
|
355
|
+
border: '1px solid #ffe58f',
|
|
356
|
+
borderRadius: 4
|
|
357
|
+
}}>
|
|
358
|
+
<Tag color="orange">Mock</Tag> 后端未连接,当前显示模拟数据
|
|
359
|
+
</div>
|
|
360
|
+
)}
|
|
361
|
+
|
|
362
|
+
<Card title="脚本中心" extra={
|
|
363
|
+
<Space>
|
|
364
|
+
<Button icon={<ImportOutlined/>} onClick={() => setCurlModalOpen(true)}>导入 Curl</Button>
|
|
365
|
+
<Button icon={<CloudServerOutlined/>} onClick={() => setOpenApiModalOpen(true)}>导入
|
|
366
|
+
OpenAPI</Button>
|
|
367
|
+
<Button icon={<PlayCircleOutlined/>} onClick={loadScripts}>刷新</Button>
|
|
368
|
+
<Button type="primary" icon={<PlusOutlined/>} onClick={handleCreate}>新建脚本</Button>
|
|
369
|
+
</Space>
|
|
370
|
+
}>
|
|
371
|
+
<Row gutter={16} style={{marginBottom: 16}}>
|
|
372
|
+
<Col span={6}><Statistic title="总脚本数" value={scripts.length}/></Col>
|
|
373
|
+
<Col span={6}><Statistic title="已发布" value={scripts.filter(s => s.exposeAs !== 'NONE').length}/></Col>
|
|
374
|
+
<Col span={6}><Statistic title="HTTP"
|
|
375
|
+
value={scripts.filter(s => s.exposeAs === 'HTTP' || s.exposeAs === 'BOTH').length}/></Col>
|
|
376
|
+
<Col span={6}><Statistic title="MCP"
|
|
377
|
+
value={scripts.filter(s => s.exposeAs === 'MCP' || s.exposeAs === 'BOTH').length}/></Col>
|
|
378
|
+
</Row>
|
|
379
|
+
|
|
380
|
+
<Table
|
|
381
|
+
dataSource={scripts}
|
|
382
|
+
columns={columns}
|
|
383
|
+
rowKey="id"
|
|
384
|
+
loading={loading}
|
|
385
|
+
pagination={{pageSize: 10}}
|
|
386
|
+
scroll={{x: 1300}}
|
|
387
|
+
size="small"
|
|
388
|
+
/>
|
|
389
|
+
</Card>
|
|
390
|
+
|
|
391
|
+
{/* Create/Edit Modal */}
|
|
392
|
+
<Modal
|
|
393
|
+
title={editing ? '编辑脚本' : '新建脚本'}
|
|
394
|
+
open={modalOpen}
|
|
395
|
+
onCancel={() => setModalOpen(false)}
|
|
396
|
+
onOk={handleSave}
|
|
397
|
+
width={800}
|
|
398
|
+
okText="保存"
|
|
399
|
+
cancelText="取消"
|
|
400
|
+
>
|
|
401
|
+
<Form form={form} layout="vertical" style={{marginTop: 16}}>
|
|
402
|
+
<Row gutter={16}>
|
|
403
|
+
<Col span={12}>
|
|
404
|
+
<Form.Item name="scriptName" label="脚本名称"
|
|
405
|
+
rules={[{required: true, message: '请输入名称'}]}>
|
|
406
|
+
<Input placeholder="e.g. Hello World"/>
|
|
407
|
+
</Form.Item>
|
|
408
|
+
</Col>
|
|
409
|
+
<Col span={12}>
|
|
410
|
+
<Form.Item name="scriptCode" label="脚本编码"
|
|
411
|
+
rules={[{required: true, message: '请输入编码'}]}>
|
|
412
|
+
<Input placeholder="e.g. hello-world" disabled={!!editing}/>
|
|
413
|
+
</Form.Item>
|
|
414
|
+
</Col>
|
|
415
|
+
</Row>
|
|
416
|
+
<Row gutter={16}>
|
|
417
|
+
<Col span={8}>
|
|
418
|
+
<Form.Item name="dslType" label="DSL类型">
|
|
419
|
+
<Select options={DSL_TYPES} onChange={handleDslTypeChange}/>
|
|
420
|
+
</Form.Item>
|
|
421
|
+
</Col>
|
|
422
|
+
<Col span={8}>
|
|
423
|
+
<Form.Item name="exposeAs" label="暴露方式">
|
|
424
|
+
<Select options={EXPOSE_OPTIONS}/>
|
|
425
|
+
</Form.Item>
|
|
426
|
+
</Col>
|
|
427
|
+
<Col span={8}>
|
|
428
|
+
<Form.Item name="version" label="版本">
|
|
429
|
+
<Input placeholder="1.0.0"/>
|
|
430
|
+
</Form.Item>
|
|
431
|
+
</Col>
|
|
432
|
+
</Row>
|
|
433
|
+
<Form.Item name="description" label="描述">
|
|
434
|
+
<Input placeholder="脚本描述"/>
|
|
435
|
+
</Form.Item>
|
|
436
|
+
{isApiBridge ? (
|
|
437
|
+
<Form.Item label="API Bridge 配置">
|
|
438
|
+
<ApiBridgeEditor
|
|
439
|
+
value={bridgeSourceCode}
|
|
440
|
+
onChange={setBridgeSourceCode}
|
|
441
|
+
/>
|
|
442
|
+
</Form.Item>
|
|
443
|
+
) : (
|
|
444
|
+
<>
|
|
445
|
+
<Form.Item name="inputSchema" label="输入 Schema (JSON)">
|
|
446
|
+
<TextArea rows={3}
|
|
447
|
+
placeholder='{"type": "object", "properties": {"name": {"type": "string"}}}'/>
|
|
448
|
+
</Form.Item>
|
|
449
|
+
<Form.Item name="sourceCode" label="源代码"
|
|
450
|
+
rules={[{required: true, message: '请输入源代码'}]}>
|
|
451
|
+
<TextArea
|
|
452
|
+
rows={12}
|
|
453
|
+
placeholder="输入DSL脚本代码..."
|
|
454
|
+
style={{fontFamily: 'monospace', fontSize: 13}}
|
|
455
|
+
/>
|
|
456
|
+
</Form.Item>
|
|
457
|
+
</>
|
|
458
|
+
)}
|
|
459
|
+
</Form>
|
|
460
|
+
</Modal>
|
|
461
|
+
|
|
462
|
+
{/* Run Test Drawer */}
|
|
463
|
+
<Drawer
|
|
464
|
+
title={<Space><PlayCircleOutlined/>运行测试: {runScript?.scriptName}</Space>}
|
|
465
|
+
open={runDrawerOpen}
|
|
466
|
+
onClose={() => setRunDrawerOpen(false)}
|
|
467
|
+
width={600}
|
|
468
|
+
>
|
|
469
|
+
{runScript && (
|
|
470
|
+
<div>
|
|
471
|
+
<Descriptions column={1} size="small" bordered style={{marginBottom: 16}}>
|
|
472
|
+
<Descriptions.Item label="编码">{runScript.scriptCode}</Descriptions.Item>
|
|
473
|
+
<Descriptions.Item label="DSL类型">{runScript.dslType}</Descriptions.Item>
|
|
474
|
+
<Descriptions.Item label="暴露方式">{runScript.exposeAs}</Descriptions.Item>
|
|
475
|
+
</Descriptions>
|
|
476
|
+
|
|
477
|
+
<Paragraph strong>输入参数 (JSON):</Paragraph>
|
|
478
|
+
<TextArea
|
|
479
|
+
rows={5}
|
|
480
|
+
value={runParams}
|
|
481
|
+
onChange={(e) => setRunParams(e.target.value)}
|
|
482
|
+
style={{fontFamily: 'monospace', fontSize: 13, marginBottom: 12}}
|
|
483
|
+
/>
|
|
484
|
+
|
|
485
|
+
<Button
|
|
486
|
+
type="primary"
|
|
487
|
+
icon={<PlayCircleOutlined/>}
|
|
488
|
+
loading={runLoading}
|
|
489
|
+
onClick={executeRun}
|
|
490
|
+
block
|
|
491
|
+
>
|
|
492
|
+
执行
|
|
493
|
+
</Button>
|
|
494
|
+
|
|
495
|
+
{runResult && (
|
|
496
|
+
<div style={{marginTop: 16}}>
|
|
497
|
+
<Paragraph strong>执行结果:</Paragraph>
|
|
498
|
+
<pre style={{
|
|
499
|
+
background: '#f5f5f5',
|
|
500
|
+
padding: 12,
|
|
501
|
+
borderRadius: 4,
|
|
502
|
+
maxHeight: 400,
|
|
503
|
+
overflow: 'auto',
|
|
504
|
+
fontSize: 13,
|
|
505
|
+
fontFamily: 'monospace',
|
|
506
|
+
whiteSpace: 'pre-wrap',
|
|
507
|
+
wordBreak: 'break-all',
|
|
508
|
+
}}>
|
|
509
|
+
{runResult}
|
|
510
|
+
</pre>
|
|
511
|
+
</div>
|
|
512
|
+
)}
|
|
513
|
+
</div>
|
|
514
|
+
)}
|
|
515
|
+
</Drawer>
|
|
516
|
+
|
|
517
|
+
{/* 导入 Curl */}
|
|
518
|
+
<CurlImportModal
|
|
519
|
+
open={curlModalOpen}
|
|
520
|
+
onClose={() => setCurlModalOpen(false)}
|
|
521
|
+
onSuccess={loadScripts}
|
|
522
|
+
/>
|
|
523
|
+
|
|
524
|
+
{/* 导入 OpenAPI */}
|
|
525
|
+
<OpenApiImportModal
|
|
526
|
+
open={openApiModalOpen}
|
|
527
|
+
onClose={() => setOpenApiModalOpen(false)}
|
|
528
|
+
onSuccess={loadScripts}
|
|
529
|
+
/>
|
|
530
|
+
</div>
|
|
531
|
+
)
|
|
532
|
+
}
|