left menu -> top menu 이동 완료

jsp, css, menu-path.js 모두 동시에 변경해야 완료됨.
dev
박성영 4 months ago
parent e5d7f267f9
commit de4dc5a160

@ -0,0 +1,113 @@
---
적용: 항상
---
# Project Guidelines
## 기본 중요 가이드
### 한글로 대화, 한글 필수
### 테스트소스는 작성 불가!!
### 중요로직 주석 필수 (한글)
### 각 컨트롤러 클래스에 스웨거 적용
### 기존소스의 공백제거 등 로직에 필요없는 부분은 수정 불가!!
### mvc 패턴, springboot + mybatis + jsp 구조, tiles 사용
### session 보안
### 기본적인 코딩 스타일은 noticeSample 참조 (xml, java, jsp)
### url 은 기본적으로 <c:url/> 사용
### 정적 리소스는 src/main/webapp/resources/css,js,xit 등등 해당 위치에 있음.
- 모달은 src/main/webapp/WEB-INF/views/system/loginLog/list.jsp :: 로그인로그 목록 조회 페이지 참조
- 팝업은 src/main/webapp/WEB-INF/views/system/user/auth_popup.jsp :: 팝업 호출 참조
- 모달과 메인윈도우는 src/main/webapp/resources/xit/xit-common.css 에 css 추가
- 팝업은 src/main/webapp/resources/xit/xit-popup.css 에 css 추가
### 신규 sql, DDL 생성시 DB-DDL/maria/dictionary/column_word_dictionary.md, 컬럼 단어사전 무조건 참조!!
### 단어사전에 없다면 단어사전 신규 추가!! (규칙은 무조건 지켜야함 특히, 일시->dttm, 같은 경우)
### 시간이란 명칭의 컬럼도 LocalDateTime 일경우 일시(dttm) 으로 변경하여 저장
### mybatis 기본적으로 camelCase 적용되어 있어 컬럼의 알리아스(별시) 별도로 사용하지 않아도 돼
### DB 구조는 DB-DDL/maria/ddl/xitframework/*.sql 참조
### 기본적인 key 는 시컨스를 이용, 총 20자리, 데이터약어 4자리, 시컨스포함 16자리
```mariadb
SELECT CONCAT('BBSN', LPAD(NEXTVAL(seq_notice_id), 16, '0'))
```
### paging 처리 시 controller 에서 1.totalCount 구하기, 2.setTotalCount, 3.setPagingYn 순서 중요!! 해당순서를 지켜야지만 에러 발생안함.
```java
// 1. 총 개수 조회
int totalCount = excelSampleService.selectExcelSampleListTotalCount(paramVO);
// 2. 응답 데이터 구성
paramVO.setTotalCount(totalCount);
// 3. 페이징 처리
paramVO.setPagingYn("Y");
```
## 2 주요 기술 및 라이브러리
### 2.1 핵심 기술 스택
| 기술 | 버전 | 설명 |
|------|------|------|
| Java | 개발: 1.8, 배포: 1.8 | 자바 개발 및 실행 환경 |
| Spring Boot | 2.7.18 | 스프링 기반 애플리케이션 개발 프레임워크 |
| 전자정부 프레임워크 | 4.3.0 | 한국 정부 표준 웹 개발 프레임워크 |
| Servlet | 3.1 | 웹 애플리케이션 표준 |
| Gradle | - | 빌드 및 의존성 관리 도구 |
| MariaDB | - | 관계형 데이터베이스 |
## 2.2 주요 라이브러리
| 라이브러리 | 버전 | 설명 |
|------------|------|------|
| MyBatis | 2.3.1 | SQL 매핑 프레임워크 |
| Apache Tiles | 3.0.8 | 레이아웃 템플릿 엔진 |
| TOAST UI Grid | 4.19.2 | 자바스크립트 그리드 라이브러리 |
| Lombok | - | 자바 코드 생성 라이브러리 |
| Apache Commons Text | 1.10.0 | 텍스트 처리 유틸리티 |
| Apache POI | 5.3.0 | 엑셀 파일 처리 라이브러리 |
## 3. 프로젝트 구조
### 3.1 디렉토리 구조
```
xit-framework/
├── DB-DDL/ # 데이터베이스 스크립트
│ └── maria/ # MariaDB 스크립트
│ ├── ddl/ # 테이블 정의 스크립트
│ └── dml/ # 샘플 데이터 스크립트
├── src/
│ ├── main/
│ │ ├── java/ # 자바 소스 코드
│ │ │ ├── egovframework/ # 전자정부 프레임워크 확장 코드
│ │ │ └── go/kr/project/ # 프로젝트 소스 코드
│ │ ├── resources/ # 리소스 파일
│ │ │ ├── mybatis/ # MyBatis 설정 및 매퍼
│ │ │ └── application.yml # 애플리케이션 설정 파일
│ │ └── webapp/ # 웹 리소스
│ │ ├── resources/ # 정적 리소스 (CSS, JS, 이미지 등)
│ │ └── WEB-INF/views/ # JSP 뷰 파일
│ └── test/ # 테스트 코드
└── build.gradle # Gradle 빌드 스크립트
```
### 3.2 패키지 구조
```
go.kr.project/
├── common/ # 공통 컴포넌트
├── login/ # 로그인 관련 기능
│ ├── controller/ # 컨트롤러 클래스
│ ├── mapper/ # MyBatis 매퍼 인터페이스
│ ├── model/ # 데이터 모델 클래스
│ └── service/ # 서비스 클래스
└── system/ # 시스템 관리 기능
├── auth/ # 권한 관리
├── code/ # 코드 관리
├── group/ # 그룹 관리
├── menu/ # 메뉴 관리
├── role/ # 역할 관리
└── user/ # 사용자 관리
├── controller/ # 컨트롤러 클래스
├── mapper/ # MyBatis 매퍼 인터페이스
├── model/ # 데이터 모델 클래스
└── service/ # 서비스 클래스
egovframework/
├── config/ # 프레임워크 설정
├── exception/ # 예외 처리
├── filter/ # 필터
├── interceptor/ # 인터셉터
└── util/ # 유틸리티 클래스
```

