Files
sam-api/public/tenant/assets/js/permission_menu.js
2025-08-10 02:36:50 +09:00

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,'&amp;').replace(/</g,'&lt;')
.replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
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 '&nbsp;'.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 };
})();