// js/ui.js - 管理所有与UI渲染和DOM操作相关的函数 // 集中管理所有需要操作的DOM元素 export const DOMElements = { sidebar: document.getElementById('sidebar'), menuToggleBtn: document.getElementById('menu-toggle-btn'), mainContent: document.querySelector('.main-content'), configListPanel: document.getElementById('config-list-panel'), configFormPanel: document.getElementById('config-form-panel'), renderedOutputPanel: document.getElementById('rendered-output-panel'), configForm: document.getElementById('config-form'), formTitle: document.getElementById('form-title'), backToListBtn: document.getElementById('back-to-list-btn'), domainInput: document.getElementById('domain'), originalFilenameInput: document.getElementById('original-filename'), headersContainer: document.getElementById('headers-container'), addNewConfigBtn: document.getElementById('add-new-config-btn'), cancelEditBtn: document.getElementById('cancel-edit-btn'), addHeaderBtn: document.getElementById('add-header-btn'), configListContainer: document.getElementById('config-list'), renderedContentCode: document.getElementById('rendered-content'), toastContainer: document.getElementById('toast-container'), dialogContainer: document.getElementById('dialog-container'), themeToggleInput: document.getElementById('theme-toggle-input'), caddyStatusIndicator: document.getElementById('caddy-status-indicator'), caddyActionButtonContainer: document.getElementById('caddy-action-button-container'), logoutBtn: document.getElementById('logout-btn'), upstreamFieldset: document.getElementById('upstream-fieldset'), fileserverFieldset: document.getElementById('fileserver-fieldset'), upstreamHeadersContainer: document.getElementById('upstream-headers-container'), addUpstreamHeaderBtn: document.getElementById('add-upstream-header-btn'), }; export function switchView(viewToShow) { [DOMElements.configListPanel, DOMElements.configFormPanel, DOMElements.renderedOutputPanel] .forEach(view => view.classList.add('hidden')); if (viewToShow) viewToShow.classList.remove('hidden'); DOMElements.addNewConfigBtn.disabled = (viewToShow === DOMElements.configFormPanel); } export function renderConfigList(filenames) { DOMElements.configListContainer.innerHTML = ''; if (!filenames || filenames.length === 0) { DOMElements.configListContainer.innerHTML = '

还没有任何配置,请创建一个。

'; return; } filenames.forEach(filename => { const item = document.createElement('li'); item.className = 'config-item'; item.dataset.filename = filename; item.innerHTML = `${filename}
`; DOMElements.configListContainer.appendChild(item); }); } export function createCustomSelect(containerId, options, onSelect) { const container = document.getElementById(containerId); container.innerHTML = `
`; const selectedDiv = container.querySelector('.select-selected'); const itemsDiv = container.querySelector('.select-items'); const hiddenInput = container.querySelector('input[type="hidden"]'); itemsDiv.innerHTML = ''; options.forEach((option, index) => { const item = document.createElement('div'); item.textContent = option; item.dataset.value = option; if (index === 0) { selectedDiv.textContent = option; hiddenInput.value = option; } item.addEventListener('click', function(e) { selectedDiv.textContent = this.textContent; hiddenInput.value = this.dataset.value; itemsDiv.classList.remove('select-show'); selectedDiv.classList.remove('select-arrow-active'); onSelect && onSelect(this.dataset.value); e.stopPropagation(); }); itemsDiv.appendChild(item); }); selectedDiv.addEventListener('click', (e) => { e.stopPropagation(); document.querySelectorAll('.select-items.select-show').forEach(openSelect => { if (openSelect !== itemsDiv) { openSelect.classList.remove('select-show'); openSelect.previousElementSibling.classList.remove('select-arrow-active'); } }); itemsDiv.classList.toggle('select-show'); selectedDiv.classList.toggle('select-arrow-active'); }); document.addEventListener('click', () => { itemsDiv.classList.remove('select-show'); selectedDiv.classList.remove('select-arrow-active'); }); } export function addKeyValueInput(container, keyName, valueName, key = '', value = '') { const div = document.createElement('div'); div.className = 'header-entry'; div.innerHTML = ` `; container.appendChild(div); } export function fillForm(config, originalFilename) { DOMElements.originalFilenameInput.value = originalFilename; DOMElements.domainInput.value = config.domain; document.getElementById('upstream').value = config.upstream?.upstream || ''; document.getElementById('file_dir_path').value = config.file_server?.file_dir_path || ''; document.getElementById('enable_browser').checked = config.file_server?.enable_browser || false; const selectContainer = document.getElementById('custom-select-tmpl'); selectContainer.querySelector('.select-selected').textContent = config.tmpl_type; selectContainer.querySelector('input[type="hidden"]').value = config.tmpl_type; DOMElements.headersContainer.innerHTML = ''; if (config.headers) Object.entries(config.headers).forEach(([k, v]) => v.forEach(val => addKeyValueInput(DOMElements.headersContainer, 'header_key', 'header_value', k, val))); DOMElements.upstreamHeadersContainer.innerHTML = ''; if (config.upstream?.upstream_headers) Object.entries(config.upstream.upstream_headers).forEach(([k, v]) => v.forEach(val => addKeyValueInput(DOMElements.upstreamHeadersContainer, 'upstream_header_key', 'upstream_header_value', k, val))); document.getElementById('enable_log').checked = config.log?.enable_log || false; document.getElementById('enable_error_page').checked = config.error_page?.enable_error_page || false; document.getElementById('enable_encode').checked = config.encode?.enable_encode || false; } export function showRenderedConfig(configs, filename) { const targetConfig = configs.find(c => c.filename === filename); if (targetConfig && targetConfig.rendered_content) { DOMElements.renderedContentCode.textContent = atob(targetConfig.rendered_content); DOMElements.renderedOutputPanel.classList.remove('hidden'); } else { DOMElements.renderedOutputPanel.classList.add('hidden'); } } function createButton(text, className, onClick) { const button = document.createElement('button'); button.className = `btn ${className}`; button.innerHTML = `${text}`; button.addEventListener('click', onClick); return button; } export function updateCaddyStatusView(status, handlers) { const { handleReloadCaddy, handleStopCaddy, handleStartCaddy } = handlers; const dot = DOMElements.caddyStatusIndicator.querySelector('.status-dot'); const text = DOMElements.caddyStatusIndicator.querySelector('.status-text'); const buttonContainer = DOMElements.caddyActionButtonContainer; dot.className = 'status-dot'; buttonContainer.innerHTML = ''; let statusText, dotClass; switch (status) { case 'running': statusText = '运行中'; dotClass = 'running'; buttonContainer.appendChild(createButton('重载配置', 'btn-warning', handleReloadCaddy)); buttonContainer.appendChild(createButton('停止 Caddy', 'btn-danger', handleStopCaddy)); break; case 'stopped': statusText = '已停止'; dotClass = 'stopped'; buttonContainer.appendChild(createButton('启动 Caddy', 'btn-success', handleStartCaddy)); break; case 'checking': statusText = '检查中...'; dotClass = 'checking'; break; default: statusText = '状态未知'; dotClass = 'error'; break; } text.textContent = statusText; dot.classList.add(dotClass); } export function updateFormVisibility(selectedTemplate, availableTemplates) { const showUpstream = selectedTemplate === 'reverse_proxy' && availableTemplates.includes('reverse_proxy'); const showFileServer = selectedTemplate === 'file_server' && availableTemplates.includes('file_server'); DOMElements.upstreamFieldset.classList.toggle('hidden', !showUpstream); DOMElements.fileserverFieldset.classList.toggle('hidden', !showFileServer); }