const UsersPage = { async render() { return '
'; }, async mount() { document.getElementById('page-title').textContent = '用户管理'; document.getElementById('page-actions').innerHTML = ''; await this.load(); }, async load() { const ct = document.getElementById('page-content'); try { const users = await API.get('/users'); const srcLabel = { netease:'网易', skin:'皮肤站' }; ct.innerHTML = `
${users.map(u => ` `).join('')}
#用户名邮箱游戏名游戏UID来源身份角色状态时间操作
${u.id}${U.esc(u.username)}${U.esc(u.email)} ${U.esc(u.game_name)}${U.esc(u.game_uid)} ${srcLabel[u.source] || u.source || '网易'} ${(u.identity_count||0) > 1 ? `${u.identity_count} 个` : (u.identity_count||0) === 1 ? '1' : '-'} ${U.badge(u.role,'role')} ${u.active ? '启用' : '禁用'} ${U.date(u.created_at)}
`; } catch (e) { ct.innerHTML = `
${e.message}
`; } }, createUser() { this.loadServersForCreate(); }, async loadServersForCreate() { let groups = []; try { groups = await API.get('/polls/groups'); } catch {} const checks = groups.map(g => { return ``; }).join(''); U.modal('添加用户', `
`); document.getElementById('cu-form').onsubmit = async e => { e.preventDefault(); const servers = []; document.querySelectorAll('.cu-srv:checked').forEach(cb => { const [g,s] = cb.value.split('::'); servers.push({group_name:g,server_name:s}); }); try { await API.post('/users', { username: document.getElementById('cu-u').value, password: document.getElementById('cu-p').value, email: document.getElementById('cu-e').value, role: document.getElementById('cu-r').value, game_name: document.getElementById('cu-gn').value, game_uid: document.getElementById('cu-gu').value, source: document.getElementById('cu-s').value, admin_servers: servers, }); App.closeModal(); this.load(); } catch (ex) { alert(ex.message); } }; setTimeout(() => UsersPage.toggleCreateServers(), 50); }, toggleCreateServers() { const role = document.getElementById('cu-r').value; const div = document.getElementById('cu-servers'); div.classList.toggle('hidden', role === 'player'); }, editUser(id, username, email, gameName, gameUid, role, active, source) { this.loadUserServers(id, username, email, gameName, gameUid, role, active, source); }, async loadUserServers(id, username, email, gameName, gameUid, role, active, source) { let groups = [], userServers = [], identities = []; try { groups = await API.get('/polls/groups'); } catch {} try { userServers = await API.get('/users/'+id+'/servers'); } catch {} try { identities = await API.get('/users/'+id).then(u => u.identities || []); } catch {} const checks = groups.map(g => { const key = g.group_name+'::'+g.server_name; const has = userServers.some(s => s.group_name===g.group_name && s.server_name===g.server_name); return ``; }).join(''); U.modal('编辑用户', `
${checks||'无可用服务器(先在投票→分组管理中添加)'}
${identities.length===0?'暂无身份记录':identities.map(it=>`
${it.source==='netease'?'网易端':'皮肤站'} ${U.esc(it.game_name)}${it.game_uid?' (UID: '+U.esc(it.game_uid)+')':''} ${U.date(it.created_at)} ${identities.length>1?``:'主身份'}
`).join('')}
`); document.getElementById('eu-form').onsubmit = async e => { e.preventDefault(); const servers = []; document.querySelectorAll('.eu-srv:checked').forEach(cb => { const [g,s] = cb.value.split('::'); servers.push({group_name:g,server_name:s}); }); const data = { email: document.getElementById('eu-e').value, role: document.getElementById('eu-r').value, game_name: document.getElementById('eu-gn').value, game_uid: document.getElementById('eu-gu').value, active: document.getElementById('eu-a').value === '1', source: document.getElementById('eu-s').value, admin_servers: servers, }; const pw = document.getElementById('eu-p').value; if (pw) data.password = pw; try { await API.put('/users/'+id, data); App.closeModal(); this.load(); } catch (ex) { alert(ex.message); } }; // 来源切换: 皮肤站时显示 UUID 查询辅助 const euToggleSource = () => { const isSkin = document.getElementById('eu-ident-source').value === 'skin'; document.getElementById('eu-ident-site-group').classList.toggle('hidden', !isSkin); }; document.getElementById('eu-ident-source').onchange = euToggleSource; euToggleSource(); // 自动带出站点配置的皮肤站地址 API.get('/settings/skin-site').then(r => { if (r.site && !document.getElementById('eu-ident-site').value) document.getElementById('eu-ident-site').value = r.site; }).catch(()=>{}); // 皮肤站用户名 → UUID 查询 document.getElementById('eu-ident-fetch').onclick = async () => { const site = document.getElementById('eu-ident-site').value.trim(); const name = document.getElementById('eu-ident-name').value.trim(); if (!site) return alert('请填写皮肤站地址(来源为皮肤站时显示)'); if (!name) return alert('请先填写游戏名(皮肤站用户名)'); const btn = document.getElementById('eu-ident-fetch'); btn.disabled = true; btn.innerHTML = ''; try { const r = await API.get(`/settings/uuid-lookup?site=${encodeURIComponent(site)}&name=${encodeURIComponent(name)}`); if (r.found) { document.getElementById('eu-ident-uid').value = r.uuid; alert('已获取 UUID: ' + r.uuid); } else alert(r.message || '未找到该用户名'); } catch (ex) { alert(ex.message); } btn.disabled = false; btn.innerHTML = ''; }; }, async addIdentity(userId) { const source = document.getElementById('eu-ident-source').value; const name = document.getElementById('eu-ident-name').value.trim(); const uid = document.getElementById('eu-ident-uid').value.trim(); if (!name) return alert('请填写游戏名'); try { await API.post(`/users/${userId}/identities`, { source, game_name: name, game_uid: uid }); alert('绑定成功'); // 刷新弹窗中的身份列表 const u = await API.get('/users/'+userId); const box = document.getElementById('identities-box'); const list = u.identities || []; box.innerHTML = list.length===0 ? '暂无身份记录' : list.map(it=>`
${it.source==='netease'?'网易端':'皮肤站'} ${U.esc(it.game_name)}${it.game_uid?' (UID: '+U.esc(it.game_uid)+')':''} ${U.date(it.created_at)} ${list.length>1?``:'主身份'}
`).join(''); document.getElementById('eu-ident-name').value = ''; document.getElementById('eu-ident-uid').value = ''; } catch (ex) { alert(ex.message); } }, async removeIdentity(userId, identityId) { if (!await U.confirm('确认解绑该身份?')) return; try { await API.delete(`/users/${userId}/identities/${identityId}`); alert('已解绑'); const u = await API.get('/users/'+userId); const box = document.getElementById('identities-box'); const list = u.identities || []; box.innerHTML = list.length===0 ? '暂无身份记录' : list.map(it=>`
${it.source==='netease'?'网易端':'皮肤站'} ${U.esc(it.game_name)}${it.game_uid?' (UID: '+U.esc(it.game_uid)+')':''} ${U.date(it.created_at)} ${list.length>1?``:'主身份'}
`).join(''); } catch (ex) { alert(ex.message); } } };