254 lines
13 KiB
PHP
254 lines
13 KiB
PHP
<?php
|
|
// 부서 권한 설정 (트리형) — 메뉴관리에서 활성화된 메뉴만 대상
|
|
$CURRENT_SECTION = 'permission';
|
|
include '../inc/header.php';
|
|
?>
|
|
|
|
<div class="container" style="max-width:1280px; margin-top:24px;">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header d-flex flex-wrap gap-2 align-items-center">
|
|
<strong>부서 권한 설정 (트리)</strong>
|
|
<select class="form-select form-select-sm" id="deptSelect" style="width:240px;">
|
|
<option value="1">경영지원팀</option>
|
|
<option value="2">영업팀</option>
|
|
<option value="3" selected>개발팀</option>
|
|
</select>
|
|
<div class="ms-auto d-flex flex-wrap gap-2">
|
|
<button class="btn btn-sm btn-outline-primary" id="btnAllAllow">전체 허용</button>
|
|
<button class="btn btn-sm btn-outline-danger" id="btnAllDeny">전체 금지</button>
|
|
<button class="btn btn-sm btn-outline-secondary" id="btnClear">초기화</button>
|
|
<button class="btn btn-sm btn-primary" id="btnSave">저장</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<div class="d-flex flex-wrap gap-2 mb-2">
|
|
<div class="input-group" style="max-width:420px;">
|
|
<input type="text" class="form-control" id="searchInput" placeholder="메뉴명/코드 검색">
|
|
<button class="btn btn-outline-secondary" id="btnSearch">검색</button>
|
|
<button class="btn btn-outline-secondary" id="btnResetSearch">초기화</button>
|
|
</div>
|
|
<div class="small text-muted ms-auto">
|
|
* 부모 설정은 하위로 전파됩니다. 자식이 섞이면 부모 칸은 흐릿 표시됩니다.
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-responsive" style="max-height:64vh; overflow:auto;">
|
|
<table class="table table-sm align-middle" id="permTable">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th style="width:320px;">메뉴</th>
|
|
<th class="text-center" style="width:90px;">
|
|
읽기<br><input type="checkbox" id="hdr_read">
|
|
</th>
|
|
<th class="text-center" style="width:90px;">
|
|
쓰기<br><input type="checkbox" id="hdr_create">
|
|
</th>
|
|
<th class="text-center" style="width:90px;">
|
|
수정<br><input type="checkbox" id="hdr_update">
|
|
</th>
|
|
<th class="text-center" style="width:90px;">
|
|
삭제<br><input type="checkbox" id="hdr_delete">
|
|
</th>
|
|
<th class="text-center" style="width:90px;">
|
|
결재<br><input type="checkbox" id="hdr_approve">
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody><!-- PermissionMenu가 렌더 --></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
/* 메뉴관리 페이지와 동일한 크기/톤 */
|
|
.menu-name { font-size:14px; font-weight:500; }
|
|
.menu-url { color:#6c757d; font-size:12px;}
|
|
.indent { font-family: ui-monospace, Menlo, Consolas, monospace; color:#6c757d; }
|
|
</style>
|
|
|
|
<!-- 공통 트리 모듈 로드 -->
|
|
<script src="/tenant/assets/js/permission_menu.js"></script>
|
|
|
|
<script>
|
|
// ====== 샘플 데이터(실전에서는 AJAX 로드하세요) ======
|
|
const MENU_DATA = [
|
|
{id:1, parent_id:null, title:'대시보드', code:'dashboard', path:'/tenant/member/dashboard.php', source:'system'},
|
|
{id:2, parent_id:null, title:'수주', code:'order', path:null, source:'system'},
|
|
{id:21, parent_id:2, title:'수주 관리', code:'order.manage', path:'/tenant/order/manage.php', source:'system'},
|
|
{id:22, parent_id:2, title:'수주 등록/수정', code:'order.edit', path:'/tenant/order/edit.php', source:'system'},
|
|
|
|
{id:3, parent_id:null, title:'생산', code:'production', path:null, source:'system'},
|
|
{id:31, parent_id:3, title:'작업 지시', code:'production.wi', path:'/tenant/production/work_instruction.php', source:'workflow'},
|
|
{id:32, parent_id:3, title:'스크린 작업', code:'production.screen', path:'/tenant/production/screen_work.php', source:'workflow'},
|
|
|
|
{id:4, parent_id:null, title:'자재', code:'material', path:null, source:'system'},
|
|
{id:41, parent_id:4, title:'수입 검사 대장', code:'material.insp', path:'/tenant/material/inspection_list.php', source:'workflow'},
|
|
|
|
{id:5, parent_id:null, title:'게시판', code:'board', path:null, source:'system'},
|
|
{id:51, parent_id:5, title:'공지사항', code:'board.notice', path:'/tenant/board/notice_list.php', source:'board'},
|
|
{id:52, parent_id:5, title:'Q&A', code:'board.qna', path:'/tenant/board/qna_list.php', source:'board'},
|
|
];
|
|
|
|
// 테넌트에서 “메뉴 사용 설정”으로 켜둔 메뉴만 권한 대상
|
|
const TENANT_USE = {
|
|
'dashboard': true,
|
|
'order': true, 'order.manage': true, 'order.edit': false,
|
|
'production': true, 'production.wi': true, 'production.screen': true,
|
|
'material': true, 'material.insp': false,
|
|
'board': true, 'board.notice': true, 'board.qna': false
|
|
};
|
|
|
|
// 현재 선택 부서의 권한 상태 (페이지 state)
|
|
// 실제 구현: deptSelect 변경 시 AJAX 로드해서 채워넣기
|
|
const DEPT_PERMS = {}; // code: {read,create,update,delete,approve}
|
|
|
|
// 공통 트리 데이터 주입
|
|
PermissionMenu.setData(MENU_DATA, TENANT_USE);
|
|
|
|
// 행 렌더
|
|
function renderRows(){
|
|
const rows = PermissionMenu.buildRows((node, depth) => {
|
|
const p = (DEPT_PERMS[node.code] ||= {read:false,create:false,update:false,delete:false,approve:false});
|
|
return ['read','create','update','delete','approve'].map(perm =>
|
|
`<td class="text-center">
|
|
<input type="checkbox" class="perm" data-perm="${perm}" ${p[perm]?'checked':''}>
|
|
</td>`
|
|
).join('');
|
|
});
|
|
document.querySelector('#permTable tbody').innerHTML = rows;
|
|
applyIndeterminate();
|
|
}
|
|
|
|
// 전파/요약
|
|
function setChildrenPerm(code, perm, on){
|
|
PermissionMenu.childrenOf(code).forEach(ch=>{
|
|
(DEPT_PERMS[ch.code] ||= {read:false,create:false,update:false,delete:false,approve:false});
|
|
DEPT_PERMS[ch.code][perm] = on;
|
|
setChildrenPerm(ch.code, perm, on);
|
|
});
|
|
}
|
|
function updateParentPerms(code, perm){
|
|
const parent = PermissionMenu.parentOf(code);
|
|
if (!parent) return;
|
|
const kids = PermissionMenu.childrenOf(parent.code);
|
|
const states = kids.map(k => !!(DEPT_PERMS[k.code] && DEPT_PERMS[k.code][perm]));
|
|
const allOn = states.every(Boolean);
|
|
const allOff = states.every(s=>!s);
|
|
(DEPT_PERMS[parent.code] ||= {read:false,create:false,update:false,delete:false,approve:false});
|
|
DEPT_PERMS[parent.code][perm] = allOn ? true : (allOff ? false : true);
|
|
updateParentPerms(parent.code, perm);
|
|
}
|
|
function applyIndeterminate(){
|
|
// 행별 indeterminate
|
|
document.querySelectorAll('#permTable tbody tr').forEach(tr=>{
|
|
const code = tr.dataset.code;
|
|
const kids = PermissionMenu.childrenOf(code);
|
|
tr.querySelectorAll('input.perm').forEach(chk=>{
|
|
chk.indeterminate = false;
|
|
if (kids.length){
|
|
const perm = chk.dataset.perm;
|
|
const onCnt = kids.filter(k => !!(DEPT_PERMS[k.code] && DEPT_PERMS[k.code][perm])).length;
|
|
if (onCnt>0 && onCnt<kids.length) chk.indeterminate = true;
|
|
}
|
|
});
|
|
});
|
|
// 헤더 요약
|
|
['read','create','update','delete','approve'].forEach(perm=>{
|
|
const hdr = document.querySelector('#hdr_'+perm);
|
|
if(!hdr) return;
|
|
const codes = PermissionMenu.activeCodes();
|
|
const vals = codes.map(code => !!(DEPT_PERMS[code] && DEPT_PERMS[code][perm]));
|
|
const onCnt = vals.filter(Boolean).length;
|
|
hdr.indeterminate = (onCnt>0 && onCnt<vals.length);
|
|
hdr.checked = (onCnt===vals.length);
|
|
});
|
|
}
|
|
|
|
// 이벤트
|
|
document.addEventListener('change', e=>{
|
|
const el = e.target;
|
|
if (!el.classList.contains('perm')) return;
|
|
const tr = el.closest('tr');
|
|
const code = tr.dataset.code;
|
|
const perm = el.dataset.perm;
|
|
(DEPT_PERMS[code] ||= {read:false,create:false,update:false,delete:false,approve:false});
|
|
DEPT_PERMS[code][perm] = el.checked;
|
|
setChildrenPerm(code, perm, el.checked);
|
|
updateParentPerms(code, perm);
|
|
renderRows();
|
|
});
|
|
|
|
// 헤더 전체 허용/금지(권한별)
|
|
['read','create','update','delete','approve'].forEach(perm=>{
|
|
const hdr = document.querySelector('#hdr_'+perm);
|
|
if (!hdr) return;
|
|
hdr.addEventListener('change', ()=>{
|
|
PermissionMenu.activeCodes().forEach(code=>{
|
|
(DEPT_PERMS[code] ||= {read:false,create:false,update:false,delete:false,approve:false});
|
|
DEPT_PERMS[code][perm] = hdr.checked;
|
|
});
|
|
renderRows();
|
|
});
|
|
});
|
|
|
|
// 검색
|
|
document.querySelector('#btnSearch').addEventListener('click', ()=>{
|
|
const q = document.querySelector('#searchInput').value.trim().toLowerCase();
|
|
document.querySelectorAll('#permTable tbody tr').forEach(tr=>{
|
|
const name = tr.querySelector('td:first-child').innerText.toLowerCase();
|
|
const code = tr.dataset.code.toLowerCase();
|
|
tr.style.display = (q==='' || name.includes(q) || code.includes(q)) ? '' : 'none';
|
|
});
|
|
});
|
|
document.querySelector('#btnResetSearch').addEventListener('click', ()=>{
|
|
document.querySelector('#searchInput').value='';
|
|
document.querySelectorAll('#permTable tbody tr').forEach(tr=> tr.style.display='');
|
|
});
|
|
|
|
// 전체 허용/금지/초기화
|
|
document.querySelector('#btnAllAllow').addEventListener('click', ()=>{
|
|
PermissionMenu.activeCodes().forEach(code=>{
|
|
DEPT_PERMS[code] = {read:true,create:true,update:true,delete:true,approve:true};
|
|
});
|
|
renderRows();
|
|
});
|
|
document.querySelector('#btnAllDeny').addEventListener('click', ()=>{
|
|
PermissionMenu.activeCodes().forEach(code=>{
|
|
DEPT_PERMS[code] = {read:false,create:false,update:false,delete:false,approve:false};
|
|
});
|
|
renderRows();
|
|
});
|
|
document.querySelector('#btnClear').addEventListener('click', ()=>{
|
|
PermissionMenu.activeCodes().forEach(code=>{
|
|
DEPT_PERMS[code] = {read:false,create:false,update:false,delete:false,approve:false};
|
|
});
|
|
renderRows();
|
|
});
|
|
|
|
// 부서 변경 → 서버에서 권한 로드 (샘플)
|
|
document.querySelector('#deptSelect').addEventListener('change', function(){
|
|
const id = this.value;
|
|
alert('부서 권한 로드(샘플): GET /api/permission/department_get.php?dept_id='+id);
|
|
// 실제: AJAX로 DEPT_PERMS 갱신 후 renderRows();
|
|
});
|
|
|
|
// 저장
|
|
document.querySelector('#btnSave').addEventListener('click', ()=>{
|
|
const deptId = document.querySelector('#deptSelect').value;
|
|
const payload = {};
|
|
PermissionMenu.activeCodes().forEach(code=>{
|
|
payload[code] = DEPT_PERMS[code] || {read:false,create:false,update:false,delete:false,approve:false};
|
|
});
|
|
console.log('SAVE department permissions', {deptId, perms: payload});
|
|
alert('저장(샘플): POST /api/permission/department_save.php');
|
|
// $.post('/api/permission/department_save.php', { dept_id:deptId, data: JSON.stringify(payload) })
|
|
});
|
|
|
|
renderRows();
|
|
</script>
|
|
|
|
<?php include '../inc/footer.php'; ?>
|