work-agent 0.1.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.
Files changed (245) hide show
  1. package/README.md +234 -0
  2. package/app/(admin)/approvals/page.tsx +16 -0
  3. package/app/(admin)/audit/page.tsx +18 -0
  4. package/app/(admin)/layout.tsx +47 -0
  5. package/app/(admin)/scheduled-tasks/page.tsx +17 -0
  6. package/app/(admin)/settings/page.tsx +46 -0
  7. package/app/(admin)/skills/[name]/page.tsx +378 -0
  8. package/app/(admin)/skills/page.tsx +406 -0
  9. package/app/(admin)/statistics/page.tsx +416 -0
  10. package/app/(admin)/tickets/[id]/page.tsx +348 -0
  11. package/app/(admin)/tickets/new/page.tsx +309 -0
  12. package/app/(admin)/tickets/page.tsx +27 -0
  13. package/app/api/audit/route.ts +30 -0
  14. package/app/api/auth/feishu/callback/route.ts +72 -0
  15. package/app/api/auth/feishu/login/route.ts +17 -0
  16. package/app/api/auth/feishu/sso/route.ts +78 -0
  17. package/app/api/auth/login/route.ts +85 -0
  18. package/app/api/auth/oauth/route.ts +168 -0
  19. package/app/api/config/providers/route.ts +105 -0
  20. package/app/api/config/route.ts +115 -0
  21. package/app/api/config/status/route.ts +56 -0
  22. package/app/api/config/test/route.ts +212 -0
  23. package/app/api/documents/[id]/route.ts +88 -0
  24. package/app/api/documents/route.ts +53 -0
  25. package/app/api/health/route.ts +32 -0
  26. package/app/api/knowledge/[id]/route.ts +152 -0
  27. package/app/api/knowledge/from-session/route.ts +27 -0
  28. package/app/api/knowledge/route.ts +100 -0
  29. package/app/api/market/knowledge/[id]/route.ts +92 -0
  30. package/app/api/market/knowledge/route.ts +130 -0
  31. package/app/api/marketplace/skills/[id]/approve/route.ts +68 -0
  32. package/app/api/marketplace/skills/[id]/certify/route.ts +54 -0
  33. package/app/api/marketplace/skills/[id]/install/route.ts +180 -0
  34. package/app/api/marketplace/skills/[id]/promote-to-system/route.ts +219 -0
  35. package/app/api/marketplace/skills/[id]/rate/route.ts +90 -0
  36. package/app/api/marketplace/skills/[id]/ratings/route.ts +55 -0
  37. package/app/api/marketplace/skills/[id]/reject/route.ts +68 -0
  38. package/app/api/marketplace/skills/[id]/route.ts +177 -0
  39. package/app/api/marketplace/skills/route.ts +235 -0
  40. package/app/api/memory/route.ts +40 -0
  41. package/app/api/my/files/[id]/route.ts +52 -0
  42. package/app/api/my/files/route.ts +230 -0
  43. package/app/api/my/knowledge/route.ts +36 -0
  44. package/app/api/pi-chat/route.ts +443 -0
  45. package/app/api/recommend/route.ts +38 -0
  46. package/app/api/scheduled-tasks/[id]/execute/route.ts +132 -0
  47. package/app/api/scheduled-tasks/[id]/route.ts +165 -0
  48. package/app/api/scheduled-tasks/[id]/toggle/route.ts +53 -0
  49. package/app/api/scheduled-tasks/route.ts +101 -0
  50. package/app/api/sessions/[id]/messages/route.ts +212 -0
  51. package/app/api/sessions/route.ts +101 -0
  52. package/app/api/share/file/[id]/route.ts +37 -0
  53. package/app/api/skills/[name]/execute/route.ts +121 -0
  54. package/app/api/skills/[name]/route.ts +167 -0
  55. package/app/api/skills/create/route.ts +65 -0
  56. package/app/api/skills/generate/route.ts +405 -0
  57. package/app/api/skills/installed/route.ts +151 -0
  58. package/app/api/skills/route.ts +174 -0
  59. package/app/api/skills/translate/route.ts +40 -0
  60. package/app/api/skills/user/[name]/route.ts +159 -0
  61. package/app/api/skills/user/route.ts +90 -0
  62. package/app/api/statistics/route.ts +94 -0
  63. package/app/api/task-executions/[id]/route.ts +34 -0
  64. package/app/api/task-executions/route.ts +29 -0
  65. package/app/api/tickets/[id]/approve/route.ts +129 -0
  66. package/app/api/tickets/[id]/execute/route.ts +201 -0
  67. package/app/api/tickets/[id]/route.ts +127 -0
  68. package/app/api/tickets/route.ts +103 -0
  69. package/app/api/user/skills/route.ts +175 -0
  70. package/app/api/users/route.ts +80 -0
  71. package/app/chat/page.tsx +5 -0
  72. package/app/globals.css +84 -0
  73. package/app/h5/layout.tsx +5 -0
  74. package/app/h5/mobile-approvals-page.tsx +167 -0
  75. package/app/h5/mobile-chat-page.tsx +951 -0
  76. package/app/h5/mobile-profile-page.tsx +147 -0
  77. package/app/h5/mobile-tickets-page.tsx +121 -0
  78. package/app/h5/page.tsx +23 -0
  79. package/app/h5/ticket-action-buttons.tsx +80 -0
  80. package/app/layout.tsx +26 -0
  81. package/app/login/page.tsx +318 -0
  82. package/app/market/knowledge/[id]/page.tsx +77 -0
  83. package/app/market/knowledge/page.tsx +358 -0
  84. package/app/market/layout.tsx +29 -0
  85. package/app/market/page.tsx +18 -0
  86. package/app/market/skills/page.tsx +397 -0
  87. package/app/my/files/page.tsx +511 -0
  88. package/app/my/knowledge/[id]/page.tsx +271 -0
  89. package/app/my/knowledge/new/page.tsx +234 -0
  90. package/app/my/knowledge/page.tsx +248 -0
  91. package/app/my/layout.tsx +32 -0
  92. package/app/my/memory/page.tsx +164 -0
  93. package/app/my/page.tsx +18 -0
  94. package/app/my/scheduled-tasks/[id]/edit/page.tsx +290 -0
  95. package/app/my/scheduled-tasks/[id]/executions/page.tsx +275 -0
  96. package/app/my/scheduled-tasks/[id]/page.tsx +284 -0
  97. package/app/my/scheduled-tasks/new/page.tsx +230 -0
  98. package/app/my/scheduled-tasks/page.tsx +27 -0
  99. package/app/my/skills/[name]/page.tsx +320 -0
  100. package/app/my/skills/new/page.tsx +394 -0
  101. package/app/my/skills/page.tsx +303 -0
  102. package/app/page.tsx +2288 -0
  103. package/app/share/[sessionId]/page.tsx +226 -0
  104. package/app/share/file/[id]/page.tsx +140 -0
  105. package/bin/README.md +63 -0
  106. package/bin/generate-api-system +300 -0
  107. package/bin/postinstall.js +95 -0
  108. package/bin/work-agent.js +173 -0
  109. package/components/ai-elements/agent.tsx +142 -0
  110. package/components/ai-elements/artifact.tsx +149 -0
  111. package/components/ai-elements/attachments.tsx +427 -0
  112. package/components/ai-elements/audio-player.tsx +232 -0
  113. package/components/ai-elements/canvas.tsx +26 -0
  114. package/components/ai-elements/chain-of-thought.tsx +223 -0
  115. package/components/ai-elements/checkpoint.tsx +72 -0
  116. package/components/ai-elements/code-block.tsx +555 -0
  117. package/components/ai-elements/commit.tsx +449 -0
  118. package/components/ai-elements/confirmation.tsx +173 -0
  119. package/components/ai-elements/connection.tsx +28 -0
  120. package/components/ai-elements/context.tsx +410 -0
  121. package/components/ai-elements/controls.tsx +19 -0
  122. package/components/ai-elements/conversation.tsx +167 -0
  123. package/components/ai-elements/edge.tsx +144 -0
  124. package/components/ai-elements/environment-variables.tsx +325 -0
  125. package/components/ai-elements/file-tree.tsx +298 -0
  126. package/components/ai-elements/image.tsx +25 -0
  127. package/components/ai-elements/inline-citation.tsx +294 -0
  128. package/components/ai-elements/jsx-preview.tsx +250 -0
  129. package/components/ai-elements/message.tsx +367 -0
  130. package/components/ai-elements/mic-selector.tsx +372 -0
  131. package/components/ai-elements/model-selector.tsx +214 -0
  132. package/components/ai-elements/node.tsx +72 -0
  133. package/components/ai-elements/open-in-chat.tsx +367 -0
  134. package/components/ai-elements/package-info.tsx +235 -0
  135. package/components/ai-elements/panel.tsx +16 -0
  136. package/components/ai-elements/persona.tsx +280 -0
  137. package/components/ai-elements/plan.tsx +144 -0
  138. package/components/ai-elements/prompt-input.tsx +1341 -0
  139. package/components/ai-elements/queue.tsx +275 -0
  140. package/components/ai-elements/reasoning.tsx +355 -0
  141. package/components/ai-elements/sandbox.tsx +133 -0
  142. package/components/ai-elements/schema-display.tsx +473 -0
  143. package/components/ai-elements/shimmer.tsx +78 -0
  144. package/components/ai-elements/snippet.tsx +141 -0
  145. package/components/ai-elements/sources.tsx +78 -0
  146. package/components/ai-elements/speech-input.tsx +324 -0
  147. package/components/ai-elements/stack-trace.tsx +531 -0
  148. package/components/ai-elements/suggestion.tsx +58 -0
  149. package/components/ai-elements/task.tsx +88 -0
  150. package/components/ai-elements/terminal.tsx +277 -0
  151. package/components/ai-elements/test-results.tsx +497 -0
  152. package/components/ai-elements/tool.tsx +174 -0
  153. package/components/ai-elements/toolbar.tsx +17 -0
  154. package/components/ai-elements/transcription.tsx +126 -0
  155. package/components/ai-elements/voice-selector.tsx +525 -0
  156. package/components/ai-elements/web-preview.tsx +282 -0
  157. package/components/audit-log-list.tsx +114 -0
  158. package/components/chat/EmptyPreviewState.tsx +12 -0
  159. package/components/chat/KnowledgePickerDialog.tsx +464 -0
  160. package/components/chat/KnowledgePreview.tsx +70 -0
  161. package/components/chat/KnowledgePreviewPanel.tsx +86 -0
  162. package/components/chat/MentionInput.tsx +309 -0
  163. package/components/chat/OrganizeDialog.tsx +258 -0
  164. package/components/chat/RecommendationBanner.tsx +94 -0
  165. package/components/chat/SaveToKnowledgeDialog.tsx +193 -0
  166. package/components/chat/SkillSelector.tsx +305 -0
  167. package/components/chat/SkillSwitcher.tsx +163 -0
  168. package/components/client-layout.tsx +15 -0
  169. package/components/knowledge/KnowledgeMetadataPanel.tsx +293 -0
  170. package/components/layout-wrapper.tsx +18 -0
  171. package/components/mobile-layout.tsx +62 -0
  172. package/components/scheduled-task-list.tsx +356 -0
  173. package/components/setup-guide.tsx +484 -0
  174. package/components/sub-nav.tsx +54 -0
  175. package/components/ticket-detail-content.tsx +383 -0
  176. package/components/ticket-list.tsx +366 -0
  177. package/components/top-nav.tsx +132 -0
  178. package/components/ui/accordion.tsx +58 -0
  179. package/components/ui/alert.tsx +59 -0
  180. package/components/ui/avatar.tsx +50 -0
  181. package/components/ui/badge.tsx +36 -0
  182. package/components/ui/button-group.tsx +83 -0
  183. package/components/ui/button.tsx +57 -0
  184. package/components/ui/card.tsx +91 -0
  185. package/components/ui/carousel.tsx +262 -0
  186. package/components/ui/collapsible.tsx +11 -0
  187. package/components/ui/command.tsx +153 -0
  188. package/components/ui/dialog.tsx +122 -0
  189. package/components/ui/dropdown-menu.tsx +200 -0
  190. package/components/ui/hover-card.tsx +29 -0
  191. package/components/ui/input-group.tsx +170 -0
  192. package/components/ui/input.tsx +22 -0
  193. package/components/ui/label.tsx +26 -0
  194. package/components/ui/popover.tsx +31 -0
  195. package/components/ui/progress.tsx +28 -0
  196. package/components/ui/scroll-area.tsx +48 -0
  197. package/components/ui/select.tsx +174 -0
  198. package/components/ui/separator.tsx +31 -0
  199. package/components/ui/spinner.tsx +16 -0
  200. package/components/ui/switch.tsx +29 -0
  201. package/components/ui/table.tsx +120 -0
  202. package/components/ui/tabs.tsx +55 -0
  203. package/components/ui/textarea.tsx +22 -0
  204. package/components/ui/tooltip.tsx +30 -0
  205. package/components/welcome-guide.tsx +182 -0
  206. package/components.json +24 -0
  207. package/lib/command-parser.ts +331 -0
  208. package/lib/dangerous-commands.ts +672 -0
  209. package/lib/db.ts +2250 -0
  210. package/lib/feishu-auth.ts +135 -0
  211. package/lib/file-storage.ts +306 -0
  212. package/lib/file-tool.ts +583 -0
  213. package/lib/knowledge-tool.ts +152 -0
  214. package/lib/knowledge-types.ts +66 -0
  215. package/lib/market-client.ts +313 -0
  216. package/lib/market-db.ts +736 -0
  217. package/lib/market-types.ts +51 -0
  218. package/lib/memory-tool.ts +211 -0
  219. package/lib/memory.ts +197 -0
  220. package/lib/pi-config.ts +436 -0
  221. package/lib/pi-session.ts +799 -0
  222. package/lib/pinyin.ts +13 -0
  223. package/lib/recommendation.ts +227 -0
  224. package/lib/risk-estimator.ts +350 -0
  225. package/lib/scheduled-task-tool.ts +184 -0
  226. package/lib/scheduler-init.ts +43 -0
  227. package/lib/scheduler.ts +416 -0
  228. package/lib/secure-bash-tool.ts +413 -0
  229. package/lib/skill-engine.ts +396 -0
  230. package/lib/skill-generator.ts +269 -0
  231. package/lib/skill-loader.ts +234 -0
  232. package/lib/skill-tool.ts +188 -0
  233. package/lib/skill-types.ts +82 -0
  234. package/lib/skills-init.ts +58 -0
  235. package/lib/ticket-tool.ts +246 -0
  236. package/lib/user-skill-types.ts +30 -0
  237. package/lib/user-skills.ts +362 -0
  238. package/lib/utils.ts +6 -0
  239. package/lib/workflow.ts +154 -0
  240. package/lib/zip-tool.ts +191 -0
  241. package/next.config.js +8 -0
  242. package/package.json +106 -0
  243. package/public/.gitkeep +1 -0
  244. package/public/icon.svg +1 -0
  245. package/tsconfig.json +42 -0
