feat(teacher-portal): 完成参考项目差距闭环 P3-P7 全量实现

- P3 考试/作业/成绩 mutation + 详情页 + 批改界面 + 乐观更新 + 多 Tab 同步
- P4 知识图谱 SVG 可视化 + 学情分析仪表盘 + parent-portal Remote
- P5 WebSocket 通知中心 + AI 出题(SSE) + AI 教案 + AI 学情报告
- P6 可观测性硬化:Sentry + WebVitals + OTel + A11y + 性能配置 + Cookie 迁移
- P7 参考项目差距闭环:新增 35 个页面覆盖 13 个缺失模块
  - attendance(考勤 4 页)/questions(题库)/textbooks(教材 2 页)
  - classes/[id] 详情 + classes/schedule 课表
  - course-plans(2 页)/diagnostic(2 页)/error-book/practice
  - exams/[id]/build 组卷 + exams/[id]/analytics 考后分析
  - exams/[id]/edit-rich 富文本编辑 + exams/[id]/proctoring 监考
  - grades/entry 批量录入 + grades/stats 统计 + grades/analytics 分析 + grades/report-card 报告卡
  - homework/submissions 列表 + assignments/[id]/submissions 批量批改
  - homework/submissions/[submissionId] 单份批改 + scan-grading 扫描批改
  - lesson-plans 编辑器 + library + calendar + heatmap 5 页
  - elective 选修课 3 页 /leave 请假 /schedule-changes 调课
- P7 基础设施:61 GraphQL operations + 5 handlers + 13 fixtures + 11 viewports
- 集成 browser.ts/server.ts 注册所有 p7 handlers(fallthrough 顺序)
- viewports.ts 扩展 11 个新导航项
- 验证:tsc --noEmit 零错误 + eslint 零错误
- 文档:workline.md 新增 §5 P7 参考项目差距闭环(含完整文件清单)
This commit is contained in:
SpecialX
2026-07-13 14:27:04 +08:00
parent f13ca612e6
commit d49d211425
117 changed files with 30867 additions and 108 deletions

View File

