diff --git a/web/src/views/DashboardView.vue b/web/src/views/DashboardView.vue index aa0a956..59b83b5 100644 --- a/web/src/views/DashboardView.vue +++ b/web/src/views/DashboardView.vue @@ -380,6 +380,19 @@ const readiness = computed(() => { ] }) +const operationAlerts = computed(() => { + const counts = data.value?.counts + if (!counts) return [] + const alerts = [] as Array<{ level: 'critical' | 'warning'; title: string; detail: string; route: string }> + const unpublished = counts.teachingTasks - counts.publishedTeachingTasks + const unscheduled = counts.publishedTeachingTasks - counts.scheduledTeachingTasks + if (unpublished > 0) alerts.push({ level: 'warning', title: '教学任务尚未发布', detail: `${unpublished} 个教学班尚未发布,后续排课与选课无法推进。`, route: '/teaching-tasks' }) + if (unscheduled > 0) alerts.push({ level: 'critical', title: '课表覆盖存在缺口', detail: `${unscheduled} 个已发布教学班尚未进入课表。`, route: hasRole('SuperAdmin', 'AcademicAdmin') ? '/schedules' : '/class-timetable' }) + if (counts.submittedGradeSheets > 0) alerts.push({ level: 'warning', title: '成绩审核等待处理', detail: `${counts.submittedGradeSheets} 张成绩登记册已提交,等待审核或发布。`, route: '/grades' }) + if (counts.openCourseSelectionRounds > 0 && counts.courseEnrollments === 0) alerts.push({ level: 'warning', title: '开放选课尚无有效记录', detail: `${counts.openCourseSelectionRounds} 个选课批次开放中,但当前未发现有效选课。`, route: '/course-selections' }) + return alerts +}) + const primaryActionRoute = computed(() => todoItems.value[0]?.route ?? quickActions.value[0]?.route ?? '/notifications', ) @@ -511,6 +524,14 @@ onMounted(async () => { +
+
OPERATION WATCH

运行预警

{{ operationAlerts.length ? `${operationAlerts.length} 个运行断点需要关注` : '当前未发现运行断点' }}
+
+ +
+

教学任务、课表与成绩流程当前衔接正常。

+
+
@@ -715,6 +736,17 @@ onMounted(async () => { line-height: 1.7; } +.operation-alerts { padding: 21px 24px; border: 1px solid #e5e8ed; background: #fff; } +.operation-alerts header { display: flex; justify-content: space-between; gap: 16px; align-items: end; } +.operation-alerts header span { color: var(--dashboard-teal); font: 700 10px/1 Consolas,monospace; letter-spacing: .12em; } +.operation-alerts h2 { margin: 7px 0 0; color: var(--dashboard-navy); font-size: 19px; } +.operation-alerts header small { color: #697789; font-size: 11px; } +.operation-alert-list { margin-top: 16px; display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 10px; } +.operation-alert-list button { padding: 14px 15px; display: grid; gap: 5px; text-align: left; border: 1px solid #e8e1d3; border-left: 3px solid var(--dashboard-amber); background: #fffcf6; } +.operation-alert-list button.critical { border-left-color: #ba5145; background: #fff9f8; } +.operation-alert-list b { color: #334254; font-size: 13px; }.operation-alert-list span { color: #6d7888; font-size: 11px; line-height: 1.5; }.operation-alert-list i { color: #8b6d3a; font-size: 11px; font-style: normal; } +.operation-alerts > p { margin: 15px 0 0; color: #627184; font-size: 12px; } + .overview-hero::after { content: ""; position: absolute; @@ -1322,6 +1354,8 @@ button:focus-visible { } @media (max-width: 760px) { + .operation-alerts { padding: 18px; } + .operation-alert-list { grid-template-columns: 1fr; } .greeting-insights { padding: 15px; } .greeting-insights > div { grid-template-columns: 1fr; } .counselor-risk-list button { grid-template-columns: 1fr auto; }