From 604aa256f6eece3f2c680c01fe892b2ed3d161dd Mon Sep 17 00:00:00 2001 From: hskwon Date: Thu, 27 Nov 2025 10:25:02 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EB=A9=94=EB=89=B4=20=ED=8A=B8?= =?UTF-8?q?=EB=A6=AC=20=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=ED=99=94=20=EB=B0=8F=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - public/js/menu-tree.js 공통 스크립트 생성 - 테이블(tr.menu-row) / div(.menu-item) 둘 다 지원 - toggleChildren, hideChildren, showChildren 함수 통합 - 권한 관리 페이지들 메뉴 트리 디자인 통일 - role-permissions, department-permissions, user-permissions - 폴더/파일 아이콘, 접기/펼치기 버튼, chevron 아이콘 - menu-row 클래스 및 data 속성 추가 - permission-analyze 접기/펼치기 기능 추가 - data-parent-id, data-depth 속성 추가 - 폴더 버튼 클릭으로 하위 메뉴 토글 - menus 페이지 스크립트 공통화 - 각 페이지 중복 코드 제거 및 공통 menu-tree.js 로드 --- public/js/menu-tree.js | 63 +++++++++++++++++++ .../department-permissions/index.blade.php | 1 + .../partials/permission-matrix.blade.php | 35 +++++++++-- resources/views/menus/index.blade.php | 46 +------------- .../views/menus/partials/table.blade.php | 30 ++++++--- .../views/permission-analyze/index.blade.php | 1 + .../partials/menu-tree.blade.php | 18 ++++-- .../views/role-permissions/index.blade.php | 1 + .../partials/permission-matrix.blade.php | 35 +++++++++-- .../views/user-permissions/index.blade.php | 1 + .../partials/permission-matrix.blade.php | 35 +++++++++-- 11 files changed, 196 insertions(+), 70 deletions(-) create mode 100644 public/js/menu-tree.js diff --git a/public/js/menu-tree.js b/public/js/menu-tree.js new file mode 100644 index 00000000..976aaa07 --- /dev/null +++ b/public/js/menu-tree.js @@ -0,0 +1,63 @@ +/** + * 메뉴 트리 공통 스크립트 + * - 부서 권한 관리 (department-permissions) - 테이블 기반 + * - 개인 권한 관리 (user-permissions) - 테이블 기반 + * - 권한 분석 (permission-analyze) - div 기반 + */ + +// 자식 메뉴 접기/펼치기 +window.toggleChildren = function(menuId) { + const button = document.querySelector(`.toggle-btn[data-menu-id="${menuId}"]`); + if (!button) return; + + const chevron = button.querySelector('.chevron-icon'); + if (!chevron) return; + + const isCollapsed = chevron.classList.contains('rotate-[-90deg]'); + + if (isCollapsed) { + // 펼치기 + chevron.classList.remove('rotate-[-90deg]'); + showChildren(menuId); + } else { + // 접기 + chevron.classList.add('rotate-[-90deg]'); + hideChildren(menuId); + } +}; + +// 자식 요소 선택자 (테이블: tr.menu-row, div: .menu-item) +function getChildElements(parentId) { + // 테이블 기반 (tr.menu-row) + let children = document.querySelectorAll(`tr.menu-row[data-parent-id="${parentId}"]`); + if (children.length > 0) return children; + + // div 기반 (.menu-item) + return document.querySelectorAll(`.menu-item[data-parent-id="${parentId}"]`); +} + +// 재귀적으로 자식 메뉴 숨기기 +function hideChildren(parentId) { + const children = getChildElements(parentId); + children.forEach(child => { + child.style.display = 'none'; + const childId = child.getAttribute('data-menu-id'); + hideChildren(childId); + }); +} + +// 재귀적으로 직계 자식만 표시 +function showChildren(parentId) { + const children = getChildElements(parentId); + children.forEach(child => { + child.style.display = ''; + const childId = child.getAttribute('data-menu-id'); + const childButton = child.querySelector(`.toggle-btn[data-menu-id="${childId}"]`); + if (childButton) { + const chevron = childButton.querySelector('.chevron-icon'); + if (chevron && !chevron.classList.contains('rotate-[-90deg]')) { + showChildren(childId); + } + } + }); +} \ No newline at end of file diff --git a/resources/views/department-permissions/index.blade.php b/resources/views/department-permissions/index.blade.php index e77bc88e..7ca36320 100644 --- a/resources/views/department-permissions/index.blade.php +++ b/resources/views/department-permissions/index.blade.php @@ -148,4 +148,5 @@ function reloadPermissions() { } }); + @endsection diff --git a/resources/views/department-permissions/partials/permission-matrix.blade.php b/resources/views/department-permissions/partials/permission-matrix.blade.php index 2f392564..3e277603 100644 --- a/resources/views/department-permissions/partials/permission-matrix.blade.php +++ b/resources/views/department-permissions/partials/permission-matrix.blade.php @@ -20,16 +20,43 @@ $permissionTypes = ['view', 'create', 'update', 'delete', 'approve', 'export', 'manage']; @endphp @forelse($menus as $index => $menu) - + {{ $index + 1 }} -
+
+ {{-- 트리 구조 표시 --}} @if(($menu->depth ?? 0) > 0) - + └─ @endif - {{ $menu->name }} + + {{-- 폴더/아이템 아이콘 (폴더는 클릭으로 접기/펼치기) --}} + @if($menu->has_children) + + @else + + + + @endif + + {{-- 메뉴명 --}} + + {{ $menu->name }} +
diff --git a/resources/views/menus/index.blade.php b/resources/views/menus/index.blade.php index dcde7456..024d8fdb 100644 --- a/resources/views/menus/index.blade.php +++ b/resources/views/menus/index.blade.php @@ -142,50 +142,6 @@ class="bg-white rounded-lg shadow-sm overflow-hidden"> }); }; - // 자식 메뉴 접기/펼치기 - window.toggleChildren = function(menuId) { - const button = document.querySelector(`.toggle-btn[data-menu-id="${menuId}"]`); - const svg = button.querySelector('svg'); - const isCollapsed = svg.classList.contains('rotate-[-90deg]'); - - if (isCollapsed) { - // 펼치기 - svg.classList.remove('rotate-[-90deg]'); - showChildren(menuId); - } else { - // 접기 - svg.classList.add('rotate-[-90deg]'); - hideChildren(menuId); - } - }; - - // 재귀적으로 자식 메뉴 숨기기 - function hideChildren(parentId) { - const children = document.querySelectorAll(`tr.menu-row[data-parent-id="${parentId}"]`); - children.forEach(child => { - child.style.display = 'none'; - const childId = child.getAttribute('data-menu-id'); - // 하위의 하위도 숨기기 - hideChildren(childId); - }); - } - - // 재귀적으로 직계 자식만 표시 - function showChildren(parentId) { - const children = document.querySelectorAll(`tr.menu-row[data-parent-id="${parentId}"]`); - children.forEach(child => { - child.style.display = ''; - // 하위 메뉴는 해당 메뉴가 펼쳐져 있을 때만 표시 - const childId = child.getAttribute('data-menu-id'); - const childButton = child.querySelector(`.toggle-btn[data-menu-id="${childId}"]`); - if (childButton) { - const childSvg = childButton.querySelector('svg'); - // 자식이 접혀있으면 그 하위는 보여주지 않음 - if (!childSvg.classList.contains('rotate-[-90deg]')) { - showChildren(childId); - } - } - }); - } + @endpush diff --git a/resources/views/menus/partials/table.blade.php b/resources/views/menus/partials/table.blade.php index 1b24e5ad..48bbbcff 100644 --- a/resources/views/menus/partials/table.blade.php +++ b/resources/views/menus/partials/table.blade.php @@ -21,24 +21,36 @@ {{ $menu->id }} -
+
+ {{-- 트리 구조 표시 --}} + @if(($menu->depth ?? 0) > 0) + └─ + @endif + + {{-- 폴더/아이템 아이콘 (폴더는 클릭으로 접기/펼치기) --}} @if($menu->has_children) @else - + + + @endif - @if(($menu->depth ?? 0) > 0) - - @endif -
-
{{ $menu->name }}
+ + {{-- 메뉴 정보 --}} +
+ + {{ $menu->name }} + @if($menu->is_external) (외부) @endif diff --git a/resources/views/permission-analyze/index.blade.php b/resources/views/permission-analyze/index.blade.php index 481bee35..a3a37df8 100644 --- a/resources/views/permission-analyze/index.blade.php +++ b/resources/views/permission-analyze/index.blade.php @@ -295,4 +295,5 @@ function exportCsv() { } }); + @endpush \ No newline at end of file diff --git a/resources/views/permission-analyze/partials/menu-tree.blade.php b/resources/views/permission-analyze/partials/menu-tree.blade.php index 65337825..55f3ed28 100644 --- a/resources/views/permission-analyze/partials/menu-tree.blade.php +++ b/resources/views/permission-analyze/partials/menu-tree.blade.php @@ -2,6 +2,8 @@