94 lines
2.2 KiB
Vue
94 lines
2.2 KiB
Vue
<template>
|
|
<div class="layout">
|
|
<AppHeader v-model="activeMenu" @update:model-value="handleMenuChange" />
|
|
|
|
<!-- 서브메뉴 바 -->
|
|
<SubMenuBar
|
|
:show-submenu-bar="showSubmenuBar"
|
|
:active-menu="activeMenu"
|
|
:sub-menus="subMenus"
|
|
@submenu-click="onSubMenuClick"
|
|
/>
|
|
<!-- 탭 바 -->
|
|
<TabBar />
|
|
|
|
<main class="main">
|
|
<slot />
|
|
</main>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import AppHeader from "../components/layout/AppHeader.vue";
|
|
import SubMenuBar from "../components/layout/SubMenuBar.vue";
|
|
import TabBar from "../components/layout/TabBar.vue";
|
|
import { ref, computed } from "vue";
|
|
import { useTabsStore } from "../stores/tab";
|
|
import { usePermissionsStore } from "~/stores/permissions";
|
|
|
|
const activeMenu = ref("HOME");
|
|
const showSubmenuBar = ref(false);
|
|
|
|
const tabsStore = useTabsStore();
|
|
const permissionStore = usePermissionsStore();
|
|
|
|
// 권한 기반 서브메뉴 생성
|
|
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,
|
|
}));
|
|
});
|
|
|
|
async function handleMenuChange(_menuCode: string) {
|
|
if (activeMenu.value === "HOME") {
|
|
showSubmenuBar.value = false;
|
|
await navigateTo("/");
|
|
} else {
|
|
showSubmenuBar.value = true;
|
|
}
|
|
}
|
|
|
|
function onSubMenuClick(sub: {
|
|
key: string;
|
|
label: string;
|
|
to: string;
|
|
componentName: string;
|
|
}) {
|
|
tabsStore.updateActiveTab(sub);
|
|
}
|
|
</script>
|
|
|
|
<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;
|
|
}
|
|
</style>
|