/** * 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 = `
视图: ${this.availableTemplates.map(template => ` `).join('')}
`; } /** * 更新模板选择器状态 * @param {string} activeTemplate - 当前激活的模板 */ updateTemplateSelector(activeTemplate) { const buttons = document.querySelectorAll('.template-switch'); buttons.forEach(btn => { if (btn.dataset.template === activeTemplate) { btn.className = 'template-switch px-3 py-1 text-sm rounded bg-blue-500 text-white'; } else { btn.className = 'template-switch px-3 py-1 text-sm rounded bg-gray-200 text-gray-700 hover:bg-gray-300'; } }); } /** * 获取模板显示名称 * @param {string} template - 模板名称 */ getTemplateDisplayName(template) { const names = { 'list': '列表', 'card': '卡片', 'timeline': '时间轴' }; return names[template] || template; } /** * 渲染分页 * @param {Object} pagination - 分页信息 */ renderPagination({ page, perPage, total }) { const container = document.getElementById('pagination'); if (!container) return; const pages = Math.ceil(total / perPage); const startPage = Math.max(1, page - 2); const endPage = Math.min(pages, page + 2); let html = `
共 ${total} 条记录,第 ${page} / ${pages} 页
`; if (page > 1) { html += ` `; } for (let i = startPage; i <= endPage; i++) { html += ` `; } if (page < pages) { html += ` `; } html += `
`; container.innerHTML = html; } /** * 处理搜索 * @param {string} query - 搜索关键词 */ handleSearch(query) { clearTimeout(this.searchTimeout); this.searchTimeout = setTimeout(() => { if (this.currentTable) { this.loadTableData(this.currentTable, { search: query, page: 1 }); this.updateURL(); } }, 300); } /** * 跳转到指定页面 * @param {number} page - 页码 */ goToPage(page) { if (this.currentTable) { this.loadTableData(this.currentTable, { page }); this.updateURL(); } } /** * 切换排序 * @param {string} field - 排序字段 */ toggleSort(field) { const url = new URL(window.location); const currentSort = url.searchParams.get('sort'); const currentOrder = url.searchParams.get('order') || 'asc'; let newOrder = 'asc'; if (currentSort === field && currentOrder === 'asc') { newOrder = 'desc'; } if (this.currentTable) { this.loadTableData(this.currentTable, { sort: field, order: newOrder, page: 1 }); this.updateURL(); } } /** * 刷新数据 */ async refreshData() { if (this.currentTable) { templateEngine.clearCache(); await this.loadTableData(this.currentTable); } } /** * 更新URL参数 */ updateURL() { const url = new URL(window.location); url.searchParams.set('table', this.currentTable); url.searchParams.set('template', this.currentTemplate); const currentOptions = this.getCurrentOptions(); if (currentOptions.page > 1) { url.searchParams.set('page', currentOptions.page); } else { url.searchParams.delete('page'); } if (currentOptions.search) { url.searchParams.set('search', currentOptions.search); } else { url.searchParams.delete('search'); } window.history.pushState({}, '', url); } /** * 显示加载状态 */ showLoading() { this.isLoading = true; const container = document.getElementById('mainContent'); if (container) { container.innerHTML = `
加载中...
`; } } /** * 隐藏加载状态 */ hideLoading() { this.isLoading = false; } /** * 更新内容 * @param {string} content - 内容HTML */ updateContent(content) { const container = document.getElementById('mainContent'); if (container) { container.innerHTML = content; } } /** * 显示错误消息 * @param {string} message - 错误消息 */ showError(message) { const container = document.getElementById('mainContent'); if (container) { container.innerHTML = `

错误

${message}

`; } } /** * 关闭模态框 */ closeModal() { const modal = document.getElementById('detailModal'); if (modal) { modal.classList.add('hidden'); } } } // 全局UI控制器实例 window.uiController = new UIController();