feat(exams,homework,parent): V3 审计深度修复 — 批量批改/考试分析/提交反馈/家长视图/移动端优化
V3-5: exam-actions.tsx 集成 useExamHomeworkFeatures hook,按角色控制菜单项可见性 V3-7: 批量批改 — 新增 batchAutoGradeSubmissions data-access + Server Action + HomeworkBatchGradingView 组件 V3-8: 考试分析仪表盘 — 新增 getExamAnalytics stats-service + ExamAnalyticsDashboard 组件 + /teacher/exams/[id]/analytics 路由 V3-9: 提交后即时反馈页 — 新增 HomeworkSubmissionResult 组件 + /student/learning/assignments/[id]/result 路由 V3-11: 家长考试详情 — 新增 ChildExamDetail 组件 + getStudentExamResults data-access + child-detail-panel exams Tab V3-12: 移动端触控优化 — 题目导航与考试操作按钮 44px 最小触控目标 修复: instrumentation.ts 适配器补全 questionCount/averageScore/overdueCount 字段 修复: exam-homework-port.ts 类型导入对齐 ExamWithQuestionsForHomework 修复: trend-line-chart.tsx 数据类型允许 undefined(classAverage 可选场景) 同步更新 004/005 架构文档
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
import {
|
||||
getStudentDashboardGrades,
|
||||
getStudentHomeworkAssignments,
|
||||
getStudentExamResults,
|
||||
} from "@/modules/homework/data-access"
|
||||
import { getStudentGradeSummary } from "@/modules/grades/data-access"
|
||||
import { getGradeNameById } from "@/modules/school/data-access"
|
||||
@@ -22,6 +23,7 @@ import type {
|
||||
ChildDashboardData,
|
||||
ChildHomeworkSummaryData,
|
||||
ChildScheduleItem,
|
||||
ChildWeeklyScheduleItem,
|
||||
ParentChildRelation,
|
||||
ParentDashboardData,
|
||||
} from "./types"
|
||||
@@ -174,26 +176,50 @@ const buildTodaySchedule = (
|
||||
.sort((a, b) => a.startTime.localeCompare(b.startTime))
|
||||
}
|
||||
|
||||
const buildWeeklySchedule = (
|
||||
schedule: Awaited<ReturnType<typeof getStudentSchedule>>,
|
||||
): ChildWeeklyScheduleItem[] => {
|
||||
return schedule
|
||||
.map((s) => ({
|
||||
id: s.id,
|
||||
classId: s.classId,
|
||||
className: s.className,
|
||||
course: s.course,
|
||||
startTime: s.startTime,
|
||||
endTime: s.endTime,
|
||||
location: s.location ?? null,
|
||||
weekday: s.weekday,
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
a.weekday === b.weekday
|
||||
? a.startTime.localeCompare(b.startTime)
|
||||
: a.weekday - b.weekday,
|
||||
)
|
||||
}
|
||||
|
||||
export const getChildDashboardData = cache(
|
||||
async (studentId: string, relation: string | null = null): Promise<ChildDashboardData | null> => {
|
||||
const basicInfo = await getChildBasicInfo(studentId, relation)
|
||||
if (!basicInfo) return null
|
||||
|
||||
const [enrolledClasses, schedule, assignments, gradeTrend, gradeSummary] = await Promise.all([
|
||||
const [enrolledClasses, schedule, assignments, gradeTrend, gradeSummary, examResults] = await Promise.all([
|
||||
getStudentClasses(studentId),
|
||||
getStudentSchedule(studentId),
|
||||
getStudentHomeworkAssignments(studentId),
|
||||
getStudentDashboardGrades(studentId),
|
||||
getStudentGradeSummary(studentId),
|
||||
getStudentExamResults(studentId),
|
||||
])
|
||||
|
||||
return {
|
||||
basicInfo,
|
||||
enrolledClasses,
|
||||
todaySchedule: buildTodaySchedule(schedule),
|
||||
weeklySchedule: buildWeeklySchedule(schedule),
|
||||
homeworkSummary: buildHomeworkSummary(assignments),
|
||||
gradeTrend,
|
||||
gradeSummary,
|
||||
examResults,
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -224,3 +250,20 @@ export const getParentDashboardData = cache(
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
* 获取家长所有子女的轻量列表(id + name),用于详情页头部多子女切换器。
|
||||
* 一次批量查询,避免 N+1。
|
||||
*/
|
||||
export const getChildNameList = cache(
|
||||
async (parentId: string): Promise<Array<{ id: string; name: string | null }>> => {
|
||||
const relations = await getChildren(parentId)
|
||||
if (relations.length === 0) return []
|
||||
|
||||
const nameMap = await getUserNamesByIds(relations.map((r) => r.studentId))
|
||||
return relations.map((r) => ({
|
||||
id: r.studentId,
|
||||
name: nameMap.get(r.studentId)?.name ?? null,
|
||||
}))
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user