mock-service-cli 4.11.0 → 4.12.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.
@@ -1,472 +1,496 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="zh-CN">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>API 概览 - Mock Service</title>
7
- <link rel="icon" type="image/svg+xml" href="/favicon-api-overview.svg">
7
+ <link rel="icon" type="image/svg+xml" href="/favicon-api-overview.svg" />
8
8
  <style>
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- }
14
-
15
- body {
16
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
17
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
- min-height: 100vh;
19
- padding: 20px;
20
- }
21
-
22
- .container {
23
- max-width: 1200px;
24
- margin: 0 auto;
25
- }
26
-
27
- .header {
28
- background: white;
29
- padding: 24px;
30
- border-radius: 12px;
31
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
32
- margin-bottom: 20px;
33
- }
34
-
35
- .header h1 {
36
- color: #333;
37
- font-size: 28px;
38
- margin-bottom: 8px;
39
- }
40
-
41
- .header p {
42
- color: #666;
43
- font-size: 14px;
44
- }
45
-
46
- .stat-cards {
47
- display: grid;
48
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
49
- gap: 16px;
50
- margin-bottom: 20px;
51
- }
52
-
53
- .stat-card {
54
- background: white;
55
- padding: 20px;
56
- border-radius: 12px;
57
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
58
- }
59
-
60
- .stat-card .number {
61
- font-size: 36px;
62
- font-weight: bold;
63
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
64
- -webkit-background-clip: text;
65
- -webkit-text-fill-color: transparent;
66
- background-clip: text;
67
- }
68
-
69
- .stat-card .label {
70
- color: #666;
71
- font-size: 14px;
72
- margin-top: 4px;
73
- }
74
-
75
- .api-container {
76
- background: white;
77
- border-radius: 12px;
78
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
79
- overflow: hidden;
80
- }
81
-
82
- .search-box {
83
- padding: 20px;
84
- border-bottom: 1px solid #eee;
85
- }
86
-
87
- .search-box input {
88
- width: 100%;
89
- padding: 12px 16px;
90
- border: 2px solid #e0e0e0;
91
- border-radius: 8px;
92
- font-size: 14px;
93
- transition: border-color 0.3s;
94
- }
95
-
96
- .search-box input:focus {
97
- outline: none;
98
- border-color: #667eea;
99
- }
100
-
101
- .directory {
102
- border-bottom: 1px solid #eee;
103
- }
104
-
105
- .directory-header {
106
- display: flex;
107
- align-items: center;
108
- padding: 16px 20px;
109
- cursor: pointer;
110
- background: #f8f9fa;
111
- transition: background 0.3s;
112
- }
113
-
114
- .directory-header:hover {
115
- background: #e9ecef;
116
- }
117
-
118
- .toggle-icon {
119
- width: 20px;
120
- height: 20px;
121
- margin-right: 12px;
122
- transition: transform 0.3s;
123
- }
124
-
125
- .toggle-icon.collapsed {
126
- transform: rotate(-90deg);
127
- }
128
-
129
- .directory-name {
130
- font-weight: 600;
131
- color: #333;
132
- flex: 1;
133
- }
134
-
135
- .api-count {
136
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
137
- color: white;
138
- padding: 4px 12px;
139
- border-radius: 12px;
140
- font-size: 12px;
141
- font-weight: 600;
142
- }
143
-
144
- .directory-content {
145
- max-height: 5000px;
146
- overflow: hidden;
147
- transition: max-height 0.3s ease-out;
148
- }
149
-
150
- .directory-content.collapsed {
151
- max-height: 0;
152
- }
153
-
154
- .api-item {
155
- display: flex;
156
- align-items: center;
157
- padding: 14px 20px 14px 52px;
158
- border-bottom: 1px solid #f0f0f0;
159
- cursor: pointer;
160
- transition: background 0.2s;
161
- }
162
-
163
- .api-item:hover {
164
- background: #f8f9fa;
165
- }
166
-
167
- .api-item:last-child {
168
- border-bottom: none;
169
- }
170
-
171
- .method-badge {
172
- padding: 4px 10px;
173
- border-radius: 6px;
174
- font-size: 12px;
175
- font-weight: 700;
176
- text-transform: uppercase;
177
- margin-right: 12px;
178
- min-width: 70px;
179
- text-align: center;
180
- }
181
-
182
- .method-get { background: #61affe; color: white; }
183
- .method-post { background: #49cc90; color: white; }
184
- .method-put { background: #fca130; color: white; }
185
- .method-delete { background: #f93e3e; color: white; }
186
- .method-patch { background: #50e3c2; color: white; }
187
- .method-head { background: #9012fe; color: white; }
188
- .method-options { background: #0d5aa7; color: white; }
189
-
190
- .api-url {
191
- flex: 1;
192
- color: #333;
193
- font-family: 'Courier New', monospace;
194
- font-size: 14px;
195
- }
196
-
197
- .test-btn {
198
- padding: 6px 16px;
199
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
200
- color: white;
201
- border: none;
202
- border-radius: 6px;
203
- font-size: 13px;
204
- font-weight: 600;
205
- cursor: pointer;
206
- transition: transform 0.2s, box-shadow 0.2s;
207
- }
208
-
209
- .test-btn:hover {
210
- transform: translateY(-1px);
211
- box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
212
- }
213
-
214
- .modal {
215
- display: none;
216
- position: fixed;
217
- top: 0;
218
- left: 0;
219
- right: 0;
220
- bottom: 0;
221
- background: rgba(0, 0, 0, 0.5);
222
- z-index: 1000;
223
- padding: 20px;
224
- }
225
-
226
- .modal.active {
227
- display: flex;
228
- align-items: center;
229
- justify-content: center;
230
- }
231
-
232
- .modal-content {
233
- background: white;
234
- border-radius: 12px;
235
- max-width: 85vw;
236
- width: 100%;
237
- max-height: 90vh;
238
- overflow-y: auto;
239
- box-shadow: 0 20px 25px rgba(0, 0, 0, 0.1);
240
- }
241
-
242
- .modal-header {
243
- display: flex;
244
- justify-content: space-between;
245
- align-items: center;
246
- padding: 20px;
247
- border-bottom: 1px solid #eee;
248
- }
249
-
250
- .modal-title {
251
- font-size: 20px;
252
- font-weight: 600;
253
- color: #333;
254
- }
255
-
256
- .close-btn {
257
- width: 32px;
258
- height: 32px;
259
- border: none;
260
- background: #f0f0f0;
261
- border-radius: 6px;
262
- cursor: pointer;
263
- font-size: 18px;
264
- color: #666;
265
- display: flex;
266
- align-items: center;
267
- justify-content: center;
268
- transition: background 0.2s;
269
- }
270
-
271
- .close-btn:hover {
272
- background: #e0e0e0;
273
- }
274
-
275
- .modal-body {
276
- padding: 20px;
277
- }
278
-
279
- .api-info-row {
280
- display: flex;
281
- margin-bottom: 16px;
282
- align-items: center;
283
- }
284
-
285
- .api-info-label {
286
- width: 100px;
287
- font-weight: 600;
288
- color: #666;
289
- }
290
-
291
- .api-info-value {
292
- flex: 1;
293
- color: #333;
294
- }
295
-
296
- .request-section {
297
- margin-top: 24px;
298
- }
299
-
300
- .section-title {
301
- font-size: 16px;
302
- font-weight: 600;
303
- color: #333;
304
- margin-bottom: 12px;
305
- }
306
-
307
- .request-body-input {
308
- width: 100%;
309
- min-height: 120px;
310
- padding: 12px;
311
- border: 2px solid #e0e0e0;
312
- border-radius: 8px;
313
- font-family: 'Courier New', monospace;
314
- font-size: 13px;
315
- resize: vertical;
316
- }
317
-
318
- .request-body-input:focus {
319
- outline: none;
320
- border-color: #667eea;
321
- }
322
-
323
- .send-btn {
324
- margin-top: 16px;
325
- padding: 12px 24px;
326
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
327
- color: white;
328
- border: none;
329
- border-radius: 8px;
330
- font-size: 14px;
331
- font-weight: 600;
332
- cursor: pointer;
333
- transition: transform 0.2s, box-shadow 0.2s;
334
- }
335
-
336
- .send-btn:hover {
337
- transform: translateY(-2px);
338
- box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
339
- }
340
-
341
- .send-btn:disabled {
342
- opacity: 0.6;
343
- cursor: not-allowed;
344
- transform: none;
345
- }
346
-
347
- .response-section {
348
- margin-top: 24px;
349
- }
350
-
351
- .response-status {
352
- display: inline-flex;
353
- align-items: center;
354
- padding: 6px 12px;
355
- border-radius: 6px;
356
- font-weight: 600;
357
- margin-bottom: 12px;
358
- }
359
-
360
- .response-status.success {
361
- background: #d4edda;
362
- color: #155724;
363
- }
364
-
365
- .response-status.error {
366
- background: #f8d7da;
367
- color: #721c24;
368
- }
369
-
370
- .response-output {
371
- width: 100%;
372
- min-height: 150px;
373
- padding: 12px;
374
- background: #f8f9fa;
375
- border: 1px solid #e0e0e0;
376
- border-radius: 8px;
377
- font-family: 'Courier New', monospace;
378
- font-size: 13px;
379
- white-space: pre-wrap;
380
- word-break: break-all;
381
- }
382
-
383
- .loading {
384
- display: inline-block;
385
- width: 16px;
386
- height: 16px;
387
- border: 2px solid #ffffff;
388
- border-radius: 50%;
389
- border-top-color: transparent;
390
- animation: spin 0.8s linear infinite;
391
- margin-right: 8px;
392
- }
393
-
394
- @keyframes spin {
395
- to { transform: rotate(360deg); }
396
- }
397
-
398
- .empty-state {
399
- text-align: center;
400
- padding: 60px 20px;
401
- color: #999;
402
- }
403
-
404
- .empty-state svg {
405
- width: 100px;
406
- height: 100px;
407
- margin-bottom: 20px;
408
- opacity: 0.5;
409
- }
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
17
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
+ min-height: 100vh;
19
+ padding: 20px;
20
+ }
21
+
22
+ .container {
23
+ max-width: 1400px;
24
+ margin: 0 auto;
25
+ }
26
+
27
+ .header {
28
+ background: white;
29
+ padding: 24px;
30
+ border-radius: 12px;
31
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
32
+ margin-bottom: 20px;
33
+ }
34
+
35
+ .header h1 {
36
+ color: #333;
37
+ font-size: 28px;
38
+ margin-bottom: 8px;
39
+ }
40
+
41
+ .header p {
42
+ color: #666;
43
+ font-size: 14px;
44
+ }
45
+
46
+ .stat-cards {
47
+ display: grid;
48
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
49
+ gap: 16px;
50
+ margin-bottom: 20px;
51
+ }
52
+
53
+ .stat-card {
54
+ background: white;
55
+ padding: 20px;
56
+ border-radius: 12px;
57
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
58
+ }
59
+
60
+ .stat-card .number {
61
+ font-size: 36px;
62
+ font-weight: bold;
63
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
64
+ -webkit-background-clip: text;
65
+ -webkit-text-fill-color: transparent;
66
+ background-clip: text;
67
+ }
68
+
69
+ .stat-card .label {
70
+ color: #666;
71
+ font-size: 14px;
72
+ margin-top: 4px;
73
+ }
74
+
75
+ .api-container {
76
+ background: white;
77
+ border-radius: 12px;
78
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
79
+ overflow: hidden;
80
+ }
81
+
82
+ .search-box {
83
+ padding: 20px;
84
+ border-bottom: 1px solid #eee;
85
+ }
86
+
87
+ .search-box input {
88
+ width: 100%;
89
+ padding: 12px 16px;
90
+ border: 2px solid #e0e0e0;
91
+ border-radius: 8px;
92
+ font-size: 14px;
93
+ transition: border-color 0.3s;
94
+ }
95
+
96
+ .search-box input:focus {
97
+ outline: none;
98
+ border-color: #667eea;
99
+ }
100
+
101
+ .directory {
102
+ border-bottom: 1px solid #eee;
103
+ }
104
+
105
+ .directory-header {
106
+ display: flex;
107
+ align-items: center;
108
+ padding: 16px 20px;
109
+ cursor: pointer;
110
+ background: #f8f9fa;
111
+ transition: background 0.3s;
112
+ }
113
+
114
+ .directory-header:hover {
115
+ background: #e9ecef;
116
+ }
117
+
118
+ .toggle-icon {
119
+ width: 20px;
120
+ height: 20px;
121
+ margin-right: 12px;
122
+ transition: transform 0.3s;
123
+ }
124
+
125
+ .toggle-icon.collapsed {
126
+ transform: rotate(-90deg);
127
+ }
128
+
129
+ .directory-name {
130
+ font-weight: 600;
131
+ color: #333;
132
+ flex: 1;
133
+ }
134
+
135
+ .api-count {
136
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
137
+ color: white;
138
+ padding: 4px 12px;
139
+ border-radius: 12px;
140
+ font-size: 12px;
141
+ font-weight: 600;
142
+ }
143
+
144
+ .directory-content {
145
+ max-height: 5000px;
146
+ overflow: hidden;
147
+ transition: max-height 0.3s ease-out;
148
+ }
149
+
150
+ .directory-content.collapsed {
151
+ max-height: 0;
152
+ }
153
+
154
+ .api-item {
155
+ display: flex;
156
+ align-items: center;
157
+ padding: 14px 20px 14px 52px;
158
+ border-bottom: 1px solid #f0f0f0;
159
+ cursor: pointer;
160
+ transition: background 0.2s;
161
+ }
162
+
163
+ .api-item:hover {
164
+ background: #f8f9fa;
165
+ }
166
+
167
+ .api-item:last-child {
168
+ border-bottom: none;
169
+ }
170
+
171
+ .method-badge {
172
+ padding: 4px 10px;
173
+ border-radius: 6px;
174
+ font-size: 12px;
175
+ font-weight: 700;
176
+ text-transform: uppercase;
177
+ margin-right: 12px;
178
+ min-width: 70px;
179
+ text-align: center;
180
+ }
181
+
182
+ .method-get {
183
+ background: #61affe;
184
+ color: white;
185
+ }
186
+ .method-post {
187
+ background: #49cc90;
188
+ color: white;
189
+ }
190
+ .method-put {
191
+ background: #fca130;
192
+ color: white;
193
+ }
194
+ .method-delete {
195
+ background: #f93e3e;
196
+ color: white;
197
+ }
198
+ .method-patch {
199
+ background: #50e3c2;
200
+ color: white;
201
+ }
202
+ .method-head {
203
+ background: #9012fe;
204
+ color: white;
205
+ }
206
+ .method-options {
207
+ background: #0d5aa7;
208
+ color: white;
209
+ }
210
+
211
+ .api-url {
212
+ flex: 1;
213
+ color: #333;
214
+ font-family: 'Courier New', monospace;
215
+ font-size: 14px;
216
+ }
217
+
218
+ .test-btn {
219
+ padding: 6px 16px;
220
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
221
+ color: white;
222
+ border: none;
223
+ border-radius: 6px;
224
+ font-size: 13px;
225
+ font-weight: 600;
226
+ cursor: pointer;
227
+ transition:
228
+ transform 0.2s,
229
+ box-shadow 0.2s;
230
+ }
231
+
232
+ .test-btn:hover {
233
+ transform: translateY(-1px);
234
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
235
+ }
236
+
237
+ .modal {
238
+ display: none;
239
+ position: fixed;
240
+ top: 0;
241
+ left: 0;
242
+ right: 0;
243
+ bottom: 0;
244
+ background: rgba(0, 0, 0, 0.5);
245
+ z-index: 1000;
246
+ padding: 20px;
247
+ }
248
+
249
+ .modal.active {
250
+ display: flex;
251
+ align-items: center;
252
+ justify-content: center;
253
+ }
254
+
255
+ .modal-content {
256
+ background: white;
257
+ border-radius: 12px;
258
+ max-width: 85vw;
259
+ width: 100%;
260
+ max-height: 90vh;
261
+ overflow-y: auto;
262
+ box-shadow: 0 20px 25px rgba(0, 0, 0, 0.1);
263
+ }
264
+
265
+ .modal-header {
266
+ display: flex;
267
+ justify-content: space-between;
268
+ align-items: center;
269
+ padding: 20px;
270
+ border-bottom: 1px solid #eee;
271
+ }
272
+
273
+ .modal-title {
274
+ font-size: 20px;
275
+ font-weight: 600;
276
+ color: #333;
277
+ }
278
+
279
+ .close-btn {
280
+ width: 32px;
281
+ height: 32px;
282
+ border: none;
283
+ background: #f0f0f0;
284
+ border-radius: 6px;
285
+ cursor: pointer;
286
+ font-size: 18px;
287
+ color: #666;
288
+ display: flex;
289
+ align-items: center;
290
+ justify-content: center;
291
+ transition: background 0.2s;
292
+ }
293
+
294
+ .close-btn:hover {
295
+ background: #e0e0e0;
296
+ }
297
+
298
+ .modal-body {
299
+ padding: 20px;
300
+ }
301
+
302
+ .api-info-row {
303
+ display: flex;
304
+ margin-bottom: 16px;
305
+ align-items: center;
306
+ }
307
+
308
+ .api-info-label {
309
+ width: 100px;
310
+ font-weight: 600;
311
+ color: #666;
312
+ }
313
+
314
+ .api-info-value {
315
+ flex: 1;
316
+ color: #333;
317
+ }
318
+
319
+ .request-section {
320
+ margin-top: 24px;
321
+ }
322
+
323
+ .section-title {
324
+ font-size: 16px;
325
+ font-weight: 600;
326
+ color: #333;
327
+ margin-bottom: 12px;
328
+ }
329
+
330
+ .request-body-input {
331
+ width: 100%;
332
+ min-height: 120px;
333
+ padding: 12px;
334
+ border: 2px solid #e0e0e0;
335
+ border-radius: 8px;
336
+ font-family: 'Courier New', monospace;
337
+ font-size: 13px;
338
+ resize: vertical;
339
+ }
340
+
341
+ .request-body-input:focus {
342
+ outline: none;
343
+ border-color: #667eea;
344
+ }
345
+
346
+ .send-btn {
347
+ margin-top: 16px;
348
+ padding: 12px 24px;
349
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
350
+ color: white;
351
+ border: none;
352
+ border-radius: 8px;
353
+ font-size: 14px;
354
+ font-weight: 600;
355
+ cursor: pointer;
356
+ transition:
357
+ transform 0.2s,
358
+ box-shadow 0.2s;
359
+ }
360
+
361
+ .send-btn:hover {
362
+ transform: translateY(-2px);
363
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
364
+ }
365
+
366
+ .send-btn:disabled {
367
+ opacity: 0.6;
368
+ cursor: not-allowed;
369
+ transform: none;
370
+ }
371
+
372
+ .response-section {
373
+ margin-top: 24px;
374
+ }
375
+
376
+ .response-status {
377
+ display: inline-flex;
378
+ align-items: center;
379
+ padding: 6px 12px;
380
+ border-radius: 6px;
381
+ font-weight: 600;
382
+ margin-bottom: 12px;
383
+ }
384
+
385
+ .response-status.success {
386
+ background: #d4edda;
387
+ color: #155724;
388
+ }
389
+
390
+ .response-status.error {
391
+ background: #f8d7da;
392
+ color: #721c24;
393
+ }
394
+
395
+ .response-output {
396
+ width: 100%;
397
+ min-height: 150px;
398
+ padding: 12px;
399
+ background: #f8f9fa;
400
+ border: 1px solid #e0e0e0;
401
+ border-radius: 8px;
402
+ font-family: 'Courier New', monospace;
403
+ font-size: 13px;
404
+ white-space: pre-wrap;
405
+ word-break: break-all;
406
+ }
407
+
408
+ .loading {
409
+ display: inline-block;
410
+ width: 16px;
411
+ height: 16px;
412
+ border: 2px solid #ffffff;
413
+ border-radius: 50%;
414
+ border-top-color: transparent;
415
+ animation: spin 0.8s linear infinite;
416
+ margin-right: 8px;
417
+ }
418
+
419
+ @keyframes spin {
420
+ to {
421
+ transform: rotate(360deg);
422
+ }
423
+ }
424
+
425
+ .empty-state {
426
+ text-align: center;
427
+ padding: 60px 20px;
428
+ color: #999;
429
+ }
430
+
431
+ .empty-state svg {
432
+ width: 100px;
433
+ height: 100px;
434
+ margin-bottom: 20px;
435
+ opacity: 0.5;
436
+ }
410
437
  </style>
411
- </head>
412
- <body>
438
+ </head>
439
+ <body>
413
440
  <div class="container">
414
- <div class="header">
415
- <h1>🎯 API 概览</h1>
416
- <p>探索和测试您的 Mock API 接口</p>
417
- </div>
441
+ <div class="header">
442
+ <h1>🎯 API 概览</h1>
443
+ <p>探索和测试您的 Mock API 接口</p>
444
+ </div>
418
445
 
419
- <div class="stat-cards" id="statCards">
420
- </div>
446
+ <div class="stat-cards" id="statCards"></div>
421
447
 
422
- <div class="api-container">
423
- <div class="search-box">
424
- <input type="text" id="searchInput" placeholder="搜索 API 接口 (输入 URL 或 HTTP 方法)...">
425
- </div>
426
- <div id="apiList">
427
- </div>
448
+ <div class="api-container">
449
+ <div class="search-box">
450
+ <input type="text" id="searchInput" placeholder="搜索 API 接口 (输入 URL 或 HTTP 方法)..." />
428
451
  </div>
452
+ <div id="apiList"></div>
453
+ </div>
429
454
  </div>
430
455
 
431
456
  <div class="modal" id="apiModal">
432
- <div class="modal-content">
433
- <div class="modal-header">
434
- <div class="modal-title" id="modalTitle">API 测试</div>
435
- <button class="close-btn" onclick="closeModal()">×</button>
436
- </div>
437
- <div class="modal-body" id="modalBody">
438
- </div>
457
+ <div class="modal-content">
458
+ <div class="modal-header">
459
+ <div class="modal-title" id="modalTitle">API 测试</div>
460
+ <button class="close-btn" onclick="closeModal()">×</button>
439
461
  </div>
462
+ <div class="modal-body" id="modalBody"></div>
463
+ </div>
440
464
  </div>
441
465
 
442
466
  <script>
443
- let apiData = null;
444
- let currentApi = null;
445
-
446
- document.addEventListener('DOMContentLoaded', () => {
447
- fetchApiData();
448
- document.getElementById('searchInput').addEventListener('input', filterApis);
467
+ let apiData = null;
468
+ let currentApi = null;
469
+
470
+ document.addEventListener('DOMContentLoaded', () => {
471
+ fetchApiData();
472
+ document.getElementById('searchInput').addEventListener('input', filterApis);
473
+ });
474
+
475
+ async function fetchApiData() {
476
+ try {
477
+ const response = await fetch('/__api-data');
478
+ apiData = await response.json();
479
+ renderStatCards();
480
+ renderApiList();
481
+ } catch (error) {
482
+ console.error('Failed to fetch API data:', error);
483
+ }
484
+ }
485
+
486
+ function renderStatCards() {
487
+ const methodCounts = {};
488
+ apiData.apis.forEach(api => {
489
+ methodCounts[api.method] = (methodCounts[api.method] || 0) + 1;
449
490
  });
450
491
 
451
- async function fetchApiData() {
452
- try {
453
- const response = await fetch('/__api-data');
454
- apiData = await response.json();
455
- renderStatCards();
456
- renderApiList();
457
- } catch (error) {
458
- console.error('Failed to fetch API data:', error);
459
- }
460
- }
461
-
462
- function renderStatCards() {
463
- const methodCounts = {};
464
- apiData.apis.forEach(api => {
465
- methodCounts[api.method] = (methodCounts[api.method] || 0) + 1;
466
- });
467
-
468
- const container = document.getElementById('statCards');
469
- let html = `
492
+ const container = document.getElementById('statCards');
493
+ let html = `
470
494
  <div class="stat-card">
471
495
  <div class="number">${apiData.apis.length}</div>
472
496
  <div class="label">总接口数</div>
@@ -477,36 +501,35 @@
477
501
  </div>
478
502
  `;
479
503
 
480
- Object.entries(methodCounts).forEach(([method, count]) => {
481
- html += `
504
+ Object.entries(methodCounts).forEach(([method, count]) => {
505
+ html += `
482
506
  <div class="stat-card">
483
507
  <div class="number">${count}</div>
484
508
  <div class="label">${method.toUpperCase()}</div>
485
509
  </div>
486
510
  `;
487
- });
511
+ });
488
512
 
489
- container.innerHTML = html;
490
- }
513
+ container.innerHTML = html;
514
+ }
491
515
 
492
- function renderApiList(filterText = '') {
493
- const container = document.getElementById('apiList');
494
- const searchLower = filterText.toLowerCase();
516
+ function renderApiList(filterText = '') {
517
+ const container = document.getElementById('apiList');
518
+ const searchLower = filterText.toLowerCase();
495
519
 
496
- let html = '';
497
- let hasResults = false;
520
+ let html = '';
521
+ let hasResults = false;
498
522
 
499
- Object.entries(apiData.groupedApis).forEach(([dir, apis]) => {
500
- const filteredApis = apis.filter(api =>
501
- api.url.toLowerCase().includes(searchLower) ||
502
- api.method.toLowerCase().includes(searchLower)
503
- );
523
+ Object.entries(apiData.groupedApis).forEach(([dir, apis]) => {
524
+ const filteredApis = apis.filter(
525
+ api => api.url.toLowerCase().includes(searchLower) || api.method.toLowerCase().includes(searchLower)
526
+ );
504
527
 
505
- if (filteredApis.length === 0) return;
506
- hasResults = true;
528
+ if (filteredApis.length === 0) return;
529
+ hasResults = true;
507
530
 
508
- const dirName = dir.replace(/\\/g, '/');
509
- html += `
531
+ const dirName = dir.replace(/\\/g, '/');
532
+ html += `
510
533
  <div class="directory">
511
534
  <div class="directory-header" onclick="toggleDirectory(this)">
512
535
  <svg class="toggle-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -520,10 +543,10 @@
520
543
  </div>
521
544
  </div>
522
545
  `;
523
- });
546
+ });
524
547
 
525
- if (!hasResults) {
526
- html = `
548
+ if (!hasResults) {
549
+ html = `
527
550
  <div class="empty-state">
528
551
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
529
552
  <circle cx="11" cy="11" r="8"></circle>
@@ -533,51 +556,55 @@
533
556
  <p>尝试使用其他关键词搜索</p>
534
557
  </div>
535
558
  `;
536
- }
537
-
538
- container.innerHTML = html;
539
559
  }
540
560
 
541
- function renderApiItem(api) {
542
- return `
561
+ container.innerHTML = html;
562
+ }
563
+
564
+ function renderApiItem(api) {
565
+ return `
543
566
  <div class="api-item" onclick="openApiModal('${api.method}', '${api.url.replace(/'/g, "\\'")}', '${api.file.replace(/'/g, "\\'")}')">
544
567
  <span class="method-badge method-${api.method.toLowerCase()}">${api.method}</span>
545
568
  <span class="api-url">${api.url}</span>
546
569
  <button class="test-btn" onclick="event.stopPropagation(); testApi('${api.method}', '${api.url.replace(/'/g, "\\'")}')">测试</button>
547
570
  </div>
548
571
  `;
549
- }
572
+ }
550
573
 
551
- function toggleDirectory(header) {
552
- const icon = header.querySelector('.toggle-icon');
553
- const content = header.nextElementSibling;
554
- icon.classList.toggle('collapsed');
555
- content.classList.toggle('collapsed');
556
- }
574
+ function toggleDirectory(header) {
575
+ const icon = header.querySelector('.toggle-icon');
576
+ const content = header.nextElementSibling;
577
+ icon.classList.toggle('collapsed');
578
+ content.classList.toggle('collapsed');
579
+ }
557
580
 
558
- function filterApis(e) {
559
- renderApiList(e.target.value);
560
- }
581
+ function filterApis(e) {
582
+ renderApiList(e.target.value);
583
+ }
561
584
 
562
- function openApiModal(method, url, file) {
563
- currentApi = { method, url, file };
564
- const modal = document.getElementById('apiModal');
565
- const title = document.getElementById('modalTitle');
566
- const body = document.getElementById('modalBody');
585
+ function openApiModal(method, url, file) {
586
+ currentApi = { method, url, file };
587
+ const modal = document.getElementById('apiModal');
588
+ const title = document.getElementById('modalTitle');
589
+ const body = document.getElementById('modalBody');
567
590
 
568
- title.innerHTML = `<span class="method-badge method-${method.toLowerCase()}">${method}</span> ${url}`;
591
+ title.innerHTML = `<span class="method-badge method-${method.toLowerCase()}">${method}</span> ${url}`;
569
592
 
570
- body.innerHTML = `
593
+ body.innerHTML = `
571
594
  <div class="api-info-row">
572
595
  <span class="api-info-label">文件路径:</span>
573
596
  <span class="api-info-value">${file}</span>
574
597
  </div>
575
- ${(method === 'POST' || method === 'PUT' || method === 'PATCH') ? `
598
+ ${
599
+ method === 'POST' || method === 'PUT' || method === 'PATCH'
600
+ ? `
576
601
  <div class="request-section">
577
602
  <div class="section-title">请求体 (JSON)</div>
578
603
  <textarea class="request-body-input" id="requestBody" placeholder='{"key": "value"}'></textarea>
579
604
  </div>
580
- ` : ''}
605
+ `
606
+ : ''
607
+ }
581
608
  <button class="send-btn" id="sendBtn" onclick="sendRequest()">发送请求</button>
582
609
  <div class="response-section" id="responseSection" style="display: none;">
583
610
  <div class="section-title">响应</div>
@@ -586,100 +613,100 @@
586
613
  </div>
587
614
  `;
588
615
 
589
- modal.classList.add('active');
590
- }
591
-
592
- function closeModal() {
593
- document.getElementById('apiModal').classList.remove('active');
594
- currentApi = null;
595
- }
596
-
597
- async function testApi(method, url) {
598
- currentApi = { method, url };
599
- await sendRequestDirect(method, url);
600
- }
601
-
602
- async function sendRequest() {
603
- if (!currentApi) return;
604
- await sendRequestDirect(currentApi.method, currentApi.url);
605
- }
606
-
607
- async function sendRequestDirect(method, url) {
608
- const sendBtn = document.getElementById('sendBtn');
609
- const responseSection = document.getElementById('responseSection');
610
- const responseStatus = document.getElementById('responseStatus');
611
- const responseOutput = document.getElementById('responseOutput');
612
-
613
- let requestBody = null;
614
- const requestBodyEl = document.getElementById('requestBody');
615
- if (requestBodyEl) {
616
- const bodyText = requestBodyEl.value.trim();
617
- if (bodyText) {
618
- try {
619
- requestBody = JSON.parse(bodyText);
620
- } catch (e) {
621
- alert('请求体 JSON 格式错误');
622
- return;
623
- }
624
- }
625
- }
626
-
627
- if (sendBtn) {
628
- sendBtn.disabled = true;
629
- sendBtn.innerHTML = '<span class="loading"></span>发送中...';
630
- }
631
-
616
+ modal.classList.add('active');
617
+ }
618
+
619
+ function closeModal() {
620
+ document.getElementById('apiModal').classList.remove('active');
621
+ currentApi = null;
622
+ }
623
+
624
+ async function testApi(method, url) {
625
+ currentApi = { method, url };
626
+ await sendRequestDirect(method, url);
627
+ }
628
+
629
+ async function sendRequest() {
630
+ if (!currentApi) return;
631
+ await sendRequestDirect(currentApi.method, currentApi.url);
632
+ }
633
+
634
+ async function sendRequestDirect(method, url) {
635
+ const sendBtn = document.getElementById('sendBtn');
636
+ const responseSection = document.getElementById('responseSection');
637
+ const responseStatus = document.getElementById('responseStatus');
638
+ const responseOutput = document.getElementById('responseOutput');
639
+
640
+ let requestBody = null;
641
+ const requestBodyEl = document.getElementById('requestBody');
642
+ if (requestBodyEl) {
643
+ const bodyText = requestBodyEl.value.trim();
644
+ if (bodyText) {
632
645
  try {
633
- const options = {
634
- method: method,
635
- headers: {
636
- 'Content-Type': 'application/json'
637
- }
638
- };
639
-
640
- if (requestBody) {
641
- options.body = JSON.stringify(requestBody);
642
- }
643
-
644
- const response = await fetch(url, options);
645
- const data = await response.json();
646
-
647
- if (responseSection) responseSection.style.display = 'block';
648
- if (responseStatus) {
649
- responseStatus.className = 'response-status success';
650
- responseStatus.textContent = `✓ ${response.status} ${response.statusText}`;
651
- }
652
- if (responseOutput) {
653
- responseOutput.textContent = JSON.stringify(data, null, 2);
654
- }
655
- } catch (error) {
656
- if (responseSection) responseSection.style.display = 'block';
657
- if (responseStatus) {
658
- responseStatus.className = 'response-status error';
659
- responseStatus.textContent = '✗ 请求失败';
660
- }
661
- if (responseOutput) {
662
- responseOutput.textContent = error.message;
663
- }
664
- }
665
-
666
- if (sendBtn) {
667
- sendBtn.disabled = false;
668
- sendBtn.innerHTML = '发送请求';
646
+ requestBody = JSON.parse(bodyText);
647
+ } catch (e) {
648
+ alert('请求体 JSON 格式错误');
649
+ return;
669
650
  }
651
+ }
670
652
  }
671
653
 
672
- document.getElementById('apiModal').addEventListener('click', (e) => {
673
- if (e.target === e.currentTarget) {
674
- closeModal();
675
- }
676
- });
654
+ if (sendBtn) {
655
+ sendBtn.disabled = true;
656
+ sendBtn.innerHTML = '<span class="loading"></span>发送中...';
657
+ }
677
658
 
678
- document.addEventListener('keydown', (e) => {
679
- if (e.key === 'Escape') {
680
- closeModal();
659
+ try {
660
+ const options = {
661
+ method: method,
662
+ headers: {
663
+ 'Content-Type': 'application/json'
681
664
  }
682
- });
665
+ };
666
+
667
+ if (requestBody) {
668
+ options.body = JSON.stringify(requestBody);
669
+ }
670
+
671
+ const response = await fetch(url, options);
672
+ const data = await response.json();
673
+
674
+ if (responseSection) responseSection.style.display = 'block';
675
+ if (responseStatus) {
676
+ responseStatus.className = 'response-status success';
677
+ responseStatus.textContent = `✓ ${response.status} ${response.statusText}`;
678
+ }
679
+ if (responseOutput) {
680
+ responseOutput.textContent = JSON.stringify(data, null, 2);
681
+ }
682
+ } catch (error) {
683
+ if (responseSection) responseSection.style.display = 'block';
684
+ if (responseStatus) {
685
+ responseStatus.className = 'response-status error';
686
+ responseStatus.textContent = '✗ 请求失败';
687
+ }
688
+ if (responseOutput) {
689
+ responseOutput.textContent = error.message;
690
+ }
691
+ }
692
+
693
+ if (sendBtn) {
694
+ sendBtn.disabled = false;
695
+ sendBtn.innerHTML = '发送请求';
696
+ }
697
+ }
698
+
699
+ document.getElementById('apiModal').addEventListener('click', e => {
700
+ if (e.target === e.currentTarget) {
701
+ closeModal();
702
+ }
703
+ });
704
+
705
+ document.addEventListener('keydown', e => {
706
+ if (e.key === 'Escape') {
707
+ closeModal();
708
+ }
709
+ });
683
710
  </script>
684
- </body>
711
+ </body>
685
712
  </html>