185 lines
6.3 KiB
Vue
185 lines
6.3 KiB
Vue
<template>
|
|
<div class="flex h-screen">
|
|
<!-- Sidebar -->
|
|
<aside class="w-64 flex flex-col h-full border-r" style="border-color: var(--p-dialog-border-color)">
|
|
<!-- Logo -->
|
|
<div class="p-2 border-b" style="border-color: var(--p-dialog-border-color)">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 bg-gradient-to-br from-primary-500 to-primary-600 rounded-xl flex items-center justify-center shadow-lg">
|
|
<i class="pi pi-database text-xl text-white"></i>
|
|
</div>
|
|
<div>
|
|
<h1 class="text-lg font-bold text-white">Репликация БД</h1>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- PanelMenu -->
|
|
<PanelMenu
|
|
:model="menuItems"
|
|
v-model:expanded-keys="expandedKeys"
|
|
class="w-full border-none bg-transparent flex-1 overflow-y-auto">
|
|
<template #item="{item, active, hasSubmenu}">
|
|
<Link v-if="item.url" class="flex items-center cursor-pointer px-4 py-2" :href="item.url">
|
|
<span :class="item.icon" style="font-size: 14px;" />
|
|
<span class="ml-2 text-sm">{{ item.label }}</span>
|
|
<span v-if="hasSubmenu" class="pi pi-angle-down ml-auto" style="font-size: 14px;" />
|
|
</Link>
|
|
<div v-else class="text-sm flex items-center cursor-pointer px-4 py-2">
|
|
<span :class="item.icon" style="font-size: 14px;" />
|
|
<span :class="{'ml-2': item.icon}">{{ item.label }}</span>
|
|
<span v-if="hasSubmenu" class="pi pi-angle-down ml-auto" style="font-size: 14px;" />
|
|
</div>
|
|
</template>
|
|
</PanelMenu>
|
|
|
|
<!-- User Section -->
|
|
<div class="p-4 border-t" style="border-color: var(--p-dialog-border-color)">
|
|
<div class="flex items-center gap-3 p-2 rounded-lg">
|
|
<Avatar label="A" shape="circle" size="normal" />
|
|
<div class="flex-1">
|
|
<p class="text-sm font-medium">Администратор</p>
|
|
<p class="text-xs">admin@db.local</p>
|
|
</div>
|
|
<Button icon="pi pi-sign-out" text rounded severity="secondary" size="small" />
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- Main Content -->
|
|
<main class="flex-1 overflow-y-auto">
|
|
<!-- Top Toolbar -->
|
|
<Toolbar class="border-t-0! border-l-0! border-r-0! border-b! rounded-none! px-6!" style="border-color: var(--p-dialog-border-color)">
|
|
<template #start>
|
|
<slot name="header" />
|
|
</template>
|
|
<template #end>
|
|
<div class="flex items-center gap-2">
|
|
<Button
|
|
:icon="isDark ? 'pi pi-sun' : 'pi pi-moon'"
|
|
@click="toggleTheme"
|
|
severity="secondary"
|
|
text
|
|
rounded
|
|
size="small"
|
|
/>
|
|
<Button icon="pi pi-bell" severity="secondary" text rounded size="small" />
|
|
</div>
|
|
</template>
|
|
</Toolbar>
|
|
|
|
<!-- Page Content -->
|
|
<div class="p-6">
|
|
<slot />
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { inject, ref } from 'vue';
|
|
import { usePage, Link } from '@inertiajs/vue3';
|
|
import PanelMenu from 'primevue/panelmenu';
|
|
import Toolbar from 'primevue/toolbar';
|
|
import Button from 'primevue/button';
|
|
import Avatar from 'primevue/avatar';
|
|
|
|
const page = usePage();
|
|
const { isDark, toggle } = inject('theme');
|
|
|
|
const expandedKeys = ref({'0': true});
|
|
|
|
const menuItems = [
|
|
{
|
|
key: '0',
|
|
label: 'Панель управления',
|
|
items: [
|
|
{
|
|
key: '0_1',
|
|
label: 'Главная',
|
|
icon: 'pi pi-home',
|
|
url: '/',
|
|
},
|
|
{
|
|
key: '0_2',
|
|
label: 'Базы данных',
|
|
icon: 'pi pi-database',
|
|
items: [
|
|
{
|
|
key: '0_2_1',
|
|
label: 'Исходные БД',
|
|
icon: 'pi pi-server',
|
|
url: '/databases/source',
|
|
},
|
|
{
|
|
key: '0_2_2',
|
|
label: 'Целевые БД',
|
|
icon: 'pi pi-cloud-download',
|
|
url: '/databases/target',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
key: '0_3',
|
|
label: 'Схемы',
|
|
icon: 'pi pi-table',
|
|
url: '/schemas',
|
|
},
|
|
{
|
|
key: '0_4',
|
|
label: 'Расписания миграций',
|
|
icon: 'pi pi-calendar',
|
|
url: '/migrations',
|
|
},
|
|
]
|
|
},
|
|
];
|
|
|
|
// Определяем активный пункт меню
|
|
const getActiveItem = () => {
|
|
const url = page.url;
|
|
if (url === '/') return 'Главная';
|
|
if (url.startsWith('/databases/source')) return 'Исходные БД';
|
|
if (url.startsWith('/databases/target')) return 'Целевые БД';
|
|
if (url.startsWith('/schemas')) return 'Схемы';
|
|
if (url.startsWith('/migrations')) return 'Расписания миграций';
|
|
return null;
|
|
};
|
|
|
|
const toggleTheme = () => {
|
|
toggle();
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Кастомизация PanelMenu */
|
|
:deep(.p-panelmenu) {
|
|
padding: 0;
|
|
}
|
|
|
|
:deep(.p-panelmenu-panel) {
|
|
border: none;
|
|
background: transparent;
|
|
}
|
|
|
|
:deep(.p-panelmenu-root-list) {
|
|
padding: 0 !important;
|
|
}
|
|
|
|
:deep(.p-panelmenu-item-link) {
|
|
padding: 0.625rem 0.75rem !important;
|
|
}
|
|
|
|
:deep(.p-panelmenu-item-icon) {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
:deep(.p-panelmenu-item.p-highlight) {
|
|
background-color: rgba(99, 102, 241, 0.1) !important;
|
|
}
|
|
|
|
:deep(.p-panelmenu-item.p-highlight .p-panelmenu-item-label) {
|
|
color: rgb(99, 102, 241) !important;
|
|
}
|
|
</style>
|