@yuku123/z-subscribe-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/dist/z-subscribe-frontend-component.es.js +3833 -0
- package/dist/z-subscribe-frontend-component.umd.js +51 -0
- package/package.json +42 -0
- package/src/index.js +4 -0
- package/src/pages/RunHistory.jsx +161 -0
- package/src/pages/SubscribeApp.jsx +25 -0
- package/src/pages/SubscriptionEdit.jsx +296 -0
- package/src/pages/SubscriptionList.jsx +460 -0
- package/src/services/api.js +76 -0
- package/src/utils/request.js +5 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import {useEffect, useState} from 'react'
|
|
2
|
+
import {useNavigate} from 'react-router-dom'
|
|
3
|
+
import {
|
|
4
|
+
Alert,
|
|
5
|
+
Button,
|
|
6
|
+
Card,
|
|
7
|
+
Drawer,
|
|
8
|
+
Form,
|
|
9
|
+
Input,
|
|
10
|
+
message,
|
|
11
|
+
Modal,
|
|
12
|
+
Popconfirm,
|
|
13
|
+
Space,
|
|
14
|
+
Table,
|
|
15
|
+
Tabs,
|
|
16
|
+
Tag,
|
|
17
|
+
Timeline,
|
|
18
|
+
Tooltip
|
|
19
|
+
} from 'antd'
|
|
20
|
+
import {
|
|
21
|
+
ApiOutlined,
|
|
22
|
+
CheckCircleOutlined,
|
|
23
|
+
CloseCircleOutlined,
|
|
24
|
+
DeleteOutlined,
|
|
25
|
+
EditOutlined,
|
|
26
|
+
ExclamationCircleOutlined,
|
|
27
|
+
PauseCircleOutlined,
|
|
28
|
+
PlayCircleOutlined,
|
|
29
|
+
PlusOutlined,
|
|
30
|
+
ProjectOutlined,
|
|
31
|
+
ReloadOutlined,
|
|
32
|
+
ThunderboltOutlined
|
|
33
|
+
} from '@ant-design/icons'
|
|
34
|
+
import request from '../utils/request'
|
|
35
|
+
|
|
36
|
+
// 抽取器类型 -> 中文 + 颜色
|
|
37
|
+
const EXTRACTOR_LABEL = {
|
|
38
|
+
STANDARD_JSON: {label: '标准 JSON', color: 'blue'},
|
|
39
|
+
RSS_XML: {label: 'RSS 2.0', color: 'orange'},
|
|
40
|
+
ATOM_XML: {label: 'Atom 1.0', color: 'gold'},
|
|
41
|
+
CUSTOM_SCRIPT: {label: '自定义脚本', color: 'purple'},
|
|
42
|
+
INTERNAL_BEAN: {label: '内部 Bean', color: 'cyan'},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 状态 -> 颜色
|
|
46
|
+
const STATUS_TAG = {
|
|
47
|
+
PENDING_VERIFY: {color: 'default', text: '待验证'},
|
|
48
|
+
VERIFIED: {color: 'green', text: '已验证'},
|
|
49
|
+
INVALID_AUTH: {color: 'red', text: '鉴权失败'},
|
|
50
|
+
FATAL: {color: 'red', text: '永久失败'},
|
|
51
|
+
DISABLED: {color: 'default', text: '已停用'},
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const RUN_STATUS_TAG = {
|
|
55
|
+
PENDING: {color: 'default'},
|
|
56
|
+
RUNNING: {color: 'processing'},
|
|
57
|
+
SUCCESS: {color: 'green'},
|
|
58
|
+
EMPTY: {color: 'default'},
|
|
59
|
+
FAILED: {color: 'red'},
|
|
60
|
+
FATAL: {color: 'red'},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function SubscriptionList() {
|
|
64
|
+
const navigate = useNavigate()
|
|
65
|
+
const [data, setData] = useState([])
|
|
66
|
+
const [loading, setLoading] = useState(false)
|
|
67
|
+
const [detail, setDetail] = useState(null) // dry-run 详情 Drawer
|
|
68
|
+
const [runs, setRuns] = useState([]) // 选中订阅的 run 列表
|
|
69
|
+
const [runItems, setRunItems] = useState({}) // runId -> items
|
|
70
|
+
const [activeRunId, setActiveRunId] = useState(null)
|
|
71
|
+
const [selectedItemIds, setSelectedItemIds] = useState([])
|
|
72
|
+
|
|
73
|
+
const fetchData = async () => {
|
|
74
|
+
setLoading(true)
|
|
75
|
+
try {
|
|
76
|
+
const res = await request.get('/subscribe/subscription/list')
|
|
77
|
+
if (res && res.success !== false) {
|
|
78
|
+
setData(Array.isArray(res.data) ? res.data : (Array.isArray(res) ? res : []))
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {
|
|
81
|
+
message.error('获取订阅列表失败: ' + (e?.message || ''))
|
|
82
|
+
} finally {
|
|
83
|
+
setLoading(false)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
fetchData()
|
|
89
|
+
}, [])
|
|
90
|
+
|
|
91
|
+
const handleDelete = (record) => {
|
|
92
|
+
Modal.confirm({
|
|
93
|
+
title: '确认删除',
|
|
94
|
+
icon: <ExclamationCircleOutlined/>,
|
|
95
|
+
content: `确定要删除订阅 "${record.name}" 吗? 运行历史会保留.`,
|
|
96
|
+
onOk: async () => {
|
|
97
|
+
try {
|
|
98
|
+
const res = await request.delete(`/subscribe/subscription/${record.id}`)
|
|
99
|
+
if (res && res.success !== false) {
|
|
100
|
+
message.success('删除成功')
|
|
101
|
+
fetchData()
|
|
102
|
+
} else {
|
|
103
|
+
message.error(res?.message || '删除失败')
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
message.error('删除失败: ' + (e?.message || ''))
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const handleEnable = async (id) => {
|
|
113
|
+
try {
|
|
114
|
+
const res = await request.post(`/subscribe/subscription/${id}/enable`)
|
|
115
|
+
if (res && res.success !== false) {
|
|
116
|
+
message.success('已启用')
|
|
117
|
+
fetchData()
|
|
118
|
+
} else {
|
|
119
|
+
message.error(res?.message || '启用失败')
|
|
120
|
+
}
|
|
121
|
+
} catch (e) {
|
|
122
|
+
message.error('启用失败: ' + (e?.message || ''))
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const handleDisable = async (id) => {
|
|
127
|
+
try {
|
|
128
|
+
const res = await request.post(`/subscribe/subscription/${id}/disable`)
|
|
129
|
+
if (res && res.success !== false) {
|
|
130
|
+
message.success('已停用')
|
|
131
|
+
fetchData()
|
|
132
|
+
} else {
|
|
133
|
+
message.error(res?.message || '停用失败')
|
|
134
|
+
}
|
|
135
|
+
} catch (e) {
|
|
136
|
+
message.error('停用失败: ' + (e?.message || ''))
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const handleTrigger = async (record) => {
|
|
141
|
+
Modal.confirm({
|
|
142
|
+
title: '手动触发',
|
|
143
|
+
content: `将立即执行一次 "${record.name}" 的抽取, 同步返回 runId.`,
|
|
144
|
+
onOk: async () => {
|
|
145
|
+
try {
|
|
146
|
+
const res = await request.post(`/subscribe/subscription/${record.id}/trigger?triggerBy=ceo`)
|
|
147
|
+
if (res && (res.data !== undefined || res.success !== false)) {
|
|
148
|
+
const runId = res.data ?? res
|
|
149
|
+
message.success(`已触发, runId = ${runId}`)
|
|
150
|
+
openRuns(record)
|
|
151
|
+
} else {
|
|
152
|
+
message.error(res?.message || '触发失败')
|
|
153
|
+
}
|
|
154
|
+
} catch (e) {
|
|
155
|
+
message.error('触发失败: ' + (e?.message || ''))
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const handleDryRun = async (record) => {
|
|
162
|
+
try {
|
|
163
|
+
const res = await request.post(`/subscribe/subscription/${record.id}/dry-run`)
|
|
164
|
+
setDetail({sub: record, result: res?.data ?? res})
|
|
165
|
+
} catch (e) {
|
|
166
|
+
setDetail({sub: record, result: {success: false, errorMessage: e?.message || '请求失败'}})
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const openRuns = async (record) => {
|
|
171
|
+
try {
|
|
172
|
+
const res = await request.get(`/subscribe/run/list?subscriptionId=${record.id}&limit=20`)
|
|
173
|
+
const list = res?.data ?? res ?? []
|
|
174
|
+
setRuns(Array.isArray(list) ? list : [])
|
|
175
|
+
setActiveRunId(null)
|
|
176
|
+
setRunItems({})
|
|
177
|
+
setSelectedItemIds([])
|
|
178
|
+
setDetail({sub: record, tab: 'runs'})
|
|
179
|
+
} catch (e) {
|
|
180
|
+
message.error('获取运行历史失败')
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const loadRunItems = async (runId) => {
|
|
185
|
+
if (runItems[runId]) {
|
|
186
|
+
setActiveRunId(runId)
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
const res = await request.get(`/subscribe/run/${runId}/items?limit=200`)
|
|
191
|
+
const list = res?.data ?? res ?? []
|
|
192
|
+
setRunItems(prev => ({...prev, [runId]: Array.isArray(list) ? list : []}))
|
|
193
|
+
setActiveRunId(runId)
|
|
194
|
+
} catch (e) {
|
|
195
|
+
message.error('获取抽取项失败')
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const columns = [
|
|
200
|
+
{
|
|
201
|
+
title: 'ID',
|
|
202
|
+
dataIndex: 'id',
|
|
203
|
+
width: 70,
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
title: '名称',
|
|
207
|
+
dataIndex: 'name',
|
|
208
|
+
render: (name, r) => (
|
|
209
|
+
<Space orientation="vertical" size={0}>
|
|
210
|
+
<span style={{fontWeight: 500}}>{name}</span>
|
|
211
|
+
{r.description && <span style={{color: '#999', fontSize: 12}}>{r.description}</span>}
|
|
212
|
+
</Space>
|
|
213
|
+
),
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
title: '数据源',
|
|
217
|
+
dataIndex: 'sourceType',
|
|
218
|
+
width: 90,
|
|
219
|
+
render: (t) => <Tag>{t}</Tag>,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
title: '抽取器',
|
|
223
|
+
dataIndex: 'extractorType',
|
|
224
|
+
width: 140,
|
|
225
|
+
render: (t) => {
|
|
226
|
+
const meta = EXTRACTOR_LABEL[t] || {label: t, color: 'default'}
|
|
227
|
+
return <Tag color={meta.color}>{meta.label}</Tag>
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
title: 'Cron',
|
|
232
|
+
dataIndex: 'cronExpr',
|
|
233
|
+
width: 150,
|
|
234
|
+
render: (c) => <code style={{fontSize: 12}}>{c}</code>,
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
title: '状态',
|
|
238
|
+
dataIndex: 'status',
|
|
239
|
+
width: 110,
|
|
240
|
+
render: (s) => {
|
|
241
|
+
const m = STATUS_TAG[s] || {color: 'default', text: s}
|
|
242
|
+
return <Tag color={m.color}>{m.text}</Tag>
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
title: '启用',
|
|
247
|
+
dataIndex: 'enabled',
|
|
248
|
+
width: 80,
|
|
249
|
+
render: (e, r) => e ? <Tag color="green">是</Tag> : <Tag>否</Tag>,
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
title: '最近 run',
|
|
253
|
+
dataIndex: 'lastStatus',
|
|
254
|
+
width: 110,
|
|
255
|
+
render: (s, r) => s ? (
|
|
256
|
+
<Tooltip title={r.lastError || ''}>
|
|
257
|
+
<Tag color={RUN_STATUS_TAG[s]?.color || 'default'}>{s}</Tag>
|
|
258
|
+
</Tooltip>
|
|
259
|
+
) : <span style={{color: '#ccc'}}>-</span>,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
title: '累计',
|
|
263
|
+
width: 130,
|
|
264
|
+
render: (_, r) => (
|
|
265
|
+
<Space size={4}>
|
|
266
|
+
<Tag color="green">✓ {r.successCountTotal || 0}</Tag>
|
|
267
|
+
<Tag color="red">✗ {r.failCountTotal || 0}</Tag>
|
|
268
|
+
</Space>
|
|
269
|
+
),
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
title: '操作',
|
|
273
|
+
width: 280,
|
|
274
|
+
fixed: 'right',
|
|
275
|
+
render: (_, r) => (
|
|
276
|
+
<Space size={4} wrap>
|
|
277
|
+
<Button type="link" size="small" icon={<EditOutlined/>}
|
|
278
|
+
onClick={() => navigate(`/subscribe/edit/${r.id}`)}>编辑</Button>
|
|
279
|
+
<Button type="link" size="small" icon={<ThunderboltOutlined/>}
|
|
280
|
+
onClick={() => handleDryRun(r)}>试运行</Button>
|
|
281
|
+
<Button type="link" size="small" icon={<PlayCircleOutlined/>}
|
|
282
|
+
onClick={() => handleTrigger(r)}>触发</Button>
|
|
283
|
+
<Button type="link" size="small"
|
|
284
|
+
onClick={() => openRuns(r)}>历史</Button>
|
|
285
|
+
{r.enabled ? (
|
|
286
|
+
<Button type="link" size="small" icon={<PauseCircleOutlined/>}
|
|
287
|
+
onClick={() => handleDisable(r.id)}>停用</Button>
|
|
288
|
+
) : (
|
|
289
|
+
<Button type="link" size="small" icon={<CheckCircleOutlined/>}
|
|
290
|
+
onClick={() => handleEnable(r.id)}>启用</Button>
|
|
291
|
+
)}
|
|
292
|
+
<Popconfirm title="确认删除?" onConfirm={() => handleDelete(r)} okText="删除" cancelText="取消">
|
|
293
|
+
<Button type="link" size="small" danger icon={<DeleteOutlined/>}>删除</Button>
|
|
294
|
+
</Popconfirm>
|
|
295
|
+
</Space>
|
|
296
|
+
),
|
|
297
|
+
},
|
|
298
|
+
]
|
|
299
|
+
|
|
300
|
+
return (
|
|
301
|
+
<div style={{padding: 24}}>
|
|
302
|
+
<Card
|
|
303
|
+
title={<Space><ApiOutlined/>订阅中心 - 数据源订阅</Space>}
|
|
304
|
+
extra={
|
|
305
|
+
<Space>
|
|
306
|
+
<Button icon={<ReloadOutlined/>} onClick={fetchData}>刷新</Button>
|
|
307
|
+
<Button type="primary" icon={<PlusOutlined/>}
|
|
308
|
+
onClick={() => navigate('/subscribe/new')}>新建订阅</Button>
|
|
309
|
+
</Space>
|
|
310
|
+
}
|
|
311
|
+
>
|
|
312
|
+
<Alert
|
|
313
|
+
type="info"
|
|
314
|
+
showIcon
|
|
315
|
+
style={{marginBottom: 16}}
|
|
316
|
+
message="订阅中心负责把三方 / 内部 / RSS 等数据源按 Cron 定时抽取, 归一化后落到本地, 供 CEO 立项和 Agent 协作使用."
|
|
317
|
+
/>
|
|
318
|
+
<Table
|
|
319
|
+
rowKey="id"
|
|
320
|
+
columns={columns}
|
|
321
|
+
dataSource={data}
|
|
322
|
+
loading={loading}
|
|
323
|
+
scroll={{x: 1400}}
|
|
324
|
+
size="middle"
|
|
325
|
+
pagination={{pageSize: 20, showSizeChanger: true, showTotal: t => `共 ${t} 条`}}
|
|
326
|
+
/>
|
|
327
|
+
</Card>
|
|
328
|
+
|
|
329
|
+
{/* dry-run 详情 Drawer */}
|
|
330
|
+
<Drawer
|
|
331
|
+
title={detail?.sub ? `试运行 - ${detail.sub.name}` : '运行历史'}
|
|
332
|
+
open={!!detail}
|
|
333
|
+
onClose={() => setDetail(null)}
|
|
334
|
+
width={900}
|
|
335
|
+
>
|
|
336
|
+
{detail?.tab === 'runs' ? (
|
|
337
|
+
<Tabs
|
|
338
|
+
activeKey={activeRunId ? 'items' : 'list'}
|
|
339
|
+
onChange={(k) => k === 'list' && setActiveRunId(null)}
|
|
340
|
+
items={[
|
|
341
|
+
{
|
|
342
|
+
key: 'list',
|
|
343
|
+
label: `运行列表 (${runs.length})`,
|
|
344
|
+
children: (
|
|
345
|
+
<Table
|
|
346
|
+
rowKey="id"
|
|
347
|
+
size="small"
|
|
348
|
+
dataSource={runs}
|
|
349
|
+
pagination={false}
|
|
350
|
+
onRow={(r) => ({onClick: () => loadRunItems(r.id)})}
|
|
351
|
+
columns={[
|
|
352
|
+
{title: 'runId', dataIndex: 'id', width: 70},
|
|
353
|
+
{title: '计划', dataIndex: 'scheduledAt', width: 160,
|
|
354
|
+
render: t => t ? new Date(t).toLocaleString() : '-'},
|
|
355
|
+
{title: '耗时(ms)', dataIndex: 'durationMs', width: 90},
|
|
356
|
+
{title: '状态', dataIndex: 'status', width: 90,
|
|
357
|
+
render: s => <Tag color={RUN_STATUS_TAG[s]?.color}>{s}</Tag>},
|
|
358
|
+
{title: '拉取/成功/重复/死信', width: 200,
|
|
359
|
+
render: (_, r) => `${r.fetchedCount} / ${r.successCount} / ${r.duplicateCount || 0} / ${r.deadLetterCount}`},
|
|
360
|
+
{title: '错误', dataIndex: 'errorMessage',
|
|
361
|
+
render: e => e ? <span style={{color: '#cf1322', fontSize: 12}}>{e}</span> : '-'},
|
|
362
|
+
]}
|
|
363
|
+
/>
|
|
364
|
+
),
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
key: 'items',
|
|
368
|
+
label: activeRunId ? `run#${activeRunId} 抽取项` : '抽取项',
|
|
369
|
+
disabled: !activeRunId,
|
|
370
|
+
children: (
|
|
371
|
+
<Space orientation="vertical" style={{width: '100%'}}>
|
|
372
|
+
<Space>
|
|
373
|
+
<Tag color="blue">已选 {selectedItemIds.length} 条</Tag>
|
|
374
|
+
<Button size="small" type="primary"
|
|
375
|
+
icon={<ProjectOutlined/>}
|
|
376
|
+
disabled={selectedItemIds.length === 0}
|
|
377
|
+
onClick={() => {
|
|
378
|
+
const ids = selectedItemIds.join(',')
|
|
379
|
+
navigate(`/project/new?subscriptionId=${detail.sub.id}&itemIds=${ids}`)
|
|
380
|
+
}}>
|
|
381
|
+
用所选 {selectedItemIds.length} 条立项
|
|
382
|
+
</Button>
|
|
383
|
+
</Space>
|
|
384
|
+
<Table
|
|
385
|
+
rowKey="id"
|
|
386
|
+
size="small"
|
|
387
|
+
dataSource={runItems[activeRunId] || []}
|
|
388
|
+
pagination={{pageSize: 20}}
|
|
389
|
+
rowSelection={{
|
|
390
|
+
selectedRowKeys: selectedItemIds,
|
|
391
|
+
onChange: setSelectedItemIds,
|
|
392
|
+
}}
|
|
393
|
+
columns={[
|
|
394
|
+
{title: 'id', dataIndex: 'dedupKey', width: 180, ellipsis: true},
|
|
395
|
+
{title: 'title', dataIndex: 'normalized',
|
|
396
|
+
render: (n) => {
|
|
397
|
+
try { const o = JSON.parse(n); return o.title || '(无标题)' }
|
|
398
|
+
catch { return n }
|
|
399
|
+
}},
|
|
400
|
+
{title: 'link', dataIndex: 'normalized',
|
|
401
|
+
render: (n) => {
|
|
402
|
+
try { const o = JSON.parse(n); return o.link ? <a href={o.link} target="_blank">↗</a> : '-' }
|
|
403
|
+
catch { return '-' }
|
|
404
|
+
}},
|
|
405
|
+
{title: '状态', dataIndex: 'status', width: 110,
|
|
406
|
+
render: s => <Tag>{s}</Tag>},
|
|
407
|
+
]}
|
|
408
|
+
/>
|
|
409
|
+
</Space>
|
|
410
|
+
),
|
|
411
|
+
},
|
|
412
|
+
]}
|
|
413
|
+
/>
|
|
414
|
+
) : (
|
|
415
|
+
<DryRunResultView result={detail?.result}/>
|
|
416
|
+
)}
|
|
417
|
+
</Drawer>
|
|
418
|
+
</div>
|
|
419
|
+
)
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function DryRunResultView({result}) {
|
|
423
|
+
if (!result) return null
|
|
424
|
+
if (!result.success) {
|
|
425
|
+
return <Alert type="error" showIcon message="试运行失败" description={result.errorMessage || '未知错误'}/>
|
|
426
|
+
}
|
|
427
|
+
return (
|
|
428
|
+
<Space orientation="vertical" style={{width: '100%'}} size="middle">
|
|
429
|
+
<Card size="small" title="调用结果">
|
|
430
|
+
<Space size="large" wrap>
|
|
431
|
+
<span><b>HTTP:</b> {result.httpStatus || '-'}</span>
|
|
432
|
+
<span><b>耗时:</b> {result.latencyMs}ms</span>
|
|
433
|
+
<span><b>大小:</b> {result.responseSize}B</span>
|
|
434
|
+
<span><b>样本:</b> {result.sampleCount} 条</span>
|
|
435
|
+
</Space>
|
|
436
|
+
</Card>
|
|
437
|
+
{result.firstItem && (
|
|
438
|
+
<Card size="small" title="第一条样本 (归一化后)">
|
|
439
|
+
<pre style={{background: '#f5f5f5', padding: 12, borderRadius: 4, maxHeight: 300, overflow: 'auto'}}>
|
|
440
|
+
{JSON.stringify(result.firstItem, null, 2)}
|
|
441
|
+
</pre>
|
|
442
|
+
</Card>
|
|
443
|
+
)}
|
|
444
|
+
{result.responsePreview && (
|
|
445
|
+
<Card size="small" title="响应预览 (前 4KB)">
|
|
446
|
+
<pre style={{background: '#f5f5f5', padding: 12, borderRadius: 4, maxHeight: 300, overflow: 'auto', fontSize: 12}}>
|
|
447
|
+
{result.responsePreview}
|
|
448
|
+
</pre>
|
|
449
|
+
</Card>
|
|
450
|
+
)}
|
|
451
|
+
{result.fieldErrors && result.fieldErrors.length > 0 && (
|
|
452
|
+
<Alert type="warning" showIcon message="字段问题" description={
|
|
453
|
+
<ul>{result.fieldErrors.map((e, i) => <li key={i}>{e}</li>)}</ul>
|
|
454
|
+
}/>
|
|
455
|
+
)}
|
|
456
|
+
</Space>
|
|
457
|
+
)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export default SubscriptionList
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 兼容垫片: 之前在 z-opc-main-starter-frontend/services/ 里的所有 axios 客户端
|
|
3
|
+
* 这里直接 re-export z-frontend-common 的 utils/request, 并提供 makeApi 工具.
|
|
4
|
+
*/
|
|
5
|
+
import request, {authRequest, setupInterceptors} from '../utils/request'
|
|
6
|
+
export {request as default, authRequest, setupInterceptors}
|
|
7
|
+
|
|
8
|
+
function makeApi(name) {
|
|
9
|
+
return {
|
|
10
|
+
list: (params) => request.get(`/${name}/list`, {params}).then(r => r.data),
|
|
11
|
+
page: (params) => request.get(`/${name}/page`, {params}).then(r => r.data),
|
|
12
|
+
get: (id) => request.get(`/${name}/${id}`).then(r => r.data),
|
|
13
|
+
create: (data) => request.post(`/${name}`, data).then(r => r.data),
|
|
14
|
+
update: (id, data) => request.put(`/${name}/${id}`, data).then(r => r.data),
|
|
15
|
+
delete: (id) => request.delete(`/${name}/${id}`).then(r => r.data),
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const configApi = makeApi('config')
|
|
20
|
+
export const approvalApi = makeApi('approval')
|
|
21
|
+
export const designerApi = makeApi('designer')
|
|
22
|
+
export const mcpApi = makeApi('mcp')
|
|
23
|
+
export const mockPlatformApi = makeApi('mock')
|
|
24
|
+
export const namingApi = makeApi('naming')
|
|
25
|
+
export const opsApi = makeApi('ops')
|
|
26
|
+
export const ossApi = makeApi('oss')
|
|
27
|
+
export const scriptApi = makeApi('script')
|
|
28
|
+
export const skillApi = makeApi('skill')
|
|
29
|
+
export const agentApi = makeApi('agent')
|
|
30
|
+
export const flowApi = makeApi('flow')
|
|
31
|
+
export const traceApi = makeApi('trace')
|
|
32
|
+
export const llmApi = makeApi('llm')
|
|
33
|
+
export const productApi = makeApi('product')
|
|
34
|
+
export const sceneApi = makeApi('scene')
|
|
35
|
+
export const ctcAcAccountApi = makeApi('ctc/ac/accounts')
|
|
36
|
+
export const ctcAcLoginLogApi = makeApi('ctc/ac/login-log')
|
|
37
|
+
export const ctcAcTenantApi = makeApi('ctc/ac/tenants')
|
|
38
|
+
export const ctcAcDomainApi = makeApi('ctc/ac/domains')
|
|
39
|
+
export const ctcAcOrgApi = makeApi('ctc/ac/orgs')
|
|
40
|
+
export const ctcAcDeptApi = makeApi('ctc/ac/depts')
|
|
41
|
+
export const ctcAcGroupApi = makeApi('ctc/ac/groups')
|
|
42
|
+
export const ctcAuthorizationApi = makeApi('ctc/authorization')
|
|
43
|
+
export const metaAppApi = makeApi('meta-app')
|
|
44
|
+
export const jobApi = makeApi('schedule/job')
|
|
45
|
+
export const webideApi = makeApi('webide')
|
|
46
|
+
export const privateConfigApi = makeApi('private-config')
|
|
47
|
+
export const ctcSurlApi = makeApi('ctc/surl')
|
|
48
|
+
export const ctcUserApi = makeApi('ctc/user')
|
|
49
|
+
export const agentTeamApi = makeApi('agent/team')
|
|
50
|
+
export const workspaceApi = makeApi('agent/team/workspace')
|
|
51
|
+
|
|
52
|
+
// === 项目 / 任务 / 鉴权 ===
|
|
53
|
+
export const login = (data) => request.post('/ctc/auth/login', data).then(r => r.data)
|
|
54
|
+
export const getCurrentUser = () => {
|
|
55
|
+
try { return Promise.resolve(JSON.parse(localStorage.getItem('userInfo') || 'null')) }
|
|
56
|
+
catch { return Promise.resolve(null) }
|
|
57
|
+
}
|
|
58
|
+
export const switchTenant = (tenantCode, domainCode) =>
|
|
59
|
+
authRequest.post('/ctc/auth/switch-tenant', {tenantCode: tenantCode || '', domainCode: domainCode || ''}).then(r => r.data)
|
|
60
|
+
export const listMyProjects = () => authRequest.get('/project/user/list').then(r => r.data)
|
|
61
|
+
export const createProject = (data) => request.post('/project', data).then(r => r.data)
|
|
62
|
+
export const updateProject = (id, data) => request.put(`/project/${id}`, data).then(r => r.data)
|
|
63
|
+
export const archiveProject = (id) => request.put(`/project/${id}/archive`).then(r => r.data)
|
|
64
|
+
export const listTasksByProject = (projectId) => request.get('/task/project/list', {params: {projectId}}).then(r => r.data)
|
|
65
|
+
export const createTask = (data) => request.post('/task', data).then(r => r.data)
|
|
66
|
+
export const completeTask = (id) => request.post('/task/complete', null, {params: {taskId: id}}).then(r => r.data)
|
|
67
|
+
export const moveTask = (taskId, targetListId, position) =>
|
|
68
|
+
request.put('/task/move', null, {params: {taskId, targetListId, position}}).then(r => r.data)
|
|
69
|
+
export const listTasksByList = (listId) => request.get('/task/list', {params: {listId}}).then(r => r.data)
|
|
70
|
+
export const getDomainByTenantCode = async () => [{domainCode: 'default', domainName: '默认域'}]
|
|
71
|
+
export const getTenantList = async () => []
|
|
72
|
+
export const getDynamicMenu = async () => []
|
|
73
|
+
export const userOrgRelApi = {
|
|
74
|
+
usersByDept: () => Promise.resolve([]),
|
|
75
|
+
usersByGroup: () => Promise.resolve([]),
|
|
76
|
+
}
|