流程设计
This commit is contained in:
@@ -25,7 +25,7 @@ export function createCandidateViews(context) {
|
||||
function adminNavForUser() {
|
||||
const level = state.user?.adminLevel || 'super';
|
||||
const core = [['dashboard', '工作台', 'home'], ['candidates', level === 'class' ? '本班考生' : '考生信息', 'users'], ['registrations', level === 'class' ? '报名状态' : '报名审核', 'check'], ['results', level === 'super' ? '成绩发布' : '成绩查看', 'chart']];
|
||||
if (level === 'class') return core;
|
||||
if (level === 'class') return [core[0], core[1], core[2], ['flows', '流程中心', 'check'], core[3]];
|
||||
const operations = [['flows', '流程中心', 'check'], ['centers', '考场信息', 'exam']];
|
||||
if (level === 'school') return [core[0], ['organization', '本校组织', 'users'], ['account-batches', '批量建号', 'ticket'], core[1], core[2], ...operations, core[3]];
|
||||
return [core[0], ['admins', '管理员', 'users'], core[1], core[2], ['flows', '流程监督', 'check'], ['flow-design', '流程设计', 'exam'], ['number-rules', '报名号规则', 'ticket'], ['centers', '考场信息', 'exam'], ['exams', '考试与科目', 'exam'], ['notices', '通知发布', 'bell'], ['admit', '准考证生成', 'ticket'], core[3]];
|
||||
@@ -35,7 +35,7 @@ export function createCandidateViews(context) {
|
||||
const nav = role === 'admin' ? adminNavForUser() : candidateNav;
|
||||
const roleName = role === 'admin' ? '管理后台' : '考生中心';
|
||||
const adminTitle = statusLabels[state.user?.adminLevel] || '管理员';
|
||||
return `<div class="portal"><aside class="portal-sidebar" id="portalSidebar"><div class="portal-brand">${brand()}<button data-action="close-sidebar">×</button></div><p class="portal-role">${role === 'admin' ? `${adminTitle} · ${h(state.scopeLabel || '加载中')}` : roleName}</p><nav>${nav.map(([id, label, icon]) => `<button class="${page === id ? 'active' : ''}" data-route="${role}/${id}"><span>${icons[icon]}</span>${label}${role === 'admin' && ((id === 'candidates' && state.pageData?.metrics?.pendingCandidates) || (id === 'registrations' && state.pageData?.metrics?.pendingRegistrations) || (id === 'flows' && state.pageData?.metrics?.pendingFlows)) ? '<em>待办</em>' : ''}</button>`).join('')}</nav><div class="sidebar-help"><span>当前数据范围</span><strong>${h(role === 'admin' ? state.scopeLabel : '个人数据')}</strong><small>权限在服务端同步校验</small></div></aside><main class="portal-main"><header class="portal-topbar"><button class="sidebar-toggle" data-action="open-sidebar" aria-label="打开菜单">${icons.menu}</button><div><span>${roleName}</span><b>/</b><strong>${h(title)}</strong></div><div class="portal-user">${role === 'admin' && state.user?.adminLevel !== 'class' ? `<button class="notification-button" data-route="admin/flows">${icons.bell}<i></i></button>` : ''}<span class="user-avatar">${h((state.user?.displayName || '用').slice(0, 1))}</span><span><strong>${h(state.user?.displayName)}</strong><small>${role === 'admin' ? adminTitle : `资料${statusLabels[state.profile?.status] || '未完善'}`}</small></span><button class="logout-button" data-action="logout" title="退出登录">${icons.logout}</button></div></header><section class="portal-content"><div class="portal-heading"><div><p class="overline">${role === 'admin' ? 'EXAM OPERATIONS' : 'CANDIDATE SERVICE'}</p><h1>${h(title)}</h1><p>${h(description)}</p></div>${portalHeadingAction(role, page)}</div>${content}</section></main></div>`;
|
||||
return `<div class="portal"><aside class="portal-sidebar" id="portalSidebar"><div class="portal-brand">${brand()}<button data-action="close-sidebar">×</button></div><p class="portal-role">${role === 'admin' ? `${adminTitle} · ${h(state.scopeLabel || '加载中')}` : roleName}</p><nav>${nav.map(([id, label, icon]) => `<button class="${page === id ? 'active' : ''}" data-route="${role}/${id}"><span>${icons[icon]}</span>${label}${role === 'admin' && ((id === 'candidates' && state.pageData?.metrics?.pendingCandidates) || (id === 'registrations' && state.pageData?.metrics?.pendingRegistrations) || (id === 'flows' && state.pageData?.metrics?.pendingFlows)) ? '<em>待办</em>' : ''}</button>`).join('')}</nav><div class="sidebar-help"><span>当前数据范围</span><strong>${h(role === 'admin' ? state.scopeLabel : '个人数据')}</strong><small>权限在服务端同步校验</small></div></aside><main class="portal-main"><header class="portal-topbar"><button class="sidebar-toggle" data-action="open-sidebar" aria-label="打开菜单">${icons.menu}</button><div><span>${roleName}</span><b>/</b><strong>${h(title)}</strong></div><div class="portal-user">${role === 'admin' ? `<button class="notification-button" data-route="admin/flows">${icons.bell}<i></i></button>` : ''}<span class="user-avatar">${h((state.user?.displayName || '用').slice(0, 1))}</span><span><strong>${h(state.user?.displayName)}</strong><small>${role === 'admin' ? adminTitle : `资料${statusLabels[state.profile?.status] || '未完善'}`}</small></span><button class="logout-button" data-action="logout" title="退出登录">${icons.logout}</button></div></header><section class="portal-content"><div class="portal-heading"><div><p class="overline">${role === 'admin' ? 'EXAM OPERATIONS' : 'CANDIDATE SERVICE'}</p><h1>${h(title)}</h1><p>${h(description)}</p></div>${portalHeadingAction(role, page)}</div>${content}</section></main></div>`;
|
||||
}
|
||||
|
||||
function portalHeadingAction(role, page) {
|
||||
@@ -138,7 +138,22 @@ export function createCandidateViews(context) {
|
||||
const { results, summaries = [] } = data;
|
||||
if (!results.length) return emptyState('暂时没有已发布成绩', '成绩发布后会在这里显示,同时首页会发布查分通知。', 'candidate/notices', '查看通知');
|
||||
const grouped = Object.groupBy ? Object.groupBy(results, item => item.examName) : results.reduce((acc, item) => ((acc[item.examName] ||= []).push(item), acc), {});
|
||||
return `<div class="result-groups">${Object.entries(grouped).map(([examName, items]) => { const summary = summaries.find(item => item.examId === items[0].examId); const stateText = !summary?.complete ? '等待全部科目发布' : summary.qualified == null ? '本考试不判定合格' : summary.qualified ? '合格' : '未达合格线'; const detail = summary?.passPolicy === 'rank_percent' && summary.complete ? `第 ${summary.rank} / ${summary.cohortSize} 名` : summary ? `得分率 ${summary.scoreRatio}%` : ''; return `<section class="panel result-panel"><header><div><span>${h(items[0].examCode)}</span><h2>${h(examName)}</h2></div><small>发布时间 ${formatDate(items[0].publishedAt, true)}</small></header><div class="result-summary ${summary?.qualified === true ? 'qualified' : summary?.qualified === false ? 'unqualified' : ''}"><span><small>当前总分</small><strong>${h(summary?.total ?? '—')}<em> / ${h(summary?.fullScore ?? '—')}</em></strong></span><span><small>合格判定</small><strong>${h(stateText)}</strong><em>${h(detail)}</em></span></div><div class="score-grid">${items.map(item => `<article><span>${h(item.subjectName)}</span><strong>${h(item.score)}</strong><em>${h(item.grade)}</em><small>满分 ${h(item.fullScore)} · 单科线 ${h(item.passScore)}</small></article>`).join('')}</div><footer><p>成绩仅供查询,如对成绩有异议,请在通知规定时间内申请复核。</p><strong>已发布 ${items.length} 科</strong></footer></section>`; }).join('')}</div>`;
|
||||
return `<div class="result-groups">${Object.entries(grouped).map(([examName, items]) => {
|
||||
const summary = summaries.find(item => item.examId === items[0].examId);
|
||||
const stateText = !summary?.complete ? '等待全部科目发布' : summary.qualified == null ? '本考试不判定合格' : summary.qualified ? '合格' : '未达合格线';
|
||||
const detail = summary?.passPolicy === 'rank_percent' && summary.complete ? `第 ${summary.rank} / ${summary.cohortSize} 名` : summary ? `得分率 ${summary.scoreRatio}%` : '';
|
||||
const scores = items.map(item => {
|
||||
const appeal = item.appeal;
|
||||
const latestAction = appeal?.actions?.at(-1);
|
||||
const appealPanel = appeal?.status === 'pending'
|
||||
? `<div class="score-appeal-state">${badge('pending')}<small>${h(appeal.currentStepDetail?.name || '等待处理')} · ${h(appeal.assignee?.displayName || '待分配')}</small></div>`
|
||||
: appeal?.status === 'approved'
|
||||
? `<div class="score-appeal-state">${badge('approved')}<small>${h(latestAction?.note || '复议流程已完成')}</small></div>`
|
||||
: `${appeal ? `<div class="score-appeal-state">${badge('rejected')}<small>${h(latestAction?.note || '可补充理由后重新提交')}</small></div>` : ''}<form class="score-appeal-form" data-form="score-appeal"><input type="hidden" name="resultId" value="${h(item.id)}"><textarea name="reason" rows="2" minlength="5" maxlength="500" required placeholder="填写成绩复议理由(至少 5 个字)"></textarea><button class="row-action primary" type="submit">${appeal ? '重新申请复议' : '申请成绩复议'}</button></form>`;
|
||||
return `<article><span>${h(item.subjectName)}</span><strong>${h(item.score)}</strong><em>${h(item.grade)}</em><small>满分 ${h(item.fullScore)} · 单科线 ${h(item.passScore)}</small>${appealPanel}</article>`;
|
||||
}).join('');
|
||||
return `<section class="panel result-panel"><header><div><span>${h(items[0].examCode)}</span><h2>${h(examName)}</h2></div><small>发布时间 ${formatDate(items[0].publishedAt, true)}</small></header><div class="result-summary ${summary?.qualified === true ? 'qualified' : summary?.qualified === false ? 'unqualified' : ''}"><span><small>当前总分</small><strong>${h(summary?.total ?? '—')}<em> / ${h(summary?.fullScore ?? '—')}</em></strong></span><span><small>合格判定</small><strong>${h(stateText)}</strong><em>${h(detail)}</em></span></div><div class="score-grid">${scores}</div><footer><p>如对成绩有异议,可直接提交复议,系统会按班级、学校和考试中心流程办理。</p><strong>已发布 ${items.length} 科</strong></footer></section>`;
|
||||
}).join('')}</div>`;
|
||||
}
|
||||
|
||||
function candidateNotices(notices) {
|
||||
|
||||
Reference in New Issue
Block a user