Files
bio_frontend/layouts/default.vue

195 lines
4.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import AppHeader from "../components/layout/AppHeader.vue";
import { ref, computed, watch, onMounted } from "vue";
import { useRouter } from "vue-router";
import { useTabsStore } from "../stores/tab";
import { usePermissionsStore } from "~/stores/permissions";
const router = useRouter();
const activeMenu = ref("HOME");
const showSubmenuBar = ref(false);
const tabsStore = useTabsStore();
const permissionStore = usePermissionsStore();
// 권한 초기화
onMounted(async () => {
await permissionStore.fetchPermissions();
});
// 메뉴 클릭 시 홈 이동
watch(activeMenu, newValue => {
if (newValue === "HOME") router.push("/");
});
// 권한 기반 서브메뉴 생성
const subMenus = computed(() => {
if (activeMenu.value === "HOME") return [];
// 활성 메뉴의 코드 찾기 (PG01, PG02 등)
const activeMenuCode = activeMenu.value;
// 해당 페이지그룹의 하위 페이지들 필터링 (menu_yn이 "Y"인 것만)
return permissionStore.permissions.resources.pages
.filter(page => page.parentCode === activeMenuCode)
.filter(page => page.menuYn === "Y") // 메뉴에 표시할 페이지만
.filter(page => permissionStore.hasPagePermission(page.path || ""))
.sort((a, b) => a.sortOrder - b.sortOrder)
.map(page => ({
key: page.code,
label: page.name,
to: page.path || "",
componentName: page.name,
}));
});
function onMenuClick(menu: string) {
showSubmenuBar.value = menu !== "home";
}
// ✅ 서브메뉴 클릭 → 현재 활성 탭 내용만 변경
function onSubMenuClick(sub: {
key: string;
label: string;
to: string;
componentName: string;
}) {
tabsStore.updateActiveTab(sub);
// const activeKey = tabsStore.activeTab;
// router.push(`/${activeKey}${sub.to}`);
}
// ✅ 새 탭 추가 버튼
function addNewTab() {
tabsStore.addTab();
// router.push(`/${key}/`);
}
</script>
<template>
<div class="layout">
<AppHeader v-model="activeMenu" @update:model-value="onMenuClick" />
<!-- 서브메뉴 -->
<nav
v-if="showSubmenuBar && subMenus.length > 0"
class="w-full bg-gray-100 shadow-sm px-4 py-2"
>
<div class="flex items-center space-x-6">
<span class="text-sm font-medium text-gray-600 mr-4">
{{ activeMenu }}
</span>
<div class="flex space-x-4">
<button
v-for="sub in subMenus"
:key="sub.key"
class="submenu-btn"
@click="onSubMenuClick(sub)"
>
{{ sub.label }}
</button>
</div>
</div>
</nav>
<br /><br />
<!-- -->
<div class="tab-bar">
<div
v-for="tab in tabsStore.tabs"
:key="tab.key"
class="tab-item"
:class="{ active: tabsStore.activeTab === tab.key }"
@click="tabsStore.setActiveTab(tab.key)"
>
{{ tab.label }}
<span
v-show="tabsStore.activeTab !== tab.key"
class="close-btn"
@click.stop="tabsStore.removeTab(tab.key)"
>
×
</span>
</div>
<!-- 추가 버튼 -->
<button class="add-tab-btn" @click="addNewTab"></button>
</div>
<main class="main">
<slot />
</main>
</div>
</template>
<style scoped>
.layout {
min-height: 100vh;
display: flex;
flex-direction: column;
position: relative;
}
.main {
flex: 1;
padding: 2rem;
padding-top: 0.5rem;
}
.footer {
background: #f8f9fa;
padding: 1rem;
text-align: center;
border-top: 1px solid #e9ecef;
}
.submenu-bar {
background: #f4f6fa;
border-bottom: 1px solid #e0e7ef;
padding: 0.5rem 2rem;
display: flex;
gap: 1rem;
position: absolute;
top: 80px;
left: 0;
right: 0;
z-index: 10;
}
.submenu-btn {
padding: 0.25rem 0.75rem;
font-size: 0.875rem;
color: #374151;
background: none;
border: none;
border-radius: 0.25rem;
cursor: pointer;
transition: all 0.15s ease;
}
.submenu-btn:hover {
color: #2563eb;
background-color: #eff6ff;
}
/* 탭바 스타일 */
.tab-bar {
display: flex;
gap: 6px;
padding: 0.4rem 0.8rem;
background: #fff;
border-bottom: 1px solid #ddd;
}
.tab-item {
padding: 0.3rem 0.8rem;
background: #f2f2f2;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
}
.tab-item.active {
background: #1976d2;
color: white;
}
.close-btn {
margin-left: 6px;
cursor: pointer;
}
</style>