@ -35,9 +35,9 @@
<%--<link rel="stylesheet" type="text/css" href="<c:url value='/css/jquery.treeview.css' />">--%>
<link rel="stylesheet" href="<c:url value='/plugins/DataTables/datatables.css' />">
<!-- Main Style -->
<link rel="stylesheet" href="<c:url value='/plugins/simplebar/simplebar.min.css' />">
<%-- 사이드바 제거로 인해 주석처리: <link rel="stylesheet" href="<c:url value='/plugins/simplebar/simplebar.min.css' />"> --%>
<link rel="stylesheet" href="<c:url value='/css/style.min.css"' />" id="main-css">
<link rel="stylesheet" href="<c:url value='/css/sidebar-royal.min.css' />" id="theme-css">
<%-- 사이드바 제거로 인해 주석처리: <link rel="stylesheet" href="<c:url value='/css/sidebar-royal.min.css' />" id="theme-css"> --%>
<!-- options: blue,cyan,dark,gray,green,pink,purple,red,royal,ash,crimson,namn,frost -->
<link rel="stylesheet" href="<c:url value='/css/bootstrap-datepicker.min.css' />">
<link rel="stylesheet" href="<c:url value='/css/style_new.css' />">
@ -51,7 +51,7 @@
<script type="text/javascript" src="<c:url value='/js/bootstrap-datepicker.min.js' />"></script>
<!-- Plugins -->
<%--<script type="text/javascript" src="<c:url value='/js/jquery.treeview.js' />"></script>--%>
<script type="text/javascript" src="<c:url value='/plugins/simplebar/simplebar.min.js' />"></script>
<%-- 사이드바 제거로 인해 주석처리: <script type="text/javascript" src="<c:url value='/plugins/simplebar/simplebar.min.js' />"></script> --%>
<script type="text/javascript" src="<c:url value='/plugins/feather-icons/feather.min.js' />"></script>
<script type="text/javascript" src="<c:url value='/plugins/DataTables/datatables.js' />"></script>
<script type="text/javascript" src="<c:url value='/plugins/crypto-js/crypto-js-4.0.0.js' />"></script>
@ -81,22 +81,15 @@
<link rel="stylesheet" href="<c:url value="/css/xit-multi-fileupload.css"/>" />
</head>
<body class="${sessionScope.sidebarState}">
<!-- Sidebar -->
<div class="sidebar">
<tiles:insertAttribute name="menu_header" />
<tiles:insertAttribute name="menu" />
</div>
<!-- /Sidebar -->
<!-- Main -->
<div class="main">
<body>
<!-- Main Content (전체 너비 사용) -->
<div class="main-content-full">
<tiles:insertAttribute name="main_header" />
<tiles:insertAttribute name="main" />
</div>
<!-- /Main -->
<!-- /Main Content -->
<script type="text/javascript" src="<c:url value='/js/script.min.js' />"></script>
<%-- 사이드바 제거로 인해 주석처리: <script type="text/javascript" src="<c:url value='/js/script.min.js' />"></script> --%>
</body>
</html>

