// --- static/tree-ui.js --- // ========================================== // 🌟 輔助函數:全部展開與全部收合 // ========================================== export function expandAll(elementId) { const details = document.getElementById(`details-${elementId}`); if (details) { details.open = true; // 展開自己 const subDetails = details.querySelectorAll('details'); subDetails.forEach(d => d.open = true); // 展開所有子項目 } } export function collapseAll(elementId) { const details = document.getElementById(`details-${elementId}`); if (details) { const subDetails = details.querySelectorAll('details'); subDetails.forEach(d => d.open = false); // 收合所有子項目 details.open = false; // 收合自己 } } // ========================================== // 🌟 核心函數:生成完整設備配置樹狀圖 // ========================================== export function buildTree(node, path = '', isParentCommandGroup = false) { let html = ''; if (!node || typeof node !== 'object') return html; for (const [key, value] of Object.entries(node)) { const currentPath = path ? `${path}::${key}` : key; let cliCommand = currentPath.replace(/::/g, ' ').replace(/\s*\[\d+\]/g, ''); if (typeof value === 'object' && value !== null) { const hasNestedObject = Object.values(value).some(v => typeof v === 'object' && v !== null); const isCommandGroup = isParentCommandGroup || !hasNestedObject; const elementId = `folder-${currentPath.replace(/[^a-zA-Z0-9]/g, '-')}`; const folderSuffix = isCommandGroup ? '(指令群組)' : ''; const svgFolderClosed = ``; const svgFolderOpen = ``; const svgGroupClosed = ``; const svgGroupOpen = ``; const iconClosed = isCommandGroup ? svgGroupClosed : svgFolderClosed; const iconOpen = isCommandGroup ? svgGroupOpen : svgFolderOpen; const expandAllIcon = ``; const collapseAllIcon = ``; const expandCollapseBtns = ` ${expandAllIcon} ${collapseAllIcon} `; html += `
${iconClosed} ${key}${folderSuffix} ${expandCollapseBtns}
✏️
${buildTree(value, currentPath, isCommandGroup)}
`; } else { const isVirtualIndex = /^\[\d+\]$/.test(key); const isNoCommand = (key === 'no'); const safeValue = (value === null ? '' : value).toString().replace(/"/g, """); const elementId = `leaf-${currentPath.replace(/[^a-zA-Z0-9]/g, '-')}`; if (isNoCommand) { let baseCmd = cliCommand.replace(/(^|\s)no$/, '').trim(); cliCommand = baseCmd ? `${baseCmd} no ${safeValue}` : `no ${safeValue}`; } const svgLeaf = ``; const svgDisabled = ``; const iconHtml = (icon) => `${icon}`; const valueStyle = `color: #16a085; margin-left: 5px; font-family: Courier New, monospace; display: inline-block; transform: translateY(1.5px);`; let displayContent = ''; if (isVirtualIndex) { displayContent = ` ${iconHtml(svgLeaf)} ${safeValue} `; } else if (isNoCommand) { displayContent = ` ${iconHtml(svgDisabled)} no: ${safeValue} `; } else { displayContent = ` ${iconHtml(svgLeaf)} ${key}: ${safeValue} `; } html += `
${displayContent}
✏️
`; } } return html; } // ========================================== // 🌟 核心函數:生成系統設定的過濾樹狀圖 // ========================================== export function buildRealFilterTree(data, parentPath = '', hiddenKeys = [], isParentCommandGroup = false) { if (typeof data !== 'object' || data === null) return ''; let html = `
`; for (const key in data) { const value = data[key]; const currentPath = parentPath ? `${parentPath}::${key}` : key; const isChecked = hiddenKeys.includes(currentPath) ? "checked" : ""; let cliCommand = currentPath.replace(/::/g, ' ').replace(/\s*\[\d+\]/g, ''); const svgFolderClosed = ``; const svgFolderOpen = ``; const svgGroupClosed = ``; const svgGroupOpen = ``; const svgLeaf = ``; const isFolder = typeof value === 'object' && value !== null && Object.keys(value).length > 0; const onChangeEvent = isFolder ? 'toggleChildCheckboxes(this)' : 'updateParentCheckboxState(this)'; const checkboxHtml = ``; if (isFolder) { const hasNestedObject = Object.values(value).some(v => typeof v === 'object' && v !== null); const isCommandGroup = isParentCommandGroup || !hasNestedObject; const elementId = `filter-folder-${currentPath.replace(/[^a-zA-Z0-9]/g, '-')}`; const iconClosed = isCommandGroup ? svgGroupClosed : svgFolderClosed; const iconOpen = isCommandGroup ? svgGroupOpen : svgFolderOpen; const folderSuffix = isCommandGroup ? `(指令群組)` : ''; const expandAllIcon = ``; const collapseAllIcon = ``; const expandCollapseBtns = ` ${expandAllIcon} ${collapseAllIcon} `; html += `
${checkboxHtml} ${iconClosed} ${key}${folderSuffix} ${expandCollapseBtns}
${buildRealFilterTree(value, currentPath, hiddenKeys, isCommandGroup)}
`; } else { const isVirtualIndex = /^\[\d+\]$/.test(key); let displayName = isVirtualIndex ? `項目 ${key}` : `${key}`; const safeValue = (value === null ? '' : value).toString().replace(/"/g, """); const isNoCommand = (key === 'no'); let valueHtml = `${safeValue}`; let colonHtml = `:`; let currentIcon = ``; if (isNoCommand) { let baseCmd = cliCommand.replace(/(^|\s)no$/, '').trim(); cliCommand = baseCmd ? `${baseCmd} no ${safeValue}` : `no ${safeValue}`; displayName = `no`; colonHtml = `:`; valueHtml = `${safeValue}`; currentIcon = ``; } html += `
${checkboxHtml} ${currentIcon} ${displayName}${colonHtml} ${valueHtml}
`; } } html += '
'; return html; }