const FeaturesPage = {
sort: 'time',
filterGroup: '',
filterServer: '',
async render() { return '
'; },
async mount() {
document.getElementById('page-title').textContent = '更新内容';
document.getElementById('page-actions').innerHTML = Auth.isOwner()
? ''
: '';
this.renderLayout();
await this.load();
},
renderLayout() {
const ct = document.getElementById('page-content');
ct.innerHTML = `
`;
this.loadGroupOptions();
},
async loadGroupOptions() {
try {
const groups = await API.get('/polls/groups');
const groupSel = document.getElementById('feat-group');
const serverSel = document.getElementById('feat-server');
const uniqueGroups = [...new Set(groups.map(g => g.group_name))];
groupSel.innerHTML = '' + uniqueGroups.map(g => ``).join('');
const servers = this.filterGroup ? groups.filter(g => g.group_name === this.filterGroup) : groups;
const uniqueServers = [...new Set(servers.map(g => g.server_name))];
serverSel.innerHTML = '' + uniqueServers.map(s => ``).join('');
} catch {}
},
setFilter() {
this.filterGroup = document.getElementById('feat-group').value;
this.filterServer = document.getElementById('feat-server').value;
this.loadGroupOptions();
this.load();
},
setSort(s) {
this.sort = s;
document.getElementById('sort-time').className = 'btn btn-o btn-sm ' + (s==='time'?'btn-p':'');
document.getElementById('sort-votes').className = 'btn btn-o btn-sm ' + (s==='votes'?'btn-p':'');
this.load();
},
async load() {
const ct = document.getElementById('feat-list');
U.loading(ct);
try {
const params = new URLSearchParams({ sort: this.sort, group_name: this.filterGroup, server_name: this.filterServer });
const items = await API.get('/features?' + params);
ct.innerHTML = items.length === 0 ? ''
: items.map(i => this.renderItem(i)).join('');
for (const i of items) if (Auth.logged()) this.bindVote(i);
} catch (e) { ct.innerHTML = `${e.message}
`; }
},
renderItem(i) {
const sc = i.status==='done'?'var(--s)':i.status==='planned'?'var(--p)':'var(--g5)';
const sl = i.status==='done'?'已完成':i.status==='planned'?'计划中':'待定';
return `
${U.esc(i.title)} ${i.group_name ? `— ${U.esc(i.group_name)}/${U.esc(i.server_name)}` : ''}
● ${sl}
${Auth.isOwner() ? `` : ''}
${i.description ? `
${U.esc(i.description)}
` : ''}
${i.my_vote ? '已投票' : '投票'}
`;
},
bindVote(i) {
const card = document.getElementById('feat-'+i.id);
if (!card) return;
card.querySelector('.feat-vote').addEventListener('click', async () => {
try { await API.post(`/features/${i.id}/vote`); this.load(); } catch (ex) { alert(ex.message); }
});
},
async loadGroupsForForm() { try { return await API.get('/polls/groups'); } catch { return []; } },
async showCreate() {
const groups = await this.loadGroupsForForm();
const opts = groups.map(g => ``).join('');
U.modal('添加项目', `
`);
document.getElementById('feat-form').onsubmit = async e => { e.preventDefault();
const gs = document.getElementById('ft-gs').value;
const [group_name, server_name] = gs === '::' ? ['',''] : gs.split('::');
try { await API.post('/features', { title: document.getElementById('ft-title').value, description: document.getElementById('ft-desc').value, group_name, server_name }); App.closeModal(); this.load(); } catch(ex){alert(ex.message);}
};
},
async showEdit(id, title, desc, status, gn, sn) {
const groups = await this.loadGroupsForForm();
const opts = groups.map(g => ``).join('');
U.modal('编辑', `
`);
document.getElementById('fe-edit').onsubmit = async e => { e.preventDefault();
const gs = document.getElementById('fe-gs').value;
const [group_name, server_name] = gs === '::' ? ['',''] : gs.split('::');
try { await API.put('/features/'+id, { title: document.getElementById('fe-title').value, description: document.getElementById('fe-desc').value, status: document.getElementById('fe-status').value, group_name, server_name }); App.closeModal(); this.load(); } catch(ex){alert(ex.message);}
};
},
async del(id) { if (!await U.confirm('确认删除?')) return; try { await API.req('DELETE', '/features/'+id); App.closeModal(); this.load(); } catch(ex){alert(ex.message);} }
};