- logout() on home/root page: Auth.reset() only (clear state, refresh top bar), no redirect to login; other pages still go to login - home page container: width:min(50vw+300px, 1440px, 96vw) replaces fixed max-width:800px - PC wide screens use at least half viewport, mobile near full width
81 lines
5.2 KiB
JavaScript
81 lines
5.2 KiB
JavaScript
/*
|
|
* MC Report System
|
|
* Copyright (C) 2026 Sea Network Technology Studio
|
|
* Author: CangLan <admin@sea-studio.top>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published
|
|
* by the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const HomePage = {
|
|
async render(tab) {
|
|
const t = tab || 'report';
|
|
// PC 宽屏自适应: 至少占视口一半, 大屏上限 1440px; 手机端接近全宽
|
|
return `<div id="home-content" style="width:min(50vw + 300px, 1440px, 96vw);margin:0 auto;padding:30px 20px">
|
|
<div class="tab-nav">
|
|
<button class="${t==='report'?'active':''}" data-tab="report"><i class="fas fa-flag"></i> 举报</button>
|
|
<button class="${t==='suggestion'?'active':''}" data-tab="suggestion"><i class="fas fa-lightbulb"></i> 建议</button>
|
|
<button class="${t==='appeal'?'active':''}" data-tab="appeal"><i class="fas fa-gavel"></i> 申诉</button>
|
|
<button class="${t==='tracking'?'active':''}" data-tab="tracking"><i class="fas fa-search"></i> 追踪</button>
|
|
</div>
|
|
<div id="home-tab"></div>
|
|
</div>`;
|
|
},
|
|
|
|
mount(tab) {
|
|
const t = tab || 'report';
|
|
document.querySelectorAll('#home-content .tab-nav button').forEach(b => { b.onclick = () => App.navigate('home', b.dataset.tab); });
|
|
const ct = document.getElementById('home-tab');
|
|
if (t === 'tracking') this.renderTracking(ct);
|
|
else TicketCreate.render(ct, { type: t, prefill: {}, backLink: null, hideTabs: true });
|
|
},
|
|
|
|
renderTracking(ct) {
|
|
const token = document.cookie.split('; ').find(r => r.startsWith('tracking_token='))?.split('=')[1];
|
|
ct.innerHTML = `<div class="card"><div class="card-b">
|
|
<div class="form-group"><label>追踪标识 (Cookie自动读取)</label><input id="track-token" value="${token||''}" placeholder="粘贴追踪标识"></div>
|
|
<button class="btn btn-p" id="track-search-btn"><i class="fas fa-search"></i> 查找我的工单</button>
|
|
<div id="track-result" class="mt-4"></div>
|
|
</div></div>`;
|
|
document.getElementById('track-search-btn').onclick = () => this.doSearchTracking();
|
|
},
|
|
|
|
async doSearchTracking() {
|
|
const token = document.getElementById('track-token').value.trim();
|
|
if (!token) return alert('请输入追踪标识');
|
|
const r = document.getElementById('track-result');
|
|
r.innerHTML = '<div class="loading"><i class="fas fa-spinner"></i></div>';
|
|
try {
|
|
const tickets = await API.get(`/tickets/tracking?token=${token}`);
|
|
r.innerHTML = tickets.length ? `<div class="table-wrap"><table><thead><tr><th>#</th><th>类型</th><th>标题</th><th>状态</th><th>时间</th><th>操作</th></tr></thead><tbody>${tickets.map(tk=>`<tr><td>${tk.id}</td><td>${U.badge(tk.type,'type')}</td><td>${U.esc(tk.title)}</td><td>${U.badge(tk.status,'status')}</td><td>${U.date(tk.created_at)}</td><td><button class="btn btn-o btn-sm" onclick="HomePage.showTrackDetail(${tk.id},'${U.escJs(token)}')"><i class="fas fa-eye"></i></button></td></tr>`).join('')}</tbody></table></div>` : '<div class="empty"><i class="fas fa-inbox"></i><p>未找到工单</p></div>';
|
|
} catch (e) { r.innerHTML = `<div class="alert alert-e">${e.message}</div>`; }
|
|
},
|
|
|
|
showTrackDetail(id, token) {
|
|
U.modal(`工单 #${id}`, `<div id="tkd"></div>`, '700px');
|
|
API.get(`/tickets/tracking?token=${token}`).then(tickets => {
|
|
const t = tickets.find(x=>x.id===id);
|
|
if(!t) return;
|
|
document.getElementById('tkd').innerHTML = `<div class="detail-section"><h4>基本信息</h4><div class="detail-row"><span class="lbl">标题</span><span class="val fb">${U.esc(t.title)}</span></div><div class="detail-row"><span class="lbl">类型</span><span class="val">${U.badge(t.type,'type')}</span></div><div class="detail-row"><span class="lbl">状态</span><span class="val">${U.badge(t.status,'status')}</span></div><div class="detail-row"><span class="lbl">提交者</span><span class="val">${U.esc(t.reporter_game_name)}</span></div>${t.description?`<div class="detail-row"><span class="lbl">详情</span><span class="val">${U.esc(t.description)}</span></div>`:''}</div>
|
|
<form id="anon-reply-form" class="mt-4"><div class="form-group"><textarea id="anon-reply-content" rows="3" placeholder="追加补充信息..."></textarea></div><button type="submit" class="btn btn-p btn-sm"><i class="fas fa-reply"></i> 补充</button></form>`;
|
|
document.getElementById('anon-reply-form').onsubmit = async e => {
|
|
e.preventDefault();
|
|
const c = document.getElementById('anon-reply-content').value.trim();
|
|
if (!c) return;
|
|
try { await API.post(`/tickets/${id}/anon-response`, { content: c }); alert('补充成功'); App.closeModal(); }
|
|
catch(ex) { alert(ex.message); }
|
|
};
|
|
}).catch(()=>{});
|
|
}
|
|
};
|