kindergarten_java/reading-platform-frontend/src/api/school.ts
zhonghua 829a70e448 feat: 学校端课程排期功能完善
- 排课计划参考:对齐管理端课程包详情,支持时间/课程类型/课程名称/区域活动/备注五列
- 支持两种 schedule_ref_data 格式(周排课表、课程类型说明)
- 新建排课弹窗样式提取为 CreateScheduleModal.scss 修复 SASS 编译错误
- 切换视图(列表/课表/日历)时自动刷新数据
- 排课列表、课表、日历视图增加课程类型 tag 展示
- 后端:timetable/lesson-types 接口修复,LessonTypeEnum 补充类型

Made-with: Cursor
2026-03-19 16:34:48 +08:00

1163 lines
31 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { http } from './index';
// ==================== 类型定义 ====================
export interface TeacherQueryParams {
pageNum?: number;
pageSize?: number;
keyword?: string;
status?: string;
}
export interface Teacher {
id: number;
name: string;
phone: string;
email?: string;
loginAccount: string;
status: string;
classIds: number[];
classNames?: string | string[];
lessonCount?: number;
createdAt: string;
}
export interface CreateTeacherDto {
name: string;
phone: string;
email?: string;
loginAccount: string;
password?: string;
classIds?: number[];
}
export interface StudentQueryParams {
pageNum?: number;
pageSize?: number;
classId?: number;
keyword?: string;
}
export interface Student {
id: number;
name: string;
gender?: string;
birthDate?: string;
classId: number;
className?: string;
parentName?: string;
parentPhone?: string;
lessonCount?: number;
readingCount?: number;
avgScore?: number;
createdAt: string;
}
export interface CreateStudentDto {
name: string;
gender?: string;
birthDate?: string;
classId: number;
parentName?: string;
parentPhone?: string;
}
export interface ClassInfo {
id: number;
name: string;
grade: string;
teacherId?: number;
teacherName?: string;
studentCount: number;
lessonCount: number;
createdAt?: string;
teachers?: ClassTeacher[]; // 新增:教师团队
}
export interface ClassTeacher {
id: number;
teacherId: number;
teacherName: string;
teacherPhone?: string;
teacherEmail?: string;
role: 'MAIN' | 'ASSIST' | 'CARE';
isPrimary: boolean;
createdAt?: string;
}
export interface AddClassTeacherDto {
teacherId: number;
role: 'MAIN' | 'ASSIST' | 'CARE';
isPrimary?: boolean;
}
export interface UpdateClassTeacherDto {
role?: 'MAIN' | 'ASSIST' | 'CARE';
isPrimary?: boolean;
}
export interface TransferStudentDto {
toClassId: number;
reason?: string;
}
export interface StudentClassHistory {
id: number;
fromClass: { id: number; name: string; grade: string } | null;
toClass: { id: number; name: string; grade: string };
reason?: string;
operatedBy?: number;
createdAt: string;
}
export interface CreateClassDto {
name: string;
grade: string;
teacherId?: number;
}
export interface SchoolStats {
teacherCount: number;
studentCount: number;
classCount: number;
lessonCount: number;
}
export interface PackageInfo {
packageType: string;
teacherQuota: number;
studentQuota: number;
storageQuota: number;
teacherCount: number;
studentCount: number;
storageUsed: number;
startDate: string;
expireDate: string;
status: string;
}
export interface PackageUsage {
teacher: {
used: number;
quota: number;
percentage: number;
};
student: {
used: number;
quota: number;
percentage: number;
};
storage: {
used: number;
quota: number;
percentage: number;
};
}
// ==================== 教师管理 ====================
export const getTeachers = (params: TeacherQueryParams) =>
http.get<{ list: Teacher[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/teachers', { params });
export const getTeacher = (id: number) =>
http.get<Teacher>(`/v1/school/teachers/${id}`);
export const createTeacher = (data: CreateTeacherDto) =>
http.post<Teacher>('/v1/school/teachers', data);
export const updateTeacher = (id: number, data: Partial<CreateTeacherDto>) =>
http.put<Teacher>(`/v1/school/teachers/${id}`, data);
export const deleteTeacher = (id: number) =>
http.delete(`/v1/school/teachers/${id}`);
export const resetTeacherPassword = (id: number) =>
http.post<{ tempPassword: string }>(`/v1/school/teachers/${id}/reset-password`);
// ==================== 学生管理 ====================
export const getStudents = (params: StudentQueryParams) =>
http.get<{ list: Student[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/students', { params });
export const getStudent = (id: number) =>
http.get<Student>(`/v1/school/students/${id}`);
export const createStudent = (data: CreateStudentDto) =>
http.post<Student>('/v1/school/students', data);
export const updateStudent = (id: number, data: Partial<CreateStudentDto>) =>
http.put<Student>(`/v1/school/students/${id}`, data);
export const deleteStudent = (id: number) =>
http.delete(`/v1/school/students/${id}`);
// ==================== 学生批量导入 ====================
export interface ImportResult {
success: number;
failed: number;
errors: Array<{ row: number; message: string }>;
}
export interface ImportTemplate {
headers: string[];
example: string[];
notes: string[];
}
export const getStudentImportTemplate = () =>
http.get<ImportTemplate>('/v1/school/students/import/template');
export const importStudents = (file: File, defaultClassId?: number): Promise<ImportResult> => {
const formData = new FormData();
formData.append('file', file);
const params = defaultClassId ? { defaultClassId } : {};
return http.post('/v1/school/students/import', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
params,
});
};
// ==================== 班级管理 ====================
export const getClasses = () =>
http.get<{ list: ClassInfo[]; total: number }>('/v1/school/classes').then(res => res.list);
export const getClass = (id: number) =>
http.get<ClassInfo>(`/v1/school/classes/${id}`);
export const createClass = (data: CreateClassDto) =>
http.post<ClassInfo>('/v1/school/classes', data);
export const updateClass = (id: number, data: Partial<CreateClassDto>) =>
http.put<ClassInfo>(`/v1/school/classes/${id}`, data);
export const deleteClass = (id: number) =>
http.delete(`/v1/school/classes/${id}`);
export const getClassStudents = (classId: number, params?: { pageNum?: number; pageSize?: number; keyword?: string }) =>
http.get<{ list: Student[]; total: number; pageNum: number; pageSize: number; pages: number; class?: ClassInfo }>(`/v1/school/classes/${classId}/students`, { params });
// ==================== 统计数据 ====================
export const getSchoolStats = () =>
http.get<SchoolStats>('/v1/school/stats');
export const getActiveTeachers = (limit?: number) =>
http.get<Array<{ id: number; name: string; lessonCount: number }>>('/v1/school/stats/teachers', { params: { limit } });
export const getCourseUsageStats = () =>
http.get<Array<{ courseId: number; courseName: string; usageCount: number }>>('/v1/school/stats/courses');
export const getRecentActivities = (limit?: number) =>
http.get<Array<{ id: number; type: string; title: string; time: string }>>('/v1/school/stats/activities', { params: { limit } });
// ==================== 套餐信息(旧 API保留兼容 ====================
export const getPackageInfo = () =>
http.get<PackageInfo>('/v1/school/package');
export const getPackageUsage = () =>
http.get<PackageUsage>('/v1/school/package/usage');
// ==================== 套餐管理(两层结构) ====================
// 课程包项(对应后端 CourseCollectionResponse.CoursePackageItem
export interface CoursePackageItem {
id: number | string;
name: string;
description?: string;
gradeLevels: string[];
courseCount: number;
sortOrder?: number;
}
// 课程套餐(最上层,对应后端 CourseCollectionResponse
export interface CourseCollection {
id: number | string;
name: string;
description?: string;
price: number;
discountPrice?: number;
discountType?: string;
gradeLevels: string[];
status: string;
packageCount: number;
createdAt: string;
publishedAt?: string;
submittedAt?: string;
reviewedAt?: string;
updatedAt?: string;
startDate?: string; // 开始日期(租户套餐)
endDate?: string; // 结束日期(租户套餐)
packages?: CoursePackageItem[]; // 包含的课程包列表
}
// 课程包中间层7 步流程创建的教学资源)
export interface CoursePackage {
id: number | string;
name: string;
description?: string;
pictureBookName?: string;
gradeTags?: string[];
gradeLevels?: string[];
status: string;
courseCount: number;
duration?: number;
sortOrder?: number;
courses?: Array<{
id: number;
name: string;
gradeLevel: string;
sortOrder: number;
scheduleRefData?: string;
}>;
}
export interface RenewPackageDto {
endDate: string;
pricePaid?: number;
}
// 获取课程套餐列表(三层架构)
export const getCourseCollections = () =>
http.get<CourseCollection[]>('/v1/school/packages');
// 获取课程套餐下的课程包列表(返回 CoursePackageItem 列表)
export const getCourseCollectionPackages = (collectionId: number | string) =>
http.get<CoursePackageItem[]>(`/v1/school/packages/${collectionId}/packages`);
// 续费课程套餐(三层架构)
export const renewCollection = (collectionId: number, data: RenewPackageDto) =>
http.post<void>(`/v1/school/packages/${collectionId}/renew`, data);
// ==================== 系统设置 ====================
export interface SystemSettings {
id: number;
tenantId: number;
schoolName?: string;
schoolLogo?: string;
address?: string;
notifyOnLesson: boolean;
notifyOnTask: boolean;
notifyOnGrowth: boolean;
createdAt: string;
updatedAt: string;
}
export interface UpdateSettingsDto {
schoolName?: string;
schoolLogo?: string;
address?: string;
notifyOnLesson?: boolean;
notifyOnTask?: boolean;
notifyOnGrowth?: boolean;
}
export const getSettings = () =>
http.get<SystemSettings>('/v1/school/settings');
export const updateSettings = (data: UpdateSettingsDto) =>
http.put<SystemSettings>('/v1/school/settings', data);
// ==================== 课程管理 ====================
export interface Course {
id: number;
tenantId?: number;
name: string;
code?: string;
description?: string;
coverUrl?: string;
coverImagePath?: string;
pictureBookName?: string;
category?: string;
ageRange?: string;
difficultyLevel?: string;
durationMinutes?: number;
duration?: number;
objectives?: string;
status: string;
isSystem: number;
version?: string;
usageCount?: number;
teacherCount?: number;
gradeTags?: string[];
domainTags?: string[];
createdAt?: string;
updatedAt?: string;
publishedAt?: string;
}
export interface SchoolCourseQueryParams {
keyword?: string;
grade?: string; // 小班|中班|大班
domain?: string; // 健康|语言|社会|科学|艺术(传英文码)
lessonType?: string; // INTRODUCTION|COLLECTIVE|LANGUAGE|HEALTH|SCIENCE|SOCIAL|ART
}
export const getSchoolCourses = (params?: SchoolCourseQueryParams) =>
http.get<Course[]>('/v1/school/courses', { params });
export const getSchoolCourse = (id: number) =>
http.get<Course>(`/v1/school/courses/${id}`);
// ==================== 班级教师管理 ====================
export const getClassTeachers = (classId: number) =>
http.get<ClassTeacher[]>(`/v1/school/classes/${classId}/teachers`);
export const addClassTeacher = (classId: number, data: AddClassTeacherDto) =>
http.post<ClassTeacher>(`/v1/school/classes/${classId}/teachers`, data);
export const updateClassTeacher = (classId: number, teacherId: number, data: UpdateClassTeacherDto) =>
http.put<ClassTeacher>(`/v1/school/classes/${classId}/teachers/${teacherId}`, data);
export const removeClassTeacher = (classId: number, teacherId: number) =>
http.delete<{ message: string }>(`/v1/school/classes/${classId}/teachers/${teacherId}`);
// ==================== 学生调班 ====================
export const transferStudent = (studentId: number, data: TransferStudentDto) =>
http.post<{ message: string }>(`/v1/school/students/${studentId}/transfer`, data);
export const getStudentClassHistory = (studentId: number) =>
http.get<StudentClassHistory[]>(`/v1/school/students/${studentId}/history`);
// ==================== 排课管理 ====================
// 课程类型枚举(与后端 LessonTypeEnum 对齐)
export type LessonType =
| 'INTRODUCTION' | 'INTRO' | 'COLLECTIVE'
| 'LANGUAGE' | 'HEALTH' | 'SCIENCE' | 'SOCIAL' | 'SOCIETY' | 'ART'
| 'DOMAIN_HEALTH' | 'DOMAIN_LANGUAGE' | 'DOMAIN_SOCIAL' | 'DOMAIN_SCIENCE' | 'DOMAIN_ART';
// 课程类型信息
export interface LessonTypeInfo {
lessonType: string;
lessonTypeName: string;
count: number;
}
// 日历视图 - 每日排课项
export interface DayScheduleItem {
id: number;
className: string;
coursePackageName: string;
courseName?: string; // 兼容 coursePackageName 的别名
lessonType?: string;
lessonTypeName: string;
teacherName: string;
scheduledTime: string;
status: string;
}
// 日历视图响应
export interface CalendarViewResponse {
startDate: string;
endDate: string;
schedules: Record<string, DayScheduleItem[]>;
}
// 批量创建排课请求(按班级)
export interface CreateSchedulesByClassesDto {
coursePackageId: number;
courseId: number;
lessonType: LessonType;
classIds: number[];
teacherId: number;
scheduledDate: string;
scheduledTime: string;
repeatType?: 'NONE' | 'WEEKLY' | 'BIWEEKLY';
repeatEndDate?: string;
note?: string;
}
export interface SchedulePlan {
id: number;
tenantId: number;
name?: string;
classId: number;
className?: string;
courseId: number;
courseName?: string;
coursePackageId?: number;
coursePackageName?: string;
lessonType?: LessonType;
lessonTypeName?: string;
teacherId?: number;
teacherName?: string;
scheduledDate?: string;
scheduledTime?: string;
weekDay?: number;
repeatType: 'NONE' | 'DAILY' | 'WEEKLY' | 'BIWEEKLY';
repeatEndDate?: string;
source: 'SCHOOL' | 'TEACHER';
status: 'ACTIVE' | 'CANCELLED' | 'scheduled' | 'cancelled';
note?: string;
createdBy?: number;
createdAt?: string;
updatedAt?: string;
}
export interface CreateScheduleDto {
classId: number;
courseId: number;
teacherId?: number;
scheduledDate?: string;
scheduledTime?: string;
weekDay?: number;
repeatType: 'NONE' | 'DAILY' | 'WEEKLY';
repeatEndDate?: string;
note?: string;
}
export interface UpdateScheduleDto {
teacherId?: number;
scheduledDate?: string;
scheduledTime?: string;
weekDay?: number;
repeatType?: 'NONE' | 'DAILY' | 'WEEKLY';
repeatEndDate?: string;
note?: string;
status?: string;
}
export interface ScheduleQueryParams {
classId?: number;
teacherId?: number;
courseId?: number;
startDate?: string;
endDate?: string;
status?: string;
source?: string;
pageNum?: number;
pageSize?: number;
}
export interface TimetableItem {
date: string;
weekDay: number;
schedules: SchedulePlan[];
}
export interface TimetableQueryParams {
startDate: string;
endDate: string;
classId?: number;
teacherId?: number;
}
export const getSchedules = (params?: ScheduleQueryParams) =>
http.get<{ list: SchedulePlan[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/schedules', { params });
export const getSchedule = (id: number) =>
http.get<SchedulePlan>(`/v1/school/schedules/${id}`);
export const createSchedule = (data: CreateScheduleDto) =>
http.post<SchedulePlan>('/v1/school/schedules', data);
export const updateSchedule = (id: number, data: UpdateScheduleDto) =>
http.put<SchedulePlan>(`/v1/school/schedules/${id}`, data);
export const cancelSchedule = (id: number) =>
http.delete<{ message: string }>(`/v1/school/schedules/${id}`);
export const getTimetable = (params: TimetableQueryParams) =>
http.get<{
byDate: Record<string, SchedulePlan[]>;
byWeekDay: Record<number, SchedulePlan[]>;
total: number;
}>('/v1/school/schedules/timetable', { params });
export interface BatchScheduleItem {
classId: number;
courseId: number;
teacherId?: number;
scheduledDate: string;
scheduledTime?: string;
note?: string;
}
export interface BatchCreateResult {
success: number;
failed: number;
results: SchedulePlan[];
errors: Array<{ index: number; message: string }>;
}
export const batchCreateSchedules = (schedules: BatchScheduleItem[]) =>
http.post<BatchCreateResult>('/v1/school/schedules/batch', { schedules });
// 获取课程包的课程类型列表
export const getCoursePackageLessonTypes = (coursePackageId: number) =>
http.get<LessonTypeInfo[]>(`/v1/school/schedules/course-packages/${coursePackageId}/lesson-types`);
// 批量创建排课(按班级)
export const createSchedulesByClasses = (data: CreateSchedulesByClassesDto) =>
http.post<SchedulePlan[]>('/v1/school/schedules/batch-by-classes', data);
// 获取日历视图数据
export const getCalendarViewData = (params?: {
startDate?: string;
endDate?: string;
classId?: number;
teacherId?: number;
}) => http.get<CalendarViewResponse>('/v1/school/schedules/calendar', { params });
// ==================== 趋势与分布统计 ====================
export interface LessonTrendItem {
month: string;
lessonCount: number;
studentCount: number;
}
export interface CourseDistributionItem {
name: string;
value: number;
}
export const getLessonTrend = (months?: number) =>
http.get<LessonTrendItem[]>('/v1/school/stats/lesson-trend', { params: { months } });
export const getCourseDistribution = () =>
http.get<CourseDistributionItem[]>('/v1/school/stats/course-distribution');
// ==================== 数据导出 ====================
export const exportLessons = (startDate?: string, endDate?: string) => {
const params = new URLSearchParams();
if (startDate) params.append('startDate', startDate);
if (endDate) params.append('endDate', endDate);
const token = localStorage.getItem('token');
const url = `/api/v1/school/export/lessons?${params.toString()}`;
return fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
},
}).then((res) => {
if (!res.ok) throw new Error('导出失败');
return res.blob();
}).then((blob) => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `授课记录_${startDate || ''}_${endDate || ''}.xlsx`;
a.click();
window.URL.revokeObjectURL(url);
});
};
export const exportTeacherStats = (startDate?: string, endDate?: string) => {
const params = new URLSearchParams();
if (startDate) params.append('startDate', startDate);
if (endDate) params.append('endDate', endDate);
const token = localStorage.getItem('token');
const url = `/api/v1/school/export/teacher-stats?${params.toString()}`;
return fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
},
}).then((res) => {
if (!res.ok) throw new Error('导出失败');
return res.blob();
}).then((blob) => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `教师绩效统计_${startDate || ''}_${endDate || ''}.xlsx`;
a.click();
window.URL.revokeObjectURL(url);
});
};
export const exportStudentStats = (classId?: number) => {
const params = new URLSearchParams();
if (classId) params.append('classId', String(classId));
const token = localStorage.getItem('token');
const url = `/api/v1/school/export/student-stats?${params.toString()}`;
return fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
},
}).then((res) => {
if (!res.ok) throw new Error('导出失败');
return res.blob();
}).then((blob) => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `学生统计.xlsx`;
a.click();
window.URL.revokeObjectURL(url);
});
};
// ==================== 排课模板 ====================
export interface ScheduleTemplate {
id: number;
tenantId: number;
name: string;
courseId: number;
courseName?: string;
classId?: number;
className?: string;
teacherId?: number;
teacherName?: string;
scheduledTime?: string;
weekDay?: number;
duration: number;
isDefault: boolean;
createdAt: string;
updatedAt: string;
}
export interface CreateScheduleTemplateDto {
name: string;
courseId: number;
classId?: number;
teacherId?: number;
scheduledTime?: string;
weekDay?: number;
duration?: number;
isDefault?: boolean;
}
export interface UpdateScheduleTemplateDto {
name?: string;
classId?: number;
teacherId?: number;
scheduledTime?: string;
weekDay?: number;
duration?: number;
isDefault?: boolean;
}
export interface ApplyTemplateDto {
scheduledDate: string;
classId?: number;
teacherId?: number;
}
export const getScheduleTemplates = (params?: { classId?: number; courseId?: number }) =>
http.get<ScheduleTemplate[]>('/v1/school/schedule-templates', { params });
export const getScheduleTemplate = (id: number) =>
http.get<ScheduleTemplate>(`/v1/school/schedule-templates/${id}`);
export const createScheduleTemplate = (data: CreateScheduleTemplateDto) =>
http.post<ScheduleTemplate>('/v1/school/schedule-templates', data);
export const updateScheduleTemplate = (id: number, data: UpdateScheduleTemplateDto) =>
http.put<ScheduleTemplate>(`/v1/school/schedule-templates/${id}`, data);
export const deleteScheduleTemplate = (id: number) =>
http.delete<{ message: string }>(`/v1/school/schedule-templates/${id}`);
export const applyScheduleTemplate = (id: number, data: ApplyTemplateDto) =>
http.post<SchedulePlan>(`/v1/school/schedule-templates/${id}/apply`, data);
// ==================== 操作日志 ====================
export interface OperationLog {
id: number;
tenantId: number;
userId: number;
userType: string;
action: string;
module: string;
description: string;
targetId: number | null;
oldValue: string | null;
newValue: string | null;
ipAddress: string | null;
createdAt: string;
}
export interface OperationLogStats {
total: number;
modules: { name: string; count: number }[];
actions: { name: string; count: number }[];
}
export const getOperationLogs = (params?: {
pageNum?: number;
pageSize?: number;
module?: string;
action?: string;
startDate?: string;
endDate?: string;
}) => http.get<{ records: OperationLog[]; total: number; pageNum: number; pageSize: number; pages: number }>(
'/v1/school/operation-logs',
{ params }
).then(res => ({
list: res.records || [],
total: res.total || 0,
pageNum: res.pageNum || 1,
pageSize: res.pageSize || 10,
pages: res.pages || 0,
}));
export const getOperationLogStats = (startDate?: string, endDate?: string) =>
http.get<{ totalLogs: number; byModule: Record<string, number>; byOperator: Record<string, number> }>('/v1/school/operation-logs/stats', {
params: { startDate, endDate },
}).then(res => ({
totalLogs: res.totalLogs || 0,
byModule: res.byModule || {},
byOperator: res.byOperator || {},
}));
export const getOperationLogById = (id: number) =>
http.get<OperationLog>(`/v1/school/operation-logs/${id}`);
// ==================== 任务模板 API ====================
export interface TaskTemplate {
id: number;
tenantId: number;
name: string;
description?: string;
taskType: 'READING' | 'ACTIVITY' | 'HOMEWORK';
relatedCourseId?: number;
defaultDuration: number;
isDefault: boolean;
status: string;
createdBy: number;
createdAt: string;
updatedAt: string;
course?: {
id: number;
name: string;
pictureBookName?: string;
};
}
export interface CreateTaskTemplateDto {
name: string;
description?: string;
taskType: 'READING' | 'ACTIVITY' | 'HOMEWORK';
relatedCourseId?: number;
defaultDuration?: number;
isDefault?: boolean;
}
export interface UpdateTaskTemplateDto {
name?: string;
description?: string;
relatedCourseId?: number;
defaultDuration?: number;
isDefault?: boolean;
status?: string;
}
export const getTaskTemplates = (params?: {
pageNum?: number;
pageSize?: number;
taskType?: string;
keyword?: string;
}) => http.get<{ list: TaskTemplate[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/task-templates', { params })
.then(res => ({
list: res.list || res.records || [],
total: res.total || 0,
pageNum: res.pageNum || 1,
pageSize: res.pageSize || 10,
pages: res.pages || 0,
}));
export const getTaskTemplate = (id: number) =>
http.get<TaskTemplate>(`/v1/school/task-templates/${id}`);
export const getDefaultTaskTemplate = (taskType: string) =>
http.get<TaskTemplate | null>(`/v1/school/task-templates/default/${taskType}`);
export const createTaskTemplate = (data: CreateTaskTemplateDto) =>
http.post<TaskTemplate>('/v1/school/task-templates', data);
export const updateTaskTemplate = (id: number, data: UpdateTaskTemplateDto) =>
http.put<TaskTemplate>(`/v1/school/task-templates/${id}`, data);
export const deleteTaskTemplate = (id: number) =>
http.delete<{ message: string }>(`/v1/school/task-templates/${id}`);
// ==================== 任务统计 API ====================
export interface TaskStats {
totalTasks: number;
publishedTasks: number;
completedTasks: number;
inProgressTasks: number;
pendingCount: number;
totalCompletions: number;
completionRate: number;
}
export interface TaskStatsByType {
[key: string]: {
total: number;
completed: number;
rate: number;
};
}
export interface TaskStatsByClass {
classId: number;
className: string;
grade: string;
total: number;
completed: number;
rate: number;
}
export interface MonthlyTaskStats {
month: string;
tasks: number;
completions: number;
completed: number;
rate: number;
}
// 后端没有任务统计接口,返回空数据
export const getTaskStats = () =>
Promise.resolve({
totalTasks: 0,
publishedTasks: 0,
completedTasks: 0,
inProgressTasks: 0,
pendingCount: 0,
totalCompletions: 0,
completionRate: 0,
});
export const getTaskStatsByType = () =>
Promise.resolve({});
export const getTaskStatsByClass = () =>
Promise.resolve([]);
export const getMonthlyTaskStats = (_months?: number) =>
Promise.resolve([]);
// ==================== 任务管理 API ====================
export interface SchoolTask {
id: number;
tenantId: number;
title: string;
description?: string;
taskType: 'READING' | 'ACTIVITY' | 'HOMEWORK';
targetType: 'CLASS' | 'STUDENT';
relatedCourseId?: number;
course?: {
id: number;
name: string;
};
startDate: string;
endDate: string;
status: 'DRAFT' | 'PUBLISHED' | 'ARCHIVED';
createdBy: number;
targetCount?: number;
completionCount?: number;
createdAt: string;
updatedAt: string;
}
export interface TaskCompletion {
id: number;
taskId: number;
studentId: number;
studentName: string;
className: string;
status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED';
completedAt?: string;
parentFeedback?: string;
rating?: number;
}
export interface CreateSchoolTaskDto {
title: string;
description?: string;
taskType: 'READING' | 'ACTIVITY' | 'HOMEWORK';
targetType: 'CLASS' | 'STUDENT';
targetIds: number[];
relatedCourseId?: number;
startDate: string;
endDate: string;
}
export interface UpdateSchoolTaskDto {
title?: string;
description?: string;
taskType?: 'READING' | 'ACTIVITY' | 'HOMEWORK';
relatedCourseId?: number;
startDate?: string;
endDate?: string;
status?: 'DRAFT' | 'PUBLISHED' | 'ARCHIVED';
}
export const getSchoolTasks = (params?: {
pageNum?: number;
pageSize?: number;
status?: string;
taskType?: string;
keyword?: string;
}) => http.get<{ list: SchoolTask[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/tasks', { params });
export const getSchoolTask = (id: number) =>
http.get<SchoolTask>(`/v1/school/tasks/${id}`);
export const createSchoolTask = (data: CreateSchoolTaskDto) =>
http.post<SchoolTask>('/v1/school/tasks', data);
export const updateSchoolTask = (id: number, data: UpdateSchoolTaskDto) =>
http.put<SchoolTask>(`/v1/school/tasks/${id}`, data);
export const deleteSchoolTask = (id: number) =>
http.delete<{ message: string }>(`/v1/school/tasks/${id}`);
export const getSchoolTaskCompletions = (taskId: number) =>
http.get<TaskCompletion[]>(`/v1/school/tasks/${taskId}/completions`);
export const getSchoolClasses = () =>
http.get<ClassInfo[]>('/v1/school/classes');
// ==================== 数据报告 API ====================
export interface ReportOverview {
totalLessons: number;
activeTeacherCount: number;
usedCourseCount: number;
avgRating: number;
}
export interface TeacherReport {
id: number;
name: string;
lessonCount: number;
courseCount: number;
feedbackCount: number;
avgRating: number;
}
export interface CourseReport {
id: number;
name: string;
lessonCount: number;
teacherCount: number;
studentCount: number;
avgRating: number;
}
export interface StudentReport {
id: number;
name: string;
className: string;
lessonCount: number;
avgFocus: number;
avgParticipation: number;
}
export const getReportOverview = () =>
http.get<ReportOverview>('/v1/school/reports/overview');
export const getTeacherReports = () =>
http.get<TeacherReport[]>('/v1/school/reports/teachers');
export const getCourseReports = () =>
http.get<CourseReport[]>('/v1/school/reports/courses');
export const getStudentReports = () =>
http.get<StudentReport[]>('/v1/school/reports/students');
// ==================== 家长管理 ====================
export interface ParentQueryParams {
pageNum?: number;
pageSize?: number;
keyword?: string;
status?: string;
}
export interface ParentChild {
id: number;
name: string;
relationship: string;
class?: {
id: number;
name: string;
};
}
export interface Parent {
id: number;
name: string;
phone: string;
email?: string;
loginAccount: string;
status: string;
tenantId: number;
childrenCount: number;
children?: ParentChild[];
createdAt: string;
}
export interface CreateParentDto {
name: string;
phone: string;
email?: string;
loginAccount: string;
password?: string;
}
export interface UpdateParentDto {
name?: string;
phone?: string;
email?: string;
}
export interface AddChildDto {
studentId: number;
relationship: string;
}
export const getParents = (params?: ParentQueryParams) =>
http.get<{ list: Parent[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/parents', { params });
export const getParent = (id: number) =>
http.get<Parent>(`/v1/school/parents/${id}`);
export const createParent = (data: CreateParentDto) =>
http.post<Parent>('/v1/school/parents', data);
export const updateParent = (id: number, data: UpdateParentDto) =>
http.put<Parent>(`/v1/school/parents/${id}`, data);
export const deleteParent = (id: number) =>
http.delete<{ message: string }>(`/v1/school/parents/${id}`);
export const resetParentPassword = (id: number) =>
http.post<{ tempPassword: string }>(`/v1/school/parents/${id}/reset-password`);
export const getParentChildren = async (parentId: number): Promise<ParentChild[]> => {
const parent = await http.get<Parent & { children: ParentChild[] }>(`/v1/school/parents/${parentId}`);
return parent.children || [];
};
export const addChildToParent = (parentId: number, data: AddChildDto) =>
http.post<ParentChild>(`/v1/school/parents/${parentId}/children/${data.studentId}`, { relationship: data.relationship });
export const removeChildFromParent = (parentId: number, studentId: number) =>
http.delete<{ message: string }>(`/v1/school/parents/${parentId}/children/${studentId}`);