首页问候

This commit is contained in:
2026-08-10 10:03:35 +08:00 Unverified
parent 64b67bb3ce
commit 40064cedda
3 changed files with 279 additions and 4 deletions
+60 -2
View File
@@ -70,6 +70,18 @@ interface GradeItem {
publishedAt?: string
}
interface DashboardGreeting {
title: string
subtitle: string
label: string
insights: Array<{
label: string
value: string
hint: string
tone: 'calm' | 'positive' | 'attention'
}>
}
const router = useRouter()
const auth = useAuthStore()
const loading = ref(true)
@@ -79,6 +91,7 @@ const notifications = ref<NotificationItem[]>([])
const unreadCount = ref(0)
const exams = ref<ExamItem[]>([])
const grades = ref<GradeItem[]>([])
const dashboardGreeting = ref<DashboardGreeting | null>(null)
const now = ref(new Date())
const categoryLabels: Record<string, string> = {
@@ -281,6 +294,7 @@ async function loadOverview() {
http.get('/notifications', { params: { page: 1, pageSize: 5 } }),
http.get('/exams/my-schedule'),
http.get('/grades/student/transcript'),
http.get<DashboardGreeting>('/dashboard/greeting'),
])
if (results[0].status === 'fulfilled') {
@@ -304,6 +318,9 @@ async function loadOverview() {
} else {
failedSections.value.push('考试成绩')
}
if (results[4].status === 'fulfilled') {
dashboardGreeting.value = results[4].value.data
}
loading.value = false
}
@@ -315,8 +332,8 @@ onMounted(loadOverview)
<section class="student-overview-hero">
<div class="student-hero-copy">
<span class="section-kicker">MY ACADEMIC DAY</span>
<h2>{{ greeting }}{{ auth.user?.displayName ?? '同学' }}</h2>
<p>{{ todayLabel }}<template v-if="timetable?.term"> · {{ timetable.term.name }}</template></p>
<h2>{{ dashboardGreeting?.title ?? `${greeting}${auth.user?.displayName ?? '同学'}` }}</h2>
<p>{{ dashboardGreeting?.subtitle ?? todayLabel }}<template v-if="!dashboardGreeting && timetable?.term"> · {{ timetable.term.name }}</template></p>
</div>
<div class="today-status">
<span>今日课程</span>
@@ -325,6 +342,15 @@ onMounted(loadOverview)
</div>
</section>
<section v-if="dashboardGreeting?.insights.length" class="student-greeting-insights" :aria-label="dashboardGreeting.label">
<span>{{ dashboardGreeting.label }}</span>
<article v-for="insight in dashboardGreeting.insights" :key="insight.label" :class="insight.tone">
<small>{{ insight.label }}</small>
<strong>{{ insight.value }}</strong>
<em>{{ insight.hint }}</em>
</article>
</section>
<el-alert
v-if="failedSections.length"
type="warning"
@@ -539,6 +565,36 @@ onMounted(loadOverview)
font-size: 13px;
}
.student-greeting-insights {
padding: 15px 21px;
display: grid;
grid-template-columns: 105px repeat(3, minmax(0, 1fr));
gap: 12px;
border: 1px solid #dce6eb;
background: #f7faf9;
}
.student-greeting-insights > span {
align-self: center;
color: var(--teal);
font: 700 10px/1.5 Consolas, monospace;
letter-spacing: .11em;
}
.student-greeting-insights article {
min-width: 0;
padding-left: 12px;
display: grid;
gap: 3px;
border-left: 2px solid #a9bcc6;
}
.student-greeting-insights article.positive { border-color: var(--teal); }
.student-greeting-insights article.attention { border-color: #c4812a; }
.student-greeting-insights small { color: #667488; font-size: 10px; }
.student-greeting-insights strong { color: var(--ink); font-size: 19px; }
.student-greeting-insights em { overflow: hidden; color: var(--muted); font-size: 10px; font-style: normal; text-overflow: ellipsis; white-space: nowrap; }
.today-status {
min-width: 180px;
margin-left: auto;
@@ -871,12 +927,14 @@ onMounted(loadOverview)
}
@media (max-width: 980px) {
.student-greeting-insights { grid-template-columns: 1fr repeat(3, minmax(0, 1fr)); }
.student-overview-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 600px) {
.student-greeting-insights { padding: 15px 18px; grid-template-columns: 1fr; }
.student-overview {
gap: 12px;
}