Files
MC_Report/public/js/pages/tickets.js

109 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const TicketsPage = {
tab: 'all',
filters: {},
async render() { return '<div class="loading"><i class="fas fa-spinner"></i></div>'; },
async mount() {
document.getElementById('page-title').textContent = 'å·¥å<C2A5>•列表';
document.getElementById('page-actions').innerHTML = `
<button class="btn btn-p btn-sm" onclick="App.navigate('tickets','create?type=report')"><i class="fas fa-flag"></i> 举报</button>
<button class="btn btn-s btn-sm" onclick="App.navigate('tickets','create?type=suggestion')"><i class="fas fa-lightbulb"></i> 建议</button>
`;
this.renderLayout();
await this.loadTab(this.tab);
},
renderLayout() {
const ct = document.getElementById('page-content');
const isStaff = Auth.isAdmin();
ct.innerHTML = `
<div class="tab-nav" id="ticket-tabs">
<button data-tab="all" class="active">全部</button>
<button data-tab="pending">待认�/button>
<button data-tab="claimed">已接å<C2A5>?/button>
${Auth.user().role === 'player' ? '<button data-tab="mine">æˆçš„å·¥å<C2A5>•</button>' : ''}
</div>
<div class="filter-bar">
<select id="ft-type" onchange="TicketsPage.applyFilters()"><option value="">全部类型</option>${Object.entries(U.TYPE).map(([k,v])=>`<option value="${k}">${v}</option>`).join('')}</select>
<select id="ft-status" onchange="TicketsPage.applyFilters()"><option value="">全部状�/option>${Object.entries(U.STATUS).map(([k,v])=>`<option value="${k}">${v}</option>`).join('')}</select>
<select id="ft-priority" onchange="TicketsPage.applyFilters()"><option value="">全部优先�/option>${Object.entries(U.PRIORITY).map(([k,v])=>`<option value="${k}">${v}</option>`).join('')}</select>
<input id="ft-search" placeholder="æ<>œç´¢..." onkeyup="if(event.key==='Enter')TicketsPage.applyFilters()">
<button class="btn btn-o btn-sm" onclick="TicketsPage.applyFilters()"><i class="fas fa-search"></i></button>
<button class="btn btn-o btn-sm" onclick="TicketsPage.resetFilters()"><i class="fas fa-undo"></i></button>
</div>
<div id="ticket-list"></div>
`;
document.querySelectorAll('#ticket-tabs button').forEach(btn => {
btn.onclick = () => {
document.querySelectorAll('#ticket-tabs button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
this.tab = btn.dataset.tab;
this.loadTab(this.tab);
};
});
},
async loadTab(tab) {
const list = document.getElementById('ticket-list');
U.loading(list);
try {
const params = new URLSearchParams();
if (this.filters.type) params.set('type', this.filters.type);
if (this.filters.status) params.set('status', this.filters.status);
if (this.filters.priority) params.set('priority', this.filters.priority);
if (this.filters.search) params.set('search', this.filters.search);
if (tab === 'pending') params.set('claimed', '0');
else if (tab === 'claimed') params.set('claimed', '1');
else if (tab === 'mine') params.set('status', '');
const qs = params.toString();
const tickets = await API.get('/tickets' + (qs ? '?' + qs : ''));
if (tab === 'mine') {
this.renderList(list, tickets);
return;
}
this.renderList(list, tickets);
} catch (e) { list.innerHTML = `<div class="alert alert-e">${e.message}</div>`; }
},
renderList(ct, tickets) {
ct.innerHTML = tickets.length === 0
? `<div class="empty"><i class="fas fa-inbox"></i><p>æšæ— å·¥å<C2A5>•</p></div>`
: `<div class="card table-wrap"><table><thead><tr>
<th>#</th><th>类型</th><th>标题</th><th>æ<><C3A6>交è€?/th><th>对象</th><th>状æ€?/th><th>优先çº?/th><th>è´Ÿè´£äº?/th><th>æ—¶é—´</th><th>æ“<C3A6>作</th>
</tr></thead><tbody>${tickets.map(t => `
<tr>
<td>${t.id}</td>
<td>${U.badge(t.type,'type')}</td>
<td><a href="#" onclick="App.navigate('ticket',${t.id});return false" style="color:var(--p);text-decoration:none;font-weight:600">${U.esc(t.title)}</a></td>
<td>${U.esc(t.reporter_game_name)}<br><span class="tm ts">UID:${U.esc(t.reporter_game_uid)}</span></td>
<td>${t.target_game_name?`${U.esc(t.target_game_name)}${t.target_game_uid?`<br><span class="tm ts">UID:${U.esc(t.target_game_uid)}</span>`:''}`:'-'}</td>
<td>${U.badge(t.status,'status')}</td>
<td>${U.badge(t.priority,'priority')}</td>
<td>${t.assignee_name?`<span class="badge bg-admin">${U.esc(t.assignee_name)}</span>`:`<span class="tm">待认�/span>`}</td>
<td class="ts">${U.date(t.updated_at)}</td>
<td><button class="btn btn-o btn-sm" onclick="App.navigate('ticket',${t.id})"><i class="fas fa-eye"></i></button></td>
</tr>`).join('')}</tbody></table></div>`;
},
applyFilters() {
this.filters = {
type: document.getElementById('ft-type').value,
status: document.getElementById('ft-status').value,
priority: document.getElementById('ft-priority').value,
search: document.getElementById('ft-search').value,
};
this.loadTab(this.tab);
},
resetFilters() {
this.filters = {};
document.querySelectorAll('.filter-bar select, .filter-bar input').forEach(el => { if (el.tagName === 'SELECT' || el.tagName === 'INPUT') el.value = ''; });
this.loadTab(this.tab);
}
};