finstep-template-cli 1.0.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.
Files changed (51) hide show
  1. package/README.md +41 -0
  2. package/cli.js +117 -0
  3. package/package.json +39 -0
  4. package/template/.env.development +3 -0
  5. package/template/.env.production +3 -0
  6. package/template/.env.staging +3 -0
  7. package/template/.env.test +3 -0
  8. package/template/.eslintrc.cjs +21 -0
  9. package/template/README.md +69 -0
  10. package/template/auto-imports.d.ts +47 -0
  11. package/template/eslint.config.js +26 -0
  12. package/template/index.html +16 -0
  13. package/template/package.json +46 -0
  14. package/template/postcss.config.js +5 -0
  15. package/template/public/logo.svg +85 -0
  16. package/template/public/vite.svg +1 -0
  17. package/template/src/App.css +42 -0
  18. package/template/src/App.tsx +25 -0
  19. package/template/src/api/home/index.ts +56 -0
  20. package/template/src/api/home/typings.d.ts +8 -0
  21. package/template/src/assets/logo.svg +85 -0
  22. package/template/src/assets/react.svg +1 -0
  23. package/template/src/components/admin-layout.tsx +211 -0
  24. package/template/src/components/f-b-footer.tsx +46 -0
  25. package/template/src/components/f-b-header.tsx +894 -0
  26. package/template/src/components/global-loading.tsx +143 -0
  27. package/template/src/components/hero-section.tsx +371 -0
  28. package/template/src/components/products-preview.tsx +175 -0
  29. package/template/src/components/stats-section.tsx +85 -0
  30. package/template/src/components/trusted-by.tsx +53 -0
  31. package/template/src/hooks/useGlobalLoading.ts +57 -0
  32. package/template/src/index.css +341 -0
  33. package/template/src/main.tsx +30 -0
  34. package/template/src/pages/admin/index.tsx +361 -0
  35. package/template/src/pages/admin/pages/applications/index.tsx +558 -0
  36. package/template/src/pages/home/index.tsx +129 -0
  37. package/template/src/router/index.tsx +9 -0
  38. package/template/src/router/routes.tsx +30 -0
  39. package/template/src/stores/loading.store.ts +46 -0
  40. package/template/src/stores/root.store.ts +22 -0
  41. package/template/src/stores/store-context.tsx +43 -0
  42. package/template/src/stores/user.store.ts +46 -0
  43. package/template/src/utils/index.ts +14 -0
  44. package/template/src/utils/request.ts +116 -0
  45. package/template/src/utils/tokenManager.ts +168 -0
  46. package/template/src/vite-env.d.ts +1 -0
  47. package/template/tailwind.config.js +19 -0
  48. package/template/tsconfig.app.json +29 -0
  49. package/template/tsconfig.json +7 -0
  50. package/template/tsconfig.node.json +26 -0
  51. package/template/vite.config.ts +36 -0
