/** * UI控制器 - 管理模板切换和用户界面交互 */ class UIController { constructor() { this.currentTable = null; this.currentTemplate = 'list'; this.availableTemplates = []; this.isLoading = false; this.searchTimeout = null; this.init(); } /** * 初始化UI控制器 */ init() { this.bindEvents(); this.setupKeyboardShortcuts(); this.setupResponsiveUI(); this.loadFromURL(); } /** * 绑定事件监听器 */ bindEvents() { // 表选择器 const tableSelector = document.getElementById('tableSelector'); if (tableSelector) { tableSelector.addEventListener('change', (e) => { this.changeTable(e.target.value); }); } // 搜索框 const searchInput = document.getElementById('searchInput'); if (searchInput) { searchInput.addEventListener('input', (e) => { this.handleSearch(e.target.value); }); } // 分页 document.addEventListener('click', (e) => { if (e.target.classList.contains('pagination-link')) { e.preventDefault(); const page = parseInt(e.target.dataset.page); this.goToPage(page); } }); // 排序 document.addEventListener('click', (e) => { if (e.target.classList.contains('sort-header')) { e.preventDefault(); const field = e.target.dataset.field; this.toggleSort(field); } }); // 模板切换 document.addEventListener('click', (e) => { if (e.target.classList.contains('template-switch')) { e.preventDefault(); const template = e.target.dataset.template; this.switchTemplate(template); } }); // 刷新按钮 const refreshBtn = document.getElementById('refreshBtn'); if (refreshBtn) { refreshBtn.addEventListener('click', () => { this.refreshData(); }); } // 浏览器历史记录 window.addEventListener('popstate', () => { this.loadFromURL(); }); } /** * 设置键盘快捷键 */ setupKeyboardShortcuts() { document.addEventListener('keydown', (e) => { // Ctrl/Cmd + R: 刷新 if ((e.ctrlKey || e.metaKey) && e.key === 'r') { e.preventDefault(); this.refreshData(); } // Ctrl/Cmd + K: 聚焦搜索 if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); const searchInput = document.getElementById('searchInput'); if (searchInput) searchInput.focus(); } // Escape: 关闭详情弹窗 if (e.key === 'Escape') { this.closeModal(); } }); } /** * 设置响应式UI */ setupResponsiveUI() { // 监听窗口大小变化 let resizeTimeout; window.addEventListener('resize', () => { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { this.adjustUILayout(); }, 250); }); } /** * 调整UI布局 */ adjustUILayout() { const width = window.innerWidth; const container = document.getElementById('mainContent'); if (!container) return; if (width < 768) { container.classList.add('mobile-layout'); container.classList.remove('desktop-layout'); } else { container.classList.add('desktop-layout'); container.classList.remove('mobile-layout'); } } /** * 从URL加载配置 */ loadFromURL() { const url = new URL(window.location); const table = url.searchParams.get('table'); const template = url.searchParams.get('template') || 'list'; const page = parseInt(url.searchParams.get('page')) || 1; const search = url.searchParams.get('search') || ''; if (table) { this.currentTable = table; this.currentTemplate = template; this.loadTableData(table, { page, search, template }); } } /** * 切换数据表 * @param {string} tableName - 表名 */ async changeTable(tableName) { if (this.isLoading) return; this.currentTable = tableName; this.currentTemplate = 'list'; // 重置为默认模板 try { // 并行获取可用模板和预加载模板 const [templates] = await Promise.all([ apiClient.getAvailableTemplates(tableName), templateEngine.preloadTemplates(tableName, ['list', 'card']) ]); this.availableTemplates = templates.templates; this.renderTemplateSelector(); // 加载数据 await this.loadTableData(tableName); // 更新URL this.updateURL(); } catch (error) { console.error('Failed to change table:', error); this.showError('切换数据表失败:' + error.message); } } /** * 切换模板 * @param {string} templateName - 模板名称 */ async switchTemplate(templateName) { if (this.isLoading || !this.currentTable) return; this.currentTemplate = templateName; try { await templateEngine.switchTemplate(templateName); this.updateURL(); this.updateTemplateSelector(templateName); } catch (error) { console.error('Failed to switch template:', error); this.showError('切换模板失败:' + error.message); } } /** * 加载数据表数据 * @param {string} tableName - 表名 * @param {Object} options - 选项 */ async loadTableData(tableName, options = {}) { const { page = 1, perPage = 20, search = '', sort = '', order = 'desc', template = this.currentTemplate } = options; this.showLoading(); try { const rendered = await templateEngine.renderTable(tableName, { page, perPage, search, sort, order, template }); this.updateContent(rendered); this.renderPagination({ page, perPage, total: templateEngine.currentData?.total || 0 }); } catch (error) { console.error('Failed to load table data:', error); this.showError('加载数据失败:' + error.message); } finally { this.hideLoading(); } } /** * 渲染模板选择器 */ renderTemplateSelector() { const container = document.getElementById('templateSelector'); if (!container) return; container.innerHTML = `
${message}