This commit is contained in:
biss committed 2026-07-21 13:25:03 +08:00
1 parent ae528b20e9
commit 1b6e860013
5 files changed
+35 -8

No files matched your search

+6 -2
View File
@@ -357,6 +357,10 @@ export function buildSeedOperations(state) {
} }
function stateFromRows(rows) { function stateFromRows(rows) {
const parseJson = (value, fallback) => {
if (value != null && typeof value === 'object') return value;
try { return JSON.parse(value || JSON.stringify(fallback)); } catch { return fallback; }
};
const subjectsByExam = new Map(); const subjectsByExam = new Map();
for (const row of rows.subjects) { for (const row of rows.subjects) {
const fullScore = Number(row.full_score ?? 150); const fullScore = Number(row.full_score ?? 150);
@@ -518,7 +522,7 @@ function stateFromRows(rows) {
postalCode: row.postal_code || '', postalCode: row.postal_code || '',
guardianName: row.guardian_name || '', guardianName: row.guardian_name || '',
guardianPhone: row.guardian_phone || '', guardianPhone: row.guardian_phone || '',
specialtyTypes: (() => { try { return JSON.parse(row.specialty_types || '[]'); } catch { return []; } })(), specialtyTypes: parseJson(row.specialty_types, []),
specialtyCertificate: row.specialty_certificate || '', specialtyCertificate: row.specialty_certificate || '',
policyEligibility: row.policy_eligibility || '', policyEligibility: row.policy_eligibility || '',
profileCompleted: Boolean(row.profile_completed), profileCompleted: Boolean(row.profile_completed),
@@ -759,7 +763,7 @@ function stateFromRows(rows) {
userId: row.user_id || null, userId: row.user_id || null,
schoolId: row.school_id || null, schoolId: row.school_id || null,
status: row.status, status: row.status,
payload: (() => { try { return JSON.parse(row.payload_json || '{}'); } catch { return {}; } })(), payload: parseJson(row.payload_json, {}),
createdAt: row.created_at, createdAt: row.created_at,
updatedAt: row.updated_at updatedAt: row.updated_at
})), })),
+1 -1
View File
@@ -73,7 +73,7 @@ const mysqlBusinessTables = [
'schools', 'school_classes', 'candidate_profiles', 'notices', 'exams', 'exam_subjects', 'schools', 'school_classes', 'candidate_profiles', 'notices', 'exams', 'exam_subjects',
'registrations', 'registration_subjects', 'exam_arrangement_plans', 'admit_cards', 'admit_card_subjects', 'registrations', 'registration_subjects', 'exam_arrangement_plans', 'admit_cards', 'admit_card_subjects',
'results', 'test_centers', 'test_rooms', 'center_change_requests', 'center_change_rooms', 'results', 'test_centers', 'test_rooms', 'center_change_requests', 'center_change_rooms',
'candidate_account_batches', 'candidate_account_batch_items', 'workflow_instances', 'workflow_actions', 'audit_logs' 'candidate_account_batches', 'candidate_account_batch_items', 'workflow_instances', 'workflow_actions', 'admission_records', 'audit_logs'
]; ];
async function prepareMysql() { async function prepareMysql() {
+9 -1
View File
@@ -61,8 +61,15 @@ export function createAdminViews(context) {
'flow-design': () => adminFlowDesign(data.workflows), 'number-rules': () => adminNumberRules(data), admissions: () => adminAdmissions(data), security: () => accountSecurity(data) 'flow-design': () => adminFlowDesign(data.workflows), 'number-rules': () => adminNumberRules(data), admissions: () => adminAdmissions(data), security: () => accountSecurity(data)
}[page](); }[page]();
app.innerHTML = portalShell('admin', page, content, ...meta[page]); app.innerHTML = portalShell('admin', page, content, ...meta[page]);
if (page === 'admissions') mountPreferenceLedger(data);
} catch (error) { renderError(error); } } catch (error) { renderError(error); }
} }
function mountPreferenceLedger(data) {
const container = app.querySelector('.portal-content');
if (!container || !data.preferences?.length) return;
container.insertAdjacentHTML('beforeend', `<section class="panel data-panel admission-preference-ledger"><div class="panel-title"><div><h2>考生志愿只读台账</h2><p>仅超级管理员可查看;系统不提供任何管理员修改入口。</p></div><span>${data.preferences.length} 份</span></div><div class="table-scroll"><table><thead><tr><th>报名号 / 考生</th><th>考试</th><th>轮次</th><th>志愿顺序</th><th>本人提交时间</th></tr></thead><tbody>${data.preferences.map(item => `<tr><td><strong>${h(item.candidate.name)}</strong><small class="mono">${h(item.candidate.registrationNumber)}</small></td><td>${h(data.exams.find(exam => exam.id === item.examId)?.name || item.examId)}</td><td>第 ${h(item.payload.round || 1)} 轮</td><td>${item.choices.map((choice, index) => `${index + 1}. ${h(choice.schoolName)} · ${h(choice.categoryCode)}`).join('<br>')}</td><td>${formatDate(item.payload.submittedAt, true)}</td></tr>`).join('')}</tbody></table></div></section>`);
}
function adminDashboard(data) { function adminDashboard(data) {
const m = data.metrics; const m = data.metrics;
@@ -139,7 +146,8 @@ export function createAdminViews(context) {
const selected = data.settings.find(item => item.status !== 'completed') || data.settings[0]; const selected = data.settings.find(item => item.status !== 'completed') || data.settings[0];
const pendingPlans = data.plans.filter(item => item.status === 'pending'); const pendingPlans = data.plans.filter(item => item.status === 'pending');
const withdrawals = data.placements.filter(item => item.status === 'withdrawal_pending'); const withdrawals = data.placements.filter(item => item.status === 'withdrawal_pending');
const phaseLabels = { draft: '草稿', filling: '志愿填报中', closed: '填报已截止', matching: '投档中', school_review: '学校审核中', supplementary: '补录填报中', completed: '录取完成' }; const phaseLabels = { draft: '草稿', filling: '志愿填报中', closed: '填报已截止' };
if (selected && !phaseLabels[selected.status]) phaseLabels[selected.status] = { matching: '投档中', school_review: '学校审核中', supplementary: '补录填报中', completed: '录取完成' }[selected.status] || selected.status;
return `<section class="admission-command-banner"><div><span>ADMISSION COMMAND</span><h2>中考招生录取控制台</h2><p>志愿内容仅超级管理员可见且不可代改;投档和录取状态变更均进入审计日志。</p></div><dl><div><dt>待审计划</dt><dd>${pendingPlans.length}</dd></div><div><dt>学校审核中</dt><dd>${data.placements.filter(item => item.status === 'school_review').length}</dd></div><div><dt>退档待审</dt><dd>${withdrawals.length}</dd></div><div><dt>正式录取</dt><dd>${data.placements.filter(item => item.status === 'final').length}</dd></div></dl></section><div class="admission-admin-grid"><section class="panel admission-settings-panel"><div class="panel-title"><div><h2>考试志愿设置</h2><p>不是所有考试都需要开启。</p></div></div><form data-form="admission-setting"><label><span>考试 *</span><select name="examId" required>${data.exams.map(exam => `<option value="${h(exam.id)}" ${exam.id === selected?.examId ? 'selected' : ''}>${h(exam.name)}</option>`).join('')}</select></label><div class="field-row"><label><span>填报开始</span><input name="preferenceStart" type="datetime-local" value="${h(selected?.payload?.preferenceStart?.slice(0,16))}"></label><label><span>填报结束</span><input name="preferenceEnd" type="datetime-local" value="${h(selected?.payload?.preferenceEnd?.slice(0,16))}"></label></div><div class="field-row"><label><span>当前阶段</span><select name="status">${Object.entries(phaseLabels).map(([value,label]) => `<option value="${value}" ${value === (selected?.status || 'draft') ? 'selected' : ''}>${label}</option>`).join('')}</select></label><label><span>最多志愿数</span><input name="maxChoices" type="number" min="1" max="20" value="${h(selected?.payload?.maxChoices || 5)}"></label></div><label><span>考生进度说明</span><input name="progress" value="${h(selected?.payload?.progress || '')}" placeholder="例如:招生学校正在审核投档名单"></label><label class="agreement"><input type="checkbox" name="enabled" ${selected?.payload?.enabled ? 'checked' : ''}><span>为本场考试启用志愿填报</span></label><label class="agreement"><input type="checkbox" name="autoPublish" ${selected?.payload?.autoPublish === false ? '' : 'checked'}><span>录取结束后自动脱敏公示</span></label><button class="solid-button" type="submit">保存志愿设置</button></form>${selected ? `<div class="admission-control-actions"><button class="solid-button" data-action="admission-match" data-exam-id="${h(selected.examId)}">按规则投档</button><button class="ghost-button" data-action="admission-finalize" data-exam-id="${h(selected.examId)}">结束录取并通知</button><button class="ghost-button" data-action="admission-supplementary" data-exam-id="${h(selected.examId)}">开启补录</button></div>` : ''}</section><section class="panel admission-account-panel"><div class="panel-title"><div><h2>招生学校账号</h2><p>账号只能查看本校投档名单和本校计划。</p></div><span>${data.schoolAccounts.length} 个</span></div><form data-form="admission-account"><label><span>招生学校 *</span><select name="schoolId" required>${data.schools.map(school => `<option value="${h(school.id)}">${h(school.name)}</option>`).join('')}</select></label><div class="field-row"><label><span>登录账号 *</span><input name="username" required></label><label><span>初始密码 *</span><input name="password" type="password" minlength="8" required></label></div><label><span>显示名称</span><input name="displayName" placeholder="学校招生办公室"></label><button class="solid-button" type="submit">创建招生学校账号</button></form></section></div><section class="panel admission-plan-console"><div class="panel-title"><div><h2>代上传招生计划</h2><p>每行格式:类别名称 | 计划人数 | 特长类型(普通生留空)。指标分配可由招生学校提交后审核。</p></div></div><form data-form="admission-plan"><div class="field-row"><label><span>考试 *</span><select name="examId">${data.exams.map(exam => `<option value="${h(exam.id)}">${h(exam.name)}</option>`).join('')}</select></label><label><span>招生学校 *</span><select name="schoolId">${data.schools.map(school => `<option value="${h(school.id)}">${h(school.name)}</option>`).join('')}</select></label></div><label><span>招生类别与计划 *</span><textarea name="categoriesText" rows="4" required placeholder="普通生 | 120 |&#10;体育特长生 | 8 | 田径"></textarea></label><button class="solid-button" type="submit">代上传并审核通过</button></form></section><section class="panel data-panel"><div class="panel-title"><h2>招生计划审核</h2><span>${pendingPlans.length} 份待审</span></div><div class="table-scroll"><table><thead><tr><th>考试 / 学校</th><th>计划构成</th><th>提交人</th><th>状态</th><th>操作</th></tr></thead><tbody>${data.plans.map(plan => `<tr><td><strong>${h(plan.examName)}</strong><small Line truncated return `<section class="admission-command-banner"><div><span>ADMISSION COMMAND</span><h2>中考招生录取控制台</h2><p>志愿内容仅超级管理员可见且不可代改;投档和录取状态变更均进入审计日志。</p></div><dl><div><dt>待审计划</dt><dd>${pendingPlans.length}</dd></div><div><dt>学校审核中</dt><dd>${data.placements.filter(item => item.status === 'school_review').length}</dd></div><div><dt>退档待审</dt><dd>${withdrawals.length}</dd></div><div><dt>正式录取</dt><dd>${data.placements.filter(item => item.status === 'final').length}</dd></div></dl></section><div class="admission-admin-grid"><section class="panel admission-settings-panel"><div class="panel-title"><div><h2>考试志愿设置</h2><p>不是所有考试都需要开启。</p></div></div><form data-form="admission-setting"><label><span>考试 *</span><select name="examId" required>${data.exams.map(exam => `<option value="${h(exam.id)}" ${exam.id === selected?.examId ? 'selected' : ''}>${h(exam.name)}</option>`).join('')}</select></label><div class="field-row"><label><span>填报开始</span><input name="preferenceStart" type="datetime-local" value="${h(selected?.payload?.preferenceStart?.slice(0,16))}"></label><label><span>填报结束</span><input name="preferenceEnd" type="datetime-local" value="${h(selected?.payload?.preferenceEnd?.slice(0,16))}"></label></div><div class="field-row"><label><span>当前阶段</span><select name="status">${Object.entries(phaseLabels).map(([value,label]) => `<option value="${value}" ${value === (selected?.status || 'draft') ? 'selected' : ''}>${label}</option>`).join('')}</select></label><label><span>最多志愿数</span><input name="maxChoices" type="number" min="1" max="20" value="${h(selected?.payload?.maxChoices || 5)}"></label></div><label><span>考生进度说明</span><input name="progress" value="${h(selected?.payload?.progress || '')}" placeholder="例如:招生学校正在审核投档名单"></label><label class="agreement"><input type="checkbox" name="enabled" ${selected?.payload?.enabled ? 'checked' : ''}><span>为本场考试启用志愿填报</span></label><label class="agreement"><input type="checkbox" name="autoPublish" ${selected?.payload?.autoPublish === false ? '' : 'checked'}><span>录取结束后自动脱敏公示</span></label><button class="solid-button" type="submit">保存志愿设置</button></form>${selected ? `<div class="admission-control-actions"><button class="solid-button" data-action="admission-match" data-exam-id="${h(selected.examId)}">按规则投档</button><button class="ghost-button" data-action="admission-finalize" data-exam-id="${h(selected.examId)}">结束录取并通知</button><button class="ghost-button" data-action="admission-supplementary" data-exam-id="${h(selected.examId)}">开启补录</button></div>` : ''}</section><section class="panel admission-account-panel"><div class="panel-title"><div><h2>招生学校账号</h2><p>账号只能查看本校投档名单和本校计划。</p></div><span>${data.schoolAccounts.length} 个</span></div><form data-form="admission-account"><label><span>招生学校 *</span><select name="schoolId" required>${data.schools.map(school => `<option value="${h(school.id)}">${h(school.name)}</option>`).join('')}</select></label><div class="field-row"><label><span>登录账号 *</span><input name="username" required></label><label><span>初始密码 *</span><input name="password" type="password" minlength="8" required></label></div><label><span>显示名称</span><input name="displayName" placeholder="学校招生办公室"></label><button class="solid-button" type="submit">创建招生学校账号</button></form></section></div><section class="panel admission-plan-console"><div class="panel-title"><div><h2>代上传招生计划</h2><p>每行格式:类别名称 | 计划人数 | 特长类型(普通生留空)。指标分配可由招生学校提交后审核。</p></div></div><form data-form="admission-plan"><div class="field-row"><label><span>考试 *</span><select name="examId">${data.exams.map(exam => `<option value="${h(exam.id)}">${h(exam.name)}</option>`).join('')}</select></label><label><span>招生学校 *</span><select name="schoolId">${data.schools.map(school => `<option value="${h(school.id)}">${h(school.name)}</option>`).join('')}</select></label></div><label><span>招生类别与计划 *</span><textarea name="categoriesText" rows="4" required placeholder="普通生 | 120 |&#10;体育特长生 | 8 | 田径"></textarea></label><button class="solid-button" type="submit">代上传并审核通过</button></form></section><section class="panel data-panel"><div class="panel-title"><h2>招生计划审核</h2><span>${pendingPlans.length} 份待审</span></div><div class="table-scroll"><table><thead><tr><th>考试 / 学校</th><th>计划构成</th><th>提交人</th><th>状态</th><th>操作</th></tr></thead><tbody>${data.plans.map(plan => `<tr><td><strong>${h(plan.examName)}</strong><small Line truncated
} }
Loaded 3 of 5 files, more files were not shown because too many files have changed in this diff. Show more