pytest-dsl 0.10.0__py3-none-any.whl → 0.11.1__py3-none-any.whl
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.
- pytest_dsl/cli.py +533 -89
- pytest_dsl/core/custom_keyword_manager.py +1 -4
- pytest_dsl/core/http_client.py +2 -2
- pytest_dsl/core/keyword_manager.py +77 -3
- pytest_dsl/core/plugin_discovery.py +38 -1
- pytest_dsl/keywords/http_keywords.py +90 -34
- pytest_dsl/remote/keyword_client.py +30 -17
- pytest_dsl/remote/keyword_server.py +18 -1
- pytest_dsl/remote/variable_bridge.py +41 -8
- pytest_dsl/templates/keywords_report.html +862 -0
- {pytest_dsl-0.10.0.dist-info → pytest_dsl-0.11.1.dist-info}/METADATA +2 -1
- {pytest_dsl-0.10.0.dist-info → pytest_dsl-0.11.1.dist-info}/RECORD +16 -15
- {pytest_dsl-0.10.0.dist-info → pytest_dsl-0.11.1.dist-info}/WHEEL +0 -0
- {pytest_dsl-0.10.0.dist-info → pytest_dsl-0.11.1.dist-info}/entry_points.txt +0 -0
- {pytest_dsl-0.10.0.dist-info → pytest_dsl-0.11.1.dist-info}/licenses/LICENSE +0 -0
- {pytest_dsl-0.10.0.dist-info → pytest_dsl-0.11.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,862 @@
|
|
1
|
+
{# 关键字卡片宏定义 #}
|
2
|
+
{% macro render_keyword_card(keyword) %}
|
3
|
+
<div class="keyword-card">
|
4
|
+
<div class="keyword-header" onclick="toggleKeyword(this)">
|
5
|
+
<div class="keyword-name">{{ keyword.name }}</div>
|
6
|
+
<div>
|
7
|
+
<span class="keyword-category category-{{ keyword.category }}">
|
8
|
+
{{ category_names[keyword.category] }}
|
9
|
+
</span>
|
10
|
+
{% if keyword.source_info and keyword.source_info.name != category_names[keyword.category] %}
|
11
|
+
<span class="source-badge">{{ keyword.source_info.name }}</span>
|
12
|
+
{% endif %}
|
13
|
+
<span class="expand-icon">▼</span>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
<div class="keyword-details">
|
17
|
+
{% if keyword.file_location %}
|
18
|
+
<div class="file-location">📁 {{ keyword.file_location }}</div>
|
19
|
+
{% elif keyword.source_info and keyword.source_info.module %}
|
20
|
+
<div class="file-location">📦 {{ keyword.source_info.module }}</div>
|
21
|
+
{% endif %}
|
22
|
+
|
23
|
+
<div class="parameters">
|
24
|
+
<h4>参数:</h4>
|
25
|
+
{% if keyword.parameters %}
|
26
|
+
{% for param in keyword.parameters %}
|
27
|
+
<div class="parameter">
|
28
|
+
<div class="parameter-name">
|
29
|
+
{{ param.name }}
|
30
|
+
{% if param.mapping and param.mapping != param.name %}
|
31
|
+
<span class="parameter-mapping">({{ param.mapping }})</span>
|
32
|
+
{% endif %}
|
33
|
+
{% if param.default is defined %}
|
34
|
+
<span class="parameter-default">默认: {{ param.default }}</span>
|
35
|
+
{% endif %}
|
36
|
+
</div>
|
37
|
+
<div class="parameter-description">{{ param.description }}</div>
|
38
|
+
</div>
|
39
|
+
{% endfor %}
|
40
|
+
{% else %}
|
41
|
+
<p>无参数</p>
|
42
|
+
{% endif %}
|
43
|
+
</div>
|
44
|
+
|
45
|
+
{% if keyword.documentation %}
|
46
|
+
<div class="documentation">
|
47
|
+
<strong>说明:</strong> {{ keyword.documentation }}
|
48
|
+
</div>
|
49
|
+
{% endif %}
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
{% endmacro %}
|
53
|
+
|
54
|
+
<!DOCTYPE html>
|
55
|
+
<html lang="zh-CN">
|
56
|
+
<head>
|
57
|
+
<meta charset="UTF-8">
|
58
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
59
|
+
<title>pytest-dsl 关键字列表</title>
|
60
|
+
<style>
|
61
|
+
* {
|
62
|
+
box-sizing: border-box;
|
63
|
+
margin: 0;
|
64
|
+
padding: 0;
|
65
|
+
}
|
66
|
+
|
67
|
+
body {
|
68
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
69
|
+
background-color: #f5f5f5;
|
70
|
+
height: 100vh;
|
71
|
+
overflow: hidden;
|
72
|
+
}
|
73
|
+
|
74
|
+
.container {
|
75
|
+
display: flex;
|
76
|
+
height: 100vh;
|
77
|
+
background: white;
|
78
|
+
}
|
79
|
+
|
80
|
+
/* 左侧侧边栏 */
|
81
|
+
.sidebar {
|
82
|
+
width: 400px;
|
83
|
+
background: #f8f9fa;
|
84
|
+
border-right: 1px solid #dee2e6;
|
85
|
+
display: flex;
|
86
|
+
flex-direction: column;
|
87
|
+
}
|
88
|
+
|
89
|
+
.sidebar-header {
|
90
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
91
|
+
color: white;
|
92
|
+
padding: 20px;
|
93
|
+
text-align: center;
|
94
|
+
}
|
95
|
+
|
96
|
+
.sidebar-header h1 {
|
97
|
+
font-size: 1.5em;
|
98
|
+
font-weight: 300;
|
99
|
+
margin-bottom: 10px;
|
100
|
+
}
|
101
|
+
|
102
|
+
.sidebar-header .summary {
|
103
|
+
font-size: 0.9em;
|
104
|
+
opacity: 0.9;
|
105
|
+
}
|
106
|
+
|
107
|
+
.search-section {
|
108
|
+
padding: 15px;
|
109
|
+
border-bottom: 1px solid #dee2e6;
|
110
|
+
}
|
111
|
+
|
112
|
+
.search-box {
|
113
|
+
width: 100%;
|
114
|
+
padding: 10px 15px;
|
115
|
+
border: 1px solid #dee2e6;
|
116
|
+
border-radius: 20px;
|
117
|
+
font-size: 0.9em;
|
118
|
+
outline: none;
|
119
|
+
}
|
120
|
+
|
121
|
+
.search-box:focus {
|
122
|
+
border-color: #667eea;
|
123
|
+
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
124
|
+
}
|
125
|
+
|
126
|
+
.filter-tabs {
|
127
|
+
display: flex;
|
128
|
+
background: #e9ecef;
|
129
|
+
margin: 15px;
|
130
|
+
border-radius: 8px;
|
131
|
+
overflow: hidden;
|
132
|
+
flex-wrap: wrap;
|
133
|
+
}
|
134
|
+
|
135
|
+
.filter-tab {
|
136
|
+
flex: 1;
|
137
|
+
min-width: 60px;
|
138
|
+
padding: 8px 12px;
|
139
|
+
text-align: center;
|
140
|
+
cursor: pointer;
|
141
|
+
background: transparent;
|
142
|
+
border: none;
|
143
|
+
font-size: 0.75em;
|
144
|
+
transition: all 0.3s ease;
|
145
|
+
}
|
146
|
+
|
147
|
+
.filter-tab:hover {
|
148
|
+
background: rgba(102, 126, 234, 0.1);
|
149
|
+
}
|
150
|
+
|
151
|
+
.filter-tab.active {
|
152
|
+
background: #667eea;
|
153
|
+
color: white;
|
154
|
+
}
|
155
|
+
|
156
|
+
/* 树结构列表 */
|
157
|
+
.tree-container {
|
158
|
+
flex: 1;
|
159
|
+
overflow-y: auto;
|
160
|
+
padding: 0 15px 15px;
|
161
|
+
}
|
162
|
+
|
163
|
+
.tree-node {
|
164
|
+
margin-bottom: 5px;
|
165
|
+
}
|
166
|
+
|
167
|
+
.tree-category {
|
168
|
+
position: sticky;
|
169
|
+
top: 0;
|
170
|
+
background: #f8f9fa;
|
171
|
+
z-index: 10;
|
172
|
+
}
|
173
|
+
|
174
|
+
.category-header {
|
175
|
+
display: flex;
|
176
|
+
align-items: center;
|
177
|
+
padding: 10px;
|
178
|
+
cursor: pointer;
|
179
|
+
border-radius: 6px;
|
180
|
+
margin: 5px 0;
|
181
|
+
font-weight: 600;
|
182
|
+
color: #495057;
|
183
|
+
background: #e9ecef;
|
184
|
+
}
|
185
|
+
|
186
|
+
.category-header:hover {
|
187
|
+
background: #dee2e6;
|
188
|
+
}
|
189
|
+
|
190
|
+
.category-icon {
|
191
|
+
margin-right: 8px;
|
192
|
+
font-size: 0.8em;
|
193
|
+
transition: transform 0.3s ease;
|
194
|
+
}
|
195
|
+
|
196
|
+
.category-icon.expanded {
|
197
|
+
transform: rotate(90deg);
|
198
|
+
}
|
199
|
+
|
200
|
+
.category-badge {
|
201
|
+
margin-left: auto;
|
202
|
+
background: #6c757d;
|
203
|
+
color: white;
|
204
|
+
padding: 2px 8px;
|
205
|
+
border-radius: 10px;
|
206
|
+
font-size: 0.7em;
|
207
|
+
font-weight: normal;
|
208
|
+
}
|
209
|
+
|
210
|
+
.category-keywords {
|
211
|
+
margin-left: 20px;
|
212
|
+
display: none;
|
213
|
+
}
|
214
|
+
|
215
|
+
.category-keywords.expanded {
|
216
|
+
display: block;
|
217
|
+
}
|
218
|
+
|
219
|
+
.keyword-item {
|
220
|
+
padding: 8px 12px;
|
221
|
+
margin: 2px 0;
|
222
|
+
cursor: pointer;
|
223
|
+
border-radius: 4px;
|
224
|
+
border-left: 3px solid transparent;
|
225
|
+
transition: all 0.3s ease;
|
226
|
+
font-size: 0.9em;
|
227
|
+
}
|
228
|
+
|
229
|
+
.keyword-item:hover {
|
230
|
+
background: #e9ecef;
|
231
|
+
border-left-color: #dee2e6;
|
232
|
+
}
|
233
|
+
|
234
|
+
.keyword-item.selected {
|
235
|
+
background: #667eea;
|
236
|
+
color: white;
|
237
|
+
border-left-color: #495057;
|
238
|
+
}
|
239
|
+
|
240
|
+
.keyword-name {
|
241
|
+
font-weight: 500;
|
242
|
+
}
|
243
|
+
|
244
|
+
.keyword-preview {
|
245
|
+
font-size: 0.8em;
|
246
|
+
opacity: 0.7;
|
247
|
+
margin-top: 2px;
|
248
|
+
}
|
249
|
+
|
250
|
+
.source-badge {
|
251
|
+
background: #17a2b8;
|
252
|
+
color: white;
|
253
|
+
padding: 2px 6px;
|
254
|
+
border-radius: 8px;
|
255
|
+
font-size: 0.65em;
|
256
|
+
margin-left: 4px;
|
257
|
+
}
|
258
|
+
|
259
|
+
/* 右侧详情区域 */
|
260
|
+
.detail-panel {
|
261
|
+
flex: 1;
|
262
|
+
display: flex;
|
263
|
+
flex-direction: column;
|
264
|
+
background: white;
|
265
|
+
}
|
266
|
+
|
267
|
+
.detail-header {
|
268
|
+
padding: 20px 30px;
|
269
|
+
border-bottom: 1px solid #dee2e6;
|
270
|
+
background: #f8f9fa;
|
271
|
+
}
|
272
|
+
|
273
|
+
.detail-title {
|
274
|
+
font-size: 1.8em;
|
275
|
+
font-weight: 600;
|
276
|
+
color: #495057;
|
277
|
+
margin-bottom: 10px;
|
278
|
+
}
|
279
|
+
|
280
|
+
.detail-meta {
|
281
|
+
display: flex;
|
282
|
+
gap: 15px;
|
283
|
+
flex-wrap: wrap;
|
284
|
+
}
|
285
|
+
|
286
|
+
.meta-item {
|
287
|
+
display: flex;
|
288
|
+
align-items: center;
|
289
|
+
gap: 5px;
|
290
|
+
font-size: 0.9em;
|
291
|
+
color: #6c757d;
|
292
|
+
}
|
293
|
+
|
294
|
+
.category-badge-large {
|
295
|
+
padding: 4px 12px;
|
296
|
+
border-radius: 12px;
|
297
|
+
font-size: 0.8em;
|
298
|
+
font-weight: 500;
|
299
|
+
text-transform: uppercase;
|
300
|
+
}
|
301
|
+
|
302
|
+
.category-builtin {
|
303
|
+
background: #d4edda;
|
304
|
+
color: #155724;
|
305
|
+
}
|
306
|
+
.category-plugin {
|
307
|
+
background: #cff4fc;
|
308
|
+
color: #055160;
|
309
|
+
}
|
310
|
+
.category-custom {
|
311
|
+
background: #fff3cd;
|
312
|
+
color: #856404;
|
313
|
+
}
|
314
|
+
.category-project_custom {
|
315
|
+
background: #cce5ff;
|
316
|
+
color: #004085;
|
317
|
+
}
|
318
|
+
.category-remote {
|
319
|
+
background: #f8d7da;
|
320
|
+
color: #721c24;
|
321
|
+
}
|
322
|
+
|
323
|
+
.detail-content {
|
324
|
+
flex: 1;
|
325
|
+
padding: 30px;
|
326
|
+
overflow-y: auto;
|
327
|
+
}
|
328
|
+
|
329
|
+
.detail-section {
|
330
|
+
margin-bottom: 30px;
|
331
|
+
}
|
332
|
+
|
333
|
+
.section-title {
|
334
|
+
font-size: 1.2em;
|
335
|
+
font-weight: 600;
|
336
|
+
color: #495057;
|
337
|
+
margin-bottom: 15px;
|
338
|
+
border-bottom: 2px solid #e9ecef;
|
339
|
+
padding-bottom: 5px;
|
340
|
+
}
|
341
|
+
|
342
|
+
.parameters-list {
|
343
|
+
display: grid;
|
344
|
+
gap: 15px;
|
345
|
+
}
|
346
|
+
|
347
|
+
.parameter-card {
|
348
|
+
background: #f8f9fa;
|
349
|
+
border: 1px solid #e9ecef;
|
350
|
+
border-radius: 8px;
|
351
|
+
padding: 15px;
|
352
|
+
}
|
353
|
+
|
354
|
+
.parameter-header {
|
355
|
+
display: flex;
|
356
|
+
justify-content: space-between;
|
357
|
+
align-items: flex-start;
|
358
|
+
margin-bottom: 8px;
|
359
|
+
}
|
360
|
+
|
361
|
+
.parameter-name {
|
362
|
+
font-weight: 600;
|
363
|
+
color: #495057;
|
364
|
+
font-size: 1em;
|
365
|
+
}
|
366
|
+
|
367
|
+
.parameter-mapping {
|
368
|
+
color: #6c757d;
|
369
|
+
font-style: italic;
|
370
|
+
font-size: 0.9em;
|
371
|
+
}
|
372
|
+
|
373
|
+
.parameter-default {
|
374
|
+
background: #fff3cd;
|
375
|
+
color: #856404;
|
376
|
+
padding: 2px 8px;
|
377
|
+
border-radius: 4px;
|
378
|
+
font-size: 0.8em;
|
379
|
+
font-weight: 500;
|
380
|
+
}
|
381
|
+
|
382
|
+
.parameter-description {
|
383
|
+
color: #6c757d;
|
384
|
+
line-height: 1.5;
|
385
|
+
}
|
386
|
+
|
387
|
+
.documentation {
|
388
|
+
background: #f8f9fa;
|
389
|
+
border-left: 4px solid #667eea;
|
390
|
+
padding: 20px;
|
391
|
+
border-radius: 0 8px 8px 0;
|
392
|
+
color: #495057;
|
393
|
+
line-height: 1.6;
|
394
|
+
}
|
395
|
+
|
396
|
+
.file-location {
|
397
|
+
background: #e9ecef;
|
398
|
+
padding: 10px 15px;
|
399
|
+
border-radius: 6px;
|
400
|
+
font-family: 'Courier New', monospace;
|
401
|
+
font-size: 0.9em;
|
402
|
+
color: #6c757d;
|
403
|
+
}
|
404
|
+
|
405
|
+
.empty-state {
|
406
|
+
display: flex;
|
407
|
+
flex-direction: column;
|
408
|
+
align-items: center;
|
409
|
+
justify-content: center;
|
410
|
+
height: 100%;
|
411
|
+
color: #6c757d;
|
412
|
+
text-align: center;
|
413
|
+
}
|
414
|
+
|
415
|
+
.empty-state-icon {
|
416
|
+
font-size: 4em;
|
417
|
+
margin-bottom: 20px;
|
418
|
+
opacity: 0.3;
|
419
|
+
}
|
420
|
+
|
421
|
+
.empty-state-title {
|
422
|
+
font-size: 1.2em;
|
423
|
+
margin-bottom: 10px;
|
424
|
+
}
|
425
|
+
|
426
|
+
.empty-state-text {
|
427
|
+
opacity: 0.7;
|
428
|
+
}
|
429
|
+
|
430
|
+
.no-results {
|
431
|
+
text-align: center;
|
432
|
+
padding: 40px 20px;
|
433
|
+
color: #6c757d;
|
434
|
+
}
|
435
|
+
|
436
|
+
/* 滚动条样式 */
|
437
|
+
.tree-container::-webkit-scrollbar,
|
438
|
+
.detail-content::-webkit-scrollbar {
|
439
|
+
width: 6px;
|
440
|
+
}
|
441
|
+
|
442
|
+
.tree-container::-webkit-scrollbar-track,
|
443
|
+
.detail-content::-webkit-scrollbar-track {
|
444
|
+
background: #f1f1f1;
|
445
|
+
}
|
446
|
+
|
447
|
+
.tree-container::-webkit-scrollbar-thumb,
|
448
|
+
.detail-content::-webkit-scrollbar-thumb {
|
449
|
+
background: #c1c1c1;
|
450
|
+
border-radius: 3px;
|
451
|
+
}
|
452
|
+
|
453
|
+
.tree-container::-webkit-scrollbar-thumb:hover,
|
454
|
+
.detail-content::-webkit-scrollbar-thumb:hover {
|
455
|
+
background: #a1a1a1;
|
456
|
+
}
|
457
|
+
|
458
|
+
/* 响应式设计 */
|
459
|
+
@media (max-width: 768px) {
|
460
|
+
.sidebar {
|
461
|
+
width: 100%;
|
462
|
+
position: absolute;
|
463
|
+
z-index: 1000;
|
464
|
+
transform: translateX(-100%);
|
465
|
+
transition: transform 0.3s ease;
|
466
|
+
}
|
467
|
+
|
468
|
+
.sidebar.mobile-open {
|
469
|
+
transform: translateX(0);
|
470
|
+
}
|
471
|
+
|
472
|
+
.detail-panel {
|
473
|
+
width: 100%;
|
474
|
+
}
|
475
|
+
}
|
476
|
+
</style>
|
477
|
+
</head>
|
478
|
+
<body>
|
479
|
+
<div class="container">
|
480
|
+
<!-- 左侧侧边栏 -->
|
481
|
+
<div class="sidebar">
|
482
|
+
<div class="sidebar-header">
|
483
|
+
<h1>pytest-dsl 关键字</h1>
|
484
|
+
<div class="summary">
|
485
|
+
总计 {{ summary.total_count }} 个关键字
|
486
|
+
{% for category, count in summary.category_counts.items() %}
|
487
|
+
| {{ category_names[category] }}: {{ count }}
|
488
|
+
{% endfor %}
|
489
|
+
</div>
|
490
|
+
</div>
|
491
|
+
|
492
|
+
<div class="search-section">
|
493
|
+
<input type="text" class="search-box" placeholder="搜索关键字..."
|
494
|
+
onkeyup="filterKeywords(this.value)">
|
495
|
+
</div>
|
496
|
+
|
497
|
+
<div class="filter-tabs">
|
498
|
+
<button class="filter-tab active" onclick="showCategory('all')">全部</button>
|
499
|
+
{% for category in ['builtin', 'plugin', 'custom', 'project_custom', 'remote'] %}
|
500
|
+
{% if categories.get(category) %}
|
501
|
+
<button class="filter-tab" onclick="showCategory('{{ category }}')">
|
502
|
+
{{ category_names[category] }}
|
503
|
+
</button>
|
504
|
+
{% endif %}
|
505
|
+
{% endfor %}
|
506
|
+
</div>
|
507
|
+
|
508
|
+
<div class="tree-container">
|
509
|
+
<!-- 全部关键字(按来源分组) -->
|
510
|
+
<div id="category-all" class="category-section active">
|
511
|
+
{% for source_name, keyword_list in source_groups.items() %}
|
512
|
+
<div class="tree-node">
|
513
|
+
<div class="category-header" onclick="toggleCategory(this)">
|
514
|
+
<span class="category-icon">▶</span>
|
515
|
+
{{ source_name }}
|
516
|
+
<span class="category-badge">{{ keyword_list|length }}</span>
|
517
|
+
</div>
|
518
|
+
<div class="category-keywords">
|
519
|
+
{% for keyword in keyword_list %}
|
520
|
+
<div class="keyword-item" onclick="showKeywordDetail('{{ keyword.name }}')">
|
521
|
+
<div class="keyword-name">{{ keyword.name }}</div>
|
522
|
+
<div class="keyword-preview">
|
523
|
+
{% if keyword.parameters %}
|
524
|
+
{{ keyword.parameters|length }} 个参数
|
525
|
+
{% else %}
|
526
|
+
无参数
|
527
|
+
{% endif %}
|
528
|
+
</div>
|
529
|
+
</div>
|
530
|
+
{% endfor %}
|
531
|
+
</div>
|
532
|
+
</div>
|
533
|
+
{% endfor %}
|
534
|
+
</div>
|
535
|
+
|
536
|
+
<!-- 各分类的关键字 -->
|
537
|
+
{% for category, keyword_list in categories.items() %}
|
538
|
+
<div id="category-{{ category }}" class="category-section" style="display: none;">
|
539
|
+
{% if category == 'plugin' %}
|
540
|
+
<!-- 插件关键字按插件名分组 -->
|
541
|
+
{% set plugin_groups = {} %}
|
542
|
+
{% for keyword in keyword_list %}
|
543
|
+
{% set plugin_name = keyword.source_info.name %}
|
544
|
+
{% if plugin_name not in plugin_groups %}
|
545
|
+
{% set _ = plugin_groups.update({plugin_name: []}) %}
|
546
|
+
{% endif %}
|
547
|
+
{% set _ = plugin_groups[plugin_name].append(keyword) %}
|
548
|
+
{% endfor %}
|
549
|
+
|
550
|
+
{% for plugin_name, keywords in plugin_groups.items() %}
|
551
|
+
<div class="tree-node">
|
552
|
+
<div class="category-header" onclick="toggleCategory(this)">
|
553
|
+
<span class="category-icon">▶</span>
|
554
|
+
插件: {{ plugin_name }}
|
555
|
+
<span class="category-badge">{{ keywords|length }}</span>
|
556
|
+
</div>
|
557
|
+
<div class="category-keywords">
|
558
|
+
{% for keyword in keywords %}
|
559
|
+
<div class="keyword-item" onclick="showKeywordDetail('{{ keyword.name }}')">
|
560
|
+
<div class="keyword-name">{{ keyword.name }}</div>
|
561
|
+
<div class="keyword-preview">
|
562
|
+
{% if keyword.parameters %}
|
563
|
+
{{ keyword.parameters|length }} 个参数
|
564
|
+
{% else %}
|
565
|
+
无参数
|
566
|
+
{% endif %}
|
567
|
+
</div>
|
568
|
+
</div>
|
569
|
+
{% endfor %}
|
570
|
+
</div>
|
571
|
+
</div>
|
572
|
+
{% endfor %}
|
573
|
+
{% elif category == 'project_custom' %}
|
574
|
+
<!-- 项目自定义关键字按位置分组 -->
|
575
|
+
{% set project_location_groups = {} %}
|
576
|
+
{% for keyword in keyword_list %}
|
577
|
+
{% set location = keyword.file_location or '未知位置' %}
|
578
|
+
{% if location not in project_location_groups %}
|
579
|
+
{% set _ = project_location_groups.update({location: []}) %}
|
580
|
+
{% endif %}
|
581
|
+
{% set _ = project_location_groups[location].append(keyword) %}
|
582
|
+
{% endfor %}
|
583
|
+
|
584
|
+
{% for location, keywords in project_location_groups.items() %}
|
585
|
+
<div class="tree-node">
|
586
|
+
<div class="category-header" onclick="toggleCategory(this)">
|
587
|
+
<span class="category-icon">▶</span>
|
588
|
+
{{ location }}
|
589
|
+
<span class="category-badge">{{ keywords|length }}</span>
|
590
|
+
</div>
|
591
|
+
<div class="category-keywords">
|
592
|
+
{% for keyword in keywords %}
|
593
|
+
<div class="keyword-item" onclick="showKeywordDetail('{{ keyword.name }}')">
|
594
|
+
<div class="keyword-name">{{ keyword.name }}</div>
|
595
|
+
<div class="keyword-preview">
|
596
|
+
{% if keyword.parameters %}
|
597
|
+
{{ keyword.parameters|length }} 个参数
|
598
|
+
{% else %}
|
599
|
+
无参数
|
600
|
+
{% endif %}
|
601
|
+
</div>
|
602
|
+
</div>
|
603
|
+
{% endfor %}
|
604
|
+
</div>
|
605
|
+
</div>
|
606
|
+
{% endfor %}
|
607
|
+
{% else %}
|
608
|
+
<!-- 其他分类直接显示 -->
|
609
|
+
<div class="tree-node">
|
610
|
+
<div class="category-keywords expanded">
|
611
|
+
{% for keyword in keyword_list %}
|
612
|
+
<div class="keyword-item" onclick="showKeywordDetail('{{ keyword.name }}')">
|
613
|
+
<div class="keyword-name">{{ keyword.name }}</div>
|
614
|
+
<div class="keyword-preview">
|
615
|
+
{% if keyword.parameters %}
|
616
|
+
{{ keyword.parameters|length }} 个参数
|
617
|
+
{% else %}
|
618
|
+
无参数
|
619
|
+
{% endif %}
|
620
|
+
</div>
|
621
|
+
</div>
|
622
|
+
{% endfor %}
|
623
|
+
</div>
|
624
|
+
</div>
|
625
|
+
{% endif %}
|
626
|
+
</div>
|
627
|
+
{% endfor %}
|
628
|
+
</div>
|
629
|
+
</div>
|
630
|
+
|
631
|
+
<!-- 右侧详情面板 -->
|
632
|
+
<div class="detail-panel">
|
633
|
+
<div id="keyword-detail-content">
|
634
|
+
<div class="empty-state">
|
635
|
+
<div class="empty-state-icon">📋</div>
|
636
|
+
<div class="empty-state-title">选择一个关键字</div>
|
637
|
+
<div class="empty-state-text">点击左侧列表中的关键字查看详细信息</div>
|
638
|
+
</div>
|
639
|
+
</div>
|
640
|
+
</div>
|
641
|
+
</div>
|
642
|
+
|
643
|
+
<script>
|
644
|
+
// 关键字数据
|
645
|
+
const keywordsData = {
|
646
|
+
{% for keyword in keywords %}
|
647
|
+
'{{ keyword.name }}': {{ keyword|tojson }},
|
648
|
+
{% endfor %}
|
649
|
+
};
|
650
|
+
|
651
|
+
let currentCategory = 'all';
|
652
|
+
let searchTerm = '';
|
653
|
+
|
654
|
+
function showCategory(category) {
|
655
|
+
currentCategory = category;
|
656
|
+
|
657
|
+
// 隐藏所有分类
|
658
|
+
const sections = document.querySelectorAll('.category-section');
|
659
|
+
sections.forEach(section => section.style.display = 'none');
|
660
|
+
|
661
|
+
// 显示选中的分类
|
662
|
+
const targetSection = document.getElementById('category-' + category);
|
663
|
+
if (targetSection) {
|
664
|
+
targetSection.style.display = 'block';
|
665
|
+
}
|
666
|
+
|
667
|
+
// 更新标签状态
|
668
|
+
const tabs = document.querySelectorAll('.filter-tab');
|
669
|
+
tabs.forEach(tab => tab.classList.remove('active'));
|
670
|
+
event.target.classList.add('active');
|
671
|
+
|
672
|
+
// 清空搜索
|
673
|
+
document.querySelector('.search-box').value = '';
|
674
|
+
searchTerm = '';
|
675
|
+
|
676
|
+
// 清空详情
|
677
|
+
showEmptyState();
|
678
|
+
}
|
679
|
+
|
680
|
+
function toggleCategory(element) {
|
681
|
+
const icon = element.querySelector('.category-icon');
|
682
|
+
const keywords = element.nextElementSibling;
|
683
|
+
|
684
|
+
if (keywords.classList.contains('expanded')) {
|
685
|
+
keywords.classList.remove('expanded');
|
686
|
+
icon.textContent = '▶';
|
687
|
+
} else {
|
688
|
+
keywords.classList.add('expanded');
|
689
|
+
icon.textContent = '▼';
|
690
|
+
}
|
691
|
+
}
|
692
|
+
|
693
|
+
function showKeywordDetail(keywordName) {
|
694
|
+
const keyword = keywordsData[keywordName];
|
695
|
+
if (!keyword) return;
|
696
|
+
|
697
|
+
// 更新选中状态
|
698
|
+
document.querySelectorAll('.keyword-item').forEach(item => {
|
699
|
+
item.classList.remove('selected');
|
700
|
+
});
|
701
|
+
event.currentTarget.classList.add('selected');
|
702
|
+
|
703
|
+
// 构建详情HTML
|
704
|
+
const categoryNames = {
|
705
|
+
'builtin': '内置',
|
706
|
+
'plugin': '插件',
|
707
|
+
'custom': '自定义',
|
708
|
+
'project_custom': '项目自定义',
|
709
|
+
'remote': '远程'
|
710
|
+
};
|
711
|
+
|
712
|
+
let parametersHtml = '';
|
713
|
+
if (keyword.parameters && keyword.parameters.length > 0) {
|
714
|
+
parametersHtml = `
|
715
|
+
<div class="detail-section">
|
716
|
+
<div class="section-title">参数</div>
|
717
|
+
<div class="parameters-list">
|
718
|
+
${keyword.parameters.map(param => `
|
719
|
+
<div class="parameter-card">
|
720
|
+
<div class="parameter-header">
|
721
|
+
<div>
|
722
|
+
<div class="parameter-name">${param.name}</div>
|
723
|
+
${param.mapping && param.mapping !== param.name ?
|
724
|
+
`<div class="parameter-mapping">(${param.mapping})</div>` : ''}
|
725
|
+
</div>
|
726
|
+
${param.default !== undefined ?
|
727
|
+
`<div class="parameter-default">默认: ${param.default}</div>` : ''}
|
728
|
+
</div>
|
729
|
+
<div class="parameter-description">${param.description || ''}</div>
|
730
|
+
</div>
|
731
|
+
`).join('')}
|
732
|
+
</div>
|
733
|
+
</div>
|
734
|
+
`;
|
735
|
+
} else {
|
736
|
+
parametersHtml = `
|
737
|
+
<div class="detail-section">
|
738
|
+
<div class="section-title">参数</div>
|
739
|
+
<p style="color: #6c757d; font-style: italic;">此关键字无需参数</p>
|
740
|
+
</div>
|
741
|
+
`;
|
742
|
+
}
|
743
|
+
|
744
|
+
let documentationHtml = '';
|
745
|
+
if (keyword.documentation) {
|
746
|
+
documentationHtml = `
|
747
|
+
<div class="detail-section">
|
748
|
+
<div class="section-title">说明</div>
|
749
|
+
<div class="documentation">${keyword.documentation}</div>
|
750
|
+
</div>
|
751
|
+
`;
|
752
|
+
}
|
753
|
+
|
754
|
+
let sourceInfoHtml = '';
|
755
|
+
if (keyword.source_info) {
|
756
|
+
let locationText = '';
|
757
|
+
if (keyword.file_location) {
|
758
|
+
locationText = keyword.file_location;
|
759
|
+
} else if (keyword.source_info.module) {
|
760
|
+
locationText = keyword.source_info.module;
|
761
|
+
} else {
|
762
|
+
locationText = keyword.source_info.name;
|
763
|
+
}
|
764
|
+
|
765
|
+
sourceInfoHtml = `
|
766
|
+
<div class="detail-section">
|
767
|
+
<div class="section-title">来源信息</div>
|
768
|
+
<div class="file-location">📍 ${locationText}</div>
|
769
|
+
</div>
|
770
|
+
`;
|
771
|
+
}
|
772
|
+
|
773
|
+
const detailHtml = `
|
774
|
+
<div class="detail-header">
|
775
|
+
<div class="detail-title">${keyword.name}</div>
|
776
|
+
<div class="detail-meta">
|
777
|
+
<div class="meta-item">
|
778
|
+
<span class="category-badge-large category-${keyword.category}">
|
779
|
+
${categoryNames[keyword.category]}
|
780
|
+
</span>
|
781
|
+
</div>
|
782
|
+
${keyword.source_info && keyword.source_info.name !== categoryNames[keyword.category] ?
|
783
|
+
`<div class="meta-item">🔌 ${keyword.source_info.name}</div>` : ''}
|
784
|
+
${keyword.parameters ?
|
785
|
+
`<div class="meta-item">📝 ${keyword.parameters.length} 个参数</div>` :
|
786
|
+
'<div class="meta-item">📝 无参数</div>'}
|
787
|
+
</div>
|
788
|
+
</div>
|
789
|
+
<div class="detail-content">
|
790
|
+
${parametersHtml}
|
791
|
+
${documentationHtml}
|
792
|
+
${sourceInfoHtml}
|
793
|
+
</div>
|
794
|
+
`;
|
795
|
+
|
796
|
+
document.getElementById('keyword-detail-content').innerHTML = detailHtml;
|
797
|
+
}
|
798
|
+
|
799
|
+
function showEmptyState() {
|
800
|
+
const emptyStateHtml = `
|
801
|
+
<div class="empty-state">
|
802
|
+
<div class="empty-state-icon">📋</div>
|
803
|
+
<div class="empty-state-title">选择一个关键字</div>
|
804
|
+
<div class="empty-state-text">点击左侧列表中的关键字查看详细信息</div>
|
805
|
+
</div>
|
806
|
+
`;
|
807
|
+
document.getElementById('keyword-detail-content').innerHTML = emptyStateHtml;
|
808
|
+
}
|
809
|
+
|
810
|
+
function filterKeywords(term) {
|
811
|
+
searchTerm = term.toLowerCase();
|
812
|
+
const activeSection = document.querySelector('.category-section[style="display: block;"], .category-section.active');
|
813
|
+
const keywordItems = activeSection.querySelectorAll('.keyword-item');
|
814
|
+
let visibleCount = 0;
|
815
|
+
|
816
|
+
keywordItems.forEach(item => {
|
817
|
+
const name = item.querySelector('.keyword-name').textContent.toLowerCase();
|
818
|
+
const isVisible = name.includes(searchTerm);
|
819
|
+
item.style.display = isVisible ? 'block' : 'none';
|
820
|
+
if (isVisible) visibleCount++;
|
821
|
+
});
|
822
|
+
|
823
|
+
// 处理分类的显示/隐藏
|
824
|
+
const categoryNodes = activeSection.querySelectorAll('.tree-node');
|
825
|
+
categoryNodes.forEach(node => {
|
826
|
+
const visibleKeywords = node.querySelectorAll('.keyword-item[style="display: block;"], .keyword-item:not([style])');
|
827
|
+
const hasVisibleKeywords = Array.from(visibleKeywords).some(item =>
|
828
|
+
item.style.display !== 'none'
|
829
|
+
);
|
830
|
+
node.style.display = hasVisibleKeywords ? 'block' : 'none';
|
831
|
+
});
|
832
|
+
|
833
|
+
// 显示无结果提示
|
834
|
+
let noResults = activeSection.querySelector('.no-results');
|
835
|
+
if (visibleCount === 0 && searchTerm.trim() !== '') {
|
836
|
+
if (!noResults) {
|
837
|
+
noResults = document.createElement('div');
|
838
|
+
noResults.className = 'no-results';
|
839
|
+
noResults.innerHTML = '<div>🔍</div><div>未找到匹配的关键字</div>';
|
840
|
+
activeSection.appendChild(noResults);
|
841
|
+
}
|
842
|
+
noResults.style.display = 'block';
|
843
|
+
} else if (noResults) {
|
844
|
+
noResults.style.display = 'none';
|
845
|
+
}
|
846
|
+
|
847
|
+
// 如果有搜索结果,清空详情面板
|
848
|
+
if (searchTerm.trim() !== '') {
|
849
|
+
showEmptyState();
|
850
|
+
}
|
851
|
+
}
|
852
|
+
|
853
|
+
// 初始化:展开第一个分类
|
854
|
+
document.addEventListener('DOMContentLoaded', function() {
|
855
|
+
const firstCategoryHeader = document.querySelector('.category-header');
|
856
|
+
if (firstCategoryHeader) {
|
857
|
+
toggleCategory(firstCategoryHeader);
|
858
|
+
}
|
859
|
+
});
|
860
|
+
</script>
|
861
|
+
</body>
|
862
|
+
</html>
|