From 1e73b480a0f96332e0fcd7e697214b4c07019e4c Mon Sep 17 00:00:00 2001 From: zhonghua Date: Tue, 24 Mar 2026 10:05:28 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=E6=96=B0=E5=A2=9E=E5=AD=A6=E7=94=9F?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E6=A8=A1=E6=9D=BF=E6=8E=A5=E5=8F=A3=E5=8F=8A?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=98=85=E8=AF=BB=E4=BB=BB=E5=8A=A1=E6=97=A5?= =?UTF-8?q?=E6=9C=9F=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 GET /api/v1/school/students/import/template 接口,返回导入模板配置 - 实现阅读任务 startDate/endDate 日期范围查询(按任务日期交集筛选) - 支持阅读任务按 startDate/endDate 排序 Made-with: Cursor --- .../school/SchoolStudentController.java | 18 +++++++ .../StudentImportTemplateResponse.java | 27 +++++++++++ .../service/impl/TaskServiceImpl.java | 48 ++++++++++++++----- 3 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 reading-platform-java/src/main/java/com/reading/platform/dto/response/StudentImportTemplateResponse.java 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 1862aa0..148c8a2 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 @@ -13,6 +13,7 @@ import com.reading.platform.common.security.SecurityUtils; import com.reading.platform.dto.request.StudentCreateRequest; import com.reading.platform.dto.request.StudentUpdateRequest; import com.reading.platform.dto.request.TransferStudentRequest; +import com.reading.platform.dto.response.StudentImportTemplateResponse; import com.reading.platform.dto.response.StudentResponse; import com.reading.platform.dto.response.StudentTransferHistoryItemResponse; import com.reading.platform.entity.Student; @@ -24,6 +25,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -137,6 +139,22 @@ public class SchoolStudentController { return Result.success(); } + @Operation(summary = "Get student import template") + @GetMapping("/import/template") + public Result getImportTemplate() { + return Result.success(new StudentImportTemplateResponse( + Arrays.asList("姓名", "性别", "出生日期", "班级ID", "家长姓名", "家长电话"), + Arrays.asList("张小明", "男", "2020-01-15", "1", "张三", "13800138000"), + Arrays.asList( + "姓名为必填项", + "性别可选:男/女,默认为男", + "出生日期格式:YYYY-MM-DD", + "班级ID为必填项,可在班级管理中查看", + "家长姓名和家长电话为选填项" + ) + )); + } + @Operation(summary = "Get student class transfer history") @GetMapping("/{id}/history") public Result> getStudentClassHistory(@PathVariable Long id) { diff --git a/reading-platform-java/src/main/java/com/reading/platform/dto/response/StudentImportTemplateResponse.java b/reading-platform-java/src/main/java/com/reading/platform/dto/response/StudentImportTemplateResponse.java new file mode 100644 index 0000000..c75f013 --- /dev/null +++ b/reading-platform-java/src/main/java/com/reading/platform/dto/response/StudentImportTemplateResponse.java @@ -0,0 +1,27 @@ +package com.reading.platform.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 学生导入模板响应 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "学生导入模板") +public class StudentImportTemplateResponse { + + @Schema(description = "表头列表") + private List headers; + + @Schema(description = "示例行") + private List example; + + @Schema(description = "说明事项") + private List notes; +} diff --git a/reading-platform-java/src/main/java/com/reading/platform/service/impl/TaskServiceImpl.java b/reading-platform-java/src/main/java/com/reading/platform/service/impl/TaskServiceImpl.java index 68e5070..50e27b4 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/service/impl/TaskServiceImpl.java +++ b/reading-platform-java/src/main/java/com/reading/platform/service/impl/TaskServiceImpl.java @@ -712,19 +712,45 @@ public class TaskServiceImpl extends ServiceImpl wrapper.eq(Task::getStatus, status); } - // TODO: 实现更多筛选条件 - // - classIds: 班级筛选(需要关联 task_target 表) - // - teacherIds: 教师筛选 - // - dateType/startDate/endDate: 时间筛选 - // - completionRate: 完成率筛选 - // - sortBy/sortOrder: 排序 + // 时间筛选:任务的 startDate~dueDate 与查询范围有交集的纳入结果 + // 交集条件:task.startDate <= endDate AND task.dueDate >= startDate + if (StringUtils.hasText(startDate) && StringUtils.hasText(endDate)) { + try { + LocalDate rangeStart = LocalDate.parse(startDate); + LocalDate rangeEnd = LocalDate.parse(endDate); + // 任务开始日期 <= 查询结束日期(任务不能完全在查询范围之后) + wrapper.le(Task::getStartDate, rangeEnd); + // 任务截止日期 >= 查询开始日期(任务不能完全在查询范围之前) + wrapper.and(w -> w.ge(Task::getDueDate, rangeStart).or().isNull(Task::getDueDate)); + } catch (Exception e) { + log.warn("解析日期参数失败 startDate={}, endDate={}", startDate, endDate, e); + } + } else if (StringUtils.hasText(startDate)) { + try { + LocalDate rangeStart = LocalDate.parse(startDate); + wrapper.ge(Task::getDueDate, rangeStart); + } catch (Exception e) { + log.warn("解析 startDate 参数失败: {}", startDate, e); + } + } else if (StringUtils.hasText(endDate)) { + try { + LocalDate rangeEnd = LocalDate.parse(endDate); + wrapper.le(Task::getStartDate, rangeEnd); + } catch (Exception e) { + log.warn("解析 endDate 参数失败: {}", endDate, e); + } + } - // 默认排序 - if ("completionRate".equals(sortBy)) { - // 完成率排序需要特殊处理,暂时使用创建时间 - wrapper.orderByDesc(Task::getCreatedAt); + // TODO: classIds、teacherIds、completionRate 筛选待实现 + + // 排序 + boolean asc = "asc".equalsIgnoreCase(sortOrder); + if ("startDate".equals(sortBy)) { + wrapper.orderBy(true, asc, Task::getStartDate); + } else if ("endDate".equals(sortBy) || "dueDate".equals(sortBy)) { + wrapper.orderBy(true, asc, Task::getDueDate); } else { - wrapper.orderByDesc(Task::getCreatedAt); + wrapper.orderBy(true, asc, Task::getCreatedAt); } Page taskPage = taskMapper.selectPage(page, wrapper); From c2d194b45d48b2649c56d0ccf79595b747649929 Mon Sep 17 00:00:00 2001 From: zhonghua Date: Tue, 24 Mar 2026 11:04:09 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=E7=8F=AD=E7=BA=A7=E5=AD=A6?= =?UTF-8?q?=E7=94=9F=E5=88=97=E8=A1=A8=E8=BF=94=E5=9B=9E=E4=B8=BB=E5=AE=B6?= =?UTF-8?q?=E9=95=BF=E5=A7=93=E5=90=8D=E4=B8=8E=E8=81=94=E7=B3=BB=E7=94=B5?= =?UTF-8?q?=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SchoolClassController: 学生列表补充 parentName、parentPhone - TeacherCourseController: 学生列表补充 parentName、parentPhone - 通过 ParentStudentMapper 查询主家长(isPrimary=1),关联 Parent 获取信息 Made-with: Cursor --- .../school/SchoolClassController.java | 19 +++++++++++++++++++ .../teacher/TeacherCourseController.java | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolClassController.java b/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolClassController.java index e105aba..0f21c7b 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolClassController.java +++ b/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolClassController.java @@ -21,9 +21,12 @@ import com.reading.platform.dto.response.StudentResponse; import com.reading.platform.entity.Clazz; import com.reading.platform.entity.ClassTeacher; import com.reading.platform.entity.Lesson; +import com.reading.platform.entity.ParentStudent; import com.reading.platform.entity.Student; import com.reading.platform.entity.Teacher; import com.reading.platform.mapper.ClassTeacherMapper; +import com.reading.platform.mapper.ParentMapper; +import com.reading.platform.mapper.ParentStudentMapper; import com.reading.platform.mapper.LessonMapper; import com.reading.platform.service.ClassService; import com.reading.platform.service.StudentService; @@ -52,6 +55,8 @@ public class SchoolClassController { private final StudentMapper studentMapper; private final TeacherService teacherService; private final LessonMapper lessonMapper; + private final ParentMapper parentMapper; + private final ParentStudentMapper parentStudentMapper; @Operation(summary = "Create class") @Log(module = LogModule.CLASS, type = LogOperationType.CREATE, description = "创建新班级") @@ -205,6 +210,20 @@ public class SchoolClassController { classService.getClassByIdWithTenantCheck(id, tenantId); Page page = studentService.getStudentsByClassId(id, pageNum, pageSize, keyword); List voList = studentMapper.toVO(page.getRecords()); + for (StudentResponse vo : voList) { + var parentRelation = parentStudentMapper.selectOne( + new LambdaQueryWrapper() + .eq(ParentStudent::getStudentId, vo.getId()) + .eq(ParentStudent::getIsPrimary, 1) + .last("LIMIT 1")); + if (parentRelation != null) { + var parent = parentMapper.selectById(parentRelation.getParentId()); + if (parent != null) { + vo.setParentName(parent.getName()); + vo.setParentPhone(parent.getPhone()); + } + } + } return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize())); } diff --git a/reading-platform-java/src/main/java/com/reading/platform/controller/teacher/TeacherCourseController.java b/reading-platform-java/src/main/java/com/reading/platform/controller/teacher/TeacherCourseController.java index 0c055ce..3723773 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/controller/teacher/TeacherCourseController.java +++ b/reading-platform-java/src/main/java/com/reading/platform/controller/teacher/TeacherCourseController.java @@ -19,11 +19,14 @@ import com.reading.platform.dto.response.StudentResponse; import com.reading.platform.dto.response.TeacherResponse; import com.reading.platform.entity.Clazz; import com.reading.platform.entity.Lesson; +import com.reading.platform.entity.ParentStudent; import com.reading.platform.entity.CoursePackage; import com.reading.platform.entity.Student; import com.reading.platform.entity.Teacher; import com.reading.platform.entity.CourseLesson; import com.reading.platform.mapper.LessonMapper; +import com.reading.platform.mapper.ParentMapper; +import com.reading.platform.mapper.ParentStudentMapper; import com.reading.platform.service.ClassService; import com.reading.platform.service.CourseLessonService; import com.reading.platform.service.CoursePackageService; @@ -55,6 +58,8 @@ public class TeacherCourseController { private final StudentMapper studentMapper; private final TeacherMapper teacherMapper; private final LessonMapper lessonMapper; + private final ParentMapper parentMapper; + private final ParentStudentMapper parentStudentMapper; @Operation(summary = "获取教师的班级列表") @GetMapping("/classes") @@ -183,6 +188,20 @@ public class TeacherCourseController { Clazz clazz = classService.getClassByIdWithTenantCheck(id, tenantId); Page page = studentService.getStudentsByClassId(id, pageNum, pageSize, keyword); List voList = studentMapper.toVO(page.getRecords()); + for (StudentResponse vo : voList) { + var parentRelation = parentStudentMapper.selectOne( + new LambdaQueryWrapper() + .eq(ParentStudent::getStudentId, vo.getId()) + .eq(ParentStudent::getIsPrimary, 1) + .last("LIMIT 1")); + if (parentRelation != null) { + var parent = parentMapper.selectById(parentRelation.getParentId()); + if (parent != null) { + vo.setParentName(parent.getName()); + vo.setParentPhone(parent.getPhone()); + } + } + } int studentCount = studentService.getStudentListByClassId(id).size(); long lessonCount = 0; From 5cc99d232af306b181796855054e00c2c53acbc0 Mon Sep 17 00:00:00 2001 From: zhonghua Date: Tue, 24 Mar 2026 11:22:11 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=E7=8F=AD=E7=BA=A7=E5=AD=A6=E7=94=9F?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E4=B8=8E=E5=B9=B3=E5=9D=87=E5=BE=97=E5=88=86?= =?UTF-8?q?=E4=B8=80=E8=87=B4=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 前端: 搜索改为调用接口查询,移除 Math.random() 模拟数据 - 前端: 修复搜索图标点击、清空后重新请求 - 后端: StudentResponse 新增 readingCount/lessonCount/avgScore - 后端: StudentRecordMapper 添加批量统计学生阅读与得分 - 后端: getClassStudents 返回真实统计数据 Made-with: Cursor --- .../teacher/classes/ClassStudentsView.vue | 45 ++++++++++++------- .../teacher/TeacherCourseController.java | 19 ++++++++ .../dto/response/StudentResponse.java | 9 ++++ .../platform/mapper/StudentRecordMapper.java | 17 +++++++ 4 files changed, 73 insertions(+), 17 deletions(-) diff --git a/reading-platform-frontend/src/views/teacher/classes/ClassStudentsView.vue b/reading-platform-frontend/src/views/teacher/classes/ClassStudentsView.vue index 5901fe3..9e3ed64 100644 --- a/reading-platform-frontend/src/views/teacher/classes/ClassStudentsView.vue +++ b/reading-platform-frontend/src/views/teacher/classes/ClassStudentsView.vue @@ -51,12 +51,13 @@ placeholder="搜索学生" style="width: 200px;" allow-clear + @search="handleSearch" /> @@ -167,7 +168,7 @@ ") + List> selectStudentStats(@Param("studentIds") List studentIds); + /** * 统计指定时间段内有上课记录的去重学生数 * @param startTime 开始时间 From f288155c8c1254edb6f443843e90754d2ef49774 Mon Sep 17 00:00:00 2001 From: zhonghua Date: Tue, 24 Mar 2026 11:33:43 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat(KidsMode):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E6=8C=89=E9=92=AE=E5=88=B0=E4=B8=BB=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E5=8C=BA=EF=BC=8C=E4=BD=BF=E7=94=A8=E5=A4=A7=E5=8F=B7?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- .../teacher/lessons/components/KidsMode.vue | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/reading-platform-frontend/src/views/teacher/lessons/components/KidsMode.vue b/reading-platform-frontend/src/views/teacher/lessons/components/KidsMode.vue index 25d815f..0424440 100644 --- a/reading-platform-frontend/src/views/teacher/lessons/components/KidsMode.vue +++ b/reading-platform-frontend/src/views/teacher/lessons/components/KidsMode.vue @@ -70,12 +70,10 @@ -
-
- -
-
{{ props.currentLesson?.name || '准备开始' }}
-
点击下方资源开始播放
+
@@ -198,6 +196,7 @@ import { VolumeX, MonitorPlay, Lightbulb, + Play, } from 'lucide-vue-next'; import EbookViewer from './viewers/EbookViewer.vue'; import Player from '@/views/office/player.vue'; @@ -801,6 +800,12 @@ const handleResourceByIndex = (index: number) => { } }; +// 播放当前选中的资源 +const playCurrentResource = () => { + if (allLessonResources.value.length === 0) return; + handleResourceByIndex(currentResourceIndex.value); +}; + // 上一个资源 const prevResource = () => { if (currentResourceIndex.value > 0) { @@ -1227,6 +1232,37 @@ onUnmounted(() => { opacity: 0.7; color: #8D6E63; } + + .play-area-btn { + display: flex; + align-items: center; + justify-content: center; + width: 160px; + height: 160px; + margin-top: 32px; + padding: 0; + border-radius: 50%; + background: linear-gradient(135deg, #FF9800 0%, #FF6F00 100%); + border: 4px solid #E65100; + color: white; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 8px 32px rgba(255, 111, 0, 0.45); + + &:hover:not(:disabled) { + transform: scale(1.08); + box-shadow: 0 12px 40px rgba(255, 111, 0, 0.55); + } + + &:active:not(:disabled) { + transform: scale(0.98); + } + + &:disabled { + opacity: 0.4; + cursor: not-allowed; + } + } } // 底部固定导航 @@ -1511,7 +1547,8 @@ onUnmounted(() => { .resources-section { margin-bottom: 16px; - padding: 4px 0; /* 为动画留出上下呼吸空间,避免被父级裁剪 */ + padding: 4px 0; + /* 为动画留出上下呼吸空间,避免被父级裁剪 */ .section-label { display: flex; @@ -1533,7 +1570,8 @@ onUnmounted(() => { display: flex; gap: 16px; overflow-x: auto; - padding: 16px 12px; /* 增加内边距,为 scale/shadow 动画留出空间 */ + padding: 16px 12px; + /* 增加内边距,为 scale/shadow 动画留出空间 */ flex-wrap: wrap; &::-webkit-scrollbar {