|
1 | 1 | <script> |
2 | 2 | import { onMount } from 'svelte'; |
3 | | - import { loadUserdataTemplates, getTemplatesByCategory, getAIScenarios, getVulhubScenarios } from '../../lib/userdataTemplates.js'; |
| 3 | + import { loadUserdataTemplates, getTemplatesByCategory, getAIScenarios, getVulhubScenarios, getC2Scenarios } from '../../lib/userdataTemplates.js'; |
4 | 4 | |
5 | 5 | let { t, onTabChange } = $props(); |
6 | 6 | let specialModuleTab = $state('vulhub'); |
7 | 7 | let selectedAIScenario = $state(null); |
8 | 8 | let selectedVulhubScenario = $state(null); |
| 9 | + let selectedC2Scenario = $state(null); |
9 | 10 | let copied = $state(false); |
10 | 11 | let aiSearchQuery = $state(''); |
11 | 12 | let vulhubSearchQuery = $state(''); |
| 13 | + let c2SearchQuery = $state(''); |
12 | 14 | let templates = $state([]); |
13 | 15 | let templatesLoading = $state(true); |
14 | 16 | |
|
42 | 44 | return scenarios; |
43 | 45 | }); |
44 | 46 | |
| 47 | + let c2Scenarios = $derived(() => { |
| 48 | + let scenarios = getC2Scenarios(templates); |
| 49 | + if (c2SearchQuery) { |
| 50 | + const query = c2SearchQuery.toLowerCase(); |
| 51 | + scenarios = scenarios.filter(s => |
| 52 | + (s.nameZh || s.name).toLowerCase().includes(query) || |
| 53 | + s.name.toLowerCase().includes(query) |
| 54 | + ); |
| 55 | + } |
| 56 | + return scenarios; |
| 57 | + }); |
| 58 | + |
45 | 59 | function selectAIScenario(scenario) { |
46 | 60 | selectedAIScenario = scenario; |
47 | 61 | } |
|
50 | 64 | selectedVulhubScenario = scenario; |
51 | 65 | } |
52 | 66 | |
| 67 | + function selectC2Scenario(scenario) { |
| 68 | + selectedC2Scenario = scenario; |
| 69 | + } |
| 70 | + |
53 | 71 | async function copyToClipboard() { |
54 | 72 | if (!selectedVulhubScenario) return; |
55 | 73 | try { |
|
197 | 215 | </div> |
198 | 216 | {:else if specialModuleTab === 'c2'} |
199 | 217 | <div class="bg-white rounded-xl border border-gray-100 p-4 sm:p-6 md:p-8"> |
200 | | - {#if templates.length === 0} |
| 218 | + {#if templatesLoading} |
| 219 | + <div class="flex items-center justify-center h-32"> |
| 220 | + <div class="w-6 h-6 border-2 border-gray-100 border-t-orange-500 rounded-full animate-spin"></div> |
| 221 | + </div> |
| 222 | + {:else if templates.length === 0} |
201 | 223 | <div class="bg-blue-50 border border-blue-100 rounded-xl p-5 mb-4"> |
202 | 224 | <div class="flex items-start gap-3"> |
203 | 225 | <svg class="w-5 h-5 text-blue-500 flex-shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> |
|
215 | 237 | </div> |
216 | 238 | </div> |
217 | 239 | {:else} |
218 | | - <div class="text-center py-8"> |
219 | | - <p class="text-[13px] text-gray-500">暂无可用的 C2 场景</p> |
220 | | - <p class="text-[12px] text-gray-400 mt-1">此模块功能开发中...</p> |
221 | | - </div> |
222 | | - <div class="bg-blue-50 rounded-lg p-4 sm:p-6 text-center"> |
223 | | - <p class="text-[13px] text-blue-800 mb-2">C2 场景管理模块</p> |
224 | | - <p class="text-[12px] text-blue-600">此功能正在开发中,敬请期待</p> |
| 240 | + <div class="mb-4"> |
| 241 | + <input |
| 242 | + type="text" |
| 243 | + placeholder={t.searchPlaceholder || '搜索...'} |
| 244 | + class="w-full h-10 px-3 text-[13px] bg-gray-50 border-0 rounded-lg text-gray-900 placeholder-gray-400 focus:ring-2 focus:ring-gray-900 focus:ring-offset-1 transition-shadow" |
| 245 | + bind:value={c2SearchQuery} |
| 246 | + /> |
| 247 | + {#if c2Scenarios().length > 0} |
| 248 | + <div class="flex flex-wrap gap-2 mt-3"> |
| 249 | + {#each c2Scenarios() as scenario} |
| 250 | + <button |
| 251 | + class="px-3 py-2 text-[12px] font-medium rounded-lg transition-all {selectedC2Scenario?.name === scenario.name ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}" |
| 252 | + onclick={() => selectC2Scenario(scenario)} |
| 253 | + > |
| 254 | + {scenario.nameZh || scenario.name} |
| 255 | + </button> |
| 256 | + {/each} |
| 257 | + </div> |
| 258 | + {:else} |
| 259 | + <p class="text-[12px] text-gray-500 mt-3 text-center">未找到匹配的 C2 场景</p> |
| 260 | + {/if} |
225 | 261 | </div> |
| 262 | + |
| 263 | + {#if selectedC2Scenario} |
| 264 | + <div class="border-t border-gray-100 pt-4"> |
| 265 | + {#if selectedC2Scenario.description} |
| 266 | + <p class="text-[13px] text-gray-700 mb-3">{selectedC2Scenario.description}</p> |
| 267 | + {/if} |
| 268 | + {#if selectedC2Scenario.script} |
| 269 | + <pre class="bg-gray-900 text-gray-100 text-[12px] p-4 rounded-lg overflow-x-auto max-h-96 overflow-y-auto font-mono">{selectedC2Scenario.script}</pre> |
| 270 | + {/if} |
| 271 | + </div> |
| 272 | + {:else} |
| 273 | + <div class="bg-blue-50 rounded-lg p-4 sm:p-6 text-center"> |
| 274 | + <p class="text-[13px] text-blue-800 mb-2">选择一个 C2 场景查看部署脚本</p> |
| 275 | + <p class="text-[12px] text-blue-600">脚本将在云服务器上自动安装 C2 工具</p> |
| 276 | + </div> |
| 277 | + {/if} |
226 | 278 | {/if} |
227 | 279 | </div> |
228 | 280 | {:else if specialModuleTab === 'ai'} |
|
0 commit comments