[UI 개선] 서브메뉴 및 탭 바 컴포넌트 추가, AppHeader 및 기본 레이아웃 수정, API 호출 로직 개선

This commit is contained in:
2025-09-24 16:25:30 +09:00
parent f83782813d
commit f9dde4eb09
10 changed files with 338 additions and 490 deletions

View File

@@ -1,6 +1,28 @@
<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 { ref, computed, watch, onMounted } from "vue";
import SubMenuBar from "../components/layout/SubMenuBar.vue";
import TabBar from "../components/layout/TabBar.vue";
import { ref, computed } from "vue";
import { useRouter } from "vue-router";
import { useTabsStore } from "../stores/tab";
import { usePermissionsStore } from "~/stores/permissions";
@@ -12,16 +34,6 @@ 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 [];
@@ -43,11 +55,15 @@ const subMenus = computed(() => {
}));
});
function onMenuClick(menu: string) {
showSubmenuBar.value = menu !== "home";
function handleMenuChange(_menuCode: string) {
if (activeMenu.value === "HOME") {
showSubmenuBar.value = false;
router.push("/");
} else {
showSubmenuBar.value = true;
}
}
// ✅ 서브메뉴 클릭 → 현재 활성 탭 내용만 변경
function onSubMenuClick(sub: {
key: string;
label: string;
@@ -55,72 +71,9 @@ function onSubMenuClick(sub: {
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;
@@ -139,56 +92,4 @@ function addNewTab() {
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>