Browse Source

VIP课程组详情

Steven 4 months ago
parent
commit
6fc834524e
78 changed files with 5148 additions and 242 deletions
  1. 138 0
      KulexiuForStudent/KulexiuForStudent.xcodeproj/project.pbxproj
  2. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/courseDetail_bg.imageset/Contents.json
  3. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/courseDetail_bg.imageset/courseDetail_bg@2x.png
  4. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/courseDetail_bg.imageset/courseDetail_bg@3x.png
  5. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_attendence.imageset/Contents.json
  6. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_attendence.imageset/course_attendence@2x.png
  7. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_attendence.imageset/course_attendence@3x.png
  8. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_noArrive.imageset/Contents.json
  9. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_noArrive.imageset/course_noArrive@2x.png
  10. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_noArrive.imageset/course_noArrive@3x.png
  11. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_plan_icon.imageset/Contents.json
  12. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_plan_icon.imageset/course_plan_icon@2x.png
  13. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_plan_icon.imageset/course_plan_icon@3x.png
  14. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/group_addPlan.imageset/Contents.json
  15. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/group_addPlan.imageset/group_addPlan@2x.png
  16. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/group_addPlan.imageset/group_addPlan@3x.png
  17. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/plan_edit.imageset/Contents.json
  18. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/plan_edit.imageset/plan_edit@2x.png
  19. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/plan_edit.imageset/plan_edit@3x.png
  20. 22 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSNetworkingManager.h
  21. 37 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSNetworkingManager.m
  22. 7 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/AccompanyCourse/Controller/AccompanyCourseGroupViewController.h
  23. 195 69
      KulexiuForStudent/KulexiuForStudent/Module/Course/AccompanyCourse/Controller/AccompanyCourseGroupViewController.m
  24. 0 2
      KulexiuForStudent/KulexiuForStudent/Module/Course/AccompanyCourse/Controller/AccompanyDetailViewController.h
  25. 0 2
      KulexiuForStudent/KulexiuForStudent/Module/Course/Controller/CourseViewController.m
  26. 0 2
      KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/Controller/MusicRoomDetailViewController.h
  27. 7 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/Controller/MusicRoomGroupViewController.h
  28. 219 68
      KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/Controller/MusicRoomGroupViewController.m
  29. 19 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomCourseListCell.h
  30. 135 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomCourseListCell.m
  31. 202 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomCourseListCell.xib
  32. 25 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomGroupHeadView.h
  33. 163 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomGroupHeadView.m
  34. 161 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomGroupHeadView.xib
  35. 18 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomStudentPageView.h
  36. 20 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomStudentPageView.m
  37. 24 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupNavView.h
  38. 51 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupNavView.m
  39. 70 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupNavView.xib
  40. 24 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAddView.h
  41. 54 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAddView.m
  42. 89 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAddView.xib
  43. 24 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAlertView.h
  44. 166 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAlertView.m
  45. 157 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAlertView.xib
  46. 26 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanDescView.h
  47. 100 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanDescView.m
  48. 111 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanDescView.xib
  49. 41 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/GroupCourseSortView.h
  50. 101 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/GroupCourseSortView.m
  51. 192 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/GroupCourseSortView.xib
  52. 6 4
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Controller/VIPCourseGroupViewController.h
  53. 191 65
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Controller/VIPCourseGroupViewController.m
  54. 0 2
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Controller/VipCouseDetailViewController.h
  55. 30 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/CourseGroupDetailModel.h
  56. 169 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/CourseGroupDetailModel.m
  57. 23 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/CourseStudentVos.h
  58. 120 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/CourseStudentVos.m
  59. 29 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/GroupCourseListModel.h
  60. 181 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/GroupCourseListModel.m
  61. 30 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseGroupBodyView.h
  62. 586 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseGroupBodyView.m
  63. 27 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseInfoView.h
  64. 166 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseInfoView.m
  65. 167 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseInfoView.xib
  66. 21 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseListCell.h
  67. 139 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseListCell.m
  68. 207 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseListCell.xib
  69. 27 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/VipCouseInfoCell.h
  70. 90 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/VipCouseInfoCell.m
  71. 170 0
      KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/VipCouseInfoCell.xib
  72. 0 2
      KulexiuForStudent/KulexiuForStudent/Module/Home/Controller/HomeViewController.m
  73. 6 1
      KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/AccompanyCourseGroup/MyAccompanyCourseGroupView.m
  74. 11 11
      KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/MusicRoomCourseGroup/MyMusicGroupListCell.xib
  75. 5 1
      KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/MusicRoomCourseGroup/MyMusicRoomCourseGroupView.m
  76. 1 2
      KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/MyLessonBodyView.m
  77. 11 11
      KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/VIPCourseGroup/MyProgramCourseGroupCell.xib
  78. 5 0
      KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/VIPCourseGroup/MyVipCourseGroupView.m

+ 138 - 0
KulexiuForStudent/KulexiuForStudent.xcodeproj/project.pbxproj

@@ -481,6 +481,31 @@
 		BC542E5D2840A60100633781 /* UserSettingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC542E5B2840A60000633781 /* UserSettingViewController.m */; };
 		BC542E612840A60E00633781 /* UseBodyView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC542E5E2840A60D00633781 /* UseBodyView.m */; };
 		BC542E622840A60E00633781 /* UseBodyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC542E5F2840A60D00633781 /* UseBodyView.xib */; };
+		BC5A4B3F2CF40BEF008C2FDD /* GroupCourseListModel.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B3E2CF40BEF008C2FDD /* GroupCourseListModel.m */; };
+		BC5A4B402CF40BEF008C2FDD /* CourseGroupDetailModel.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B3A2CF40BEF008C2FDD /* CourseGroupDetailModel.m */; };
+		BC5A4B412CF40BEF008C2FDD /* CourseStudentVos.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B3C2CF40BEF008C2FDD /* CourseStudentVos.m */; };
+		BC5A4B4D2CF40BF9008C2FDD /* ProgramCourseInfoView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B452CF40BF9008C2FDD /* ProgramCourseInfoView.m */; };
+		BC5A4B4E2CF40BF9008C2FDD /* VipCouseInfoCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B4B2CF40BF9008C2FDD /* VipCouseInfoCell.m */; };
+		BC5A4B4F2CF40BF9008C2FDD /* ProgramCourseGroupBodyView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B432CF40BF9008C2FDD /* ProgramCourseGroupBodyView.m */; };
+		BC5A4B502CF40BF9008C2FDD /* ProgramCourseListCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B482CF40BF9008C2FDD /* ProgramCourseListCell.m */; };
+		BC5A4B512CF40BF9008C2FDD /* ProgramCourseInfoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC5A4B462CF40BF9008C2FDD /* ProgramCourseInfoView.xib */; };
+		BC5A4B522CF40BF9008C2FDD /* ProgramCourseListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC5A4B492CF40BF9008C2FDD /* ProgramCourseListCell.xib */; };
+		BC5A4B532CF40BF9008C2FDD /* VipCouseInfoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC5A4B4C2CF40BF9008C2FDD /* VipCouseInfoCell.xib */; };
+		BC5A4B642CF40C35008C2FDD /* CourseGroupPlanDescView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B5E2CF40C35008C2FDD /* CourseGroupPlanDescView.m */; };
+		BC5A4B652CF40C35008C2FDD /* CourseGroupNavView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B552CF40C35008C2FDD /* CourseGroupNavView.m */; };
+		BC5A4B662CF40C35008C2FDD /* CourseGroupPlanAddView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B582CF40C35008C2FDD /* CourseGroupPlanAddView.m */; };
+		BC5A4B672CF40C35008C2FDD /* CourseGroupPlanAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B5B2CF40C35008C2FDD /* CourseGroupPlanAlertView.m */; };
+		BC5A4B682CF40C35008C2FDD /* GroupCourseSortView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B612CF40C35008C2FDD /* GroupCourseSortView.m */; };
+		BC5A4B692CF40C35008C2FDD /* CourseGroupPlanAlertView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC5A4B5C2CF40C35008C2FDD /* CourseGroupPlanAlertView.xib */; };
+		BC5A4B6A2CF40C35008C2FDD /* CourseGroupPlanDescView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC5A4B5F2CF40C35008C2FDD /* CourseGroupPlanDescView.xib */; };
+		BC5A4B6B2CF40C35008C2FDD /* CourseGroupPlanAddView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC5A4B592CF40C35008C2FDD /* CourseGroupPlanAddView.xib */; };
+		BC5A4B6C2CF40C35008C2FDD /* CourseGroupNavView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC5A4B562CF40C35008C2FDD /* CourseGroupNavView.xib */; };
+		BC5A4B6D2CF40C35008C2FDD /* GroupCourseSortView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC5A4B622CF40C35008C2FDD /* GroupCourseSortView.xib */; };
+		BC5A4B882CF42104008C2FDD /* MusicRoomGroupHeadView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B842CF42104008C2FDD /* MusicRoomGroupHeadView.m */; };
+		BC5A4B892CF42104008C2FDD /* MusicRoomStudentPageView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B872CF42104008C2FDD /* MusicRoomStudentPageView.m */; };
+		BC5A4B8A2CF42104008C2FDD /* MusicRoomCourseListCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A4B812CF42104008C2FDD /* MusicRoomCourseListCell.m */; };
+		BC5A4B8B2CF42104008C2FDD /* MusicRoomCourseListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC5A4B822CF42104008C2FDD /* MusicRoomCourseListCell.xib */; };
+		BC5A4B8C2CF42104008C2FDD /* MusicRoomGroupHeadView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC5A4B852CF42104008C2FDD /* MusicRoomGroupHeadView.xib */; };
 		BC5F765029001C8D00B433E0 /* HomeDragButton.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5F764F29001C8D00B433E0 /* HomeDragButton.m */; };
 		BC60E3CC287D552800B05441 /* AccountDeleteViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC60E3C5287D552800B05441 /* AccountDeleteViewController.m */; };
 		BC60E3CD287D552800B05441 /* DeleteAccountBodyView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC60E3C9287D552800B05441 /* DeleteAccountBodyView.m */; };
@@ -1863,6 +1888,46 @@
 		BC542E5E2840A60D00633781 /* UseBodyView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UseBodyView.m; sourceTree = "<group>"; };
 		BC542E5F2840A60D00633781 /* UseBodyView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UseBodyView.xib; sourceTree = "<group>"; };
 		BC542E602840A60D00633781 /* UseBodyView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UseBodyView.h; sourceTree = "<group>"; };
+		BC5A4B392CF40BEF008C2FDD /* CourseGroupDetailModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CourseGroupDetailModel.h; sourceTree = "<group>"; };
+		BC5A4B3A2CF40BEF008C2FDD /* CourseGroupDetailModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CourseGroupDetailModel.m; sourceTree = "<group>"; };
+		BC5A4B3B2CF40BEF008C2FDD /* CourseStudentVos.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CourseStudentVos.h; sourceTree = "<group>"; };
+		BC5A4B3C2CF40BEF008C2FDD /* CourseStudentVos.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CourseStudentVos.m; sourceTree = "<group>"; };
+		BC5A4B3D2CF40BEF008C2FDD /* GroupCourseListModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GroupCourseListModel.h; sourceTree = "<group>"; };
+		BC5A4B3E2CF40BEF008C2FDD /* GroupCourseListModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GroupCourseListModel.m; sourceTree = "<group>"; };
+		BC5A4B422CF40BF9008C2FDD /* ProgramCourseGroupBodyView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProgramCourseGroupBodyView.h; sourceTree = "<group>"; };
+		BC5A4B432CF40BF9008C2FDD /* ProgramCourseGroupBodyView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ProgramCourseGroupBodyView.m; sourceTree = "<group>"; };
+		BC5A4B442CF40BF9008C2FDD /* ProgramCourseInfoView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProgramCourseInfoView.h; sourceTree = "<group>"; };
+		BC5A4B452CF40BF9008C2FDD /* ProgramCourseInfoView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ProgramCourseInfoView.m; sourceTree = "<group>"; };
+		BC5A4B462CF40BF9008C2FDD /* ProgramCourseInfoView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ProgramCourseInfoView.xib; sourceTree = "<group>"; };
+		BC5A4B472CF40BF9008C2FDD /* ProgramCourseListCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProgramCourseListCell.h; sourceTree = "<group>"; };
+		BC5A4B482CF40BF9008C2FDD /* ProgramCourseListCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ProgramCourseListCell.m; sourceTree = "<group>"; };
+		BC5A4B492CF40BF9008C2FDD /* ProgramCourseListCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ProgramCourseListCell.xib; sourceTree = "<group>"; };
+		BC5A4B4A2CF40BF9008C2FDD /* VipCouseInfoCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VipCouseInfoCell.h; sourceTree = "<group>"; };
+		BC5A4B4B2CF40BF9008C2FDD /* VipCouseInfoCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VipCouseInfoCell.m; sourceTree = "<group>"; };
+		BC5A4B4C2CF40BF9008C2FDD /* VipCouseInfoCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = VipCouseInfoCell.xib; sourceTree = "<group>"; };
+		BC5A4B542CF40C35008C2FDD /* CourseGroupNavView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CourseGroupNavView.h; sourceTree = "<group>"; };
+		BC5A4B552CF40C35008C2FDD /* CourseGroupNavView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CourseGroupNavView.m; sourceTree = "<group>"; };
+		BC5A4B562CF40C35008C2FDD /* CourseGroupNavView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CourseGroupNavView.xib; sourceTree = "<group>"; };
+		BC5A4B572CF40C35008C2FDD /* CourseGroupPlanAddView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CourseGroupPlanAddView.h; sourceTree = "<group>"; };
+		BC5A4B582CF40C35008C2FDD /* CourseGroupPlanAddView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CourseGroupPlanAddView.m; sourceTree = "<group>"; };
+		BC5A4B592CF40C35008C2FDD /* CourseGroupPlanAddView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CourseGroupPlanAddView.xib; sourceTree = "<group>"; };
+		BC5A4B5A2CF40C35008C2FDD /* CourseGroupPlanAlertView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CourseGroupPlanAlertView.h; sourceTree = "<group>"; };
+		BC5A4B5B2CF40C35008C2FDD /* CourseGroupPlanAlertView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CourseGroupPlanAlertView.m; sourceTree = "<group>"; };
+		BC5A4B5C2CF40C35008C2FDD /* CourseGroupPlanAlertView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CourseGroupPlanAlertView.xib; sourceTree = "<group>"; };
+		BC5A4B5D2CF40C35008C2FDD /* CourseGroupPlanDescView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CourseGroupPlanDescView.h; sourceTree = "<group>"; };
+		BC5A4B5E2CF40C35008C2FDD /* CourseGroupPlanDescView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CourseGroupPlanDescView.m; sourceTree = "<group>"; };
+		BC5A4B5F2CF40C35008C2FDD /* CourseGroupPlanDescView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CourseGroupPlanDescView.xib; sourceTree = "<group>"; };
+		BC5A4B602CF40C35008C2FDD /* GroupCourseSortView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GroupCourseSortView.h; sourceTree = "<group>"; };
+		BC5A4B612CF40C35008C2FDD /* GroupCourseSortView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GroupCourseSortView.m; sourceTree = "<group>"; };
+		BC5A4B622CF40C35008C2FDD /* GroupCourseSortView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = GroupCourseSortView.xib; sourceTree = "<group>"; };
+		BC5A4B802CF42104008C2FDD /* MusicRoomCourseListCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MusicRoomCourseListCell.h; sourceTree = "<group>"; };
+		BC5A4B812CF42104008C2FDD /* MusicRoomCourseListCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MusicRoomCourseListCell.m; sourceTree = "<group>"; };
+		BC5A4B822CF42104008C2FDD /* MusicRoomCourseListCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MusicRoomCourseListCell.xib; sourceTree = "<group>"; };
+		BC5A4B832CF42104008C2FDD /* MusicRoomGroupHeadView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MusicRoomGroupHeadView.h; sourceTree = "<group>"; };
+		BC5A4B842CF42104008C2FDD /* MusicRoomGroupHeadView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MusicRoomGroupHeadView.m; sourceTree = "<group>"; };
+		BC5A4B852CF42104008C2FDD /* MusicRoomGroupHeadView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MusicRoomGroupHeadView.xib; sourceTree = "<group>"; };
+		BC5A4B862CF42104008C2FDD /* MusicRoomStudentPageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MusicRoomStudentPageView.h; sourceTree = "<group>"; };
+		BC5A4B872CF42104008C2FDD /* MusicRoomStudentPageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MusicRoomStudentPageView.m; sourceTree = "<group>"; };
 		BC5F764E29001C8D00B433E0 /* HomeDragButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeDragButton.h; sourceTree = "<group>"; };
 		BC5F764F29001C8D00B433E0 /* HomeDragButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HomeDragButton.m; sourceTree = "<group>"; };
 		BC60E3C5287D552800B05441 /* AccountDeleteViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AccountDeleteViewController.m; sourceTree = "<group>"; };
@@ -3093,6 +3158,7 @@
 		275FA1FC27E7356B00CFEA2E /* View */ = {
 			isa = PBXGroup;
 			children = (
+				BC5A4B632CF40C35008C2FDD /* CourseGroupView */,
 				BC119225280ED8E800A716F7 /* CourseNavView.h */,
 				BC119226280ED8E800A716F7 /* CourseNavView.m */,
 				BC119229280ED8E800A716F7 /* CourseNavView.xib */,
@@ -4939,10 +5005,40 @@
 				BC494A79286958EC00CCD343 /* MusicRoomCourseInfoCell.h */,
 				BC494A7A286958EC00CCD343 /* MusicRoomCourseInfoCell.m */,
 				BC494A7B286958EC00CCD343 /* MusicRoomCourseInfoCell.xib */,
+				BC5A4B802CF42104008C2FDD /* MusicRoomCourseListCell.h */,
+				BC5A4B812CF42104008C2FDD /* MusicRoomCourseListCell.m */,
+				BC5A4B822CF42104008C2FDD /* MusicRoomCourseListCell.xib */,
+				BC5A4B832CF42104008C2FDD /* MusicRoomGroupHeadView.h */,
+				BC5A4B842CF42104008C2FDD /* MusicRoomGroupHeadView.m */,
+				BC5A4B852CF42104008C2FDD /* MusicRoomGroupHeadView.xib */,
+				BC5A4B862CF42104008C2FDD /* MusicRoomStudentPageView.h */,
+				BC5A4B872CF42104008C2FDD /* MusicRoomStudentPageView.m */,
 			);
 			path = View;
 			sourceTree = "<group>";
 		};
