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,318 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, Suspense } from 'react';
4
+ import { useRouter, useSearchParams } from 'next/navigation';
5
+ import { Button } from '@/components/ui/button';
6
+ import { Input } from '@/components/ui/input';
7
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
8
+ import { Bot, Shield, User, LogIn } from 'lucide-react';
9
+
10
+ function isInFeishu(): boolean {
11
+ if (typeof window === 'undefined') return false;
12
+ const userAgent = navigator.userAgent.toLowerCase();
13
+ return userAgent.includes('feishu') || userAgent.includes('lark');
14
+ }
15
+
16
+ declare global {
17
+ interface Window {
18
+ tt?: any;
19
+ }
20
+ }
21
+
22
+ function LoginPageContent() {
23
+ const [username, setUsername] = useState('');
24
+ const [password, setPassword] = useState('');
25
+ const [mode, setMode] = useState<'guest' | 'admin'>('guest');
26
+ const [loading, setLoading] = useState(false);
27
+ const [feishuLoading, setFeishuLoading] = useState(false);
28
+ const [error, setError] = useState('');
29
+ const router = useRouter();
30
+ const searchParams = useSearchParams();
31
+
32
+ useEffect(() => {
33
+ console.log('[login] searchParams:', Object.fromEntries(searchParams));
34
+
35
+ const userId = localStorage.getItem('userId');
36
+ if (userId) {
37
+ console.log('[login] already logged in, redirect to home');
38
+ router.push('/');
39
+ return;
40
+ }
41
+
42
+ const feishuLogin = searchParams.get('feishu_login');
43
+ const urlUserId = searchParams.get('userId');
44
+ const urlUsername = searchParams.get('username');
45
+ const urlRole = searchParams.get('role');
46
+ console.log('[login] feishu_login:', feishuLogin, 'userId:', urlUserId, 'username:', urlUsername, 'role:', urlRole);
47
+
48
+ if (feishuLogin === 'true' && urlUserId) {
49
+ console.log('[login] set userId from url:', urlUserId);
50
+ localStorage.setItem('userId', urlUserId);
51
+ if (urlUsername) {
52
+ localStorage.setItem('username', urlUsername);
53
+ }
54
+ if (urlRole) {
55
+ localStorage.setItem('role', urlRole);
56
+ }
57
+ console.log('[login] redirect to home');
58
+ window.location.href = '/';
59
+ return;
60
+ }
61
+
62
+ const errorParam = searchParams.get('error');
63
+ if (errorParam) {
64
+ setError('飞书登录失败,请重试');
65
+ }
66
+
67
+ if (isInFeishu()) {
68
+ handleFeishuSSO();
69
+ }
70
+ }, [router, searchParams]);
71
+
72
+ const handleFeishuSSO = async () => {
73
+ if (feishuLoading) return;
74
+ setFeishuLoading(true);
75
+ setError('');
76
+
77
+ try {
78
+ const script = document.createElement('script');
79
+ script.src = 'https://lf1-cdn-tos.bytegoofy.com/goofy/lark/op/h5-js-sdk-1.5.38.js';
80
+ script.async = true;
81
+
82
+ await new Promise((resolve, reject) => {
83
+ script.onload = resolve;
84
+ script.onerror = reject;
85
+ document.body.appendChild(script);
86
+ });
87
+
88
+ if (window.tt) {
89
+ const { code } = await new Promise<{ code: string }>((resolve, reject) => {
90
+ (window as any).tt.oauth({
91
+ success: (res: any) => resolve(res),
92
+ fail: (err: any) => reject(err),
93
+ });
94
+ });
95
+
96
+ const res = await fetch('/api/auth/feishu/sso', {
97
+ method: 'POST',
98
+ headers: { 'Content-Type': 'application/json' },
99
+ body: JSON.stringify({ code }),
100
+ });
101
+
102
+ const data = await res.json();
103
+
104
+ if (!res.ok) {
105
+ throw new Error(data.error || '飞书登录失败');
106
+ }
107
+
108
+ localStorage.setItem('userId', data.user.id);
109
+ localStorage.setItem('username', data.user.displayName || data.user.username);
110
+ localStorage.setItem('role', data.user.role);
111
+ localStorage.setItem('feishuAvatar', data.user.avatar || '');
112
+
113
+ window.location.href = '/';
114
+ }
115
+ } catch (err) {
116
+ console.error('飞书免登录失败:', err);
117
+ } finally {
118
+ setFeishuLoading(false);
119
+ }
120
+ };
121
+
122
+ const handleFeishuLogin = async () => {
123
+ setLoading(true);
124
+ setError('');
125
+
126
+ try {
127
+ const res = await fetch('/api/auth/feishu/login');
128
+ const data = await res.json();
129
+
130
+ if (!res.ok) {
131
+ throw new Error(data.error || '获取飞书登录链接失败');
132
+ }
133
+
134
+ window.location.href = data.url;
135
+ } catch (err) {
136
+ setError(err instanceof Error ? err.message : '飞书登录失败');
137
+ setLoading(false);
138
+ }
139
+ };
140
+
141
+ const handleLogin = async () => {
142
+ if (!username.trim()) {
143
+ setError(mode === 'admin' ? '请输入管理员用户名' : '请输入用户名');
144
+ return;
145
+ }
146
+
147
+ if (mode === 'admin' && !password) {
148
+ setError('请输入管理员密码');
149
+ return;
150
+ }
151
+
152
+ setLoading(true);
153
+ setError('');
154
+
155
+ try {
156
+ const res = await fetch('/api/auth/login', {
157
+ method: 'POST',
158
+ headers: { 'Content-Type': 'application/json' },
159
+ body: JSON.stringify({
160
+ username: username.trim(),
161
+ password: mode === 'admin' ? password : undefined,
162
+ mode
163
+ }),
164
+ });
165
+
166
+ const data = await res.json();
167
+
168
+ if (!res.ok) {
169
+ throw new Error(data.error || '登录失败');
170
+ }
171
+
172
+ localStorage.setItem('userId', data.user.id);
173
+ localStorage.setItem('username', data.user.username);
174
+ localStorage.setItem('role', data.user.role);
175
+
176
+ window.location.href = '/';
177
+ } catch (err) {
178
+ setError(err instanceof Error ? err.message : '登录失败');
179
+ } finally {
180
+ setLoading(false);
181
+ }
182
+ };
183
+
184
+ const handleKeyPress = (e: React.KeyboardEvent) => {
185
+ if (e.key === 'Enter') {
186
+ handleLogin();
187
+ }
188
+ };
189
+
190
+ return (
191
+ <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900">
192
+ <Card className="w-[400px] bg-white/95 backdrop-blur">
193
+ <CardHeader className="text-center">
194
+ <div className="mx-auto mb-4 w-16 h-16 bg-gradient-to-br from-blue-500 to-purple-600 rounded-2xl flex items-center justify-center">
195
+ <Bot className="w-8 h-8 text-white" />
196
+ </div>
197
+ <CardTitle className="text-2xl font-bold">智能工作助手</CardTitle>
198
+ <CardDescription>
199
+ {mode === 'admin' ? '管理员登录' : '输入用户名开始使用'}
200
+ </CardDescription>
201
+ </CardHeader>
202
+ <CardContent className="space-y-4">
203
+ <div className="flex gap-2 p-1 bg-gray-100 rounded-lg">
204
+ <button
205
+ type="button"
206
+ onClick={() => setMode('guest')}
207
+ className={`flex-1 flex items-center justify-center gap-2 py-2 px-3 rounded-md text-sm font-medium transition-all ${
208
+ mode === 'guest'
209
+ ? 'bg-white text-gray-900 shadow-sm'
210
+ : 'text-gray-500 hover:text-gray-700'
211
+ }`}
212
+ >
213
+ <User className="w-4 h-4" />
214
+ 游客
215
+ </button>
216
+ <button
217
+ type="button"
218
+ onClick={() => setMode('admin')}
219
+ className={`flex-1 flex items-center justify-center gap-2 py-2 px-3 rounded-md text-sm font-medium transition-all ${
220
+ mode === 'admin'
221
+ ? 'bg-white text-gray-900 shadow-sm'
222
+ : 'text-gray-500 hover:text-gray-700'
223
+ }`}
224
+ >
225
+ <Shield className="w-4 h-4" />
226
+ 管理员
227
+ </button>
228
+ </div>
229
+
230
+ <div className="space-y-3">
231
+ <div>
232
+ <label className="text-sm text-gray-600 mb-1 block">
233
+ {mode === 'admin' ? '管理员用户名' : '用户名'}
234
+ </label>
235
+ <Input
236
+ type="text"
237
+ placeholder={mode === 'admin' ? '请输入管理员用户名' : '请输入用户名'}
238
+ value={username}
239
+ onChange={(e) => setUsername(e.target.value)}
240
+ onKeyPress={handleKeyPress}
241
+ disabled={loading || feishuLoading}
242
+ autoFocus
243
+ />
244
+ </div>
245
+
246
+ {mode === 'admin' && (
247
+ <div>
248
+ <label className="text-sm text-gray-600 mb-1 block">密码</label>
249
+ <Input
250
+ type="password"
251
+ placeholder="请输入管理员密码"
252
+ value={password}
253
+ onChange={(e) => setPassword(e.target.value)}
254
+ onKeyPress={handleKeyPress}
255
+ disabled={loading || feishuLoading}
256
+ />
257
+ </div>
258
+ )}
259
+ </div>
260
+
261
+ {error && (
262
+ <p className="text-sm text-red-500 text-center">{error}</p>
263
+ )}
264
+
265
+ <Button
266
+ className="w-full"
267
+ onClick={handleLogin}
268
+ disabled={loading || feishuLoading}
269
+ >
270
+ {loading ? '登录中...' : (mode === 'admin' ? '管理员登录' : '开始使用')}
271
+ </Button>
272
+
273
+ <div className="relative">
274
+ <div className="absolute inset-0 flex items-center">
275
+ <span className="w-full border-t" />
276
+ </div>
277
+ <div className="relative flex justify-center text-xs uppercase">
278
+ <span className="bg-white px-2 text-gray-500">或</span>
279
+ </div>
280
+ </div>
281
+
282
+ <Button
283
+ variant="outline"
284
+ className="w-full"
285
+ onClick={handleFeishuLogin}
286
+ disabled={loading || feishuLoading}
287
+ >
288
+ <LogIn className="w-4 h-4 mr-2" />
289
+ {feishuLoading ? '飞书登录中...' : '飞书账号登录'}
290
+ </Button>
291
+
292
+ {mode === 'guest' && (
293
+ <p className="text-xs text-gray-500 text-center">
294
+ 游客模式下可体验基础功能,部分功能受限
295
+ </p>
296
+ )}
297
+ {mode === 'admin' && (
298
+ <p className="text-xs text-gray-500 text-center">
299
+ 管理员可配置系统默认Skill和管理工单
300
+ </p>
301
+ )}
302
+ </CardContent>
303
+ </Card>
304
+ </div>
305
+ );
306
+ }
307
+
308
+ export default function LoginPage() {
309
+ return (
310
+ <Suspense fallback={
311
+ <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900">
312
+ <div className="text-white">Loading...</div>
313
+ </div>
314
+ }>
315
+ <LoginPageContent />
316
+ </Suspense>
317
+ );
318
+ }
@@ -0,0 +1,77 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+ import { useParams } from 'next/navigation';
5
+ import { ArrowLeft } from 'lucide-react';
6
+ import Link from 'next/link';
7
+ import { Button } from '@/components/ui/button';
8
+ import { KnowledgePreview } from '@/components/chat/KnowledgePreview';
9
+
10
+ export default function MarketKnowledgeDetailPage() {
11
+ const params = useParams();
12
+ const knowledgeId = params.id as string;
13
+
14
+ const [knowledge, setKnowledge] = useState<any>(null);
15
+ const [loading, setLoading] = useState(true);
16
+
17
+ useEffect(() => {
18
+ async function loadKnowledge() {
19
+ try {
20
+ const response = await fetch(`/api/market/knowledge?id=${knowledgeId}`);
21
+ if (response.ok) {
22
+ const data = await response.json();
23
+ setKnowledge(data.knowledge);
24
+ }
25
+ } catch (error) {
26
+ console.error('Failed to load knowledge:', error);
27
+ } finally {
28
+ setLoading(false);
29
+ }
30
+ }
31
+ loadKnowledge();
32
+ }, [knowledgeId]);
33
+
34
+ if (loading) {
35
+ return (
36
+ <div className="min-h-screen bg-gray-50 flex items-center justify-center">
37
+ <div className="text-gray-500">加载中...</div>
38
+ </div>
39
+ );
40
+ }
41
+
42
+ if (!knowledge) {
43
+ return (
44
+ <div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center">
45
+ <h1 className="text-2xl font-bold text-gray-900 mb-2">知识不存在</h1>
46
+ <p className="text-gray-500 mb-4">该知识可能已被删除或未发布到市场</p>
47
+ <Link href="/market/knowledge">
48
+ <Button variant="outline">
49
+ <ArrowLeft className="w-4 h-4 mr-2" />
50
+ 返回市场知识
51
+ </Button>
52
+ </Link>
53
+ </div>
54
+ );
55
+ }
56
+
57
+ return (
58
+ <div className="min-h-screen bg-gray-50">
59
+ <div className="max-w-4xl mx-auto px-4 py-8">
60
+ <Link href="/market/knowledge" className="inline-flex items-center text-sm text-gray-500 hover:text-gray-700 mb-4">
61
+ <ArrowLeft className="w-4 h-4 mr-1" />
62
+ 返回市场知识
63
+ </Link>
64
+
65
+ <KnowledgePreview
66
+ title={knowledge.title}
67
+ content={knowledge.content}
68
+ category={knowledge.category}
69
+ tags={knowledge.tags}
70
+ author={knowledge.author}
71
+ updatedAt={knowledge.updatedAt}
72
+ type={knowledge.type}
73
+ />
74
+ </div>
75
+ </div>
76
+ );
77
+ }