权限
This commit is contained in:
@@ -81,6 +81,38 @@ function formObject(form) {
|
||||
return Object.fromEntries(new FormData(form).entries());
|
||||
}
|
||||
|
||||
function updateRegistrationSelection() {
|
||||
const table = document.querySelector('#registrationTable');
|
||||
if (!table) return;
|
||||
const selectable = [...table.querySelectorAll('[data-registration-select]:not(:disabled)')];
|
||||
const visible = selectable.filter(input => !input.closest('tr').hidden);
|
||||
const selected = selectable.filter(input => input.checked);
|
||||
const selectAll = table.querySelector('[data-registration-select-all]');
|
||||
if (selectAll) {
|
||||
selectAll.checked = visible.length > 0 && visible.every(input => input.checked);
|
||||
selectAll.indeterminate = visible.some(input => input.checked) && !selectAll.checked;
|
||||
}
|
||||
const count = document.querySelector('[data-registration-selection-count]');
|
||||
if (count) count.textContent = selected.length;
|
||||
document.querySelectorAll('[data-action="bulk-registration-review"]').forEach(button => { button.disabled = selected.length === 0; });
|
||||
}
|
||||
|
||||
function applyTableFilters(tableId) {
|
||||
const table = document.getElementById(tableId);
|
||||
if (!table) return;
|
||||
const search = [...document.querySelectorAll('[data-action="table-search"]')].find(input => input.dataset.target === tableId);
|
||||
const query = search?.value.trim().toLowerCase() || '';
|
||||
const status = [...document.querySelectorAll('[data-action="status-filter"]')].find(button => button.dataset.target === tableId && button.classList.contains('active'))?.dataset.status || 'all';
|
||||
const filters = [...document.querySelectorAll('[data-table-filter]')].filter(select => select.dataset.target === tableId && select.value);
|
||||
table.querySelectorAll('tbody tr[data-filter-row], tbody tr[data-status]').forEach(row => {
|
||||
const matchesSearch = !query || row.textContent.toLowerCase().includes(query);
|
||||
const matchesStatus = status === 'all' || String(row.dataset.status || '').split(/\s+/).includes(status);
|
||||
const matchesFilters = filters.every(select => String(row.dataset[select.dataset.tableFilter] || '') === select.value);
|
||||
row.hidden = !(matchesSearch && matchesStatus && matchesFilters);
|
||||
});
|
||||
if (tableId === 'registrationTable') updateRegistrationSelection();
|
||||
}
|
||||
|
||||
async function refreshPublic() {
|
||||
state.publicData = await api('/api/public/home');
|
||||
}
|
||||
@@ -227,12 +259,43 @@ document.addEventListener('click', async event => {
|
||||
toast(archived ? '账户已归档' : '账户已恢复', `${data.scopeLabel} · ${data.count} 个账户状态已更新`); return renderRoute();
|
||||
}
|
||||
if (action === 'review-registration') return openRegistrationReview(target.dataset.id);
|
||||
if (action === 'bulk-registration-review') {
|
||||
const ids = [...document.querySelectorAll('#registrationTable [data-registration-select]:checked')].map(input => input.value);
|
||||
if (!ids.length) return toast('请先选择报名记录', '仅待审核记录可批量处理');
|
||||
const status = target.dataset.status;
|
||||
const promptText = status === 'rejected' ? '请填写批量退回原因(必填)' : '填写批量审核意见(可留空)';
|
||||
const reviewNote = window.prompt(promptText, '');
|
||||
if (reviewNote === null) return;
|
||||
if (status === 'rejected' && !reviewNote.trim()) return toast('请填写退回原因', '考生需要根据原因修改报名');
|
||||
let completed = 0;
|
||||
const failures = [];
|
||||
for (const id of ids) {
|
||||
try {
|
||||
await api(`/api/admin/registrations/${id}`, { method: 'PATCH', body: { status, reviewNote } });
|
||||
completed += 1;
|
||||
} catch (error) {
|
||||
failures.push(error.message);
|
||||
}
|
||||
}
|
||||
toast(status === 'rejected' ? `已退回 ${completed} 条报名` : `已处理 ${completed} 条报名`, failures.length ? `${failures.length} 条未完成,请刷新后重试` : '报名状态与流程轨迹已同步更新');
|
||||
return renderRoute();
|
||||
}
|
||||
if (action === 'confirm-payment') {
|
||||
if (!window.confirm(`确认已收到 ${target.dataset.name} 的“${target.dataset.exam}”考试费用吗?`)) return;
|
||||
await api(`/api/admin/payments/${target.dataset.id}`, { method: 'PATCH' });
|
||||
toast('缴费已确认', '考生端状态、确认人和确认时间已同步记录');
|
||||
return renderRoute();
|
||||
}
|
||||
if (action === 'update-payment') {
|
||||
const paid = target.dataset.status === 'paid';
|
||||
const message = paid
|
||||
? `确认将 ${target.dataset.name} 的“${target.dataset.exam}”标记为已缴费吗?`
|
||||
: `确认撤销 ${target.dataset.name} 的“${target.dataset.exam}”缴费记录,并改为待缴费吗?`;
|
||||
if (!window.confirm(message)) return;
|
||||
await api(`/api/admin/payments/${target.dataset.id}`, { method: 'PATCH', body: { status: target.dataset.status } });
|
||||
toast(paid ? '已标记为已缴费' : '已改为待缴费', paid ? '确认人和确认时间已同步记录' : '原确认人和确认时间已清除');
|
||||
return renderRoute();
|
||||
}
|
||||
if (action === 'excel-download') {
|
||||
const query = new URLSearchParams();
|
||||
if (target.dataset.template === '1') query.set('template', '1');
|
||||
@@ -297,15 +360,14 @@ document.addEventListener('click', async event => {
|
||||
}
|
||||
if (action === 'status-filter') {
|
||||
target.parentElement.querySelectorAll('button').forEach(button => button.classList.toggle('active', button === target));
|
||||
document.querySelectorAll(`#${target.dataset.target} tbody tr`).forEach(row => row.hidden = target.dataset.status !== 'all' && !String(row.dataset.status || '').split(/\s+/).includes(target.dataset.status));
|
||||
applyTableFilters(target.dataset.target);
|
||||
}
|
||||
} catch (error) { toast('操作未完成', error.message); }
|
||||
});
|
||||
|
||||
document.addEventListener('input', event => {
|
||||
if (event.target.matches('[data-action="table-search"]')) {
|
||||
const query = event.target.value.trim().toLowerCase();
|
||||
document.querySelectorAll(`#${event.target.dataset.target} tbody tr`).forEach(row => row.hidden = !row.textContent.toLowerCase().includes(query));
|
||||
applyTableFilters(event.target.dataset.target);
|
||||
}
|
||||
if (event.target.matches('.subject-options input')) {
|
||||
const form = event.target.closest('form');
|
||||
@@ -320,6 +382,15 @@ document.addEventListener('input', event => {
|
||||
});
|
||||
|
||||
document.addEventListener('change', event => {
|
||||
if (event.target.matches('[data-table-filter]')) applyTableFilters(event.target.dataset.target);
|
||||
if (event.target.matches('[data-registration-select]')) updateRegistrationSelection();
|
||||
if (event.target.matches('[data-registration-select-all]')) {
|
||||
const table = document.getElementById(event.target.dataset.target);
|
||||
table?.querySelectorAll('[data-registration-select]:not(:disabled)').forEach(input => {
|
||||
if (!input.closest('tr').hidden) input.checked = event.target.checked;
|
||||
});
|
||||
updateRegistrationSelection();
|
||||
}
|
||||
if (event.target.matches('[data-excel-file]')) {
|
||||
const input = event.target;
|
||||
const file = input.files?.[0];
|
||||
@@ -674,7 +745,7 @@ function openCandidatePasswordReset(id) {
|
||||
|
||||
function openRegistrationReview(id) {
|
||||
const reg = state.pageData.registrations.find(item => item.id === id);
|
||||
const canReview = reg.status === 'pending' && reg.workflow?.assignee?.id === state.user.id;
|
||||
const canReview = reg.status === 'pending' && (reg.workflow?.assignee?.id === state.user.id || state.user.adminLevel === 'super');
|
||||
setModal(`<div class="modal-head"><div><span>REGISTRATION REVIEW</span><h2>${canReview ? '处理' : '查看'}考试报名</h2><p>${h(reg.candidate?.name)} · ${h(reg.exam.name)}</p></div><button data-action="close-modal">×</button></div><div class="registration-review"><div><span>报考科目</span><p>${reg.subjects.map(subject => `<b>${h(subject.name)}</b>`).join('')}</p></div><dl><div><dt>账户报名号</dt><dd class="mono">${h(reg.registrationNumber || reg.candidate?.candidateNumber || '账户号码异常')}</dd></div><div><dt>当前步骤</dt><dd>${h(reg.workflow?.currentStepDetail?.name || '流程已结束')}</dd></div><div><dt>责任人</dt><dd>${h(reg.workflow?.assignee?.displayName || '—')}</dd></div><div><dt>缴费状态</dt><dd>${badge(reg.paymentStatus)}</dd></div></dl></div>${canReview ? `<form class="modal-form" data-form="registration-review"><input type="hidden" name="id" value="${h(reg.id)}"><label><span>审核结论</span><select name="status"><option value="approved">通过当前步骤</option><option value="rejected">退回报名</option></select></label><label><span>审核意见</span><textarea name="reviewNote" rows="3" placeholder="可填写审核说明">${h(reg.reviewNote || '')}</textarea></label><div class="modal-foot"><button type="button" class="ghost-button" data-action="close-modal">取消</button><button type="submit" class="solid-button">确认处理</button></div></form>` : '<div class="modal-foot"><button class="solid-button" data-action="close-modal">关闭</button></div>'}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user