const U = { STATUS: { pending:'待处理', processing:'处理中', awaiting_info:'待补充', appealing:'申诉中', resolved:'已解决', rejected:'已驳回', closed:'已关闭' }, TYPE: { report:'举报', suggestion:'建议', appeal:'申诉', result_appeal:'结果申诉' }, ROLE: { owner:'服主', admin:'管理员', player:'玩家' }, PRIORITY: { low:'低', medium:'中', high:'高', urgent:'紧急' }, badge(s, t) { if (t === 'status') return `${this.STATUS[s]||s}`; if (t === 'type') return `${this.TYPE[s]||s}`; if (t === 'role') return `${this.ROLE[s]||s}`; if (t === 'priority') return `${this.PRIORITY[s]||s}`; return s; }, date(d) { if (!d) return '-'; const dt = new Date(d); if (isNaN(dt.getTime())) return d; const p = n => String(n).padStart(2, '0'); return `${dt.getFullYear()}-${p(dt.getMonth()+1)}-${p(dt.getDate())} ${p(dt.getHours())}:${p(dt.getMinutes())}`; }, esc(s) { if (!s) return ''; return String(s).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"').replace(/'/g,'''); }, escJs(s) { if (!s) return ''; return String(s).replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/"/g,'"').replace(/\n/g,'\\n'); }, // ---- 动态来源(缓存, 注册/绑定身份/用户管理等处共用) ---- _sourcesCache: null, async sources(force) { if (force || !this._sourcesCache) { try { this._sourcesCache = await API.get('/sources'); } catch { this._sourcesCache = []; } } return this._sourcesCache; }, // 生成下拉 HTML; includeEmpty 时带"不指定"选项 async sourcesOptions(selected, includeEmpty) { const list = await this.sources(); const opts = (includeEmpty ? '' : '') + list.map(s => ``).join(''); return opts; }, // code → 显示标签(找不到时显示原 code) async sourceLabel(code) { if (!code) return '-'; const list = await this.sources(); const hit = list.find(s => s.code === code); return hit ? hit.label : code; }, async sourceBadge(code) { if (!code) return '-'; const list = await this.sources(); const hit = list.find(s => s.code === code); return `${this.esc(hit ? hit.label : code)}`; }, showAlert(containerId, type, msg) { const ct = document.getElementById(containerId || 'page-content'); if (!ct) return; const el = document.createElement('div'); el.className = `alert alert-${type === 'success' ? 's' : type === 'error' ? 'e' : 'i'}`; el.textContent = msg; ct.prepend(el); setTimeout(() => el.remove(), 6000); }, loading(ct) { if (!ct) return; ct.innerHTML = '

加载中...

'; }, modal(title, html, w) { const o = document.getElementById('modal-overlay'); if (!o) return; document.getElementById('modal-title').textContent = title; document.getElementById('modal-body').innerHTML = html; o.querySelector('.modal-box').style.width = w || ''; o.classList.remove('hidden'); }, confirm(msg) { return new Promise(resolve => { this.modal('确认', `

${this.esc(msg)}

`); document.querySelector('.btn-confirm-cancel').onclick = () => { App.closeModal(); resolve(false); }; document.querySelector('.btn-confirm-ok').onclick = () => { App.closeModal(); resolve(true); }; }); } };