feat(portal-shell): lesson-plans 模块 6 页迁移(教师域 §9.1 B2)
§9.1 line 629 教师域 lesson-plans 行:
- /lesson-plans (列表) / /new (表单) / /library (教案库)
- /calendar (日历) / /heatmap (热力图) / /[planId]/edit (工作台)
契约:全 ❌ schema 无 → MSW 兜底 + @contract-pending
新增文件:
- src/lib/api/lesson-plans.ts (8 hooks)
- src/lib/api/operations/lesson-plans.graphql.ts (8 documents)
- src/features/teacher/lesson-plans/ (clients + transformations + tests)
- src/app/shell/teacher/lesson-plans/ (6 page.tsx + loading.tsx + error.tsx)
修改文件:
- src/mocks/graphql-data.ts (mock 数据 + handler cases)
- src/messages/{zh-CN,en}.json (lessonPlans i18n 命名空间)
- src/lib/api/{index,operations/index}.ts (导出 lesson-plans)
- src/shared/lib/route-permissions.ts (lesson-plans 路由权限声明)
- scripts/check-page-count.ts (baseline 31 → 37)
DoD 验收(§11.3 11 项):
- typecheck 0 errors
- lint 0 errors
- vitest 405 tests passed (新增 48 tests)
- lint:tokens 0 errors
- check:pages 37 PASS
- route-permissions 已声明
- 三态(loading/error/empty)齐备
- @contract-pending + MSW 兜底
- i18n zh-CN + en 同步
关联:ARCHITECTURE.md §5.3 / §5.4 / §9.1 / §10 P2 / §11.3 / §11.4
契约工单:docs/architecture/issues/contracts/core-edu_contract.md
This commit is contained in:
@@ -17,6 +17,7 @@ export * from "./teacher";
|
||||
export * from "./exams";
|
||||
export * from "./homework";
|
||||
export * from "./grades";
|
||||
export * from "./lesson-plans";
|
||||
export * from "./student";
|
||||
export * from "./parent";
|
||||
export * from "./admin";
|
||||
|
||||
517
apps/portal-shell/src/lib/api/lesson-plans.ts
Normal file
517
apps/portal-shell/src/lib/api/lesson-plans.ts
Normal file
@@ -0,0 +1,517 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* Lesson Plans domain API(ARCHITECTURE.md §5.1 / §5.3 / §9.1 教师域教案模块)
|
||||
*
|
||||
* 契约状态:全 ❌(schema 无 lessonPlan(id) / lessonPlans / lessonPlanLibrary /
|
||||
* lessonPlanCalendar / lessonPlanHeatmap 根字段,也无 Mutation 类型)
|
||||
* → 所有查询与 mutation 走 MSW 兜底(@contract-pending)
|
||||
*
|
||||
* 命名说明:本域 hook 命名遵循 homework.ts 模式(`useLessonPlansList` 而非
|
||||
* `useLessonPlans`),以避免与 teacher.ts 中存量 `useLessonPlans(classId)`
|
||||
* (用于 lesson-plan-editor widget)冲突。详情类型用 `LessonPlanDetail`
|
||||
* 同样为避免与 teacher.ts 的 `LessonPlan` 接口冲突。
|
||||
*
|
||||
* 契约工单:docs/architecture/issues/contracts/core-edu_contract.md#lesson-plans
|
||||
* 后端补齐后:重跑 normalize + codegen → 关闭 skipDocumentsValidation → 切换 fetcher → 删 mock
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.3 契约纪律 / §5.4 MSW 兜底 / §9.1 / §11.4 契约工单
|
||||
*/
|
||||
import type { FetchPolicy } from "@apollo/client";
|
||||
|
||||
import { useWidgetMutation } from "../useWidgetMutation";
|
||||
import { useWidgetQuery } from "../useWidgetQuery";
|
||||
import { ApiError } from "./errors";
|
||||
import {
|
||||
CREATE_LESSON_PLAN_DOC,
|
||||
DELETE_LESSON_PLAN_DOC,
|
||||
GET_LESSON_PLAN_CALENDAR_DOC,
|
||||
GET_LESSON_PLAN_DOC,
|
||||
GET_LESSON_PLAN_HEATMAP_DOC,
|
||||
GET_LESSON_PLAN_LIBRARY_DOC,
|
||||
GET_LESSON_PLANS_LIST_DOC,
|
||||
UPDATE_LESSON_PLAN_DOC,
|
||||
} from "./operations/lesson-plans.graphql";
|
||||
import type { UseQueryResult } from "./types";
|
||||
|
||||
// ===== 数据类型(@contract-pending,与 MSW mock 数据形状对齐)=====
|
||||
|
||||
/** 教案状态枚举 */
|
||||
export type LessonPlanStatus = "DRAFT" | "PUBLISHED" | "ARCHIVED";
|
||||
|
||||
/** 教案大纲节点(编辑器左侧树) */
|
||||
export interface LessonPlanOutlineNode {
|
||||
id: string;
|
||||
title: string;
|
||||
type: "section" | "topic" | "activity" | "assessment";
|
||||
order: number;
|
||||
children?: LessonPlanOutlineNode[];
|
||||
}
|
||||
|
||||
/** 教案关联资源 */
|
||||
export interface LessonPlanResource {
|
||||
id: string;
|
||||
name: string;
|
||||
type: "link" | "file" | "video" | "image";
|
||||
url: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 教案实体(完整字段,用于编辑工作台)。
|
||||
*
|
||||
* 命名为 `LessonPlanDetail` 以避免与 teacher.ts 的 `LessonPlan` 接口冲突
|
||||
* (后者是 widget 时代的简化 stub,被 lesson-plan-editor widget 使用)。
|
||||
*/
|
||||
export interface LessonPlanDetail {
|
||||
id: string;
|
||||
title: string;
|
||||
gradeId: string;
|
||||
subjectId: string;
|
||||
objectives: string;
|
||||
content: string;
|
||||
attachments: string[];
|
||||
duration: number;
|
||||
status: LessonPlanStatus;
|
||||
outline: LessonPlanOutlineNode[];
|
||||
resources: LessonPlanResource[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** 教案列表项(轻量字段集,用于列表渲染) */
|
||||
export interface LessonPlanListItem {
|
||||
id: string;
|
||||
title: string;
|
||||
gradeId: string;
|
||||
subjectId: string;
|
||||
objectives: string;
|
||||
duration: number;
|
||||
status: LessonPlanStatus;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** 列表查询响应(@contract-pending 假契约形状,MSW 返回此结构) */
|
||||
interface LessonPlansListResponse {
|
||||
lessonPlans: {
|
||||
items: LessonPlanListItem[];
|
||||
total: number;
|
||||
};
|
||||
}
|
||||
|
||||
/** 单查响应(@contract-pending,MSW 返回此结构) */
|
||||
interface LessonPlanResponse {
|
||||
lessonPlan: LessonPlanDetail | null;
|
||||
}
|
||||
|
||||
/** 创建教案输入 */
|
||||
export interface CreateLessonPlanInput {
|
||||
title: string;
|
||||
gradeId: string;
|
||||
subjectId: string;
|
||||
objectives?: string;
|
||||
content?: string;
|
||||
attachments?: string[];
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
/** 创建教案 mutation 响应(@contract-pending) */
|
||||
interface CreateLessonPlanResponse {
|
||||
createLessonPlan: { id: string } | null;
|
||||
}
|
||||
|
||||
/** 更新教案输入 */
|
||||
export interface UpdateLessonPlanInput {
|
||||
id: string;
|
||||
title?: string;
|
||||
objectives?: string;
|
||||
content?: string;
|
||||
attachments?: string[];
|
||||
duration?: number;
|
||||
status?: LessonPlanStatus;
|
||||
outline?: LessonPlanOutlineNode[];
|
||||
resources?: LessonPlanResource[];
|
||||
}
|
||||
|
||||
/** 更新教案 mutation 响应(@contract-pending) */
|
||||
interface UpdateLessonPlanResponse {
|
||||
updateLessonPlan: { id: string } | null;
|
||||
}
|
||||
|
||||
/** 删除教案 mutation 响应(@contract-pending) */
|
||||
interface DeleteLessonPlanResponse {
|
||||
deleteLessonPlan: { id: string } | null;
|
||||
}
|
||||
|
||||
// ===== 教案库类型(@contract-pending 全 MSW)=====
|
||||
|
||||
/** 教案库列表项(含共享库展示字段) */
|
||||
export interface LessonPlanLibraryItem {
|
||||
id: string;
|
||||
title: string;
|
||||
subjectId: string;
|
||||
subjectName: string;
|
||||
grade: string;
|
||||
duration: number;
|
||||
authorName: string;
|
||||
downloadCount: number;
|
||||
rating: number;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** 教案库查询响应 */
|
||||
interface LessonPlanLibraryResponse {
|
||||
lessonPlanLibrary: {
|
||||
items: LessonPlanLibraryItem[];
|
||||
total: number;
|
||||
};
|
||||
}
|
||||
|
||||
// ===== 日历类型(@contract-pending 全 MSW)=====
|
||||
|
||||
/** 日历单条教案排期 */
|
||||
export interface CalendarLessonPlan {
|
||||
id: string;
|
||||
title: string;
|
||||
className: string;
|
||||
subjectName: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
status: LessonPlanStatus;
|
||||
}
|
||||
|
||||
/** 日历单日条目 */
|
||||
export interface CalendarDay {
|
||||
date: string;
|
||||
lessonPlans: CalendarLessonPlan[];
|
||||
}
|
||||
|
||||
/** 日历完整数据 */
|
||||
export interface LessonPlanCalendar {
|
||||
month: string;
|
||||
days: CalendarDay[];
|
||||
}
|
||||
|
||||
/** 日历查询响应 */
|
||||
interface LessonPlanCalendarResponse {
|
||||
lessonPlanCalendar: LessonPlanCalendar | null;
|
||||
}
|
||||
|
||||
// ===== 热力图类型(@contract-pending 全 MSW)=====
|
||||
|
||||
/** 热力图单元格 */
|
||||
export interface HeatmapCell {
|
||||
date: string;
|
||||
count: number;
|
||||
intensity: 0 | 1 | 2 | 3 | 4;
|
||||
}
|
||||
|
||||
/** 热力图完整数据 */
|
||||
export interface LessonPlanHeatmap {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
cells: HeatmapCell[];
|
||||
maxCount: number;
|
||||
}
|
||||
|
||||
/** 热力图查询响应 */
|
||||
interface LessonPlanHeatmapResponse {
|
||||
lessonPlanHeatmap: LessonPlanHeatmap | null;
|
||||
}
|
||||
|
||||
// ===== 查询选项 =====
|
||||
|
||||
export interface LessonPlanQueryOptions {
|
||||
enabled?: boolean;
|
||||
pollInterval?: number;
|
||||
fetchPolicy?: FetchPolicy;
|
||||
}
|
||||
|
||||
// ===== Hooks =====
|
||||
|
||||
/**
|
||||
* 查询教案列表(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 lessonPlans 根字段,由 MSW handlers 返回 mock 数据。
|
||||
* 后端补齐列表查询后切换到真实 fetcher,页面无需改动。
|
||||
*
|
||||
* 命名为 `useLessonPlansList` 以避免与 teacher.ts 的 `useLessonPlans` 冲突
|
||||
* (对齐 homework.ts 的 `useHomeworkList` 模式)。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 列表页 / §11.4 契约工单
|
||||
*/
|
||||
export function useLessonPlansList(
|
||||
filter: {
|
||||
gradeId?: string;
|
||||
subjectId?: string;
|
||||
status?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
},
|
||||
options?: LessonPlanQueryOptions,
|
||||
): UseQueryResult<{ items: LessonPlanListItem[]; total: number }> {
|
||||
const result = useWidgetQuery<
|
||||
LessonPlansListResponse,
|
||||
{
|
||||
gradeId?: string;
|
||||
subjectId?: string;
|
||||
status?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
>(
|
||||
GET_LESSON_PLANS_LIST_DOC,
|
||||
{
|
||||
gradeId: filter.gradeId,
|
||||
subjectId: filter.subjectId,
|
||||
status: filter.status,
|
||||
limit: filter.limit,
|
||||
offset: filter.offset,
|
||||
},
|
||||
{
|
||||
enabled: options?.enabled ?? true,
|
||||
fetchPolicy: options?.fetchPolicy,
|
||||
pollInterval: options?.pollInterval,
|
||||
},
|
||||
);
|
||||
return {
|
||||
data: result.data?.lessonPlans,
|
||||
loading: result.loading,
|
||||
error: result.error,
|
||||
refetch: result.refetch,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 id 查询教案详情(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 lessonPlan(id) 根字段,由 MSW handlers 返回 mock 数据。
|
||||
* 用于 /shell/teacher/lesson-plans/[planId]/edit 工作台页。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 工作台页 / §11.4 契约工单
|
||||
*/
|
||||
export function useLessonPlan(
|
||||
id: string,
|
||||
options?: LessonPlanQueryOptions,
|
||||
): UseQueryResult<LessonPlanDetail | null> {
|
||||
const result = useWidgetQuery<LessonPlanResponse, { id: string }>(
|
||||
GET_LESSON_PLAN_DOC,
|
||||
{ id },
|
||||
{
|
||||
...options,
|
||||
enabled: options?.enabled ?? id.length > 0,
|
||||
},
|
||||
);
|
||||
return {
|
||||
data: result.data?.lessonPlan ?? null,
|
||||
loading: result.loading,
|
||||
error: result.error,
|
||||
refetch: result.refetch,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建教案 mutation(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 Mutation 类型,由 MSW handlers 返回 mock 数据。
|
||||
* 用于 /shell/teacher/lesson-plans/new 表单页。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 表单页 / §11.4 契约工单
|
||||
*/
|
||||
export function useCreateLessonPlan(): {
|
||||
run: (input: CreateLessonPlanInput) => Promise<{ id: string }>;
|
||||
loading: boolean;
|
||||
error: unknown;
|
||||
} {
|
||||
const {
|
||||
run: rawRun,
|
||||
loading,
|
||||
error,
|
||||
} = useWidgetMutation<
|
||||
CreateLessonPlanResponse,
|
||||
{ input: CreateLessonPlanInput }
|
||||
>(CREATE_LESSON_PLAN_DOC);
|
||||
|
||||
const run = async (input: CreateLessonPlanInput): Promise<{ id: string }> => {
|
||||
const data = await rawRun({ input });
|
||||
if (!data?.createLessonPlan) {
|
||||
throw new ApiError("Failed to create lesson plan", "INTERNAL_ERROR");
|
||||
}
|
||||
return data.createLessonPlan;
|
||||
};
|
||||
|
||||
return { run, loading, error };
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新教案 mutation(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 Mutation 类型,由 MSW handlers 返回 mock 数据。
|
||||
* 用于 /shell/teacher/lesson-plans/[planId]/edit 工作台页保存。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 工作台页 / §11.4 契约工单
|
||||
*/
|
||||
export function useUpdateLessonPlan(): {
|
||||
run: (input: UpdateLessonPlanInput) => Promise<{ id: string }>;
|
||||
loading: boolean;
|
||||
error: unknown;
|
||||
} {
|
||||
const {
|
||||
run: rawRun,
|
||||
loading,
|
||||
error,
|
||||
} = useWidgetMutation<
|
||||
UpdateLessonPlanResponse,
|
||||
{ input: UpdateLessonPlanInput }
|
||||
>(UPDATE_LESSON_PLAN_DOC);
|
||||
|
||||
const run = async (input: UpdateLessonPlanInput): Promise<{ id: string }> => {
|
||||
const data = await rawRun({ input });
|
||||
if (!data?.updateLessonPlan) {
|
||||
throw new ApiError("Failed to update lesson plan", "INTERNAL_ERROR");
|
||||
}
|
||||
return data.updateLessonPlan;
|
||||
};
|
||||
|
||||
return { run, loading, error };
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除教案 mutation(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 Mutation 类型,由 MSW handlers 返回 mock 数据。
|
||||
* 用于列表/编辑页删除按钮。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 列表页 / §11.4 契约工单
|
||||
*/
|
||||
export function useDeleteLessonPlan(): {
|
||||
run: (id: string) => Promise<{ id: string }>;
|
||||
loading: boolean;
|
||||
error: unknown;
|
||||
} {
|
||||
const {
|
||||
run: rawRun,
|
||||
loading,
|
||||
error,
|
||||
} = useWidgetMutation<DeleteLessonPlanResponse, { id: string }>(
|
||||
DELETE_LESSON_PLAN_DOC,
|
||||
);
|
||||
|
||||
const run = async (id: string): Promise<{ id: string }> => {
|
||||
const data = await rawRun({ id });
|
||||
if (!data?.deleteLessonPlan) {
|
||||
throw new ApiError("Failed to delete lesson plan", "INTERNAL_ERROR");
|
||||
}
|
||||
return data.deleteLessonPlan;
|
||||
};
|
||||
|
||||
return { run, loading, error };
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询教案库列表(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 lessonPlanLibrary 根字段,由 MSW handlers 返回 mock 数据。
|
||||
* 用于 /shell/teacher/lesson-plans/library 教案库列表页。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 教案库页 / §11.4 契约工单
|
||||
*/
|
||||
export function useLessonPlanLibrary(
|
||||
filter: {
|
||||
subjectId?: string;
|
||||
grade?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
},
|
||||
options?: LessonPlanQueryOptions,
|
||||
): UseQueryResult<{ items: LessonPlanLibraryItem[]; total: number }> {
|
||||
const result = useWidgetQuery<
|
||||
LessonPlanLibraryResponse,
|
||||
{
|
||||
subjectId?: string;
|
||||
grade?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
>(
|
||||
GET_LESSON_PLAN_LIBRARY_DOC,
|
||||
{
|
||||
subjectId: filter.subjectId,
|
||||
grade: filter.grade,
|
||||
limit: filter.limit,
|
||||
offset: filter.offset,
|
||||
},
|
||||
{
|
||||
enabled: options?.enabled ?? true,
|
||||
fetchPolicy: options?.fetchPolicy,
|
||||
pollInterval: options?.pollInterval,
|
||||
},
|
||||
);
|
||||
return {
|
||||
data: result.data?.lessonPlanLibrary,
|
||||
loading: result.loading,
|
||||
error: result.error,
|
||||
refetch: result.refetch,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询教案日历数据(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 lessonPlanCalendar 根字段,由 MSW handlers 返回 mock 数据。
|
||||
* 用于 /shell/teacher/lesson-plans/calendar 日历视图。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 日历视图 / §11.4 契约工单
|
||||
*/
|
||||
export function useLessonPlanCalendar(
|
||||
month: string,
|
||||
options?: LessonPlanQueryOptions,
|
||||
): UseQueryResult<LessonPlanCalendar | null> {
|
||||
const result = useWidgetQuery<LessonPlanCalendarResponse, { month: string }>(
|
||||
GET_LESSON_PLAN_CALENDAR_DOC,
|
||||
{ month },
|
||||
{
|
||||
...options,
|
||||
enabled: options?.enabled ?? month.length > 0,
|
||||
},
|
||||
);
|
||||
return {
|
||||
data: result.data?.lessonPlanCalendar ?? null,
|
||||
loading: result.loading,
|
||||
error: result.error,
|
||||
refetch: result.refetch,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询教案热力图数据(@contract-pending,MSW 兜底)。
|
||||
*
|
||||
* schema 无 lessonPlanHeatmap 根字段,由 MSW handlers 返回 mock 数据。
|
||||
* 用于 /shell/teacher/lesson-plans/heatmap 热力图视图。
|
||||
*
|
||||
* 关联:ARCHITECTURE.md §5.4 / §9.1 热力图视图 / §11.4 契约工单
|
||||
*/
|
||||
export function useLessonPlanHeatmap(
|
||||
range: { startDate: string; endDate: string },
|
||||
options?: LessonPlanQueryOptions,
|
||||
): UseQueryResult<LessonPlanHeatmap | null> {
|
||||
const result = useWidgetQuery<
|
||||
LessonPlanHeatmapResponse,
|
||||
{ startDate: string; endDate: string }
|
||||
>(
|
||||
GET_LESSON_PLAN_HEATMAP_DOC,
|
||||
{ startDate: range.startDate, endDate: range.endDate },
|
||||
{
|
||||
...options,
|
||||
enabled:
|
||||
options?.enabled ??
|
||||
(range.startDate.length > 0 && range.endDate.length > 0),
|
||||
},
|
||||
);
|
||||
return {
|
||||
data: result.data?.lessonPlanHeatmap ?? null,
|
||||
loading: result.loading,
|
||||
error: result.error,
|
||||
refetch: result.refetch,
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,7 @@ export * from "./teacher.graphql";
|
||||
export * from "./exams.graphql";
|
||||
export * from "./homework.graphql";
|
||||
export * from "./grades.graphql";
|
||||
export * from "./lesson-plans.graphql";
|
||||
export * from "./student.graphql";
|
||||
export * from "./parent.graphql";
|
||||
export * from "./admin.graphql";
|
||||
|
||||
207
apps/portal-shell/src/lib/api/operations/lesson-plans.graphql.ts
Normal file
207
apps/portal-shell/src/lib/api/operations/lesson-plans.graphql.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
// Lesson Plans domain GraphQL documents (ARCHITECTURE.md §5.3 契约纪律 / §9.1)
|
||||
//
|
||||
// 拆分原则:
|
||||
// - 全部 8 个查询/mutation:❌ schema 无对应字段/Mutation 类型
|
||||
// → 走 MSW 兜底(@contract-pending),等待后端补齐契约
|
||||
//
|
||||
// 注:combined-schema.graphql 中 lessonPlanStatus 仅在 ai 子图存在(状态轮询用),
|
||||
// core-edu 子图无 lessonPlan(id) / lessonPlans / lessonPlanLibrary / lessonPlanCalendar /
|
||||
// lessonPlanHeatmap 根字段,也无 Mutation 类型 → 全部走 MSW 兜底。
|
||||
//
|
||||
// 契约工单:docs/architecture/issues/contracts/core-edu_contract.md#lesson-plans
|
||||
// 关联:ARCHITECTURE.md §5.3 / §5.4 / §9.1 / §11.4
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// ── 假契约查询(@contract-pending)─────────────────────────────
|
||||
// 列表查询:schema 无 lessonPlans 根字段
|
||||
// 页面通过 MSW 兜底获取列表数据,后端补齐后切换 fetcher 指向真实查询
|
||||
//
|
||||
// 命名说明:operation name 与常量名使用 `GetLessonPlansList` /
|
||||
// `GET_LESSON_PLANS_LIST_DOC`,以避免与 teacher.graphql.ts 中存量
|
||||
// `GetLessonPlans` / `GET_LESSON_PLANS_DOC`(widget lesson-plan-editor 用)
|
||||
// 冲突。MSW handlers 也通过不同 operationName 区分两种响应形状。
|
||||
//
|
||||
// 契约工单:core-edu_contract.md#lesson-plans-list
|
||||
export const GET_LESSON_PLANS_LIST_DOC = gql`
|
||||
query GetLessonPlansList(
|
||||
$gradeId: ID
|
||||
$subjectId: ID
|
||||
$status: String
|
||||
$limit: Int
|
||||
$offset: Int
|
||||
) {
|
||||
lessonPlans(
|
||||
gradeId: $gradeId
|
||||
subjectId: $subjectId
|
||||
status: $status
|
||||
limit: $limit
|
||||
offset: $offset
|
||||
) {
|
||||
items {
|
||||
id
|
||||
title
|
||||
gradeId
|
||||
subjectId
|
||||
objectives
|
||||
duration
|
||||
status
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 单查(@contract-pending)────────────────────────────────────
|
||||
// schema 无 lessonPlan(id) 根字段 → MSW 兜底
|
||||
// 用于 /shell/teacher/lesson-plans/[planId]/edit 工作台页
|
||||
// 契约工单:core-edu_contract.md#lesson-plan-detail
|
||||
export const GET_LESSON_PLAN_DOC = gql`
|
||||
query GetLessonPlan($id: ID!) {
|
||||
lessonPlan(id: $id) {
|
||||
id
|
||||
title
|
||||
gradeId
|
||||
subjectId
|
||||
objectives
|
||||
content
|
||||
attachments
|
||||
duration
|
||||
status
|
||||
outline {
|
||||
id
|
||||
title
|
||||
type
|
||||
order
|
||||
children {
|
||||
id
|
||||
title
|
||||
type
|
||||
order
|
||||
}
|
||||
}
|
||||
resources {
|
||||
id
|
||||
name
|
||||
type
|
||||
url
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 假契约变更(@contract-pending)─────────────────────────────
|
||||
// 创建教案:schema 无 Mutation 类型
|
||||
// 页面通过 MSW 兜底提交,后端补齐 mutation 后切换 fetcher
|
||||
// 契约工单:core-edu_contract.md#create-lesson-plan
|
||||
export const CREATE_LESSON_PLAN_DOC = gql`
|
||||
mutation CreateLessonPlan($input: CreateLessonPlanInput!) {
|
||||
createLessonPlan(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 更新教案 mutation(@contract-pending)──────────────────────
|
||||
// schema 无 Mutation 类型 → MSW 兜底
|
||||
// 用于 /shell/teacher/lesson-plans/[planId]/edit 工作台页保存
|
||||
// 契约工单:core-edu_contract.md#update-lesson-plan
|
||||
export const UPDATE_LESSON_PLAN_DOC = gql`
|
||||
mutation UpdateLessonPlan($input: UpdateLessonPlanInput!) {
|
||||
updateLessonPlan(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 删除教案 mutation(@contract-pending)──────────────────────
|
||||
// schema 无 Mutation 类型 → MSW 兜底
|
||||
// 用于列表/编辑页删除按钮
|
||||
// 契约工单:core-edu_contract.md#delete-lesson-plan
|
||||
export const DELETE_LESSON_PLAN_DOC = gql`
|
||||
mutation DeleteLessonPlan($id: ID!) {
|
||||
deleteLessonPlan(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 教案库(@contract-pending 全 MSW)──────────────────────────
|
||||
// schema 无 lessonPlanLibrary 根字段 → MSW 兜底
|
||||
// 用于 /shell/teacher/lesson-plans/library 教案库列表页
|
||||
// 契约工单:core-edu_contract.md#lesson-plan-library
|
||||
export const GET_LESSON_PLAN_LIBRARY_DOC = gql`
|
||||
query GetLessonPlanLibrary(
|
||||
$subjectId: ID
|
||||
$grade: String
|
||||
$limit: Int
|
||||
$offset: Int
|
||||
) {
|
||||
lessonPlanLibrary(
|
||||
subjectId: $subjectId
|
||||
grade: $grade
|
||||
limit: $limit
|
||||
offset: $offset
|
||||
) {
|
||||
items {
|
||||
id
|
||||
title
|
||||
subjectId
|
||||
subjectName
|
||||
grade
|
||||
duration
|
||||
authorName
|
||||
downloadCount
|
||||
rating
|
||||
updatedAt
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 教案日历(@contract-pending 全 MSW)────────────────────────
|
||||
// schema 无 lessonPlanCalendar 根字段 → MSW 兜底
|
||||
// 用于 /shell/teacher/lesson-plans/calendar 日历视图
|
||||
// 契约工单:core-edu_contract.md#lesson-plan-calendar
|
||||
export const GET_LESSON_PLAN_CALENDAR_DOC = gql`
|
||||
query GetLessonPlanCalendar($month: String!) {
|
||||
lessonPlanCalendar(month: $month) {
|
||||
month
|
||||
days {
|
||||
date
|
||||
lessonPlans {
|
||||
id
|
||||
title
|
||||
className
|
||||
subjectName
|
||||
startTime
|
||||
endTime
|
||||
status
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// ── 教案热力图(@contract-pending 全 MSW)──────────────────────
|
||||
// schema 无 lessonPlanHeatmap 根字段 → MSW 兜底
|
||||
// 用于 /shell/teacher/lesson-plans/heatmap 热力图视图
|
||||
// 契约工单:core-edu_contract.md#lesson-plan-heatmap
|
||||
export const GET_LESSON_PLAN_HEATMAP_DOC = gql`
|
||||
query GetLessonPlanHeatmap($startDate: String!, $endDate: String!) {
|
||||
lessonPlanHeatmap(startDate: $startDate, endDate: $endDate) {
|
||||
startDate
|
||||
endDate
|
||||
cells {
|
||||
date
|
||||
count
|
||||
intensity
|
||||
}
|
||||
maxCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
Reference in New Issue
Block a user