+		BC5A4B632CF40C35008C2FDD /* CourseGroupView */ = {
+			isa = PBXGroup;
+			children = (
+				BC5A4B542CF40C35008C2FDD /* CourseGroupNavView.h */,
+				BC5A4B552CF40C35008C2FDD /* CourseGroupNavView.m */,
+				BC5A4B562CF40C35008C2FDD /* CourseGroupNavView.xib */,
+				BC5A4B572CF40C35008C2FDD /* CourseGroupPlanAddView.h */,
+				BC5A4B582CF40C35008C2FDD /* CourseGroupPlanAddView.m */,
+				BC5A4B592CF40C35008C2FDD /* CourseGroupPlanAddView.xib */,
+				BC5A4B5A2CF40C35008C2FDD /* CourseGroupPlanAlertView.h */,
+				BC5A4B5B2CF40C35008C2FDD /* CourseGroupPlanAlertView.m */,
+				BC5A4B5C2CF40C35008C2FDD /* CourseGroupPlanAlertView.xib */,
+				BC5A4B5D2CF40C35008C2FDD /* CourseGroupPlanDescView.h */,
+				BC5A4B5E2CF40C35008C2FDD /* CourseGroupPlanDescView.m */,
+				BC5A4B5F2CF40C35008C2FDD /* CourseGroupPlanDescView.xib */,
+				BC5A4B602CF40C35008C2FDD /* GroupCourseSortView.h */,
+				BC5A4B612CF40C35008C2FDD /* GroupCourseSortView.m */,
+				BC5A4B622CF40C35008C2FDD /* GroupCourseSortView.xib */,
+			);
+			path = CourseGroupView;
+			sourceTree = "<group>";
+		};
 		BC5F764D29001C5A00B433E0 /* HomeDragButton */ = {
 			isa = PBXGroup;
 			children = (
@@ -6261,6 +6357,12 @@
 		BCFB9B992CE49F7A00B66BC0 /* Model */ = {
 			isa = PBXGroup;
 			children = (
+				BC5A4B392CF40BEF008C2FDD /* CourseGroupDetailModel.h */,
+				BC5A4B3A2CF40BEF008C2FDD /* CourseGroupDetailModel.m */,
+				BC5A4B3B2CF40BEF008C2FDD /* CourseStudentVos.h */,
+				BC5A4B3C2CF40BEF008C2FDD /* CourseStudentVos.m */,
+				BC5A4B3D2CF40BEF008C2FDD /* GroupCourseListModel.h */,
+				BC5A4B3E2CF40BEF008C2FDD /* GroupCourseListModel.m */,
 			);
 			path = Model;
 			sourceTree = "<group>";
@@ -6268,6 +6370,17 @@
 		BCFB9B9A2CE49F7A00B66BC0 /* View */ = {
 			isa = PBXGroup;
 			children = (
+				BC5A4B422CF40BF9008C2FDD /* ProgramCourseGroupBodyView.h */,
+				BC5A4B432CF40BF9008C2FDD /* ProgramCourseGroupBodyView.m */,
+				BC5A4B442CF40BF9008C2FDD /* ProgramCourseInfoView.h */,
+				BC5A4B452CF40BF9008C2FDD /* ProgramCourseInfoView.m */,
+				BC5A4B462CF40BF9008C2FDD /* ProgramCourseInfoView.xib */,
+				BC5A4B472CF40BF9008C2FDD /* ProgramCourseListCell.h */,
+				BC5A4B482CF40BF9008C2FDD /* ProgramCourseListCell.m */,
+				BC5A4B492CF40BF9008C2FDD /* ProgramCourseListCell.xib */,
+				BC5A4B4A2CF40BF9008C2FDD /* VipCouseInfoCell.h */,
+				BC5A4B4B2CF40BF9008C2FDD /* VipCouseInfoCell.m */,
+				BC5A4B4C2CF40BF9008C2FDD /* VipCouseInfoCell.xib */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -6564,6 +6677,11 @@
 				BC106C3B2A9338A7000759A9 /* custom_loading.json in Resources */,
 				BC89AC1A2AB08FA100B077AF /* TenantMusicCollectionViewCell.xib in Resources */,
 				2723B63227F157D500E0B90B /* GroupSettingBodyView.xib in Resources */,
+				BC5A4B692CF40C35008C2FDD /* CourseGroupPlanAlertView.xib in Resources */,
+				BC5A4B6A2CF40C35008C2FDD /* CourseGroupPlanDescView.xib in Resources */,
+				BC5A4B6B2CF40C35008C2FDD /* CourseGroupPlanAddView.xib in Resources */,
+				BC5A4B6C2CF40C35008C2FDD /* CourseGroupNavView.xib in Resources */,
+				BC5A4B6D2CF40C35008C2FDD /* GroupCourseSortView.xib in Resources */,
 				BCC0F65C2A8CD8F500C4EFA4 /* TxClassroomChatMineCell.xib in Resources */,
 				BC71D272288804CD0010F14B /* img_27.png in Resources */,
 				BCFE53F728128A9600AD6786 /* TeacherShowCell.xib in Resources */,
@@ -6606,6 +6724,9 @@
 				BCC583C028A9EC6400BAB4CF /* cloud_animation_15.png in Resources */,
 				BC119280280FB10900A716F7 /* AccompanyRemarkCell.xib in Resources */,
 				BCC409012AFCE6B500C60249 /* MusicPublicContentView.xib in Resources */,
+				BC5A4B512CF40BF9008C2FDD /* ProgramCourseInfoView.xib in Resources */,
+				BC5A4B522CF40BF9008C2FDD /* ProgramCourseListCell.xib in Resources */,
+				BC5A4B532CF40BF9008C2FDD /* VipCouseInfoCell.xib in Resources */,
 				BC7354992CEED2EB00C2B35C /* MyCourseGroupNavView.xib in Resources */,
 				BC71D271288804CD0010F14B /* img_26.png in Resources */,
 				BC12639F28FF8E7400509E90 /* HomeRecommendMusicView.xib in Resources */,
@@ -6736,6 +6857,8 @@
 				BC71D273288804CD0010F14B /* img_33.png in Resources */,
 				BCC583C628A9EC6400BAB4CF /* cloud_animation_13.png in Resources */,
 				BCC583C428A9EC6400BAB4CF /* cloud_animation_16.png in Resources */,
+				BC5A4B8B2CF42104008C2FDD /* MusicRoomCourseListCell.xib in Resources */,
+				BC5A4B8C2CF42104008C2FDD /* MusicRoomGroupHeadView.xib in Resources */,
 				BCFDA65828BCA2000022B497 /* live_animation.json in Resources */,
 				BCB5B2D62ABB1E13005BF25D /* KSEmptyStatusView.xib in Resources */,
 				BC31BF1E2B21925700F7D538 /* TunerSettingView.xib in Resources */,
@@ -7172,6 +7295,9 @@
 				BC0D95362AC2AAEC00E54D3F /* PasswordCheckViewController.m in Sources */,
 				277935B327E324A90010E277 /* FSCalendarCollectionView.m in Sources */,
 				BCB9FA46286EDCD7005D766B /* KSTipsAlert.m in Sources */,
+				BC5A4B882CF42104008C2FDD /* MusicRoomGroupHeadView.m in Sources */,
+				BC5A4B892CF42104008C2FDD /* MusicRoomStudentPageView.m in Sources */,
+				BC5A4B8A2CF42104008C2FDD /* MusicRoomCourseListCell.m in Sources */,
 				2779352227E324A60010E277 /* UITextView+ZWLimitCounter.m in Sources */,
 				BC106C222A933869000759A9 /* TXLiveMessageModel.m in Sources */,
 				27F9032927E87C2E00C08A19 /* NetworkingCheckController.m in Sources */,
@@ -7184,6 +7310,10 @@
 				BC106C022A933829000759A9 /* TXLiveUser.m in Sources */,
 				BC8B6E7D285869B500866917 /* KSUMShareManager.m in Sources */,
 				BC802DAB28BC6EDA0079E350 /* HomeHotTalentView.m in Sources */,
+				BC5A4B4D2CF40BF9008C2FDD /* ProgramCourseInfoView.m in Sources */,
+				BC5A4B4E2CF40BF9008C2FDD /* VipCouseInfoCell.m in Sources */,
+				BC5A4B4F2CF40BF9008C2FDD /* ProgramCourseGroupBodyView.m in Sources */,
+				BC5A4B502CF40BF9008C2FDD /* ProgramCourseListCell.m in Sources */,
 				BCBFDF41281157340052AFE5 /* HomeButtonView.m in Sources */,
 				2779358127E324A80010E277 /* StateView.m in Sources */,
 				BCECE2542B3D670500C0D555 /* FeedbackBodyView.m in Sources */,
@@ -7399,6 +7529,11 @@
 				BCC0F6072A8CD86C00C4EFA4 /* TXClassroomMsgCenter.m in Sources */,
 				BCC0F6E62A8CE24400C4EFA4 /* WhiteUtils.m in Sources */,
 				275FA1E127E7351900CFEA2E /* KSTabBarViewController.m in Sources */,
+				BC5A4B642CF40C35008C2FDD /* CourseGroupPlanDescView.m in Sources */,
+				BC5A4B652CF40C35008C2FDD /* CourseGroupNavView.m in Sources */,
+				BC5A4B662CF40C35008C2FDD /* CourseGroupPlanAddView.m in Sources */,
+				BC5A4B672CF40C35008C2FDD /* CourseGroupPlanAlertView.m in Sources */,
+				BC5A4B682CF40C35008C2FDD /* GroupCourseSortView.m in Sources */,
 				BC98527C2BCCC56C008F0435 /* AlbumSwitchView.m in Sources */,
 				BC11926B280FAF5900A716F7 /* AccompanyAlertView.m in Sources */,
 				BCC0F66C2A8CD8F500C4EFA4 /* TXClassTitleView.m in Sources */,
@@ -7436,6 +7571,9 @@
 				BCC9A7B62C955CB500F5D005 /* KSPlatformDownloadAlert.m in Sources */,
 				BCC0F6592A8CD8F500C4EFA4 /* TXChatAreaView.m in Sources */,
 				2723B5CD27F157BE00E0B90B /* GroupListModel.m in Sources */,
+				BC5A4B3F2CF40BEF008C2FDD /* GroupCourseListModel.m in Sources */,
+				BC5A4B402CF40BEF008C2FDD /* CourseGroupDetailModel.m in Sources */,
+				BC5A4B412CF40BEF008C2FDD /* CourseStudentVos.m in Sources */,
 				2723B63327F157D500E0B90B /* ChatComplainBodyView.m in Sources */,
 				BC85A9E72C6B570E003C1ABE /* KSSpectrumView.m in Sources */,
 				2779357927E324A70010E277 /* KSImageButton.m in Sources */,

+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/courseDetail_bg.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "courseDetail_bg@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "courseDetail_bg@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/courseDetail_bg.imageset/courseDetail_bg@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/courseDetail_bg.imageset/courseDetail_bg@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_attendence.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "course_attendence@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "course_attendence@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_attendence.imageset/course_attendence@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_attendence.imageset/course_attendence@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_noArrive.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "course_noArrive@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "course_noArrive@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_noArrive.imageset/course_noArrive@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_noArrive.imageset/course_noArrive@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_plan_icon.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "course_plan_icon@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "course_plan_icon@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_plan_icon.imageset/course_plan_icon@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/course_plan_icon.imageset/course_plan_icon@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/group_addPlan.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "group_addPlan@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "group_addPlan@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/group_addPlan.imageset/group_addPlan@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/group_addPlan.imageset/group_addPlan@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/plan_edit.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "plan_edit@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "plan_edit@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/plan_edit.imageset/plan_edit@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/MyCourse/plan_edit.imageset/plan_edit@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/KSNetworkingManager.h

@@ -1116,6 +1116,28 @@ NS_ASSUME_NONNULL_BEGIN
 /// @param success 成功
 /// @param faliure 失败
 + (void)myCourseGroupRequest:(NSString *)post courseType:(NSString *)courseType search:(NSString *)search subjectId:(NSString *)subjectId status:(NSString *)status success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure;
+
+// courseGroup/getCourseGroup
+
+/// 查询课程组详情
+/// @param post post
+/// @param courseGroupId 课程组编号
+/// @param success 成功
+/// @param faliure 失败
++ (void)getCourseGroupDetailRequest:(NSString *)post courseGroupId:(NSString *)courseGroupId success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure;
+
+// /courseSchedule/list
+
+/// 查询课程组关联课程
+/// @param post post
+/// @param courseGroupId 课程组编号
+/// @param classMonth 年月
+/// @param attendanceStatus 考勤状态
+/// @param status 课程状态
+/// @param success 成功
+/// @param faliure 失败
++ (void)courseScheduleListRequest:(NSString *)post courseGroupId:(NSString *)courseGroupId classMonth:(NSString *)classMonth attendanceStatus:(NSString *)attendanceStatus courseStatus:(NSString *)status success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure;
+
 @end
 
 NS_ASSUME_NONNULL_END

+ 37 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/KSNetworkingManager.m

@@ -2129,4 +2129,41 @@
     [parm setValue:status forKey:@"status"];
     [self request:post andWithUrl:url and:parm success:success faliure:faliure];
 }
+
+// courseGroup/getCourseGroup
+
+/// 查询课程组详情
+/// @param post post
+/// @param courseGroupId 课程组编号
+/// @param success 成功
+/// @param faliure 失败
++ (void)getCourseGroupDetailRequest:(NSString *)post courseGroupId:(NSString *)courseGroupId success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure {
+    [self configRequestMethodForm];
+    NSString *url = [NSString stringWithFormat:@"%@%@",hostURL, @"/api-student/courseGroup/getCourseGroup"];
+    NSMutableDictionary *parm = [NSMutableDictionary dictionary];
+    [parm setValue:courseGroupId forKey:@"courseGroupId"];
+    [self request:post andWithUrl:url and:parm success:success faliure:faliure];
+}
+
+// /courseSchedule/list
+
+/// 查询课程组关联课程
+/// @param post post
+/// @param courseGroupId 课程组编号
+/// @param classMonth 年月
+/// @param attendanceStatus 考勤状态
+/// @param status 课程状态
+/// @param success 成功
+/// @param faliure 失败
++ (void)courseScheduleListRequest:(NSString *)post courseGroupId:(NSString *)courseGroupId classMonth:(NSString *)classMonth attendanceStatus:(NSString *)attendanceStatus courseStatus:(NSString *)status success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure {
+    
+    [self configRequestMethodJSON];
+    NSString *url = [NSString stringWithFormat:@"%@%@",hostURL, @"/api-student/courseSchedule/list"];
+    NSMutableDictionary *parm = [NSMutableDictionary dictionary];
+    [parm setValue:courseGroupId forKey:@"courseGroupId"];
+    [parm setValue:classMonth forKey:@"classMonth"];
+    [parm setValue:attendanceStatus forKey:@"attendanceStatus"];
+    [parm setValue:status forKey:@"status"];
+    [self request:post andWithUrl:url and:parm success:success faliure:faliure];
+}
 @end

+ 7 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/AccompanyCourse/Controller/AccompanyCourseGroupViewController.h

@@ -6,6 +6,9 @@
 //
 
 #import "KSBaseViewController.h"
+#import "JXCategoryView.h"
+#import "JXPagerView.h"
+
 
 NS_ASSUME_NONNULL_BEGIN
 // 趣纠课--课程组
@@ -13,6 +16,10 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property (nonatomic, strong) NSString *courseGroupId; // 课程组ID
 
+@property (nonatomic, strong) JXPagerView *pagerView;
+@property (nonatomic, strong, readonly) JXCategoryTitleView *categoryView;
+@property (nonatomic, strong) NSArray <NSString *> *titles;
+
 @end
 
 NS_ASSUME_NONNULL_END

+ 195 - 69
KulexiuForStudent/KulexiuForStudent/Module/Course/AccompanyCourse/Controller/AccompanyCourseGroupViewController.m

@@ -6,24 +6,34 @@
 //
 
 #import "AccompanyCourseGroupViewController.h"
-#import <RecordCheckManager.h>
-#import "KSPremissionAlert.h"
-#import "OnlineClassManager.h"
-#import "HomeworkDetailModel.h"
+#import "CourseGroupNavView.h"
+#import "JXCategoryView.h"
+#import "JXPagerListRefreshView.h"
+#import "ProgramCourseInfoView.h"
+#import "ProgramCourseGroupBodyView.h"
+#import "CourseGroupDetailModel.h"
 
-@interface AccompanyCourseGroupViewController ()
+@interface AccompanyCourseGroupViewController ()<JXPagerViewDelegate,JXCategoryViewDelegate>
 
-@property (nonatomic, strong) NSString *courseId;
+@property (nonatomic, strong) NSMutableArray *listViewArray;
 
-@property (nonatomic, strong) NSString *studentId;
+@property (nonatomic, strong) CourseGroupNavView *navView;
 
-@property (nonatomic, assign) NSInteger joinRoomBeforeTime; // 上课开始时间
+@property (nonatomic, assign) NSInteger headHeight;
 
-@property (nonatomic, assign) NSInteger quitRomeEndTime;    // 下课截止时间
+@property (nonatomic, assign) NSInteger topViewHeight;
 
-@property (nonatomic, strong) HomeworkDetailModel *homeworkModel;
+@property (nonatomic, strong) ProgramCourseInfoView *infoView;
 
-@property (nonatomic, strong) OnlineClassManager *classManager;
+@property (nonatomic, assign) BOOL isFirstLoad;
+
+@property (nonatomic, strong) CourseGroupDetailModel *detailModel;
+
+@property (nonatomic, assign) NSInteger enterStartTime;  // 开课前可进入时间配置(分钟)
+
+@property (nonatomic, assign) NSInteger quitEndTime;    // 结束后退出时间配置(分钟)
+
+@property (nonatomic, strong) UIImageView *bgImageView;
 
 @end
 
@@ -32,85 +42,201 @@
 - (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
+    self.ks_prefersNavigationBarHidden = YES;
+    [self configUI];
+    self.isFirstLoad = YES;
 }
 
-- (void)tryJoinRoom {
-    
-    NSDateFormatter *dateFormatter = [NSObject getDateformatter];
-    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
-    NSDate *beginDate = [dateFormatter dateFromString:self.homeworkModel.startTime];
-    NSDate *endDate = [dateFormatter dateFromString:self.homeworkModel.endTime];
-    NSDate *currentDate = [NSDate date];
-    NSTimeInterval beginTimeInterval = [beginDate timeIntervalSinceDate:currentDate];
-    NSTimeInterval endTimeInterval = [currentDate timeIntervalSinceDate:endDate];
-    if (beginTimeInterval <= self.joinRoomBeforeTime * 60 && endTimeInterval < 0) {
-        [self joinClassRoom];
-    }
-    else if (endTimeInterval > 0) {
-        [LOADING_MANAGER MBShowAUTOHidingInWindow:@"该课程已结束"];
-    }
-    else {
-        NSString *tipsString = [NSString stringWithFormat:@"课程还未开始,请在上课前%zd分钟进入", self.joinRoomBeforeTime];
-        [LOADING_MANAGER MBShowAUTOHidingInWindow:tipsString];
-    }
-}
-
-// 加入房间
-- (void)joinClassRoom {
+- (void)configUI {
+    [self.scrollView removeFromSuperview];
+    UIImage *bgImage = [UIImage imageNamed:@"courseDetail_bg"];
+    NSInteger height = (NSInteger)(bgImage.size.height / bgImage.size.width * KPortraitWidth);
+    self.bgImageView = [[UIImageView alloc] initWithImage:bgImage];
+    self.bgImageView.frame = CGRectMake(0, 0, KPortraitWidth, height);
+    [self.view addSubview:self.bgImageView];
     
-    // 加入房间前判断摄像头和麦克风逻辑
-    [RecordCheckManager checkCameraPremissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        [self afterCheckCameraCheckMic:type];
+    [self.view addSubview:self.navView];
+    CGFloat navHeight = [CourseGroupNavView getViewHeight];
+    [self.navView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.top.mas_equalTo(self.view);
+        make.height.mas_equalTo(navHeight);
     }];
+    self.headHeight = CGFLOAT_MIN;
+    self.titles = @[@""];
+    [self configCategoryView:navHeight];
 }
 
-- (void)afterCheckCameraCheckMic:(PREMISSIONTYPE)cameraType {
-    [RecordCheckManager checkMicPermissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        if (type == PREMISSIONTYPE_YES && cameraType == PREMISSIONTYPE_YES) {
-            // 判断是否进行课前检测
-            [self.classManager joinRoomWithId:self.courseId subjectName:self.homeworkModel.subjectName classEndTime:self.homeworkModel.endTime inViewController:self];
+- (void)requestCourseInfo {
+    
+    [KSNetworkingManager getCourseGroupDetailRequest:KS_POST courseGroupId:self.courseGroupId success:^(NSDictionary * _Nonnull dic) {
+        if ([dic ks_integerValueForKey:@"code"] == 200 && [dic ks_boolValueForKey:@"status"]) {
+            NSDictionary *parm = [dic ks_dictionaryValueForKey:@"data"];
+            self.detailModel = [[CourseGroupDetailModel alloc] initWithDictionary:parm];
+            // 配置信息
+            NSDictionary *config = [[dic ks_dictionaryValueForKey:@"data"] ks_dictionaryValueForKey:@"sysConfig"];
+            self.enterStartTime = [config ks_integerValueForKey:@"practiceStartTime"];
+            self.quitEndTime = [config ks_integerValueForKey:@"practiceEndTime"];
         }
         else {
-            NSString *content = @"";
-            CHECKDEVICETYPE checkType = CHECKDEVICETYPE_BOTH;
-            if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_NO) {
-                content = @"请开启相机和麦克风访问权限";
-                checkType = CHECKDEVICETYPE_BOTH;
-            }
-            else if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_YES) {
-                content =  @"请开启相机访问权限";
-                checkType = CHECKDEVICETYPE_CAMREA;
-            }
-            else if (cameraType == PREMISSIONTYPE_YES && type == PREMISSIONTYPE_NO) {
-                content = @"请开启麦克风访问权限";
-                checkType = CHECKDEVICETYPE_MIC;
-            }
-            [self showAlertWithMessage:content type:checkType];
+            [LOADING_MANAGER MBShowAUTOHidingInWindow:MESSAGEKEY];
         }
+        [self refreshHeadView];
+    } faliure:^(NSError * _Nonnull error) {
+        [self refreshHeadView];
     }];
+}
+
+- (void)refreshHeadView {
+    [self refreshListPage];
+    self.topViewHeight = [ProgramCourseInfoView getViewHeightWithPlanString:self.detailModel.coursePlan];
+    [self.infoView configWithSource:self.detailModel];
+    [self.pagerView reloadData];
+}
+
+- (void)configCategoryView:(NSInteger)topHeight {
     
+    _categoryView = [[JXCategoryTitleView alloc] initWithFrame:CGRectMake(0, topHeight, KPortraitWidth, self.headHeight)];
+    self.categoryView.titles = self.titles;
+    self.categoryView.delegate = self;
+    self.categoryView.titleFont = [UIFont systemFontOfSize:16.0f];
+    self.categoryView.titleSelectedFont = [UIFont systemFontOfSize:16.0f weight:UIFontWeightSemibold];
+    self.categoryView.titleSelectedColor = HexRGB(0x333333);
+    self.categoryView.titleColor = HexRGB(0x666666);
+    self.categoryView.titleColorGradientEnabled = YES;
+    
+    _pagerView = [self preferredPagingView];
+    self.pagerView.frame = CGRectMake(0, topHeight, KPortraitWidth, KPortraitHeight - topHeight);
+    self.pagerView.backgroundColor = [UIColor clearColor];
+    self.pagerView.mainTableView.backgroundColor = [UIColor clearColor];
+    self.pagerView.listContainerView.backgroundColor = [UIColor clearColor];
+//    self.pagerView.mainTableView.gestureDelegate = self;
+    self.categoryView.listContainer = (id<JXCategoryViewListContainer>)self.pagerView.listContainerView;
+    self.pagerView.listContainerView.listCellBackgroundColor = [UIColor clearColor];
+    [self.view addSubview:self.pagerView];
 }
 
-- (void)showAlertWithMessage:(NSString *)message type:(CHECKDEVICETYPE)deviceType {
-    [KSPremissionAlert shareInstanceDisplayImage:deviceType message:message showInView:self.view cancel:^{
-        
-    } confirm:^{
-        [self openSettingView];
-    }];
+- (void)viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    self.navigationController.interactivePopGestureRecognizer.enabled = (self.categoryView.selectedIndex == 0);
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+    [super viewWillAppear:animated];
+    self.navigationController.interactivePopGestureRecognizer.enabled = YES;
+    // 刷新课程详情
+    [self requestCourseInfo];
+    
+    if (self.isFirstLoad == NO) {
+        [self refreshListPage];
+    }
+    self.isFirstLoad = NO;
+    
+}
+
+- (void)refreshListPage {
+    id value = self.listViewArray[0];
+    if ([value isKindOfClass:[ProgramCourseGroupBodyView class]]) {
+        ProgramCourseGroupBodyView *listView = (ProgramCourseGroupBodyView *)value;
+        listView.enterStartTime = self.enterStartTime;
+        listView.quitEndTime = self.quitEndTime;
+        [listView beginFirstRefresh];
+    }
+}
+
+- (JXPagerView *)preferredPagingView {
+    return [[JXPagerListRefreshView alloc] initWithDelegate:self];
+}
+
+#pragma mark - JXPagerViewDelegate
+
+- (UIView *)tableHeaderViewInPagerView:(JXPagerView *)pagerView {
+    return self.infoView;
+}
+
+- (NSUInteger)tableHeaderViewHeightInPagerView:(JXPagerView *)pagerView {
+    return self.topViewHeight;
+}
+
+- (NSUInteger)heightForPinSectionHeaderInPagerView:(JXPagerView *)pagerView {
+    return self.headHeight;
+}
+
+- (UIView *)viewForPinSectionHeaderInPagerView:(JXPagerView *)pagerView {
+    return self.categoryView;
+}
+
+- (NSInteger)numberOfListsInPagerView:(JXPagerView *)pagerView {
+    //和categoryView的item数量一致
+    return self.titles.count;
+}
+
+- (id<JXPagerViewListViewDelegate>)pagerView:(JXPagerView *)pagerView initListAtIndex:(NSInteger)index {
+    ProgramCourseGroupBodyView *listView = [[ProgramCourseGroupBodyView alloc] init];
+    listView.naviController = self.navigationController;
+    listView.selectIndex = index;
+    listView.courseGroupId = self.courseGroupId;
+    listView.courseType = COURSE_GROUP_TYPE_ACCOMPANY;
+    if (self.enterStartTime) {
+        listView.enterStartTime = self.enterStartTime;
+        listView.quitEndTime = self.quitEndTime;
+    }
+    [self.listViewArray replaceObjectAtIndex:index withObject:listView];
+    self.listViewArray[index] = listView;
+    [listView beginFirstRefresh];
+    return listView;
+}
+
+- (void)pagerView:(JXPagerView *)pagerView mainTableViewDidScroll:(UIScrollView *)scrollView {
     
+    NSLog(@"sscroll content offset y %f", scrollView.contentOffset.y);
+    CGFloat height = self.topViewHeight;
+    CGFloat space = scrollView.contentOffset.y;
+    if (space > 100) {
+        CGFloat rate = (space - 100) / (height - 100);
+        NSLog(@"rate ---------%f",rate);
+        self.navView.backgroundColor = HexRGBAlpha(0xffffff, rate);
+        if (rate == 1) {
+            self.bgImageView.hidden = YES;
+        }
+        else {
+            self.bgImageView.hidden = NO;
+        }
+    }
+    else {
+        self.navView.backgroundColor = [UIColor clearColor];
+        self.bgImageView.hidden = NO;
+    }
 }
 
-- (void)openSettingView {
-    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
+- (void)dealloc {
+   [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 
 #pragma mark ----- lazying
-- (OnlineClassManager *)classManager {
-    if (!_classManager) {
-        _classManager = [[OnlineClassManager alloc] init];
+
+- (CourseGroupNavView *)navView {
+    if (!_navView) {
+        _navView = [CourseGroupNavView sharedInstance];
+        MJWeakSelf;
+        [_navView configWithNavTitle:@"趣纠课详情" callback:^{
+            [weakSelf backAction];
+        }];
+    }
+    return _navView;
+}
+- (NSMutableArray *)listViewArray {
+    if (!_listViewArray) {
+        _listViewArray = [NSMutableArray arrayWithArray:@[@""]];
     }
-    return _classManager;
+    return _listViewArray;
 }
+- (ProgramCourseInfoView *)infoView {
+    if (!_infoView) {
+        _infoView  =[ProgramCourseInfoView sharedInstance];
+    }
+    return _infoView;
+}
+
 /*
 #pragma mark - Navigation
 

+ 0 - 2
KulexiuForStudent/KulexiuForStudent/Module/Course/AccompanyCourse/Controller/AccompanyDetailViewController.h

@@ -15,8 +15,6 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property (nonatomic, strong) NSString *courseGroupId;
 
-@property (nonatomic, strong) NSString *teacherId;
-
 @property (nonatomic, assign) NSInteger joinRoomBeforeTime; // 上课开始时间
 
 @property (nonatomic, assign) NSInteger quitRomeEndTime;    // 下课截止时间

+ 0 - 2
KulexiuForStudent/KulexiuForStudent/Module/Course/Controller/CourseViewController.m

@@ -470,7 +470,6 @@
     AccompanyDetailViewController *detailVC = [[AccompanyDetailViewController alloc] init];
     detailVC.courseId = source.courseId;
     detailVC.courseGroupId = source.courseGoupId;
-    detailVC.teacherId = source.userId;
     [self.navigationController pushViewController:detailVC animated:YES];
 }
 
@@ -478,7 +477,6 @@
     MusicRoomDetailViewController *detailVC = [[MusicRoomDetailViewController alloc] init];
     detailVC.courseId = source.courseId;
     detailVC.courseGroupId = source.courseGoupId;
-    detailVC.teacherId = source.userId;
     [self.navigationController pushViewController:detailVC animated:YES];
 }
 

+ 0 - 2
KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/Controller/MusicRoomDetailViewController.h

@@ -17,8 +17,6 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property (nonatomic, strong) NSString *courseGroupId;
 
-@property (nonatomic, strong) NSString *teacherId;
-
 @property (nonatomic, assign) NSInteger joinRoomBeforeTime; // 上课开始时间
 
 @property (nonatomic, assign) NSInteger quitRomeEndTime;    // 下课截止时间

+ 7 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/Controller/MusicRoomGroupViewController.h

@@ -6,6 +6,9 @@
 //
 
 #import "KSBaseViewController.h"
+#import "JXCategoryView.h"
+#import "JXPagerView.h"
+
 
 NS_ASSUME_NONNULL_BEGIN
 // 琴房课--课程组
@@ -13,6 +16,10 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property (nonatomic, strong) NSString *courseGroupId; // 课程组ID
 
+@property (nonatomic, strong) JXPagerView *pagerView;
+@property (nonatomic, strong, readonly) JXCategoryTitleView *categoryView;
+@property (nonatomic, strong) NSArray <NSString *> *titles;
+
 @end
 
 NS_ASSUME_NONNULL_END

+ 219 - 68
KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/Controller/MusicRoomGroupViewController.m

@@ -6,26 +6,35 @@
 //
 
 #import "MusicRoomGroupViewController.h"
-#import <RecordCheckManager.h>
-#import "KSPremissionAlert.h"
-#import "OnlineClassManager.h"
-#import "HomeworkDetailModel.h"
+#import "CourseGroupNavView.h"
+#import "JXCategoryView.h"
+#import "JXPagerListRefreshView.h"
+#import "ProgramCourseGroupBodyView.h"
+#import "CourseGroupDetailModel.h"
+#import "MusicRoomGroupHeadView.h"
+#import "MusicRoomStudentPageView.h"
 
+@interface MusicRoomGroupViewController ()<JXPagerViewDelegate,JXCategoryViewDelegate,JXPagerMainTableViewGestureDelegate>
 
-@interface MusicRoomGroupViewController ()
+@property (nonatomic, strong) NSMutableArray *listViewArray;
 
-@property (nonatomic, strong) NSString *courseId;
+@property (nonatomic, strong) CourseGroupNavView *navView;
 
-@property (nonatomic, strong) NSString *studentId;
+@property (nonatomic, assign) NSInteger headHeight;
 
-@property (nonatomic, assign) NSInteger joinRoomBeforeTime; // 上课开始时间
+@property (nonatomic, assign) NSInteger topViewHeight;
 
-@property (nonatomic, assign) NSInteger quitRomeEndTime;    // 下课截止时间
+@property (nonatomic, strong) MusicRoomGroupHeadView *infoView;
 
-@property (nonatomic, strong) HomeworkDetailModel *homeworkModel;
+@property (nonatomic, assign) BOOL isFirstLoad;
 
-@property (nonatomic, strong) OnlineClassManager *classManager;
+@property (nonatomic, strong) CourseGroupDetailModel *detailModel;
 
+@property (nonatomic, assign) NSInteger enterStartTime;  // 开课前可进入时间配置(分钟)
+
+@property (nonatomic, assign) NSInteger quitEndTime;    // 结束后退出时间配置(分钟)
+
+@property (nonatomic, strong) UIImageView *bgImageView;
 
 @end
 
@@ -34,84 +43,226 @@
 - (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
+    self.ks_prefersNavigationBarHidden = YES;
+    [self configUI];
+    self.isFirstLoad = YES;
 }
 
-- (void)tryJoinRoom {
+- (void)configUI {
+    [self.scrollView removeFromSuperview];
+    UIImage *bgImage = [UIImage imageNamed:@"courseDetail_bg"];
+    NSInteger height = (NSInteger)(bgImage.size.height / bgImage.size.width * KPortraitWidth);
+    self.bgImageView = [[UIImageView alloc] initWithImage:bgImage];
+    self.bgImageView.frame = CGRectMake(0, 0, KPortraitWidth, height);
+    [self.view addSubview:self.bgImageView];
     
-    NSDateFormatter *dateFormatter = [NSObject getDateformatter];
-    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
-    NSDate *beginDate = [dateFormatter dateFromString:self.homeworkModel.startTime];
-    NSDate *endDate = [dateFormatter dateFromString:self.homeworkModel.endTime];
-    NSDate *currentDate = [NSDate date];
-    NSTimeInterval beginTimeInterval = [beginDate timeIntervalSinceDate:currentDate];
-    NSTimeInterval endTimeInterval = [currentDate timeIntervalSinceDate:endDate];
-    if (beginTimeInterval <= self.joinRoomBeforeTime * 60 && endTimeInterval < 0) {
-        [self joinClassRoom];
-    }
-    else if (endTimeInterval > 0) {
-        [LOADING_MANAGER MBShowAUTOHidingInWindow:@"该课程已结束"];
+    [self.view addSubview:self.navView];
+    CGFloat navHeight = [CourseGroupNavView getViewHeight];
+    [self.navView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.top.mas_equalTo(self.view);
+        make.height.mas_equalTo(navHeight);
+    }];
+    self.headHeight = 44.0f;
+    self.titles = @[@"课程列表",@"上课学生"];
+    [self configCategoryView:navHeight];
+}
+
+- (void)requestCourseInfo {
+    
+    [KSNetworkingManager getCourseGroupDetailRequest:KS_POST courseGroupId:self.courseGroupId success:^(NSDictionary * _Nonnull dic) {
+        if ([dic ks_integerValueForKey:@"code"] == 200 && [dic ks_boolValueForKey:@"status"]) {
+            NSDictionary *parm = [dic ks_dictionaryValueForKey:@"data"];
+            self.detailModel = [[CourseGroupDetailModel alloc] initWithDictionary:parm];
+            // 配置信息
+            NSDictionary *config = [[dic ks_dictionaryValueForKey:@"data"] ks_dictionaryValueForKey:@"sysConfig"];
+            self.enterStartTime = [config ks_integerValueForKey:@"pianoStartTime"];
+            self.quitEndTime = [config ks_integerValueForKey:@"pianoEndTime"];
+        }
+        else {
+            [LOADING_MANAGER MBShowAUTOHidingInWindow:MESSAGEKEY];
+        }
+        [self refreshHeadView];
+    } faliure:^(NSError * _Nonnull error) {
+        [self refreshHeadView];
+    }];
+}
+
+- (void)refreshHeadView {
+    [self refreshListPage];
+    self.topViewHeight = [MusicRoomGroupHeadView getViewHeightWithPlanString:self.detailModel.coursePlan];
+    [self.infoView configWithSource:self.detailModel];
+    [self.pagerView reloadData];
+}
+
+- (void)configCategoryView:(NSInteger)topHeight {
+    
+    _categoryView = [[JXCategoryTitleView alloc] initWithFrame:CGRectMake(0, topHeight, KPortraitWidth, self.headHeight)];
+    self.categoryView.titles = self.titles;
+    self.categoryView.delegate = self;
+    self.categoryView.titleFont = [UIFont systemFontOfSize:16.0f];
+    self.categoryView.titleSelectedFont = [UIFont systemFontOfSize:16.0f weight:UIFontWeightSemibold];
+    self.categoryView.titleSelectedColor = HexRGB(0x333333);
+    self.categoryView.titleColor = HexRGB(0x666666);
+    self.categoryView.titleColorGradientEnabled = YES;
+    
+    JXCategoryIndicatorLineView *lineView = [[JXCategoryIndicatorLineView alloc] init];
+    lineView.indicatorColor = THEMECOLOR;
+    lineView.indicatorWidth = 16;
+    lineView.indicatorHeight = 4;
+    self.categoryView.indicators = @[lineView];
+    
+    _pagerView = [self preferredPagingView];
+    self.pagerView.frame = CGRectMake(0, topHeight, KPortraitWidth, KPortraitHeight - topHeight);
+    self.pagerView.backgroundColor = [UIColor clearColor];
+    self.pagerView.mainTableView.backgroundColor = [UIColor clearColor];
+    self.pagerView.listContainerView.backgroundColor = [UIColor clearColor];
+    self.pagerView.mainTableView.gestureDelegate = self;
+    self.categoryView.listContainer = (id<JXCategoryViewListContainer>)self.pagerView.listContainerView;
+    self.pagerView.listContainerView.listCellBackgroundColor = [UIColor clearColor];
+    [self.view addSubview:self.pagerView];
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    self.navigationController.interactivePopGestureRecognizer.enabled = (self.categoryView.selectedIndex == 0);
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+    [super viewWillAppear:animated];
+    self.navigationController.interactivePopGestureRecognizer.enabled = YES;
+    // 刷新课程详情
+    [self requestCourseInfo];
+    
+    if (self.isFirstLoad == NO) {
+        [self refreshListPage];
     }
-    else {
-        NSString *tipsString = [NSString stringWithFormat:@"课程还未开始,请在上课前%zd分钟进入", self.joinRoomBeforeTime];
-        [LOADING_MANAGER MBShowAUTOHidingInWindow:tipsString];
+    self.isFirstLoad = NO;
+    
+}
+
+- (void)refreshListPage {
+    id value = self.listViewArray[0];
+    if ([value isKindOfClass:[ProgramCourseGroupBodyView class]]) {
+        ProgramCourseGroupBodyView *listView = (ProgramCourseGroupBodyView *)value;
+        listView.enterStartTime = self.enterStartTime;
+        listView.quitEndTime = self.quitEndTime;
+        [listView beginFirstRefresh];
     }
 }
 
-// 加入房间
-- (void)joinClassRoom {
+- (JXPagerView *)preferredPagingView {
+    return [[JXPagerListRefreshView alloc] initWithDelegate:self];
+}
+
+#pragma mark - JXPagerViewDelegate
+
+- (UIView *)tableHeaderViewInPagerView:(JXPagerView *)pagerView {
+    return self.infoView;
+}
+
+- (NSUInteger)tableHeaderViewHeightInPagerView:(JXPagerView *)pagerView {
+    return self.topViewHeight;
+}
+
+- (NSUInteger)heightForPinSectionHeaderInPagerView:(JXPagerView *)pagerView {
+    return self.headHeight;
+}
+
+- (UIView *)viewForPinSectionHeaderInPagerView:(JXPagerView *)pagerView {
+    return self.categoryView;
+}
+
+- (NSInteger)numberOfListsInPagerView:(JXPagerView *)pagerView {
+    //和categoryView的item数量一致
+    return self.titles.count;
+}
+
+- (id<JXPagerViewListViewDelegate>)pagerView:(JXPagerView *)pagerView initListAtIndex:(NSInteger)index {
+    if (index == 0) {
+        ProgramCourseGroupBodyView *listView = [[ProgramCourseGroupBodyView alloc] init];
+        listView.naviController = self.navigationController;
+        listView.selectIndex = index;
+        listView.courseGroupId = self.courseGroupId;
+        listView.courseType = COURSE_GROUP_TYPE_MUSICROOM;
+        [self.listViewArray replaceObjectAtIndex:index withObject:listView];
+        self.listViewArray[index] = listView;
+        [listView beginFirstRefresh];
+        return listView;
+    }
+    else {
+        MusicRoomStudentPageView *listView = [[MusicRoomStudentPageView alloc] init];
+        listView.naviController = self.navigationController;
+        listView.selectIndex = index;
+        listView.courseGroupId = self.courseGroupId;
+        [self.listViewArray replaceObjectAtIndex:index withObject:listView];
+        self.listViewArray[index] = listView;
+        [listView beginFirstRefresh];
+        return listView;
+    }
     
-    // 加入房间前判断摄像头和麦克风逻辑
-    [RecordCheckManager checkCameraPremissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        [self afterCheckCameraCheckMic:type];
-    }];
 }
 
-- (void)afterCheckCameraCheckMic:(PREMISSIONTYPE)cameraType {
-    [RecordCheckManager checkMicPermissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        if (type == PREMISSIONTYPE_YES && cameraType == PREMISSIONTYPE_YES) {
-            // 判断是否进行课前检测
-            [self.classManager joinRoomWithId:self.courseId subjectName:self.homeworkModel.subjectName classEndTime:self.homeworkModel.endTime inViewController:self];
+- (void)pagerView:(JXPagerView *)pagerView mainTableViewDidScroll:(UIScrollView *)scrollView {
+    
+    NSLog(@"sscroll content offset y %f", scrollView.contentOffset.y);
+    CGFloat height = self.topViewHeight;
+    CGFloat space = scrollView.contentOffset.y;
+    if (space > 100) {
+        CGFloat rate = (space - 100) / (height - 100);
+        NSLog(@"rate ---------%f",rate);
+        self.navView.backgroundColor = HexRGBAlpha(0xffffff, rate);
+        if (rate == 1) {
+            self.bgImageView.hidden = YES;
         }
         else {
-            NSString *content = @"";
-            CHECKDEVICETYPE checkType = CHECKDEVICETYPE_BOTH;
-            if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_NO) {
-                content = @"请开启相机和麦克风访问权限";
-                checkType = CHECKDEVICETYPE_BOTH;
-            }
-            else if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_YES) {
-                content =  @"请开启相机访问权限";
-                checkType = CHECKDEVICETYPE_CAMREA;
-            }
-            else if (cameraType == PREMISSIONTYPE_YES && type == PREMISSIONTYPE_NO) {
-                content = @"请开启麦克风访问权限";
-                checkType = CHECKDEVICETYPE_MIC;
-            }
-            [self showAlertWithMessage:content type:checkType];
+            self.bgImageView.hidden = NO;
         }
-    }];
-    
+    }
+    else {
+        self.navView.backgroundColor = [UIColor clearColor];
+        self.bgImageView.hidden = NO;
+    }
 }
 
-- (void)showAlertWithMessage:(NSString *)message type:(CHECKDEVICETYPE)deviceType {
-    [KSPremissionAlert shareInstanceDisplayImage:deviceType message:message showInView:self.view cancel:^{
-        
-    } confirm:^{
-        [self openSettingView];
-    }];
-    
+#pragma mark - JXPagerMainTableViewGestureDelegate
+
+- (BOOL)mainTableViewGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
+    //禁止categoryView左右滑动的时候,上下和左右都可以滚动
+    if (otherGestureRecognizer == self.categoryView.collectionView.panGestureRecognizer) {
+        return NO;
+    }
+    return [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]];
 }
 
-- (void)openSettingView {
-    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
+
+
+- (void)dealloc {
+   [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 
 #pragma mark ----- lazying
-- (OnlineClassManager *)classManager {
-    if (!_classManager) {
-        _classManager = [[OnlineClassManager alloc] init];
+
+- (CourseGroupNavView *)navView {
+    if (!_navView) {
+        _navView = [CourseGroupNavView sharedInstance];
+        MJWeakSelf;
+        [_navView configWithNavTitle:@"琴房课详情" callback:^{
+            [weakSelf backAction];
+        }];
+    }
+    return _navView;
+}
+- (NSMutableArray *)listViewArray {
+    if (!_listViewArray) {
+        _listViewArray = [NSMutableArray arrayWithArray:@[@"",@""]];
+    }
+    return _listViewArray;
+}
+- (MusicRoomGroupHeadView *)infoView {
+    if (!_infoView) {
+        _infoView  =[MusicRoomGroupHeadView sharedInstance];
     }
-    return _classManager;
+    return _infoView;
 }
 /*
 #pragma mark - Navigation

+ 19 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomCourseListCell.h

@@ -0,0 +1,19 @@
+//
+//  MusicRoomCourseListCell.h
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/25.
+//
+
+#import <UIKit/UIKit.h>
+#import "ProgramCourseListCell.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MusicRoomCourseListCell : UITableViewCell
+
+- (void)configWithSource:(GroupCourseListModel *)model beforeTime:(NSInteger)beforeTime callback:(GroupListModelCallback)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 135 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomCourseListCell.m

@@ -0,0 +1,135 @@
+//
+//  MusicRoomCourseListCell.m
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/25.
+//
+
+#import "MusicRoomCourseListCell.h"
+
+@interface MusicRoomCourseListCell ()
+
+@property (weak, nonatomic) IBOutlet UIButton *enterButton;
+
+@property (weak, nonatomic) IBOutlet UILabel *timeLabel;
+
+@property (weak, nonatomic) IBOutlet UILabel *statusLabel;
+
+@property (weak, nonatomic) IBOutlet UILabel *courseGroupName;
+
+@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
+
+@property (weak, nonatomic) IBOutlet UIImageView *arriveStatus;
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *arriveStatusWidth;
+
+@property (weak, nonatomic) IBOutlet UIView *subjectView;
+@property (weak, nonatomic) IBOutlet UILabel *subjectLabel;
+
+@property (nonatomic, strong) GroupCourseListModel *sourceModel;
+
+@property (nonatomic, copy) GroupListModelCallback callback;
+
+@property (nonatomic, assign) NSInteger beforeTime;
+
+@end
+
+@implementation MusicRoomCourseListCell
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    // Initialization code
+    self.selectionStyle = UITableViewCellSelectionStyleNone;
+}
+
+- (void)configWithSource:(GroupCourseListModel *)model beforeTime:(NSInteger)beforeTime callback:(GroupListModelCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+    self.sourceModel = model;
+    NSDateFormatter *formatter = [NSObject getDateformatter];
+    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
+    NSDate *startDate = [formatter dateFromString:model.startTime];
+    NSDate *endDate = [formatter dateFromString:model.endTime];
+    [formatter setDateFormat:@"yyyy-MM-dd HH:mm"];
+    NSString *lessonBegin = [formatter stringFromDate:startDate];
+    [formatter setDateFormat:@"HH:mm"];
+    NSString *lessonEnd = [formatter stringFromDate:endDate];
+    self.timeLabel.text = [NSString stringWithFormat:@"%@~%@",[NSString returnNoNullStringWithString:lessonBegin],[NSString returnNoNullStringWithString:lessonEnd]];
+    self.courseGroupName.text = [NSString returnNoNullStringWithString:model.courseGroupName];
+    CourseStudentVos *studentInfo = [model.courseStudentVos lastObject];
+    self.nameLabel.text = [NSString returnNoNullStringWithString:studentInfo.userName];
+    self.subjectLabel.text = [NSString returnNoNullStringWithString:model.subjectName];
+    if ([NSString isEmptyString:model.subjectName]) {
+        self.subjectView.hidden = YES;
+    }
+    else {
+        self.subjectView.hidden = NO;
+    }
+    [self evaluateWithSource:model];
+}
+
+- (void)evaluateWithSource:(GroupCourseListModel *)model {
+    NSString *imageName = model.attendanceStatus ? @"course_attendence" : @"course_noArrive";
+    if ([model.status isEqualToString:@"ING"]) {
+        self.statusLabel.text = @"进行中";
+        self.statusLabel.textColor = THEMECOLOR;
+        self.arriveStatus.hidden = YES;
+        self.arriveStatusWidth.constant = 0.0f;
+        self.enterButton.hidden = NO;
+        self.enterButton.userInteractionEnabled = YES;
+    }
+    else if ([model.status isEqualToString:@"NOT_START"]) {
+        self.statusLabel.text = @"未开始";
+        self.statusLabel.textColor = HexRGB(0xFF802C);
+        self.arriveStatus.hidden = YES;
+        self.arriveStatusWidth.constant = 0.0f;
+        
+        // 判断按钮显示调课还是进入教室
+        NSDate *currentDate = [NSDate date];
+        NSDate *beginDate = [self getCourseBeginDate:model.startTime];
+        NSTimeInterval beginTimeInterval = [beginDate timeIntervalSinceDate:currentDate];
+        BOOL canChange = NO;
+        if (beginTimeInterval > self.beforeTime * 60) {
+            canChange = YES;
+        }
+        if (canChange) {
+            self.enterButton.hidden = YES;
+            self.enterButton.userInteractionEnabled = NO;
+        }
+        else {
+            self.enterButton.hidden = NO;
+            self.enterButton.userInteractionEnabled = YES;
+        }
+    }
+    else if ([model.status isEqualToString:@"COMPLETE"]) {
+        self.statusLabel.text = @"已结束";
+        self.statusLabel.textColor = HexRGB(0x999999);
+        self.arriveStatus.hidden = NO;
+        self.arriveStatusWidth.constant = 36.0f;
+        self.enterButton.hidden = YES;
+        self.enterButton.userInteractionEnabled = NO;
+    }
+    [self.arriveStatus setImage:[UIImage imageNamed:imageName]];
+}
+
+
+
+- (IBAction)enterClassRoom:(id)sender {
+    if (self.callback) {
+        self.callback(self.sourceModel);
+    }
+}
+
+- (NSDate *)getCourseBeginDate:(NSString *)beginTime {
+    NSDateFormatter *dateFormatter = [NSObject getDateformatter];
+    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
+    NSDate *beginDate = [dateFormatter dateFromString:beginTime];
+    return beginDate;
+}
+- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
+    [super setSelected:selected animated:animated];
+
+    // Configure the view for the selected state
+}
+
+@end

+ 202 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomCourseListCell.xib

@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="139" id="KGk-i7-Jjw" customClass="MusicRoomCourseListCell">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="123"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="123"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="UCx-jd-4fG">
+                        <rect key="frame" x="14" y="0.0" width="292" height="111"/>
+                        <subviews>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wSI-4q-PqD">
+                                <rect key="frame" x="10" y="46" width="272" height="1"/>
+                                <color key="backgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.94901960780000005" alpha="1" colorSpace="calibratedRGB"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="1" id="BEk-Zg-MOG"/>
+                                </constraints>
+                            </view>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" image="course_time" translatesAutoresizingMaskIntoConstraints="NO" id="2QC-c9-Pmm">
+                                <rect key="frame" x="12" y="15" width="16" height="16"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="16" id="Esz-zU-sRi"/>
+                                    <constraint firstAttribute="height" constant="16" id="KDp-1L-bfs"/>
+                                </constraints>
+                            </imageView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pFx-1U-bC2">
+                                <rect key="frame" x="35" y="14" width="0.0" height="18"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="18" id="m4H-2O-wjn"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                                <color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="进行中" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Hi3-os-toX">
+                                <rect key="frame" x="238" y="12" width="42" height="22"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="22" id="MfS-RY-uQp"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
+                                <color key="textColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="course_musicRoom" translatesAutoresizingMaskIntoConstraints="NO" id="Cfn-74-up3">
+                                <rect key="frame" x="12" y="59" width="40" height="40"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="40" id="5S7-Nw-2Df"/>
+                                    <constraint firstAttribute="height" constant="40" id="xpC-QY-5lU"/>
+                                </constraints>
+                            </imageView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sbq-HP-naE">
+                                <rect key="frame" x="62" y="58" width="0.0" height="21"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="21" id="ofX-6m-b9A"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                <color key="textColor" red="0.074509803920000006" green="0.078431372550000003" blue="0.08235294118" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" image="merge_next" translatesAutoresizingMaskIntoConstraints="NO" id="pqJ-ev-XlA">
+                                <rect key="frame" x="268" y="73" width="12" height="12"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="12" id="XkL-ks-Igl"/>
+                                    <constraint firstAttribute="width" constant="12" id="reb-TO-yHH"/>
+                                </constraints>
+                            </imageView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="i7t-Y9-RL8">
+                                <rect key="frame" x="62" y="83" width="0.0" height="17"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="17" id="mmW-vx-FKw"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                                <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <view contentMode="scaleToFill" horizontalCompressionResistancePriority="752" translatesAutoresizingMaskIntoConstraints="NO" id="tlE-FT-sOf">
+                                <rect key="frame" x="67" y="83" width="8" height="17"/>
+                                <subviews>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jCh-h5-1dV">
+                                        <rect key="frame" x="4" y="8.6666666666666714" width="0.0" height="0.0"/>
+                                        <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                                        <color key="textColor" red="1" green="0.54901960780000003" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                </subviews>
+                                <color key="backgroundColor" red="1" green="0.94509803920000002" blue="0.87058823529999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <constraints>
+                                    <constraint firstItem="jCh-h5-1dV" firstAttribute="leading" secondItem="tlE-FT-sOf" secondAttribute="leading" constant="4" id="H7D-CG-joD"/>
+                                    <constraint firstAttribute="height" constant="17" id="cgU-4C-gZV"/>
+                                    <constraint firstItem="jCh-h5-1dV" firstAttribute="centerY" secondItem="tlE-FT-sOf" secondAttribute="centerY" id="gGf-QI-BSs"/>
+                                    <constraint firstAttribute="trailing" secondItem="jCh-h5-1dV" secondAttribute="trailing" constant="4" id="z8p-5d-mGH"/>
+                                </constraints>
+                                <userDefinedRuntimeAttributes>
+                                    <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                        <real key="value" value="4"/>
+                                    </userDefinedRuntimeAttribute>
+                                </userDefinedRuntimeAttributes>
+                            </view>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="OUr-fR-uAC">
+                                <rect key="frame" x="67" y="59.666666666666657" width="36" height="18"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="18" id="KRv-2A-H0z"/>
+                                    <constraint firstAttribute="width" constant="36" id="h94-gF-Hxx"/>
+                                </constraints>
+                            </imageView>
+                            <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DLH-WQ-IQA">
+                                <rect key="frame" x="215" y="12" width="62" height="22"/>
+                                <color key="backgroundColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="62" id="TwZ-4I-UWI"/>
+                                    <constraint firstAttribute="height" constant="22" id="g5J-Eg-HIe"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <state key="normal" title="开始上课"/>
+                                <connections>
+                                    <action selector="enterClassRoom:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="1OP-dY-Yk7"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                        <constraints>
+                            <constraint firstItem="tlE-FT-sOf" firstAttribute="centerY" secondItem="i7t-Y9-RL8" secondAttribute="centerY" id="1Rl-6t-Ho9"/>
+                            <constraint firstAttribute="bottom" secondItem="pqJ-ev-XlA" secondAttribute="bottom" constant="26" id="26r-Q1-e2T"/>
+                            <constraint firstItem="i7t-Y9-RL8" firstAttribute="leading" secondItem="sbq-HP-naE" secondAttribute="leading" id="28i-bD-EFt"/>
+                            <constraint firstItem="2QC-c9-Pmm" firstAttribute="leading" secondItem="UCx-jd-4fG" secondAttribute="leading" constant="12" id="2Es-v7-Hme"/>
+                            <constraint firstItem="sbq-HP-naE" firstAttribute="leading" secondItem="Cfn-74-up3" secondAttribute="trailing" constant="10" id="56v-Wb-J1M"/>
+                            <constraint firstItem="Cfn-74-up3" firstAttribute="leading" secondItem="UCx-jd-4fG" secondAttribute="leading" constant="12" id="A7Y-2f-Wxs"/>
+                            <constraint firstAttribute="trailing" secondItem="DLH-WQ-IQA" secondAttribute="trailing" constant="15" id="Bbi-Hk-Zf6"/>
+                            <constraint firstItem="sbq-HP-naE" firstAttribute="top" secondItem="Cfn-74-up3" secondAttribute="top" constant="-1" id="MOU-7t-oGQ"/>
+                            <constraint firstItem="OUr-fR-uAC" firstAttribute="leading" secondItem="sbq-HP-naE" secondAttribute="trailing" constant="5" id="PSn-pS-jsz"/>
+                            <constraint firstItem="pFx-1U-bC2" firstAttribute="leading" secondItem="2QC-c9-Pmm" secondAttribute="trailing" constant="7" id="Rnv-VC-xcw"/>
+                            <constraint firstItem="tlE-FT-sOf" firstAttribute="leading" secondItem="i7t-Y9-RL8" secondAttribute="trailing" constant="5" id="Six-5U-sPb"/>
+                            <constraint firstAttribute="trailing" secondItem="pqJ-ev-XlA" secondAttribute="trailing" constant="12" id="SrF-k1-2Kr"/>
+                            <constraint firstItem="Hi3-os-toX" firstAttribute="centerY" secondItem="pFx-1U-bC2" secondAttribute="centerY" id="T33-VN-QLp"/>
+                            <constraint firstItem="OUr-fR-uAC" firstAttribute="centerY" secondItem="sbq-HP-naE" secondAttribute="centerY" id="WDq-Zc-A8U"/>
+                            <constraint firstItem="2QC-c9-Pmm" firstAttribute="top" secondItem="UCx-jd-4fG" secondAttribute="top" constant="15" id="WbI-jt-uMO"/>
+                            <constraint firstItem="Cfn-74-up3" firstAttribute="top" secondItem="wSI-4q-PqD" secondAttribute="bottom" constant="12" id="XoF-TM-IIo"/>
+                            <constraint firstItem="pFx-1U-bC2" firstAttribute="centerY" secondItem="2QC-c9-Pmm" secondAttribute="centerY" id="YvQ-zv-Q5K"/>
+                            <constraint firstAttribute="trailing" secondItem="wSI-4q-PqD" secondAttribute="trailing" constant="10" id="aoN-oW-rCe"/>
+                            <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="tlE-FT-sOf" secondAttribute="trailing" constant="12" id="be7-Y7-37N"/>
+                            <constraint firstItem="wSI-4q-PqD" firstAttribute="top" secondItem="2QC-c9-Pmm" secondAttribute="bottom" constant="15" id="dad-ZX-ymX"/>
+                            <constraint firstItem="pqJ-ev-XlA" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="tlE-FT-sOf" secondAttribute="trailing" constant="5" id="eJ7-KQ-0Rj"/>
+                            <constraint firstItem="DLH-WQ-IQA" firstAttribute="top" secondItem="UCx-jd-4fG" secondAttribute="top" constant="12" id="gA7-G3-AY4"/>
+                            <constraint firstItem="wSI-4q-PqD" firstAttribute="leading" secondItem="UCx-jd-4fG" secondAttribute="leading" constant="10" id="gc5-t3-KUr"/>
+                            <constraint firstItem="DLH-WQ-IQA" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="pFx-1U-bC2" secondAttribute="trailing" constant="5" id="nme-SH-evT"/>
+                            <constraint firstAttribute="trailing" secondItem="Hi3-os-toX" secondAttribute="trailing" constant="12" id="t87-j4-eGi"/>
+                            <constraint firstAttribute="bottom" secondItem="Cfn-74-up3" secondAttribute="bottom" constant="12" id="tJn-Iq-51U"/>
+                            <constraint firstItem="pqJ-ev-XlA" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="OUr-fR-uAC" secondAttribute="trailing" constant="5" id="xdj-d5-Aop"/>
+                            <constraint firstItem="i7t-Y9-RL8" firstAttribute="top" secondItem="sbq-HP-naE" secondAttribute="bottom" constant="4" id="zE3-A5-S75"/>
+                        </constraints>
+                        <userDefinedRuntimeAttributes>
+                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                <real key="value" value="10"/>
+                            </userDefinedRuntimeAttribute>
+                        </userDefinedRuntimeAttributes>
+                    </view>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="UCx-jd-4fG" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="8gh-fF-uJW"/>
+                    <constraint firstAttribute="trailing" secondItem="UCx-jd-4fG" secondAttribute="trailing" constant="14" id="iAE-3F-UUb"/>
+                    <constraint firstAttribute="bottom" secondItem="UCx-jd-4fG" secondAttribute="bottom" constant="12" id="llf-js-h9S"/>
+                    <constraint firstItem="UCx-jd-4fG" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="14" id="mHn-g6-TAX"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="aW0-zy-SZf"/>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <connections>
+                <outlet property="arriveStatus" destination="OUr-fR-uAC" id="K75-NM-Vzl"/>
+                <outlet property="arriveStatusWidth" destination="h94-gF-Hxx" id="Mkl-jT-o8q"/>
+                <outlet property="courseGroupName" destination="sbq-HP-naE" id="5bC-oh-W68"/>
+                <outlet property="enterButton" destination="DLH-WQ-IQA" id="P61-7Z-K2S"/>
+                <outlet property="nameLabel" destination="i7t-Y9-RL8" id="Btv-eF-ts8"/>
+                <outlet property="statusLabel" destination="Hi3-os-toX" id="9Zi-HP-fRg"/>
+                <outlet property="subjectLabel" destination="jCh-h5-1dV" id="ynG-tc-A9m"/>
+                <outlet property="subjectView" destination="tlE-FT-sOf" id="PQa-L1-WKc"/>
+                <outlet property="timeLabel" destination="pFx-1U-bC2" id="xBC-G1-PeH"/>
+            </connections>
+            <point key="canvasLocation" x="51.908396946564885" y="53.169014084507047"/>
+        </tableViewCell>
+    </objects>
+    <resources>
+        <image name="course_musicRoom" width="40" height="40"/>
+        <image name="course_time" width="16" height="16"/>
+        <image name="merge_next" width="14" height="14"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 25 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomGroupHeadView.h

@@ -0,0 +1,25 @@
+//
+//  MusicRoomGroupHeadView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2024/11/25.
+//
+
+#import <UIKit/UIKit.h>
+#import "ProgramCourseInfoView.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MusicRoomGroupHeadView : UIView
+
++ (instancetype)sharedInstance;
+
+- (void)configWithSource:(CourseGroupDetailModel *)model;
+
+- (void)programActionCallback:(ProgramPlanAction)callback;
+
++ (CGFloat)getViewHeightWithPlanString:(NSString *)planString;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 163 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomGroupHeadView.m

@@ -0,0 +1,163 @@
+//
+//  MusicRoomGroupHeadView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2024/11/25.
+//
+
+#import "MusicRoomGroupHeadView.h"
+#import "CourseGroupPlanAddView.h"
+#import "CourseGroupPlanDescView.h"
+
+@interface MusicRoomGroupHeadView ()
+
+@property (weak, nonatomic) IBOutlet UIView *bgView;
+
+@property (weak, nonatomic) IBOutlet UILabel *courseGroupTitle;
+
+@property (weak, nonatomic) IBOutlet UILabel *statusLabel;
+
+@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
+
+@property (weak, nonatomic) IBOutlet UIView *subjectView;
+
+@property (weak, nonatomic) IBOutlet UILabel *subjectLabel;
+
+@property (weak, nonatomic) IBOutlet UILabel *courseDesc;
+
+@property (weak, nonatomic) IBOutlet UIView *planContainer;
+
+@property (nonatomic, strong) CourseGroupPlanAddView *addView;
+
+@property (nonatomic, strong) CourseGroupPlanDescView *planDescView;
+
+@property (nonatomic, copy) ProgramPlanAction callback;
+
+
+@end
+
+@implementation MusicRoomGroupHeadView
+
++ (instancetype)sharedInstance {
+    MusicRoomGroupHeadView *view = [[[NSBundle mainBundle] loadNibNamed:@"MusicRoomGroupHeadView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+
+- (void)configWithSource:(CourseGroupDetailModel *)model {
+    [self.planContainer removeAllSubViews];
+    if ([NSString isEmptyString:model.coursePlan]) {
+        [self.planContainer addSubview:self.addView];
+        CGFloat addViewHeight = [CourseGroupPlanAddView getViewHeight];
+        [self.addView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.top.bottom.right.left.mas_equalTo(self.planContainer);
+            make.height.mas_equalTo(addViewHeight);
+        }];
+        self.bgView.backgroundColor = [UIColor whiteColor];
+    }
+    else {
+        CGFloat planHeight = [CourseGroupPlanDescView getViewHeight:model.coursePlan];
+        self.bgView.backgroundColor = [UIColor clearColor];
+        [self.planContainer addSubview:self.planDescView];
+        [self.planDescView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.right.top.bottom.mas_equalTo(self.planContainer);
+            make.height.mas_equalTo(planHeight);
+        }];
+        [self.planDescView configWithPlanDesc:model.coursePlan];
+    }
+    self.courseGroupTitle.text = [NSString returnNoNullStringWithString:model.courseGroupName];
+    /// 状态
+    [self evaluateGroupStatus:model.status];
+    
+    self.nameLabel.text = [NSString returnNoNullStringWithString:model.studentName];
+    self.subjectLabel.text = [NSString returnNoNullStringWithString:model.subjectName];
+    if ([NSString isEmptyString:model.subjectName]) {
+        self.subjectView.hidden = YES;
+    }
+    else {
+        self.subjectView.hidden = NO;
+    }
+    // 课程描述
+    NSMutableAttributedString *attrs = [[NSMutableAttributedString alloc] initWithString:@"已上课时 " attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0f weight:UIFontWeightRegular],NSForegroundColorAttributeName:HexRGB(0x999999)}];
+    NSAttributedString *finishNumAttr = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%.0f", model.completeCourseNum] attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0f weight:UIFontWeightSemibold],NSForegroundColorAttributeName:THEMECOLOR}];
+    [attrs appendAttributedString:finishNumAttr];
+    
+    NSAttributedString *totalDescAttr = [[NSAttributedString alloc] initWithString:@" /总课时 " attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0f weight:UIFontWeightRegular],NSForegroundColorAttributeName:HexRGB(0x999999)}];
+    [attrs appendAttributedString:totalDescAttr];
+    NSAttributedString *totalNumAttr = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%.0f", model.courseNum] attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0f weight:UIFontWeightSemibold],NSForegroundColorAttributeName:THEMECOLOR}];
+    [attrs appendAttributedString:totalNumAttr];
+    self.courseDesc.attributedText = attrs;
+}
+
+- (void)evaluateGroupStatus:(NSString *)status {
+    if ([status isEqualToString:@"ING"]) {
+        self.statusLabel.text = @"已开课";
+        self.statusLabel.textColor = THEMECOLOR;
+    }
+    else if ([status isEqualToString:@"NOT_START"]) {
+        self.statusLabel.text = @"未开课";
+        self.statusLabel.textColor = HexRGB(0xFF802C);
+    }
+    else if ([status isEqualToString:@"COMPLETE"]) {
+        self.statusLabel.text = @"已结课";
+        self.statusLabel.textColor = HexRGB(0x999999);
+    }
+    else if ([status isEqualToString:@"CANCEL"]) {
+        self.statusLabel.text = @"已取消";
+        self.statusLabel.textColor = HexRGB(0x999999);
+    }
+}
+
+
+- (void)programActionCallback:(ProgramPlanAction)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+    
+}
+
++ (CGFloat)getViewHeightWithPlanString:(NSString *)planString {
+    if ([NSString isEmptyString:planString]) {
+        return 106.0f + [CourseGroupPlanAddView getViewHeight];
+    }
+    else {
+        return 106.0f + [CourseGroupPlanDescView getViewHeight:planString];
+    }
+}
+
+- (CourseGroupPlanAddView *)addView {
+    if (!_addView) {
+        _addView = [CourseGroupPlanAddView sharedInstance];
+        MJWeakSelf;
+        [_addView addPlanAction:^{
+            [weakSelf showPlanView];
+        }];
+    }
+    return _addView;
+}
+
+- (CourseGroupPlanDescView *)planDescView {
+    if (!_planDescView) {
+        _planDescView = [CourseGroupPlanDescView sharedInstance];
+        MJWeakSelf;
+        [_planDescView modifyPlanAction:^{
+            [weakSelf showPlanView];
+        }];
+    }
+    return _planDescView;
+}
+
+- (void)showPlanView {
+    if (self.callback) {
+        self.callback();
+    }
+}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 161 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomGroupHeadView.xib

@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="MusicRoomGroupHeadView">
+            <rect key="frame" x="0.0" y="0.0" width="393" height="165"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wrx-dE-SgG">
+                    <rect key="frame" x="14" y="12" width="365" height="153"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0tb-gl-GAm">
+                    <rect key="frame" x="14" y="106" width="365" height="59"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3kP-Ps-23r">
+                    <rect key="frame" x="14" y="12" width="365" height="94"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vQS-vg-LHr">
+                            <rect key="frame" x="12" y="12" width="0.0" height="22"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="22" id="Obf-xa-0v4"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
+                            <color key="textColor" red="0.074509803920000006" green="0.078431372550000003" blue="0.08235294118" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GSb-pn-BPN">
+                            <rect key="frame" x="353" y="12" width="0.0" height="0.0"/>
+                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
+                            <color key="textColor" red="1" green="0.50196078430000002" blue="0.17254901959999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" image="course_musicRoom" translatesAutoresizingMaskIntoConstraints="NO" id="Npd-J5-fnH">
+                            <rect key="frame" x="12" y="42" width="40" height="40"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="40" id="M3f-FW-yji"/>
+                                <constraint firstAttribute="width" constant="40" id="rXG-3a-sd7"/>
+                            </constraints>
+                        </imageView>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iST-OW-7X3">
+                            <rect key="frame" x="62" y="43" width="0.0" height="17"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="17" id="Ycj-jK-sgP"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrange_course_icon" translatesAutoresizingMaskIntoConstraints="NO" id="fIc-0h-KFj">
+                            <rect key="frame" x="63" y="68" width="12" height="14"/>
+                        </imageView>
+                        <view contentMode="scaleToFill" horizontalCompressionResistancePriority="752" translatesAutoresizingMaskIntoConstraints="NO" id="QDz-cd-OLe">
+                            <rect key="frame" x="67" y="43" width="8" height="17"/>
+                            <subviews>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9c5-M6-iVP">
+                                    <rect key="frame" x="4" y="8.6666666666666643" width="0.0" height="0.0"/>
+                                    <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                                    <color key="textColor" red="1" green="0.54901960780000003" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                            </subviews>
+                            <color key="backgroundColor" red="1" green="0.94509803920000002" blue="0.87058823529999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="trailing" secondItem="9c5-M6-iVP" secondAttribute="trailing" constant="4" id="C0X-oI-Cfv"/>
+                                <constraint firstItem="9c5-M6-iVP" firstAttribute="centerY" secondItem="QDz-cd-OLe" secondAttribute="centerY" id="itq-W9-oo1"/>
+                                <constraint firstAttribute="height" constant="17" id="ncg-NH-lmM"/>
+                                <constraint firstItem="9c5-M6-iVP" firstAttribute="leading" secondItem="QDz-cd-OLe" secondAttribute="leading" constant="4" id="w2V-js-HDd"/>
+                            </constraints>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="4"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                        </view>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xdo-Ff-H8E">
+                            <rect key="frame" x="79" y="67" width="0.0" height="16"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="16" id="04a-1z-wWI"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                            <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="Xdo-Ff-H8E" firstAttribute="centerY" secondItem="fIc-0h-KFj" secondAttribute="centerY" id="7n6-YD-bYN"/>
+                        <constraint firstItem="QDz-cd-OLe" firstAttribute="leading" secondItem="iST-OW-7X3" secondAttribute="trailing" constant="5" id="8Rb-GF-gev"/>
+                        <constraint firstItem="iST-OW-7X3" firstAttribute="leading" secondItem="Npd-J5-fnH" secondAttribute="trailing" constant="10" id="Awe-Nm-Pwv"/>
+                        <constraint firstItem="fIc-0h-KFj" firstAttribute="top" secondItem="iST-OW-7X3" secondAttribute="bottom" constant="8" id="Ewa-Lc-2zy"/>
+                        <constraint firstItem="Xdo-Ff-H8E" firstAttribute="leading" secondItem="fIc-0h-KFj" secondAttribute="trailing" constant="4" id="IEg-tz-Q5W"/>
+                        <constraint firstItem="Npd-J5-fnH" firstAttribute="leading" secondItem="3kP-Ps-23r" secondAttribute="leading" constant="12" id="IcQ-Fs-PtO"/>
+                        <constraint firstAttribute="trailing" secondItem="GSb-pn-BPN" secondAttribute="trailing" constant="12" id="K7Y-Hn-eff"/>
+                        <constraint firstItem="iST-OW-7X3" firstAttribute="top" secondItem="vQS-vg-LHr" secondAttribute="bottom" constant="9" id="Rwq-nQ-WqA"/>
+                        <constraint firstItem="GSb-pn-BPN" firstAttribute="top" secondItem="3kP-Ps-23r" secondAttribute="top" constant="12" id="X7Z-ED-naC"/>
+                        <constraint firstItem="GSb-pn-BPN" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="vQS-vg-LHr" secondAttribute="trailing" constant="10" id="ayq-8w-4xI"/>
+                        <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Xdo-Ff-H8E" secondAttribute="trailing" constant="12" id="b9M-Wn-ZNN"/>
+                        <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="QDz-cd-OLe" secondAttribute="trailing" constant="12" id="lAQ-oJ-xG1"/>
+                        <constraint firstItem="fIc-0h-KFj" firstAttribute="leading" secondItem="Npd-J5-fnH" secondAttribute="trailing" constant="11" id="mzz-xu-2YN"/>
+                        <constraint firstItem="QDz-cd-OLe" firstAttribute="centerY" secondItem="iST-OW-7X3" secondAttribute="centerY" id="oFQ-49-a57"/>
+                        <constraint firstItem="vQS-vg-LHr" firstAttribute="top" secondItem="3kP-Ps-23r" secondAttribute="top" constant="12" id="ou3-Mg-jMS"/>
+                        <constraint firstItem="Npd-J5-fnH" firstAttribute="top" secondItem="vQS-vg-LHr" secondAttribute="bottom" constant="8" id="vVT-dy-T2x"/>
+                        <constraint firstItem="vQS-vg-LHr" firstAttribute="leading" secondItem="3kP-Ps-23r" secondAttribute="leading" constant="12" id="w7s-8y-Rf8"/>
+                        <constraint firstAttribute="height" constant="94" id="y9Q-93-MmA"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstAttribute="trailing" secondItem="3kP-Ps-23r" secondAttribute="trailing" constant="14" id="4Gh-jb-yAi"/>
+                <constraint firstItem="3kP-Ps-23r" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="14" id="7Z1-ip-D1N"/>
+                <constraint firstItem="3kP-Ps-23r" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="12" id="9e9-i7-Jgm"/>
+                <constraint firstItem="wrx-dE-SgG" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="12" id="Al5-Rm-C0Z"/>
+                <constraint firstAttribute="trailing" secondItem="wrx-dE-SgG" secondAttribute="trailing" constant="14" id="DTK-zx-sfz"/>
+                <constraint firstItem="0tb-gl-GAm" firstAttribute="top" secondItem="3kP-Ps-23r" secondAttribute="bottom" id="OTH-gW-AXS"/>
+                <constraint firstAttribute="bottom" secondItem="0tb-gl-GAm" secondAttribute="bottom" id="UEV-bo-0Tb"/>
+                <constraint firstItem="0tb-gl-GAm" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="14" id="Z9F-sm-8PK"/>
+                <constraint firstAttribute="trailing" secondItem="0tb-gl-GAm" secondAttribute="trailing" constant="14" id="cv8-dL-9Gc"/>
+                <constraint firstItem="wrx-dE-SgG" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="14" id="fw1-kS-Z7J"/>
+                <constraint firstAttribute="bottom" secondItem="wrx-dE-SgG" secondAttribute="bottom" id="o5w-t8-08c"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="bgView" destination="wrx-dE-SgG" id="vqH-3S-COv"/>
+                <outlet property="courseDesc" destination="Xdo-Ff-H8E" id="6SI-WO-Xnh"/>
+                <outlet property="courseGroupTitle" destination="vQS-vg-LHr" id="CXK-hg-2hy"/>
+                <outlet property="nameLabel" destination="iST-OW-7X3" id="B1a-Jb-X70"/>
+                <outlet property="planContainer" destination="0tb-gl-GAm" id="n6L-Bo-VCM"/>
+                <outlet property="statusLabel" destination="GSb-pn-BPN" id="VE3-Bk-L0q"/>
+                <outlet property="subjectLabel" destination="9c5-M6-iVP" id="R3p-Ed-rYK"/>
+                <outlet property="subjectView" destination="QDz-cd-OLe" id="w4g-KL-8CK"/>
+            </connections>
+            <point key="canvasLocation" x="46.564885496183201" y="51.760563380281695"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="arrange_course_icon" width="12" height="14"/>
+        <image name="course_musicRoom" width="40" height="40"/>
+    </resources>
+</document>

+ 18 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomStudentPageView.h

@@ -0,0 +1,18 @@
+//
+//  MusicRoomStudentPageView.h
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/25.
+//
+
+#import "KSJXBodyView.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MusicRoomStudentPageView : KSJXBodyView
+
+@property (nonatomic, strong) NSString *courseGroupId;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 20 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/MusicRoom/View/MusicRoomStudentPageView.m

@@ -0,0 +1,20 @@
+//
+//  MusicRoomStudentPageView.m
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/25.
+//
+
+#import "MusicRoomStudentPageView.h"
+
+@implementation MusicRoomStudentPageView
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 24 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupNavView.h

@@ -0,0 +1,24 @@
+//
+//  CourseGroupNavView.h
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef void(^CourseGroupNavCallback)(void);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CourseGroupNavView : UIView
+
++ (instancetype)sharedInstance;
+
+- (void)configWithNavTitle:(NSString *)navTitle callback:(CourseGroupNavCallback)callback;
+
++ (CGFloat)getViewHeight;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 51 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupNavView.m

@@ -0,0 +1,51 @@
+//
+//  CourseGroupNavView.m
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import "CourseGroupNavView.h"
+
+@interface CourseGroupNavView ()
+
+@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
+
+@property (nonatomic, copy) CourseGroupNavCallback callback;
+
+@end
+
+@implementation CourseGroupNavView
+
+
++ (instancetype)sharedInstance {
+    CourseGroupNavView *view = [[[NSBundle mainBundle] loadNibNamed:@"CourseGroupNavView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)configWithNavTitle:(NSString *)navTitle callback:(CourseGroupNavCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+    self.titleLabel.text = [NSString returnNoNullStringWithString:navTitle];
+}
+
+- (IBAction)backAction:(id)sender {
+    if (self.callback) {
+        self.callback();
+    }
+}
+
++ (CGFloat)getViewHeight {
+    
+    return kNaviBarHeight;
+}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 70 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupNavView.xib

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="CourseGroupNavView">
+            <rect key="frame" x="0.0" y="0.0" width="393" height="100"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IZU-kJ-vdx">
+                    <rect key="frame" x="0.0" y="56" width="393" height="44"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="back_black" translatesAutoresizingMaskIntoConstraints="NO" id="h4o-PW-QRb">
+                            <rect key="frame" x="15" y="12" width="12" height="20"/>
+                        </imageView>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NQi-ZX-f8h">
+                            <rect key="frame" x="0.0" y="0.0" width="44" height="44"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="44" id="f0m-aT-yWJ"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="backAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="BvX-eN-dun"/>
+                            </connections>
+                        </button>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="VIP定制课详情" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fok-Mj-6d9">
+                            <rect key="frame" x="136.66666666666666" y="11" width="119.99999999999997" height="22"/>
+                            <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="18"/>
+                            <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="NQi-ZX-f8h" firstAttribute="top" secondItem="IZU-kJ-vdx" secondAttribute="top" id="0OO-Hb-fnE"/>
+                        <constraint firstItem="Fok-Mj-6d9" firstAttribute="centerY" secondItem="IZU-kJ-vdx" secondAttribute="centerY" id="5uQ-9y-fY7"/>
+                        <constraint firstAttribute="bottom" secondItem="NQi-ZX-f8h" secondAttribute="bottom" id="E8x-ll-Cld"/>
+                        <constraint firstItem="Fok-Mj-6d9" firstAttribute="centerX" secondItem="IZU-kJ-vdx" secondAttribute="centerX" id="MUP-yd-kTl"/>
+                        <constraint firstItem="NQi-ZX-f8h" firstAttribute="leading" secondItem="IZU-kJ-vdx" secondAttribute="leading" id="fFI-Ea-Wao"/>
+                        <constraint firstAttribute="height" constant="44" id="fkZ-cn-gsY"/>
+                        <constraint firstItem="h4o-PW-QRb" firstAttribute="leading" secondItem="IZU-kJ-vdx" secondAttribute="leading" constant="15" id="ihx-d9-9d5"/>
+                        <constraint firstItem="h4o-PW-QRb" firstAttribute="centerY" secondItem="IZU-kJ-vdx" secondAttribute="centerY" id="p5Y-Ng-77Q"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstAttribute="bottom" secondItem="IZU-kJ-vdx" secondAttribute="bottom" id="ZaP-e3-Ifq"/>
+                <constraint firstAttribute="trailing" secondItem="IZU-kJ-vdx" secondAttribute="trailing" id="bOV-Sr-khy"/>
+                <constraint firstItem="IZU-kJ-vdx" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="maz-sq-QWG"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="titleLabel" destination="Fok-Mj-6d9" id="Lvl-y1-QrB"/>
+            </connections>
+            <point key="canvasLocation" x="34.351145038167935" y="-42.253521126760567"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="back_black" width="12" height="20"/>
+    </resources>
+</document>

+ 24 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAddView.h

@@ -0,0 +1,24 @@
+//
+//  CourseGroupPlanAddView.h
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef void(^AddGroupPlanCallback)(void);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CourseGroupPlanAddView : UIView
+
++ (instancetype)sharedInstance;
+
+- (void)addPlanAction:(AddGroupPlanCallback)callback;
+
++ (CGFloat)getViewHeight;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 54 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAddView.m

@@ -0,0 +1,54 @@
+//
+//  CourseGroupPlanAddView.m
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import "CourseGroupPlanAddView.h"
+
+@interface CourseGroupPlanAddView ()
+
+@property (weak, nonatomic) IBOutlet UIView *colorBg;
+
+@property (nonatomic, copy) AddGroupPlanCallback callback;
+
+@end
+
+@implementation CourseGroupPlanAddView
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    CAGradientLayer *colorLayer = [UIView createGradientLayerFromColor:HexRGBAlpha(0x2DC7AA, 0.1f) startPoint:CGPointMake(0.54, 0) endColor:HexRGBAlpha(0x2DC7AA, 0.0f) endPoint:CGPointMake(0.54, 1) bounds:CGRectMake(0, 0, KPortraitWidth - 26, 36)];
+    [self.colorBg.layer addSublayer:colorLayer];
+}
+
++ (instancetype)sharedInstance {
+    CourseGroupPlanAddView *view = [[[NSBundle mainBundle] loadNibNamed:@"CourseGroupPlanAddView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)addPlanAction:(AddGroupPlanCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+
+- (IBAction)buttonAction:(id)sender {
+    if (self.callback) {
+        self.callback();
+    }
+}
+
++ (CGFloat)getViewHeight {
+    return 60.0f;
+}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 89 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAddView.xib

@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="CourseGroupPlanAddView">
+            <rect key="frame" x="0.0" y="0.0" width="393" height="60"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8Aa-kp-bYI">
+                    <rect key="frame" x="12" y="12" width="369" height="36"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="color" keyPath="borderColor">
+                            <color key="value" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="0.40000000000000002" colorSpace="custom" customColorSpace="sRGB"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
+                            <real key="value" value="1"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="6"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hTw-6B-RQf">
+                    <rect key="frame" x="12" y="12" width="369" height="36"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="group_addPlan" translatesAutoresizingMaskIntoConstraints="NO" id="zZL-6i-GRC">
+                            <rect key="frame" x="127.66666666666666" y="8" width="22" height="20"/>
+                        </imageView>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="添加课程规划" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wvq-0R-j6d">
+                            <rect key="frame" x="151.66666666666666" y="9" width="90" height="18"/>
+                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
+                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="c7A-Hr-wRT">
+                            <rect key="frame" x="0.0" y="0.0" width="369" height="36"/>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="buttonAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="4JR-qG-yqX"/>
+                            </connections>
+                        </button>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="c7A-Hr-wRT" firstAttribute="leading" secondItem="hTw-6B-RQf" secondAttribute="leading" id="G5B-Oy-WRC"/>
+                        <constraint firstItem="Wvq-0R-j6d" firstAttribute="centerY" secondItem="hTw-6B-RQf" secondAttribute="centerY" id="HQT-Yg-whk"/>
+                        <constraint firstItem="c7A-Hr-wRT" firstAttribute="top" secondItem="hTw-6B-RQf" secondAttribute="top" id="WAm-6h-1Bx"/>
+                        <constraint firstAttribute="bottom" secondItem="c7A-Hr-wRT" secondAttribute="bottom" id="lGw-Zm-vyN"/>
+                        <constraint firstItem="Wvq-0R-j6d" firstAttribute="centerX" secondItem="hTw-6B-RQf" secondAttribute="centerX" constant="12" id="lPb-wr-J1K"/>
+                        <constraint firstItem="Wvq-0R-j6d" firstAttribute="leading" secondItem="zZL-6i-GRC" secondAttribute="trailing" constant="2" id="wOB-7P-0VW"/>
+                        <constraint firstAttribute="trailing" secondItem="c7A-Hr-wRT" secondAttribute="trailing" id="xdD-8m-RPB"/>
+                        <constraint firstItem="Wvq-0R-j6d" firstAttribute="centerY" secondItem="zZL-6i-GRC" secondAttribute="centerY" id="zVc-rj-3ZG"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="8Aa-kp-bYI" firstAttribute="bottom" secondItem="hTw-6B-RQf" secondAttribute="bottom" id="35i-G3-qUh"/>
+                <constraint firstItem="hTw-6B-RQf" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="12" id="4hF-Ht-R4Y"/>
+                <constraint firstItem="8Aa-kp-bYI" firstAttribute="trailing" secondItem="hTw-6B-RQf" secondAttribute="trailing" id="Nph-aE-9YU"/>
+                <constraint firstItem="hTw-6B-RQf" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="12" id="cC9-8l-Qeq"/>
+                <constraint firstItem="8Aa-kp-bYI" firstAttribute="leading" secondItem="hTw-6B-RQf" secondAttribute="leading" id="hGh-n2-ygv"/>
+                <constraint firstAttribute="bottom" secondItem="hTw-6B-RQf" secondAttribute="bottom" constant="12" id="j6u-AX-i9J"/>
+                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="hTw-6B-RQf" secondAttribute="trailing" constant="12" id="pUv-Te-Kkt"/>
+                <constraint firstItem="8Aa-kp-bYI" firstAttribute="top" secondItem="hTw-6B-RQf" secondAttribute="top" id="zCo-UK-6DO"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="colorBg" destination="8Aa-kp-bYI" id="pCY-Y0-BxF"/>
+            </connections>
+            <point key="canvasLocation" x="41.984732824427482" y="-57.74647887323944"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="group_addPlan" width="22" height="20"/>
+    </resources>
+</document>

+ 24 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAlertView.h

@@ -0,0 +1,24 @@
+//
+//  CourseGroupPlanAlertView.h
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef void(^PlanModifyCallback)(NSString * _Nullable planString);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CourseGroupPlanAlertView : UIView
+
++ (instancetype)sharedInstance;
+
+- (void)configWithDesc:(NSString *)planString callback:(PlanModifyCallback)callback;
+
+- (void)showInView:(UIView *)displayView;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 166 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAlertView.m

@@ -0,0 +1,166 @@
+//
+//  CourseGroupPlanAlertView.m
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import "CourseGroupPlanAlertView.h"
+
+@interface CourseGroupPlanAlertView ()<UITextViewDelegate>
+
+@property (weak, nonatomic) IBOutlet UIView *bgView;
+
+@property (weak, nonatomic) IBOutlet UITextView *textView;
+
+@property (weak, nonatomic) IBOutlet UILabel *tipsLabel;
+
+@property (weak, nonatomic) IBOutlet UILabel *countLabel;
+
+@property (nonatomic, copy) PlanModifyCallback callback;
+
+@end
+
+@implementation CourseGroupPlanAlertView
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    self.textView.delegate = self;
+    self.textView.textContainer.lineFragmentPadding = 0;
+    self.textView.textContainerInset = UIEdgeInsetsZero;
+    self.bgView.layer.cornerRadius = 12.0f;
+    self.bgView.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
+}
+
++ (instancetype)sharedInstance {
+    CourseGroupPlanAlertView *view = [[[NSBundle mainBundle] loadNibNamed:@"CourseGroupPlanAlertView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)configWithDesc:(NSString *)planString callback:(PlanModifyCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+    self.countLabel.text = [NSString stringWithFormat:@"%zd/200",planString.length];
+    self.tipsLabel.hidden = [NSString isEmptyString:planString] ? NO : YES;
+    
+    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init];
+    paragraphStyle.lineSpacing = 4.0f;
+    UIFont *font = [UIFont systemFontOfSize:15 weight:UIFontWeightRegular];
+    
+    NSDictionary *attributes = @{NSFontAttributeName:font,NSParagraphStyleAttributeName:paragraphStyle,NSForegroundColorAttributeName:HexRGBAlpha(0x000000, 0.9)};
+    
+        self.textView.attributedText = [[NSAttributedString alloc] initWithString:planString attributes:attributes];
+}
+
+- (void)showInView:(UIView *)displayView {
+    if (!displayView) {
+        displayView = [NSObject getKeyWindow];
+    }
+    if (![displayView.subviews containsObject:self]) {
+        [displayView addSubview:self];
+        [self mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.right.top.bottom.mas_equalTo(displayView);
+        }];
+    }
+}
+
+- (IBAction)cancelAction:(id)sender {
+    [self endEditing:YES];
+    [self removeFromSuperview];
+}
+
+- (IBAction)sureAction:(id)sender {
+    [self endEditing:YES];
+    [self removeFromSuperview];
+    if (self.callback) {
+        self.callback(self.textView.text);
+    }
+}
+
+#pragma mark   ---- delegate
+
+- (void)textViewDidBeginEditing:(UITextView *)textView {
+    
+    self.tipsLabel.hidden = YES;
+}
+
+
+- (void)textViewDidEndEditing:(UITextView *)textView {
+    if ([NSString isEmptyString:textView.text]) {
+        self.tipsLabel.hidden = NO;
+    }
+}
+
+- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
+    UITextRange *markedTextRange = textView.markedTextRange;
+    if (markedTextRange) {
+        // 当前处于拼音输入状态,暂不更新 attributedText
+        return YES;
+    }
+    NSInteger limitCount = 200;
+
+    NSString *newText = [[textView text] stringByReplacingCharactersInRange:range withString:text];
+    if (newText.length > limitCount) {
+        newText = [newText substringWithRange:NSMakeRange(0, limitCount)];
+        textView.text = newText;
+    }
+    return YES;
+}
+
+- (void)updateTextViewLineHeight:(UITextView *)textView {
+    UITextRange *markedTextRange = textView.markedTextRange;
+    if (markedTextRange) {
+        // 当前处于拼音输入状态,暂不更新 attributedText
+        return;
+    }
+    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
+    paragraphStyle.lineSpacing = 4.0f;
+    UIFont *font = [UIFont systemFontOfSize:15 weight:UIFontWeightRegular];
+    [UIView setAnimationsEnabled:NO];
+    NSRange selectedRange = textView.selectedRange;
+    NSMutableAttributedString *attrs = [[NSMutableAttributedString alloc] initWithString:textView.text attributes:@{NSParagraphStyleAttributeName:paragraphStyle,NSFontAttributeName:font, NSForegroundColorAttributeName:HexRGBAlpha(0x000000, 0.9)}];
+    textView.attributedText = attrs;
+    textView.selectedRange = selectedRange;
+    [UIView setAnimationsEnabled:YES];
+}
+
+- (void)textViewDidChange:(UITextView *)textView {
+    // 获取当前高亮的部分(如果正在拼音输入状态)
+    UITextRange *selectedRange = [textView markedTextRange];
+    UITextPosition *position = [textView positionFromPosition:selectedRange.start offset:0];
+    
+    // 如果没有高亮选择的文本,说明不是拼音输入状态
+    if (!position) {
+        // 获取当前textView的内容
+        NSInteger limitCount = 200;
+        NSString *currentText = textView.text;
+        
+        // 如果文本超出最大长度,进行截取
+        if (currentText.length > limitCount) {
+            NSString *limitedText = [currentText substringToIndex:limitCount];
+            textView.text = limitedText;
+        }
+        
+        self.countLabel.text = [NSString stringWithFormat:@"%zd/200",textView.text.length];
+    }
+    [self updateTextViewLineHeight:textView];
+}
+
+- (BOOL)textViewShouldEndEditing:(UITextView *)textView {
+    [self endEditing:YES];
+    return YES;
+}
+
+- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
+    [self endEditing:YES];
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 157 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanAlertView.xib

@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="CourseGroupPlanAlertView">
+            <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DQA-Yl-R8S">
+                    <rect key="frame" x="0.0" y="526" width="393" height="326"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="添加课程规划" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1QL-rw-ewk">
+                            <rect key="frame" x="142.66666666666666" y="18" width="108" height="22"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="22" id="hX4-6a-Zj4"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="18"/>
+                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="cancle_button" translatesAutoresizingMaskIntoConstraints="NO" id="Y1F-10-7UQ">
+                            <rect key="frame" x="365" y="22" width="16" height="16"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="16" id="EG8-Ky-7Sd"/>
+                                <constraint firstAttribute="height" constant="16" id="FkL-Xu-D9p"/>
+                            </constraints>
+                        </imageView>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="zSN-d7-MKE">
+                            <rect key="frame" x="349" y="5" width="44" height="44"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="44" id="9Jv-Jv-EAx"/>
+                                <constraint firstAttribute="width" constant="44" id="hMm-g7-FOv"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="cancelAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="fap-2U-u0a"/>
+                            </connections>
+                        </button>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KlC-4W-rhW">
+                            <rect key="frame" x="14" y="58" width="365" height="164"/>
+                            <subviews>
+                                <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="ozS-Ji-bWH">
+                                    <rect key="frame" x="12" y="13" width="341" height="117"/>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <color key="textColor" systemColor="labelColor"/>
+                                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                    <textInputTraits key="textInputTraits" autocapitalizationType="sentences" smartInsertDeleteType="no"/>
+                                </textView>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0/500" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="G0D-65-phx">
+                                    <rect key="frame" x="314" y="130" width="39" height="22"/>
+                                    <constraints>
+                                        <constraint firstAttribute="height" constant="22" id="yPF-OZ-aOh"/>
+                                    </constraints>
+                                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                    <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="请填写您的课程规划" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dyn-hF-Vp7">
+                                    <rect key="frame" x="12" y="12" width="134" height="22"/>
+                                    <constraints>
+                                        <constraint firstAttribute="height" constant="22" id="SoL-Bj-P4o"/>
+                                    </constraints>
+                                    <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                    <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                            </subviews>
+                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <constraints>
+                                <constraint firstAttribute="trailing" secondItem="G0D-65-phx" secondAttribute="trailing" constant="12" id="71Q-c4-eXa"/>
+                                <constraint firstAttribute="bottom" secondItem="G0D-65-phx" secondAttribute="bottom" constant="12" id="8Wg-1x-pHO"/>
+                                <constraint firstAttribute="trailing" secondItem="ozS-Ji-bWH" secondAttribute="trailing" constant="12" id="8e6-SR-s73"/>
+                                <constraint firstItem="G0D-65-phx" firstAttribute="top" secondItem="ozS-Ji-bWH" secondAttribute="bottom" id="Iso-mr-f0P"/>
+                                <constraint firstItem="ozS-Ji-bWH" firstAttribute="leading" secondItem="KlC-4W-rhW" secondAttribute="leading" constant="12" id="LZC-VX-mmd"/>
+                                <constraint firstItem="dyn-hF-Vp7" firstAttribute="top" secondItem="KlC-4W-rhW" secondAttribute="top" constant="12" id="MpQ-9w-EIp"/>
+                                <constraint firstAttribute="height" constant="164" id="cGh-hR-PH0"/>
+                                <constraint firstItem="dyn-hF-Vp7" firstAttribute="leading" secondItem="KlC-4W-rhW" secondAttribute="leading" constant="12" id="ds5-kN-W89"/>
+                                <constraint firstItem="ozS-Ji-bWH" firstAttribute="top" secondItem="KlC-4W-rhW" secondAttribute="top" constant="13" id="ndy-E0-arG"/>
+                            </constraints>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="10"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                        </view>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bSw-AS-d2f">
+                            <rect key="frame" x="28" y="252" width="337" height="44"/>
+                            <color key="backgroundColor" red="0.1764705882352941" green="0.7803921568627451" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="44" id="nZe-p3-TfW"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" title="确认"/>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="22"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                            <connections>
+                                <action selector="sureAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="9Fs-C3-Wg6"/>
+                            </connections>
+                        </button>
+                    </subviews>
+                    <color key="backgroundColor" red="0.97254901960784312" green="0.97647058823529409" blue="0.9882352941176471" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="326" id="624-Qs-q1U"/>
+                        <constraint firstAttribute="trailing" secondItem="zSN-d7-MKE" secondAttribute="trailing" id="Epz-qx-RsF"/>
+                        <constraint firstAttribute="trailing" secondItem="bSw-AS-d2f" secondAttribute="trailing" constant="28" id="MVV-Rp-CSe"/>
+                        <constraint firstItem="KlC-4W-rhW" firstAttribute="leading" secondItem="DQA-Yl-R8S" secondAttribute="leading" constant="14" id="Q3H-6K-oko"/>
+                        <constraint firstAttribute="trailing" secondItem="KlC-4W-rhW" secondAttribute="trailing" constant="14" id="WRg-YL-ErL"/>
+                        <constraint firstItem="KlC-4W-rhW" firstAttribute="top" secondItem="1QL-rw-ewk" secondAttribute="bottom" constant="18" id="Z2e-fu-vi5"/>
+                        <constraint firstItem="Y1F-10-7UQ" firstAttribute="top" secondItem="DQA-Yl-R8S" secondAttribute="top" constant="22" id="aG1-xd-gZH"/>
+                        <constraint firstAttribute="trailing" secondItem="Y1F-10-7UQ" secondAttribute="trailing" constant="12" id="kqU-8h-Js2"/>
+                        <constraint firstItem="1QL-rw-ewk" firstAttribute="centerX" secondItem="DQA-Yl-R8S" secondAttribute="centerX" id="luJ-zE-jqI"/>
+                        <constraint firstItem="bSw-AS-d2f" firstAttribute="leading" secondItem="DQA-Yl-R8S" secondAttribute="leading" constant="28" id="nZY-Dj-LfI"/>
+                        <constraint firstItem="zSN-d7-MKE" firstAttribute="top" secondItem="DQA-Yl-R8S" secondAttribute="top" constant="5" id="qAE-lY-A6C"/>
+                        <constraint firstItem="1QL-rw-ewk" firstAttribute="top" secondItem="DQA-Yl-R8S" secondAttribute="top" constant="18" id="qR3-ln-1dr"/>
+                        <constraint firstItem="bSw-AS-d2f" firstAttribute="top" secondItem="KlC-4W-rhW" secondAttribute="bottom" constant="30" id="vcv-8u-U0A"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
+            <constraints>
+                <constraint firstAttribute="trailing" secondItem="DQA-Yl-R8S" secondAttribute="trailing" id="AKz-ei-ODR"/>
+                <constraint firstItem="DQA-Yl-R8S" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="atY-XB-bLV"/>
+                <constraint firstAttribute="bottom" secondItem="DQA-Yl-R8S" secondAttribute="bottom" id="ynd-t1-AZ3"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="bgView" destination="DQA-Yl-R8S" id="f1A-iE-nz9"/>
+                <outlet property="countLabel" destination="G0D-65-phx" id="yDz-EG-MmP"/>
+                <outlet property="textView" destination="ozS-Ji-bWH" id="r4e-Xa-fMb"/>
+                <outlet property="tipsLabel" destination="dyn-hF-Vp7" id="lRV-me-VWS"/>
+            </connections>
+            <point key="canvasLocation" x="9.9236641221374047" y="19.718309859154932"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="cancle_button" width="20" height="20"/>
+        <systemColor name="labelColor">
+            <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 26 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanDescView.h

@@ -0,0 +1,26 @@
+//
+//  CourseGroupPlanDescView.h
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef void(^ModifyGroupPlanCallback)(void);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CourseGroupPlanDescView : UIView
+
++ (instancetype)sharedInstance;
+
+- (void)configWithPlanDesc:(NSString *)planDesc;
+
+- (void)modifyPlanAction:(ModifyGroupPlanCallback)callback;
+
++ (CGFloat)getViewHeight:(NSString *)planString;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 100 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanDescView.m

@@ -0,0 +1,100 @@
+//
+//  CourseGroupPlanDescView.m
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import "CourseGroupPlanDescView.h"
+
+@interface CourseGroupPlanDescView ()<UITextViewDelegate>
+
+@property (weak, nonatomic) IBOutlet UITextView *textView;
+
+@property (nonatomic, copy) ModifyGroupPlanCallback callback;
+
+@end
+
+@implementation CourseGroupPlanDescView
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    self.textView.delegate = self;
+    self.textView.textContainer.lineFragmentPadding = 0;
+    self.textView.textContainerInset = UIEdgeInsetsZero;
+}
++ (instancetype)sharedInstance {
+    CourseGroupPlanDescView *view = [[[NSBundle mainBundle] loadNibNamed:@"CourseGroupPlanDescView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)configWithPlanDesc:(NSString *)planDesc {
+    
+    NSMutableParagraphStyle *paragraphStyle = [CourseGroupPlanDescView getParagraphStyleWithFont:[UIFont systemFontOfSize:13.0f] lineHeight:22.0f];
+    CGFloat baselineOffset = [CourseGroupPlanDescView getBaseOffline:[UIFont systemFontOfSize:13.0f] lineHeight:22.0f];
+    NSMutableAttributedString *attrs = [[NSMutableAttributedString alloc] initWithString:planDesc attributes:@{NSParagraphStyleAttributeName:paragraphStyle, NSBaselineOffsetAttributeName:@(baselineOffset),NSFontAttributeName:[UIFont systemFontOfSize:13.0f], NSForegroundColorAttributeName:HexRGB(0x777777)}];
+    self.textView.attributedText = attrs;
+}
+
+- (void)modifyPlanAction:(ModifyGroupPlanCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+
+- (IBAction)buttonAction:(id)sender {
+    if (self.callback) {
+        self.callback();
+    }
+}
+
+
++ (CGFloat)getBaseOffline:(UIFont *)font lineHeight:(CGFloat)lineHeight {
+    CGFloat baselineOffset = (lineHeight - font.lineHeight) / 4;
+    return baselineOffset;
+}
+
++ (NSMutableParagraphStyle *)getParagraphStyleWithFont:(UIFont *)font lineHeight:(CGFloat)lineHeigh {
+    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
+    paragraphStyle.maximumLineHeight = lineHeigh;
+    paragraphStyle.minimumLineHeight = lineHeigh;
+    return paragraphStyle;
+}
+
+
++ (CGFloat)getViewHeight:(NSString *)planString {
+    NSMutableParagraphStyle *paragraphStyle = [self getParagraphStyleWithFont:[UIFont systemFontOfSize:13.0f] lineHeight:22.0f];
+    CGFloat baselineOffset = [self getBaseOffline:[UIFont systemFontOfSize:13.0f] lineHeight:22.0f];
+    CGFloat height = [planString boundingRectWithSize:CGSizeMake(KPortraitWidth - 26, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSParagraphStyleAttributeName:paragraphStyle, NSBaselineOffsetAttributeName:@(baselineOffset),NSFontAttributeName:[UIFont systemFontOfSize:13.0f]} context:nil].size.height + 1 + 42 + 10 + 12;
+    if (height < 96) {
+        height = 96.0f;
+    }
+    if (height > 152) {
+        height = 152.0f;
+    }
+    return height;
+}
+
+// 在 UITextView 滚动开始时禁用 UITableView 滚动
+- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
+    [[NSNotificationCenter defaultCenter] postNotificationName:@"UITextViewScroll" object:nil];
+}
+
+// 在 UITextView 停止滚动后恢复 UITableView 滚动
+- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
+    [[NSNotificationCenter defaultCenter] postNotificationName:@"UITextViewEndScroll" object:nil];
+}
+
+
+- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
+    [[NSNotificationCenter defaultCenter] postNotificationName:@"UITextViewEndScroll" object:nil];
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 111 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/CourseGroupPlanDescView.xib

@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="CourseGroupPlanDescView">
+            <rect key="frame" x="0.0" y="0.0" width="393" height="145"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7vY-rm-il4">
+                    <rect key="frame" x="0.0" y="12" width="393" height="133"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="course_plan_icon" translatesAutoresizingMaskIntoConstraints="NO" id="0CG-ac-gwS">
+                            <rect key="frame" x="12" y="12" width="18" height="18"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="18" id="TiP-dm-FSr"/>
+                                <constraint firstAttribute="width" constant="18" id="zCt-qc-sAF"/>
+                            </constraints>
+                        </imageView>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="课程规划" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ext-kp-pD1">
+                            <rect key="frame" x="36" y="12" width="60" height="18"/>
+                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
+                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="plan_edit" translatesAutoresizingMaskIntoConstraints="NO" id="I6t-ba-QZt">
+                            <rect key="frame" x="339" y="14" width="14" height="14"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="14" id="Ssd-tX-Qpj"/>
+                                <constraint firstAttribute="height" constant="14" id="bQq-ov-G2J"/>
+                            </constraints>
+                        </imageView>
+                        <label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="修改" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="17Y-QM-ifr">
+                            <rect key="frame" x="355" y="13" width="26" height="16"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                            <color key="textColor" red="0.46666666666666667" green="0.46666666666666667" blue="0.46666666666666667" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <button hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gyP-HZ-5E0">
+                            <rect key="frame" x="333" y="5" width="60" height="34"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="60" id="kQn-sa-5Qa"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="buttonAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="7O5-OQ-PEc"/>
+                            </connections>
+                        </button>
+                        <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="djD-FS-SLG">
+                            <rect key="frame" x="12" y="42" width="369" height="81"/>
+                            <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                            <color key="textColor" red="0.46666666666666667" green="0.46666666666666667" blue="0.46666666666666667" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                            <textInputTraits key="textInputTraits" autocapitalizationType="sentences" smartInsertDeleteType="no"/>
+                        </textView>
+                    </subviews>
+                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                    <constraints>
+                        <constraint firstItem="17Y-QM-ifr" firstAttribute="leading" secondItem="I6t-ba-QZt" secondAttribute="trailing" constant="2" id="6tq-hO-O19"/>
+                        <constraint firstAttribute="trailing" secondItem="gyP-HZ-5E0" secondAttribute="trailing" id="8h7-nk-xKs"/>
+                        <constraint firstAttribute="trailing" secondItem="17Y-QM-ifr" secondAttribute="trailing" constant="12" id="HQk-s5-KMm"/>
+                        <constraint firstItem="djD-FS-SLG" firstAttribute="top" secondItem="0CG-ac-gwS" secondAttribute="bottom" constant="12" id="O2v-6x-zf5"/>
+                        <constraint firstItem="0CG-ac-gwS" firstAttribute="leading" secondItem="7vY-rm-il4" secondAttribute="leading" constant="12" id="Zco-rw-GXs"/>
+                        <constraint firstItem="djD-FS-SLG" firstAttribute="leading" secondItem="7vY-rm-il4" secondAttribute="leading" constant="12" id="aeJ-Cj-3JN"/>
+                        <constraint firstItem="gyP-HZ-5E0" firstAttribute="top" secondItem="7vY-rm-il4" secondAttribute="top" constant="5" id="cqp-5F-Lnx"/>
+                        <constraint firstItem="0CG-ac-gwS" firstAttribute="top" secondItem="7vY-rm-il4" secondAttribute="top" constant="12" id="fFp-ya-en1"/>
+                        <constraint firstAttribute="trailing" secondItem="djD-FS-SLG" secondAttribute="trailing" constant="12" id="hNV-TX-AfM"/>
+                        <constraint firstItem="Ext-kp-pD1" firstAttribute="leading" secondItem="0CG-ac-gwS" secondAttribute="trailing" constant="6" id="jhx-Yc-Zkl"/>
+                        <constraint firstItem="Ext-kp-pD1" firstAttribute="centerY" secondItem="0CG-ac-gwS" secondAttribute="centerY" id="msL-eA-jvm"/>
+                        <constraint firstItem="17Y-QM-ifr" firstAttribute="centerY" secondItem="Ext-kp-pD1" secondAttribute="centerY" id="mvg-ov-0fM"/>
+                        <constraint firstItem="I6t-ba-QZt" firstAttribute="centerY" secondItem="Ext-kp-pD1" secondAttribute="centerY" id="nM8-HE-qkG"/>
+                        <constraint firstAttribute="bottom" secondItem="djD-FS-SLG" secondAttribute="bottom" constant="10" id="yPj-fN-IJf"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="7vY-rm-il4" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="9Y6-W1-3Sp"/>
+                <constraint firstAttribute="bottom" secondItem="7vY-rm-il4" secondAttribute="bottom" id="mAf-kq-7c6"/>
+                <constraint firstAttribute="trailing" secondItem="7vY-rm-il4" secondAttribute="trailing" id="nxI-wi-jUn"/>
+                <constraint firstItem="7vY-rm-il4" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="12" id="per-Lb-guY"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="textView" destination="djD-FS-SLG" id="3te-uV-dmn"/>
+            </connections>
+            <point key="canvasLocation" x="75.572519083969468" y="45.422535211267608"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="course_plan_icon" width="18" height="18"/>
+        <image name="plan_edit" width="14" height="14"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 41 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/GroupCourseSortView.h

@@ -0,0 +1,41 @@
+//
+//  GroupCourseSortView.h
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef NS_ENUM(NSInteger, SORT_TYPE) {
+    SORT_TYPE_TIME = 1,   // 课程时间
+    SORT_TYPE_STATUS = 2, // 课程状态
+    SORT_TYPE_ATTENDENCE = 3,  // 考勤筛选
+    SORT_TYPE_HIDDEN = 4, // 隐藏
+};
+
+typedef void(^SortActionBlock)(SORT_TYPE type);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface GroupCourseSortView : UIView
+
+@property (weak, nonatomic) IBOutlet UILabel *firstLabel;
+@property (weak, nonatomic) IBOutlet UILabel *secondLabel;
+@property (weak, nonatomic) IBOutlet UILabel *thirdLabel;
+
+@property (nonatomic, assign) BOOL firstArrowUp;
+
+@property (nonatomic, assign) BOOL secondArrowUp;
+
+@property (nonatomic, assign) BOOL thirdArrowUp;
+
++ (instancetype)sharedInstance;
+
++ (CGFloat)getViewHeight;
+
+- (void)sortAction:(SortActionBlock)block;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 101 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/GroupCourseSortView.m

@@ -0,0 +1,101 @@
+//
+//  GroupCourseSortView.m
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import "GroupCourseSortView.h"
+
+@interface GroupCourseSortView ()
+
+@property (nonatomic, copy) SortActionBlock block;
+
+@property (weak, nonatomic) IBOutlet UIImageView *firstArrow;
+@property (weak, nonatomic) IBOutlet UIImageView *secondArrow;
+@property (weak, nonatomic) IBOutlet UIImageView *thirdArrow;
+
+
+@end
+
+@implementation GroupCourseSortView
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    self.firstArrowUp = NO;
+    self.secondArrowUp = NO;
+    self.thirdArrowUp = NO;
+}
+
++ (instancetype)sharedInstance {
+    GroupCourseSortView *view = [[[NSBundle mainBundle] loadNibNamed:@"GroupCourseSortView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)sortAction:(SortActionBlock)block {
+    if (block) {
+        self.block = block;
+    }
+}
+
+- (IBAction)buttonClickAction:(UIButton *)sender {
+    NSInteger index = sender.tag - 1000;
+    BOOL isHidden;
+    if (index == 1) {
+        self.firstArrowUp = !self.firstArrowUp;
+        self.secondArrowUp = NO;
+        self.thirdArrowUp = NO;
+        isHidden = !self.firstArrowUp;
+    }
+    else if (index == 2) {
+        self.secondArrowUp = !self.secondArrowUp;
+        self.firstArrowUp = NO;
+        self.thirdArrowUp = NO;
+        isHidden = !self.secondArrowUp;
+    }
+    else {
+        self.thirdArrowUp = !self.thirdArrowUp;
+        self.firstArrowUp = NO;
+        self.secondArrowUp = NO;
+        isHidden = !self.thirdArrowUp;
+    }
+    
+    if (self.block) {
+        if (isHidden) {
+            index = 4;
+        }
+        self.block(index);
+    }
+}
+
+- (void)setFirstArrowUp:(BOOL)firstArrowUp {
+    _firstArrowUp = firstArrowUp;
+    NSString *imageName = firstArrowUp ? @"sort_up_blue" : @"sort_down_grey";
+    [_firstArrow setImage:[UIImage imageNamed:imageName]];
+}
+
+- (void)setSecondArrowUp:(BOOL)secondArrowUp {
+    _secondArrowUp = secondArrowUp;
+    NSString *imageName = secondArrowUp ? @"sort_up_blue" : @"sort_down_grey";
+    [_secondArrow setImage:[UIImage imageNamed:imageName]];
+
+
+}
+
+- (void)setThirdArrowUp:(BOOL)thirdArrowUp {
+    _thirdArrowUp = thirdArrowUp;
+    NSString *imageName = thirdArrowUp ? @"sort_up_blue" : @"sort_down_grey";
+    [_thirdArrow setImage:[UIImage imageNamed:imageName]];
+}
+
++ (CGFloat)getViewHeight {
+    return 50.0f;
+}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 192 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/View/CourseGroupView/GroupCourseSortView.xib

@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="GroupCourseSortView">
+            <rect key="frame" x="0.0" y="0.0" width="385" height="55"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nUQ-28-ciH">
+                    <rect key="frame" x="14" y="0.0" width="357" height="55"/>
+                    <subviews>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3ud-K6-gBw">
+                            <rect key="frame" x="0.0" y="0.0" width="112.33333333333333" height="55"/>
+                            <subviews>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="XXXX年XX月" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cfT-LB-SNv">
+                                    <rect key="frame" x="7.3333333333333286" y="19" width="89.999999999999986" height="17"/>
+                                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                    <color key="textColor" red="0.074509803921568626" green="0.078431372549019607" blue="0.082352941176470587" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="sort_down_grey" translatesAutoresizingMaskIntoConstraints="NO" id="aFu-Ok-mVy">
+                                    <rect key="frame" x="102.33333333333333" y="25" width="9" height="5"/>
+                                    <constraints>
+                                        <constraint firstAttribute="height" constant="5" id="f2v-0M-H3l"/>
+                                        <constraint firstAttribute="width" constant="9" id="zTE-EP-L2e"/>
+                                    </constraints>
+                                </imageView>
+                                <button opaque="NO" tag="1001" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ac9-lB-T0k">
+                                    <rect key="frame" x="0.0" y="0.0" width="112.33333333333333" height="55"/>
+                                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                    <state key="normal">
+                                        <color key="titleColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="calibratedRGB"/>
+                                    </state>
+                                    <connections>
+                                        <action selector="buttonClickAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="IiH-Q8-iJf"/>
+                                    </connections>
+                                </button>
+                            </subviews>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <constraints>
+                                <constraint firstItem="cfT-LB-SNv" firstAttribute="centerX" secondItem="3ud-K6-gBw" secondAttribute="centerX" constant="-4" id="6fu-Rx-39P"/>
+                                <constraint firstAttribute="bottom" secondItem="Ac9-lB-T0k" secondAttribute="bottom" id="FjI-Y5-7yE"/>
+                                <constraint firstItem="cfT-LB-SNv" firstAttribute="centerY" secondItem="3ud-K6-gBw" secondAttribute="centerY" id="J0t-LD-eL1"/>
+                                <constraint firstItem="Ac9-lB-T0k" firstAttribute="leading" secondItem="3ud-K6-gBw" secondAttribute="leading" id="g1O-Id-15a"/>
+                                <constraint firstItem="aFu-Ok-mVy" firstAttribute="centerY" secondItem="3ud-K6-gBw" secondAttribute="centerY" id="gDl-5L-mjU"/>
+                                <constraint firstItem="aFu-Ok-mVy" firstAttribute="leading" secondItem="cfT-LB-SNv" secondAttribute="trailing" constant="5" id="llT-OJ-w6i"/>
+                                <constraint firstItem="Ac9-lB-T0k" firstAttribute="top" secondItem="3ud-K6-gBw" secondAttribute="top" id="ofj-Sg-mn8"/>
+                                <constraint firstAttribute="trailing" secondItem="Ac9-lB-T0k" secondAttribute="trailing" id="tlR-cR-LCw"/>
+                            </constraints>
+                        </view>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Z8Q-fT-2Vr">
+                            <rect key="frame" x="122.33333333333334" y="0.0" width="112.33333333333334" height="55"/>
+                            <subviews>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="全部状态" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vxK-r7-9sg">
+                                    <rect key="frame" x="24.333333333333314" y="19" width="56" height="17"/>
+                                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                    <color key="textColor" red="0.074509803921568626" green="0.078431372549019607" blue="0.082352941176470587" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="sort_down_grey" translatesAutoresizingMaskIntoConstraints="NO" id="idf-14-rMz">
+                                    <rect key="frame" x="85.333333333333314" y="25" width="9" height="5"/>
+                                    <constraints>
+                                        <constraint firstAttribute="width" constant="9" id="2rB-eK-C35"/>
+                                        <constraint firstAttribute="height" constant="5" id="PIL-WN-xkd"/>
+                                    </constraints>
+                                </imageView>
+                                <button opaque="NO" tag="1002" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="j1t-mT-XyA">
+                                    <rect key="frame" x="0.0" y="0.0" width="112.33333333333333" height="55"/>
+                                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                    <state key="normal">
+                                        <color key="titleColor" red="0.1019607843" green="0.1019607843" blue="0.1019607843" alpha="1" colorSpace="calibratedRGB"/>
+                                    </state>
+                                    <connections>
+                                        <action selector="buttonClickAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="o6Z-q3-BLc"/>
+                                    </connections>
+                                </button>
+                            </subviews>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <constraints>
+                                <constraint firstItem="j1t-mT-XyA" firstAttribute="leading" secondItem="Z8Q-fT-2Vr" secondAttribute="leading" id="0ib-n1-ca3"/>
+                                <constraint firstAttribute="bottom" secondItem="j1t-mT-XyA" secondAttribute="bottom" id="FZL-fn-2Dt"/>
+                                <constraint firstItem="vxK-r7-9sg" firstAttribute="centerX" secondItem="Z8Q-fT-2Vr" secondAttribute="centerX" constant="-4" id="MaH-dZ-7ix"/>
+                                <constraint firstItem="vxK-r7-9sg" firstAttribute="centerY" secondItem="Z8Q-fT-2Vr" secondAttribute="centerY" id="VqG-LO-GJj"/>
+                                <constraint firstAttribute="trailing" secondItem="j1t-mT-XyA" secondAttribute="trailing" id="VxY-bB-unG"/>
+                                <constraint firstItem="j1t-mT-XyA" firstAttribute="top" secondItem="Z8Q-fT-2Vr" secondAttribute="top" id="cMc-ie-YnM"/>
+                                <constraint firstItem="idf-14-rMz" firstAttribute="leading" secondItem="vxK-r7-9sg" secondAttribute="trailing" constant="5" id="dLB-Vt-NCZ"/>
+                                <constraint firstItem="idf-14-rMz" firstAttribute="centerY" secondItem="Z8Q-fT-2Vr" secondAttribute="centerY" id="yDm-pc-vUp"/>
+                            </constraints>
+                        </view>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yMy-1B-zxV">
+                            <rect key="frame" x="244.66666666666671" y="0.0" width="112.33333333333334" height="55"/>
+                            <subviews>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="全部考勤" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9Xc-2m-8mt">
+                                    <rect key="frame" x="24.333333333333314" y="19" width="56" height="17"/>
+                                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                    <color key="textColor" red="0.074509803921568626" green="0.078431372549019607" blue="0.082352941176470587" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="sort_down_grey" translatesAutoresizingMaskIntoConstraints="NO" id="grW-VA-qlz">
+                                    <rect key="frame" x="85.333333333333314" y="25" width="9" height="5"/>
+                                    <constraints>
+                                        <constraint firstAttribute="height" constant="5" id="hAG-sG-mv7"/>
+                                        <constraint firstAttribute="width" constant="9" id="sqn-jl-TbO"/>
+                                    </constraints>
+                                </imageView>
+                                <button opaque="NO" tag="1003" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5J6-Qp-5o7">
+                                    <rect key="frame" x="0.0" y="0.0" width="112.33333333333333" height="55"/>
+                                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                    <state key="normal">
+                                        <color key="titleColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="calibratedRGB"/>
+                                    </state>
+                                    <connections>
+                                        <action selector="buttonClickAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="8YM-XU-MyE"/>
+                                    </connections>
+                                </button>
+                            </subviews>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <constraints>
+                                <constraint firstItem="5J6-Qp-5o7" firstAttribute="leading" secondItem="yMy-1B-zxV" secondAttribute="leading" id="0pe-f0-slu"/>
+                                <constraint firstItem="grW-VA-qlz" firstAttribute="centerY" secondItem="yMy-1B-zxV" secondAttribute="centerY" id="47f-2b-PEp"/>
+                                <constraint firstItem="9Xc-2m-8mt" firstAttribute="centerX" secondItem="yMy-1B-zxV" secondAttribute="centerX" constant="-4" id="4bT-SA-PhH"/>
+                                <constraint firstAttribute="trailing" secondItem="5J6-Qp-5o7" secondAttribute="trailing" id="8jw-zM-6iK"/>
+                                <constraint firstItem="5J6-Qp-5o7" firstAttribute="top" secondItem="yMy-1B-zxV" secondAttribute="top" id="HUX-nk-toA"/>
+                                <constraint firstAttribute="bottom" secondItem="5J6-Qp-5o7" secondAttribute="bottom" id="SLT-UZ-dPb"/>
+                                <constraint firstItem="9Xc-2m-8mt" firstAttribute="centerY" secondItem="yMy-1B-zxV" secondAttribute="centerY" id="jhO-f9-KY6"/>
+                                <constraint firstItem="grW-VA-qlz" firstAttribute="leading" secondItem="9Xc-2m-8mt" secondAttribute="trailing" constant="5" id="m1B-rA-bFZ"/>
+                            </constraints>
+                        </view>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="Z8Q-fT-2Vr" firstAttribute="top" secondItem="3ud-K6-gBw" secondAttribute="top" id="7Vc-Lw-Cz5"/>
+                        <constraint firstItem="yMy-1B-zxV" firstAttribute="top" secondItem="3ud-K6-gBw" secondAttribute="top" id="7vF-v2-rbp"/>
+                        <constraint firstItem="3ud-K6-gBw" firstAttribute="top" secondItem="nUQ-28-ciH" secondAttribute="top" id="Kwb-PF-i4E"/>
+                        <constraint firstAttribute="trailing" secondItem="yMy-1B-zxV" secondAttribute="trailing" id="a8j-cM-Bk3"/>
+                        <constraint firstItem="Z8Q-fT-2Vr" firstAttribute="width" secondItem="3ud-K6-gBw" secondAttribute="width" id="dvD-UR-61H"/>
+                        <constraint firstItem="Z8Q-fT-2Vr" firstAttribute="leading" secondItem="3ud-K6-gBw" secondAttribute="trailing" constant="10" id="g49-fx-rbj"/>
+                        <constraint firstAttribute="bottom" secondItem="3ud-K6-gBw" secondAttribute="bottom" id="jS1-OF-iaY"/>
+                        <constraint firstItem="yMy-1B-zxV" firstAttribute="leading" secondItem="Z8Q-fT-2Vr" secondAttribute="trailing" constant="10" id="obg-cI-Ey6"/>
+                        <constraint firstItem="3ud-K6-gBw" firstAttribute="leading" secondItem="nUQ-28-ciH" secondAttribute="leading" id="pPn-cT-EMU"/>
+                        <constraint firstItem="Z8Q-fT-2Vr" firstAttribute="bottom" secondItem="3ud-K6-gBw" secondAttribute="bottom" id="s1y-gn-zl5"/>
+                        <constraint firstItem="yMy-1B-zxV" firstAttribute="width" secondItem="3ud-K6-gBw" secondAttribute="width" id="uUy-6G-XIb"/>
+                        <constraint firstItem="yMy-1B-zxV" firstAttribute="bottom" secondItem="3ud-K6-gBw" secondAttribute="bottom" id="yBY-DV-CGv"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="size" keyPath="shadowOffset">
+                            <size key="value" width="0.0" height="2"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowOpacity">
+                            <real key="value" value="1"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowRadius">
+                            <real key="value" value="7"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="color" keyPath="shadowUIColor">
+                            <color key="value" red="0.0" green="0.0" blue="0.0" alpha="0.070000000000000007" colorSpace="calibratedRGB"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstAttribute="bottom" secondItem="nUQ-28-ciH" secondAttribute="bottom" id="8Ge-mS-YzV"/>
+                <constraint firstAttribute="trailing" secondItem="nUQ-28-ciH" secondAttribute="trailing" constant="14" id="9yc-y0-4fF"/>
+                <constraint firstItem="nUQ-28-ciH" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="14" id="igM-Q3-fAI"/>
+                <constraint firstItem="nUQ-28-ciH" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="yVO-Aa-YHu"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="firstArrow" destination="aFu-Ok-mVy" id="HfH-fU-Kon"/>
+                <outlet property="firstLabel" destination="cfT-LB-SNv" id="fxC-FN-FHH"/>
+                <outlet property="secondArrow" destination="idf-14-rMz" id="vtq-9G-6Dc"/>
+                <outlet property="secondLabel" destination="vxK-r7-9sg" id="fKB-7V-W3z"/>
+                <outlet property="thirdArrow" destination="grW-VA-qlz" id="KLm-di-ir9"/>
+                <outlet property="thirdLabel" destination="9Xc-2m-8mt" id="1q0-e6-Dmr"/>
+            </connections>
+            <point key="canvasLocation" x="41.984732824427482" y="-148.94366197183101"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="sort_down_grey" width="9" height="5"/>
+    </resources>
+</document>

+ 6 - 4
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Controller/VIPCourseGroupViewController.h

@@ -6,10 +6,8 @@
 //
 
 #import "KSBaseViewController.h"
-#import <RecordCheckManager.h>
-#import "KSPremissionAlert.h"
-#import "OnlineClassManager.h"
-#import "HomeworkDetailModel.h"
+#import "JXCategoryView.h"
+#import "JXPagerView.h"
 
 NS_ASSUME_NONNULL_BEGIN
 
@@ -18,6 +16,10 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property (nonatomic, strong) NSString *courseGroupId; // 课程组ID
 
+@property (nonatomic, strong) JXPagerView *pagerView;
+@property (nonatomic, strong, readonly) JXCategoryTitleView *categoryView;
+@property (nonatomic, strong) NSArray <NSString *> *titles;
+
 @end
 
 NS_ASSUME_NONNULL_END

+ 191 - 65
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Controller/VIPCourseGroupViewController.m

@@ -6,20 +6,35 @@
 //
 
 #import "VIPCourseGroupViewController.h"
+#import "CourseGroupNavView.h"
+#import "JXCategoryView.h"
+#import "JXPagerListRefreshView.h"
+#import "ProgramCourseInfoView.h"
+#import "ProgramCourseGroupBodyView.h"
+#import "CourseGroupDetailModel.h"
 
-@interface VIPCourseGroupViewController ()
 
-@property (nonatomic, strong) NSString *courseId;
+@interface VIPCourseGroupViewController ()<JXPagerViewDelegate,JXCategoryViewDelegate>
 
-@property (nonatomic, strong) NSString *studentId;
+@property (nonatomic, strong) NSMutableArray *listViewArray;
 
-@property (nonatomic, assign) NSInteger joinRoomBeforeTime; // 上课开始时间
+@property (nonatomic, strong) CourseGroupNavView *navView;
 
-@property (nonatomic, assign) NSInteger quitRomeEndTime;    // 下课截止时间
+@property (nonatomic, assign) NSInteger headHeight;
 
-@property (nonatomic, strong) HomeworkDetailModel *homeworkModel;
+@property (nonatomic, assign) NSInteger topViewHeight;
 
-@property (nonatomic, strong) OnlineClassManager *classManager;
+@property (nonatomic, strong) ProgramCourseInfoView *infoView;
+
+@property (nonatomic, assign) BOOL isFirstLoad;
+
+@property (nonatomic, strong) CourseGroupDetailModel *detailModel;
+
+@property (nonatomic, assign) NSInteger enterStartTime;  // 开课前可进入时间配置(分钟)
+
+@property (nonatomic, assign) NSInteger quitEndTime;    // 结束后退出时间配置(分钟)
+
+@property (nonatomic, strong) UIImageView *bgImageView;
 
 @end
 
@@ -28,84 +43,195 @@
 - (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
+    self.ks_prefersNavigationBarHidden = YES;
+    [self configUI];
+    self.isFirstLoad = YES;
 }
 
-- (void)tryJoinRoom {
-    
-    NSDateFormatter *dateFormatter = [NSObject getDateformatter];
-    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
-    NSDate *beginDate = [dateFormatter dateFromString:self.homeworkModel.startTime];
-    NSDate *endDate = [dateFormatter dateFromString:self.homeworkModel.endTime];
-    NSDate *currentDate = [NSDate date];
-    NSTimeInterval beginTimeInterval = [beginDate timeIntervalSinceDate:currentDate];
-    NSTimeInterval endTimeInterval = [currentDate timeIntervalSinceDate:endDate];
-    if (beginTimeInterval <= self.joinRoomBeforeTime * 60 && endTimeInterval < 0) {
-        [self joinClassRoom];
-    }
-    else if (endTimeInterval > 0) {
-        [LOADING_MANAGER MBShowAUTOHidingInWindow:@"该课程已结束"];
-    }
-    else {
-        NSString *tipsString = [NSString stringWithFormat:@"课程还未开始,请在上课前%zd分钟进入", self.joinRoomBeforeTime];
-        [LOADING_MANAGER MBShowAUTOHidingInWindow:tipsString];
-    }
-}
-
-// 加入房间
-- (void)joinClassRoom {
+- (void)configUI {
+    [self.scrollView removeFromSuperview];
+    UIImage *bgImage = [UIImage imageNamed:@"courseDetail_bg"];
+    NSInteger height = (NSInteger)(bgImage.size.height / bgImage.size.width * KPortraitWidth);
+    self.bgImageView = [[UIImageView alloc] initWithImage:bgImage];
+    self.bgImageView.frame = CGRectMake(0, 0, KPortraitWidth, height);
+    [self.view addSubview:self.bgImageView];
     
-    // 加入房间前判断摄像头和麦克风逻辑
-    [RecordCheckManager checkCameraPremissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        [self afterCheckCameraCheckMic:type];
+    [self.view addSubview:self.navView];
+    CGFloat navHeight = [CourseGroupNavView getViewHeight];
+    [self.navView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.top.mas_equalTo(self.view);
+        make.height.mas_equalTo(navHeight);
     }];
+    self.headHeight = CGFLOAT_MIN;
+    self.titles = @[@""];
+    [self configCategoryView:navHeight];
 }
 
-- (void)afterCheckCameraCheckMic:(PREMISSIONTYPE)cameraType {
-    [RecordCheckManager checkMicPermissionAvaiableCallback:^(PREMISSIONTYPE type) {
-        if (type == PREMISSIONTYPE_YES && cameraType == PREMISSIONTYPE_YES) {
-            // 判断是否进行课前检测
-            [self.classManager joinRoomWithId:self.courseId subjectName:self.homeworkModel.subjectName classEndTime:self.homeworkModel.endTime inViewController:self];
+- (void)requestCourseInfo {
+    
+    [KSNetworkingManager getCourseGroupDetailRequest:KS_POST courseGroupId:self.courseGroupId success:^(NSDictionary * _Nonnull dic) {
+        if ([dic ks_integerValueForKey:@"code"] == 200 && [dic ks_boolValueForKey:@"status"]) {
+            NSDictionary *parm = [dic ks_dictionaryValueForKey:@"data"];
+            self.detailModel = [[CourseGroupDetailModel alloc] initWithDictionary:parm];
+            
+            // 配置信息
+            NSDictionary *config = [[dic ks_dictionaryValueForKey:@"data"] ks_dictionaryValueForKey:@"sysConfig"];
+            self.enterStartTime = [config ks_integerValueForKey:@"vipStartTime"];
+            self.quitEndTime = [config ks_integerValueForKey:@"vipEndTime"];
         }
         else {
-            NSString *content = @"";
-            CHECKDEVICETYPE checkType = CHECKDEVICETYPE_BOTH;
-            if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_NO) {
-                content = @"请开启相机和麦克风访问权限";
-                checkType = CHECKDEVICETYPE_BOTH;
-            }
-            else if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_YES) {
-                content =  @"请开启相机访问权限";
-                checkType = CHECKDEVICETYPE_CAMREA;
-            }
-            else if (cameraType == PREMISSIONTYPE_YES && type == PREMISSIONTYPE_NO) {
-                content = @"请开启麦克风访问权限";
-                checkType = CHECKDEVICETYPE_MIC;
-            }
-            [self showAlertWithMessage:content type:checkType];
+            [LOADING_MANAGER MBShowAUTOHidingInWindow:MESSAGEKEY];
         }
+        [self refreshHeadView];
+    } faliure:^(NSError * _Nonnull error) {
+        [self refreshHeadView];
     }];
+}
+
+- (void)refreshHeadView {
+    [self refreshListPage];
+    self.topViewHeight = [ProgramCourseInfoView getViewHeightWithPlanString:self.detailModel.coursePlan];
+    [self.infoView configWithSource:self.detailModel];
+    [self.pagerView reloadData];
+}
+
+- (void)configCategoryView:(NSInteger)topHeight {
+    
+    _categoryView = [[JXCategoryTitleView alloc] initWithFrame:CGRectMake(0, topHeight, KPortraitWidth, self.headHeight)];
+    self.categoryView.titles = self.titles;
+    self.categoryView.delegate = self;
+    self.categoryView.titleFont = [UIFont systemFontOfSize:16.0f];
+    self.categoryView.titleSelectedFont = [UIFont systemFontOfSize:16.0f weight:UIFontWeightSemibold];
+    self.categoryView.titleSelectedColor = HexRGB(0x333333);
+    self.categoryView.titleColor = HexRGB(0x666666);
+    self.categoryView.titleColorGradientEnabled = YES;
     
+    _pagerView = [self preferredPagingView];
+    self.pagerView.frame = CGRectMake(0, topHeight, KPortraitWidth, KPortraitHeight - topHeight);
+    self.pagerView.backgroundColor = [UIColor clearColor];
+    self.pagerView.mainTableView.backgroundColor = [UIColor clearColor];
+    self.pagerView.listContainerView.backgroundColor = [UIColor clearColor];
+//    self.pagerView.mainTableView.gestureDelegate = self;
+    self.categoryView.listContainer = (id<JXCategoryViewListContainer>)self.pagerView.listContainerView;
+    self.pagerView.listContainerView.listCellBackgroundColor = [UIColor clearColor];
+    [self.view addSubview:self.pagerView];
 }
 
-- (void)showAlertWithMessage:(NSString *)message type:(CHECKDEVICETYPE)deviceType {
-    [KSPremissionAlert shareInstanceDisplayImage:deviceType message:message showInView:self.view cancel:^{
-        
-    } confirm:^{
-        [self openSettingView];
-    }];
+- (void)viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    self.navigationController.interactivePopGestureRecognizer.enabled = (self.categoryView.selectedIndex == 0);
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+    [super viewWillAppear:animated];
+    self.navigationController.interactivePopGestureRecognizer.enabled = YES;
+    // 刷新课程详情
+    [self requestCourseInfo];
+    if (self.isFirstLoad == NO) {
+        [self refreshListPage];
+    }
+    self.isFirstLoad = NO;
+}
+
+- (void)refreshListPage {
+    id value = self.listViewArray[0];
+    if ([value isKindOfClass:[ProgramCourseGroupBodyView class]]) {
+        ProgramCourseGroupBodyView *listView = (ProgramCourseGroupBodyView *)value;
+        listView.enterStartTime = self.enterStartTime;
+        listView.quitEndTime = self.quitEndTime;
+        [listView beginFirstRefresh];
+    }
+}
+
+- (JXPagerView *)preferredPagingView {
+    return [[JXPagerListRefreshView alloc] initWithDelegate:self];
+}
+
+#pragma mark - JXPagerViewDelegate
+
+- (UIView *)tableHeaderViewInPagerView:(JXPagerView *)pagerView {
+    return self.infoView;
+}
+
+- (NSUInteger)tableHeaderViewHeightInPagerView:(JXPagerView *)pagerView {
+    return self.topViewHeight;
+}
+
+- (NSUInteger)heightForPinSectionHeaderInPagerView:(JXPagerView *)pagerView {
+    return self.headHeight;
+}
+
+- (UIView *)viewForPinSectionHeaderInPagerView:(JXPagerView *)pagerView {
+    return self.categoryView;
+}
+
+- (NSInteger)numberOfListsInPagerView:(JXPagerView *)pagerView {
+    //和categoryView的item数量一致
+    return self.titles.count;
+}
+
+- (id<JXPagerViewListViewDelegate>)pagerView:(JXPagerView *)pagerView initListAtIndex:(NSInteger)index {
+    ProgramCourseGroupBodyView *listView = [[ProgramCourseGroupBodyView alloc] init];
+    listView.naviController = self.navigationController;
+    listView.selectIndex = index;
+    listView.courseGroupId = self.courseGroupId;
+    listView.courseType = COURSE_GROUP_TYPE_VIP;
+    [self.listViewArray replaceObjectAtIndex:index withObject:listView];
+    self.listViewArray[index] = listView;
+    [listView beginFirstRefresh];
+    return listView;
+}
+
+- (void)pagerView:(JXPagerView *)pagerView mainTableViewDidScroll:(UIScrollView *)scrollView {
     
+    NSLog(@"sscroll content offset y %f", scrollView.contentOffset.y);
+    CGFloat height = self.topViewHeight;
+    CGFloat space = scrollView.contentOffset.y;
+    if (space > 100) {
+        CGFloat rate = (space - 100) / (height - 100);
+        NSLog(@"rate ---------%f",rate);
+        self.navView.backgroundColor = HexRGBAlpha(0xffffff, rate);
+        if (rate == 1) {
+            self.bgImageView.hidden = YES;
+        }
+        else {
+            self.bgImageView.hidden = NO;
+        }
+    }
+    else {
+        self.navView.backgroundColor = [UIColor clearColor];
+        self.bgImageView.hidden = NO;
+    }
 }
 
-- (void)openSettingView {
-    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
+- (void)dealloc {
+   [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 
 #pragma mark ----- lazying
-- (OnlineClassManager *)classManager {
-    if (!_classManager) {
-        _classManager = [[OnlineClassManager alloc] init];
+
+- (CourseGroupNavView *)navView {
+    if (!_navView) {
+        _navView = [CourseGroupNavView sharedInstance];
+        MJWeakSelf;
+        [_navView configWithNavTitle:@"VIP定制课设置" callback:^{
+            [weakSelf backAction];
+        }];
+    }
+    return _navView;
+}
+- (NSMutableArray *)listViewArray {
+    if (!_listViewArray) {
+        _listViewArray = [NSMutableArray arrayWithArray:@[@""]];
+    }
+    return _listViewArray;
+}
+
+- (ProgramCourseInfoView *)infoView {
+    if (!_infoView) {
+        _infoView  =[ProgramCourseInfoView sharedInstance];
     }
-    return _classManager;
+    return _infoView;
 }
 
 /*

+ 0 - 2
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Controller/VipCouseDetailViewController.h

@@ -15,8 +15,6 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property (nonatomic, strong) NSString *courseGroupId;
 
-@property (nonatomic, strong) NSString *studentId;
-
 @property (nonatomic, assign) NSInteger joinRoomBeforeTime; // 上课开始时间
 
 @property (nonatomic, assign) NSInteger quitRomeEndTime;    // 下课截止时间

+ 30 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/CourseGroupDetailModel.h

@@ -0,0 +1,30 @@
+//
+//  CourseGroupDetailModel.h
+//
+//  Created by Steven  on 2024/11/22
+//  Copyright (c) 2024 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+
+@interface CourseGroupDetailModel : NSObject <NSCoding, NSCopying>
+
+@property (nonatomic, strong) NSString *status;
+@property (nonatomic, strong) NSString *coursePlan;
+@property (nonatomic, strong) NSString *courseGroupId;
+@property (nonatomic, strong) NSString *courseGroupName;
+@property (nonatomic, assign) double completeCourseNum;
+@property (nonatomic, strong) NSString *subjectId;
+@property (nonatomic, assign) double studentNum;
+@property (nonatomic, strong) NSString *subjectName;
+@property (nonatomic, strong) NSString *studentName;
+@property (nonatomic, assign) double courseNum;
+@property (nonatomic, strong) NSString *studentAvatar;
+
++ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
+- (instancetype)initWithDictionary:(NSDictionary *)dict;
+- (NSDictionary *)dictionaryRepresentation;
+
+@end

+ 169 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/CourseGroupDetailModel.m

@@ -0,0 +1,169 @@
+//
+//  CourseGroupDetailModel.m
+//
+//  Created by Steven  on 2024/11/22
+//  Copyright (c) 2024 __MyCompanyName__. All rights reserved.
+//
+
+#import "CourseGroupDetailModel.h"
+
+
+NSString *const kCourseGroupDetailModelStatus = @"status";
+NSString *const kCourseGroupDetailModelCoursePlan = @"coursePlan";
+NSString *const kCourseGroupDetailModelCourseGroupId = @"courseGroupId";
+NSString *const kCourseGroupDetailModelCourseGroupName = @"courseGroupName";
+NSString *const kCourseGroupDetailModelCompleteCourseNum = @"completeCourseNum";
+NSString *const kCourseGroupDetailModelSubjectId = @"subjectId";
+NSString *const kCourseGroupDetailModelStudentNum = @"studentNum";
+NSString *const kCourseGroupDetailModelSubjectName = @"subjectName";
+NSString *const kCourseGroupDetailModelStudentName = @"studentName";
+NSString *const kCourseGroupDetailModelCourseNum = @"courseNum";
+NSString *const kCourseGroupDetailModelStudentAvatar = @"studentAvatar";
+
+
+@interface CourseGroupDetailModel ()
+
+- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
+
+@end
+
+@implementation CourseGroupDetailModel
+
+@synthesize status = _status;
+@synthesize coursePlan = _coursePlan;
+@synthesize courseGroupId = _courseGroupId;
+@synthesize courseGroupName = _courseGroupName;
+@synthesize completeCourseNum = _completeCourseNum;
+@synthesize subjectId = _subjectId;
+@synthesize studentNum = _studentNum;
+@synthesize subjectName = _subjectName;
+@synthesize studentName = _studentName;
+@synthesize courseNum = _courseNum;
+@synthesize studentAvatar = _studentAvatar;
+
+
++ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict
+{
+    return [[self alloc] initWithDictionary:dict];
+}
+
+- (instancetype)initWithDictionary:(NSDictionary *)dict
+{
+    self = [super init];
+    
+    // This check serves to make sure that a non-NSDictionary object
+    // passed into the model class doesn't break the parsing.
+    if(self && [dict isKindOfClass:[NSDictionary class]]) {
+            self.status = [self objectOrNilForKey:kCourseGroupDetailModelStatus fromDictionary:dict];
+            self.coursePlan = [self objectOrNilForKey:kCourseGroupDetailModelCoursePlan fromDictionary:dict];
+            self.courseGroupId = [self objectOrNilForKey:kCourseGroupDetailModelCourseGroupId fromDictionary:dict];
+            self.courseGroupName = [self objectOrNilForKey:kCourseGroupDetailModelCourseGroupName fromDictionary:dict];
+            self.completeCourseNum = [[self objectOrNilForKey:kCourseGroupDetailModelCompleteCourseNum fromDictionary:dict] doubleValue];
+            self.subjectId = [self objectOrNilForKey:kCourseGroupDetailModelSubjectId fromDictionary:dict];
+            self.studentNum = [[self objectOrNilForKey:kCourseGroupDetailModelStudentNum fromDictionary:dict] doubleValue];
+            self.subjectName = [self objectOrNilForKey:kCourseGroupDetailModelSubjectName fromDictionary:dict];
+            self.studentName = [self objectOrNilForKey:kCourseGroupDetailModelStudentName fromDictionary:dict];
+            self.courseNum = [[self objectOrNilForKey:kCourseGroupDetailModelCourseNum fromDictionary:dict] doubleValue];
+            self.studentAvatar = [self objectOrNilForKey:kCourseGroupDetailModelStudentAvatar fromDictionary:dict];
+
+    }
+    
+    return self;
+    
+}
+
+- (NSDictionary *)dictionaryRepresentation
+{
+    NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
+    [mutableDict setValue:self.status forKey:kCourseGroupDetailModelStatus];
+    [mutableDict setValue:self.coursePlan forKey:kCourseGroupDetailModelCoursePlan];
+    [mutableDict setValue:self.courseGroupId forKey:kCourseGroupDetailModelCourseGroupId];
+    [mutableDict setValue:self.courseGroupName forKey:kCourseGroupDetailModelCourseGroupName];
+    [mutableDict setValue:[NSNumber numberWithDouble:self.completeCourseNum] forKey:kCourseGroupDetailModelCompleteCourseNum];
+    [mutableDict setValue:self.subjectId forKey:kCourseGroupDetailModelSubjectId];
+    [mutableDict setValue:[NSNumber numberWithDouble:self.studentNum] forKey:kCourseGroupDetailModelStudentNum];
+    [mutableDict setValue:self.subjectName forKey:kCourseGroupDetailModelSubjectName];
+    [mutableDict setValue:self.studentName forKey:kCourseGroupDetailModelStudentName];
+    [mutableDict setValue:[NSNumber numberWithDouble:self.courseNum] forKey:kCourseGroupDetailModelCourseNum];
+    [mutableDict setValue:self.studentAvatar forKey:kCourseGroupDetailModelStudentAvatar];
+
+    return [NSDictionary dictionaryWithDictionary:mutableDict];
+}
+
+- (NSString *)description 
+{
+    return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
+}
+
+#pragma mark - Helper Method
+- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict
+{
+    id object = [dict objectForKey:aKey];
+    if ([object isKindOfClass:[NSNumber class]]) {
+        NSNumber *number = object;
+        object = [number stringValue];
+    }
+    return [object isEqual:[NSNull null]] ? nil : object;
+}
+
+
+#pragma mark - NSCoding Methods
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+    self = [super init];
+
+    self.status = [aDecoder decodeObjectForKey:kCourseGroupDetailModelStatus];
+    self.coursePlan = [aDecoder decodeObjectForKey:kCourseGroupDetailModelCoursePlan];
+    self.courseGroupId = [aDecoder decodeObjectForKey:kCourseGroupDetailModelCourseGroupId];
+    self.courseGroupName = [aDecoder decodeObjectForKey:kCourseGroupDetailModelCourseGroupName];
+    self.completeCourseNum = [aDecoder decodeDoubleForKey:kCourseGroupDetailModelCompleteCourseNum];
+    self.subjectId = [aDecoder decodeObjectForKey:kCourseGroupDetailModelSubjectId];
+    self.studentNum = [aDecoder decodeDoubleForKey:kCourseGroupDetailModelStudentNum];
+    self.subjectName = [aDecoder decodeObjectForKey:kCourseGroupDetailModelSubjectName];
+    self.studentName = [aDecoder decodeObjectForKey:kCourseGroupDetailModelStudentName];
+    self.courseNum = [aDecoder decodeDoubleForKey:kCourseGroupDetailModelCourseNum];
+    self.studentAvatar = [aDecoder decodeObjectForKey:kCourseGroupDetailModelStudentAvatar];
+    return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+
+    [aCoder encodeObject:_status forKey:kCourseGroupDetailModelStatus];
+    [aCoder encodeObject:_coursePlan forKey:kCourseGroupDetailModelCoursePlan];
+    [aCoder encodeObject:_courseGroupId forKey:kCourseGroupDetailModelCourseGroupId];
+    [aCoder encodeObject:_courseGroupName forKey:kCourseGroupDetailModelCourseGroupName];
+    [aCoder encodeDouble:_completeCourseNum forKey:kCourseGroupDetailModelCompleteCourseNum];
+    [aCoder encodeObject:_subjectId forKey:kCourseGroupDetailModelSubjectId];
+    [aCoder encodeDouble:_studentNum forKey:kCourseGroupDetailModelStudentNum];
+    [aCoder encodeObject:_subjectName forKey:kCourseGroupDetailModelSubjectName];
+    [aCoder encodeObject:_studentName forKey:kCourseGroupDetailModelStudentName];
+    [aCoder encodeDouble:_courseNum forKey:kCourseGroupDetailModelCourseNum];
+    [aCoder encodeObject:_studentAvatar forKey:kCourseGroupDetailModelStudentAvatar];
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+    CourseGroupDetailModel *copy = [[CourseGroupDetailModel alloc] init];
+    
+    if (copy) {
+
+        copy.status = [self.status copyWithZone:zone];
+        copy.coursePlan = [self.coursePlan copyWithZone:zone];
+        copy.courseGroupId = [self.courseGroupId copyWithZone:zone];
+        copy.courseGroupName = [self.courseGroupName copyWithZone:zone];
+        copy.completeCourseNum = self.completeCourseNum;
+        copy.subjectId = [self.subjectId copyWithZone:zone];
+        copy.studentNum = self.studentNum;
+        copy.subjectName = [self.subjectName copyWithZone:zone];
+        copy.studentName = [self.studentName copyWithZone:zone];
+        copy.courseNum = self.courseNum;
+        copy.studentAvatar = [self.studentAvatar copyWithZone:zone];
+    }
+    
+    return copy;
+}
+
+
+@end

+ 23 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/CourseStudentVos.h

@@ -0,0 +1,23 @@
+//
+//  CourseStudentVos.h
+//
+//  Created by Steven  on 2024/11/22
+//  Copyright (c) 2024 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+
+@interface CourseStudentVos : NSObject <NSCoding, NSCopying>
+
+@property (nonatomic, strong) NSString *avatar;
+@property (nonatomic, strong) NSString *userId;
+@property (nonatomic, strong) NSString *imUserId;
+@property (nonatomic, strong) NSString *userName;
+
++ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
+- (instancetype)initWithDictionary:(NSDictionary *)dict;
+- (NSDictionary *)dictionaryRepresentation;
+
+@end

+ 120 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/CourseStudentVos.m

@@ -0,0 +1,120 @@
+//
+//  CourseStudentVos.m
+//
+//  Created by Steven  on 2024/11/22
+//  Copyright (c) 2024 __MyCompanyName__. All rights reserved.
+//
+
+#import "CourseStudentVos.h"
+
+
+NSString *const kCourseStudentVosAvatar = @"avatar";
+NSString *const kCourseStudentVosUserId = @"userId";
+NSString *const kCourseStudentVosImUserId = @"imUserId";
+NSString *const kCourseStudentVosUserName = @"userName";
+
+
+@interface CourseStudentVos ()
+
+- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
+
+@end
+
+@implementation CourseStudentVos
+
+@synthesize avatar = _avatar;
+@synthesize userId = _userId;
+@synthesize imUserId = _imUserId;
+@synthesize userName = _userName;
+
+
++ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict
+{
+    return [[self alloc] initWithDictionary:dict];
+}
+
+- (instancetype)initWithDictionary:(NSDictionary *)dict
+{
+    self = [super init];
+    
+    // This check serves to make sure that a non-NSDictionary object
+    // passed into the model class doesn't break the parsing.
+    if(self && [dict isKindOfClass:[NSDictionary class]]) {
+            self.avatar = [self objectOrNilForKey:kCourseStudentVosAvatar fromDictionary:dict];
+            self.userId = [self objectOrNilForKey:kCourseStudentVosUserId fromDictionary:dict];
+            self.imUserId = [self objectOrNilForKey:kCourseStudentVosImUserId fromDictionary:dict];
+            self.userName = [self objectOrNilForKey:kCourseStudentVosUserName fromDictionary:dict];
+
+    }
+    
+    return self;
+    
+}
+
+- (NSDictionary *)dictionaryRepresentation
+{
+    NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
+    [mutableDict setValue:self.avatar forKey:kCourseStudentVosAvatar];
+    [mutableDict setValue:self.userId forKey:kCourseStudentVosUserId];
+    [mutableDict setValue:self.imUserId forKey:kCourseStudentVosImUserId];
+    [mutableDict setValue:self.userName forKey:kCourseStudentVosUserName];
+
+    return [NSDictionary dictionaryWithDictionary:mutableDict];
+}
+
+- (NSString *)description 
+{
+    return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
+}
+
+#pragma mark - Helper Method
+- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict
+{
+    id object = [dict objectForKey:aKey];
+    if ([object isKindOfClass:[NSNumber class]]) {
+        NSNumber *number = object;
+        object = [number stringValue];
+    }
+    return [object isEqual:[NSNull null]] ? nil : object;
+}
+
+
+#pragma mark - NSCoding Methods
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+    self = [super init];
+
+    self.avatar = [aDecoder decodeObjectForKey:kCourseStudentVosAvatar];
+    self.userId = [aDecoder decodeObjectForKey:kCourseStudentVosUserId];
+    self.imUserId = [aDecoder decodeObjectForKey:kCourseStudentVosImUserId];
+    self.userName = [aDecoder decodeObjectForKey:kCourseStudentVosUserName];
+    return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+
+    [aCoder encodeObject:_avatar forKey:kCourseStudentVosAvatar];
+    [aCoder encodeObject:_userId forKey:kCourseStudentVosUserId];
+    [aCoder encodeObject:_imUserId forKey:kCourseStudentVosImUserId];
+    [aCoder encodeObject:_userName forKey:kCourseStudentVosUserName];
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+    CourseStudentVos *copy = [[CourseStudentVos alloc] init];
+    
+    if (copy) {
+
+        copy.avatar = [self.avatar copyWithZone:zone];
+        copy.userId = [self.userId copyWithZone:zone];
+        copy.imUserId = [self.imUserId copyWithZone:zone];
+        copy.userName = [self.userName copyWithZone:zone];
+    }
+    
+    return copy;
+}
+
+
+@end

+ 29 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/GroupCourseListModel.h

@@ -0,0 +1,29 @@
+//
+//  GroupCourseListModel.h
+//
+//  Created by Steven  on 2024/11/22
+//  Copyright (c) 2024 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "CourseStudentVos.h"
+
+
+@interface GroupCourseListModel : NSObject <NSCoding, NSCopying>
+
+@property (nonatomic, strong) NSString *status;
+@property (nonatomic, strong) NSString *classDate;
+@property (nonatomic, strong) NSString *endTime;
+@property (nonatomic, strong) NSString *courseId;
+@property (nonatomic, strong) NSArray *courseStudentVos;
+@property (nonatomic, strong) NSString *courseGoupId;
+@property (nonatomic, strong) NSString *startTime;
+@property (nonatomic, strong) NSString *courseGroupName;
+@property (nonatomic, assign) BOOL attendanceStatus;
+@property (nonatomic, strong) NSString *subjectName;
+
++ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
+- (instancetype)initWithDictionary:(NSDictionary *)dict;
+- (NSDictionary *)dictionaryRepresentation;
+
+@end

+ 181 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/Model/GroupCourseListModel.m

@@ -0,0 +1,181 @@
+//
+//  GroupCourseListModel.m
+//
+//  Created by Steven  on 2024/11/22
+//  Copyright (c) 2024 __MyCompanyName__. All rights reserved.
+//
+
+#import "GroupCourseListModel.h"
+
+
+NSString *const kGroupCourseListModelStatus = @"status";
+NSString *const kGroupCourseListModelClassDate = @"classDate";
+NSString *const kGroupCourseListModelEndTime = @"endTime";
+NSString *const kGroupCourseListModelCourseId = @"courseId";
+NSString *const kGroupCourseListModelCourseStudentVos = @"courseStudentVos";
+NSString *const kGroupCourseListModelCourseGoupId = @"courseGoupId";
+NSString *const kGroupCourseListModelStartTime = @"startTime";
+NSString *const kGroupCourseListModelCourseGroupName = @"courseGroupName";
+NSString *const kGroupCourseListModelAttendanceStatus = @"attendanceStatus";
+NSString *const kGroupCourseListModelSubjectName = @"subjectName";
+
+@interface GroupCourseListModel ()
+
+- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
+
+@end
+
+@implementation GroupCourseListModel
+
+@synthesize status = _status;
+@synthesize classDate = _classDate;
+@synthesize endTime = _endTime;
+@synthesize courseId = _courseId;
+@synthesize courseStudentVos = _courseStudentVos;
+@synthesize courseGoupId = _courseGoupId;
+@synthesize startTime = _startTime;
+@synthesize courseGroupName = _courseGroupName;
+@synthesize attendanceStatus = _attendanceStatus;
+@synthesize subjectName = _subjectName;
+
++ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict
+{
+    return [[self alloc] initWithDictionary:dict];
+}
+
+- (instancetype)initWithDictionary:(NSDictionary *)dict
+{
+    self = [super init];
+    
+    // This check serves to make sure that a non-NSDictionary object
+    // passed into the model class doesn't break the parsing.
+    if(self && [dict isKindOfClass:[NSDictionary class]]) {
+            self.status = [self objectOrNilForKey:kGroupCourseListModelStatus fromDictionary:dict];
+            self.classDate = [self objectOrNilForKey:kGroupCourseListModelClassDate fromDictionary:dict];
+            self.endTime = [self objectOrNilForKey:kGroupCourseListModelEndTime fromDictionary:dict];
+            self.courseId = [self objectOrNilForKey:kGroupCourseListModelCourseId fromDictionary:dict];
+    NSObject *receivedCourseStudentVos = [dict objectForKey:kGroupCourseListModelCourseStudentVos];
+    NSMutableArray *parsedCourseStudentVos = [NSMutableArray array];
+    if ([receivedCourseStudentVos isKindOfClass:[NSArray class]]) {
+        for (NSDictionary *item in (NSArray *)receivedCourseStudentVos) {
+            if ([item isKindOfClass:[NSDictionary class]]) {
+                [parsedCourseStudentVos addObject:[CourseStudentVos modelObjectWithDictionary:item]];
+            }
+       }
+    } else if ([receivedCourseStudentVos isKindOfClass:[NSDictionary class]]) {
+       [parsedCourseStudentVos addObject:[CourseStudentVos modelObjectWithDictionary:(NSDictionary *)receivedCourseStudentVos]];
+    }
+
+    self.courseStudentVos = [NSArray arrayWithArray:parsedCourseStudentVos];
+            self.courseGoupId = [self objectOrNilForKey:kGroupCourseListModelCourseGoupId fromDictionary:dict];
+            self.startTime = [self objectOrNilForKey:kGroupCourseListModelStartTime fromDictionary:dict];
+            self.courseGroupName = [self objectOrNilForKey:kGroupCourseListModelCourseGroupName fromDictionary:dict];
+            self.attendanceStatus = [[self objectOrNilForKey:kGroupCourseListModelAttendanceStatus fromDictionary:dict] boolValue];
+
+    }
+    self.subjectName = [self objectOrNilForKey:kGroupCourseListModelSubjectName fromDictionary:dict];
+    return self;
+    
+}
+
+- (NSDictionary *)dictionaryRepresentation
+{
+    NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
+    [mutableDict setValue:self.status forKey:kGroupCourseListModelStatus];
+    [mutableDict setValue:self.classDate forKey:kGroupCourseListModelClassDate];
+    [mutableDict setValue:self.endTime forKey:kGroupCourseListModelEndTime];
+    [mutableDict setValue:self.courseId forKey:kGroupCourseListModelCourseId];
+    NSMutableArray *tempArrayForCourseStudentVos = [NSMutableArray array];
+    for (NSObject *subArrayObject in self.courseStudentVos) {
+        if([subArrayObject respondsToSelector:@selector(dictionaryRepresentation)]) {
+            // This class is a model object
+            [tempArrayForCourseStudentVos addObject:[subArrayObject performSelector:@selector(dictionaryRepresentation)]];
+        } else {
+            // Generic object
+            [tempArrayForCourseStudentVos addObject:subArrayObject];
+        }
+    }
+    [mutableDict setValue:[NSArray arrayWithArray:tempArrayForCourseStudentVos] forKey:kGroupCourseListModelCourseStudentVos];
+    [mutableDict setValue:self.courseGoupId forKey:kGroupCourseListModelCourseGoupId];
+    [mutableDict setValue:self.startTime forKey:kGroupCourseListModelStartTime];
+    [mutableDict setValue:self.courseGroupName forKey:kGroupCourseListModelCourseGroupName];
+    [mutableDict setValue:[NSNumber numberWithBool:self.attendanceStatus] forKey:kGroupCourseListModelAttendanceStatus];
+    [mutableDict setValue:self.subjectName forKey:kGroupCourseListModelSubjectName];
+    
+    return [NSDictionary dictionaryWithDictionary:mutableDict];
+}
+
+- (NSString *)description 
+{
+    return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
+}
+
+#pragma mark - Helper Method
+- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict
+{
+    id object = [dict objectForKey:aKey];
+    if ([object isKindOfClass:[NSNumber class]]) {
+        NSNumber *number = object;
+        object = [number stringValue];
+    }
+    return [object isEqual:[NSNull null]] ? nil : object;
+}
+
+
+#pragma mark - NSCoding Methods
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+    self = [super init];
+
+    self.status = [aDecoder decodeObjectForKey:kGroupCourseListModelStatus];
+    self.classDate = [aDecoder decodeObjectForKey:kGroupCourseListModelClassDate];
+    self.endTime = [aDecoder decodeObjectForKey:kGroupCourseListModelEndTime];
+    self.courseId = [aDecoder decodeObjectForKey:kGroupCourseListModelCourseId];
+    self.courseStudentVos = [aDecoder decodeObjectForKey:kGroupCourseListModelCourseStudentVos];
+    self.courseGoupId = [aDecoder decodeObjectForKey:kGroupCourseListModelCourseGoupId];
+    self.startTime = [aDecoder decodeObjectForKey:kGroupCourseListModelStartTime];
+    self.courseGroupName = [aDecoder decodeObjectForKey:kGroupCourseListModelCourseGroupName];
+    self.attendanceStatus = [aDecoder decodeBoolForKey:kGroupCourseListModelAttendanceStatus];
+    self.subjectName = [aDecoder decodeObjectForKey:kGroupCourseListModelSubjectName];
+    return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+
+    [aCoder encodeObject:_status forKey:kGroupCourseListModelStatus];
+    [aCoder encodeObject:_classDate forKey:kGroupCourseListModelClassDate];
+    [aCoder encodeObject:_endTime forKey:kGroupCourseListModelEndTime];
+    [aCoder encodeObject:_courseId forKey:kGroupCourseListModelCourseId];
+    [aCoder encodeObject:_courseStudentVos forKey:kGroupCourseListModelCourseStudentVos];
+    [aCoder encodeObject:_courseGoupId forKey:kGroupCourseListModelCourseGoupId];
+    [aCoder encodeObject:_startTime forKey:kGroupCourseListModelStartTime];
+    [aCoder encodeObject:_courseGroupName forKey:kGroupCourseListModelCourseGroupName];
+    [aCoder encodeBool:_attendanceStatus forKey:kGroupCourseListModelAttendanceStatus];
+    [aCoder encodeObject:_subjectName forKey:kGroupCourseListModelSubjectName];
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+    GroupCourseListModel *copy = [[GroupCourseListModel alloc] init];
+    
+    if (copy) {
+
+        copy.status = [self.status copyWithZone:zone];
+        copy.classDate = [self.classDate copyWithZone:zone];
+        copy.endTime = [self.endTime copyWithZone:zone];
+        copy.courseId = [self.courseId copyWithZone:zone];
+        copy.courseStudentVos = [self.courseStudentVos copyWithZone:zone];
+        copy.courseGoupId = [self.courseGoupId copyWithZone:zone];
+        copy.startTime = [self.startTime copyWithZone:zone];
+        copy.courseGroupName = [self.courseGroupName copyWithZone:zone];
+        copy.attendanceStatus = self.attendanceStatus;
+        copy.subjectName = [self.subjectName copyWithZone:zone];
+    }
+    
+    return copy;
+}
+
+
+@end

+ 30 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseGroupBodyView.h

@@ -0,0 +1,30 @@
+//
+//  ProgramCourseGroupBodyView.h
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import "KSJXBodyView.h"
+
+typedef NS_ENUM(NSInteger, COURSE_GROUP_TYPE) {
+    COURSE_GROUP_TYPE_VIP,
+    COURSE_GROUP_TYPE_ACCOMPANY,
+    COURSE_GROUP_TYPE_MUSICROOM,
+};
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ProgramCourseGroupBodyView : KSJXBodyView
+
+@property (nonatomic, strong) NSString *courseGroupId;
+
+@property (nonatomic, assign) COURSE_GROUP_TYPE courseType;
+
+@property (nonatomic, assign) NSInteger enterStartTime;  // 开课前可进入时间配置(分钟)
+
+@property (nonatomic, assign) NSInteger quitEndTime;    // 结束后退出时间配置(分钟)
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 586 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseGroupBodyView.m

@@ -0,0 +1,586 @@
+//
+//  ProgramCourseGroupBodyView.m
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import "ProgramCourseGroupBodyView.h"
+#import "GroupCourseSortView.h"
+#import "StateView.h"
+#import "Reachability.h"
+#import "ProgramCourseListCell.h"
+#import "MusicRoomCourseListCell.h"
+#import "AccompanyDetailViewController.h"
+#import "VipCouseDetailViewController.h"
+#import "MusicRoomDetailViewController.h"
+#import "GroupCourseListModel.h"
+#import "OnlineClassManager.h"
+#import "KSPremissionAlert.h"
+#import "RecordCheckManager.h"
+#import <KSChoosePicker.h>
+#import <KSFullDatePicker.h>
+
+@interface ProgramCourseGroupBodyView ()<UITableViewDelegate,UITableViewDataSource>
+
+@property (nonatomic, strong) NSDateFormatter *dateFormatter;
+
+@property (nonatomic, strong) NSMutableArray *dataArray;
+
+@property (nonatomic, strong) StateView *promptView;
+@property (nonatomic, strong) UIView *promptPlaceView;
+
+@property (nonatomic, assign) BOOL networkAvaiable; // 网络是否可用
+
+@property (nonatomic, strong) GroupCourseSortView *sortView;
+
+@property (nonatomic, strong) NSString *classDate;
+
+@property (nonatomic, strong) NSString *attendanceStatus;
+
+@property (nonatomic, strong) NSString *courseStatus;
+
+@property (nonatomic, strong) OnlineClassManager *classManager;
+
+@property (nonatomic, assign) NSInteger secondChooseIndex;
+
+@property (nonatomic, assign) NSInteger thirdChooseIndex;
+
+@property (nonatomic, strong) NSMutableArray *statusArray;
+
+@property (nonatomic, strong) NSMutableArray *attendenceStatusArray;
+
+@property (nonatomic, assign) NSInteger joinRoomBeforeTime; // 上课开始时间
+
+@property (nonatomic, assign) NSInteger quitRomeEndTime;    // 下课截止时间
+
+@end
+
+@implementation ProgramCourseGroupBodyView
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    if (self) {
+        self.backgroundColor = [UIColor clearColor];
+        self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height) style:UITableViewStylePlain];
+        self.tableView.backgroundColor = [UIColor clearColor];
+        self.tableView.showsVerticalScrollIndicator = NO;
+        
+        self.tableView.dataSource = self;
+        self.tableView.delegate = self;
+        self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+        [self addSubview:self.tableView];
+        self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
+        [self.tableView registerNib:[UINib nibWithNibName:@"ProgramCourseListCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"ProgramCourseListCell"];
+        [self.tableView registerNib:[UINib nibWithNibName:@"MusicRoomCourseListCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"MusicRoomCourseListCell"];
+        
+        UIView *bottomView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KPortraitWidth, iPhoneXSafeBottomMargin)];
+        bottomView.backgroundColor = [UIColor clearColor];
+        self.tableView.tableFooterView = bottomView;
+        [self configDefault];
+        
+        MJWeakSelf;
+        self.tableView.mj_header = [KSGifRefreshHeader headerWithRefreshingBlock:^{
+            [weakSelf resetParamenter];
+            [weakSelf requestData];
+        }];
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(forceScroll) name:@"UITextViewScroll" object:nil];
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enableScroll) name:@"UITextViewEndScroll" object:nil];
+    }
+    return self;
+}
+
+- (void)configDefault {
+    [self.dateFormatter setDateFormat:@"yyyy-MM"];
+    NSDate *currentDate = [NSDate date];
+    self.classDate = [self.dateFormatter stringFromDate:currentDate];
+}
+
+- (void)forceScroll {
+    self.tableView.scrollEnabled = NO;
+}
+
+- (void)enableScroll {
+    self.tableView.scrollEnabled = YES;
+}
+
+- (void)refreshAndRequestData {
+    [self resetParamenter];
+    [self requestData];
+}
+
+- (void)resetParamenter {
+    
+    self.dataArray = [NSMutableArray array];
+    [self.tableView.mj_footer resetNoMoreData];
+    [self setPromptString:@"暂无内容" imageName:@"empty_course" inView:self.tableView];
+    [self.tableView reloadData];
+}
+
+- (void)endRefresh {
+    @weakObj(self);
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        @strongObj(self);
+        [self.tableView.mj_header endRefreshing];
+    });
+}
+
+- (void)requestRoomConfig {
+    [KSNetworkingManager selectRoomConfigRequest:KS_GET success:^(NSDictionary * _Nonnull dic) {
+        if ([dic ks_integerValueForKey:@"code"] == 200 && [dic ks_boolValueForKey:@"status"]) {
+            NSDictionary *result = [dic ks_dictionaryValueForKey:@"data"];
+            self.joinRoomBeforeTime = [result ks_integerValueForKey:@"practiceStartTime"];
+            self.quitRomeEndTime = [result ks_integerValueForKey:@"practiceEndTime"];
+        }
+        else {
+            [LOADING_MANAGER MBShowAUTOHidingInWindow:MESSAGEKEY];
+        }
+    } faliure:^(NSError * _Nonnull error) {
+        
+    }];
+}
+
+- (void)requestData {
+    [KSNetworkingManager courseScheduleListRequest:KS_POST courseGroupId:self.courseGroupId classMonth:self.classDate attendanceStatus:self.attendanceStatus courseStatus:self.courseStatus success:^(NSDictionary * _Nonnull dic) {
+        [self endRefresh];
+        if ([dic ks_integerValueForKey:@"code"] == 200 && [dic ks_boolValueForKey:@"status"]) {
+            NSArray *sourceArray = [dic ks_arrayValueForKey:@"data"];
+            for (NSDictionary *parm in sourceArray) {
+                GroupCourseListModel *model = [[GroupCourseListModel alloc] initWithDictionary:parm];
+                [self.dataArray addObject:model];
+            }
+        }
+        else {
+            [LOADING_MANAGER MBShowAUTOHidingInWindow:MESSAGEKEY];
+        }
+        [self.tableView reloadData];
+        [self changePromptLabelStateWithArray:self.dataArray];
+    } faliure:^(NSError * _Nonnull error) {
+        [self endRefresh];
+        if (self.networkAvaiable == NO) {
+            [self setPromptString:@"暂无网络" imageName:@"no_networking" inView:self.tableView];
+        }
+        [self.dataArray removeAllObjects];
+        [self.tableView reloadData];
+        [self changePromptLabelStateWithArray:self.dataArray];
+    }];
+}
+
+- (void)beginRefreshImmediately {
+    [self.tableView.mj_header beginRefreshing];
+}
+
+- (void)beginFirstRefresh {
+    if (!self.isHeaderRefreshed) {
+        [self beginRefreshImmediately];
+    }
+}
+
+- (void)selectCellAtIndexPath:(NSIndexPath *)indexPath {
+    
+    if (self.lastSelectedIndexPath == indexPath) {
+        return;
+    }
+    if (self.lastSelectedIndexPath != nil) {
+        UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:self.lastSelectedIndexPath];
+        [cell setSelected:NO animated:NO];
+    }
+    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
+    [cell setSelected:YES animated:NO];
+    self.lastSelectedIndexPath = indexPath;
+}
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    CGFloat sortViewHeight = [GroupCourseSortView getViewHeight];
+
+    if (![self.subviews containsObject:self.sortView]) {
+        [self addSubview:self.sortView];
+        [self.sortView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.right.top.mas_equalTo(self);
+            make.height.mas_equalTo(sortViewHeight);
+        }];
+        [self.dateFormatter setDateFormat:@"yyyy年MM月"];
+        NSDate *currentDate = [NSDate date];
+        [self.sortView.firstLabel setText:[self.dateFormatter stringFromDate:currentDate]];
+    }
+
+    [self.tableView mas_remakeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.bottom.mas_equalTo(self);
+        make.top.mas_equalTo(self.sortView.mas_bottom);
+    }];
+}
+
+#pragma mark - UITableViewDataSource
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+    return self.dataArray.count;
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+    return 123.0f;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    GroupCourseListModel *model = self.dataArray[indexPath.row];
+    if (self.courseType == COURSE_GROUP_TYPE_VIP || self.courseType == COURSE_GROUP_TYPE_ACCOMPANY) {
+        ProgramCourseListCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ProgramCourseListCell"];
+        MJWeakSelf;
+        [cell configWithSource:model beforeTime:self.enterStartTime callback:^(GroupCourseListModel * _Nonnull model) {
+            [weakSelf enterClassRoom:model];
+        }];
+        return cell;
+    }
+    else {
+        MusicRoomCourseListCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MusicRoomCourseListCell"];
+        MJWeakSelf;
+        [cell configWithSource:model beforeTime:self.enterStartTime callback:^(GroupCourseListModel * _Nonnull model) {
+            [weakSelf enterClassRoom:model];
+        }];
+        return cell;
+    }
+}
+
+- (void)enterClassRoom:(GroupCourseListModel *)model {
+    
+    NSDateFormatter *dateFormatter = [NSObject getDateformatter];
+    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
+    NSDate *beginDate = [dateFormatter dateFromString:model.startTime];
+    NSDate *endDate = [dateFormatter dateFromString:model.endTime];
+    NSDate *currentDate = [NSDate date];
+    NSTimeInterval beginTimeInterval = [beginDate timeIntervalSinceDate:currentDate];
+    NSTimeInterval endTimeInterval = [currentDate timeIntervalSinceDate:endDate];
+    if (beginTimeInterval <= self.joinRoomBeforeTime * 60 && endTimeInterval < 0) {
+        [self joinClassRoom:model];
+    }
+    else if (endTimeInterval > 0) {
+        [LOADING_MANAGER MBShowAUTOHidingInWindow:@"该课程已结束"];
+    }
+    else {
+        NSString *tipsString = [NSString stringWithFormat:@"课程还未开始,请在上课前%zd分钟进入", self.joinRoomBeforeTime];
+        [LOADING_MANAGER MBShowAUTOHidingInWindow:tipsString];
+    }
+    
+    [self joinClassRoom:model];
+}
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+    // 课程详情
+    GroupCourseListModel *courseModel = self.dataArray[indexPath.row];
+    if (self.courseType == COURSE_GROUP_TYPE_VIP) {
+        VipCouseDetailViewController *detailVC = [[VipCouseDetailViewController alloc] init];
+        detailVC.courseId = courseModel.courseId;
+        detailVC.courseGroupId = courseModel.courseGoupId;
+        [self.naviController pushViewController:detailVC animated:YES];
+    }
+    else if (self.courseType == COURSE_GROUP_TYPE_ACCOMPANY) {
+        AccompanyDetailViewController *detailVC = [[AccompanyDetailViewController alloc] init];
+        detailVC.courseId = courseModel.courseId;
+        detailVC.courseGroupId = courseModel.courseGoupId;
+        [self.naviController pushViewController:detailVC animated:YES];
+    }
+    else if (self.courseType == COURSE_GROUP_TYPE_MUSICROOM) {
+        MusicRoomDetailViewController *ctrl = [[MusicRoomDetailViewController alloc] init];
+        ctrl.courseId = courseModel.courseId;
+        ctrl.courseGroupId = courseModel.courseGoupId;
+        [self.naviController pushViewController:ctrl animated:YES];
+    }
+}
+
+#pragma mark ---- lazying
+- (GroupCourseSortView *)sortView {
+    if (!_sortView) {
+        _sortView = [GroupCourseSortView sharedInstance];
+        MJWeakSelf;
+        [_sortView sortAction:^(SORT_TYPE type) {
+            [weakSelf sortWithType:type];
+        }];
+    }
+    return _sortView;
+}
+
+- (NSMutableArray *)statusArray {
+    if (!_statusArray) {
+        _statusArray = [NSMutableArray arrayWithObject:@[]];
+    }
+    return _statusArray;
+}
+
+- (OnlineClassManager *)classManager {
+    if (!_classManager) {
+        _classManager = [[OnlineClassManager alloc] init];
+    }
+    return _classManager;
+}
+
+
+// 加入房间
+- (void)joinClassRoom:(GroupCourseListModel *)model {
+    
+    // 加入房间前判断摄像头和麦克风逻辑
+    [RecordCheckManager checkCameraPremissionAvaiableCallback:^(PREMISSIONTYPE type) {
+        [self afterCheckCameraCheckMic:type source:model];
+    }];
+}
+
+- (void)afterCheckCameraCheckMic:(PREMISSIONTYPE)cameraType source:(GroupCourseListModel *)model {
+    [RecordCheckManager checkMicPermissionAvaiableCallback:^(PREMISSIONTYPE type) {
+        if (type == PREMISSIONTYPE_YES && cameraType == PREMISSIONTYPE_YES) {
+            // 判断是否进行课前检测
+            [self.classManager joinRoomWithId:model.courseId subjectName:model.subjectName classEndTime:model.endTime inViewController:(KSBaseViewController *)self.naviController.visibleViewController];
+        }
+        else {
+            NSString *content = @"";
+            CHECKDEVICETYPE checkType = CHECKDEVICETYPE_BOTH;
+            if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_NO) {
+                content = @"请开启相机和麦克风访问权限";
+                checkType = CHECKDEVICETYPE_BOTH;
+            }
+            else if (cameraType == PREMISSIONTYPE_NO && type == PREMISSIONTYPE_YES) {
+                content =  @"请开启相机访问权限";
+                checkType = CHECKDEVICETYPE_CAMREA;
+            }
+            else if (cameraType == PREMISSIONTYPE_YES && type == PREMISSIONTYPE_NO) {
+                content = @"请开启麦克风访问权限";
+                checkType = CHECKDEVICETYPE_MIC;
+            }
+            [self showAlertWithMessage:content type:checkType];
+        }
+    }];
+    
+}
+
+- (void)showAlertWithMessage:(NSString *)message type:(CHECKDEVICETYPE)deviceType {
+    [KSPremissionAlert shareInstanceDisplayImage:deviceType message:message showInView:[NSObject getKeyWindow] cancel:^{
+        
+    } confirm:^{
+        [self openSettingView];
+    }];
+    
+}
+
+- (void)openSettingView {
+    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
+}
+/**
+ 设置没有数据时的显示
+ 
+ @param promptString 提示语
+ @param imgName 图片名称
+ @param view 显示在什么地方
+ */
+- (void)setPromptString:(NSString *)promptString imageName:(NSString *)imgName inView:(UIView *)view {
+    if (self.promptView != nil) {
+        [self.promptView removeFromSuperview];
+    }
+    else {
+        self.promptView = [[StateView alloc]init];
+        self.promptView.frame = CGRectMake(0, 0, KPortraitWidth, KPortraitHeight - 300);
+    }
+    _promptPlaceView = view;
+    //当请求不到数据时 ,自定义提示view 将会出现;
+    self.promptView.imageName = imgName;
+    self.promptView.alpha = 0.0f;
+    [self.promptView setText:promptString];
+    [view addSubview:self.promptView];
+}
+
+// 结束刷新后调用方法
+- (void)changePromptLabelStateWithArray:(NSMutableArray *)array {
+    NSInteger count;
+    if (array.count) {
+        count = array.count;
+    } else {
+        count = 0;
+    }
+    
+    [UIView animateWithDuration:0.1 animations:^{
+        [[self promptView] setAlpha:count ? 0.0f :1.0f ] ;
+    }] ;
+    
+}
+
+- (BOOL)networkAvaiable {
+    return [self checkNetworkAvaiable];
+}
+
+- (BOOL)checkNetworkAvaiable {
+    BOOL isExistenceNetwork = YES;
+    Reachability *reach = [Reachability reachabilityWithHostName:@"www.apple.com"];
+    switch ([reach currentReachabilityStatus]) {
+        case NotReachable:
+            isExistenceNetwork = NO;
+            //NSLog(@"notReachable");
+            break;
+        case ReachableViaWiFi:
+            isExistenceNetwork = YES;
+            //NSLog(@"WIFI");
+            break;
+        case ReachableViaWWAN:
+            isExistenceNetwork = YES;
+            //NSLog(@"3G");
+            break;
+    }
+    return isExistenceNetwork;
+}
+- (void)dealloc {
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+
+- (NSDateFormatter *)dateFormatter {
+    if (!_dateFormatter) {
+        _dateFormatter = [NSObject getDateformatter];
+    }
+    return _dateFormatter;
+}
+- (void)sortWithType:(SORT_TYPE)type {
+    
+    if (type == SORT_TYPE_TIME) { // time
+        [self showPickerView];
+    }
+    else if (type == SORT_TYPE_STATUS) { // 状态
+        MJWeakSelf;
+        KSChoosePicker *picker = [[KSChoosePicker alloc] initWithTitle:@"课程状态" sourceData:@[@"全部",@"未开始",@"进行中",@"已结束"]  lastChooseIndex:self.secondChooseIndex sureButtonColor:THEMECOLOR chooseReturnWithBlock:^(NSString * _Nonnull returnValue, NSInteger chooseIndex) {
+            weakSelf.secondChooseIndex = chooseIndex;
+            if (chooseIndex == 0) {
+                [weakSelf.sortView.secondLabel setText:@"全部状态"];
+                weakSelf.courseStatus = nil;
+            }
+            else {
+                [weakSelf.sortView.secondLabel setText:returnValue];
+                [weakSelf evaluateStatusWithIndex:chooseIndex isCourseStatus:YES];
+            }
+            self.sortView.secondArrowUp = NO;
+            [weakSelf refreshAndRequestData];
+        } cancel:^{
+            self.sortView.secondArrowUp = NO;
+        }];
+        [picker showPicker];
+    }
+    else if (type == SORT_TYPE_ATTENDENCE) {
+        
+        MJWeakSelf;
+        KSChoosePicker *picker = [[KSChoosePicker alloc] initWithTitle:@"考勤" sourceData:@[@"全部",@"到课",@"旷课"]  lastChooseIndex:self.secondChooseIndex sureButtonColor:THEMECOLOR chooseReturnWithBlock:^(NSString * _Nonnull returnValue, NSInteger chooseIndex) {
+            weakSelf.thirdChooseIndex = chooseIndex;
+            if (chooseIndex == 0) {
+                [weakSelf.sortView.thirdLabel setText:@"全部考勤"];
+                weakSelf.attendanceStatus = nil;
+            }
+            else {
+                [weakSelf.sortView.thirdLabel setText:returnValue];
+                [weakSelf evaluateStatusWithIndex:chooseIndex isCourseStatus:NO];
+            }
+            self.sortView.thirdArrowUp = NO;
+            [weakSelf refreshAndRequestData];
+        } cancel:^{
+            self.sortView.thirdArrowUp = NO;
+        }];
+        [picker showPicker];
+    }
+    else {
+        [self hiddenPopView];
+    }
+}
+
+- (void)evaluateStatusWithIndex:(NSInteger)chooseIndex isCourseStatus:(BOOL)isCourseStatus {
+    if (isCourseStatus) {
+        switch (chooseIndex) {
+            case 0:
+            {
+                self.courseStatus = nil;
+                [self.sortView.secondLabel setText:@"全部状态"];
+            }
+                break;
+            case 1:
+            {
+                self.courseStatus = @"NOT_START";
+            }
+                break;
+            case 2:
+            {
+                self.courseStatus = @"ING";
+            }
+                break;
+            case 3:
+            {
+                self.courseStatus = @"COMPLETE";
+            }
+                break;
+                
+            default:
+                break;
+        }
+    }
+    else {
+        switch (chooseIndex) {
+            case 0:
+            {
+                self.attendanceStatus = nil;
+                [self.sortView.thirdLabel setText:@"全部考勤"];
+            }
+                break;
+            case 1:
+            {
+                self.attendanceStatus = @"1";
+            }
+                break;
+            case 2:
+            {
+                self.attendanceStatus = @"0";
+            }
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+- (void)showPickerView {
+    [self.dateFormatter setDateFormat:@"yyyy-MM"];
+    NSDate *preDate = [NSDate date];
+    if (![NSString isEmptyString:self.classDate]) {
+        preDate = [self.dateFormatter dateFromString:self.classDate];
+    }
+    
+    KSFullDatePicker *picker = [[KSFullDatePicker alloc] initWithTitle:@"" date:preDate pickMode:KSDATEPICKER_MODE_YEAR_MONTH sureButtonColor:THEMECOLOR selectDateBlock:^(NSString *date) {
+
+        self.classDate = date;
+        NSString *displayTime = [self getTimeDisplay:date];
+        [self.sortView.firstLabel setText:displayTime];
+        [self resetPickerStatus];
+        // 请求数据
+        [self refreshAndRequestData];
+    } cancleBlock:^{
+        [self resetPickerStatus];
+    }];
+    [picker show];
+}
+
+- (void)hiddenPopView {
+    self.sortView.firstArrowUp = NO;
+    self.sortView.secondArrowUp = NO;
+    self.sortView.thirdArrowUp = NO;
+}
+
+
+- (NSString *)getTimeDisplay:(NSString *)chooseMonth {
+    [self.dateFormatter setDateFormat:@"yyyy-MM"];
+    NSDate *chooseDate = [self.dateFormatter dateFromString:chooseMonth];
+    [self.dateFormatter setDateFormat:@"yyyy年MM月"];
+    NSString *displayTime = [self.dateFormatter stringFromDate:chooseDate];
+    return displayTime;
+}
+
+- (void)resetPickerStatus {
+    self.sortView.firstArrowUp = NO;
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 27 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseInfoView.h

@@ -0,0 +1,27 @@
+//
+//  ProgramCourseInfoView.h
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import <UIKit/UIKit.h>
+#import "CourseGroupDetailModel.h"
+
+typedef void(^ProgramPlanAction)(void);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ProgramCourseInfoView : UIView
+
++ (instancetype)sharedInstance;
+
+- (void)configWithSource:(CourseGroupDetailModel *)model;
+
+- (void)programActionCallback:(ProgramPlanAction)callback;
+
++ (CGFloat)getViewHeightWithPlanString:(NSString *)planString;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 166 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseInfoView.m

@@ -0,0 +1,166 @@
+//
+//  ProgramCourseInfoView.m
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import "ProgramCourseInfoView.h"
+#import "CourseGroupPlanAddView.h"
+#import "CourseGroupPlanDescView.h"
+
+@interface ProgramCourseInfoView ()
+
+@property (weak, nonatomic) IBOutlet UIView *bgView;
+
+@property (weak, nonatomic) IBOutlet UILabel *courseGroupTitle;
+
+@property (weak, nonatomic) IBOutlet UILabel *statusLabel;
+
+@property (weak, nonatomic) IBOutlet UIImageView *avatar;
+
+@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
+
+@property (weak, nonatomic) IBOutlet UIView *subjectView;
+
+@property (weak, nonatomic) IBOutlet UILabel *subjectLabel;
+
+@property (weak, nonatomic) IBOutlet UILabel *courseDesc;
+
+@property (weak, nonatomic) IBOutlet UIView *planContainer;
+
+@property (nonatomic, strong) CourseGroupPlanAddView *addView;
+
+@property (nonatomic, strong) CourseGroupPlanDescView *planDescView;
+
+@property (nonatomic, copy) ProgramPlanAction callback;
+
+@end
+
+@implementation ProgramCourseInfoView
+
++ (instancetype)sharedInstance {
+    
+    ProgramCourseInfoView *view = [[[NSBundle mainBundle] loadNibNamed:@"ProgramCourseInfoView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)configWithSource:(CourseGroupDetailModel *)model {
+    [self.planContainer removeAllSubViews];
+    if ([NSString isEmptyString:model.coursePlan]) {
+        [self.planContainer addSubview:self.addView];
+        CGFloat addViewHeight = [CourseGroupPlanAddView getViewHeight];
+        [self.addView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.top.bottom.right.left.mas_equalTo(self.planContainer);
+            make.height.mas_equalTo(addViewHeight);
+        }];
+        self.bgView.backgroundColor = [UIColor whiteColor];
+    }
+    else {
+        CGFloat planHeight = [CourseGroupPlanDescView getViewHeight:model.coursePlan];
+        self.bgView.backgroundColor = [UIColor clearColor];
+        [self.planContainer addSubview:self.planDescView];
+        [self.planDescView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.right.top.bottom.mas_equalTo(self.planContainer);
+            make.height.mas_equalTo(planHeight);
+        }];
+        [self.planDescView configWithPlanDesc:model.coursePlan];
+    }
+    self.courseGroupTitle.text = [NSString returnNoNullStringWithString:model.courseGroupName];
+    /// 状态
+    [self evaluateGroupStatus:model.status];
+    
+    [self.avatar sd_setImageWithURL:[NSURL URLWithString:[model.studentAvatar getUrlEndcodeString]] placeholderImage:[UIImage imageNamed:STUDENT_AVATAR]];
+    self.nameLabel.text = [NSString returnNoNullStringWithString:model.studentName];
+    self.subjectLabel.text = [NSString returnNoNullStringWithString:model.subjectName];
+    if ([NSString isEmptyString:model.subjectName]) {
+        self.subjectView.hidden = YES;
+    }
+    else {
+        self.subjectView.hidden = NO;
+    }
+    // 课程描述
+    NSMutableAttributedString *attrs = [[NSMutableAttributedString alloc] initWithString:@"已上课时 " attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0f weight:UIFontWeightRegular],NSForegroundColorAttributeName:HexRGB(0x999999)}];
+    NSAttributedString *finishNumAttr = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%.0f", model.completeCourseNum] attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0f weight:UIFontWeightSemibold],NSForegroundColorAttributeName:THEMECOLOR}];
+    [attrs appendAttributedString:finishNumAttr];
+    
+    NSAttributedString *totalDescAttr = [[NSAttributedString alloc] initWithString:@" /总课时 " attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0f weight:UIFontWeightRegular],NSForegroundColorAttributeName:HexRGB(0x999999)}];
+    [attrs appendAttributedString:totalDescAttr];
+    NSAttributedString *totalNumAttr = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%.0f", model.courseNum] attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0f weight:UIFontWeightSemibold],NSForegroundColorAttributeName:THEMECOLOR}];
+    [attrs appendAttributedString:totalNumAttr];
+    self.courseDesc.attributedText = attrs;
+}
+
+- (void)evaluateGroupStatus:(NSString *)status {
+    if ([status isEqualToString:@"ING"]) {
+        self.statusLabel.text = @"已开课";
+        self.statusLabel.textColor = THEMECOLOR;
+    }
+    else if ([status isEqualToString:@"NOT_START"]) {
+        self.statusLabel.text = @"未开课";
+        self.statusLabel.textColor = HexRGB(0xFF802C);
+    }
+    else if ([status isEqualToString:@"COMPLETE"]) {
+        self.statusLabel.text = @"已结课";
+        self.statusLabel.textColor = HexRGB(0x999999);
+    }
+    else if ([status isEqualToString:@"CANCEL"]) {
+        self.statusLabel.text = @"已取消";
+        self.statusLabel.textColor = HexRGB(0x999999);
+    }
+}
+
+
+- (void)programActionCallback:(ProgramPlanAction)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+    
+}
+
++ (CGFloat)getViewHeightWithPlanString:(NSString *)planString {
+    if ([NSString isEmptyString:planString]) {
+        return 106.0f + [CourseGroupPlanAddView getViewHeight];
+    }
+    else {
+        return 106.0f + [CourseGroupPlanDescView getViewHeight:planString];
+    }
+}
+
+- (CourseGroupPlanAddView *)addView {
+    if (!_addView) {
+        _addView = [CourseGroupPlanAddView sharedInstance];
+        MJWeakSelf;
+        [_addView addPlanAction:^{
+            [weakSelf showPlanView];
+        }];
+    }
+    return _addView;
+}
+
+- (CourseGroupPlanDescView *)planDescView {
+    if (!_planDescView) {
+        _planDescView = [CourseGroupPlanDescView sharedInstance];
+        MJWeakSelf;
+        [_planDescView modifyPlanAction:^{
+            [weakSelf showPlanView];
+        }];
+    }
+    return _planDescView;
+}
+
+- (void)showPlanView {
+    if (self.callback) {
+        self.callback();
+    }
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 167 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseInfoView.xib

@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="ProgramCourseInfoView">
+            <rect key="frame" x="0.0" y="0.0" width="393" height="150"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0qi-vD-Oua">
+                    <rect key="frame" x="14" y="12" width="365" height="138"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TTp-sQ-B0k">
+                    <rect key="frame" x="14" y="12" width="365" height="94"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Qdk-Iv-HHj">
+                            <rect key="frame" x="12" y="12" width="0.0" height="22"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="22" id="Kha-MR-xBB"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
+                            <color key="textColor" red="0.074509803920000006" green="0.078431372550000003" blue="0.08235294118" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CAz-5b-toA">
+                            <rect key="frame" x="353" y="12" width="0.0" height="0.0"/>
+                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
+                            <color key="textColor" red="1" green="0.50196078430000002" blue="0.17254901959999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" image="teacher_avatar" translatesAutoresizingMaskIntoConstraints="NO" id="Rai-VY-YLo">
+                            <rect key="frame" x="12" y="42" width="40" height="40"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="40" id="8DA-BV-8bO"/>
+                                <constraint firstAttribute="height" constant="40" id="QOM-dg-D6z"/>
+                            </constraints>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="20"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                        </imageView>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="J5P-Tl-Nui">
+                            <rect key="frame" x="62" y="43" width="0.0" height="17"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="17" id="bRJ-dW-7MW"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrange_course_icon" translatesAutoresizingMaskIntoConstraints="NO" id="XT8-k1-pUz">
+                            <rect key="frame" x="63" y="68" width="12" height="14"/>
+                        </imageView>
+                        <view contentMode="scaleToFill" horizontalCompressionResistancePriority="752" translatesAutoresizingMaskIntoConstraints="NO" id="ERt-aV-DBH">
+                            <rect key="frame" x="67" y="43" width="8" height="17"/>
+                            <subviews>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mAJ-jN-D5e">
+                                    <rect key="frame" x="4" y="8.6666666666666643" width="0.0" height="0.0"/>
+                                    <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                                    <color key="textColor" red="1" green="0.54901960780000003" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                            </subviews>
+                            <color key="backgroundColor" red="1" green="0.94509803920000002" blue="0.87058823529999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="17" id="C22-aR-RpW"/>
+                                <constraint firstItem="mAJ-jN-D5e" firstAttribute="leading" secondItem="ERt-aV-DBH" secondAttribute="leading" constant="4" id="CC9-1M-6oE"/>
+                                <constraint firstAttribute="trailing" secondItem="mAJ-jN-D5e" secondAttribute="trailing" constant="4" id="KEw-Zn-4A8"/>
+                                <constraint firstItem="mAJ-jN-D5e" firstAttribute="centerY" secondItem="ERt-aV-DBH" secondAttribute="centerY" id="Tpv-Ne-nBQ"/>
+                            </constraints>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="4"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                        </view>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oly-2P-Xkc">
+                            <rect key="frame" x="79" y="67" width="0.0" height="16"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="16" id="eDB-zy-wct"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                            <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="trailing" secondItem="CAz-5b-toA" secondAttribute="trailing" constant="12" id="62C-yK-QC9"/>
+                        <constraint firstItem="ERt-aV-DBH" firstAttribute="leading" secondItem="J5P-Tl-Nui" secondAttribute="trailing" constant="5" id="8Er-0C-7BN"/>
+                        <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Oly-2P-Xkc" secondAttribute="trailing" constant="12" id="BZC-m7-J5U"/>
+                        <constraint firstItem="XT8-k1-pUz" firstAttribute="top" secondItem="J5P-Tl-Nui" secondAttribute="bottom" constant="8" id="CoR-b0-0sW"/>
+                        <constraint firstItem="CAz-5b-toA" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Qdk-Iv-HHj" secondAttribute="trailing" constant="10" id="Gb3-QX-dbH"/>
+                        <constraint firstItem="Qdk-Iv-HHj" firstAttribute="leading" secondItem="TTp-sQ-B0k" secondAttribute="leading" constant="12" id="IIc-Jh-oNg"/>
+                        <constraint firstItem="XT8-k1-pUz" firstAttribute="leading" secondItem="Rai-VY-YLo" secondAttribute="trailing" constant="11" id="IhI-s9-e47"/>
+                        <constraint firstItem="CAz-5b-toA" firstAttribute="top" secondItem="TTp-sQ-B0k" secondAttribute="top" constant="12" id="Ipx-qZ-nB7"/>
+                        <constraint firstItem="Oly-2P-Xkc" firstAttribute="centerY" secondItem="XT8-k1-pUz" secondAttribute="centerY" id="Lq5-mC-qTV"/>
+                        <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="ERt-aV-DBH" secondAttribute="trailing" constant="12" id="Tmv-2u-dev"/>
+                        <constraint firstItem="Qdk-Iv-HHj" firstAttribute="top" secondItem="TTp-sQ-B0k" secondAttribute="top" constant="12" id="Tsv-yc-kpD"/>
+                        <constraint firstItem="Rai-VY-YLo" firstAttribute="leading" secondItem="TTp-sQ-B0k" secondAttribute="leading" constant="12" id="UXu-Pn-bKK"/>
+                        <constraint firstItem="J5P-Tl-Nui" firstAttribute="top" secondItem="Qdk-Iv-HHj" secondAttribute="bottom" constant="9" id="d9U-0l-oCX"/>
+                        <constraint firstAttribute="height" constant="94" id="fku-rU-8na"/>
+                        <constraint firstItem="Rai-VY-YLo" firstAttribute="top" secondItem="Qdk-Iv-HHj" secondAttribute="bottom" constant="8" id="gFs-Gy-Jsh"/>
+                        <constraint firstItem="Oly-2P-Xkc" firstAttribute="leading" secondItem="XT8-k1-pUz" secondAttribute="trailing" constant="4" id="pxe-EP-4rh"/>
+                        <constraint firstItem="J5P-Tl-Nui" firstAttribute="leading" secondItem="Rai-VY-YLo" secondAttribute="trailing" constant="10" id="sHB-UT-QED"/>
+                        <constraint firstItem="ERt-aV-DBH" firstAttribute="centerY" secondItem="J5P-Tl-Nui" secondAttribute="centerY" id="yTp-TD-ifl"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gsx-fa-Tcd">
+                    <rect key="frame" x="14" y="106" width="365" height="44"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstAttribute="trailing" secondItem="TTp-sQ-B0k" secondAttribute="trailing" constant="14" id="2fm-xo-bkO"/>
+                <constraint firstItem="0qi-vD-Oua" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="12" id="7Hc-MS-0a3"/>
+                <constraint firstItem="TTp-sQ-B0k" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="12" id="CYh-Mp-xtb"/>
+                <constraint firstItem="0qi-vD-Oua" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="14" id="MWq-23-xVB"/>
+                <constraint firstItem="gsx-fa-Tcd" firstAttribute="top" secondItem="TTp-sQ-B0k" secondAttribute="bottom" id="QiW-va-YxJ"/>
+                <constraint firstAttribute="trailing" secondItem="gsx-fa-Tcd" secondAttribute="trailing" constant="14" id="T39-15-PVh"/>
+                <constraint firstAttribute="trailing" secondItem="0qi-vD-Oua" secondAttribute="trailing" constant="14" id="aoo-Jk-WcP"/>
+                <constraint firstItem="TTp-sQ-B0k" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="14" id="oFM-mO-VSd"/>
+                <constraint firstItem="gsx-fa-Tcd" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="14" id="pYh-cr-W5m"/>
+                <constraint firstAttribute="bottom" secondItem="gsx-fa-Tcd" secondAttribute="bottom" id="rIL-hn-g64"/>
+                <constraint firstAttribute="bottom" secondItem="0qi-vD-Oua" secondAttribute="bottom" id="vg2-s2-xDR"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="avatar" destination="Rai-VY-YLo" id="Tzn-mk-ayG"/>
+                <outlet property="bgView" destination="0qi-vD-Oua" id="KlE-17-Ux1"/>
+                <outlet property="courseDesc" destination="Oly-2P-Xkc" id="B4s-bE-y4K"/>
+                <outlet property="courseGroupTitle" destination="Qdk-Iv-HHj" id="82U-Cc-EQr"/>
+                <outlet property="nameLabel" destination="J5P-Tl-Nui" id="0H1-Zi-zbs"/>
+                <outlet property="planContainer" destination="gsx-fa-Tcd" id="Ife-Bl-L6a"/>
+                <outlet property="statusLabel" destination="CAz-5b-toA" id="LEe-vR-eoP"/>
+                <outlet property="subjectLabel" destination="mAJ-jN-D5e" id="yUP-Mv-1nr"/>
+                <outlet property="subjectView" destination="ERt-aV-DBH" id="oN3-N8-u1a"/>
+            </connections>
+            <point key="canvasLocation" x="41.984732824427482" y="-138.73239436619718"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="arrange_course_icon" width="12" height="14"/>
+        <image name="teacher_avatar" width="150" height="150"/>
+    </resources>
+</document>

+ 21 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseListCell.h

@@ -0,0 +1,21 @@
+//
+//  ProgramCourseListCell.h
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import <UIKit/UIKit.h>
+#import "GroupCourseListModel.h"
+
+typedef void(^GroupListModelCallback)(GroupCourseListModel * _Nonnull model);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ProgramCourseListCell : UITableViewCell
+
+- (void)configWithSource:(GroupCourseListModel *)model beforeTime:(NSInteger)beforeTime callback:(GroupListModelCallback)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 139 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseListCell.m

@@ -0,0 +1,139 @@
+//
+//  ProgramCourseListCell.m
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/22.
+//
+
+#import "ProgramCourseListCell.h"
+
+@interface ProgramCourseListCell ()
+
+@property (weak, nonatomic) IBOutlet UIButton *enterButton;
+
+@property (weak, nonatomic) IBOutlet UILabel *timeLabel;
+
+@property (weak, nonatomic) IBOutlet UILabel *statusLabel;
+
+@property (weak, nonatomic) IBOutlet UILabel *courseGroupName;
+
+@property (weak, nonatomic) IBOutlet UIImageView *avatar;
+
+@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
+
+@property (weak, nonatomic) IBOutlet UIImageView *arriveStatus;
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *arriveStatusWidth;
+
+@property (weak, nonatomic) IBOutlet UIView *subjectView;
+@property (weak, nonatomic) IBOutlet UILabel *subjectLabel;
+
+@property (nonatomic, strong) GroupCourseListModel *sourceModel;
+
+@property (nonatomic, copy) GroupListModelCallback callback;
+
+@property (nonatomic, assign) NSInteger beforeTime;
+
+@end
+
+@implementation ProgramCourseListCell
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    // Initialization code
+    self.selectionStyle = UITableViewCellSelectionStyleNone;
+}
+
+- (void)configWithSource:(GroupCourseListModel *)model beforeTime:(NSInteger)beforeTime callback:(GroupListModelCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+    self.sourceModel = model;
+    NSDateFormatter *formatter = [NSObject getDateformatter];
+    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
+    NSDate *startDate = [formatter dateFromString:model.startTime];
+    NSDate *endDate = [formatter dateFromString:model.endTime];
+    [formatter setDateFormat:@"yyyy-MM-dd HH:mm"];
+    NSString *lessonBegin = [formatter stringFromDate:startDate];
+    [formatter setDateFormat:@"HH:mm"];
+    NSString *lessonEnd = [formatter stringFromDate:endDate];
+    self.timeLabel.text = [NSString stringWithFormat:@"%@~%@",[NSString returnNoNullStringWithString:lessonBegin],[NSString returnNoNullStringWithString:lessonEnd]];
+    self.courseGroupName.text = [NSString returnNoNullStringWithString:model.courseGroupName];
+    CourseStudentVos *studentInfo = [model.courseStudentVos lastObject];
+    [self.avatar sd_setImageWithURL:[NSURL URLWithString:[studentInfo.avatar getUrlEndcodeString]] placeholderImage:[UIImage imageNamed:STUDENT_AVATAR]];
+    self.nameLabel.text = [NSString returnNoNullStringWithString:studentInfo.userName];
+    self.subjectLabel.text = [NSString returnNoNullStringWithString:model.subjectName];
+    if ([NSString isEmptyString:model.subjectName]) {
+        self.subjectView.hidden = YES;
+    }
+    else {
+        self.subjectView.hidden = NO;
+    }
+    [self evaluateWithSource:model];
+}
+
+- (void)evaluateWithSource:(GroupCourseListModel *)model {
+    NSString *imageName = model.attendanceStatus ? @"course_attendence" : @"course_noArrive";
+    if ([model.status isEqualToString:@"ING"]) {
+        self.statusLabel.text = @"进行中";
+        self.statusLabel.textColor = THEMECOLOR;
+        self.arriveStatus.hidden = YES;
+        self.arriveStatusWidth.constant = 0.0f;
+        self.enterButton.hidden = NO;
+        self.enterButton.userInteractionEnabled = YES;
+    }
+    else if ([model.status isEqualToString:@"NOT_START"]) {
+        self.statusLabel.text = @"未开始";
+        self.statusLabel.textColor = HexRGB(0xFF802C);
+        self.arriveStatus.hidden = YES;
+        self.arriveStatusWidth.constant = 0.0f;
+        
+        // 判断按钮显示调课还是进入教室
+        NSDate *currentDate = [NSDate date];
+        NSDate *beginDate = [self getCourseBeginDate:model.startTime];
+        NSTimeInterval beginTimeInterval = [beginDate timeIntervalSinceDate:currentDate];
+        BOOL canChange = NO;
+        if (beginTimeInterval > self.beforeTime * 60) {
+            canChange = YES;
+        }
+        if (canChange) {
+            self.enterButton.hidden = YES;
+            self.enterButton.userInteractionEnabled = NO;
+        }
+        else {
+            self.enterButton.hidden = NO;
+            self.enterButton.userInteractionEnabled = YES;
+        }
+    }
+    else if ([model.status isEqualToString:@"COMPLETE"]) {
+        self.statusLabel.text = @"已结束";
+        self.statusLabel.textColor = HexRGB(0x999999);
+        self.arriveStatus.hidden = NO;
+        self.arriveStatusWidth.constant = 36.0f;
+        self.enterButton.hidden = YES;
+        self.enterButton.userInteractionEnabled = NO;
+    }
+    [self.arriveStatus setImage:[UIImage imageNamed:imageName]];
+}
+
+
+
+- (IBAction)enterClassRoom:(id)sender {
+    if (self.callback) {
+        self.callback(self.sourceModel);
+    }
+}
+
+- (NSDate *)getCourseBeginDate:(NSString *)beginTime {
+    NSDateFormatter *dateFormatter = [NSObject getDateformatter];
+    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
+    NSDate *beginDate = [dateFormatter dateFromString:beginTime];
+    return beginDate;
+}
+
+- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
+    [super setSelected:selected animated:animated];
+
+    // Configure the view for the selected state
+}
+
+@end

+ 207 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/ProgramCourseListCell.xib

@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="129" id="KGk-i7-Jjw" customClass="ProgramCourseListCell">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="123"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="123"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DbI-ca-fVq">
+                        <rect key="frame" x="14" y="0.0" width="292" height="111"/>
+                        <subviews>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="cms-Uu-AjN">
+                                <rect key="frame" x="10" y="46" width="272" height="1"/>
+                                <color key="backgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.94901960780000005" alpha="1" colorSpace="calibratedRGB"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="1" id="cxi-5O-CuU"/>
+                                </constraints>
+                            </view>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" image="course_time" translatesAutoresizingMaskIntoConstraints="NO" id="zF6-bQ-6kl">
+                                <rect key="frame" x="12" y="15" width="16" height="16"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="16" id="EOd-pk-RGo"/>
+                                    <constraint firstAttribute="height" constant="16" id="um2-KO-HvN"/>
+                                </constraints>
+                            </imageView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IZA-Ec-v3k">
+                                <rect key="frame" x="35" y="14" width="0.0" height="18"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="18" id="efI-vt-L5E"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                                <color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="进行中" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2NK-NE-SfV">
+                                <rect key="frame" x="238" y="12" width="42" height="22"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="22" id="Jye-K4-VKj"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
+                                <color key="textColor" red="0.1764705882352941" green="0.7803921568627451" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="student_avatar" translatesAutoresizingMaskIntoConstraints="NO" id="fC0-s3-zGf">
+                                <rect key="frame" x="12" y="59" width="40" height="40"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="40" id="44E-TM-UhJ"/>
+                                    <constraint firstAttribute="height" constant="40" id="TFm-z7-8Kk"/>
+                                </constraints>
+                                <userDefinedRuntimeAttributes>
+                                    <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                        <real key="value" value="20"/>
+                                    </userDefinedRuntimeAttribute>
+                                </userDefinedRuntimeAttributes>
+                            </imageView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="n5o-cB-ZAV">
+                                <rect key="frame" x="62" y="58" width="0.0" height="21"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="21" id="ohl-08-mEU"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                <color key="textColor" red="0.074509803921568626" green="0.078431372549019607" blue="0.082352941176470587" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" image="merge_next" translatesAutoresizingMaskIntoConstraints="NO" id="qS2-Zu-yal">
+                                <rect key="frame" x="268" y="73" width="12" height="12"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="12" id="G2z-Hu-GQl"/>
+                                    <constraint firstAttribute="height" constant="12" id="fMg-wX-e8L"/>
+                                </constraints>
+                            </imageView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VwE-ev-4NJ">
+                                <rect key="frame" x="62" y="83" width="0.0" height="17"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="17" id="Vmz-gh-Vrv"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                                <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <view contentMode="scaleToFill" horizontalCompressionResistancePriority="752" translatesAutoresizingMaskIntoConstraints="NO" id="IN8-fl-KKM">
+                                <rect key="frame" x="67" y="83" width="8" height="17"/>
+                                <subviews>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Uk4-i9-HBZ">
+                                        <rect key="frame" x="4" y="8.6666666666666714" width="0.0" height="0.0"/>
+                                        <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                                        <color key="textColor" red="1" green="0.54901960780000003" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                </subviews>
+                                <color key="backgroundColor" red="1" green="0.94509803920000002" blue="0.87058823529999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <constraints>
+                                    <constraint firstItem="Uk4-i9-HBZ" firstAttribute="centerY" secondItem="IN8-fl-KKM" secondAttribute="centerY" id="Afl-dU-Iqo"/>
+                                    <constraint firstItem="Uk4-i9-HBZ" firstAttribute="leading" secondItem="IN8-fl-KKM" secondAttribute="leading" constant="4" id="mXI-kd-vdQ"/>
+                                    <constraint firstAttribute="trailing" secondItem="Uk4-i9-HBZ" secondAttribute="trailing" constant="4" id="uLk-w6-yIl"/>
+                                    <constraint firstAttribute="height" constant="17" id="yWL-aA-QpW"/>
+                                </constraints>
+                                <userDefinedRuntimeAttributes>
+                                    <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                        <real key="value" value="4"/>
+                                    </userDefinedRuntimeAttribute>
+                                </userDefinedRuntimeAttributes>
+                            </view>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="nM7-0u-1Vp">
+                                <rect key="frame" x="67" y="59.666666666666657" width="36" height="18"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="18" id="JoD-PD-ToL"/>
+                                    <constraint firstAttribute="width" constant="36" id="gMd-pw-6Q3"/>
+                                </constraints>
+                            </imageView>
+                            <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Dqu-Jf-1D9">
+                                <rect key="frame" x="215" y="12" width="62" height="22"/>
+                                <color key="backgroundColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="62" id="QYf-fZ-JiH"/>
+                                    <constraint firstAttribute="height" constant="22" id="ajo-2K-RFf"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <state key="normal" title="开始上课"/>
+                                <connections>
+                                    <action selector="enterClassRoom:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="fN5-pz-tM3"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                        <constraints>
+                            <constraint firstAttribute="bottom" secondItem="qS2-Zu-yal" secondAttribute="bottom" constant="26" id="1mi-w0-eDU"/>
+                            <constraint firstItem="n5o-cB-ZAV" firstAttribute="leading" secondItem="fC0-s3-zGf" secondAttribute="trailing" constant="10" id="7Hn-gP-Gfh"/>
+                            <constraint firstItem="Dqu-Jf-1D9" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="IZA-Ec-v3k" secondAttribute="trailing" constant="5" id="9SA-Em-VRf"/>
+                            <constraint firstAttribute="trailing" secondItem="cms-Uu-AjN" secondAttribute="trailing" constant="10" id="9bT-Vd-f8p"/>
+                            <constraint firstAttribute="bottom" secondItem="fC0-s3-zGf" secondAttribute="bottom" constant="12" id="Ah1-VO-xwc"/>
+                            <constraint firstItem="2NK-NE-SfV" firstAttribute="centerY" secondItem="IZA-Ec-v3k" secondAttribute="centerY" id="EMl-9N-RG5"/>
+                            <constraint firstAttribute="trailing" secondItem="qS2-Zu-yal" secondAttribute="trailing" constant="12" id="EVJ-Qc-kNM"/>
+                            <constraint firstItem="VwE-ev-4NJ" firstAttribute="leading" secondItem="n5o-cB-ZAV" secondAttribute="leading" id="Ehn-EO-ici"/>
+                            <constraint firstItem="fC0-s3-zGf" firstAttribute="top" secondItem="cms-Uu-AjN" secondAttribute="bottom" constant="12" id="F67-Fp-zCL"/>
+                            <constraint firstItem="Dqu-Jf-1D9" firstAttribute="top" secondItem="DbI-ca-fVq" secondAttribute="top" constant="12" id="FVr-bj-Ma1"/>
+                            <constraint firstItem="cms-Uu-AjN" firstAttribute="top" secondItem="zF6-bQ-6kl" secondAttribute="bottom" constant="15" id="Guj-pG-kNB"/>
+                            <constraint firstItem="nM7-0u-1Vp" firstAttribute="leading" secondItem="n5o-cB-ZAV" secondAttribute="trailing" constant="5" id="Quj-Mk-SRG"/>
+                            <constraint firstItem="IN8-fl-KKM" firstAttribute="leading" secondItem="VwE-ev-4NJ" secondAttribute="trailing" constant="5" id="S5x-NE-bKj"/>
+                            <constraint firstItem="fC0-s3-zGf" firstAttribute="leading" secondItem="DbI-ca-fVq" secondAttribute="leading" constant="12" id="Uo9-fx-EKh"/>
+                            <constraint firstAttribute="trailing" secondItem="Dqu-Jf-1D9" secondAttribute="trailing" constant="15" id="Uu6-A8-Gya"/>
+                            <constraint firstItem="VwE-ev-4NJ" firstAttribute="top" secondItem="n5o-cB-ZAV" secondAttribute="bottom" constant="4" id="VY7-N5-kOX"/>
+                            <constraint firstItem="IN8-fl-KKM" firstAttribute="centerY" secondItem="VwE-ev-4NJ" secondAttribute="centerY" id="Xfo-bh-3Dl"/>
+                            <constraint firstAttribute="trailing" secondItem="2NK-NE-SfV" secondAttribute="trailing" constant="12" id="YbB-Iw-Wws"/>
+                            <constraint firstItem="n5o-cB-ZAV" firstAttribute="top" secondItem="fC0-s3-zGf" secondAttribute="top" constant="-1" id="Zf7-e6-HR1"/>
+                            <constraint firstItem="nM7-0u-1Vp" firstAttribute="centerY" secondItem="n5o-cB-ZAV" secondAttribute="centerY" id="iKM-Qu-8QT"/>
+                            <constraint firstItem="zF6-bQ-6kl" firstAttribute="top" secondItem="DbI-ca-fVq" secondAttribute="top" constant="15" id="k8n-KT-orp"/>
+                            <constraint firstItem="qS2-Zu-yal" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="nM7-0u-1Vp" secondAttribute="trailing" constant="5" id="klr-PU-0yf"/>
+                            <constraint firstItem="IZA-Ec-v3k" firstAttribute="centerY" secondItem="zF6-bQ-6kl" secondAttribute="centerY" id="nL8-bs-J29"/>
+                            <constraint firstItem="zF6-bQ-6kl" firstAttribute="leading" secondItem="DbI-ca-fVq" secondAttribute="leading" constant="12" id="nNW-ef-6OP"/>
+                            <constraint firstItem="cms-Uu-AjN" firstAttribute="leading" secondItem="DbI-ca-fVq" secondAttribute="leading" constant="10" id="pGW-8N-GaP"/>
+                            <constraint firstItem="qS2-Zu-yal" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="IN8-fl-KKM" secondAttribute="trailing" constant="5" id="xy4-vl-cX0"/>
+                            <constraint firstItem="IZA-Ec-v3k" firstAttribute="leading" secondItem="zF6-bQ-6kl" secondAttribute="trailing" constant="7" id="z5j-XG-UJ1"/>
+                        </constraints>
+                        <userDefinedRuntimeAttributes>
+                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                <real key="value" value="10"/>
+                            </userDefinedRuntimeAttribute>
+                        </userDefinedRuntimeAttributes>
+                    </view>
+                </subviews>
+                <constraints>
+                    <constraint firstAttribute="bottom" secondItem="DbI-ca-fVq" secondAttribute="bottom" constant="12" id="Gum-lh-wy7"/>
+                    <constraint firstAttribute="trailing" secondItem="DbI-ca-fVq" secondAttribute="trailing" constant="14" id="KJf-W8-8pJ"/>
+                    <constraint firstItem="DbI-ca-fVq" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="VBR-sN-KEK"/>
+                    <constraint firstItem="DbI-ca-fVq" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="14" id="iVf-sb-Hba"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="aW0-zy-SZf"/>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <connections>
+                <outlet property="arriveStatus" destination="nM7-0u-1Vp" id="dGM-jD-dZB"/>
+                <outlet property="arriveStatusWidth" destination="gMd-pw-6Q3" id="BSA-9V-x7b"/>
+                <outlet property="avatar" destination="fC0-s3-zGf" id="eQU-zt-sOx"/>
+                <outlet property="courseGroupName" destination="n5o-cB-ZAV" id="02s-Db-N24"/>
+                <outlet property="enterButton" destination="Dqu-Jf-1D9" id="FZy-Cb-KFs"/>
+                <outlet property="nameLabel" destination="VwE-ev-4NJ" id="MOV-h8-dpI"/>
+                <outlet property="statusLabel" destination="2NK-NE-SfV" id="uu3-nS-teP"/>
+                <outlet property="subjectLabel" destination="Uk4-i9-HBZ" id="xbf-8n-kcl"/>
+                <outlet property="subjectView" destination="IN8-fl-KKM" id="vm7-LO-lCn"/>
+                <outlet property="timeLabel" destination="IZA-Ec-v3k" id="oiu-tG-ipj"/>
+            </connections>
+            <point key="canvasLocation" x="39.694656488549619" y="49.647887323943664"/>
+        </tableViewCell>
+    </objects>
+    <resources>
+        <image name="course_time" width="16" height="16"/>
+        <image name="merge_next" width="14" height="14"/>
+        <image name="student_avatar" width="150" height="150"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 27 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/VipCouseInfoCell.h

@@ -0,0 +1,27 @@
+//
+//  VipCouseInfoCell.h
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/13.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef void(^VipChatCallback)(void);
+
+
+@interface VipCouseInfoCell : UITableViewCell
+
+@property (weak, nonatomic) IBOutlet UILabel *statusLabel;
+
+@property (nonatomic, assign) BOOL hideChatButton;
+
+- (void)configWithStartTime:(NSString *)beginTime endTime:(NSString *)endTime studentAvatar:(NSString *)studentAvatar studentName:(NSString *)studentName studentId:(NSString *)studentId studentSubject:(NSString *)studentSubject;
+
+- (void)chatCalkback:(VipChatCallback)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 90 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/VipCouseInfoCell.m

@@ -0,0 +1,90 @@
+//
+//  VipCouseInfoCell.m
+//  KulexiuForTeacher
+//
+//  Created by 王智 on 2024/11/13.
+//
+
+#import "VipCouseInfoCell.h"
+
+@interface VipCouseInfoCell ()
+
+@property (weak, nonatomic) IBOutlet UILabel *courseTime;
+@property (weak, nonatomic) IBOutlet UIImageView *studentAvatar;
+@property (weak, nonatomic) IBOutlet UILabel *studentName;
+@property (weak, nonatomic) IBOutlet UILabel *studentSubject;
+
+@property (weak, nonatomic) IBOutlet UIButton *chatButton;
+
+@property (nonatomic, strong) NSString *userId;
+
+@property (nonatomic, strong) NSString *userName;
+
+@property (nonatomic, copy) VipChatCallback callback;
+
+@end
+
+@implementation VipCouseInfoCell
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    // Initialization code
+    self.selectionStyle = UITableViewCellSelectionStyleNone;
+}
+
+- (void)configWithStartTime:(NSString *)beginTime endTime:(NSString *)endTime studentAvatar:(NSString *)studentAvatar studentName:(NSString *)studentName studentId:(NSString *)studentId studentSubject:(NSString *)studentSubject {
+    // time
+    NSDateFormatter *formatter = [NSObject getDateformatter];
+    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
+    NSDate *startDate = [formatter dateFromString:beginTime];
+    NSDate *endDate = [formatter dateFromString:endTime];
+    [formatter setDateFormat:@"yyyy/MM/dd HH:mm"];
+    NSString *lessonBegin = [formatter stringFromDate:startDate];
+    [formatter setDateFormat:@"HH:mm"];
+    NSString *lessonEnd = [formatter stringFromDate:endDate];
+    self.courseTime.text = [NSString stringWithFormat:@"%@~%@",[NSString returnNoNullStringWithString:lessonBegin],[NSString returnNoNullStringWithString:lessonEnd]];
+    if ([NSString isEmptyString:studentName]) {
+        self.studentName.text = [NSString stringWithFormat:@"游客%@",studentId];
+    }
+    else {
+        self.studentName.text = studentName;
+    }
+    self.studentName.text = [NSString returnNoNullStringWithString:studentName];
+    [self.studentAvatar sd_setImageWithURL:[NSURL URLWithString:[studentAvatar getUrlEndcodeString]] placeholderImage:[UIImage imageNamed:STUDENT_AVATAR]];
+    self.studentSubject.text = [NSString returnNoNullStringWithString:studentSubject];
+
+    self.userId = studentId;
+    self.userName = studentName;
+}
+
+- (void)setHideChatButton:(BOOL)hideChatButton {
+    _hideChatButton = hideChatButton;
+    if (hideChatButton) {
+        self.chatButton.hidden = YES;
+        self.chatButton.userInteractionEnabled = NO;
+    }
+    else {
+        self.chatButton.hidden = NO;
+        self.chatButton.userInteractionEnabled = YES;
+    }
+}
+
+- (void)chatCalkback:(VipChatCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+
+- (IBAction)chatAction:(id)sender {
+    if (self.callback) {
+        self.callback();
+    }
+}
+
+- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
+    [super setSelected:selected animated:animated];
+
+    // Configure the view for the selected state
+}
+
+@end

+ 170 - 0
KulexiuForStudent/KulexiuForStudent/Module/Course/VipCourse/View/VipCouseInfoCell.xib

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="145" id="KGk-i7-Jjw" customClass="VipCouseInfoCell">
+            <rect key="frame" x="0.0" y="0.0" width="335" height="145"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="335" height="145"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yVi-pw-eI6">
+                        <rect key="frame" x="14" y="10" width="307" height="123"/>
+                        <subviews>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3dh-xL-h1k">
+                                <rect key="frame" x="10" y="38" width="287" height="1"/>
+                                <color key="backgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.94901960780000005" alpha="1" colorSpace="calibratedRGB"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="1" id="h97-r3-fgZ"/>
+                                </constraints>
+                            </view>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="course_time" translatesAutoresizingMaskIntoConstraints="NO" id="B1g-pw-mYI">
+                                <rect key="frame" x="11" y="12" width="16" height="16"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="16" id="Su3-Id-msD"/>
+                                    <constraint firstAttribute="height" constant="16" id="hr8-TJ-Ukd"/>
+                                </constraints>
+                            </imageView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="49U-qQ-4uN">
+                                <rect key="frame" x="34" y="11" width="0.0" height="18"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="18" id="f06-F1-3xZ"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                                <color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QvR-ZO-p6W">
+                                <rect key="frame" x="246" y="20" width="50" height="0.0"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="50" id="oKu-05-8Na"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
+                                <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="calibratedRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="student_avatar" translatesAutoresizingMaskIntoConstraints="NO" id="3cO-85-Ox1">
+                                <rect key="frame" x="11" y="56" width="47" height="47"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="47" id="YsZ-Ob-XKz"/>
+                                    <constraint firstAttribute="width" constant="47" id="p7L-Dk-Ijn"/>
+                                </constraints>
+                                <userDefinedRuntimeAttributes>
+                                    <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                        <real key="value" value="5"/>
+                                    </userDefinedRuntimeAttribute>
+                                </userDefinedRuntimeAttributes>
+                            </imageView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UrD-Un-gOT">
+                                <rect key="frame" x="68" y="56" width="0.0" height="24"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="24" id="DRM-MQ-t61"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
+                                <color key="textColor" red="0.1019607843" green="0.1019607843" blue="0.1019607843" alpha="1" colorSpace="calibratedRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IV2-zi-jmv">
+                                <rect key="frame" x="68" y="83" width="10" height="20"/>
+                                <subviews>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4gL-4k-HYe">
+                                        <rect key="frame" x="5" y="0.0" width="0.0" height="20"/>
+                                        <fontDescription key="fontDescription" type="system" pointSize="11"/>
+                                        <color key="textColor" red="1" green="0.54901960780000003" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                </subviews>
+                                <color key="backgroundColor" red="1" green="0.94509803920000002" blue="0.87058823529999996" alpha="1" colorSpace="calibratedRGB"/>
+                                <constraints>
+                                    <constraint firstAttribute="trailing" secondItem="4gL-4k-HYe" secondAttribute="trailing" constant="5" id="4Np-oM-yIe"/>
+                                    <constraint firstItem="4gL-4k-HYe" firstAttribute="top" secondItem="IV2-zi-jmv" secondAttribute="top" id="9Bt-0w-zaH"/>
+                                    <constraint firstAttribute="height" constant="20" id="a1L-GP-2lN"/>
+                                    <constraint firstAttribute="bottom" secondItem="4gL-4k-HYe" secondAttribute="bottom" id="q0F-8u-0Qg"/>
+                                    <constraint firstItem="4gL-4k-HYe" firstAttribute="leading" secondItem="IV2-zi-jmv" secondAttribute="leading" constant="5" id="t9E-CA-Jd2"/>
+                                </constraints>
+                                <userDefinedRuntimeAttributes>
+                                    <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                        <real key="value" value="4"/>
+                                    </userDefinedRuntimeAttribute>
+                                </userDefinedRuntimeAttributes>
+                            </view>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Yoe-Ih-DzZ">
+                                <rect key="frame" x="68" y="48" width="40" height="40"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="40" id="SJF-dV-iio"/>
+                                    <constraint firstAttribute="height" constant="40" id="aFY-li-BZY"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <state key="normal" image="course_chat"/>
+                                <connections>
+                                    <action selector="chatAction:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="Ume-RJ-nmk"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                        <constraints>
+                            <constraint firstAttribute="bottom" secondItem="3cO-85-Ox1" secondAttribute="bottom" constant="20" id="5Uk-ie-H5V"/>
+                            <constraint firstItem="QvR-ZO-p6W" firstAttribute="centerY" secondItem="49U-qQ-4uN" secondAttribute="centerY" id="8KI-hF-cDG"/>
+                            <constraint firstItem="B1g-pw-mYI" firstAttribute="top" secondItem="yVi-pw-eI6" secondAttribute="top" constant="12" id="AAO-VS-NM0"/>
+                            <constraint firstItem="Yoe-Ih-DzZ" firstAttribute="leading" secondItem="UrD-Un-gOT" secondAttribute="trailing" id="BJb-7I-w1j"/>
+                            <constraint firstItem="3cO-85-Ox1" firstAttribute="leading" secondItem="yVi-pw-eI6" secondAttribute="leading" constant="11" id="JKe-4J-32l"/>
+                            <constraint firstItem="3dh-xL-h1k" firstAttribute="leading" secondItem="yVi-pw-eI6" secondAttribute="leading" constant="10" id="Pmq-eE-IH9"/>
+                            <constraint firstItem="B1g-pw-mYI" firstAttribute="leading" secondItem="yVi-pw-eI6" secondAttribute="leading" constant="11" id="SeV-ws-KO7"/>
+                            <constraint firstAttribute="trailing" secondItem="3dh-xL-h1k" secondAttribute="trailing" constant="10" id="TcN-xQ-24N"/>
+                            <constraint firstItem="Yoe-Ih-DzZ" firstAttribute="centerY" secondItem="UrD-Un-gOT" secondAttribute="centerY" id="Wyc-9Z-gYa"/>
+                            <constraint firstItem="QvR-ZO-p6W" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="49U-qQ-4uN" secondAttribute="trailing" constant="10" id="bRB-zu-1kI"/>
+                            <constraint firstItem="UrD-Un-gOT" firstAttribute="top" secondItem="3cO-85-Ox1" secondAttribute="top" id="bvX-WF-eM6"/>
+                            <constraint firstItem="IV2-zi-jmv" firstAttribute="leading" secondItem="UrD-Un-gOT" secondAttribute="leading" id="cnk-Zz-l60"/>
+                            <constraint firstAttribute="trailing" secondItem="QvR-ZO-p6W" secondAttribute="trailing" constant="11" id="fgk-vu-WAC"/>
+                            <constraint firstItem="3dh-xL-h1k" firstAttribute="top" secondItem="B1g-pw-mYI" secondAttribute="bottom" constant="10" id="hT9-dL-fWG"/>
+                            <constraint firstItem="UrD-Un-gOT" firstAttribute="leading" secondItem="3cO-85-Ox1" secondAttribute="trailing" constant="10" id="hdz-n1-iJc"/>
+                            <constraint firstItem="3cO-85-Ox1" firstAttribute="top" secondItem="3dh-xL-h1k" secondAttribute="bottom" constant="17" id="jTo-xF-3ih"/>
+                            <constraint firstItem="49U-qQ-4uN" firstAttribute="centerY" secondItem="B1g-pw-mYI" secondAttribute="centerY" id="qxD-34-ckW"/>
+                            <constraint firstItem="IV2-zi-jmv" firstAttribute="bottom" secondItem="3cO-85-Ox1" secondAttribute="bottom" id="rNx-Sz-vHU"/>
+                            <constraint firstItem="49U-qQ-4uN" firstAttribute="leading" secondItem="B1g-pw-mYI" secondAttribute="trailing" constant="7" id="sGV-5F-Php"/>
+                        </constraints>
+                        <userDefinedRuntimeAttributes>
+                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                <real key="value" value="10"/>
+                            </userDefinedRuntimeAttribute>
+                        </userDefinedRuntimeAttributes>
+                    </view>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="yVi-pw-eI6" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="62n-c0-8Ja"/>
+                    <constraint firstAttribute="trailing" secondItem="yVi-pw-eI6" secondAttribute="trailing" constant="14" id="GN1-88-SxQ"/>
+                    <constraint firstItem="yVi-pw-eI6" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="14" id="kcc-Dh-wvc"/>
+                    <constraint firstAttribute="bottom" secondItem="yVi-pw-eI6" secondAttribute="bottom" constant="12" id="nGz-Cw-zKm"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="aW0-zy-SZf"/>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <connections>
+                <outlet property="chatButton" destination="Yoe-Ih-DzZ" id="gQx-81-zPe"/>
+                <outlet property="courseTime" destination="49U-qQ-4uN" id="iaX-q1-K4j"/>
+                <outlet property="studentAvatar" destination="3cO-85-Ox1" id="Eme-uy-gve"/>
+                <outlet property="studentName" destination="UrD-Un-gOT" id="dal-Eo-Oyd"/>
+                <outlet property="studentSubject" destination="4gL-4k-HYe" id="I41-1H-tE7"/>
+            </connections>
+            <point key="canvasLocation" x="67.938931297709928" y="64.436619718309856"/>
+        </tableViewCell>
+    </objects>
+    <resources>
+        <image name="course_chat" width="19" height="18"/>
+        <image name="course_time" width="16" height="16"/>
+        <image name="student_avatar" width="150" height="150"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 0 - 2
KulexiuForStudent/KulexiuForStudent/Module/Home/Controller/HomeViewController.m

@@ -1799,14 +1799,12 @@
         AccompanyDetailViewController *detailVC = [[AccompanyDetailViewController alloc] init];
         detailVC.courseId = courseModel.courseId;
         detailVC.courseGroupId = courseModel.courseGroupId;
-        detailVC.teacherId = courseModel.teacherId;
         [self.navigationController pushViewController:detailVC animated:YES];
     }
     else if (type == RECENTCOURSE_TYPE_MUSICCLASS) { // 琴房课
         MusicRoomDetailViewController *detailVC = [[MusicRoomDetailViewController alloc] init];
         detailVC.courseId = courseModel.courseId;
         detailVC.courseGroupId = courseModel.courseGroupId;
-        detailVC.teacherId = courseModel.teacherId;
         [self.navigationController pushViewController:detailVC animated:YES];
     }
     else {

+ 6 - 1
KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/AccompanyCourseGroup/MyAccompanyCourseGroupView.m

@@ -12,6 +12,7 @@
 #import "Reachability.h"
 #import "ProgramCourseGroupModel.h"
 #import "MyCourseRankSortView.h"
+#import "AccompanyCourseGroupViewController.h"
 
 @interface MyAccompanyCourseGroupView ()<UITableViewDelegate,UITableViewDataSource>
 
@@ -198,7 +199,11 @@
 
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
     // 课程详情
-    
+    // 课程详情
+    ProgramCourseGroupModel *model = self.dataArray[indexPath.row];
+    AccompanyCourseGroupViewController *ctrl = [[AccompanyCourseGroupViewController alloc] init];
+    ctrl.courseGroupId = model.courseGroupId;
+    [self.naviController pushViewController:ctrl animated:YES];
 }
 
 #pragma mark ------ lazying

+ 11 - 11
KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/MusicRoomCourseGroup/MyMusicGroupListCell.xib

@@ -21,8 +21,8 @@
                     <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wVO-PU-gXJ">
                         <rect key="frame" x="14" y="0.0" width="292" height="94"/>
                         <subviews>
-                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="张老师琴房课" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dy9-VA-eiu">
-                                <rect key="frame" x="12" y="12" width="95.333333333333329" height="22"/>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dy9-VA-eiu">
+                                <rect key="frame" x="12" y="12" width="0.0" height="22"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="22" id="p5C-Kg-rww"/>
                                 </constraints>
@@ -30,8 +30,8 @@
                                 <color key="textColor" red="0.074509803920000006" green="0.078431372550000003" blue="0.08235294118" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                 <nil key="highlightedColor"/>
                             </label>
-                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="未开课" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1Ol-S1-0uy">
-                                <rect key="frame" x="238" y="12" width="42" height="17"/>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1Ol-S1-0uy">
+                                <rect key="frame" x="280" y="12" width="0.0" height="0.0"/>
                                 <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
                                 <color key="textColor" red="1" green="0.50196078430000002" blue="0.17254901959999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                 <nil key="highlightedColor"/>
@@ -43,8 +43,8 @@
                                     <constraint firstAttribute="width" constant="40" id="McX-ua-4GD"/>
                                 </constraints>
                             </imageView>
-                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="张涵宇、向冰冰等5人" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hQh-fY-QGG">
-                                <rect key="frame" x="60" y="42" width="128" height="17"/>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hQh-fY-QGG">
+                                <rect key="frame" x="60" y="42" width="0.0" height="17"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="17" id="cta-ti-zwQ"/>
                                 </constraints>
@@ -53,10 +53,10 @@
                                 <nil key="highlightedColor"/>
                             </label>
                             <view contentMode="scaleToFill" horizontalCompressionResistancePriority="752" translatesAutoresizingMaskIntoConstraints="NO" id="vge-mo-L76">
-                                <rect key="frame" x="193" y="42" width="38" height="17"/>
+                                <rect key="frame" x="65" y="42" width="8" height="17"/>
                                 <subviews>
-                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="单簧管" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HuW-TI-Qg2">
-                                        <rect key="frame" x="4" y="2.6666666666666643" width="30" height="12"/>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HuW-TI-Qg2">
+                                        <rect key="frame" x="4" y="8.6666666666666643" width="0.0" height="0.0"/>
                                         <fontDescription key="fontDescription" type="system" pointSize="10"/>
                                         <color key="textColor" red="1" green="0.54901960780000003" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                         <nil key="highlightedColor"/>
@@ -75,8 +75,8 @@
                                     </userDefinedRuntimeAttribute>
                                 </userDefinedRuntimeAttributes>
                             </view>
-                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="已上课时 4 /总课时 6" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pMp-Fa-WxS">
-                                <rect key="frame" x="77.000000000000014" y="64.666666666666671" width="132.33333333333337" height="17"/>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pMp-Fa-WxS">
+                                <rect key="frame" x="77" y="73" width="0.0" height="0.0"/>
                                 <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                 <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="calibratedRGB"/>
                                 <nil key="highlightedColor"/>

+ 5 - 1
KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/MusicRoomCourseGroup/MyMusicRoomCourseGroupView.m

@@ -12,6 +12,7 @@
 #import "Reachability.h"
 #import "ProgramCourseGroupModel.h"
 #import "MyCourseRankSortView.h"
+#import "MusicRoomGroupViewController.h"
 
 @interface MyMusicRoomCourseGroupView ()<UITableViewDelegate,UITableViewDataSource>
 
@@ -196,7 +197,10 @@
 
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
     // 课程组详情
-    
+    ProgramCourseGroupModel *model = self.dataArray[indexPath.row];
+    MusicRoomGroupViewController *ctrl = [[MusicRoomGroupViewController alloc] init];
+    ctrl.courseGroupId = model.courseGroupId;
+    [self.naviController pushViewController:ctrl animated:YES];
 }
 
 #pragma mark ------ lazying

+ 1 - 2
KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/MyLessonBodyView.m

@@ -474,7 +474,7 @@
     AccompanyDetailViewController *detailVC = [[AccompanyDetailViewController alloc] init];
     detailVC.courseId = model.courseId;
     detailVC.courseGroupId = model.courseGoupId;
-    detailVC.teacherId = model.userId;
+//    detailVC.teacherId = model.userId;
     [self.naviController pushViewController:detailVC animated:YES];
 }
 
@@ -514,7 +514,6 @@
     MusicRoomDetailViewController *detailVC = [[MusicRoomDetailViewController alloc] init];
     detailVC.courseId = model.courseId;
     detailVC.courseGroupId = model.courseGroupId;
-    detailVC.teacherId = model.teacherId;
     [self.naviController pushViewController:detailVC animated:YES];
 }
 

+ 11 - 11
KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/VIPCourseGroup/MyProgramCourseGroupCell.xib

@@ -20,8 +20,8 @@
                     <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hOG-9r-FYS">
                         <rect key="frame" x="14" y="0.0" width="292" height="94"/>
                         <subviews>
-                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="VIP定制课-张涵宇" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fSB-R2-WNn">
-                                <rect key="frame" x="12" y="12" width="130.33333333333334" height="22"/>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fSB-R2-WNn">
+                                <rect key="frame" x="12" y="12" width="0.0" height="22"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="22" id="yBY-pf-3Gt"/>
                                 </constraints>
@@ -29,8 +29,8 @@
                                 <color key="textColor" red="0.074509803921568626" green="0.078431372549019607" blue="0.082352941176470587" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                 <nil key="highlightedColor"/>
                             </label>
-                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="未开课" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YcB-0r-jIC">
-                                <rect key="frame" x="238" y="12" width="42" height="17"/>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YcB-0r-jIC">
+                                <rect key="frame" x="280" y="12" width="0.0" height="0.0"/>
                                 <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
                                 <color key="textColor" red="1" green="0.50196078431372548" blue="0.17254901960784313" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                 <nil key="highlightedColor"/>
@@ -47,8 +47,8 @@
                                     </userDefinedRuntimeAttribute>
                                 </userDefinedRuntimeAttributes>
                             </imageView>
-                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="向冰冰" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6KB-MT-3Xa">
-                                <rect key="frame" x="62" y="43" width="39" height="17"/>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6KB-MT-3Xa">
+                                <rect key="frame" x="62" y="43" width="0.0" height="17"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="17" id="6JM-FU-U7f"/>
                                 </constraints>
@@ -60,10 +60,10 @@
                                 <rect key="frame" x="63" y="68" width="12" height="14"/>
                             </imageView>
                             <view contentMode="scaleToFill" horizontalCompressionResistancePriority="752" translatesAutoresizingMaskIntoConstraints="NO" id="fHv-aO-hTk">
-                                <rect key="frame" x="106" y="43" width="38" height="17"/>
+                                <rect key="frame" x="67" y="43" width="8" height="17"/>
                                 <subviews>
-                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="单簧管" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fmo-ut-d02">
-                                        <rect key="frame" x="4" y="2.6666666666666643" width="30" height="12"/>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fmo-ut-d02">
+                                        <rect key="frame" x="4" y="8.6666666666666643" width="0.0" height="0.0"/>
                                         <fontDescription key="fontDescription" type="system" pointSize="10"/>
                                         <color key="textColor" red="1" green="0.5490196078431373" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                         <nil key="highlightedColor"/>
@@ -82,8 +82,8 @@
                                     </userDefinedRuntimeAttribute>
                                 </userDefinedRuntimeAttributes>
                             </view>
-                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="已上课时 34 /总课时 20" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JcJ-gf-HY4">
-                                <rect key="frame" x="79" y="67" width="129" height="16"/>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JcJ-gf-HY4">
+                                <rect key="frame" x="79" y="67" width="0.0" height="16"/>
                                 <constraints>
                                     <constraint firstAttribute="height" constant="16" id="LXf-Qg-nV3"/>
                                 </constraints>

+ 5 - 0
KulexiuForStudent/KulexiuForStudent/Module/Mine/MineCourse/View/VIPCourseGroup/MyVipCourseGroupView.m

@@ -12,6 +12,7 @@
 #import "Reachability.h"
 #import "ProgramCourseGroupModel.h"
 #import "MyCourseRankSortView.h"
+#import "VIPCourseGroupViewController.h"
 
 @interface MyVipCourseGroupView ()<UITableViewDelegate,UITableViewDataSource>
 
@@ -199,6 +200,10 @@
 
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
     // 课程详情
+    ProgramCourseGroupModel *model = self.dataArray[indexPath.row];
+    VIPCourseGroupViewController *ctrl = [[VIPCourseGroupViewController alloc] init];
+    ctrl.courseGroupId = model.courseGroupId;
+    [self.naviController pushViewController:ctrl animated:YES];
 }
 
 #pragma mark ------ lazying