/* * MC Report System * Copyright (C) 2026 Sea Network Technology Studio * Author: CangLan * * 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 . */ const TicketCreate = { files: [], render(container, { type, prefill, backLink, hideTabs }) { const user = Auth.user(); const isReport = type === 'report'; const isSuggestion = type === 'suggestion'; const isAppeal = type === 'appeal'; const typeLabel = isReport ? '举报' : isAppeal ? '申诉' : '建议'; this.files = []; this.identities = []; // 登录用户: 拉取多来源身份(网易端+皮肤站可同时绑定) if (user) { API.get('/auth/identities').then(list => { this.identities = list || []; const sel = container.querySelector('#identity-select'); const nameGroup = container.querySelector('#reporter-name-group'); if (sel) { if (this.identities.length > 0) { // 有身份: 显示身份选择, 隐藏游戏名输入框(身份即代表提交者) sel.innerHTML = this.identities.map((it, i) => ``).join(''); sel.closest('.form-group').classList.remove('hidden'); if (nameGroup) { nameGroup.classList.add('hidden'); const inp = nameGroup.querySelector('input[name="reporter_game_name"]'); if (inp) inp.required = false; // 隐藏字段不再参与原生必填校验 } } else { // 无身份: 保留游戏名输入框兜底 sel.closest('.form-group').classList.add('hidden'); if (nameGroup) nameGroup.classList.remove('hidden'); } } }).catch(() => {}); } // 子服务器列表(server_groups: 分组/子服) this.servers = []; API.get('/polls/server-list').then(list => { this.servers = list || []; const sel = container.querySelector('#server-select'); if (sel) { sel.innerHTML = '' + this.servers.map(g => ``).join(''); } }).catch(() => {}); container.innerHTML = ` ${hideTabs ? '' : `
`}
提交${typeLabel}
${user ? `` : ''}
${isReport ? `
` : isAppeal ? `
` : `
`}
上传文件 (图片/视频,每个最大20MB)

点击选择文件或拖拽到此处

支持: JPG/PNG/GIF/WebP/MP4/WebM   每个最大50MB

${backLink ? `` : ''}
`; this.bindFileUpload(container); this.bindSubmit(container, type); container.querySelectorAll('#type-tabs button').forEach(btn => { btn.addEventListener('click', () => { TicketCreate.render(container, { type: btn.dataset.type, prefill, backLink }); }); }); }, bindFileUpload(container) { const drop = container.querySelector('#file-drop'); const input = container.querySelector('#file-input'); const list = container.querySelector('#file-list'); drop.onclick = () => input.click(); input.onchange = () => this.addFiles(input.files, list); drop.ondragover = e => { e.preventDefault(); drop.style.borderColor = 'var(--p)'; }; drop.ondragleave = () => { drop.style.borderColor = 'var(--g300)'; }; drop.ondrop = e => { e.preventDefault(); drop.style.borderColor = 'var(--g300)'; this.addFiles(e.dataTransfer.files, list); }; }, addFiles(fileList, listEl) { const allowed = ['image/jpeg','image/png','image/gif','image/webp','video/mp4','video/webm']; for (const f of fileList) { if (!allowed.includes(f.type)) { alert(`不支持的文件类型: ${f.name}`); continue; } if (f.size > 50 * 1024 * 1024) { alert(`文件过大: ${f.name}`); continue; } if (this.files.length >= 5) { alert('最多5个文件'); break; } this.files.push(f); } this.refreshFileList(listEl); }, refreshFileList(listEl) { listEl.innerHTML = this.files.map((f, i) => `
${U.esc(f.name)} (${(f.size / 1024 / 1024).toFixed(2)}MB)
`).join(''); }, removeFile(i) { this.files.splice(i, 1); this.refreshFileList(document.getElementById('file-list')); }, async bindSubmit(container, type) { const form = container.querySelector('#ticket-form'); form.addEventListener('submit', async e => { e.preventDefault(); const fd = new FormData(form); // 身份选择: 选中多来源身份时覆盖游戏名/UID const sel = container.querySelector('#identity-select'); if (sel && sel.value !== '') { const it = this.identities[parseInt(sel.value)]; if (it) { fd.set('reporter_game_name', it.game_name); fd.set('reporter_game_uid', it.game_uid || ''); } } if (type === 'report') { const tgn = fd.get('target_game_name'); const tgu = fd.get('target_game_uid'); if (!tgn && !tgu) { return alert('举报需至少填写对方游戏名或UID之一'); } } for (const f of this.files) fd.append('files', f); const btn = form.querySelector('[type="submit"]'); btn.disabled = true; btn.innerHTML = ' 提交中...'; try { const res = await API.req('POST', '/tickets', fd); if (res.tracking_token) { document.cookie = `tracking_token=${res.tracking_token};path=/;max-age=${365*24*3600};SameSite=Lax`; } alert('提交成功!工单编号: #' + res.id); if (Auth.logged()) App.navigate('tickets'); else App.navigate('tracking'); // 匿名: 直达追踪页查看工单与回复 } catch (ex) { alert(ex.message); btn.disabled = false; btn.innerHTML = ' 提交'; } }); } };