@@ -0,0 +1,499 @@
/**
* GraphQL operations - P7 扩展(选修课 + 富文本编辑 + 监考 + 扫描批改 + 课标热力图)
*
* 维护者ai13teacher-portal
* 关联graphql.ts P2/P3 扩展、workline.md §3 P7 交付物
*
* schema 尚未在 ARB-001 P2 第一版中定义,开发期通过 MSW 拦截返回 mock 响应;
* 上游就绪后由 ai03 在 teacher-bff.graphql 扩展,届时合并本文件到 graphql.ts。
*
* 查询范围P7-advanced
* - ElectiveCourses / ElectiveCourseDetail选修课列表 + 详情
* - CreateElectiveCourse / UpdateElectiveCourse / PublishElectiveCourse / CancelElectiveCourse
* - ExamRichEditor / SaveExamRichContent富文本试卷内容
* - ProctoringStatus / ProctoringEvent监考状态 + 事件
* - SubmissionScanGrading / SaveScanGrading扫描批改
* - LessonPlanHeatmap课标覆盖热力图
*/
import { gql } from "urql";
// ============ Types选修课 ============
/** 选修课状态 */
export type ElectiveCourseStatus =
| "draft"
| "open"
| "closed"
| "cancelled";
/** 选课学生项 */
export interface ElectiveStudent {
studentId: string;
studentName: string;
studentNo: string;
enrolledAt: string;
status: "enrolled" | "cancelled";
}
/** 选修课列表项 */
export interface ElectiveCourseItem {
id: string;
title: string;
subject: string;
grade: string;
capacity: number;
enrolledCount: number;
teacherId: string;
teacherName: string;
semester: string;
status: ElectiveCourseStatus;
description: string | null;
}
/** 选修课详情(含学生列表) */
export interface ElectiveCourseDetail extends ElectiveCourseItem {
students: ElectiveStudent[];
createdAt: string;
updatedAt: string;
}
/** 创建选修课输入 */
export interface CreateElectiveCourseInput {
title: string;
subject: string;
grade: string;
capacity: number;
semester: string;
description?: string | null;
}
/** 更新选修课输入 */
export interface UpdateElectiveCourseInput {
id: string;
title?: string;
subject?: string;
grade?: string;
capacity?: number;
semester?: string;
description?: string | null;
}
/** 创建/更新结果 */
export interface ElectiveCourseMutationResult {
id: string;
status: ElectiveCourseStatus;
updatedAt: string;
}
// ============ Types富文本编辑 ============
/** 富文本试卷内容Tiptap examNodes JSON 结构mock 用 JSON */
export interface ExamRichContent {
examId: string;
title: string;
totalScore: number;
questionCount: number;
content: unknown; // Tiptap JSON结构由编辑器决定
updatedAt: string;
}
/** 保存富文本输入 */
export interface SaveExamRichContentInput {
examId: string;
content: unknown;
}
/** 保存富文本结果 */
export interface SaveExamRichContentResult {
examId: string;
updatedAt: string;
}
// ============ Types监考 ============
/** 监考学生状态 */
export type ProctoringStudentStatus =
| "online"
| "offline"
| "submitted"
| "flagged";
/** 监考摘要 */
export interface ProctoringSummary {
examId: string;
expectedCount: number;
onlineCount: number;
submittedCount: number;
flaggedCount: number;
isProctoring: boolean;
startedAt: string | null;
}
/** 监考学生状态项 */
export interface ProctoringStudent {
studentId: string;
studentName: string;
studentNo: string;
status: ProctoringStudentStatus;
lastEventAt: string | null;
ipAddress: string | null;
}
/** 监考事件类型 */
export type ProctoringEventType = "WARNING" | "FLAG" | "NOTE";
/** 监考事件项 */
export interface ProctoringEvent {
eventId: string;
examId: string;
studentId: string;
studentName: string;
studentNo: string;
eventType: ProctoringEventType;
note: string | null;
createdAt: string;
}
/** 监考状态完整结构 */
export interface ProctoringStatus {
summary: ProctoringSummary;
students: ProctoringStudent[];
recentEvents: ProctoringEvent[];
}
/** 提交监考事件输入 */
export interface ProctoringEventInput {
examId: string;
studentId: string;
eventType: ProctoringEventType;
note?: string | null;
}
/** 提交监考事件结果 */
export interface ProctoringEventResult {
eventId: string;
createdAt: string;
}
// ============ Types扫描批改 ============
/** 扫描图片项 */
export interface ScanImage {
imageId: string;
url: string;
page: number;
}
/** 扫描批改单题项 */
export interface ScanGradingItem {
questionId: string;
questionTitle: string;
questionOrder: number;
maxScore: number;
recognizedAnswer: string;
aiSuggestedScore: number | null;
aiSuggestion: string | null;
score: number | null;
feedback: string | null;
}
/** 扫描批改视图 */
export interface SubmissionScanGrading {
submissionId: string;
homeworkTitle: string;
studentName: string;
studentNo: string;
submittedAt: string;
images: ScanImage[];
items: ScanGradingItem[];
prevSubmissionId: string | null;
nextSubmissionId: string | null;
}
/** 保存扫描批改单项 */
export interface SaveScanGradingItem {
questionId: string;
score: number;
feedback?: string | null;
}
/** 保存扫描批改输入 */
export interface SaveScanGradingInput {
submissionId: string;
items: SaveScanGradingItem[];
}
/** 保存扫描批改结果 */
export interface SaveScanGradingResult {
submissionId: string;
totalScore: number;
savedAt: string;
}
// ============ Types课标热力图 ============
/** 热力图知识点项 */
export interface HeatmapKnowledgePoint {
kpId: string;
name: string;
chapterName: string;
}
/** 热力图课案项 */
export interface HeatmapLessonPlan {
planId: string;
title: string;
}
/** 热力图覆盖矩阵单元格 */
export interface HeatmapCell {
kpId: string;
planId: string;
coverage: number; // 0-3 覆盖课案数
}
/** 课标覆盖热力图 */
export interface LessonPlanHeatmap {
textbookId: string;
textbookTitle: string;
knowledgePoints: HeatmapKnowledgePoint[];
lessonPlans: HeatmapLessonPlan[];
cells: HeatmapCell[];
}
// ============ Queries / Mutations选修课 ============
/** 选修课列表查询(按状态筛选,教师范围) */
export const ElectiveCoursesQuery = gql`
query ElectiveCourses($status: String, $subject: String) {
electiveCourses(status: $status, subject: $subject) {
id
title
subject
grade
capacity
enrolledCount
teacherId
teacherName
semester
status
description
}
}
`;
/** 选修课详情查询(含学生列表) */
export const ElectiveCourseDetailQuery = gql`
query ElectiveCourseDetail($id: ID!) {
electiveCourseDetail(id: $id) {
id
title
subject
grade
capacity
enrolledCount
teacherId
teacherName
semester
status
description
students {
studentId
studentName
studentNo
enrolledAt
status
}
createdAt
updatedAt
}
}
`;
/** 创建选修课 Mutation */
export const CreateElectiveCourseMutation = gql`
mutation CreateElectiveCourse($input: CreateElectiveCourseInput!) {
createElectiveCourse(input: $input) {
id
status
updatedAt
}
}
`;
/** 更新选修课 Mutation */
export const UpdateElectiveCourseMutation = gql`
mutation UpdateElectiveCourse($input: UpdateElectiveCourseInput!) {
updateElectiveCourse(input: $input) {
id
status
updatedAt
}
}
`;
/** 发布选修课 Mutation */
export const PublishElectiveCourseMutation = gql`
mutation PublishElectiveCourse($id: ID!) {
publishElectiveCourse(id: $id) {
id
status
updatedAt
}
}
`;
/** 取消选修课 Mutation */
export const CancelElectiveCourseMutation = gql`
mutation CancelElectiveCourse($id: ID!) {
cancelElectiveCourse(id: $id) {
id
status
updatedAt
}
}
`;
// ============ Queries / Mutations富文本编辑 ============
/** 富文本试卷内容查询 */
export const ExamRichEditorQuery = gql`
query ExamRichEditor($examId: ID!) {
examRichEditor(examId: $examId) {
examId
title
totalScore
questionCount
content
updatedAt
}
}
`;
/** 保存富文本内容 Mutation */
export const SaveExamRichContentMutation = gql`
mutation SaveExamRichContent($input: SaveExamRichContentInput!) {
saveExamRichContent(input: $input) {
examId
updatedAt
}
}
`;
// ============ Queries / Mutations监考 ============
/** 监考状态查询 */
export const ProctoringStatusQuery = gql`
query ProctoringStatus($examId: ID!) {
proctoringStatus(examId: $examId) {
summary {
examId
expectedCount
onlineCount
submittedCount
flaggedCount
isProctoring
startedAt
}
students {
studentId
studentName
studentNo
status
lastEventAt
ipAddress
}
recentEvents {
eventId
examId
studentId
studentName
studentNo
eventType
note
createdAt
}
}
}
`;
/** 提交监考事件 Mutation */
export const ProctoringEventMutation = gql`
mutation ProctoringEvent($input: ProctoringEventInput!) {
proctoringEvent(input: $input) {
eventId
createdAt
}
}
`;
// ============ Queries / Mutations扫描批改 ============
/** 扫描批改视图查询 */
export const SubmissionScanGradingQuery = gql`
query SubmissionScanGrading($submissionId: ID!) {
submissionScanGrading(submissionId: $submissionId) {
submissionId
homeworkTitle
studentName
studentNo
submittedAt
images {
imageId
url
page
}
items {
questionId
questionTitle
questionOrder
maxScore
recognizedAnswer
aiSuggestedScore
aiSuggestion
score
feedback
}
prevSubmissionId
nextSubmissionId
}
}
`;
/** 保存扫描批改 Mutation */
export const SaveScanGradingMutation = gql`
mutation SaveScanGrading($input: SaveScanGradingInput!) {
saveScanGrading(input: $input) {
submissionId
totalScore
savedAt
}
}
`;
// ============ Queries课标热力图 ============
/** 课标覆盖热力图查询(按教材 ID */
export const LessonPlanHeatmapQuery = gql`
query LessonPlanHeatmap($textbookId: ID!) {
lessonPlanHeatmap(textbookId: $textbookId) {
textbookId
textbookTitle
knowledgePoints {
kpId
name
chapterName
}
lessonPlans {
planId
title
}
cells {
kpId
planId
coverage
}
}
}
`;