refactor(modules): update classes, course-plans, diagnostic, questions, settings, student, layout
- Update classes data-access (invitations, main) for invitation management - Update course-plans actions, data-access, and types - Update diagnostic data-access for report queries - Update questions data-access for question bank queries - Update settings actions, ai-provider-settings-card, data-access, and types - Update student course-filters, student-courses-view, student-schedule-filters, student-schedule-view - Update layout app-sidebar, site-header, and navigation config
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
import {
|
||||
getCoursePlans,
|
||||
getCoursePlanById,
|
||||
getGradeCoursePlanProgress,
|
||||
createCoursePlan,
|
||||
updateCoursePlan,
|
||||
deleteCoursePlan,
|
||||
@@ -22,7 +23,7 @@ import {
|
||||
updateCoursePlanItem,
|
||||
deleteCoursePlanItem,
|
||||
} from "./data-access"
|
||||
import type { CoursePlanWithItems, GetCoursePlansParams, CoursePlanListItem } from "./types"
|
||||
import type { CoursePlanWithItems, GetCoursePlansParams, CoursePlanListItem, GradeCoursePlanProgressResult } from "./types"
|
||||
|
||||
const revalidatePlanPaths = (id?: string) => {
|
||||
revalidatePath("/admin/course-plans")
|
||||
@@ -261,3 +262,23 @@ export async function toggleCoursePlanItemCompletedAction(
|
||||
return handleActionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 年级仪表盘 - 维度4:获取年级下所有班级的教学计划进度。
|
||||
*/
|
||||
export async function getGradeCoursePlanProgressAction(
|
||||
gradeId: string
|
||||
): Promise<ActionState<GradeCoursePlanProgressResult>> {
|
||||
try {
|
||||
await requirePermission(Permissions.COURSE_PLAN_READ)
|
||||
|
||||
if (!gradeId || gradeId.trim().length === 0) {
|
||||
return { success: false, message: "Invalid grade id" }
|
||||
}
|
||||
|
||||
const data = await getGradeCoursePlanProgress({ gradeId })
|
||||
return { success: true, data }
|
||||
} catch (e) {
|
||||
return handleActionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import type {
|
||||
CoursePlanStatus,
|
||||
CoursePlanWithItems,
|
||||
GetCoursePlansParams,
|
||||
GradeCoursePlanProgressItem,
|
||||
GradeCoursePlanProgressResult,
|
||||
ReorderCoursePlanItemInput,
|
||||
} from "./types"
|
||||
import type {
|
||||
@@ -324,3 +326,100 @@ export const getSubjectOptions = cache(async (): Promise<{ id: string; name: str
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 年级仪表盘 - 维度4:获取年级下所有班级的教学计划进度。
|
||||
* 通过 getClassesByGradeId 获取年级下所有班级,再用 inArray 查询 course_plans,
|
||||
* 关联 course_plan_items 统计条目完成情况。
|
||||
*/
|
||||
export const getGradeCoursePlanProgress = cache(
|
||||
async (params: { gradeId: string }): Promise<GradeCoursePlanProgressResult> => {
|
||||
const { getClassesByGradeId } = await import("@/modules/classes/data-access")
|
||||
const classRows = await getClassesByGradeId(params.gradeId)
|
||||
|
||||
if (classRows.length === 0) {
|
||||
return {
|
||||
gradeId: params.gradeId,
|
||||
overall: { totalPlans: 0, totalHours: 0, completedHours: 0, progressRate: 0, activePlans: 0, completedPlans: 0 },
|
||||
items: [],
|
||||
}
|
||||
}
|
||||
|
||||
const classIds = classRows.map((c) => c.id)
|
||||
|
||||
// 查询年级下所有教学计划(含班级/科目/教师名称)
|
||||
const planRows = await buildPlanSelect()
|
||||
.where(inArray(coursePlans.classId, classIds))
|
||||
.orderBy(asc(classes.name), asc(subjects.name))
|
||||
|
||||
if (planRows.length === 0) {
|
||||
return {
|
||||
gradeId: params.gradeId,
|
||||
overall: { totalPlans: 0, totalHours: 0, completedHours: 0, progressRate: 0, activePlans: 0, completedPlans: 0 },
|
||||
items: [],
|
||||
}
|
||||
}
|
||||
|
||||
const planIds = planRows.map((p) => p.id)
|
||||
|
||||
// 查询所有计划的周次条目,统计完成情况
|
||||
const itemRows = await db
|
||||
.select({
|
||||
planId: coursePlanItems.planId,
|
||||
isCompleted: coursePlanItems.isCompleted,
|
||||
})
|
||||
.from(coursePlanItems)
|
||||
.where(inArray(coursePlanItems.planId, planIds))
|
||||
|
||||
const itemStatsByPlan = new Map<string, { total: number; completed: number }>()
|
||||
for (const it of itemRows) {
|
||||
const entry = itemStatsByPlan.get(it.planId) ?? { total: 0, completed: 0 }
|
||||
entry.total += 1
|
||||
if (it.isCompleted) entry.completed += 1
|
||||
itemStatsByPlan.set(it.planId, entry)
|
||||
}
|
||||
|
||||
const items: GradeCoursePlanProgressItem[] = planRows.map((p) => {
|
||||
const totalHours = Number(p.totalHours)
|
||||
const completedHours = Number(p.completedHours)
|
||||
const progressRate = totalHours > 0
|
||||
? Math.round((completedHours / totalHours) * 1000) / 10
|
||||
: 0
|
||||
const itemStats = itemStatsByPlan.get(p.id) ?? { total: 0, completed: 0 }
|
||||
return {
|
||||
planId: p.id,
|
||||
classId: p.classId,
|
||||
className: p.className,
|
||||
subjectId: p.subjectId,
|
||||
subjectName: p.subjectName,
|
||||
teacherName: p.teacherName,
|
||||
semester: p.semester,
|
||||
totalHours,
|
||||
completedHours,
|
||||
progressRate,
|
||||
status: p.status,
|
||||
itemCount: itemStats.total,
|
||||
completedItemCount: itemStats.completed,
|
||||
}
|
||||
})
|
||||
|
||||
const totalHours = items.reduce((sum, i) => sum + i.totalHours, 0)
|
||||
const completedHours = items.reduce((sum, i) => sum + i.completedHours, 0)
|
||||
const progressRate = totalHours > 0
|
||||
? Math.round((completedHours / totalHours) * 1000) / 10
|
||||
: 0
|
||||
|
||||
return {
|
||||
gradeId: params.gradeId,
|
||||
overall: {
|
||||
totalPlans: items.length,
|
||||
totalHours,
|
||||
completedHours,
|
||||
progressRate,
|
||||
activePlans: items.filter((i) => i.status === "active").length,
|
||||
completedPlans: items.filter((i) => i.status === "completed").length,
|
||||
},
|
||||
items,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -58,3 +58,40 @@ export interface ReorderCoursePlanItemInput {
|
||||
id: string
|
||||
week: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 年级仪表盘 - 维度4:年级下各班级/科目的课本进度。
|
||||
*/
|
||||
export interface GradeCoursePlanProgressItem {
|
||||
planId: string
|
||||
classId: string
|
||||
className: string | null
|
||||
subjectId: string
|
||||
subjectName: string | null
|
||||
teacherName: string | null
|
||||
semester: CoursePlanSemester
|
||||
totalHours: number
|
||||
completedHours: number
|
||||
/** 进度百分比 0-100 */
|
||||
progressRate: number
|
||||
status: CoursePlanStatus
|
||||
/** 周次条目总数 */
|
||||
itemCount: number
|
||||
/** 已完成条目数 */
|
||||
completedItemCount: number
|
||||
}
|
||||
|
||||
export interface GradeCoursePlanProgressResult {
|
||||
gradeId: string
|
||||
/** 年级整体进度汇总 */
|
||||
overall: {
|
||||
totalPlans: number
|
||||
totalHours: number
|
||||
completedHours: number
|
||||
progressRate: number
|
||||
activePlans: number
|
||||
completedPlans: number
|
||||
}
|
||||
/** 按班级 + 科目拆分的进度列表 */
|
||||
items: GradeCoursePlanProgressItem[]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user