Files
MC_Report/public/js/app.js

121 lines
6.0 KiB
JavaScript

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() {
this.loadFooter();
window.addEventListener('hashchange', () => this.route());
this.route();
},
async loadFooter() {
try {
const s = await API.get('/settings/settings');
const cr = document.getElementById('footer-copyright');
const icp = document.getElementById('footer-icp');
if (cr && s.copyright) cr.innerHTML = s.copyright.value;
if (icp && s.icp && s.icp.value) icp.innerHTML = `<a href="https://beian.miit.gov.cn" target="_blank">${s.icp.value}</a>`;
} catch {}
},
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') { this.showPublic('home', param); return; }
if (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 = '<button class="btn btn-o btn-sm" onclick="App.navigate(\'tickets\')"><i class="fas fa-arrow-left"></i> 返回</button>';
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, param) {
document.getElementById('top-auth').classList.remove('hidden');
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;
Promise.resolve(comp.render(param)).then(html => { pub.innerHTML = html; if (comp.mount) comp.mount(param); });
},
showLayout() {
document.getElementById('top-auth').classList.add('hidden');
document.getElementById('public-page').classList.add('hidden');
document.getElementById('main-layout').classList.remove('hidden');
const u = Auth.user();
document.getElementById('user-info').innerHTML = `<div class="av">${(u.game_name||u.username).charAt(0).toUpperCase()}</div><div class="dt"><div class="nm">${u.game_name||u.username}</div><div class="rl">${U.ROLE[u.role]}</div></div>`;
},
renderSidebar(current) {
const u = Auth.user();
document.getElementById('sidebar-nav').innerHTML = this.navItems
.filter(i => i.roles.includes(u.role))
.map(i => `<div class="nav-item ${current===i.id?'active':''}" onclick="App.navigate('${i.id}')"><i class="fas ${i.icon}"></i><span>${i.label}</span></div>`).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());