2026-02-26 15:22:26 +08:00
|
|
|
|
import { http } from './index';
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 类型定义 ====================
|
|
|
|
|
|
|
|
|
|
|
|
export interface TeacherQueryParams {
|
2026-03-14 16:50:54 +08:00
|
|
|
|
pageNum?: number;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
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 {
|
2026-03-14 16:50:54 +08:00
|
|
|
|
pageNum?: number;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
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) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<{ list: Teacher[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/teachers', { params });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getTeacher = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<Teacher>(`/v1/school/teachers/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const createTeacher = (data: CreateTeacherDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<Teacher>('/v1/school/teachers', data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const updateTeacher = (id: number, data: Partial<CreateTeacherDto>) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.put<Teacher>(`/v1/school/teachers/${id}`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const deleteTeacher = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.delete(`/v1/school/teachers/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const resetTeacherPassword = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<{ tempPassword: string }>(`/v1/school/teachers/${id}/reset-password`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 学生管理 ====================
|
|
|
|
|
|
|
|
|
|
|
|
export const getStudents = (params: StudentQueryParams) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<{ list: Student[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/students', { params });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getStudent = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<Student>(`/v1/school/students/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const createStudent = (data: CreateStudentDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<Student>('/v1/school/students', data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const updateStudent = (id: number, data: Partial<CreateStudentDto>) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.put<Student>(`/v1/school/students/${id}`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const deleteStudent = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.delete(`/v1/school/students/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 学生批量导入 ====================
|
|
|
|
|
|
|
|
|
|
|
|
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 = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<ImportTemplate>('/v1/school/students/import/template');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const importStudents = (file: File, defaultClassId?: number): Promise<ImportResult> => {
|
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
|
formData.append('file', file);
|
|
|
|
|
|
|
|
|
|
|
|
const params = defaultClassId ? { defaultClassId } : {};
|
2026-03-14 16:50:54 +08:00
|
|
|
|
return http.post('/v1/school/students/import', formData, {
|
2026-02-26 15:22:26 +08:00
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': 'multipart/form-data',
|
|
|
|
|
|
},
|
|
|
|
|
|
params,
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 班级管理 ====================
|
|
|
|
|
|
|
|
|
|
|
|
export const getClasses = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<{ list: ClassInfo[]; total: number }>('/v1/school/classes').then(res => res.list);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getClass = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<ClassInfo>(`/v1/school/classes/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const createClass = (data: CreateClassDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<ClassInfo>('/v1/school/classes', data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const updateClass = (id: number, data: Partial<CreateClassDto>) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.put<ClassInfo>(`/v1/school/classes/${id}`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const deleteClass = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.delete(`/v1/school/classes/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
2026-03-14 16:50:54 +08:00
|
|
|
|
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 });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 统计数据 ====================
|
|
|
|
|
|
|
|
|
|
|
|
export const getSchoolStats = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<SchoolStats>('/v1/school/stats');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getActiveTeachers = (limit?: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<Array<{ id: number; name: string; lessonCount: number }>>('/v1/school/stats/teachers', { params: { limit } });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getCourseUsageStats = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<Array<{ courseId: number; courseName: string; usageCount: number }>>('/v1/school/stats/courses');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getRecentActivities = (limit?: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<Array<{ id: number; type: string; title: string; time: string }>>('/v1/school/stats/activities', { params: { limit } });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
2026-02-28 16:41:39 +08:00
|
|
|
|
// ==================== 套餐信息(旧API,保留兼容) ====================
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getPackageInfo = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<PackageInfo>('/v1/school/package');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getPackageUsage = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<PackageUsage>('/v1/school/package/usage');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
2026-02-28 16:41:39 +08:00
|
|
|
|
// ==================== 套餐管理(新API) ====================
|
|
|
|
|
|
|
|
|
|
|
|
export interface TenantPackage {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
tenantId: number;
|
|
|
|
|
|
packageId: number;
|
|
|
|
|
|
startDate: string;
|
|
|
|
|
|
endDate: string;
|
|
|
|
|
|
status: 'ACTIVE' | 'EXPIRED' | 'CANCELLED';
|
|
|
|
|
|
pricePaid?: number;
|
|
|
|
|
|
createdAt: string;
|
|
|
|
|
|
package: {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
description?: string;
|
|
|
|
|
|
price: number;
|
|
|
|
|
|
discountPrice?: number;
|
|
|
|
|
|
courseCount: number;
|
|
|
|
|
|
gradeLevels: string;
|
|
|
|
|
|
status: string;
|
|
|
|
|
|
courses: Array<{
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
packageId: number;
|
|
|
|
|
|
courseId: number;
|
|
|
|
|
|
gradeLevel: string;
|
|
|
|
|
|
course: {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
coverImagePath?: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
}>;
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface RenewPackageDto {
|
|
|
|
|
|
endDate: string;
|
|
|
|
|
|
pricePaid?: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const getTenantPackages = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<TenantPackage[]>('/v1/school/packages');
|
2026-02-28 16:41:39 +08:00
|
|
|
|
|
|
|
|
|
|
export const renewPackage = (packageId: number, data: RenewPackageDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<TenantPackage>(`/v1/school/packages/${packageId}/renew`, data);
|
2026-02-28 16:41:39 +08:00
|
|
|
|
|
2026-02-26 15:22:26 +08:00
|
|
|
|
// ==================== 系统设置 ====================
|
|
|
|
|
|
|
|
|
|
|
|
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 = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<SystemSettings>('/v1/school/settings');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const updateSettings = (data: UpdateSettingsDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.put<SystemSettings>('/v1/school/settings', data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 课程管理 ====================
|
|
|
|
|
|
|
|
|
|
|
|
export const getSchoolCourses = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<any[]>('/v1/school/courses');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getSchoolCourse = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<any>(`/v1/school/courses/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 班级教师管理 ====================
|
|
|
|
|
|
|
|
|
|
|
|
export const getClassTeachers = (classId: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<ClassTeacher[]>(`/v1/school/classes/${classId}/teachers`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const addClassTeacher = (classId: number, data: AddClassTeacherDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<ClassTeacher>(`/v1/school/classes/${classId}/teachers`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const updateClassTeacher = (classId: number, teacherId: number, data: UpdateClassTeacherDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.put<ClassTeacher>(`/v1/school/classes/${classId}/teachers/${teacherId}`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const removeClassTeacher = (classId: number, teacherId: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.delete<{ message: string }>(`/v1/school/classes/${classId}/teachers/${teacherId}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 学生调班 ====================
|
|
|
|
|
|
|
|
|
|
|
|
export const transferStudent = (studentId: number, data: TransferStudentDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<{ message: string }>(`/v1/school/students/${studentId}/transfer`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getStudentClassHistory = (studentId: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<StudentClassHistory[]>(`/v1/school/students/${studentId}/history`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 排课管理 ====================
|
|
|
|
|
|
|
|
|
|
|
|
export interface SchedulePlan {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
tenantId: number;
|
|
|
|
|
|
classId: number;
|
|
|
|
|
|
className: string;
|
|
|
|
|
|
courseId: number;
|
|
|
|
|
|
courseName: string;
|
|
|
|
|
|
teacherId?: number;
|
|
|
|
|
|
teacherName?: string;
|
|
|
|
|
|
scheduledDate?: string;
|
|
|
|
|
|
scheduledTime?: string;
|
|
|
|
|
|
weekDay?: number;
|
|
|
|
|
|
repeatType: 'NONE' | 'DAILY' | 'WEEKLY';
|
|
|
|
|
|
repeatEndDate?: string;
|
|
|
|
|
|
source: 'SCHOOL' | 'TEACHER';
|
|
|
|
|
|
status: 'ACTIVE' | '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;
|
2026-03-14 16:50:54 +08:00
|
|
|
|
pageNum?: number;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
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) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<{ list: SchedulePlan[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/schedules', { params });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getSchedule = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<SchedulePlan>(`/v1/school/schedules/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const createSchedule = (data: CreateScheduleDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<SchedulePlan>('/v1/school/schedules', data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const updateSchedule = (id: number, data: UpdateScheduleDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.put<SchedulePlan>(`/v1/school/schedules/${id}`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const cancelSchedule = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.delete<{ message: string }>(`/v1/school/schedules/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getTimetable = (params: TimetableQueryParams) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<TimetableItem[]>('/v1/school/schedules/timetable', { params });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
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[]) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<BatchCreateResult>('/v1/school/schedules/batch', { schedules });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 趋势与分布统计 ====================
|
|
|
|
|
|
|
|
|
|
|
|
export interface LessonTrendItem {
|
|
|
|
|
|
month: string;
|
|
|
|
|
|
lessonCount: number;
|
|
|
|
|
|
studentCount: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface CourseDistributionItem {
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
value: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const getLessonTrend = (months?: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<LessonTrendItem[]>('/v1/school/stats/lesson-trend', { params: { months } });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getCourseDistribution = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<CourseDistributionItem[]>('/v1/school/stats/course-distribution');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 数据导出 ====================
|
|
|
|
|
|
|
|
|
|
|
|
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 }) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<ScheduleTemplate[]>('/v1/school/schedule-templates', { params });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getScheduleTemplate = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<ScheduleTemplate>(`/v1/school/schedule-templates/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const createScheduleTemplate = (data: CreateScheduleTemplateDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<ScheduleTemplate>('/v1/school/schedule-templates', data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const updateScheduleTemplate = (id: number, data: UpdateScheduleTemplateDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.put<ScheduleTemplate>(`/v1/school/schedule-templates/${id}`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const deleteScheduleTemplate = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.delete<{ message: string }>(`/v1/school/schedule-templates/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const applyScheduleTemplate = (id: number, data: ApplyTemplateDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<SchedulePlan>(`/v1/school/schedule-templates/${id}/apply`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 操作日志 ====================
|
|
|
|
|
|
|
|
|
|
|
|
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?: {
|
2026-03-14 16:50:54 +08:00
|
|
|
|
pageNum?: number;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
pageSize?: number;
|
|
|
|
|
|
module?: string;
|
|
|
|
|
|
action?: string;
|
|
|
|
|
|
startDate?: string;
|
|
|
|
|
|
endDate?: string;
|
2026-03-14 16:50:54 +08:00
|
|
|
|
}) => http.get<{ records: OperationLog[]; total: number; pageNum: number; pageSize: number; pages: number }>(
|
|
|
|
|
|
'/v1/school/operation-logs',
|
2026-02-26 15:22:26 +08:00
|
|
|
|
{ params }
|
2026-03-14 16:50:54 +08:00
|
|
|
|
).then(res => ({
|
|
|
|
|
|
list: res.records || [],
|
|
|
|
|
|
total: res.total || 0,
|
|
|
|
|
|
pageNum: res.pageNum || 1,
|
|
|
|
|
|
pageSize: res.pageSize || 10,
|
|
|
|
|
|
pages: res.pages || 0,
|
|
|
|
|
|
}));
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getOperationLogStats = (startDate?: string, endDate?: string) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<{ totalLogs: number; byModule: Record<string, number>; byOperator: Record<string, number> }>('/v1/school/operation-logs/stats', {
|
2026-02-26 15:22:26 +08:00
|
|
|
|
params: { startDate, endDate },
|
2026-03-14 16:50:54 +08:00
|
|
|
|
}).then(res => ({
|
|
|
|
|
|
totalLogs: res.totalLogs || 0,
|
|
|
|
|
|
byModule: res.byModule || {},
|
|
|
|
|
|
byOperator: res.byOperator || {},
|
|
|
|
|
|
}));
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getOperationLogById = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<OperationLog>(`/v1/school/operation-logs/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 任务模板 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?: {
|
2026-03-14 16:50:54 +08:00
|
|
|
|
pageNum?: number;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
pageSize?: number;
|
|
|
|
|
|
taskType?: string;
|
|
|
|
|
|
keyword?: string;
|
2026-03-14 16:50:54 +08:00
|
|
|
|
}) => 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,
|
|
|
|
|
|
}));
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getTaskTemplate = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<TaskTemplate>(`/v1/school/task-templates/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getDefaultTaskTemplate = (taskType: string) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<TaskTemplate | null>(`/v1/school/task-templates/default/${taskType}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const createTaskTemplate = (data: CreateTaskTemplateDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<TaskTemplate>('/v1/school/task-templates', data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const updateTaskTemplate = (id: number, data: UpdateTaskTemplateDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.put<TaskTemplate>(`/v1/school/task-templates/${id}`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const deleteTaskTemplate = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.delete<{ message: string }>(`/v1/school/task-templates/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 任务统计 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-14 16:50:54 +08:00
|
|
|
|
// 后端没有任务统计接口,返回空数据
|
2026-02-26 15:22:26 +08:00
|
|
|
|
export const getTaskStats = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
Promise.resolve({
|
|
|
|
|
|
totalTasks: 0,
|
|
|
|
|
|
publishedTasks: 0,
|
|
|
|
|
|
completedTasks: 0,
|
|
|
|
|
|
inProgressTasks: 0,
|
|
|
|
|
|
pendingCount: 0,
|
|
|
|
|
|
totalCompletions: 0,
|
|
|
|
|
|
completionRate: 0,
|
|
|
|
|
|
});
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getTaskStatsByType = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
Promise.resolve({});
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getTaskStatsByClass = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
Promise.resolve([]);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
2026-03-14 16:50:54 +08:00
|
|
|
|
export const getMonthlyTaskStats = (_months?: number) =>
|
|
|
|
|
|
Promise.resolve([]);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 任务管理 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?: {
|
2026-03-14 16:50:54 +08:00
|
|
|
|
pageNum?: number;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
pageSize?: number;
|
|
|
|
|
|
status?: string;
|
|
|
|
|
|
taskType?: string;
|
|
|
|
|
|
keyword?: string;
|
2026-03-14 16:50:54 +08:00
|
|
|
|
}) => http.get<{ list: SchoolTask[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/tasks', { params });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getSchoolTask = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<SchoolTask>(`/v1/school/tasks/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const createSchoolTask = (data: CreateSchoolTaskDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<SchoolTask>('/v1/school/tasks', data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const updateSchoolTask = (id: number, data: UpdateSchoolTaskDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.put<SchoolTask>(`/v1/school/tasks/${id}`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const deleteSchoolTask = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.delete<{ message: string }>(`/v1/school/tasks/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getSchoolTaskCompletions = (taskId: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<TaskCompletion[]>(`/v1/school/tasks/${taskId}/completions`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getSchoolClasses = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<ClassInfo[]>('/v1/school/classes');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 数据报告 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 = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<ReportOverview>('/v1/school/reports/overview');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getTeacherReports = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<TeacherReport[]>('/v1/school/reports/teachers');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getCourseReports = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<CourseReport[]>('/v1/school/reports/courses');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getStudentReports = () =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<StudentReport[]>('/v1/school/reports/students');
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 家长管理 ====================
|
|
|
|
|
|
|
|
|
|
|
|
export interface ParentQueryParams {
|
2026-03-14 16:50:54 +08:00
|
|
|
|
pageNum?: number;
|
2026-02-26 15:22:26 +08:00
|
|
|
|
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) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<{ list: Parent[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/parents', { params });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getParent = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.get<Parent>(`/v1/school/parents/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const createParent = (data: CreateParentDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<Parent>('/v1/school/parents', data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const updateParent = (id: number, data: UpdateParentDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.put<Parent>(`/v1/school/parents/${id}`, data);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const deleteParent = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.delete<{ message: string }>(`/v1/school/parents/${id}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const resetParentPassword = (id: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<{ tempPassword: string }>(`/v1/school/parents/${id}/reset-password`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const getParentChildren = async (parentId: number): Promise<ParentChild[]> => {
|
2026-03-14 16:50:54 +08:00
|
|
|
|
const parent = await http.get<Parent & { children: ParentChild[] }>(`/v1/school/parents/${parentId}`);
|
2026-02-26 15:22:26 +08:00
|
|
|
|
return parent.children || [];
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const addChildToParent = (parentId: number, data: AddChildDto) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.post<ParentChild>(`/v1/school/parents/${parentId}/children/${data.studentId}`, { relationship: data.relationship });
|
2026-02-26 15:22:26 +08:00
|
|
|
|
|
|
|
|
|
|
export const removeChildFromParent = (parentId: number, studentId: number) =>
|
2026-03-14 16:50:54 +08:00
|
|
|
|
http.delete<{ message: string }>(`/v1/school/parents/${parentId}/children/${studentId}`);
|