const App = { navItems: [ { id: 'dashboard', label: '控制台', icon: 'fa-chart-simple', roles: ['owner','admin','player'] }, { id: 'unclaimed', label: '待处理工单', icon: 'fa-inbox', roles: ['owner','admin'] }, { id: 'tickets', label: '工单列表', icon: 'fa-ticket-alt', roles: ['owner','admin','player'] }, { id: 'tracking', label: '追踪工单', icon: 'fa-search', roles: ['owner','admin','player'] }, { id: 'users', label: '用户管理', icon: 'fa-users', roles: ['owner','admin'] }, { id: 'notifications', label: '通知配置', icon: 'fa-bell', roles: ['owner','admin'] }, { id: 'templates', label: '邮件模板', icon: 'fa-envelope', roles: ['owner','admin'] }, { id: 'settings', label: '系统设置', icon: 'fa-cog', roles: ['owner','admin'] }, { id: 'export', label: '数据导出', icon: 'fa-download', roles: ['owner'] }, ], init() { window.addEventListener('hashchange', () => this.route()); this.route(); }, afterLogin() { location.hash = '#/dashboard'; }, logout() { Auth.logout(); }, closeModal() { document.getElementById('modal-overlay').classList.add('hidden'); }, navigate(page, param) { location.hash = `#/${page}` + (param ? `/${param}` : ''); }, async route() { const hash = location.hash || '#/home'; const parts = hash.replace('#/', '').split('/'); const page = parts[0]; const param = parts.slice(1).join('/'); if (page !== 'install') { try { const s = await API.get('/install/status'); if (!s.installed) { location.hash = '#/install'; return; } } catch { location.hash = '#/install'; return; } } if (page === 'verify') { const params = new URLSearchParams((location.hash.split('?')[1] || '')); const token = params.get('token'); if (token) { API.get(`/auth/verify-email?token=${token}`).then(r => { alert(r.message || '验证成功'); location.hash = '#/login'; }).catch(e => { alert(e.message); location.hash = '#/home'; }); } return; } if (page === 'install') { this.showPublic('install'); return; } if (page === 'home' || page === 'login' || page === 'register') { this.showPublic(page); return; } if (!Auth.logged()) { location.hash = '#/login'; return; } this.showLayout(); this.renderSidebar(page); switch (page) { case 'dashboard': this.renderMain('控制台', Dashboard, param); break; case 'unclaimed': this.renderMain('待处理工单', UnclaimedPage, param); break; case 'tickets': if (param === 'create') { const q = new URLSearchParams((location.hash.split('?')[1] || '')); document.getElementById('page-title').textContent = '创建工单'; document.getElementById('page-actions').innerHTML = ''; TicketCreate.render(document.getElementById('page-content'), { type: q.get('type') || 'report', prefill: { game_name: Auth.user()?.game_name, game_uid: Auth.user()?.game_uid }, backLink: "App.navigate('tickets')" }); } else this.renderMain('工单列表', TicketsPage, param); break; case 'ticket': this.renderMain('工单详情', TicketDetail, param); break; case 'tracking': this.renderMain('追踪工单', TrackingPage, param); break; case 'users': this.renderMain('用户管理', UsersPage, param); break; case 'notifications': this.renderMain('通知配置', NotificationsPage, param); break; case 'templates': this.renderMain('邮件模板', TemplatesPage, param); break; case 'settings': this.renderMain('系统设置', SettingsPage, param); break; case 'export': this.renderMain('数据导出', ExportPage, param); break; default: this.renderMain('控制台', Dashboard, param); } }, showPublic(page) { document.getElementById('main-layout').classList.add('hidden'); const pub = document.getElementById('public-page'); pub.classList.remove('hidden'); const comp = page === 'login' ? LoginPage : page === 'register' ? RegisterPage : page === 'install' ? InstallPage : HomePage; comp.render().then(html => { pub.innerHTML = html; if (comp.mount) comp.mount(); }); }, showLayout() { document.getElementById('public-page').classList.add('hidden'); document.getElementById('main-layout').classList.remove('hidden'); const u = Auth.user(); document.getElementById('user-info').innerHTML = `
${(u.game_name||u.username).charAt(0).toUpperCase()}
${u.game_name||u.username}
${U.ROLE[u.role]}
`; }, renderSidebar(current) { const u = Auth.user(); document.getElementById('sidebar-nav').innerHTML = this.navItems .filter(i => i.roles.includes(u.role)) .map(i => ``).join(''); }, async renderMain(title, page, param) { document.getElementById('page-title').textContent = title; document.getElementById('page-actions').innerHTML = ''; const ct = document.getElementById('page-content'); ct.innerHTML = await page.render(param); if (page.mount) await page.mount(param); } }; document.addEventListener('DOMContentLoaded', () => App.init());