@@ -0,0 +1,416 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+ import Link from 'next/link';
5
+ import {
6
+ LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,
7
+ BarChart, Bar, Legend
8
+ } from 'recharts';
9
+ import {
10
+ MessageSquare, Users, Terminal, Wrench, FileText,
11
+ Search, Download, RefreshCw
12
+ } from 'lucide-react';
13
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
14
+ import { Button } from '@/components/ui/button';
15
+ import { Input } from '@/components/ui/input';
16
+ import { Badge } from '@/components/ui/badge';
17
+ import {
18
+ Select, SelectContent, SelectItem, SelectTrigger, SelectValue
19
+ } from '@/components/ui/select';
20
+
21
+ interface StatisticsData {
22
+ period: string;
23
+ overview: {
24
+ totalSessions: number;
25
+ activeUsers: number;
26
+ totalMessages: number;
27
+ totalSkillCalls: number;
28
+ aiGeneratedTickets: number;
29
+ };
30
+ trends: { date: string; sessionCount: number; userCount: number }[];
31
+ userStats: { userId: string; sessionCount: number; messageCount: number; skillCallCount: number; lastActiveAt: string }[];
32
+ skillStats: { skillName: string; callCount: number; successCount: number; failCount: number; successRate: number; lastCalledAt: string }[];
33
+ knowledgeStats: { documentId: string; title: string; category: string; 引用次数: number }[];
34
+ sessions: { sessionId: string; userId: string; createdAt: string; lastAccessedAt: string; messageCount: number; title?: string }[];
35
+ }
36
+
37
+ export default function StatisticsPage() {
38
+ const [data, setData] = useState<StatisticsData | null>(null);
39
+ const [loading, setLoading] = useState(true);
40
+ const [period, setPeriod] = useState<'7d' | '30d' | 'all'>('7d');
41
+ const [searchQuery, setSearchQuery] = useState('');
42
+ const [sessionSearch, setSessionSearch] = useState('');
43
+
44
+ useEffect(() => {
45
+ fetchStatistics();
46
+ }, [period]);
47
+
48
+ async function fetchStatistics() {
49
+ setLoading(true);
50
+ try {
51
+ const response = await fetch(`/api/statistics?period=${period}`);
52
+ const result = await response.json();
53
+ setData(result);
54
+ } catch (err) {
55
+ console.error('Failed to fetch statistics:', err);
56
+ } finally {
57
+ setLoading(false);
58
+ }
59
+ }
60
+
61
+ async function clearCache() {
62
+ await fetch('/api/statistics', { method: 'POST' });
63
+ fetchStatistics();
64
+ }
65
+
66
+ const filteredUserStats = data?.userStats.filter(u =>
67
+ u.userId.toLowerCase().includes(searchQuery.toLowerCase())
68
+ ) || [];
69
+
70
+ const filteredSessions = data?.sessions.filter(s =>
71
+ s.sessionId.toLowerCase().includes(sessionSearch.toLowerCase()) ||
72
+ (s.userId && s.userId.toLowerCase().includes(sessionSearch.toLowerCase())) ||
73
+ (s.title && s.title.toLowerCase().includes(sessionSearch.toLowerCase()))
74
+ ) || [];
75
+
76
+ if (loading) {
77
+ return (
78
+ <div className="flex items-center justify-center h-64">
79
+ <RefreshCw className="h-8 w-8 animate-spin text-muted-foreground" />
80
+ </div>
81
+ );
82
+ }
83
+
84
+ return (
85
+ <div className="p-6">
86
+ <div className="flex items-center justify-between mb-6">
87
+ <div>
88
+ <h1 className="text-2xl font-bold">统计分析</h1>
89
+ <p className="text-muted-foreground text-sm mt-1">查看系统使用情况和趋势</p>
90
+ </div>
91
+ <div className="flex items-center gap-3">
92
+ <Select value={period} onValueChange={(v) => setPeriod(v as '7d' | '30d' | 'all')}>
93
+ <SelectTrigger className="w-36">
94
+ <SelectValue />
95
+ </SelectTrigger>
96
+ <SelectContent>
97
+ <SelectItem value="7d">最近7天</SelectItem>
98
+ <SelectItem value="30d">最近30天</SelectItem>
99
+ <SelectItem value="all">全部历史</SelectItem>
100
+ </SelectContent>
101
+ </Select>
102
+ <Button variant="outline" size="sm" onClick={clearCache}>
103
+ <RefreshCw className="h-4 w-4 mr-2" />
104
+ 刷新
105
+ </Button>
106
+ </div>
107
+ </div>
108
+
109
+ {/* 概览统计卡片 */}
110
+ <div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6">
111
+ <StatCard
112
+ title="总会话数"
113
+ value={data?.overview.totalSessions || 0}
114
+ icon={MessageSquare}
115
+ />
116
+ <StatCard
117
+ title="活跃用户"
118
+ value={data?.overview.activeUsers || 0}
119
+ icon={Users}
120
+ />
121
+ <StatCard
122
+ title="总会话消息"
123
+ value={data?.overview.totalMessages || 0}
124
+ icon={FileText}
125
+ />
126
+ <StatCard
127
+ title="技能调用"
128
+ value={data?.overview.totalSkillCalls || 0}
129
+ icon={Terminal}
130
+ />
131
+ <StatCard
132
+ title="AI生成工单"
133
+ value={data?.overview.aiGeneratedTickets || 0}
134
+ icon={Wrench}
135
+ />
136
+ </div>
137
+
138
+ {/* 趋势图表 */}
139
+ <Card className="mb-6">
140
+ <CardHeader>
141
+ <CardTitle className="text-lg">趋势分析</CardTitle>
142
+ </CardHeader>
143
+ <CardContent>
144
+ <div className="h-72">
145
+ <ResponsiveContainer width="100%" height="100%">
146
+ <LineChart data={data?.trends || []}>
147
+ <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
148
+ <XAxis
149
+ dataKey="date"
150
+ tickFormatter={(v) => v.slice(5)}
151
+ className="text-xs"
152
+ />
153
+ <YAxis className="text-xs" />
154
+ <Tooltip
155
+ labelFormatter={(v) => v}
156
+ contentStyle={{
157
+ backgroundColor: 'white',
158
+ border: '1px solid #e5e7eb',
159
+ borderRadius: '6px'
160
+ }}
161
+ />
162
+ <Legend />
163
+ <Line
164
+ type="monotone"
165
+ dataKey="sessionCount"
166
+ name="会话数"
167
+ stroke="#3b82f6"
168
+ strokeWidth={2}
169
+ dot={{ fill: '#3b82f6', r: 3 }}
170
+ />
171
+ <Line
172
+ type="monotone"
173
+ dataKey="userCount"
174
+ name="用户数"
175
+ stroke="#10b981"
176
+ strokeWidth={2}
177
+ dot={{ fill: '#10b981', r: 3 }}
178
+ />
179
+ </LineChart>
180
+ </ResponsiveContainer>
181
+ </div>
182
+ </CardContent>
183
+ </Card>
184
+
185
+ {/* 技能使用和知识引用 */}
186
+ <div className="grid md:grid-cols-2 gap-6 mb-6">
187
+ {/* 技能使用排行 */}
188
+ <Card>
189
+ <CardHeader>
190
+ <CardTitle className="text-lg">技能使用排行</CardTitle>
191
+ </CardHeader>
192
+ <CardContent>
193
+ {data?.skillStats && data.skillStats.length > 0 ? (
194
+ <div className="h-64">
195
+ <ResponsiveContainer width="100%" height="100%">
196
+ <BarChart data={data.skillStats.slice(0, 8)} layout="vertical">
197
+ <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
198
+ <XAxis type="number" className="text-xs" />
199
+ <YAxis
200
+ type="category"
201
+ dataKey="skillName"
202
+ width={80}
203
+ className="text-xs"
204
+ tickFormatter={(v) => v.length > 8 ? v.slice(0, 8) + '...' : v}
205
+ />
206
+ <Tooltip />
207
+ <Bar dataKey="callCount" name="调用次数" fill="#6366f1" radius={[0, 4, 4, 0]} />
208
+ </BarChart>
209
+ </ResponsiveContainer>
210
+ </div>
211
+ ) : (
212
+ <div className="h-32 flex items-center justify-center text-muted-foreground">
213
+ 暂无技能调用数据
214
+ </div>
215
+ )}
216
+ {/* 技能详细列表 */}
217
+ <div className="mt-4 space-y-2 max-h-48 overflow-auto">
218
+ {data?.skillStats.slice(0, 10).map((skill, i) => (
219
+ <div key={skill.skillName} className="flex items-center justify-between text-sm p-2 rounded hover:bg-muted/50">
220
+ <div className="flex items-center gap-2">
221
+ <span className="text-muted-foreground">{i + 1}.</span>
222
+ <span className="font-medium">{skill.skillName}</span>
223
+ </div>
224
+ <div className="flex items-center gap-3">
225
+ <span className="text-muted-foreground">{skill.callCount}次</span>
226
+ <Badge variant={skill.successRate >= 80 ? 'default' : skill.successRate >= 50 ? 'outline' : 'destructive'}>
227
+ {skill.successRate}% 成功
228
+ </Badge>
229
+ </div>
230
+ </div>
231
+ ))}
232
+ </div>
233
+ </CardContent>
234
+ </Card>
235
+
236
+ {/* 知识引用排行 */}
237
+ <Card>
238
+ <CardHeader>
239
+ <CardTitle className="text-lg">知识引用排行</CardTitle>
240
+ </CardHeader>
241
+ <CardContent>
242
+ {data?.knowledgeStats && data.knowledgeStats.length > 0 ? (
243
+ <div className="h-64">
244
+ <ResponsiveContainer width="100%" height="100%">
245
+ <BarChart data={data.knowledgeStats.slice(0, 8)} layout="vertical">
246
+ <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
247
+ <XAxis type="number" className="text-xs" />
248
+ <YAxis
249
+ type="category"
250
+ dataKey="title"
251
+ width={80}
252
+ className="text-xs"
253
+ tickFormatter={(v) => v.length > 8 ? v.slice(0, 8) + '...' : v}
254
+ />
255
+ <Tooltip />
256
+ <Bar dataKey="引用次数" name="引用次数" fill="#f59e0b" radius={[0, 4, 4, 0]} />
257
+ </BarChart>
258
+ </ResponsiveContainer>
259
+ </div>
260
+ ) : (
261
+ <div className="h-32 flex items-center justify-center text-muted-foreground">
262
+ 暂无知识引用数据
263
+ </div>
264
+ )}
265
+ <div className="mt-4 space-y-2 max-h-48 overflow-auto">
266
+ {data?.knowledgeStats.slice(0, 10).map((knowledge, i) => (
267
+ <div key={knowledge.documentId} className="flex items-center justify-between text-sm p-2 rounded hover:bg-muted/50">
268
+ <div className="flex items-center gap-2 flex-1 min-w-0">
269
+ <span className="text-muted-foreground">{i + 1}.</span>
270
+ <span className="font-medium truncate">{knowledge.title}</span>
271
+ <Badge variant="outline" className="text-xs">{knowledge.category}</Badge>
272
+ </div>
273
+ <span className="text-muted-foreground ml-2">{knowledge.引用次数}次</span>
274
+ </div>
275
+ ))}
276
+ {(!data?.knowledgeStats || data.knowledgeStats.length === 0) && (
277
+ <p className="text-center text-muted-foreground py-4">暂无知识引用数据</p>
278
+ )}
279
+ </div>
280
+ </CardContent>
281
+ </Card>
282
+ </div>
283
+
284
+ {/* 用户使用统计 */}
285
+ <Card className="mb-6">
286
+ <CardHeader>
287
+ <CardTitle className="text-lg">用户使用统计</CardTitle>
288
+ </CardHeader>
289
+ <CardContent>
290
+ <div className="flex items-center gap-3 mb-4">
291
+ <div className="relative flex-1 max-w-sm">
292
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
293
+ <Input
294
+ placeholder="搜索用户..."
295
+ value={searchQuery}
296
+ onChange={(e) => setSearchQuery(e.target.value)}
297
+ className="pl-9"
298
+ />
299
+ </div>
300
+ </div>
301
+ <div className="overflow-x-auto">
302
+ <table className="w-full">
303
+ <thead>
304
+ <tr className="border-b">
305
+ <th className="text-left py-2 px-3 font-medium text-sm text-muted-foreground">用户ID</th>
306
+ <th className="text-right py-2 px-3 font-medium text-sm text-muted-foreground">会话数</th>
307
+ <th className="text-right py-2 px-3 font-medium text-sm text-muted-foreground">消息数</th>
308
+ <th className="text-right py-2 px-3 font-medium text-sm text-muted-foreground">技能调用</th>
309
+ <th className="text-right py-2 px-3 font-medium text-sm text-muted-foreground">最近活跃</th>
310
+ </tr>
311
+ </thead>
312
+ <tbody>
313
+ {filteredUserStats.slice(0, 10).map((user) => (
314
+ <tr key={user.userId} className="border-b hover:bg-muted/50">
315
+ <td className="py-2 px-3 font-medium">{user.userId}</td>
316
+ <td className="text-right py-2 px-3">{user.sessionCount}</td>
317
+ <td className="text-right py-2 px-3">{user.messageCount}</td>
318
+ <td className="text-right py-2 px-3">{user.skillCallCount}</td>
319
+ <td className="text-right py-2 px-3 text-muted-foreground text-sm">
320
+ {user.lastActiveAt ? new Date(user.lastActiveAt).toLocaleDateString('zh-CN') : '-'}
321
+ </td>
322
+ </tr>
323
+ ))}
324
+ {filteredUserStats.length === 0 && (
325
+ <tr>
326
+ <td colSpan={5} className="text-center py-8 text-muted-foreground">
327
+ 暂无用户数据
328
+ </td>
329
+ </tr>
330
+ )}
331
+ </tbody>
332
+ </table>
333
+ </div>
334
+ </CardContent>
335
+ </Card>
336
+
337
+ {/* 会话详情列表 */}
338
+ <Card>
339
+ <CardHeader>
340
+ <CardTitle className="text-lg">会话详情列表</CardTitle>
341
+ </CardHeader>
342
+ <CardContent>
343
+ <div className="flex items-center gap-3 mb-4">
344
+ <div className="relative flex-1 max-w-sm">
345
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
346
+ <Input
347
+ placeholder="搜索会话ID/用户/标题..."
348
+ value={sessionSearch}
349
+ onChange={(e) => setSessionSearch(e.target.value)}
350
+ className="pl-9"
351
+ />
352
+ </div>
353
+ <Badge variant="outline">
354
+ 共 {filteredSessions.length} 条
355
+ </Badge>
356
+ </div>
357
+ <div className="overflow-x-auto">
358
+ <table className="w-full">
359
+ <thead>
360
+ <tr className="border-b">
361
+ <th className="text-left py-2 px-3 font-medium text-sm text-muted-foreground">会话ID</th>
362
+ <th className="text-left py-2 px-3 font-medium text-sm text-muted-foreground">用户</th>
363
+ <th className="text-left py-2 px-3 font-medium text-sm text-muted-foreground">标题</th>
364
+ <th className="text-right py-2 px-3 font-medium text-sm text-muted-foreground">消息数</th>
365
+ <th className="text-left py-2 px-3 font-medium text-sm text-muted-foreground">创建时间</th>
366
+ <th className="text-left py-2 px-3 font-medium text-sm text-muted-foreground">最近活跃</th>
367
+ </tr>
368
+ </thead>
369
+ <tbody>
370
+ {filteredSessions.slice(0, 20).map((session) => (
371
+ <tr key={session.sessionId} className="border-b hover:bg-muted/50">
372
+ <td className="py-2 px-3 font-mono text-xs">{session.sessionId.slice(0, 12)}...</td>
373
+ <td className="py-2 px-3 text-sm">{session.userId || '-'}</td>
374
+ <td className="py-2 px-3 text-sm truncate max-w-xs">{session.title || '-'}</td>
375
+ <td className="text-right py-2 px-3">{session.messageCount}</td>
376
+ <td className="py-2 px-3 text-muted-foreground text-sm">
377
+ {new Date(session.createdAt).toLocaleDateString('zh-CN')}
378
+ </td>
379
+ <td className="py-2 px-3 text-muted-foreground text-sm">
380
+ {new Date(session.lastAccessedAt).toLocaleDateString('zh-CN')}
381
+ </td>
382
+ </tr>
383
+ ))}
384
+ {filteredSessions.length === 0 && (
385
+ <tr>
386
+ <td colSpan={6} className="text-center py-8 text-muted-foreground">
387
+ 暂无会话数据
388
+ </td>
389
+ </tr>
390
+ )}
391
+ </tbody>
392
+ </table>
393
+ </div>
394
+ </CardContent>
395
+ </Card>
396
+ </div>
397
+ );
398
+ }
399
+
400
+ function StatCard({ title, value, icon: Icon }: { title: string; value: number; icon: any }) {
401
+ return (
402
+ <Card>
403
+ <CardContent className="p-4">
404
+ <div className="flex items-center justify-between">
405
+ <div>
406
+ <p className="text-sm text-muted-foreground">{title}</p>
407
+ <p className="text-2xl font-bold mt-1">{value.toLocaleString()}</p>
408
+ </div>
409
+ <div className="h-10 w-10 bg-muted rounded-full flex items-center justify-center">
410
+ <Icon className="h-5 w-5 text-muted-foreground" />
411
+ </div>
412
+ </div>
413
+ </CardContent>
414
+ </Card>
415
+ );
416
+ }