@ -12,94 +12,18 @@
<c:set var="currentUrl" value="${requestScope['javax.servlet.forward.servlet_path']}" />
<!-- 디버깅: 현재 URL = ${currentUrl} -->
<style>
/* 상단 메뉴 스타일 */
.top-level-menu {
display: flex;
align-items: center;
margin-left: 15px;
}
.top-menu-item {
display: flex;
align-items: center;
padding: 0 20px;
color: #495057;
font-weight: 500;
text-decoration: none;
height: 40px;
position: relative;
transition: color 0.2s;
font-size: 16px;
}
.top-menu-item:hover {
color: #007bff;
text-decoration: none;
}
.top-menu-item.active {
color: #007bff;
font-weight: 550;
position: relative;
}
.top-menu-item.active:before {
content: '';
position: absolute;
bottom: 0;
left: 10px;
right: 10px;
height: 2px;
background-color: #007bff;
}
.top-menu-item:after {
content: '';
position: absolute;
right: 0;
top: 25%;
height: 50%;
width: 1px;
background-color: #e9ecef;
}
.top-menu-item:last-child:after {
display: none;
}
.top-menu-icon {
width: 16px;
height: 16px;
margin-right: 5px;
}
/* 반응형 스타일 */
@media (max-width: 992px) {
.top-menu-item span {
display: none;
}
.top-menu-item {
padding: 0 12px;
}
.top-menu-icon {
margin-right: 0;
width: 18px;
height: 18px;
}
}
</style>
<!-- Main header -->
<div class="main-header">
<!-- 사이드바 토글 버튼 -->
<a class="nav-link nav-link-faded rounded-circle nav-icon" href="#" data-toggle="sidebar" id="saveSidebarState">
<i class="material-icons">menu</i>
</a>
<%-- LEVEL1 DEPTH MENU 표출(클릭 시 하위에 있는 URL 중 첫번째 URL로 이동 가능) --%>
<!-- 로고 영역 -->
<div class="header-logo">
<a href="<c:url value="/"/>" class="logo">
<img src="<c:url value="/img/xit-logo/img_icon.png"/>" alt="Logo" id="main-logo">
<span class="logo-text">고양시 일산 서구</span>
</a>
</div>
<%-- 상단 메뉴와 서브메뉴 드롭다운 표출 --%>
<c:if test="${not empty sessionScope.sessionVO.menus}">
<div class="top-level-menu">
<c:forEach var="menu" items="${sessionScope.sessionVO.menus}">
@ -117,7 +41,6 @@
<%-- 메뉴 자체의 URL을 URL 패턴으로 변환하여 추가 --%>
<c:set var="menuUrlAsPattern" value="${xssUtil.unescape(menu.menuUrl)}/**" />
<c:set var="allUrlPatterns" value="${allUrlPatterns}${not empty allUrlPatterns ? ',' : ''}${menuUrlAsPattern}" />
<!-- 디버깅: 메뉴 자체 URL을 패턴으로 추가 - 메뉴 ID: ${menu.menuId}, URL: ${menu.menuUrl}, 패턴: ${menuUrlAsPattern}, 전체 패턴: ${allUrlPatterns} -->
</c:if>
<%-- 메뉴 자체에 URL이 없는 경우 첫 번째 유효한 하위 URL 찾기 --%>
@ -133,7 +56,6 @@
<%-- 하위 메뉴의 URL 패턴 수집 --%>
<c:if test="${not empty subMenu.urlPattern}">
<c:set var="allUrlPatterns" value="${allUrlPatterns}${not empty allUrlPatterns ? ',' : ''}${xssUtil.unescape(subMenu.urlPattern)}" />
<!-- 디버깅: 하위 메뉴 URL 패턴 추가 - 상위 메뉴 ID: ${menu.menuId}, 하위 메뉴 ID: ${subMenu.menuId}, 패턴: ${subMenu.urlPattern}, 전체 패턴: ${allUrlPatterns} -->
</c:if>
</c:forEach>
</c:if>
@ -146,32 +68,67 @@
<%-- 2. 메뉴의 URL 패턴과 현재 URL 비교 --%>
<c:if test="${not empty allUrlPatterns and not isMenuActive}">
<%-- 디버깅: URL 패턴 정보 --%>
<!-- 메뉴 ID: ${menu.menuId}, URL 패턴: ${allUrlPatterns}, 현재 URL: ${currentUrl} -->
<c:set var="patterns" value="${fn:split(allUrlPatterns, ',')}" />
<c:forEach var="pattern" items="${patterns}" varStatus="patternStatus">
<c:set var="trimmedPattern" value="${fn:trim(pattern)}" />
<%-- 디버깅: 개별 패턴 정보 --%>
<!-- 패턴 ${patternStatus.index}: [${trimmedPattern}] -->
<c:if test="${not empty trimmedPattern}">
<c:set var="isMatch" value="${path:match(trimmedPattern, currentUrl)}" />
<!-- 패턴 매칭 시도: 패턴=[${trimmedPattern}], URL=[${currentUrl}], 결과=[${isMatch}] -->
<c:if test="${isMatch}">
<c:set var="isMenuActive" value="true" />
<!-- 패턴 매칭 성공! 메뉴 ${menu.menuId}가 활성화됨 -->
</c:if>
</c:if>
</c:forEach>
</c:if>
<a href="<c:url value='${firstChildUrl}' />" class="top-menu-item ${isMenuActive ? 'active' : ''}" title="${menu.menuNm}"
data-menu-id="${menu.menuId}" style="${isMenuActive ? 'color: #007bff;' : ''}">
<c:if test="${not empty menu.menuIcon}"><i data-feather="${menu.menuIcon}" class="top-menu-icon"></i></c:if>
<span>${menu.menuNm}</span>
</a>
<%-- 메인 메뉴 항목 (드롭다운이 있는 경우와 없는 경우 구분) --%>
<div class="top-menu-dropdown ${isMenuActive ? 'active' : ''}" data-menu-id="${menu.menuId}">
<a href="<c:url value='${firstChildUrl}' />" class="top-menu-item ${isMenuActive ? 'active' : ''}" title="${menu.menuNm}">
<c:if test="${not empty menu.menuIcon}"><i data-feather="${menu.menuIcon}" class="top-menu-icon"></i></c:if>
<span>${menu.menuNm}</span>
<c:if test="${not empty menu.children}">
<i class="material-icons dropdown-arrow">keyboard_arrow_down</i>
</c:if>
</a>
<%-- 서브메뉴 드롭다운 --%>
<c:if test="${not empty menu.children}">
<div class="top-menu-submenu">
<c:forEach var="subMenu" items="${menu.children}">
<c:if test="${subMenu.useYn eq 'Y' and subMenu.viewYn eq 'Y'}">
<c:set var="subMenuUrl" value="${not empty subMenu.menuUrl ? xssUtil.unescape(subMenu.menuUrl) : '#'}" />
<c:set var="subMenuUrlPattern" value="${not empty subMenu.urlPattern ? xssUtil.unescape(subMenu.urlPattern) : ''}" />
<c:set var="isSubMenuActive" value="false" />
<%-- 서브메뉴 활성화 상태 확인 --%>
<%-- 1. 현재 URL이 서브메뉴 URL로 시작하는 경우 --%>
<c:if test="${not empty subMenuUrl and subMenuUrl != '#' and fn:startsWith(currentUrl, subMenuUrl)}">
<c:set var="isSubMenuActive" value="true" />
</c:if>
<%-- 2. 서브메뉴의 URL 패턴과 현재 URL 비교 --%>
<c:if test="${not empty subMenuUrlPattern and not isSubMenuActive}">
<c:set var="subPatterns" value="${fn:split(subMenuUrlPattern, ',')}" />
<c:forEach var="subPattern" items="${subPatterns}">
<c:set var="trimmedSubPattern" value="${fn:trim(subPattern)}" />
<c:if test="${not empty trimmedSubPattern}">
<c:set var="isSubMatch" value="${path:match(trimmedSubPattern, currentUrl)}" />
<c:if test="${isSubMatch}">
<c:set var="isSubMenuActive" value="true" />
</c:if>
</c:if>
</c:forEach>
</c:if>
<a href="<c:url value='${subMenuUrl}' />" class="submenu-item ${isSubMenuActive ? 'active' : ''}" title="${subMenu.menuNm}">
${subMenu.menuNm}
</a>
</c:if>
</c:forEach>
</div>
</c:if>
</div>
</c:if>
</c:forEach>
</div>
@ -226,32 +183,51 @@ $(document).ready(function() {
// 세션 타이머 초기화
initSessionTimer();
// 상단 메뉴 아이콘 초기화 (Feather 아이콘 라이브러리 사용)
if (typeof feather !== 'undefined') {
feather.replace('.top-menu-icon');
}
// Feather 아이콘 초기화 (모든 data-feather 속성을 가진 아이콘에 적용)
initializeFeatherIcons();
// 상단 메뉴 드롭다운 이벤트 처리
$('.top-menu-dropdown').hover(
function() {
// 마우스 오버 시 서브메뉴 표시
$(this).find('.top-menu-submenu').fadeIn(200);
},
function() {
// 마우스 아웃 시 서브메뉴 숨김
$(this).find('.top-menu-submenu').fadeOut(200);
}
);
$(document).on('click', '#saveSidebarState', function () {
var saveSidebarStateValue = "sidebar-collapse";
if( $("body").hasClass("sidebar-collapse") ){
saveSidebarStateValue = ""
/**
* Feather 아이콘 초기화 함수
* 모든 data-feather 속성을 가진 요소를 feather 아이콘으로 변환합니다.
*/
function initializeFeatherIcons() {
// feather 라이브러리가 로드되었는지 확인
if (typeof feather === 'undefined') {
console.log('[DEBUG_LOG] Feather 라이브러리가 로드되지 않았습니다.');
return;
}
console.log(saveSidebarStateValue);
// AJAX 요청으로 사이드바 상태 저장
$.ajax({
url: "<c:url value='/common/header/sidebar/state.ajax'/>",
type: "POST",
data: {"state":saveSidebarStateValue},
dataType: "json",
success: function(response) {
//console.log("사이드바 상태 저장 성공:", response);
},
error: function(xhr, status, error) {
console.error(xhr.responseText);
}
});
});
try {
// DOM이 준비될 때까지 잠시 기다린 후 실행
setTimeout(function() {
var $featherElements = $('[data-feather]');
console.log('[DEBUG_LOG] 발견된 feather 아이콘 요소 수:', $featherElements.length);
if ($featherElements.length > 0) {
// 모든 data-feather 속성을 가진 요소에 아이콘 적용
feather.replace();
console.log('[DEBUG_LOG] Feather 아이콘 초기화 완료');
} else {
console.log('[DEBUG_LOG] feather 아이콘 요소를 찾을 수 없습니다.');
}
}, 100);
} catch (error) {
console.error('[DEBUG_LOG] Feather 아이콘 초기화 오류:', error);
}
}
/**
* 드롭다운 메뉴 초기화 함수

@ -81,9 +81,9 @@
<li class="tit">단속자료 목록</li>
<li style="text-align: right;">
<select id="perPageSelect" class="input" style="width: 110px;">
<option value="10">페이지당 10</option>
<option value="20">페이지당 20</option>
<option value="30">페이지당 30</option>
<option value="15">페이지당 15</option>
<option value="50">페이지당 50</option>
<option value="100">페이지당 100</option>
</select>
<span class="page_number"><span id="currentPage"></span><span class="bar">/</span><span id="totalPages"></span> Pages</span>
</li>
@ -172,7 +172,7 @@
var dataSource = this.createDataSource();
// 현재 선택된 perPage 값 가져오기
var perPage = parseInt($('#perPageSelect').val() || 10, 10);
var perPage = parseInt($('#perPageSelect').val() || 15, 10);
// 그리드 설정 객체 생성
var gridConfig = new XitTuiGridConfig();
@ -180,7 +180,7 @@
// 기본 설정
gridConfig.setOptDataSource(dataSource); // 데이터소스 연결
gridConfig.setOptGridId('grid'); // 그리드를 출력할 Element ID
gridConfig.setOptGridHeight(390); // 그리드 높이(단위: px)
gridConfig.setOptGridHeight('auto'); // 그리드 높이(단위: px)
gridConfig.setOptRowHeight(30); // 그리드 행 높이(단위: px)
gridConfig.setOptRowHeaderType('rowNum'); // 행 첫번째 셀 타입(rowNum: 순번, checkbox: 체크박스, '': 출력 안함)
gridConfig.setOptUseClientSort(false); // 서버사이드 정렬 false

@ -26,19 +26,19 @@ $(function() {
// 현재 URL과 일치하는 메뉴 찾기
var currentMenuPath = "";
// 활성화된 메뉴 항목 찾기 (active 클래스가 있는 항목)
var $activeMenuItem = $('.nav.treeview.mb-4 li.nav-item > a.active');
// 상단 메뉴에서 활성화된 메뉴 항목 찾기 (active 클래스가 있는 항목)
var $activeMenuItem = $('.top-menu-item.active');
var $activeSubMenuItem = $('.submenu-item.active');
if ($activeMenuItem.length > 0) {
// 활성화된 메뉴가 있는 경우
var menuText = $activeMenuItem.text().trim();
// 활성화된 상위 메뉴가 있는 경우
var menuText = $activeMenuItem.find('span').text().trim();
currentMenuPath = menuText;
// 하위 메뉴 확인
var $activeSubMenuItem = $activeMenuItem.parent('li').find('.nav > li.show');
// 활성화된 하위 메뉴가 있는지 확인
if ($activeSubMenuItem.length > 0) {
var subMenuText = $activeSubMenuItem.find('a').text().trim();
currentMenuPath = menuText + " - " + subMenuText;
var subMenuText = $activeSubMenuItem.text().trim();
currentMenuPath = menuText + " > " + subMenuText;
}
}

@ -1,5 +1,206 @@
@charset "utf-8";
/* ===== 상단 메뉴 레이아웃 스타일 ===== */
/* 메인 컨텐츠가 전체 너비를 사용하도록 설정 */
.main-content-full {
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* 메인 헤더 스타일 */
.main-header {
display: flex;
align-items: center;
padding: 0 20px;
background-color: #fff;
border-bottom: 1px solid #e9ecef;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
height: 70px;
width: 100%;
}
/* 헤더 로고 스타일 */
.header-logo {
/*margin-right: 40px;*/
}
.header-logo .logo {
display: flex;
align-items: center;
text-decoration: none;
color: #495057;
}
.header-logo .logo img {
width: auto !important; /* style.min.css의 width: 40px 비활성화 */
height: 40px;
margin-right: 10px;
}
.header-logo .logo-text {
font-size: 18px;
font-weight: bold;
line-height: 1.2;
color: #007bff;
}
/* 상단 메뉴 드롭다운 스타일 */
.top-menu-dropdown {
position: relative;
display: inline-block;
}
.top-menu-dropdown .dropdown-arrow {
font-size: 16px;
margin-left: 5px;
transition: transform 0.2s;
}
.top-menu-dropdown:hover .dropdown-arrow {
transform: rotate(180deg);
}
.top-menu-submenu {
display: none;
position: absolute;
top: 100%;
left: 0;
min-width: 200px;
background-color: #fff;
border: 1px solid #e9ecef;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
border-radius: 4px;
z-index: 1000;
padding: 8px 0;
}
.top-menu-submenu .submenu-item {
display: block;
padding: 10px 20px;
color: #495057;
text-decoration: none;
font-size: 14px;
transition: background-color 0.2s;
}
.top-menu-submenu .submenu-item:hover {
background-color: #f8f9fa;
color: #007bff;
text-decoration: none;
}
.top-menu-submenu .submenu-item.active {
background-color: #e3f2fd;
color: #1976d2;
font-weight: 550;
}
.top-menu-submenu .submenu-item.active:hover {
background-color: #bbdefb;
color: #1565c0;
}
/* 상단 메뉴 컨테이너 스타일 */
.top-level-menu {
display: flex;
align-items: center;
margin-left: 15px;
}
/* 메뉴 간 구분자 스타일 */
.top-menu-dropdown:not(:last-child)::after {
content: '|';
position: absolute;
right: -1px;
top: 50%;
transform: translateY(-50%);
color: #dee2e6;
font-size: 14px;
font-weight: normal;
z-index: 1;
}
/* 상단 메뉴 아이콘 스타일 */
.top-menu-icon {
width: 16px;
height: 16px;
margin-right: 5px;
display: inline-block;
vertical-align: middle;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
color: inherit;
}
/* 기존 상단 메뉴 스타일 확장 */
.top-level-menu .top-menu-item {
display: flex;
align-items: center;
padding: 0 20px;
color: #495057;
font-weight: 500;
text-decoration: none;
height: 40px;
position: relative;
transition: color 0.2s;
font-size: 16px;
}
.top-level-menu .top-menu-item:hover {
color: #007bff;
text-decoration: none;
}
.top-level-menu .top-menu-item.active {
color: #007bff;
font-weight: 550;
}
.top-menu-item.active:before {
content: '';
position: absolute;
bottom: -15px;
left: 10px;
right: 10px;
height: 2px;
background-color: #007bff;
}
/* 메인 컨텐츠 영역 스타일 */
.main-content-full .main {
flex: 1;
padding: 20px;
background-color: #f8f9fa;
}
/* 반응형 스타일 */
@media (max-width: 992px) {
.header-logo {
margin-right: 20px;
}
.header-logo .logo-text {
font-size: 14px;
}
.top-level-menu .top-menu-item span {
display: none;
}
.top-level-menu .top-menu-item {
padding: 0 12px;
}
.top-menu-submenu {
min-width: 150px;
}
}
/* 버튼 그룹 스타일 */
.btn-group {
padding-top: 10px;

Loading…
Cancel
Save