@zhin.js/client 1.0.4 → 1.0.5
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/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/router/index.d.ts +25 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +49 -0
- package/dist/router/index.js.map +1 -0
- package/dist/store/index.d.ts +19 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +67 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/reducers/config.d.ts +54 -0
- package/dist/store/reducers/config.d.ts.map +1 -0
- package/dist/store/reducers/config.js +78 -0
- package/dist/store/reducers/config.js.map +1 -0
- package/dist/store/reducers/index.d.ts +13 -0
- package/dist/store/reducers/index.d.ts.map +1 -0
- package/dist/store/reducers/index.js +11 -0
- package/dist/store/reducers/index.js.map +1 -0
- package/dist/store/reducers/route.d.ts +37 -0
- package/dist/store/reducers/route.d.ts.map +1 -0
- package/dist/store/reducers/route.js +85 -0
- package/dist/store/reducers/route.js.map +1 -0
- package/dist/store/reducers/script.d.ts +17 -0
- package/dist/store/reducers/script.d.ts.map +1 -0
- package/dist/store/reducers/script.js +74 -0
- package/dist/store/reducers/script.js.map +1 -0
- package/dist/store/reducers/ui.d.ts +14 -0
- package/dist/store/reducers/ui.d.ts.map +1 -0
- package/dist/store/reducers/ui.js +23 -0
- package/dist/store/reducers/ui.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/websocket/hooks.d.ts +55 -0
- package/dist/websocket/hooks.d.ts.map +1 -0
- package/dist/websocket/hooks.js +225 -0
- package/dist/websocket/hooks.js.map +1 -0
- package/dist/websocket/index.d.ts +13 -0
- package/dist/websocket/index.d.ts.map +1 -0
- package/dist/websocket/index.js +31 -0
- package/dist/websocket/index.js.map +1 -0
- package/dist/websocket/instance.d.ts +18 -0
- package/dist/websocket/instance.d.ts.map +1 -0
- package/dist/websocket/instance.js +39 -0
- package/dist/websocket/instance.js.map +1 -0
- package/dist/websocket/manager.d.ts +110 -0
- package/dist/websocket/manager.d.ts.map +1 -0
- package/dist/websocket/manager.js +341 -0
- package/dist/websocket/manager.js.map +1 -0
- package/dist/websocket/messageHandler.d.ts +48 -0
- package/dist/websocket/messageHandler.d.ts.map +1 -0
- package/dist/websocket/messageHandler.js +140 -0
- package/dist/websocket/messageHandler.js.map +1 -0
- package/dist/websocket/types.d.ts +125 -0
- package/dist/websocket/types.d.ts.map +1 -0
- package/dist/websocket/types.js +43 -0
- package/dist/websocket/types.js.map +1 -0
- package/package.json +8 -18
- package/app/index.html +0 -13
- package/app/postcss.config.js +0 -5
- package/app/src/components/PluginConfigForm/BasicFieldRenderers.tsx +0 -253
- package/app/src/components/PluginConfigForm/CollectionFieldRenderers.tsx +0 -261
- package/app/src/components/PluginConfigForm/CompositeFieldRenderers.tsx +0 -105
- package/app/src/components/PluginConfigForm/FieldRenderer.tsx +0 -110
- package/app/src/components/PluginConfigForm/NestedFieldRenderer.tsx +0 -95
- package/app/src/components/PluginConfigForm/index.tsx +0 -237
- package/app/src/components/PluginConfigForm/types.ts +0 -46
- package/app/src/components/ThemeToggle.tsx +0 -21
- package/app/src/hooks/useTheme.ts +0 -17
- package/app/src/layouts/dashboard.tsx +0 -259
- package/app/src/main.tsx +0 -121
- package/app/src/pages/dashboard-bots.tsx +0 -198
- package/app/src/pages/dashboard-home.tsx +0 -301
- package/app/src/pages/dashboard-logs.tsx +0 -298
- package/app/src/pages/dashboard-plugin-detail.tsx +0 -360
- package/app/src/pages/dashboard-plugins.tsx +0 -166
- package/app/src/style.css +0 -1105
- package/app/src/theme/index.ts +0 -92
- package/app/tailwind.config.js +0 -70
- package/app/tsconfig.json +0 -16
- /package/{src → client}/index.ts +0 -0
- /package/{src → client}/router/index.tsx +0 -0
- /package/{src → client}/store/index.ts +0 -0
- /package/{src → client}/store/reducers/config.ts +0 -0
- /package/{src → client}/store/reducers/index.ts +0 -0
- /package/{src → client}/store/reducers/route.ts +0 -0
- /package/{src → client}/store/reducers/script.ts +0 -0
- /package/{src → client}/store/reducers/ui.ts +0 -0
- /package/{src → client}/types.ts +0 -0
- /package/{src → client}/websocket/hooks.ts +0 -0
- /package/{src → client}/websocket/index.ts +0 -0
- /package/{src → client}/websocket/instance.ts +0 -0
- /package/{src → client}/websocket/manager.ts +0 -0
- /package/{src → client}/websocket/messageHandler.ts +0 -0
- /package/{src → client}/websocket/types.ts +0 -0
|
@@ -1,360 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
2
|
-
import { useParams, useNavigate } from 'react-router'
|
|
3
|
-
import * as Themes from '@radix-ui/themes'
|
|
4
|
-
import { Icons } from '@zhin.js/client'
|
|
5
|
-
import {PluginConfigForm} from '../components/PluginConfigForm'
|
|
6
|
-
|
|
7
|
-
const { Flex, Box, Spinner, Text, Callout, Heading, Badge, Grid, Card, Button, Code, Separator, ScrollArea, Dialog } = Themes
|
|
8
|
-
|
|
9
|
-
interface PluginDetail {
|
|
10
|
-
name: string
|
|
11
|
-
filename: string
|
|
12
|
-
status: 'active' | 'inactive'
|
|
13
|
-
description: string
|
|
14
|
-
commands: Array<{
|
|
15
|
-
name: string
|
|
16
|
-
}>
|
|
17
|
-
components: Array<{
|
|
18
|
-
name: string
|
|
19
|
-
props: Record<string, any>
|
|
20
|
-
type: string
|
|
21
|
-
}>
|
|
22
|
-
middlewares: Array<{
|
|
23
|
-
id: string
|
|
24
|
-
type: string
|
|
25
|
-
}>
|
|
26
|
-
contexts: Array<{
|
|
27
|
-
name: string
|
|
28
|
-
description: string
|
|
29
|
-
}>
|
|
30
|
-
crons: Array<{
|
|
31
|
-
id: string
|
|
32
|
-
pattern: string
|
|
33
|
-
running: boolean
|
|
34
|
-
}>
|
|
35
|
-
definitions: Array<{
|
|
36
|
-
name: string
|
|
37
|
-
fields: string[]
|
|
38
|
-
}>
|
|
39
|
-
statistics: {
|
|
40
|
-
commandCount: number
|
|
41
|
-
componentCount: number
|
|
42
|
-
middlewareCount: number
|
|
43
|
-
contextCount: number
|
|
44
|
-
cronCount: number
|
|
45
|
-
definitionCount: number
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export default function DashboardPluginDetail() {
|
|
50
|
-
const { name } = useParams<{ name: string }>()
|
|
51
|
-
const navigate = useNavigate()
|
|
52
|
-
const [plugin, setPlugin] = useState<PluginDetail | null>(null)
|
|
53
|
-
const [loading, setLoading] = useState(true)
|
|
54
|
-
const [error, setError] = useState<string | null>(null)
|
|
55
|
-
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
if (name) {
|
|
58
|
-
fetchPluginDetail(name)
|
|
59
|
-
}
|
|
60
|
-
}, [name])
|
|
61
|
-
|
|
62
|
-
const fetchPluginDetail = async (pluginName: string) => {
|
|
63
|
-
try {
|
|
64
|
-
const res = await fetch(`/api/plugins/${encodeURIComponent(pluginName)}`, { credentials: 'include' })
|
|
65
|
-
if (!res.ok) throw new Error('API 请求失败')
|
|
66
|
-
|
|
67
|
-
const data = await res.json()
|
|
68
|
-
if (data.success) {
|
|
69
|
-
setPlugin(data.data)
|
|
70
|
-
setError(null)
|
|
71
|
-
} else {
|
|
72
|
-
throw new Error('数据格式错误')
|
|
73
|
-
}
|
|
74
|
-
} catch (err) {
|
|
75
|
-
setError((err as Error).message)
|
|
76
|
-
} finally {
|
|
77
|
-
setLoading(false)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (loading) {
|
|
84
|
-
return (
|
|
85
|
-
<Flex align="center" justify="center" className="h-full">
|
|
86
|
-
<Flex direction="column" align="center" gap="3">
|
|
87
|
-
<Spinner size="3" />
|
|
88
|
-
<Text size="2" color="gray">加载中...</Text>
|
|
89
|
-
</Flex>
|
|
90
|
-
</Flex>
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (error || !plugin) {
|
|
95
|
-
return (
|
|
96
|
-
<Box>
|
|
97
|
-
<Button variant="ghost" onClick={() => navigate('/plugins')} mb="4" size="2">
|
|
98
|
-
<Icons.ArrowLeft className="w-4 h-4" />
|
|
99
|
-
返回
|
|
100
|
-
</Button>
|
|
101
|
-
<Callout.Root color="red">
|
|
102
|
-
<Callout.Icon>
|
|
103
|
-
<Icons.AlertCircle />
|
|
104
|
-
</Callout.Icon>
|
|
105
|
-
<Callout.Text>
|
|
106
|
-
加载失败: {error || '插件不存在'}
|
|
107
|
-
</Callout.Text>
|
|
108
|
-
</Callout.Root>
|
|
109
|
-
</Box>
|
|
110
|
-
)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
<Box>
|
|
115
|
-
{/* 头部 */}
|
|
116
|
-
<Flex direction="column" gap="3" mb="4">
|
|
117
|
-
<Button variant="ghost" onClick={() => navigate('/plugins')} size="2">
|
|
118
|
-
<Icons.ArrowLeft className="w-4 h-4" />
|
|
119
|
-
返回
|
|
120
|
-
</Button>
|
|
121
|
-
|
|
122
|
-
<Flex align="center" gap="3">
|
|
123
|
-
<Flex align="center" justify="center" className="w-12 h-12 rounded-xl bg-blue-500/10 dark:bg-blue-400/10">
|
|
124
|
-
<Icons.Package className="w-6 h-6 text-blue-600 dark:text-blue-400" />
|
|
125
|
-
</Flex>
|
|
126
|
-
<Flex direction="column" gap="1">
|
|
127
|
-
<Flex align="center" gap="2">
|
|
128
|
-
<Heading size="5">{plugin.name}</Heading>
|
|
129
|
-
<Badge color={plugin.status === 'active' ? 'green' : 'gray'} size="1">
|
|
130
|
-
{plugin.status === 'active' ? '运行中' : '已停止'}
|
|
131
|
-
</Badge>
|
|
132
|
-
</Flex>
|
|
133
|
-
<Text size="2" color="gray">{plugin.description || '暂无描述'}</Text>
|
|
134
|
-
</Flex>
|
|
135
|
-
</Flex>
|
|
136
|
-
</Flex>
|
|
137
|
-
|
|
138
|
-
{/* 插件配置折叠面板 */}
|
|
139
|
-
<PluginConfigForm
|
|
140
|
-
pluginName={plugin.name}
|
|
141
|
-
onSuccess={() => {
|
|
142
|
-
// 配置更新会通过 WebSocket 自动同步
|
|
143
|
-
}}
|
|
144
|
-
/>
|
|
145
|
-
|
|
146
|
-
<Separator size="4" my="4" />
|
|
147
|
-
|
|
148
|
-
{/* 统计概览 - 紧凑卡片 */}
|
|
149
|
-
<Grid columns={{ initial: '2', sm: '3', md: '6' }} gap="2" mb="4">
|
|
150
|
-
<Card size="1">
|
|
151
|
-
<Flex direction="column" align="center" gap="1" p="2">
|
|
152
|
-
<Icons.Terminal className="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
|
153
|
-
<Text size="4" weight="bold">{plugin.statistics.commandCount}</Text>
|
|
154
|
-
<Text size="1" color="gray">命令</Text>
|
|
155
|
-
</Flex>
|
|
156
|
-
</Card>
|
|
157
|
-
|
|
158
|
-
<Card size="1">
|
|
159
|
-
<Flex direction="column" align="center" gap="1" p="2">
|
|
160
|
-
<Icons.Box className="w-4 h-4 text-green-600 dark:text-green-400" />
|
|
161
|
-
<Text size="4" weight="bold">{plugin.statistics.componentCount}</Text>
|
|
162
|
-
<Text size="1" color="gray">组件</Text>
|
|
163
|
-
</Flex>
|
|
164
|
-
</Card>
|
|
165
|
-
|
|
166
|
-
<Card size="1">
|
|
167
|
-
<Flex direction="column" align="center" gap="1" p="2">
|
|
168
|
-
<Icons.Layers className="w-4 h-4 text-purple-600 dark:text-purple-400" />
|
|
169
|
-
<Text size="4" weight="bold">{plugin.statistics.middlewareCount}</Text>
|
|
170
|
-
<Text size="1" color="gray">中间件</Text>
|
|
171
|
-
</Flex>
|
|
172
|
-
</Card>
|
|
173
|
-
|
|
174
|
-
<Card size="1">
|
|
175
|
-
<Flex direction="column" align="center" gap="1" p="2">
|
|
176
|
-
<Icons.Database className="w-4 h-4 text-orange-600 dark:text-orange-400" />
|
|
177
|
-
<Text size="4" weight="bold">{plugin.statistics.contextCount}</Text>
|
|
178
|
-
<Text size="1" color="gray">上下文</Text>
|
|
179
|
-
</Flex>
|
|
180
|
-
</Card>
|
|
181
|
-
|
|
182
|
-
<Card size="1">
|
|
183
|
-
<Flex direction="column" align="center" gap="1" p="2">
|
|
184
|
-
<Icons.Clock className="w-4 h-4 text-amber-600 dark:text-amber-400" />
|
|
185
|
-
<Text size="4" weight="bold">{plugin.statistics.cronCount}</Text>
|
|
186
|
-
<Text size="1" color="gray">定时任务</Text>
|
|
187
|
-
</Flex>
|
|
188
|
-
</Card>
|
|
189
|
-
|
|
190
|
-
<Card size="1">
|
|
191
|
-
<Flex direction="column" align="center" gap="1" p="2">
|
|
192
|
-
<Icons.FileText className="w-4 h-4 text-cyan-600 dark:text-cyan-400" />
|
|
193
|
-
<Text size="4" weight="bold">{plugin.statistics.definitionCount}</Text>
|
|
194
|
-
<Text size="1" color="gray">数据模型</Text>
|
|
195
|
-
</Flex>
|
|
196
|
-
</Card>
|
|
197
|
-
</Grid>
|
|
198
|
-
|
|
199
|
-
{/* 详细信息 - 紧凑布局 */}
|
|
200
|
-
<Grid columns={{ initial: '1', md: '2' }} gap="3">
|
|
201
|
-
{/* 命令列表 */}
|
|
202
|
-
{plugin.commands.length > 0 && (
|
|
203
|
-
<Card size="2">
|
|
204
|
-
<Flex direction="column" gap="2" p="3">
|
|
205
|
-
<Flex align="center" gap="2">
|
|
206
|
-
<Icons.Terminal className="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
|
207
|
-
<Heading size="3">命令</Heading>
|
|
208
|
-
<Badge size="1">{plugin.commands.length}</Badge>
|
|
209
|
-
</Flex>
|
|
210
|
-
<Separator size="4" />
|
|
211
|
-
<Flex direction="column" gap="2" className="max-h-60 overflow-y-auto">
|
|
212
|
-
{plugin.commands.map((cmd, index) => (
|
|
213
|
-
<Box key={index} className="rounded-lg bg-gray-50 dark:bg-gray-900 p-2">
|
|
214
|
-
<Flex direction="column" gap="1">
|
|
215
|
-
<Flex align="center" gap="2">
|
|
216
|
-
<Code size="2">{cmd.name}</Code>
|
|
217
|
-
</Flex>
|
|
218
|
-
</Flex>
|
|
219
|
-
</Box>
|
|
220
|
-
))}
|
|
221
|
-
</Flex>
|
|
222
|
-
</Flex>
|
|
223
|
-
</Card>
|
|
224
|
-
)}
|
|
225
|
-
|
|
226
|
-
{/* 组件列表 */}
|
|
227
|
-
{plugin.components.length > 0 && (
|
|
228
|
-
<Card size="2">
|
|
229
|
-
<Flex direction="column" gap="2" p="3">
|
|
230
|
-
<Flex align="center" gap="2">
|
|
231
|
-
<Icons.Box className="w-4 h-4 text-green-600 dark:text-green-400" />
|
|
232
|
-
<Heading size="3">组件</Heading>
|
|
233
|
-
<Badge size="1">{plugin.components.length}</Badge>
|
|
234
|
-
</Flex>
|
|
235
|
-
<Separator size="4" />
|
|
236
|
-
<Flex direction="column" gap="2" className="max-h-60 overflow-y-auto">
|
|
237
|
-
{plugin.components.map((comp, index) => (
|
|
238
|
-
<Box key={index} className="rounded-lg bg-gray-50 dark:bg-gray-900 p-2">
|
|
239
|
-
<Flex direction="column" gap="1">
|
|
240
|
-
<Flex align="center" gap="2">
|
|
241
|
-
<Code size="2">{comp.name}</Code>
|
|
242
|
-
<Badge size="1" variant="soft">{comp.type}</Badge>
|
|
243
|
-
</Flex>
|
|
244
|
-
<Text size="1" color="gray">
|
|
245
|
-
属性数: {Object.keys(comp.props).length}
|
|
246
|
-
</Text>
|
|
247
|
-
</Flex>
|
|
248
|
-
</Box>
|
|
249
|
-
))}
|
|
250
|
-
</Flex>
|
|
251
|
-
</Flex>
|
|
252
|
-
</Card>
|
|
253
|
-
)}
|
|
254
|
-
|
|
255
|
-
{/* 中间件列表 */}
|
|
256
|
-
{plugin.middlewares.length > 0 && (
|
|
257
|
-
<Card size="2">
|
|
258
|
-
<Flex direction="column" gap="2" p="3">
|
|
259
|
-
<Flex align="center" gap="2">
|
|
260
|
-
<Icons.Layers className="w-4 h-4 text-purple-600 dark:text-purple-400" />
|
|
261
|
-
<Heading size="3">中间件</Heading>
|
|
262
|
-
<Badge size="1">{plugin.middlewares.length}</Badge>
|
|
263
|
-
</Flex>
|
|
264
|
-
<Separator size="4" />
|
|
265
|
-
<Flex direction="column" gap="2" className="max-h-60 overflow-y-auto">
|
|
266
|
-
{plugin.middlewares.map((mw, index) => (
|
|
267
|
-
<Box key={index} className="rounded-lg bg-gray-50 dark:bg-gray-900 p-2">
|
|
268
|
-
<Flex align="center" gap="2">
|
|
269
|
-
<Code size="2">{mw.id}</Code>
|
|
270
|
-
<Badge size="1" variant="soft">{mw.type}</Badge>
|
|
271
|
-
</Flex>
|
|
272
|
-
</Box>
|
|
273
|
-
))}
|
|
274
|
-
</Flex>
|
|
275
|
-
</Flex>
|
|
276
|
-
</Card>
|
|
277
|
-
)}
|
|
278
|
-
|
|
279
|
-
{/* 上下文列表 */}
|
|
280
|
-
{plugin.contexts.length > 0 && (
|
|
281
|
-
<Card size="2">
|
|
282
|
-
<Flex direction="column" gap="2" p="3">
|
|
283
|
-
<Flex align="center" gap="2">
|
|
284
|
-
<Icons.Database className="w-4 h-4 text-orange-600 dark:text-orange-400" />
|
|
285
|
-
<Heading size="3">上下文</Heading>
|
|
286
|
-
<Badge size="1">{plugin.contexts.length}</Badge>
|
|
287
|
-
</Flex>
|
|
288
|
-
<Separator size="4" />
|
|
289
|
-
<Flex direction="column" gap="2" className="max-h-60 overflow-y-auto">
|
|
290
|
-
{plugin.contexts.map((ctx, index) => (
|
|
291
|
-
<Box key={index} className="rounded-lg bg-gray-50 dark:bg-gray-900 p-2">
|
|
292
|
-
<Flex direction="column" gap="1">
|
|
293
|
-
<Code size="2">{ctx.name}</Code>
|
|
294
|
-
<Text size="1" color="gray">{ctx.description}</Text>
|
|
295
|
-
</Flex>
|
|
296
|
-
</Box>
|
|
297
|
-
))}
|
|
298
|
-
</Flex>
|
|
299
|
-
</Flex>
|
|
300
|
-
</Card>
|
|
301
|
-
)}
|
|
302
|
-
|
|
303
|
-
{/* 定时任务列表 */}
|
|
304
|
-
{plugin.crons.length > 0 && (
|
|
305
|
-
<Card size="2">
|
|
306
|
-
<Flex direction="column" gap="2" p="3">
|
|
307
|
-
<Flex align="center" gap="2">
|
|
308
|
-
<Icons.Clock className="w-4 h-4 text-amber-600 dark:text-amber-400" />
|
|
309
|
-
<Heading size="3">定时任务</Heading>
|
|
310
|
-
<Badge size="1">{plugin.crons.length}</Badge>
|
|
311
|
-
</Flex>
|
|
312
|
-
<Separator size="4" />
|
|
313
|
-
<Flex direction="column" gap="2" className="max-h-60 overflow-y-auto">
|
|
314
|
-
{plugin.crons.map((cron, index) => (
|
|
315
|
-
<Box key={index} className="rounded-lg bg-gray-50 dark:bg-gray-900 p-2">
|
|
316
|
-
<Flex justify="between" align="center">
|
|
317
|
-
<Flex direction="column" gap="1">
|
|
318
|
-
<Code size="2">{cron.id}</Code>
|
|
319
|
-
<Text size="1" color="gray">{cron.pattern}</Text>
|
|
320
|
-
</Flex>
|
|
321
|
-
<Badge color={cron.running ? 'green' : 'gray'} size="1">
|
|
322
|
-
{cron.running ? '运行中' : '已停止'}
|
|
323
|
-
</Badge>
|
|
324
|
-
</Flex>
|
|
325
|
-
</Box>
|
|
326
|
-
))}
|
|
327
|
-
</Flex>
|
|
328
|
-
</Flex>
|
|
329
|
-
</Card>
|
|
330
|
-
)}
|
|
331
|
-
|
|
332
|
-
{/* 数据模型列表 */}
|
|
333
|
-
{plugin.definitions.length > 0 && (
|
|
334
|
-
<Card size="2">
|
|
335
|
-
<Flex direction="column" gap="2" p="3">
|
|
336
|
-
<Flex align="center" gap="2">
|
|
337
|
-
<Icons.FileText className="w-4 h-4 text-cyan-600 dark:text-cyan-400" />
|
|
338
|
-
<Heading size="3">数据模型</Heading>
|
|
339
|
-
<Badge size="1">{plugin.definitions.length}</Badge>
|
|
340
|
-
</Flex>
|
|
341
|
-
<Separator size="4" />
|
|
342
|
-
<Flex direction="column" gap="2" className="max-h-60 overflow-y-auto">
|
|
343
|
-
{plugin.definitions.map((definition, index) => (
|
|
344
|
-
<Box key={index} className="rounded-lg bg-gray-50 dark:bg-gray-900 p-2">
|
|
345
|
-
<Flex direction="column" gap="1">
|
|
346
|
-
<Code size="2">{definition.name}</Code>
|
|
347
|
-
<Text size="1" color="gray">
|
|
348
|
-
字段: {definition.fields.join(', ')}
|
|
349
|
-
</Text>
|
|
350
|
-
</Flex>
|
|
351
|
-
</Box>
|
|
352
|
-
))}
|
|
353
|
-
</Flex>
|
|
354
|
-
</Flex>
|
|
355
|
-
</Card>
|
|
356
|
-
)}
|
|
357
|
-
</Grid>
|
|
358
|
-
</Box>
|
|
359
|
-
)
|
|
360
|
-
}
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
2
|
-
import { useNavigate } from 'react-router'
|
|
3
|
-
import * as Themes from '@radix-ui/themes'
|
|
4
|
-
import { Icons } from '@zhin.js/client'
|
|
5
|
-
|
|
6
|
-
const { Flex, Box, Spinner, Text, Callout, Heading, Badge, Grid, Card, Separator } = Themes
|
|
7
|
-
|
|
8
|
-
interface Plugin {
|
|
9
|
-
name: string
|
|
10
|
-
status: 'active' | 'inactive'
|
|
11
|
-
commandCount: number
|
|
12
|
-
componentCount: number
|
|
13
|
-
middlewareCount: number
|
|
14
|
-
contextCount: number
|
|
15
|
-
description: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export default function DashboardPlugins() {
|
|
19
|
-
const navigate = useNavigate()
|
|
20
|
-
const [plugins, setPlugins] = useState<Plugin[]>([])
|
|
21
|
-
const [loading, setLoading] = useState(true)
|
|
22
|
-
const [error, setError] = useState<string | null>(null)
|
|
23
|
-
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
fetchPlugins()
|
|
26
|
-
const interval = setInterval(fetchPlugins, 10000)
|
|
27
|
-
return () => clearInterval(interval)
|
|
28
|
-
}, [])
|
|
29
|
-
|
|
30
|
-
const fetchPlugins = async () => {
|
|
31
|
-
try {
|
|
32
|
-
const res = await fetch('/api/plugins', { credentials: 'include' })
|
|
33
|
-
if (!res.ok) throw new Error('API 请求失败')
|
|
34
|
-
|
|
35
|
-
const data = await res.json()
|
|
36
|
-
if (data.success) {
|
|
37
|
-
setPlugins(data.data)
|
|
38
|
-
setError(null)
|
|
39
|
-
} else {
|
|
40
|
-
throw new Error('数据格式错误')
|
|
41
|
-
}
|
|
42
|
-
} catch (err) {
|
|
43
|
-
setError((err as Error).message)
|
|
44
|
-
} finally {
|
|
45
|
-
setLoading(false)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (loading) {
|
|
50
|
-
return (
|
|
51
|
-
<Flex align="center" justify="center" className="h-full">
|
|
52
|
-
<Flex direction="column" align="center" gap="3">
|
|
53
|
-
<Spinner size="3" />
|
|
54
|
-
<Text size="2" color="gray">加载中...</Text>
|
|
55
|
-
</Flex>
|
|
56
|
-
</Flex>
|
|
57
|
-
)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (error) {
|
|
61
|
-
return (
|
|
62
|
-
<Flex align="center" justify="center" className="h-full">
|
|
63
|
-
<Callout.Root color="red">
|
|
64
|
-
<Callout.Icon>
|
|
65
|
-
<Icons.AlertCircle />
|
|
66
|
-
</Callout.Icon>
|
|
67
|
-
<Callout.Text>
|
|
68
|
-
加载失败: {error}
|
|
69
|
-
</Callout.Text>
|
|
70
|
-
</Callout.Root>
|
|
71
|
-
</Flex>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return (
|
|
76
|
-
<Box>
|
|
77
|
-
{/* 页面标题 */}
|
|
78
|
-
<Flex direction="column" gap="2" mb="4">
|
|
79
|
-
<Heading size="6">插件管理</Heading>
|
|
80
|
-
<Flex align="center" gap="2">
|
|
81
|
-
<Text size="2" color="gray">共 {plugins.length} 个插件</Text>
|
|
82
|
-
<Badge color="green" size="1">{plugins.filter(p => p.status === 'active').length}</Badge>
|
|
83
|
-
<Text size="2" color="gray">个运行中</Text>
|
|
84
|
-
</Flex>
|
|
85
|
-
</Flex>
|
|
86
|
-
|
|
87
|
-
{/* 插件列表 - 紧凑网格 */}
|
|
88
|
-
<Grid columns={{ initial: '1', sm: '2', lg: '3' }} gap="3">
|
|
89
|
-
{plugins.map((plugin, index) => (
|
|
90
|
-
<Card
|
|
91
|
-
key={`${plugin.name}-${index}`}
|
|
92
|
-
className="cursor-pointer hover-lift transition-smooth"
|
|
93
|
-
onClick={() => navigate(`/plugins/${encodeURIComponent(plugin.name)}`)}
|
|
94
|
-
>
|
|
95
|
-
<Flex direction="column" gap="2" p="2">
|
|
96
|
-
{/* 头部 - 紧凑布局 */}
|
|
97
|
-
<Flex justify="between" align="center">
|
|
98
|
-
<Flex align="center" gap="2">
|
|
99
|
-
<Flex align="center" justify="center" className="w-8 h-8 rounded-lg bg-blue-500/10 dark:bg-blue-400/10">
|
|
100
|
-
<Icons.Package className="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
|
101
|
-
</Flex>
|
|
102
|
-
<Heading size="3">{plugin.name}</Heading>
|
|
103
|
-
</Flex>
|
|
104
|
-
<Badge
|
|
105
|
-
color={plugin.status === 'active' ? 'green' : 'gray'}
|
|
106
|
-
size="1"
|
|
107
|
-
>
|
|
108
|
-
{plugin.status === 'active' ? '运行中' : '已停止'}
|
|
109
|
-
</Badge>
|
|
110
|
-
</Flex>
|
|
111
|
-
|
|
112
|
-
{/* 描述 - 限制两行 */}
|
|
113
|
-
<Text size="1" color="gray" className="line-clamp-2">
|
|
114
|
-
{plugin.description || '暂无描述'}
|
|
115
|
-
</Text>
|
|
116
|
-
|
|
117
|
-
<Separator size="4" my="1" />
|
|
118
|
-
|
|
119
|
-
{/* 统计信息 - 紧凑网格 */}
|
|
120
|
-
<Grid columns={{ initial: '1', sm: '2', lg: '4' }} gap="1">
|
|
121
|
-
<Flex gap="2" justify="center" align="center" className="rounded-md bg-blue-500/5 dark:bg-blue-400/5 p-1.5">
|
|
122
|
-
<Icons.Terminal className="w-3 h-3 text-blue-600 dark:text-blue-400" />
|
|
123
|
-
<Text size="2" weight="bold" className="mt-0.5">{plugin.commandCount}</Text>
|
|
124
|
-
<Text size="1" color="gray">命令</Text>
|
|
125
|
-
</Flex>
|
|
126
|
-
|
|
127
|
-
<Flex gap="2" justify="center" align="center" className="rounded-md bg-green-500/5 dark:bg-green-400/5 p-1.5">
|
|
128
|
-
<Icons.Box className="w-3 h-3 text-green-600 dark:text-green-400" />
|
|
129
|
-
<Text size="2" weight="bold" className="mt-0.5">{plugin.componentCount}</Text>
|
|
130
|
-
<Text size="1" color="gray">组件</Text>
|
|
131
|
-
</Flex>
|
|
132
|
-
|
|
133
|
-
<Flex gap="2" justify="center" align="center" className="rounded-md bg-purple-500/5 dark:bg-purple-400/5 p-1.5">
|
|
134
|
-
<Icons.Layers className="w-3 h-3 text-purple-600 dark:text-purple-400" />
|
|
135
|
-
<Text size="2" weight="bold" className="mt-0.5">{plugin.middlewareCount}</Text>
|
|
136
|
-
<Text size="1" color="gray">中间件</Text>
|
|
137
|
-
</Flex>
|
|
138
|
-
|
|
139
|
-
<Flex gap="2" justify="center" align="center" className="rounded-md bg-orange-500/5 dark:bg-orange-400/5 p-1.5">
|
|
140
|
-
<Icons.Database className="w-3 h-3 text-orange-600 dark:text-orange-400" />
|
|
141
|
-
<Text size="2" weight="bold" className="mt-0.5">{plugin.contextCount}</Text>
|
|
142
|
-
<Text size="1" color="gray">上下文</Text>
|
|
143
|
-
</Flex>
|
|
144
|
-
</Grid>
|
|
145
|
-
</Flex>
|
|
146
|
-
</Card>
|
|
147
|
-
))}
|
|
148
|
-
</Grid>
|
|
149
|
-
|
|
150
|
-
{/* 空状态 */}
|
|
151
|
-
{plugins.length === 0 && (
|
|
152
|
-
<Card className="mt-6">
|
|
153
|
-
<Flex direction="column" align="center" gap="3" py="8">
|
|
154
|
-
<Flex align="center" justify="center" className="w-16 h-16 rounded-full bg-gray-100 dark:bg-gray-800">
|
|
155
|
-
<Icons.Package className="w-8 h-8 text-gray-400" />
|
|
156
|
-
</Flex>
|
|
157
|
-
<Flex direction="column" align="center" gap="1">
|
|
158
|
-
<Heading size="4">暂无插件</Heading>
|
|
159
|
-
<Text size="2" color="gray">请先安装并启用插件</Text>
|
|
160
|
-
</Flex>
|
|
161
|
-
</Flex>
|
|
162
|
-
</Card>
|
|
163
|
-
)}
|
|
164
|
-
</Box>
|
|
165
|
-
)
|
|
166
|
-
}
|