From e2547daa6300cedc2b869cc3a7eb6762f43bc2ae Mon Sep 17 00:00:00 2001 From: En Date: Tue, 24 Mar 2026 14:04:39 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AD=A6=E6=A0=A1=E7=AB=AF=E5=AE=B6?= =?UTF-8?q?=E9=95=BF=E7=AE=A1=E7=90=86=E9=80=89=E6=8B=A9=E5=AD=A9=E5=AD=90?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 学生列表分页参数修复:page 改为 pageNum - StudentResponse 添加 className 字段,显示班级名称 - 性别显示逻辑简化,兼容空值 - 修复 TeacherListView 中 Modal 导入错误 feat: 排课管理支持删除已取消的排课 - 新增 ScheduleRepeatType 和 ScheduleStatus 枚举 - 添加物理删除接口 /force,仅允许删除已取消的排课 - ScheduleList 和 TimetableView 增加删除按钮 --- reading-platform-frontend/src/api/school.ts | 3 + .../components/course-edit/Step1BasicInfo.vue | 8 +- .../views/school/parents/ParentListView.vue | 7 +- .../views/school/schedule/ScheduleList.vue | 22 ++++++ .../views/school/schedule/TimetableView.vue | 25 +++++++ .../views/school/teachers/TeacherListView.vue | 40 +++++++++- .../common/enums/ScheduleRepeatType.java | 54 ++++++++++++++ .../platform/common/enums/ScheduleStatus.java | 73 +++++++++++++++++++ .../school/SchoolScheduleController.java | 9 +++ .../school/SchoolStudentController.java | 1 + .../dto/response/StudentResponse.java | 3 + .../service/SchoolScheduleService.java | 8 ++ .../impl/SchoolScheduleServiceImpl.java | 37 +++++++--- 13 files changed, 276 insertions(+), 14 deletions(-) create mode 100644 reading-platform-java/src/main/java/com/reading/platform/common/enums/ScheduleRepeatType.java create mode 100644 reading-platform-java/src/main/java/com/reading/platform/common/enums/ScheduleStatus.java diff --git a/reading-platform-frontend/src/api/school.ts b/reading-platform-frontend/src/api/school.ts index 02b1503..19ac47b 100644 --- a/reading-platform-frontend/src/api/school.ts +++ b/reading-platform-frontend/src/api/school.ts @@ -613,6 +613,9 @@ export const updateSchedule = (id: number, data: UpdateScheduleDto) => export const cancelSchedule = (id: number) => http.delete<{ message: string }>(`/v1/school/schedules/${id}`); +export const deleteSchedule = (id: number) => + http.delete<{ message: string }>(`/v1/school/schedules/${id}/force`); + export const getTimetable = (params: TimetableQueryParams) => http.get<{ byDate: Record; diff --git a/reading-platform-frontend/src/components/course-edit/Step1BasicInfo.vue b/reading-platform-frontend/src/components/course-edit/Step1BasicInfo.vue index e11570e..5747c00 100644 --- a/reading-platform-frontend/src/components/course-edit/Step1BasicInfo.vue +++ b/reading-platform-frontend/src/components/course-edit/Step1BasicInfo.vue @@ -101,7 +101,7 @@ - + { const validate = async () => { try { await formRef.value?.validate(); + + // 校验课程封面 + if (!formData.coverImagePath) { + return { valid: false, errors: ['请上传课程封面'] }; + } + return { valid: true, errors: [] as string[] }; } catch (err: any) { const errorFields = err?.errorFields || []; diff --git a/reading-platform-frontend/src/views/school/parents/ParentListView.vue b/reading-platform-frontend/src/views/school/parents/ParentListView.vue index c3c541b..b1c33f4 100644 --- a/reading-platform-frontend/src/views/school/parents/ParentListView.vue +++ b/reading-platform-frontend/src/views/school/parents/ParentListView.vue @@ -246,7 +246,10 @@ @change="handleStudentTableChange" style="margin-top: 16px;"> @@ -627,7 +630,7 @@ const loadStudentsForSelect = async () => { studentsLoading.value = true; try { const result = await getStudents({ - page: studentPagination.current, + pageNum: studentPagination.current, pageSize: studentPagination.pageSize, keyword: studentSearchKeyword.value || undefined, classId: studentClassFilter.value || undefined, diff --git a/reading-platform-frontend/src/views/school/schedule/ScheduleList.vue b/reading-platform-frontend/src/views/school/schedule/ScheduleList.vue index 28e0872..6c0914a 100644 --- a/reading-platform-frontend/src/views/school/schedule/ScheduleList.vue +++ b/reading-platform-frontend/src/views/school/schedule/ScheduleList.vue @@ -77,6 +77,13 @@ > 取消 + + 删除 + @@ -166,6 +173,7 @@ import { getSchedules, updateSchedule, cancelSchedule, + deleteSchedule, getClasses, getTeachers, getSchoolCourses, @@ -195,6 +203,20 @@ const isScheduleActive = (status: string): boolean => { return status === 'ACTIVE' || status === 'scheduled'; }; +const isScheduleCancelled = (status: string): boolean => { + return status === 'CANCELLED' || status === 'cancelled'; +}; + +const handleDelete = async (id: number) => { + try { + await deleteSchedule(id); + message.success('删除成功'); + loadSchedules(); + } catch (error) { + message.error('删除失败'); + } +}; + // 数据 const loading = ref(false); const schedules = ref([]); diff --git a/reading-platform-frontend/src/views/school/schedule/TimetableView.vue b/reading-platform-frontend/src/views/school/schedule/TimetableView.vue index a02d9a3..1afa93b 100644 --- a/reading-platform-frontend/src/views/school/schedule/TimetableView.vue +++ b/reading-platform-frontend/src/views/school/schedule/TimetableView.vue @@ -121,6 +121,15 @@ +
+ + 删除 + +
@@ -138,6 +147,7 @@ import { getTimetable, getClasses, getTeachers, + deleteSchedule, type TimetableItem, type SchedulePlan, type ClassInfo, @@ -162,6 +172,21 @@ const isScheduleActive = (status: string): boolean => { return status === 'ACTIVE' || status === 'scheduled'; }; +const isScheduleCancelled = (status: string): boolean => { + return status === 'CANCELLED' || status === 'cancelled'; +}; + +const handleDelete = async (id: number) => { + try { + await deleteSchedule(id); + message.success('删除成功'); + detailVisible.value = false; + loadTimetable(); + } catch (error) { + message.error('删除失败'); + } +}; + // 日程来源辅助函数 const getScheduleSourceText = (source: string): string => { return translateScheduleSourceType(source) || source; diff --git a/reading-platform-frontend/src/views/school/teachers/TeacherListView.vue b/reading-platform-frontend/src/views/school/teachers/TeacherListView.vue index 616c491..25715f1 100644 --- a/reading-platform-frontend/src/views/school/teachers/TeacherListView.vue +++ b/reading-platform-frontend/src/views/school/teachers/TeacherListView.vue @@ -172,6 +172,12 @@
+ + + 在职 + 离职 + + @@ -216,7 +222,7 @@ import { LockOutlined, WarningOutlined, } from '@ant-design/icons-vue'; -import { message } from 'ant-design-vue'; +import { message, Modal } from 'ant-design-vue'; import type { FormInstance } from 'ant-design-vue'; import { translateGenericStatus, getGenericStatusStyle } from '@/utils/tagMaps'; import { @@ -285,13 +291,14 @@ const activeCount = computed(() => { return teachers.value.filter(t => isTeacherActive(t.status)).length; }); -const formState = reactive({ +const formState = reactive({ name: '', phone: '', email: '', loginAccount: '', password: '', classIds: [], + status: 'ACTIVE', }); const rules: Record = { @@ -350,6 +357,7 @@ const resetForm = () => { formState.loginAccount = ''; formState.password = ''; formState.classIds = []; + formState.status = 'ACTIVE'; }; const showAddModal = () => { @@ -366,6 +374,7 @@ const handleEdit = (record: Teacher) => { formState.email = record.email || ''; formState.loginAccount = record.loginAccount; formState.classIds = record.classIds || []; + formState.status = record.status || 'ACTIVE'; modalVisible.value = true; }; @@ -375,11 +384,38 @@ const handleModalOk = async () => { submitting.value = true; if (isEdit.value && formState.id) { + // 检查是否要将状态设为离职 + if (formState.status === 'INACTIVE') { + // 获取教师当前状态(用于判断是否是从 ACTIVE 变为 INACTIVE) + const teacher = teachers.value.find(t => t.id === formState.id); + const wasActive = teacher?.status === 'ACTIVE'; + + if (wasActive) { + // 弹出确认框 + const confirmed = await new Promise((resolve) => { + Modal.confirm({ + title: '确认离职操作', + content: '确定要将此教师设为离职状态吗?离职后将无法登录系统。', + okText: '确认', + cancelText: '取消', + onOk: () => resolve(true), + onCancel: () => resolve(false), + }); + }); + + if (!confirmed) { + submitting.value = false; + return; + } + } + } + await updateTeacher(formState.id, { name: formState.name, phone: formState.phone, email: formState.email, classIds: formState.classIds, + status: formState.status, }); message.success('更新成功'); } else { diff --git a/reading-platform-java/src/main/java/com/reading/platform/common/enums/ScheduleRepeatType.java b/reading-platform-java/src/main/java/com/reading/platform/common/enums/ScheduleRepeatType.java new file mode 100644 index 0000000..895578b --- /dev/null +++ b/reading-platform-java/src/main/java/com/reading/platform/common/enums/ScheduleRepeatType.java @@ -0,0 +1,54 @@ +package com.reading.platform.common.enums; + +import lombok.Getter; + +/** + * 排课重复类型枚举 + */ +@Getter +public enum ScheduleRepeatType { + + NONE("NONE", "不重复"), + DAILY("DAILY", "每日"), + WEEKLY("WEEKLY", "每周"), + BIWEEKLY("BIWEEKLY", "双周"); + + private final String code; + private final String description; + + ScheduleRepeatType(String code, String description) { + this.code = code; + this.description = description; + } + + /** + * 根据代码值转换为枚举 + */ + public static ScheduleRepeatType fromCode(String code) { + if (code == null) { + return NONE; + } + for (ScheduleRepeatType type : values()) { + if (type.getCode().equalsIgnoreCase(code)) { + return type; + } + } + return NONE; + } + + /** + * 判断是否为有效的代码值 + */ + public static boolean isValidCode(String code) { + if (code == null) { + return false; + } + for (ScheduleRepeatType type : values()) { + if (type.getCode().equalsIgnoreCase(code)) { + return true; + } + } + return false; + } + +} diff --git a/reading-platform-java/src/main/java/com/reading/platform/common/enums/ScheduleStatus.java b/reading-platform-java/src/main/java/com/reading/platform/common/enums/ScheduleStatus.java new file mode 100644 index 0000000..127c773 --- /dev/null +++ b/reading-platform-java/src/main/java/com/reading/platform/common/enums/ScheduleStatus.java @@ -0,0 +1,73 @@ +package com.reading.platform.common.enums; + +import lombok.Getter; + +/** + * 排课状态枚举 + */ +@Getter +public enum ScheduleStatus { + + ACTIVE("ACTIVE", "有效"), + SCHEDULED("SCHEDULED", "已排期"), + CANCELLED("CANCELLED", "已取消"); + + private final String code; + private final String description; + + ScheduleStatus(String code, String description) { + this.code = code; + this.description = description; + } + + /** + * 根据代码值转换为枚举 + */ + public static ScheduleStatus fromCode(String code) { + if (code == null) { + return CANCELLED; + } + for (ScheduleStatus status : values()) { + if (status.getCode().equalsIgnoreCase(code)) { + return status; + } + } + return CANCELLED; + } + + /** + * 判断是否为有效/激活状态 + */ + public static boolean isActive(String status) { + if (status == null) { + return false; + } + return ACTIVE.getCode().equalsIgnoreCase(status) || + SCHEDULED.getCode().equalsIgnoreCase(status); + } + + /** + * 判断是否为已取消状态 + */ + public static boolean isCancelled(String status) { + if (status == null) { + return false; + } + return CANCELLED.getCode().equalsIgnoreCase(status); + } + + /** + * 判断当前状态是否为激活状态 + */ + public boolean isActive() { + return this == ACTIVE || this == SCHEDULED; + } + + /** + * 判断当前状态是否为已取消状态 + */ + public boolean isCancelled() { + return this == CANCELLED; + } + +} diff --git a/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolScheduleController.java b/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolScheduleController.java index 397e606..cb1ff13 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolScheduleController.java +++ b/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolScheduleController.java @@ -116,6 +116,15 @@ public class SchoolScheduleController { return Result.success(); } + @DeleteMapping("/{id}/force") + @Operation(summary = "删除排课(物理删除)") + @Log(module = LogModule.SCHEDULE, type = LogOperationType.DELETE, description = "删除排课记录") + public Result deleteSchedule(@PathVariable Long id) { + Long tenantId = SecurityUtils.getCurrentTenantId(); + schoolScheduleService.deleteSchedule(id, tenantId); + return Result.success(); + } + @PostMapping("/batch") @Operation(summary = "批量创建排课") @Log(module = LogModule.SCHEDULE, type = LogOperationType.CREATE, description = "批量创建排课计划") diff --git a/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolStudentController.java b/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolStudentController.java index 148c8a2..61ff6ba 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolStudentController.java +++ b/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolStudentController.java @@ -102,6 +102,7 @@ public class SchoolStudentController { // 设置班级 var clazz = classService.getPrimaryClassByStudentId(vo.getId()); vo.setClassId(clazz != null ? clazz.getId() : null); + vo.setClassName(clazz != null ? clazz.getName() : null); // 设置家长信息(查询主要监护人) var parentRelation = parentStudentMapper.selectOne( diff --git a/reading-platform-java/src/main/java/com/reading/platform/dto/response/StudentResponse.java b/reading-platform-java/src/main/java/com/reading/platform/dto/response/StudentResponse.java index 8fdb8f8..660ea16 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/dto/response/StudentResponse.java +++ b/reading-platform-java/src/main/java/com/reading/platform/dto/response/StudentResponse.java @@ -55,6 +55,9 @@ public class StudentResponse { @Schema(description = "所在班级 ID(从 student_class_history 当前关联获取)") private Long classId; + @Schema(description = "所在班级名称(从 student_class_history 当前关联获取)") + private String className; + @Schema(description = "创建时间") private LocalDateTime createdAt; diff --git a/reading-platform-java/src/main/java/com/reading/platform/service/SchoolScheduleService.java b/reading-platform-java/src/main/java/com/reading/platform/service/SchoolScheduleService.java index 49a78ae..4fcf556 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/service/SchoolScheduleService.java +++ b/reading-platform-java/src/main/java/com/reading/platform/service/SchoolScheduleService.java @@ -47,6 +47,14 @@ public interface SchoolScheduleService extends IService { */ void cancelSchedule(Long id, Long tenantId); + /** + * 删除排课(物理删除,仅限已取消状态) + * + * @param id 排课 ID + * @param tenantId 租户 ID + */ + void deleteSchedule(Long id, Long tenantId); + /** * 获取排课详情 * diff --git a/reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolScheduleServiceImpl.java b/reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolScheduleServiceImpl.java index 584bce2..8889abc 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolScheduleServiceImpl.java +++ b/reading-platform-java/src/main/java/com/reading/platform/service/impl/SchoolScheduleServiceImpl.java @@ -8,6 +8,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.reading.platform.common.enums.GenericStatus; import com.reading.platform.common.enums.ScheduleSourceType; +import com.reading.platform.common.enums.ScheduleStatus; +import com.reading.platform.common.enums.ScheduleRepeatType; import com.reading.platform.common.enums.ErrorCode; import com.reading.platform.common.exception.BusinessException; import com.reading.platform.dto.request.SchedulePlanCreateRequest; @@ -182,12 +184,29 @@ public class SchoolScheduleServiceImpl extends ServiceImpl