@@ -0,0 +1,558 @@
1
+ import { useState } from "react";
2
+ import Header from "@/components/f-b-header";
3
+ import Footer from "@/components/f-b-footer";
4
+
5
+ interface Application {
6
+ id: string;
7
+ companyName: string;
8
+ name: string;
9
+ phone: string;
10
+ email: string;
11
+ position?: string;
12
+ requirements?: string;
13
+ status: "pending" | "approved" | "rejected";
14
+ submittedAt: string;
15
+ reviewedAt?: string;
16
+ reviewedBy?: string;
17
+ }
18
+
19
+ export default function ApplicationsPage() {
20
+ const [activeTab, setActiveTab] = useState<
21
+ "all" | "pending" | "approved" | "rejected"
22
+ >("all");
23
+ const [showDetailModal, setShowDetailModal] = useState(false);
24
+ const [selectedApplication, setSelectedApplication] =
25
+ useState<Application | null>(null);
26
+ const [rejectReason, setRejectReason] = useState("");
27
+
28
+ // Mock data
29
+ const [applications, setApplications] = useState<Application[]>([
30
+ {
31
+ id: "1",
32
+ companyName: "腾讯科技",
33
+ name: "张三",
34
+ phone: "13800138001",
35
+ email: "zhangsan@tencent.com",
36
+ position: "产品经理",
37
+ requirements:
38
+ "希望使用AI工具提升产品设计效率,主要用于用户画像分析和市场趋势预测。",
39
+ status: "pending",
40
+ submittedAt: "2024-01-15 14:30:25",
41
+ },
42
+ {
43
+ id: "2",
44
+ companyName: "阿里巴巴",
45
+ name: "李四",
46
+ phone: "13800138002",
47
+ email: "lisi@alibaba.com",
48
+ position: "技术总监",
49
+ requirements: "需要集成AI代理到现有系统中,用于客户服务自动化。",
50
+ status: "approved",
51
+ submittedAt: "2024-01-14 09:15:42",
52
+ reviewedAt: "2024-01-14 16:20:15",
53
+ reviewedBy: "管理员",
54
+ },
55
+ {
56
+ id: "3",
57
+ companyName: "字节跳动",
58
+ name: "王五",
59
+ phone: "13800138003",
60
+ email: "wangwu@bytedance.com",
61
+ position: "数据分析师",
62
+ requirements: "希望使用AIGC工具生成营销内容和数据报告。",
63
+ status: "rejected",
64
+ submittedAt: "2024-01-13 11:45:18",
65
+ reviewedAt: "2024-01-13 17:30:22",
66
+ reviewedBy: "管理员",
67
+ },
68
+ {
69
+ id: "4",
70
+ companyName: "百度",
71
+ name: "赵六",
72
+ phone: "13800138004",
73
+ email: "zhaoliu@baidu.com",
74
+ position: "研发工程师",
75
+ requirements: "需要AI工具协助代码生成和技术文档编写。",
76
+ status: "pending",
77
+ submittedAt: "2024-01-12 16:20:33",
78
+ },
79
+ {
80
+ id: "5",
81
+ companyName: "美团",
82
+ name: "孙七",
83
+ phone: "13800138005",
84
+ email: "sunqi@meituan.com",
85
+ position: "运营经理",
86
+ requirements: "希望使用AI分析用户行为数据,优化运营策略。",
87
+ status: "approved",
88
+ submittedAt: "2024-01-11 13:55:47",
89
+ reviewedAt: "2024-01-12 10:15:30",
90
+ reviewedBy: "管理员",
91
+ },
92
+ ]);
93
+
94
+ const filteredApplications = applications.filter((app) => {
95
+ if (activeTab === "all") return true;
96
+ return app.status === activeTab;
97
+ });
98
+
99
+ const stats = {
100
+ total: applications.length,
101
+ pending: applications.filter((app) => app.status === "pending").length,
102
+ approved: applications.filter((app) => app.status === "approved").length,
103
+ rejected: applications.filter((app) => app.status === "rejected").length,
104
+ };
105
+
106
+ const handleApprove = (id: string) => {
107
+ setApplications((prev) =>
108
+ prev.map((app) =>
109
+ app.id === id
110
+ ? {
111
+ ...app,
112
+ status: "approved" as const,
113
+ reviewedAt: new Date().toLocaleString("zh-CN"),
114
+ reviewedBy: "管理员",
115
+ }
116
+ : app
117
+ )
118
+ );
119
+ setShowDetailModal(false);
120
+ setRejectReason("");
121
+ };
122
+
123
+ const handleReject = (id: string) => {
124
+ setApplications((prev) =>
125
+ prev.map((app) =>
126
+ app.id === id
127
+ ? {
128
+ ...app,
129
+ status: "rejected" as const,
130
+ reviewedAt: new Date().toLocaleString("zh-CN"),
131
+ reviewedBy: "管理员",
132
+ }
133
+ : app
134
+ )
135
+ );
136
+ setShowDetailModal(false);
137
+ setRejectReason("");
138
+ };
139
+
140
+ const getStatusBadge = (status: Application["status"]) => {
141
+ switch (status) {
142
+ case "pending":
143
+ return (
144
+ <span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-yellow-500/20 text-yellow-400 border border-yellow-500/30">
145
+ <div className="w-1.5 h-1.5 bg-yellow-400 rounded-full mr-1.5"></div>
146
+ 待审核
147
+ </span>
148
+ );
149
+ case "approved":
150
+ return (
151
+ <span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-green-500/20 text-green-400 border border-green-500/30">
152
+ <div className="w-1.5 h-1.5 bg-green-400 rounded-full mr-1.5"></div>
153
+ 已通过
154
+ </span>
155
+ );
156
+ case "rejected":
157
+ return (
158
+ <span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-red-500/20 text-red-400 border border-red-500/30">
159
+ <div className="w-1.5 h-1.5 bg-red-400 rounded-full mr-1.5"></div>
160
+ 已拒绝
161
+ </span>
162
+ );
163
+ }
164
+ };
165
+
166
+ return (
167
+ <>
168
+ <Header />
169
+
170
+ <div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 pt-20">
171
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
172
+ {/* Page Header */}
173
+ <div className="mb-8">
174
+ <div className="flex items-center mb-4">
175
+ <div className="w-10 h-10 bg-gradient-to-r from-blue-500 to-purple-600 rounded-xl flex items-center justify-center mr-3">
176
+ <i className="ri-file-list-3-line w-5 h-5 flex items-center justify-center text-white"></i>
177
+ </div>
178
+ <div>
179
+ <h1 className="text-3xl font-bold text-white">客户申请管理</h1>
180
+ <p className="text-slate-400 mt-1">
181
+ 管理和审核客户的产品使用申请
182
+ </p>
183
+ </div>
184
+ </div>
185
+ </div>
186
+
187
+ {/* Stats Cards */}
188
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
189
+ <div className="bg-slate-900/50 backdrop-blur-xl border border-slate-800/60 rounded-2xl p-6">
190
+ <div className="flex items-center justify-between">
191
+ <div>
192
+ <p className="text-slate-400 text-sm font-medium">总申请数</p>
193
+ <p className="text-2xl font-bold text-white mt-1">
194
+ {stats.total}
195
+ </p>
196
+ </div>
197
+ <div className="w-12 h-12 bg-blue-500/20 rounded-xl flex items-center justify-center">
198
+ <i className="ri-file-list-line w-6 h-6 flex items-center justify-center text-blue-400"></i>
199
+ </div>
200
+ </div>
201
+ </div>
202
+
203
+ <div className="bg-slate-900/50 backdrop-blur-xl border border-slate-800/60 rounded-2xl p-6">
204
+ <div className="flex items-center justify-between">
205
+ <div>
206
+ <p className="text-slate-400 text-sm font-medium">待审核</p>
207
+ <p className="text-2xl font-bold text-white mt-1">
208
+ {stats.pending}
209
+ </p>
210
+ </div>
211
+ <div className="w-12 h-12 bg-yellow-500/20 rounded-xl flex items-center justify-center">
212
+ <i className="ri-time-line w-6 h-6 flex items-center justify-center text-yellow-400"></i>
213
+ </div>
214
+ </div>
215
+ </div>
216
+
217
+ <div className="bg-slate-900/50 backdrop-blur-xl border border-slate-800/60 rounded-2xl p-6">
218
+ <div className="flex items-center justify-between">
219
+ <div>
220
+ <p className="text-slate-400 text-sm font-medium">已通过</p>
221
+ <p className="text-2xl font-bold text-white mt-1">
222
+ {stats.approved}
223
+ </p>
224
+ </div>
225
+ <div className="w-12 h-12 bg-green-500/20 rounded-xl flex items-center justify-center">
226
+ <i className="ri-check-line w-6 h-6 flex items-center justify-center text-green-400"></i>
227
+ </div>
228
+ </div>
229
+ </div>
230
+
231
+ <div className="bg-slate-900/50 backdrop-blur-xl border border-slate-800/60 rounded-2xl p-6">
232
+ <div className="flex items-center justify-between">
233
+ <div>
234
+ <p className="text-slate-400 text-sm font-medium">已拒绝</p>
235
+ <p className="text-2xl font-bold text-white mt-1">
236
+ {stats.rejected}
237
+ </p>
238
+ </div>
239
+ <div className="w-12 h-12 bg-red-500/20 rounded-xl flex items-center justify-center">
240
+ <i className="ri-close-line w-6 h-6 flex items-center justify-center text-red-400"></i>
241
+ </div>
242
+ </div>
243
+ </div>
244
+ </div>
245
+
246
+ {/* Filter Tabs */}
247
+ <div className="bg-slate-900/50 backdrop-blur-xl border border-slate-800/60 rounded-2xl p-6 mb-6">
248
+ <div className="flex space-x-1 bg-slate-800/50 p-1 rounded-xl">
249
+ {[
250
+ { key: "all", label: "全部申请", count: stats.total },
251
+ { key: "pending", label: "待审核", count: stats.pending },
252
+ { key: "approved", label: "已通过", count: stats.approved },
253
+ { key: "rejected", label: "已拒绝", count: stats.rejected },
254
+ ].map((tab) => (
255
+ <button
256
+ key={tab.key}
257
+ onClick={() =>
258
+ setActiveTab(
259
+ tab.key as "pending" | "approved" | "rejected" | "all"
260
+ )
261
+ }
262
+ className={`flex-1 px-4 py-2.5 rounded-lg text-sm font-medium transition-all ${
263
+ activeTab === tab.key
264
+ ? "bg-blue-600 text-white shadow-lg shadow-blue-500/25"
265
+ : "text-slate-400 hover:text-white hover:bg-slate-700/50"
266
+ }`}
267
+ >
268
+ {tab.label}
269
+ <span
270
+ className={`ml-2 px-2 py-0.5 rounded-full text-xs ${
271
+ activeTab === tab.key
272
+ ? "bg-white/20 text-white"
273
+ : "bg-slate-600/50 text-slate-300"
274
+ }`}
275
+ >
276
+ {tab.count}
277
+ </span>
278
+ </button>
279
+ ))}
280
+ </div>
281
+ </div>
282
+
283
+ {/* Applications Table */}
284
+ <div className="bg-slate-900/50 backdrop-blur-xl border border-slate-800/60 rounded-2xl overflow-hidden">
285
+ <div className="overflow-x-auto">
286
+ <table className="w-full">
287
+ <thead className="bg-slate-800/50">
288
+ <tr>
289
+ <th className="px-6 py-4 text-left text-xs font-medium text-slate-400 uppercase tracking-wider">
290
+ 申请信息
291
+ </th>
292
+ <th className="px-6 py-4 text-left text-xs font-medium text-slate-400 uppercase tracking-wider">
293
+ 联系方式
294
+ </th>
295
+ <th className="px-6 py-4 text-left text-xs font-medium text-slate-400 uppercase tracking-wider">
296
+ 状态
297
+ </th>
298
+ <th className="px-6 py-4 text-left text-xs font-medium text-slate-400 uppercase tracking-wider">
299
+ 申请时间
300
+ </th>
301
+ <th className="px-6 py-4 text-left text-xs font-medium text-slate-400 uppercase tracking-wider">
302
+ 操作
303
+ </th>
304
+ </tr>
305
+ </thead>
306
+ <tbody className="divide-y divide-slate-800/60">
307
+ {filteredApplications.map((application) => (
308
+ <tr
309
+ key={application.id}
310
+ className="hover:bg-slate-800/30 transition-colors"
311
+ >
312
+ <td className="px-6 py-4">
313
+ <div>
314
+ <div className="text-white font-medium">
315
+ {application.name}
316
+ </div>
317
+ <div className="text-slate-400 text-sm">
318
+ {application.companyName}
319
+ </div>
320
+ {application.position && (
321
+ <div className="text-slate-500 text-xs mt-1">
322
+ {application.position}
323
+ </div>
324
+ )}
325
+ </div>
326
+ </td>
327
+ <td className="px-6 py-4">
328
+ <div>
329
+ <div className="text-slate-300 text-sm">
330
+ {application.phone}
331
+ </div>
332
+ <div className="text-slate-400 text-sm">
333
+ {application.email}
334
+ </div>
335
+ </div>
336
+ </td>
337
+ <td className="px-6 py-4">
338
+ {getStatusBadge(application.status)}
339
+ </td>
340
+ <td className="px-6 py-4">
341
+ <div className="text-slate-300 text-sm">
342
+ {application.submittedAt}
343
+ </div>
344
+ {application.reviewedAt && (
345
+ <div className="text-slate-500 text-xs mt-1">
346
+ 审核:{application.reviewedAt}
347
+ </div>
348
+ )}
349
+ </td>
350
+ <td className="px-6 py-4">
351
+ <button
352
+ onClick={() => {
353
+ setSelectedApplication(application);
354
+ setShowDetailModal(true);
355
+ }}
356
+ className="inline-flex items-center px-3 py-1.5 bg-blue-600/20 hover:bg-blue-600/30 text-blue-400 hover:text-blue-300 rounded-lg transition-all text-sm whitespace-nowrap"
357
+ >
358
+ <i className="ri-eye-line w-4 h-4 flex items-center justify-center mr-1"></i>
359
+ 查看详情
360
+ </button>
361
+ </td>
362
+ </tr>
363
+ ))}
364
+ </tbody>
365
+ </table>
366
+ </div>
367
+
368
+ {filteredApplications.length === 0 && (
369
+ <div className="text-center py-12">
370
+ <i className="ri-inbox-line w-12 h-12 flex items-center justify-center text-slate-500 mx-auto mb-4"></i>
371
+ <p className="text-slate-400">暂无申请记录</p>
372
+ </div>
373
+ )}
374
+ </div>
375
+ </div>
376
+ </div>
377
+
378
+ {/* Detail Modal */}
379
+ {showDetailModal && selectedApplication && (
380
+ <div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50">
381
+ <div className="bg-slate-900/98 backdrop-blur-xl border border-slate-800/60 rounded-2xl shadow-2xl shadow-black/40 w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto">
382
+ {/* Modal Header */}
383
+ <div className="flex items-center justify-between p-6 border-b border-slate-800/60">
384
+ <div className="flex items-center">
385
+ <div className="w-10 h-10 bg-gradient-to-r from-blue-500 to-purple-600 rounded-xl flex items-center justify-center mr-3">
386
+ <i className="ri-user-line w-5 h-5 flex items-center justify-center text-white"></i>
387
+ </div>
388
+ <div>
389
+ <h2 className="text-xl font-semibold text-white">申请详情</h2>
390
+ <p className="text-slate-400 text-sm">
391
+ {selectedApplication.name} -{" "}
392
+ {selectedApplication.companyName}
393
+ </p>
394
+ </div>
395
+ </div>
396
+ <button
397
+ onClick={() => setShowDetailModal(false)}
398
+ className="text-slate-400 hover:text-white transition-colors w-8 h-8 flex items-center justify-center rounded-lg hover:bg-slate-800/50"
399
+ >
400
+ <i className="ri-close-line w-5 h-5 flex items-center justify-center"></i>
401
+ </button>
402
+ </div>
403
+
404
+ {/* Modal Content */}
405
+ <div className="p-6">
406
+ <div className="space-y-6">
407
+ {/* Basic Info */}
408
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
409
+ <div>
410
+ <label className="block text-sm font-medium text-slate-400 mb-2">
411
+ 公司名称
412
+ </label>
413
+ <p className="text-white bg-slate-800/50 px-4 py-3 rounded-xl">
414
+ {selectedApplication.companyName}
415
+ </p>
416
+ </div>
417
+ <div>
418
+ <label className="block text-sm font-medium text-slate-400 mb-2">
419
+ 申请人姓名
420
+ </label>
421
+ <p className="text-white bg-slate-800/50 px-4 py-3 rounded-xl">
422
+ {selectedApplication.name}
423
+ </p>
424
+ </div>
425
+ </div>
426
+
427
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
428
+ <div>
429
+ <label className="block text-sm font-medium text-slate-400 mb-2">
430
+ 手机号
431
+ </label>
432
+ <p className="text-white bg-slate-800/50 px-4 py-3 rounded-xl">
433
+ {selectedApplication.phone}
434
+ </p>
435
+ </div>
436
+ <div>
437
+ <label className="block text-sm font-medium text-slate-400 mb-2">
438
+ 邮箱
439
+ </label>
440
+ <p className="text-white bg-slate-800/50 px-4 py-3 rounded-xl">
441
+ {selectedApplication.email}
442
+ </p>
443
+ </div>
444
+ </div>
445
+
446
+ {selectedApplication.position && (
447
+ <div>
448
+ <label className="block text-sm font-medium text-slate-400 mb-2">
449
+ 职位
450
+ </label>
451
+ <p className="text-white bg-slate-800/50 px-4 py-3 rounded-xl">
452
+ {selectedApplication.position}
453
+ </p>
454
+ </div>
455
+ )}
456
+
457
+ {selectedApplication.requirements && (
458
+ <div>
459
+ <label className="block text-sm font-medium text-slate-400 mb-2">
460
+ 使用需求
461
+ </label>
462
+ <p className="text-white bg-slate-800/50 px-4 py-3 rounded-xl leading-relaxed">
463
+ {selectedApplication.requirements}
464
+ </p>
465
+ </div>
466
+ )}
467
+
468
+ {/* Status and Timeline */}
469
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
470
+ <div>
471
+ <label className="block text-sm font-medium text-slate-400 mb-2">
472
+ 当前状态
473
+ </label>
474
+ <div className="bg-slate-800/50 px-4 py-3 rounded-xl">
475
+ {getStatusBadge(selectedApplication.status)}
476
+ </div>
477
+ </div>
478
+ <div>
479
+ <label className="block text-sm font-medium text-slate-400 mb-2">
480
+ 申请时间
481
+ </label>
482
+ <p className="text-white bg-slate-800/50 px-4 py-3 rounded-xl">
483
+ {selectedApplication.submittedAt}
484
+ </p>
485
+ </div>
486
+ </div>
487
+
488
+ {selectedApplication.reviewedAt && (
489
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
490
+ <div>
491
+ <label className="block text-sm font-medium text-slate-400 mb-2">
492
+ 审核时间
493
+ </label>
494
+ <p className="text-white bg-slate-800/50 px-4 py-3 rounded-xl">
495
+ {selectedApplication.reviewedAt}
496
+ </p>
497
+ </div>
498
+ <div>
499
+ <label className="block text-sm font-medium text-slate-400 mb-2">
500
+ 审核人
501
+ </label>
502
+ <p className="text-white bg-slate-800/50 px-4 py-3 rounded-xl">
503
+ {selectedApplication.reviewedBy}
504
+ </p>
505
+ </div>
506
+ </div>
507
+ )}
508
+
509
+ {/* Actions for Pending Applications */}
510
+ {selectedApplication.status === "pending" && (
511
+ <>
512
+ <div className="border-t border-slate-800 pt-6">
513
+ <h3 className="text-lg font-medium text-white mb-4">
514
+ 审核操作
515
+ </h3>
516
+
517
+ <div className="mb-4">
518
+ <label className="block text-sm font-medium text-slate-400 mb-2">
519
+ 拒绝原因(仅在拒绝时填写)
520
+ </label>
521
+ <textarea
522
+ value={rejectReason}
523
+ onChange={(e) => setRejectReason(e.target.value)}
524
+ rows={3}
525
+ className="w-full px-4 py-3 bg-slate-800/50 border border-slate-700/50 rounded-xl text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all text-sm resize-none"
526
+ placeholder="请输入拒绝原因(可选)"
527
+ />
528
+ </div>
529
+
530
+ <div className="flex space-x-4">
531
+ <button
532
+ onClick={() => handleApprove(selectedApplication.id)}
533
+ className="flex-1 bg-gradient-to-r from-green-600 to-green-700 text-white px-6 py-3 rounded-xl hover:from-green-700 hover:to-green-800 transition-all duration-300 font-medium shadow-lg shadow-green-500/25 hover:shadow-green-500/40 whitespace-nowrap"
534
+ >
535
+ <i className="ri-check-line w-4 h-4 flex items-center justify-center mr-2"></i>
536
+ 通过申请
537
+ </button>
538
+ <button
539
+ onClick={() => handleReject(selectedApplication.id)}
540
+ className="flex-1 bg-gradient-to-r from-red-600 to-red-700 text-white px-6 py-3 rounded-xl hover:from-red-700 hover:to-red-800 transition-all duration-300 font-medium shadow-lg shadow-red-500/25 hover:shadow-red-500/40 whitespace-nowrap"
541
+ >
542
+ <i className="ri-close-line w-4 h-4 flex items-center justify-center mr-2"></i>
543
+ 拒绝申请
544
+ </button>
545
+ </div>
546
+ </div>
547
+ </>
548
+ )}
549
+ </div>
550
+ </div>
551
+ </div>
552
+ </div>
553
+ )}
554
+
555
+ <Footer />
556
+ </>
557
+ );
558
+ }
@@ -0,0 +1,129 @@
1
+ import { useState, useCallback, useEffect } from "react";
2
+ import { useNavigate } from "react-router-dom";
3
+ import { Header, Footer, useGlobalMessage } from "finstep-b-components";
4
+ // import Header from "@/components/f-b-header";
5
+ // import Footer from "@/components/f-b-footer";
6
+ import HeroSection from "@/components/hero-section";
7
+ import TrustedBy from "@/components/trusted-by";
8
+ import { useStore } from "@/stores/store-context";
9
+ import { observer } from "mobx-react-lite";
10
+ import { login, feishuLogin } from "@/api/home";
11
+ import { getQueryParams } from "@/utils/index";
12
+ import { useGlobalLoading } from "@/hooks/useGlobalLoading";
13
+ // AI生成的代码 - Home 页面组件,使用上下文感知的 message API
14
+ function Home() {
15
+ const { userStore } = useStore();
16
+ const navigate = useNavigate();
17
+ const { success } = useGlobalMessage();
18
+ const { withLoading } = useGlobalLoading();
19
+ const [changeLoginMode, setChangeLoginMode] = useState<
20
+ (show: boolean) => void
21
+ >(() => () => {});
22
+ // AI生成的代码 - 使用全局loading store替换局部loading状态
23
+ // const [loading, setLoading] = useState(false);
24
+ const queryParams = getQueryParams();
25
+ const fsCode = queryParams.code; // 获取查询参数
26
+ // AI生成的代码 - 使用 useCallback 避免无限渲染
27
+ const handleLoginModeChange = useCallback(
28
+ (changeLoginModeFunc: (show: boolean) => void) => {
29
+ setChangeLoginMode(() => changeLoginModeFunc);
30
+ },
31
+ []
32
+ );
33
+ // AI生成的代码 - 处理登录逻辑,包含调试信息
34
+ const handleLoginAsync = async (params: {
35
+ phone: string;
36
+ password: string;
37
+ }) => {
38
+ console.log("handleLogin 被调用", params);
39
+ const res = await login({
40
+ username: params.phone,
41
+ password: params.password,
42
+ });
43
+ console.log("登录接口响应:", res);
44
+ userStore.setUserInfo({
45
+ userId: res.user_id,
46
+ login: true,
47
+ username: res.username,
48
+ accessToken: res.access_token,
49
+ refreshToken: res.refresh_token,
50
+ });
51
+ if (changeLoginMode) {
52
+ changeLoginMode(false);
53
+ }
54
+ success("登录成功");
55
+ };
56
+
57
+ // AI生成的代码 - 同步登录处理函数,符合 Header 组件期望的类型
58
+ const handleLogin = (params: { phone: string; password: string }) => {
59
+ handleLoginAsync(params);
60
+ };
61
+ const fsLogin = async () => {
62
+ // await withLoading(async () => {
63
+ // await new Promise((resolve) => setTimeout(resolve, 100000000));
64
+ // }, "正在进行飞书登录...");
65
+ if (userStore.userInfo.login) {
66
+ return;
67
+ }
68
+ if (fsCode) {
69
+ await withLoading(async () => {
70
+ const res = await feishuLogin({ mobile: "FEISHU@" + fsCode });
71
+ userStore.setUserInfo({
72
+ userId: res.user_id,
73
+ login: true,
74
+ username: res.username,
75
+ accessToken: res.access_token,
76
+ refreshToken: res.refresh_token,
77
+ });
78
+ }, "正在进行飞书登录...");
79
+ }
80
+ };
81
+ // AI生成的代码 - 飞书登录处理函数
82
+ const handleFeishuLogin = () => {
83
+ window.location.href = `https://accounts.feishu.cn/open-apis/authen/v1/authorize?client_id=cli_a6d3837cf537900e&redirect_uri=${encodeURIComponent(
84
+ window.location.origin + window.location.pathname
85
+ )}`;
86
+ };
87
+ // AI生成的代码 - 其他处理函数
88
+ const handleLogout = () => {
89
+ userStore.setUserInfo({
90
+ userId: 0,
91
+ login: false,
92
+ username: "",
93
+ accessToken: "",
94
+ refreshToken: "",
95
+ });
96
+ navigate("/");
97
+ };
98
+ const handleNavigate = (path: string) => {
99
+ console.log(path);
100
+ navigate(path);
101
+ };
102
+ const handleRegisterSubmit = () => {};
103
+ useEffect(() => {
104
+ fsLogin();
105
+ }, []);
106
+ return (
107
+ <div className="min-h-screen bg-slate-950">
108
+ <Header
109
+ userInfo={{
110
+ ...userStore.userInfo,
111
+ accessToken: userStore.userInfo.accessToken || "",
112
+ refreshToken: userStore.userInfo.refreshToken || "",
113
+ }}
114
+ onLogin={handleLogin}
115
+ onLogout={handleLogout}
116
+ onFeishuLogin={handleFeishuLogin}
117
+ onNavigate={handleNavigate}
118
+ onRegisterSubmit={handleRegisterSubmit}
119
+ onLoginModeChange={handleLoginModeChange}
120
+ />
121
+ <main>
122
+ <HeroSection />
123
+ <TrustedBy />
124
+ </main>
125
+ <Footer />
126
+ </div>
127
+ );
128
+ }
129
+ export default observer(Home);