File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
export interface SystemMenu {
id: string;
parentId: string | null;
name: string;
idPath: string | null;
url: string | null;
roleName: string | null;
sortOrder: number;
isActive: boolean;
isTopActive: boolean;
isDefault: boolean;
largeIconId: string | null;
largeIconName: string | null;
largeIconUrl: string | null;
smallIconId: string | null;
smallIconName: string | null;
smallIconUrl: string | null;
}
export interface SystemMenuNode extends SystemMenu {
depth: number;
children: SystemMenuNode[];
}
export const EMPTY_FIELD_PLACEHOLDER = '-';
export function formatOptionalValue(
value: string | number | null | undefined
): string {
return value === null || value === undefined || value === ''
? EMPTY_FIELD_PLACEHOLDER
: String(value);
}
export function formatActiveLabel(isActive: boolean): string {
return isActive ? '활성' : '비활성';
}
/**
* 평면 목록을 트리로 세운다. 부모가 목록에 없는 항목(권한 때문에 잘려 온 가지 등)은 뿌리로
* 올려 화면에서 사라지지 않게 한다.
*/
export function buildMenuTree(items: SystemMenu[]): SystemMenuNode[] {
const byId = new Map<string, SystemMenuNode>();
for (const item of items) {
byId.set(item.id, { ...item, depth: 0, children: [] });
}
const roots: SystemMenuNode[] = [];
for (const node of byId.values()) {
const parent = node.parentId ? byId.get(node.parentId) : undefined;
if (parent && parent !== node) {
parent.children.push(node);
} else {
roots.push(node);
}
}
function order(nodes: SystemMenuNode[], depth: number): SystemMenuNode[] {
nodes.sort((a, b) => a.sortOrder - b.sortOrder || a.id.localeCompare(b.id));
for (const node of nodes) {
node.depth = depth;
order(node.children, depth + 1);
}
return nodes;
}
return order(roots, 0);
}
/** 펼쳐진 가지만 위에서부터 한 줄씩 늘어놓는다. */
export function flattenOpenNodes(
nodes: SystemMenuNode[],
isOpen: (id: string) => boolean
): SystemMenuNode[] {
const rows: SystemMenuNode[] = [];
function walk(list: SystemMenuNode[]) {
for (const node of list) {
rows.push(node);
if (node.children.length > 0 && isOpen(node.id)) {
walk(node.children);
}
}
}
walk(nodes);
return rows;
}
export function collectNodeIds(nodes: SystemMenuNode[]): string[] {
const ids: string[] = [];
function walk(list: SystemMenuNode[]) {
for (const node of list) {
if (node.children.length > 0) {
ids.push(node.id);
walk(node.children);
}
}
}
walk(nodes);
return ids;
}
/**
* 새로 만들 메뉴의 `menuIdPath` — 조상 경로다(부모 경로 + 부모 id).
*
* 백엔드는 이 값을 받아 저장만 하고 만들어 주지 않는다(등록 SQL이 `IFNULL(#{menuIdPath}, '/')`).
* 규격을 정한 곳이 없어 뿌리는 `/`, 그 아래는 `/3`, `/3/18`로 쌓는 형식을 쓴다.
*/
export function buildChildIdPath(parent: SystemMenu | null): string {
if (!parent) {
return '/';
}
const base = parent.idPath && parent.idPath !== '/' ? parent.idPath : '';
return `${base}/${parent.id}`;
}
/** 수정·추가 팝업의 상위메뉴명 표시 — 기획의 `[3] 고객센터` 형식. */
export function formatParentLabel(parent: SystemMenu | null): string {
return parent ? `[${parent.id}] ${parent.name}` : '';
}