/** * TalkingQ Platform - App Core * Router, sidebar, topbar clock, toast notifications */ // ─── Toast ────────────────────────────────────────────────────────────────── const Toast = (() => { let container = null; function getContainer() { if (!container) { container = document.createElement('div'); container.className = 'toast-container'; document.body.appendChild(container); } return container; } function show(message, type = 'success', duration = 3000) { const icons = { success: 'check-circle', error: 'x-circle', warning: 'alert-triangle', info: 'info' }; const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.innerHTML = ` ${message} `; getContainer().appendChild(toast); lucide.createIcons({ elements: [toast] }); setTimeout(() => { toast.style.opacity = '0'; toast.style.transform = 'translateX(100%)'; toast.style.transition = 'all 0.3s'; setTimeout(() => toast.remove(), 300); }, duration); } return { show, success: (m, duration) => show(m, 'success', duration), error: (m, duration) => show(m, 'error', duration), warning: (m, duration) => show(m, 'warning', duration), info: (m, duration) => show(m, 'info', duration), }; })(); // ─── Router ────────────────────────────────────────────────────────────────── const pageNames = { 'login': '登录', 'dashboard': '数据仪表盘', 'agent-dev': '对话应用配置', 'agent-manage': '应用管理', 'device': '设备管理', 'ota': 'OTA 升级', 'session': '会话详情', 'badcase': 'Bad Case 管理', 'rbac': '权限管理 (RBAC)', 'privacy': '隐私保护 (PII)', 'settings': '系统设置', 'knowledge-base': '知识库管理', 'kb-create': '创建知识库', 'kb-detail': '知识库详情', }; const pageRenderers = {}; function registerPage(id, renderer) { pageRenderers[id] = renderer; } function navigateTo(pageId) { // Auth check - login page doesn't need auth if (pageId !== 'login' && !checkAuth()) { return; } const sidebar = document.getElementById('sidebar'); const topbar = document.querySelector('.topbar'); const mainContent = document.getElementById('mainContent'); const container = document.getElementById('pageContainer'); // Handle login page - full screen, hide sidebar and topbar if (pageId === 'login') { document.body.classList.add('login-mode'); if (pageRenderers[pageId]) { container.innerHTML = `
`; pageRenderers[pageId](document.getElementById(`page-${pageId}`)); } return; } // Restore normal layout for other pages document.body.classList.remove('login-mode'); // Update nav document.querySelectorAll('.nav-item').forEach(el => { el.classList.toggle('active', el.dataset.page === pageId); }); // Update breadcrumb const breadcrumb = document.getElementById('breadcrumb'); if (breadcrumb) breadcrumb.textContent = pageNames[pageId] || pageId; // Render page if (!container) return; if (pageRenderers[pageId]) { container.innerHTML = `
`; pageRenderers[pageId](document.getElementById(`page-${pageId}`)); } else { container.innerHTML = `

功能开发中

该页面正在建设中,敬请期待

`; lucide.createIcons(); } } // ─── Topbar Clock ──────────────────────────────────────────────────────────── function updateClock() { const el = document.getElementById('topbarTime'); if (!el) return; const now = new Date(); el.textContent = now.toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); } // ─── Sidebar Toggle ────────────────────────────────────────────────────────── function initSidebar() { const sidebar = document.getElementById('sidebar'); const toggleBtn = document.getElementById('sidebarToggle'); const menuBtn = document.getElementById('menuBtn'); toggleBtn?.addEventListener('click', () => { sidebar.classList.toggle('collapsed'); }); menuBtn?.addEventListener('click', () => { if (window.innerWidth <= 768) { sidebar.classList.toggle('mobile-open'); } else { sidebar.classList.toggle('collapsed'); } }); } // ─── Nav Click ─────────────────────────────────────────────────────────────── function initNav() { document.querySelectorAll('.nav-item[data-page]').forEach(el => { el.addEventListener('click', e => { e.preventDefault(); navigateTo(el.dataset.page); if (window.innerWidth <= 768) { document.getElementById('sidebar')?.classList.remove('mobile-open'); } }); }); } // ─── Modal Helper ──────────────────────────────────────────────────────────── function createModal({ title, content, onConfirm, confirmText = '确认', cancelText = '取消', size = '', showCancel = true }) { const overlay = document.createElement('div'); overlay.className = 'modal-overlay'; const sizeClass = size ? ` ${String(size).startsWith('modal-') ? size : `modal-${size}`}` : ''; overlay.innerHTML = `
`; document.body.appendChild(overlay); document.body.classList.add('modal-open'); lucide.createIcons({ elements: [overlay] }); const close = () => { overlay.classList.remove('open'); setTimeout(() => { overlay.remove(); if (!document.querySelector('.modal-overlay')) { document.body.classList.remove('modal-open'); } }, 200); }; overlay.querySelector('.modal-close').addEventListener('click', close); overlay.querySelector('.cancel-btn')?.addEventListener('click', close); overlay.addEventListener('click', e => { if (e.target === overlay) close(); }); if (onConfirm) { overlay.querySelector('.confirm-btn').addEventListener('click', async () => { const confirmBtn = overlay.querySelector('.confirm-btn'); confirmBtn.disabled = true; try { const result = await onConfirm(overlay.querySelector('.modal-body')); if (result === false) return; close(); } finally { if (document.body.contains(overlay)) confirmBtn.disabled = false; } }); } requestAnimationFrame(() => overlay.classList.add('open')); return { overlay, close }; } // ─── Pagination Helper ─────────────────────────────────────────────────────── function renderPagination(container, { total, page, pageSize, onChange }) { const totalPages = Math.ceil(total / pageSize); container.innerHTML = `
共 ${total} 条 ${page} / ${totalPages}
`; container.querySelectorAll('[data-page]').forEach(btn => { btn.addEventListener('click', () => !btn.disabled && onChange(parseInt(btn.dataset.page))); }); } // ─── Init ──────────────────────────────────────────────────────────────────── document.addEventListener('DOMContentLoaded', () => { initSidebar(); initNav(); updateClock(); setInterval(updateClock, 1000); lucide.createIcons(); // Check auth and navigate const loggedIn = sessionStorage.getItem('talkingq_logged_in'); const targetPage = loggedIn === 'true' ? 'dashboard' : 'login'; // Use a small delay to ensure DOM is fully ready setTimeout(() => { navigateTo(targetPage); }, 50); }); // Auth check helper function checkAuth() { return sessionStorage.getItem('talkingq_logged_in') === 'true'; } // Logout helper function doLogout() { sessionStorage.removeItem('talkingq_user'); sessionStorage.removeItem('talkingq_logged_in'); Toast.info('已退出登录'); setTimeout(() => { navigateTo('login'); }, 300); } window.Toast = Toast; window.navigateTo = navigateTo; window.registerPage = registerPage; window.createModal = createModal; window.renderPagination = renderPagination; window.checkAuth = checkAuth;