Initial commit
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-950 text-gray-100 flex">
|
||||
<!-- Sidebar -->
|
||||
<aside class="w-60 shrink-0 bg-gray-900 border-r border-gray-800 flex flex-col">
|
||||
<!-- Logo -->
|
||||
<div class="px-5 py-5 border-b border-gray-800">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<div class="w-8 h-8 rounded-lg bg-blue-600 flex items-center justify-center shrink-0">
|
||||
<UIcon name="i-heroicons-shield-exclamation" class="text-white w-5 h-5" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-white leading-tight">DIP-IDS</p>
|
||||
<p class="text-[10px] text-gray-400 leading-tight">Network IDS</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="flex-1 px-3 py-4 space-y-0.5">
|
||||
<NuxtLink
|
||||
v-for="item in navigation"
|
||||
:key="item.to"
|
||||
:to="item.to"
|
||||
class="flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors"
|
||||
:class="isActive(item.to)
|
||||
? 'bg-blue-600/20 text-blue-400 font-medium'
|
||||
: 'text-gray-400 hover:text-gray-200 hover:bg-gray-800'"
|
||||
>
|
||||
<UIcon :name="item.icon" class="w-4.5 h-4.5 shrink-0" />
|
||||
{{ item.label }}
|
||||
</NuxtLink>
|
||||
</nav>
|
||||
|
||||
<!-- Status indicator -->
|
||||
<div class="px-4 py-4 border-t border-gray-800">
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
class="w-2 h-2 rounded-full shrink-0"
|
||||
:class="captureRunning ? 'bg-green-400 animate-pulse' : 'bg-gray-600'"
|
||||
/>
|
||||
<span class="text-xs text-gray-400">
|
||||
{{ captureRunning ? 'Захват активен' : 'Захват остановлен' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main content -->
|
||||
<div class="flex-1 flex flex-col min-w-0">
|
||||
<!-- Top bar -->
|
||||
<header class="h-14 border-b border-gray-800 bg-gray-900/50 flex items-center px-6 gap-4 shrink-0">
|
||||
<h1 class="text-sm font-semibold text-gray-200 flex-1">{{ pageTitle }}</h1>
|
||||
<UButton
|
||||
v-if="captureRunning"
|
||||
color="red"
|
||||
variant="soft"
|
||||
size="xs"
|
||||
icon="i-heroicons-stop-circle"
|
||||
@click="stopCapture"
|
||||
>
|
||||
Остановить захват
|
||||
</UButton>
|
||||
<UButton
|
||||
color="blue"
|
||||
variant="soft"
|
||||
size="xs"
|
||||
icon="i-heroicons-document-arrow-down"
|
||||
@click="downloadReport"
|
||||
>
|
||||
PDF отчёт
|
||||
</UButton>
|
||||
</header>
|
||||
|
||||
<!-- Page content -->
|
||||
<main class="flex-1 overflow-auto p-6">
|
||||
<slot />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
const { status, stopCapture } = useCaptureStatus()
|
||||
|
||||
const captureRunning = computed(() => status.value?.running ?? false)
|
||||
|
||||
const navigation = [
|
||||
{ to: '/', label: 'Дашборд', icon: 'i-heroicons-chart-bar-square' },
|
||||
{ to: '/alerts', label: 'Алерты', icon: 'i-heroicons-bell-alert' },
|
||||
{ to: '/capture', label: 'Захват', icon: 'i-heroicons-radio' },
|
||||
{ to: '/stats', label: 'Статистика', icon: 'i-heroicons-chart-pie' },
|
||||
]
|
||||
|
||||
const pageTitles: Record<string, string> = {
|
||||
'/': 'Обзор',
|
||||
'/alerts': 'Журнал алертов',
|
||||
'/capture': 'Управление захватом',
|
||||
'/stats': 'Статистика трафика',
|
||||
}
|
||||
|
||||
const pageTitle = computed(() => pageTitles[route.path] ?? 'DIP-IDS')
|
||||
|
||||
function isActive(to: string) {
|
||||
return to === '/' ? route.path === '/' : route.path.startsWith(to)
|
||||
}
|
||||
|
||||
function downloadReport() {
|
||||
const a = document.createElement('a')
|
||||
a.href = '/api/reports/generate'
|
||||
a.download = `nids-report-${new Date().toISOString().slice(0, 10)}.pdf`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user