92 lines
3.5 KiB
JavaScript
92 lines
3.5 KiB
JavaScript
// /tenant/assets/js/permission_menu.js
|
|
// 공통: 메뉴 트리 데이터 관리 + 행 빌더 + 전파/요약 유틸
|
|
|
|
window.PermissionMenu = (function () {
|
|
let MENU_DATA = [];
|
|
let TENANT_USE = {}; // code:boolean (메뉴관리에서 활성화된 것만 권한 대상)
|
|
let byId = {}, byCode = {}, childrenMap = {};
|
|
|
|
// ---------- 내부 유틸 ----------
|
|
const esc = s => String(s)
|
|
.replace(/&/g,'&').replace(/</g,'<')
|
|
.replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''');
|
|
|
|
function rebuildMaps() {
|
|
const active = MENU_DATA.filter(n => !!TENANT_USE[n.code]);
|
|
byId = {}; byCode = {}; childrenMap = {};
|
|
active.forEach(n => { byId[n.id] = n; byCode[n.code] = n; });
|
|
active.forEach(n => {
|
|
const k = n.parent_id ?? 0;
|
|
(childrenMap[k] ??= []).push(n);
|
|
});
|
|
// 정렬
|
|
Object.keys(childrenMap).forEach(k => childrenMap[k].sort((a,b)=>a.title.localeCompare(b.title,'ko')));
|
|
}
|
|
|
|
function badgeSource(src) {
|
|
if (src === 'workflow') return '<span class="badge bg-warning text-dark ms-1">workflow</span>';
|
|
if (src === 'board') return '<span class="badge bg-success ms-1">board</span>';
|
|
return '<span class="badge bg-secondary ms-1">system</span>';
|
|
}
|
|
|
|
function indentOf(depth) {
|
|
return ' '.repeat(depth*4) + (depth ? '└ ' : '');
|
|
}
|
|
|
|
// ---------- 외부 API ----------
|
|
/**
|
|
* 데이터 세팅 (페이지 최초 1회)
|
|
* @param {Array} menuData - [{id,parent_id,title,code,path,source}]
|
|
* @param {Object} tenantUse - { code: true/false }
|
|
*/
|
|
function setData(menuData, tenantUse) {
|
|
MENU_DATA = Array.isArray(menuData) ? menuData : [];
|
|
TENANT_USE = tenantUse || {};
|
|
rebuildMaps();
|
|
}
|
|
|
|
/**
|
|
* 트리 순회하며 행을 만든다.
|
|
* @param {Function} rowBuilder - (node, depth) => string (추가 컬럼 HTML)
|
|
* @returns {string} HTML rows
|
|
*
|
|
* 기본 1열(메뉴명/배지/URL)은 공통이 그려주고,
|
|
* 나머지 열들은 rowBuilder가 반환한 HTML을 붙인다.
|
|
*/
|
|
function buildRows(rowBuilder) {
|
|
function walk(parentId = null, depth = 0, acc = []) {
|
|
const list = childrenMap[parentId ?? 0] || [];
|
|
for (const n of list) {
|
|
const titleCell = `
|
|
<td>
|
|
<span class="indent">${indentOf(depth)}</span>
|
|
<span class="menu-name">${esc(n.title)}</span>
|
|
${badgeSource(n.source || 'system')}
|
|
${n.path ? `<span class="menu-url ms-2 text-muted">${esc(n.path)}</span>` : ''}
|
|
</td>`;
|
|
const rest = rowBuilder ? rowBuilder(n, depth) : '';
|
|
acc.push(`<tr data-id="${n.id}" data-code="${esc(n.code)}" data-parent="${n.parent_id ?? ''}">${titleCell}${rest}</tr>`);
|
|
walk(n.id, depth + 1, acc);
|
|
}
|
|
return acc;
|
|
}
|
|
return walk(null, 0, []).join('');
|
|
}
|
|
|
|
// 전파/요약에 필요한 헬퍼들을 노출 (페이지별 권한 UI에서 사용)
|
|
function childrenOf(code) {
|
|
const node = byCode[code];
|
|
return node ? (childrenMap[node.id] || []) : [];
|
|
}
|
|
function parentOf(code) {
|
|
const node = byCode[code];
|
|
return (node && node.parent_id) ? byId[node.parent_id] : null;
|
|
}
|
|
function activeCodes() {
|
|
return Object.keys(byCode);
|
|
}
|
|
function maps() { return { byId, byCode, childrenMap }; }
|
|
|
|
return { setData, buildRows, childrenOf, parentOf, activeCodes, maps };
|
|
})();
|