浏览代码

Merge branch 'master' of http://git.dayaedu.com/lex/orchestra-app

mo 2 年之前
父节点
当前提交
cf3512b82b
共有 73 个文件被更改,包括 1426 次插入2241 次删除
  1. 0 376
      dist/project/css/companionTeacher.css
  2. 2 0
      dist/project/css/initiation.css
  3. 0 372
      dist/project/css/manageTeacher.css
  4. 2 0
      dist/project/css/preRegister.css
  5. 2 0
      dist/project/css/schoolRegister.css
  6. 0 334
      public/project/companionTeacher.html
  7. 0 377
      public/project/css/companionTeacher.css
  8. 2 0
      public/project/css/initiation.css
  9. 0 373
      public/project/css/manageTeacher.css
  10. 2 0
      public/project/css/preRegister.css
  11. 2 0
      public/project/css/schoolRegister.css
  12. 0 213
      public/project/manageTeacher.html
  13. 10 2
      public/project/schoolRegister.html
  14. 16 0
      src/router/routes-common.ts
  15. 1 3
      src/school/approval-manage/batch-adjust.tsx
  16. 12 0
      src/school/approval-manage/course-adjust.tsx
  17. 39 1
      src/school/companion-teacher/companion-teacher-register.tsx
  18. 3 1
      src/school/companion-teacher/index.tsx
  19. 1 1
      src/school/companion-teacher/unbind.tsx
  20. 3 1
      src/school/manage-teacher/index.tsx
  21. 2 0
      src/school/manage-teacher/manage-teacher-register.module.less
  22. 36 0
      src/school/manage-teacher/manage-teacher-register.tsx
  23. 5 4
      src/school/train-planning/component/course-preview/index.tsx
  24. 16 13
      src/school/train-planning/modal/timer/index.tsx
  25. 二进制
      src/student/download/images/manage_bg.png
  26. 二进制
      src/student/download/images/student_bg.png
  27. 二进制
      src/student/download/images/teacher_bg.png
  28. 22 3
      src/student/download/index.module.less
  29. 23 5
      src/student/download/index.tsx
  30. 8 4
      src/student/download/transfer.tsx
  31. 1 1
      src/student/home/index.tsx
  32. 2 1
      src/student/music-group/layout/index.module.less
  33. 6 10
      src/student/music-group/pre-apply/component/apply.tsx
  34. 13 15
      src/student/music-group/pre-apply/component/payment.tsx
  35. 3 0
      src/student/music-group/pre-apply/index.module.less
  36. 5 0
      src/student/music-group/pre-apply/index.tsx
  37. 32 20
      src/student/music-group/pre-apply/order-detail.tsx
  38. 1 1
      src/student/music-group/shop-address/address-operation.tsx
  39. 2 2
      src/views/courseList/index.tsx
  40. 12 8
      src/views/coursewarePlay/index.tsx
  41. 3 1
      src/views/exercise-after-class/index.tsx
  42. 二进制
      src/views/layout/images/bottom_manage_bg.png
  43. 二进制
      src/views/layout/images/bottom_student_bg.png
  44. 二进制
      src/views/layout/images/bottom_teacher_bg.png
  45. 二进制
      src/views/layout/images/top_bg.png
  46. 15 1
      src/views/layout/login.module.less
  47. 1 1
      src/views/layout/login.tsx
  48. 2 2
      src/views/lessonCourseware/index.tsx
  49. 二进制
      src/views/unit-test/images/exam-fail-bg.png
  50. 二进制
      src/views/unit-test/images/exam-left-btn.png
  51. 二进制
      src/views/unit-test/images/exam-pass-bg.png
  52. 二进制
      src/views/unit-test/images/exam-right-bg.png
  53. 二进制
      src/views/unit-test/images/icon-error.png
  54. 二进制
      src/views/unit-test/images/icon-song.png
  55. 二进制
      src/views/unit-test/images/icon-tag.png
  56. 9 1
      src/views/unit-test/index.tsx
  57. 9 3
      src/views/unit-test/model/choice-question/index.module.less
  58. 5 0
      src/views/unit-test/model/choice-question/index.tsx
  59. 9 3
      src/views/unit-test/model/drag-question/index.module.less
  60. 2 2
      src/views/unit-test/model/drag-question/index.tsx
  61. 49 0
      src/views/unit-test/model/error-mode/index.module.less
  62. 42 0
      src/views/unit-test/model/error-mode/index.tsx
  63. 32 12
      src/views/unit-test/model/keep-look-question/index.module.less
  64. 239 61
      src/views/unit-test/model/keep-look-question/index.tsx
  65. 87 0
      src/views/unit-test/model/play-question/index.module.less
  66. 82 0
      src/views/unit-test/model/play-question/index.tsx
  67. 74 0
      src/views/unit-test/model/result-finish/index.module.less
  68. 51 0
      src/views/unit-test/model/result-finish/index.tsx
  69. 46 0
      src/views/unit-test/practice-mode/index.module.less
  70. 216 0
      src/views/unit-test/practice-mode/index.tsx
  71. 86 0
      src/views/unit-test/test-exercise/index.module.less
  72. 58 0
      src/views/unit-test/test-exercise/index.tsx
  73. 23 13
      src/views/unit-test/unit-detail/index.tsx

+ 0 - 376
dist/project/css/companionTeacher.css

@@ -1,376 +0,0 @@
-html,
-body,
-p,
-div,
-span {
-  padding: 0;
-  margin: 0;
-  border: 0;
-}
-
-.m-toast {
-  position: fixed;
-  top: 50%;
-  left: 50%;
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: flex;
-  -webkit-box-orient: vertical;
-  -webkit-box-direction: normal;
-  -webkit-flex-direction: column;
-  flex-direction: column;
-  -webkit-box-align: center;
-  -webkit-align-items: center;
-  align-items: center;
-  -webkit-box-pack: center;
-  -webkit-justify-content: center;
-  justify-content: center;
-  box-sizing: content-box;
-  width: 88px;
-  max-width: 70%;
-  min-height: 88px;
-  padding: 16px;
-  color: #fff;
-  font-size: 14px;
-  line-height: 20px;
-  white-space: pre-wrap;
-  text-align: center;
-  word-wrap: break-word;
-  background-color: rgba(50, 50, 51, 0.88);
-  border-radius: 4px;
-  -webkit-transform: translate3d(-50%, -50%, 0);
-  transform: translate3d(-50%, -50%, 0);
-  z-index: 2002
-}
-
-.m-loading {
-  position: relative;
-  color: #fff;
-  font-size: 0;
-  vertical-align: middle;
-  padding: 4px
-}
-
-.m-loading__spinner {
-  position: relative;
-  display: inline-block;
-  width: 30px;
-  max-width: 100%;
-  height: 30px;
-  max-height: 100%;
-  vertical-align: middle;
-  -webkit-animation: m-rotate .8s linear infinite;
-  animation: m-rotate .8s linear infinite
-}
-
-.m-loading__spinner--circular {
-  -webkit-animation-duration: 2s;
-  animation-duration: 2s
-}
-
-.m-loading__circular {
-  display: block;
-  width: 100%;
-  height: 100%
-}
-
-.m-loading__circular circle {
-  -webkit-animation: m-circular 1.5s ease-in-out infinite;
-  animation: m-circular 1.5s ease-in-out infinite;
-  stroke: currentColor;
-  stroke-width: 3;
-  stroke-linecap: round
-}
-
-.m-loading__text {
-  display: inline-block;
-  margin-left: 8px;
-  color: #969799;
-  font-size: 14px;
-  vertical-align: middle
-}
-
-.m-loading--vertical {
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: flex;
-  -webkit-box-orient: vertical;
-  -webkit-box-direction: normal;
-  -webkit-flex-direction: column;
-  flex-direction: column;
-  -webkit-box-align: center;
-  -webkit-align-items: center;
-  align-items: center
-}
-
-.m-loading--vertical .m-loading__text {
-  margin: 8px 0 0
-}
-
-@-webkit-keyframes m-circular {
-  0% {
-    stroke-dasharray: 1, 200;
-    stroke-dashoffset: 0
-  }
-
-  50% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -40
-  }
-
-  100% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -120
-  }
-}
-
-@keyframes m-circular {
-  0% {
-    stroke-dasharray: 1, 200;
-    stroke-dashoffset: 0
-  }
-
-  50% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -40
-  }
-
-  100% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -120
-  }
-}
-
-.m-toast__text {
-  margin-top: 8px
-}
-
-.van-field--error .van-field__control,
-.van-field--error .van-field__control::placeholder {
-  color: #c8c9cc;
-  -webkit-text-fill-color: currentColor;
-}
-
-#app {
-  background: url('../images/manageTeacher/banner1.png') no-repeat top center #F8F8F8;
-  background-size: contain;
-  max-width: 750px;
-  margin: 0 auto;
-  min-height: 100vh;
-  overflow: hidden;
-}
-
-.banner,
-.banner img {
-  width: 100%;
-  font-size: 0;
-}
-
-.van-cell {
-  flex-direction: column;
-  font-size: 16px;
-  padding: 14px 13px;
-}
-
-.van-field__label {
-  width: 100%;
-  margin-right: 0;
-  color: #333;
-  font-size: 16px;
-  font-weight: 500;
-}
-
-.van-cell--required::before {
-  left: 15px;
-}
-
-.van-field__body {
-  margin-top: 10px;
-}
-
-.btn-submit {
-  width: 90%;
-  margin: 20px auto;
-}
-
-.system h2 {
-  font-size: 18px;
-  font-weight: 500;
-  margin: 10px 24px;
-  color: #444444;
-}
-
-.system .van-cell--required::before {
-  left: 25px;
-}
-
-.cell-group {
-  margin: 0 13px 14px;
-  border-radius: 10px;
-  padding-bottom: 10px;
-}
-
-.van-form {
-  margin-top: 194px;
-  /* background: #F8F8F8; */
-  overflow: hidden;
-  /* margin: 290px 13px 14px; */
-}
-
-.top-tips {
-  margin: 30px 13px 20px;
-  padding: 9px 7px;
-  background: #FFFFFF;
-  border-radius: 10px;
-  border: 5px solid #BE93FF;
-  font-size: 14px;
-  font-weight: 500;
-  color: #724DA9;
-  line-height: 20px;
-}
-
-.title {
-  position: absolute;
-  top: 0;
-  left: 0;
-  padding: 115px 24px 0;
-  width: 57%;
-  /* height: 154px; */
-  font-size: 28px;
-  font-weight: 600;
-  color: #FFFFFF;
-  line-height: 34px;
-  letter-spacing: 1px;
-  /* text-shadow: 0px 2px 3px rgba(0, 75, 255, 0.5); */
-}
-
-.tips {
-  font-size: 16px;
-  font-weight: 500;
-  color: #FFFFFF;
-  line-height: 22px;
-  text-shadow: 0px 1px 5px #FF5E20;
-  display: flex;
-  align-items: center;
-}
-
-.tips img {
-  margin-right: 8px;
-  width: 18px;
-  height: 18px;
-}
-
-
-.radioSection {
-  position: relative;
-}
-
-.radioItem {
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  opacity: 0;
-}
-
-.radioSection+.radioSection {
-  margin-left: 12px;
-}
-
-.van-picker__confirm {
-  color: #F67146 !important;
-}
-
-/* 弹窗 */
-.stautsS {
-  position: relative;
-}
-
-.submit-container {
-  padding: 20px 26px 26px;
-
-  text-align: center;
-}
-
-.icon-close {
-  position: absolute;
-  width: 26px;
-  height: 26px;
-  top: 12px;
-  right: 12px;
-}
-
-.submit-img {
-  width: 100%;
-}
-
-.submit-title {
-  font-size: 18px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 25px;
-}
-
-.submit-o {
-  padding-top: 10px;
-  font-size: 15px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 21px;
-}
-
-.submit-o span {
-  color: #F67146;
-}
-
-.submit-tips {
-  font-size: 14px;
-  color: #777777;
-  line-height: 20px;
-  padding-top: 5px;
-  padding-bottom: 20px;
-}
-
-.submit-container .van-button {
-  font-size: 18px;
-  font-weight: 500;
-}
-
-.cell_title {
-  margin: 0 13px 8px;
-  display: inline-block;
-  padding: 2px 6px 3px;
-  background: #64A9FF;
-  border-radius: 6px;
-  font-size: 15px;
-  font-weight: 600;
-  color: #FFFFFF;
-}
-
-.van-tag+.van-tag {
-  margin-left: 8px;
-}
-
-.protocol {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 12px;
-  color: #AAAAAA;
-  line-height: 17px;
-}
-
-.protocol .c {
-  color: #F67146;
-}
-
-
-.phoneTips {
-  margin: 12px 12px 3px;
-  background: #FFEBDD;
-  border-radius: 6px;
-  font-size: 13px;
-  color: #F67146;
-  padding: 9px 10px;
-}

+ 2 - 0
dist/project/css/initiation.css

@@ -104,6 +104,8 @@ span {
 
 .radioSection {
   position: relative;
+  min-width: 32px;
+    justify-content: center;
 }
 
 .radioItem {

+ 0 - 372
dist/project/css/manageTeacher.css

@@ -1,372 +0,0 @@
-html,
-body,
-p,
-div,
-span {
-  padding: 0;
-  margin: 0;
-  border: 0;
-}
-
-.m-toast {
-  position: fixed;
-  top: 50%;
-  left: 50%;
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: flex;
-  -webkit-box-orient: vertical;
-  -webkit-box-direction: normal;
-  -webkit-flex-direction: column;
-  flex-direction: column;
-  -webkit-box-align: center;
-  -webkit-align-items: center;
-  align-items: center;
-  -webkit-box-pack: center;
-  -webkit-justify-content: center;
-  justify-content: center;
-  box-sizing: content-box;
-  width: 88px;
-  max-width: 70%;
-  min-height: 88px;
-  padding: 16px;
-  color: #fff;
-  font-size: 14px;
-  line-height: 20px;
-  white-space: pre-wrap;
-  text-align: center;
-  word-wrap: break-word;
-  background-color: rgba(50, 50, 51, 0.88);
-  border-radius: 4px;
-  -webkit-transform: translate3d(-50%, -50%, 0);
-  transform: translate3d(-50%, -50%, 0);
-  z-index: 2002
-}
-
-.m-loading {
-  position: relative;
-  color: #fff;
-  font-size: 0;
-  vertical-align: middle;
-  padding: 4px
-}
-
-.m-loading__spinner {
-  position: relative;
-  display: inline-block;
-  width: 30px;
-  max-width: 100%;
-  height: 30px;
-  max-height: 100%;
-  vertical-align: middle;
-  -webkit-animation: m-rotate .8s linear infinite;
-  animation: m-rotate .8s linear infinite
-}
-
-.m-loading__spinner--circular {
-  -webkit-animation-duration: 2s;
-  animation-duration: 2s
-}
-
-.m-loading__circular {
-  display: block;
-  width: 100%;
-  height: 100%
-}
-
-.m-loading__circular circle {
-  -webkit-animation: m-circular 1.5s ease-in-out infinite;
-  animation: m-circular 1.5s ease-in-out infinite;
-  stroke: currentColor;
-  stroke-width: 3;
-  stroke-linecap: round
-}
-
-.m-loading__text {
-  display: inline-block;
-  margin-left: 8px;
-  color: #969799;
-  font-size: 14px;
-  vertical-align: middle
-}
-
-.m-loading--vertical {
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: flex;
-  -webkit-box-orient: vertical;
-  -webkit-box-direction: normal;
-  -webkit-flex-direction: column;
-  flex-direction: column;
-  -webkit-box-align: center;
-  -webkit-align-items: center;
-  align-items: center
-}
-
-.m-loading--vertical .m-loading__text {
-  margin: 8px 0 0
-}
-
-@-webkit-keyframes m-circular {
-  0% {
-    stroke-dasharray: 1, 200;
-    stroke-dashoffset: 0
-  }
-
-  50% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -40
-  }
-
-  100% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -120
-  }
-}
-
-@keyframes m-circular {
-  0% {
-    stroke-dasharray: 1, 200;
-    stroke-dashoffset: 0
-  }
-
-  50% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -40
-  }
-
-  100% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -120
-  }
-}
-
-.m-toast__text {
-  margin-top: 8px
-}
-
-.van-field--error .van-field__control,
-.van-field--error .van-field__control::placeholder {
-  color: #c8c9cc;
-  -webkit-text-fill-color: currentColor;
-}
-
-#app {
-  background: url('../images/manageTeacher/banner.png') no-repeat top center #F8F8F8;
-  background-size: contain;
-  max-width: 750px;
-  margin: 0 auto;
-  min-height: 100vh;
-  overflow: hidden;
-}
-
-.banner,
-.banner img {
-  width: 100%;
-  font-size: 0;
-}
-
-.van-cell {
-  flex-direction: column;
-  font-size: 16px;
-  padding: 14px 13px;
-}
-
-.van-field__label {
-  width: 100%;
-  margin-right: 0;
-  color: #333;
-  font-size: 16px;
-  font-weight: 500;
-}
-
-.van-cell--required::before {
-  left: 15px;
-}
-
-.van-field__body {
-  margin-top: 10px;
-}
-
-.btn-submit {
-  width: 90%;
-  margin: 20px auto;
-}
-
-.system h2 {
-  font-size: 18px;
-  font-weight: 500;
-  margin: 10px 24px;
-  color: #444444;
-}
-
-.system .van-cell--required::before {
-  left: 25px;
-}
-
-.cell-group {
-  margin: 0 13px 14px;
-  border-radius: 10px;
-  padding-bottom: 20px;
-}
-
-.van-form {
-  margin-top: 194px;
-  /* background: #F8F8F8; */
-  overflow: hidden;
-  /* margin: 290px 13px 14px; */
-}
-
-.top-tips {
-  margin: 30px 13px 20px;
-  padding: 9px 7px;
-  background: #FFFFFF;
-  border-radius: 10px;
-  border: 5px solid #BE93FF;
-  font-size: 14px;
-  font-weight: 500;
-  color: #724DA9;
-  line-height: 20px;
-}
-
-.title {
-  position: absolute;
-  top: 0;
-  left: 0;
-  padding: 115px 24px 0;
-  width: 57%;
-  /* height: 154px; */
-  font-size: 28px;
-  font-weight: 600;
-  color: #FFFFFF;
-  line-height: 34px;
-  letter-spacing: 1px;
-  /* text-shadow: 0px 2px 3px rgba(0, 75, 255, 0.5); */
-}
-
-.tips {
-  font-size: 16px;
-  font-weight: 500;
-  color: #FFFFFF;
-  line-height: 22px;
-  text-shadow: 0px 1px 5px #2F92FF;
-  display: flex;
-  align-items: center;
-}
-
-.tips img {
-  margin-right: 8px;
-  width: 18px;
-  height: 18px;
-}
-
-
-.radioSection {
-  position: relative;
-}
-
-.radioItem {
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  opacity: 0;
-}
-
-.radioSection+.radioSection {
-  margin-left: 12px;
-}
-
-.van-picker__confirm {
-  color: #64A9FF !important;
-}
-
-/* 弹窗 */
-.stautsS {
-  position: relative;
-}
-
-.submit-container {
-  padding: 20px 26px 26px;
-
-  text-align: center;
-}
-
-.icon-close {
-  position: absolute;
-  width: 26px;
-  height: 26px;
-  top: 12px;
-  right: 12px;
-}
-
-.submit-img {
-  width: 100%;
-}
-
-.submit-title {
-  font-size: 18px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 25px;
-}
-
-.submit-o {
-  padding-top: 10px;
-  font-size: 15px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 21px;
-}
-
-.submit-o span {
-  color: #64A9FF;
-}
-
-.submit-tips {
-  font-size: 14px;
-  color: #777777;
-  line-height: 20px;
-  padding-top: 5px;
-  padding-bottom: 20px;
-}
-
-.submit-container .van-button {
-  font-size: 18px;
-  font-weight: 500;
-}
-
-.cell_title {
-  margin: 0 13px 8px;
-  display: inline-block;
-  padding: 2px 6px 3px;
-  background: #64A9FF;
-  border-radius: 6px;
-  font-size: 15px;
-  font-weight: 600;
-  color: #FFFFFF;
-}
-
-.protocol {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 12px;
-  color: #AAAAAA;
-  line-height: 17px;
-}
-
-.protocol .c {
-  color: #F67146;
-}
-
-
-.phoneTips {
-  margin: 12px;
-  background: #FFEBDD;
-  border-radius: 6px;
-  font-size: 13px;
-  color: #F67146;
-  padding: 9px 10px;
-}

+ 2 - 0
dist/project/css/preRegister.css

@@ -104,6 +104,8 @@ span {
 
 .radioSection {
   position: relative;
+  min-width: 32px;
+    justify-content: center;
 }
 
 .radioItem {

+ 2 - 0
dist/project/css/schoolRegister.css

@@ -255,6 +255,8 @@ span {
 
 .radioSection {
   position: relative;
+  min-width: 32px;
+    justify-content: center;
 }
 
 .radioItem {

+ 0 - 334
public/project/companionTeacher.html

@@ -1,334 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-  <meta charset="utf-8">
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="apple-mobile-web-app-capable" content="yes">
-  <meta name="apple-mobile-web-app-status-bar-style" content="black">
-  <meta name="format-detection" content="telephone=no">
-  <meta name="mobile-web-app-capable" content="yes">
-  <meta name="msapplication-tap-highlight" content="no">
-  <meta name="fragment" content="!">
-  <meta name="viewport"
-    content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0, viewport-fit=cover">
-  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
-  <meta http-equiv="Pragma" content="no-cache" />
-  <meta http-equiv="Expires" content="0" />
-  <meta http-equiv="Cache" content="no-cache">
-  <meta name="description" content="">
-
-  <link rel="icon" href="../favicon.ico">
-  <title>乐团伴学指导注册</title>
-  <!-- 引入样式文件 -->
-  <link rel="stylesheet" href="./js/vant-index.css" />
-  <link rel="stylesheet" href="./css/companionTeacher.css">
-  <script src="./js/flexible.js"></script>
-</head>
-
-<body>
-  <style>
-  </style>
-  <div id="m_loading" class="m-toast m-toast--middle m-toast--loading" style="z-index: 2001;">
-    <div class="m-loading m-loading--circular m-toast__loading"><span
-        class="m-loading__spinner m-loading__spinner--circular"><svg viewBox="25 25 50 50" class="m-loading__circular">
-          <circle cx="50" cy="50" r="20" fill="none"></circle>
-        </svg></span></div>
-    <div class="m-toast__text">加载中...</div>
-  </div>
-  <div id="app">
-    <div class="title">
-      <!-- 欢迎使用管乐团双师训练系统
-      <p class="tips">办乐团就用管乐团!</p> -->
-      <p class="tips">
-        <img src="./images/manageTeacher/school-logo.png" />
-        <span v-html="decodeURI(name)"></span>
-      </p>
-    </div>
-    <van-form validate-first scroll-to-error @submit="onSubmit" ref='form' class="form">
-      <van-cell-group inset class="cell-group">
-        <van-field required label="真实姓名" v-model="forms.realName" :rules="[{ required: true, message: '请填写真实姓名' }]"
-          name="realName" placeholder="请填写真实姓名" maxlength="50">
-        </van-field>
-
-        <van-field required label="手机号码" v-model="forms.phone" type="tel" maxlength="11" minlength="11"
-          :rules="[{ required: true, message: '请输入手机号码' },{ pattern, message: '输入手机号有误' }]" name="phone"
-          placeholder="请输入学校手机号码">
-        </van-field>
-        <div class="phoneTips">
-          <van-icon name="warning" size="16" />
-          提示:手机号码将成为您管乐团管理端登录账
-        </div>
-
-        <van-field required label="身份证号码" v-model="forms.idCardNo" :rules="[
-                { required: true, message: '请输入身份证号' },
-                {
-                  pattern:
-                    /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
-                  message: '请输入正确的身份证号'
-                }
-              ]" name="idCardNo" placeholder="请输入身份证号码">
-        </van-field>
-
-        <van-field required label="性别" name="gender" :rules="[{ required: true, message: '请选择性别' }]">
-          <template #input>
-            <van-radio-group checked-color="#FF8057" v-model="forms.gender" direction="horizontal">
-              <van-tag size="large" type="primary" :plain="!(forms.gender === 1)" color="#FF8057" class="radioSection"
-                round>
-                <van-radio class="radioItem" :name="1"></van-radio>男
-              </van-tag>
-              <van-tag size="large" type="primary" :plain="!(forms.gender === 0)" color="#FF8057" class="radioSection"
-                round>
-                <van-radio class="radioItem" :name="0"></van-radio>女
-              </van-tag>
-            </van-radio-group>
-          </template>
-        </van-field>
-
-        <van-field required label="所在城市" v-model="forms.cityCodeName" readonly name="cityCodeName"
-          @click="showPicker = true" :rules="[{ required: true, message: '请选择所在城市', trigger: 'onChange' }]"
-          placeholder="请选择所在城市">
-          <template #right-icon>
-            <van-icon name="arrow" :color="checkPhone ? '#aaa' : '#323233'" size="16"></van-icon>
-          </template>
-        </van-field>
-
-        <van-field required label="声部(可多选)" v-model="forms.showSubjectIds" readonly name="showSubjectIds"
-          @click="showSubject = true" :rules="[{ required: true, message: '请选择声部', trigger: 'onChange' }]"
-          placeholder="请选择声部">
-          <template #right-icon>
-            <van-icon name="arrow" :color="checkPhone ? '#aaa' : '#323233'" size="16"></van-icon>
-          </template>
-          <template #input>
-            <div class="subjectPlaceholder" style="color:#c8c9cc" v-if="forms.subjectIds.length <= 0">请选择声部</div>
-            <div v-else>
-              <van-tag closeable size="medium" color="#FF8057" v-for="(item, index) in selectSubjects"
-                @close="onSubjectRemove(item, index)">{{ item.text }}</van-tag>
-            </div>
-          </template>
-        </van-field>
-        <van-field required label="验证码" v-model="forms.smsValidCode" name="smsValidCode"
-          :rules="[{ required: true, message: '请输入验证码', trigger: 'onChange' }]" placeholder="请输入验证码">
-          <template #button>
-            <van-button type="primary" round size="small" color="#ff8057">发送验证码</van-button>
-          </template>
-        </van-field>
-      </van-cell-group>
-
-      <div class="protocol">
-        <van-checkbox v-model="checked" icon-size="16" style="margin-right: 6px" checked-color="#FF8057"></van-checkbox>
-        <span @click="() => { checked = !checked }">请认真阅读并勾选</span><span class="c">《乐团伴学指导注册协议》</span>
-      </div>
-
-      <van-button size="large" block round class="btn-submit" color="#FF8057" :loading="btnLoading"
-        native-type="submit">完成</van-button>
-    </van-form>
-
-    <van-popup v-model:show="showPicker" position="bottom" round>
-      <van-picker show-toolbar :columns="columns" @cancel="showPicker = false" @confirm="onConfirm"
-        :columns-field-names="{ text: 'name', value: 'code', children: 'areas' }" />
-    </van-popup>
-
-    <van-popup v-model:show="showSubject" position="bottom" round>
-      <van-picker show-toolbar :columns="columnSubject" @cancel="showSubject = false" @confirm="onConfirmSubject" />
-    </van-popup>
-
-    <van-popup v-model:show="submitStatus" round style="width: 75%" :close-on-click-overlay="false">
-      <div class="stautsS">
-        <img class="icon-close" src="./images/initiation/icon-close.png" @click="submitStatus = false" />
-        <img src="./images/manageTeacher/top-banner1.png" class="submit-img" />
-        <div class="submit-container">
-          <p class="submit-title">恭喜您已成功登记为</p>
-          <p class="submit-o">{{ decodeURI(name) }} <span>【伴学指导】</span></p>
-          <p class="submit-tips">请下载管乐团老师端APP进行授课</p>
-          <van-button type="primary" color="#FF8057" block round @click="submitStatus = false">立即下载</van-button>
-        </div>
-      </div>
-    </van-popup>
-  </div>
-
-  <!-- 引入 Vue 和 Vant 的 JS 文件 -->
-  <script src="./js/vue.global.min.js"></script>
-  <script src="./js/vant.min.js"></script>
-  <script src="./js/axios.js"></script>
-  <script type="text/javascript" src="./js/utils.js"></script>
-  <script type="text/javascript" src="./js/area.js"></script>
-  <script>
-    var app = Vue.createApp({
-      data() {
-        return {
-          showPicker: false,
-          showSubject: false,
-          submitStatus: false,
-          id: getQueryVariable('id'),
-          name: getQueryVariable('name'),
-          pattern: /^1(3|4|5|6|7|8|9)\d{9}$/,
-          columns: [],
-          pickerType: null, // 下拉类型
-          selectSubjects: [], // 选中的声部
-          forms: {
-            realName: '',
-            phone: null,
-            gender: 1,
-            idCardNo: null,
-            cityCode: null,
-            cityCodeName: '',
-            provinceCode: null,
-            showSubjectIds: '',
-            subjectIds: [],
-            smsValidCode: '',
-          },
-          btnLoading: false,
-          checkPhone: false,
-          checked: true,
-          columnSubject: []
-        }
-      },
-      async mounted() {
-        if (document.querySelector('#m_loading')) {
-          document.querySelector('#m_loading').remove()
-        }
-        if (!this.id) {
-          vant.showToast('信息获取失败,请联系老师')
-        }
-        try {
-          this.setLoading(true)
-          const tempareas = []
-          areas.forEach(item => {
-            const temp = {
-              name: item.name,
-              code: item.code,
-              areas: []
-            }
-            if (item.areas && item.areas.length > 0) {
-              item.areas.forEach(child => {
-                temp.areas.push({
-                  name: child.name,
-                  code: child.code,
-                })
-              })
-            }
-            tempareas.push(temp)
-          })
-          this.columns = tempareas || []
-
-          var res = await axios.post('/api-school/open/subject/page', {
-            page: 1,
-            rows: 50
-          })
-          if (res.data.code === 200) {
-            var rows = res.data.data.rows || []
-            var tempSubjects = []
-            rows.forEach(item => {
-              tempSubjects.push({
-                text: item.name,
-                value: item.id
-              })
-            })
-            this.columnSubject = tempSubjects
-          } else {
-            vant.showToast(res.data.message)
-          }
-        } catch {
-          vant.showDialog({
-            message: '信息获取失败,请联系老师',
-            theme: 'round-button',
-            confirmButtonColor: '#64A9FF'
-          })
-        }
-        this.setLoading(false)
-      },
-      methods: {
-        async onSubmit() {
-          if (!this.checked) {
-            vant.showToast('请阅读并同意协议')
-            return
-          }
-          this.btnLoading = true
-          try {
-            var forms = this.forms
-            var res = await axios.post('/api-school/open/schoolTeacherStudent/registerTeacher', {
-              ...forms,
-              subjectIds: forms.subjectIds.join(','),
-              smsValidCode: 666666,
-              schoolId: this.id,
-            })
-            if (res.data.code === 200) {
-              this.submitStatus = true
-            } else {
-              vant.showToast(res.data.message)
-            }
-          } catch {
-            vant.showToast('保存失败,请重试')
-          }
-          this.btnLoading = false
-        },
-        setLoading(status) {
-          if (status) {
-            vant.showLoadingToast({
-              duration: 0, // 持续展示 toast
-              message: '加载中...',
-              forbidClick: true,
-              loadingType: 'spinner',
-            })
-          } else {
-            vant.closeToast()
-          }
-        },
-        onConfirm(val) {
-          const selectedOptions = val.selectedOptions[1]
-          this.forms.cityCode = selectedOptions.code
-          this.forms.cityCodeName = selectedOptions.name
-          const selectedFirst = val.selectedOptions[0]
-          this.forms.provinceCode = selectedFirst.code
-          this.showPicker = false
-        },
-        onSubjectRemove(item, index) {
-          vant.showDialog({
-            title: '提示',
-            message: '您是否删除选中的声部',
-            confirmButtonColor: '#ff8057',
-            showCancelButton: true,
-          }).then(() => {
-            this.selectSubjects.splice(index, 1)
-            var tempSubjectIds = []
-            this.selectSubjects.forEach(subject => {
-              tempSubjectIds.push(subject.value)
-            })
-            this.forms.subjectIds = tempSubjectIds
-            this.forms.showSubjectIds = tempSubjectIds.join(',')
-          })
-        },
-        // 选择声部
-        onConfirmSubject(val) {
-          // console.log(val, 'val', val.selectedOptions[0])
-          var selected = val.selectedOptions[0]
-          var isCheck = false
-          this.selectSubjects.forEach(subject => {
-            if (subject.value === selected.value) {
-              isCheck = true
-            }
-          })
-          // 判断是否有选择一样的数据
-          if (isCheck) {
-            this.showSubject = false
-            return
-          }
-
-          this.selectSubjects.push(val.selectedOptions[0])
-          var tempSubjectIds = []
-          this.selectSubjects.forEach(subject => {
-            tempSubjectIds.push(subject.value)
-          })
-          this.forms.subjectIds = tempSubjectIds
-          this.forms.showSubjectIds = tempSubjectIds.join(',')
-          this.showSubject = false
-        }
-      }
-    })
-    app.use(vant)
-    app.mount('#app')
-  </script>
-</body>
-
-</html>

+ 0 - 377
public/project/css/companionTeacher.css

@@ -1,377 +0,0 @@
-html,
-body,
-p,
-div,
-span {
-  padding: 0;
-  margin: 0;
-  border: 0;
-}
-
-.m-toast {
-  position: fixed;
-  top: 50%;
-  left: 50%;
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: flex;
-  -webkit-box-orient: vertical;
-  -webkit-box-direction: normal;
-  -webkit-flex-direction: column;
-  flex-direction: column;
-  -webkit-box-align: center;
-  -webkit-align-items: center;
-  align-items: center;
-  -webkit-box-pack: center;
-  -webkit-justify-content: center;
-  justify-content: center;
-  box-sizing: content-box;
-  width: 88px;
-  max-width: 70%;
-  min-height: 88px;
-  padding: 16px;
-  color: #fff;
-  font-size: 14px;
-  line-height: 20px;
-  white-space: pre-wrap;
-  text-align: center;
-  word-wrap: break-word;
-  background-color: rgba(50, 50, 51, 0.88);
-  border-radius: 4px;
-  -webkit-transform: translate3d(-50%, -50%, 0);
-  transform: translate3d(-50%, -50%, 0);
-  z-index: 2002
-}
-
-.m-loading {
-  position: relative;
-  color: #fff;
-  font-size: 0;
-  vertical-align: middle;
-  padding: 4px
-}
-
-.m-loading__spinner {
-  position: relative;
-  display: inline-block;
-  width: 30px;
-  max-width: 100%;
-  height: 30px;
-  max-height: 100%;
-  vertical-align: middle;
-  -webkit-animation: m-rotate .8s linear infinite;
-  animation: m-rotate .8s linear infinite
-}
-
-.m-loading__spinner--circular {
-  -webkit-animation-duration: 2s;
-  animation-duration: 2s
-}
-
-.m-loading__circular {
-  display: block;
-  width: 100%;
-  height: 100%
-}
-
-.m-loading__circular circle {
-  -webkit-animation: m-circular 1.5s ease-in-out infinite;
-  animation: m-circular 1.5s ease-in-out infinite;
-  stroke: currentColor;
-  stroke-width: 3;
-  stroke-linecap: round
-}
-
-.m-loading__text {
-  display: inline-block;
-  margin-left: 8px;
-  color: #969799;
-  font-size: 14px;
-  vertical-align: middle
-}
-
-.m-loading--vertical {
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: flex;
-  -webkit-box-orient: vertical;
-  -webkit-box-direction: normal;
-  -webkit-flex-direction: column;
-  flex-direction: column;
-  -webkit-box-align: center;
-  -webkit-align-items: center;
-  align-items: center
-}
-
-.m-loading--vertical .m-loading__text {
-  margin: 8px 0 0
-}
-
-@-webkit-keyframes m-circular {
-  0% {
-    stroke-dasharray: 1, 200;
-    stroke-dashoffset: 0
-  }
-
-  50% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -40
-  }
-
-  100% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -120
-  }
-}
-
-@keyframes m-circular {
-  0% {
-    stroke-dasharray: 1, 200;
-    stroke-dashoffset: 0
-  }
-
-  50% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -40
-  }
-
-  100% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -120
-  }
-}
-
-.m-toast__text {
-  margin-top: 8px
-}
-
-.van-field--error .van-field__control,
-.van-field--error .van-field__control::placeholder {
-  color: #c8c9cc;
-  -webkit-text-fill-color: currentColor;
-}
-
-#app {
-  background: url('../images/manageTeacher/banner1.png') no-repeat top center #F8F8F8;
-  background-size: contain;
-  max-width: 750px;
-  margin: 0 auto;
-  min-height: 100vh;
-  overflow: hidden;
-  position: relative;
-}
-
-.banner,
-.banner img {
-  width: 100%;
-  font-size: 0;
-}
-
-.van-cell {
-  flex-direction: column;
-  font-size: 16px;
-  padding: 14px 13px;
-}
-
-.van-field__label {
-  width: 100%;
-  margin-right: 0;
-  color: #333;
-  font-size: 16px;
-  font-weight: 500;
-}
-
-.van-cell--required::before {
-  left: 15px;
-}
-
-.van-field__body {
-  margin-top: 10px;
-}
-
-.btn-submit {
-  width: 90%;
-  margin: 20px auto;
-}
-
-.system h2 {
-  font-size: 18px;
-  font-weight: 500;
-  margin: 10px 24px;
-  color: #444444;
-}
-
-.system .van-cell--required::before {
-  left: 25px;
-}
-
-.cell-group {
-  margin: 0 13px 14px;
-  border-radius: 10px;
-  padding-bottom: 10px;
-}
-
-.van-form {
-  margin-top: 194px;
-  /* background: #F8F8F8; */
-  overflow: hidden;
-  /* margin: 290px 13px 14px; */
-}
-
-.top-tips {
-  margin: 30px 13px 20px;
-  padding: 9px 7px;
-  background: #FFFFFF;
-  border-radius: 10px;
-  border: 5px solid #BE93FF;
-  font-size: 14px;
-  font-weight: 500;
-  color: #724DA9;
-  line-height: 20px;
-}
-
-.title {
-  position: absolute;
-  top: 0;
-  left: 0;
-  padding: 115px 24px 0;
-  width: 57%;
-  /* height: 154px; */
-  font-size: 28px;
-  font-weight: 600;
-  color: #FFFFFF;
-  line-height: 34px;
-  letter-spacing: 1px;
-  /* text-shadow: 0px 2px 3px rgba(0, 75, 255, 0.5); */
-}
-
-.tips {
-  font-size: 16px;
-  font-weight: 500;
-  color: #FFFFFF;
-  line-height: 22px;
-  text-shadow: 0px 1px 5px #FF5E20;
-  display: flex;
-  align-items: center;
-}
-
-.tips img {
-  margin-right: 8px;
-  width: 18px;
-  height: 18px;
-}
-
-
-.radioSection {
-  position: relative;
-}
-
-.radioItem {
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  opacity: 0;
-}
-
-.radioSection+.radioSection {
-  margin-left: 12px;
-}
-
-.van-picker__confirm {
-  color: #F67146 !important;
-}
-
-/* 弹窗 */
-.stautsS {
-  position: relative;
-}
-
-.submit-container {
-  padding: 20px 26px 26px;
-
-  text-align: center;
-}
-
-.icon-close {
-  position: absolute;
-  width: 26px;
-  height: 26px;
-  top: 12px;
-  right: 12px;
-}
-
-.submit-img {
-  width: 100%;
-}
-
-.submit-title {
-  font-size: 18px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 25px;
-}
-
-.submit-o {
-  padding-top: 10px;
-  font-size: 15px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 21px;
-}
-
-.submit-o span {
-  color: #F67146;
-}
-
-.submit-tips {
-  font-size: 14px;
-  color: #777777;
-  line-height: 20px;
-  padding-top: 5px;
-  padding-bottom: 20px;
-}
-
-.submit-container .van-button {
-  font-size: 18px;
-  font-weight: 500;
-}
-
-.cell_title {
-  margin: 0 13px 8px;
-  display: inline-block;
-  padding: 2px 6px 3px;
-  background: #64A9FF;
-  border-radius: 6px;
-  font-size: 15px;
-  font-weight: 600;
-  color: #FFFFFF;
-}
-
-.van-tag+.van-tag {
-  margin-left: 8px;
-}
-
-.protocol {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 12px;
-  color: #AAAAAA;
-  line-height: 17px;
-}
-
-.protocol .c {
-  color: #F67146;
-}
-
-
-.phoneTips {
-  margin: 12px 12px 3px;
-  background: #FFEBDD;
-  border-radius: 6px;
-  font-size: 13px;
-  color: #F67146;
-  padding: 9px 10px;
-}

+ 2 - 0
public/project/css/initiation.css

@@ -105,6 +105,8 @@ span {
 
 .radioSection {
   position: relative;
+  min-width: 32px;
+    justify-content: center;
 }
 
 .radioItem {

+ 0 - 373
public/project/css/manageTeacher.css

@@ -1,373 +0,0 @@
-html,
-body,
-p,
-div,
-span {
-  padding: 0;
-  margin: 0;
-  border: 0;
-}
-
-.m-toast {
-  position: fixed;
-  top: 50%;
-  left: 50%;
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: flex;
-  -webkit-box-orient: vertical;
-  -webkit-box-direction: normal;
-  -webkit-flex-direction: column;
-  flex-direction: column;
-  -webkit-box-align: center;
-  -webkit-align-items: center;
-  align-items: center;
-  -webkit-box-pack: center;
-  -webkit-justify-content: center;
-  justify-content: center;
-  box-sizing: content-box;
-  width: 88px;
-  max-width: 70%;
-  min-height: 88px;
-  padding: 16px;
-  color: #fff;
-  font-size: 14px;
-  line-height: 20px;
-  white-space: pre-wrap;
-  text-align: center;
-  word-wrap: break-word;
-  background-color: rgba(50, 50, 51, 0.88);
-  border-radius: 4px;
-  -webkit-transform: translate3d(-50%, -50%, 0);
-  transform: translate3d(-50%, -50%, 0);
-  z-index: 2002
-}
-
-.m-loading {
-  position: relative;
-  color: #fff;
-  font-size: 0;
-  vertical-align: middle;
-  padding: 4px
-}
-
-.m-loading__spinner {
-  position: relative;
-  display: inline-block;
-  width: 30px;
-  max-width: 100%;
-  height: 30px;
-  max-height: 100%;
-  vertical-align: middle;
-  -webkit-animation: m-rotate .8s linear infinite;
-  animation: m-rotate .8s linear infinite
-}
-
-.m-loading__spinner--circular {
-  -webkit-animation-duration: 2s;
-  animation-duration: 2s
-}
-
-.m-loading__circular {
-  display: block;
-  width: 100%;
-  height: 100%
-}
-
-.m-loading__circular circle {
-  -webkit-animation: m-circular 1.5s ease-in-out infinite;
-  animation: m-circular 1.5s ease-in-out infinite;
-  stroke: currentColor;
-  stroke-width: 3;
-  stroke-linecap: round
-}
-
-.m-loading__text {
-  display: inline-block;
-  margin-left: 8px;
-  color: #969799;
-  font-size: 14px;
-  vertical-align: middle
-}
-
-.m-loading--vertical {
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: flex;
-  -webkit-box-orient: vertical;
-  -webkit-box-direction: normal;
-  -webkit-flex-direction: column;
-  flex-direction: column;
-  -webkit-box-align: center;
-  -webkit-align-items: center;
-  align-items: center
-}
-
-.m-loading--vertical .m-loading__text {
-  margin: 8px 0 0
-}
-
-@-webkit-keyframes m-circular {
-  0% {
-    stroke-dasharray: 1, 200;
-    stroke-dashoffset: 0
-  }
-
-  50% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -40
-  }
-
-  100% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -120
-  }
-}
-
-@keyframes m-circular {
-  0% {
-    stroke-dasharray: 1, 200;
-    stroke-dashoffset: 0
-  }
-
-  50% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -40
-  }
-
-  100% {
-    stroke-dasharray: 90, 150;
-    stroke-dashoffset: -120
-  }
-}
-
-.m-toast__text {
-  margin-top: 8px
-}
-
-.van-field--error .van-field__control,
-.van-field--error .van-field__control::placeholder {
-  color: #c8c9cc;
-  -webkit-text-fill-color: currentColor;
-}
-
-#app {
-  background: url('../images/manageTeacher/banner.png') no-repeat top center #F8F8F8;
-  background-size: contain;
-  max-width: 750px;
-  margin: 0 auto;
-  min-height: 100vh;
-  overflow: hidden;
-  position: relative;
-}
-
-.banner,
-.banner img {
-  width: 100%;
-  font-size: 0;
-}
-
-.van-cell {
-  flex-direction: column;
-  font-size: 16px;
-  padding: 14px 13px;
-}
-
-.van-field__label {
-  width: 100%;
-  margin-right: 0;
-  color: #333;
-  font-size: 16px;
-  font-weight: 500;
-}
-
-.van-cell--required::before {
-  left: 15px;
-}
-
-.van-field__body {
-  margin-top: 10px;
-}
-
-.btn-submit {
-  width: 90%;
-  margin: 20px auto;
-}
-
-.system h2 {
-  font-size: 18px;
-  font-weight: 500;
-  margin: 10px 24px;
-  color: #444444;
-}
-
-.system .van-cell--required::before {
-  left: 25px;
-}
-
-.cell-group {
-  margin: 0 13px 14px;
-  border-radius: 10px;
-  padding-bottom: 20px;
-}
-
-.van-form {
-  margin-top: 194px;
-  /* background: #F8F8F8; */
-  overflow: hidden;
-  /* margin: 290px 13px 14px; */
-}
-
-.top-tips {
-  margin: 30px 13px 20px;
-  padding: 9px 7px;
-  background: #FFFFFF;
-  border-radius: 10px;
-  border: 5px solid #BE93FF;
-  font-size: 14px;
-  font-weight: 500;
-  color: #724DA9;
-  line-height: 20px;
-}
-
-.title {
-  position: absolute;
-  top: 0;
-  left: 0;
-  padding: 115px 24px 0;
-  width: 57%;
-  /* height: 154px; */
-  font-size: 28px;
-  font-weight: 600;
-  color: #FFFFFF;
-  line-height: 34px;
-  letter-spacing: 1px;
-  /* text-shadow: 0px 2px 3px rgba(0, 75, 255, 0.5); */
-}
-
-.tips {
-  font-size: 16px;
-  font-weight: 500;
-  color: #FFFFFF;
-  line-height: 22px;
-  text-shadow: 0px 1px 5px #2F92FF;
-  display: flex;
-  align-items: center;
-}
-
-.tips img {
-  margin-right: 8px;
-  width: 18px;
-  height: 18px;
-}
-
-
-.radioSection {
-  position: relative;
-}
-
-.radioItem {
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  opacity: 0;
-}
-
-.radioSection+.radioSection {
-  margin-left: 12px;
-}
-
-.van-picker__confirm {
-  color: #64A9FF !important;
-}
-
-/* 弹窗 */
-.stautsS {
-  position: relative;
-}
-
-.submit-container {
-  padding: 20px 26px 26px;
-
-  text-align: center;
-}
-
-.icon-close {
-  position: absolute;
-  width: 26px;
-  height: 26px;
-  top: 12px;
-  right: 12px;
-}
-
-.submit-img {
-  width: 100%;
-}
-
-.submit-title {
-  font-size: 18px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 25px;
-}
-
-.submit-o {
-  padding-top: 10px;
-  font-size: 15px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 21px;
-}
-
-.submit-o span {
-  color: #64A9FF;
-}
-
-.submit-tips {
-  font-size: 14px;
-  color: #777777;
-  line-height: 20px;
-  padding-top: 5px;
-  padding-bottom: 20px;
-}
-
-.submit-container .van-button {
-  font-size: 18px;
-  font-weight: 500;
-}
-
-.cell_title {
-  margin: 0 13px 8px;
-  display: inline-block;
-  padding: 2px 6px 3px;
-  background: #64A9FF;
-  border-radius: 6px;
-  font-size: 15px;
-  font-weight: 600;
-  color: #FFFFFF;
-}
-
-.protocol {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 12px;
-  color: #AAAAAA;
-  line-height: 17px;
-}
-
-.protocol .c {
-  color: #F67146;
-}
-
-
-.phoneTips {
-  margin: 12px;
-  background: #FFEBDD;
-  border-radius: 6px;
-  font-size: 13px;
-  color: #F67146;
-  padding: 9px 10px;
-}

+ 2 - 0
public/project/css/preRegister.css

@@ -105,6 +105,8 @@ span {
 
 .radioSection {
   position: relative;
+  min-width: 32px;
+    justify-content: center;
 }
 
 .radioItem {

+ 2 - 0
public/project/css/schoolRegister.css

@@ -256,6 +256,8 @@ span {
 
 .radioSection {
   position: relative;
+  min-width: 32px;
+    justify-content: center;
 }
 
 .radioItem {

+ 0 - 213
public/project/manageTeacher.html

@@ -1,213 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-  <meta charset="utf-8">
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="apple-mobile-web-app-capable" content="yes">
-  <meta name="apple-mobile-web-app-status-bar-style" content="black">
-  <meta name="format-detection" content="telephone=no">
-  <meta name="mobile-web-app-capable" content="yes">
-  <meta name="msapplication-tap-highlight" content="no">
-  <meta name="fragment" content="!">
-  <meta name="viewport"
-    content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0, viewport-fit=cover">
-  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
-  <meta http-equiv="Pragma" content="no-cache" />
-  <meta http-equiv="Expires" content="0" />
-  <meta http-equiv="Cache" content="no-cache">
-  <meta name="description" content="">
-
-  <link rel="icon" href="../favicon.ico">
-  <title>乐团管理老师注册</title>
-  <!-- 引入样式文件 -->
-  <link rel="stylesheet" href="./js/vant-index.css" />
-  <link rel="stylesheet" href="./css/manageTeacher.css">
-  <script src="./js/flexible.js"></script>
-</head>
-
-<body>
-  <style>
-  </style>
-  <div id="m_loading" class="m-toast m-toast--middle m-toast--loading" style="z-index: 2001;">
-    <div class="m-loading m-loading--circular m-toast__loading"><span
-        class="m-loading__spinner m-loading__spinner--circular"><svg viewBox="25 25 50 50" class="m-loading__circular">
-          <circle cx="50" cy="50" r="20" fill="none"></circle>
-        </svg></span></div>
-    <div class="m-toast__text">加载中...</div>
-  </div>
-  <div id="app">
-    <div class="title">
-      <!-- 欢迎使用管乐团双师训练系统
-      <p class="tips">办乐团就用管乐团!</p> -->
-      <p class="tips">
-        <img src="./images/manageTeacher/school-logo.png" />
-        <span v-html="decodeURI(name)"></span>
-      </p>
-    </div>
-    <van-form validate-first scroll-to-error @submit="onSubmit" ref='form' class="form">
-      <van-cell-group inset class="cell-group">
-        <van-field required label="真实姓名" v-model="forms.realName" :rules="[{ required: true, message: '请填写真实姓名' }]"
-          name="realName" placeholder="请填写真实姓名" maxlength="50">
-        </van-field>
-
-        <div class="phoneTips">
-          <van-icon name="warning" size="16" />
-          提示:手机号码将成为您管乐团登录账
-        </div>
-        <van-field required label="手机号码" v-model="forms.phone" type="tel" maxlength="11" minlength="11"
-          :rules="[{ required: true, message: '请输入手机号码' },{ pattern, message: '输入手机号有误' }]" name="phone"
-          placeholder="请输入学校手机号码">
-        </van-field>
-
-        <van-field required label="身份证号码" v-model="forms.idCardNo" :rules="[
-                { required: true, message: '请输入身份证号' },
-                {
-                  pattern:
-                    /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
-                  message: '请输入正确的身份证号'
-                }
-              ]" name="idCardNo" placeholder="请输入身份证号码">
-        </van-field>
-
-        <van-field required label="性别" name="gender" :rules="[{ required: true, message: '请选择性别' }]">
-          <template #input>
-            <van-radio-group checked-color="#64A9FF" v-model="forms.gender" direction="horizontal">
-              <van-tag size="large" type="primary" :plain="!(forms.gender === 1)" color="#64A9FF" class="radioSection"
-                round>
-                <van-radio class="radioItem" :name="1"></van-radio>男
-              </van-tag>
-              <van-tag size="large" type="primary" :plain="!(forms.gender === 0)" color="#64A9FF" class="radioSection"
-                round>
-                <van-radio class="radioItem" :name="0"></van-radio>女
-              </van-tag>
-            </van-radio-group>
-          </template>
-        </van-field>
-      </van-cell-group>
-
-      <div class="protocol">
-        <van-checkbox v-model="checked" icon-size="16" style="margin-right: 6px"></van-checkbox>
-        <span @click="() => { checked = !checked }">请认真阅读并勾选</span><span class="c">《乐团伴学指导注册协议》</span>
-      </div>
-
-      <van-button size="large" block round class="btn-submit" color="#64A9FF" :loading="btnLoading"
-        native-type="submit">完成</van-button>
-    </van-form>
-
-    <van-popup v-model:show="showPicker" position="bottom" round>
-      <van-picker show-toolbar :columns="columns" @cancel="showPicker = false" @confirm="onConfirm"
-        :columns-field-names="{ text: 'name', value: 'code', children: 'areas' }" />
-    </van-popup>
-
-    <van-popup v-model:show="submitStatus" round style="width: 75%" :close-on-click-overlay="false">
-      <div class="stautsS">
-        <img class="icon-close" src="./images/initiation/icon-close.png" @click="submitStatus = false" />
-        <img src="./images/manageTeacher/top-banner.png" class="submit-img" />
-        <div class="submit-container">
-          <p class="submit-title">恭喜您已成功登记为</p>
-          <p class="submit-o">{{ decodeURI(name) }} <span>【管理老师】</span></p>
-          <p class="submit-tips">请下载管乐团管理端APP</p>
-          <van-button type="primary" color="#64A9FF" block round @click="submitStatus = false">立即下载</van-button>
-        </div>
-      </div>
-    </van-popup>
-  </div>
-
-  <!-- 引入 Vue 和 Vant 的 JS 文件 -->
-  <script src="./js/vue.global.min.js"></script>
-  <script src="./js/vant.min.js"></script>
-  <script src="./js/axios.js"></script>
-  <script type="text/javascript" src="./js/utils.js"></script>
-  <script>
-    var app = Vue.createApp({
-      data() {
-        return {
-          showPicker: false,
-          submitStatus: false,
-          id: getQueryVariable('id'),
-          name: getQueryVariable('name'),
-          pattern: /^1(3|4|5|6|7|8|9)\d{9}$/,
-          columns: [],
-          pickerType: null, // 下拉类型
-          forms: {
-            realName: '',
-            phone: null,
-            gender: 1,
-            idCardNo: null,
-          },
-          btnLoading: false,
-          checkPhone: false,
-          checked: true
-        }
-      },
-      async mounted() {
-        if (document.querySelector('#m_loading')) {
-          document.querySelector('#m_loading').remove()
-        }
-        if (!this.id) {
-          vant.showToast('信息获取失败,请联系老师')
-        }
-        try {
-          this.setLoading(true)
-        } catch {
-          vant.showDialog({
-            message: '信息获取失败,请联系老师',
-            theme: 'round-button',
-            confirmButtonColor: '#64A9FF'
-          })
-        }
-        this.setLoading(false)
-      },
-      methods: {
-        async onSubmit() {
-          if (!this.checked) {
-            vant.showToast('请阅读并同意协议')
-            return
-          }
-          this.btnLoading = true
-          try {
-            var forms = this.forms
-            var res = await axios.post('/api-school/open/schoolStaff/registerTeacher', {
-              ...forms,
-              schoolId: this.id,
-            })
-            if (res.data.code === 200) {
-              this.submitStatus = true
-            } else {
-              vant.showToast(res.data.message)
-            }
-          } catch {
-            vant.showToast('保存失败,请重试')
-          }
-          this.btnLoading = false
-        },
-        setLoading(status) {
-          if (status) {
-            vant.showLoadingToast({
-              duration: 0, // 持续展示 toast
-              message: '加载中...',
-              forbidClick: true,
-              loadingType: 'spinner',
-            })
-          } else {
-            vant.closeToast()
-          }
-        },
-        onSelectCity() {
-          this.showPicker = true
-        },
-        onConfirm(val) {
-          const selectedOptions = val.selectedOptions[1]
-          this.forms.cityCode = selectedOptions.code
-          this.forms.cityCodeName = selectedOptions.name
-          this.showPicker = false
-        }
-      }
-    })
-    app.use(vant)
-    app.mount('#app')
-  </script>
-</body>
-
-</html>

+ 10 - 2
public/project/schoolRegister.html

@@ -173,6 +173,7 @@
           pickerType: null, // 下拉类型
           forms: {
             name: null,
+            regionCode: null,
             cityCode: null,
             cityCodeName: '',
             provinceCode: null,
@@ -216,7 +217,7 @@
           // })
           // this.columns = tempareas || []
 
-          var res = await axios.get('/api-school/open/school/city')
+          var res = await axios.get('/api-school/open/sysArea/queryAllProvince')
           if (res.data.code === 200) {
             this.columns = res.data.data || []
           }
@@ -269,9 +270,16 @@
         onConfirm(val) {
           const selectedOptions = val.selectedOptions[1]
           this.forms.cityCode = selectedOptions.code
-          this.forms.cityCodeName = selectedOptions.name
           const selectedFirst = val.selectedOptions[0]
           this.forms.provinceCode = selectedFirst.code
+          this.forms.cityCodeName = selectedFirst.name + '/' + selectedOptions.name
+          const selectTwo = val.selectedOptions[2]
+          if (selectTwo) {
+            this.forms.regionCode = selectTwo.code
+            this.forms.cityCodeName += '/' + selectTwo.name
+          }
+
+
           this.showPicker = false
         }
       }

+ 16 - 0
src/router/routes-common.ts

@@ -102,6 +102,22 @@ export const router: RouteRecordRaw[] = [
     meta: {
       title: '测验详情'
     }
+  },
+  {
+    path: '/test-exercise',
+    name: 'test-exercise',
+    component: () => import('@/views/unit-test/test-exercise'),
+    meta: {
+      title: '测试练习'
+    }
+  },
+  {
+    path: '/practice-mode',
+    name: 'practice-mode',
+    component: () => import('@/views/unit-test/practice-mode'),
+    meta: {
+      title: '测试模式'
+    }
   }
 ]
 

+ 1 - 3
src/school/approval-manage/batch-adjust.tsx

@@ -61,12 +61,10 @@ export default defineComponent({
             endTime: forms.endTime.join('-')
           }
         })
-        console.log(data)
         router.push({
           path: '/course-preview',
           query: {
-            cacheId: data,
-            type: 'change'
+            cacheId: data
           }
         })
       } catch {

+ 12 - 0
src/school/approval-manage/course-adjust.tsx

@@ -194,6 +194,18 @@ export default defineComponent({
                 placeholder="请选择课程开始时间"
                 modelValue={forms.startTime ? dayjs(forms.startTime).format('HH:mm') : ''}
                 onClick={() => {
+                  let freeTimeCount = 0
+                  const timeDetailList = state.timerList.timeDetailList || []
+                  timeDetailList.forEach((item: any) => {
+                    if (item.enable === true) {
+                      freeTimeCount += 1
+                    }
+                  })
+                  // 判断是否有排课时间段
+                  if (freeTimeCount <= 0) {
+                    showToast('当前没有可排课时间段,请重新选择课程开始日期')
+                    return
+                  }
                   state.showPopoverCourseTime = true
                 }}
               />

+ 39 - 1
src/school/companion-teacher/companion-teacher-register.tsx

@@ -28,6 +28,7 @@ import topBanner1 from './images/top-banner1.png'
 import { checkPhone } from '@/helpers/validate'
 import OUpload from '@/components/o-upload'
 import router from '@/router'
+import dayjs from 'dayjs'
 
 export default defineComponent({
   name: 'companion-teacher-register',
@@ -41,6 +42,8 @@ export default defineComponent({
       showEducation: false,
       id: route.query.id,
       name: route.query.name,
+      t: route.query.t as any, // 过期时间
+      qrCodeStatus: false, // 二维码是否失效
       pattern: /^1(3|4|5|6|7|8|9)\d{9}$/,
       columns: [] as any,
       pickerType: null, // 下拉类型
@@ -72,6 +75,15 @@ export default defineComponent({
     })
 
     const onSubmit = async () => {
+      if (state.qrCodeStatus) {
+        showDialog({
+          title: '提示',
+          message: '二维码已失效',
+          theme: 'round-button',
+          confirmButtonColor: '#ff8057'
+        })
+        return
+      }
       if (!state.checked) {
         showToast('请阅读并同意协议')
         return
@@ -177,6 +189,32 @@ export default defineComponent({
       if (!state.id) {
         showToast('信息获取失败,请联系老师')
       }
+
+      // t: route.query.t, // 过期时间
+      try {
+        if (state.t) {
+          const { data } = await request.get('/api-school/open/paramConfig/queryByParamName', {
+            requestType: 'form',
+            params: {
+              paramName: 'qr_code_expire_hours'
+            }
+          })
+          if (dayjs(Number(state.t)).isBefore(dayjs().add(data.paramValue, 'hour'))) {
+            showDialog({
+              title: '提示',
+              message: '二维码已失效',
+              theme: 'round-button',
+              confirmButtonColor: '#ff8057'
+            })
+            state.qrCodeStatus = true
+          } else {
+            state.qrCodeStatus = false
+          }
+        }
+      } catch {
+        //
+      }
+
       try {
         const tempareas: any = []
         areas.forEach((item) => {
@@ -217,7 +255,7 @@ export default defineComponent({
         showDialog({
           message: '信息获取失败,请联系老师',
           theme: 'round-button',
-          confirmButtonColor: '#64A9FF'
+          confirmButtonColor: '#ff8057'
         })
       }
     })

+ 3 - 1
src/school/companion-teacher/index.tsx

@@ -100,7 +100,9 @@ export default defineComponent({
           '/orchestra-school/#/companion-teacher-register?id=' +
           res.data.schoolId +
           '&name=' +
-          res.data.schoolName
+          res.data.schoolName +
+          '&t=' +
+          +new Date()
 
         console.log(form.url)
       } catch {

+ 1 - 1
src/school/companion-teacher/unbind.tsx

@@ -94,7 +94,7 @@ export default defineComponent({
               path: '/course-preview',
               query: {
                 cacheId: data.cacheId,
-                type: 'change'
+                type: 'unbind'
               }
             })
           }

+ 3 - 1
src/school/manage-teacher/index.tsx

@@ -68,7 +68,9 @@ export default defineComponent({
           '/orchestra-school/#/manage-teacher-register?id=' +
           res.data.schoolId +
           '&name=' +
-          res.data.schoolName
+          res.data.schoolName +
+          '&t=' +
+          +new Date()
       } catch {
         //
       }

+ 2 - 0
src/school/manage-teacher/manage-teacher-register.module.less

@@ -131,6 +131,8 @@ span {
 
 .radioSection {
   position: relative;
+  min-width: 32px;
+  justify-content: center;
 }
 
 .radioItem {

+ 36 - 0
src/school/manage-teacher/manage-teacher-register.tsx

@@ -26,6 +26,7 @@ import schoolLogo from './images/school-logo.png'
 import iconClose from './images/icon-close.png'
 import topBanner1 from './images/top-banner.png'
 import { checkPhone } from '@/helpers/validate'
+import dayjs from 'dayjs'
 
 export default defineComponent({
   name: 'companion-teacher-register',
@@ -38,6 +39,8 @@ export default defineComponent({
       submitStatus: false,
       id: route.query.id,
       name: route.query.name,
+      t: route.query.t as any, // 过期时间
+      qrCodeStatus: false, // 二维码是否失效
       pattern: /^1(3|4|5|6|7|8|9)\d{9}$/,
       columns: [] as any,
       pickerType: null, // 下拉类型
@@ -59,6 +62,15 @@ export default defineComponent({
     })
 
     const onSubmit = async () => {
+      if (state.qrCodeStatus) {
+        showDialog({
+          title: '提示',
+          message: '二维码已失效',
+          theme: 'round-button',
+          confirmButtonColor: '#64A9FF'
+        })
+        return
+      }
       if (!state.checked) {
         showToast('请阅读并同意协议')
         return
@@ -106,6 +118,30 @@ export default defineComponent({
       if (!state.id) {
         showToast('信息获取失败,请联系老师')
       }
+
+      try {
+        if (state.t) {
+          const { data } = await request.get('/api-school/open/paramConfig/queryByParamName', {
+            requestType: 'form',
+            params: {
+              paramName: 'qr_code_expire_hours'
+            }
+          })
+          if (dayjs(Number(state.t)).isBefore(dayjs().add(data.paramValue, 'hour'))) {
+            showDialog({
+              title: '提示',
+              message: '二维码已失效',
+              theme: 'round-button',
+              confirmButtonColor: '#64A9FF'
+            })
+            state.qrCodeStatus = true
+          } else {
+            state.qrCodeStatus = false
+          }
+        }
+      } catch {
+        //
+      }
       try {
         const tempareas: any = []
         areas.forEach((item) => {

+ 5 - 4
src/school/train-planning/component/course-preview/index.tsx

@@ -51,7 +51,7 @@ export default defineComponent({
             ? { orchestraId: forms.selectOrchestraId }
             : forms.planList.orchestra[0]
           state.tabValue = selectOrchestra.orchestraId
-          console.log(forms.selectClassGroupId, forms.planList.classes[selectOrchestra.orchestraId])
+          // console.log(forms.selectClassGroupId, forms.planList.classes[selectOrchestra.orchestraId])
           const selectClasses = forms.selectClassGroupId
             ? { classGroupId: forms.selectClassGroupId }
             : forms.planList.classes[selectOrchestra.orchestraId]
@@ -62,13 +62,13 @@ export default defineComponent({
 
           state.courseValue = selectClasses.classGroupId
 
-          console.log(selectClasses.classGroupId, 'selectClasses.classGroupId')
+          // console.log(selectClasses.classGroupId, 'selectClasses.classGroupId')
 
           // 判断是否有数据
           forms.selectOrchestraId = null
           forms.selectClassGroupId = null
 
-          console.log(selectClasses.classGroupId, 'selectClasses.classGroupId 333333')
+          // console.log(selectClasses.classGroupId, 'selectClasses.classGroupId 333333')
         }
       } catch {
         //
@@ -163,7 +163,8 @@ export default defineComponent({
         }, 100)
         setTimeout(() => {
           state.isClick = false
-          if (route.query.type === 'change') {
+          // 伴学指导交接
+          if (route.query.type === 'unbind') {
             router.replace('/companion-teacher')
           } else {
             postMessage({ api: 'back', content: {} })

+ 16 - 13
src/school/train-planning/modal/timer/index.tsx

@@ -24,7 +24,7 @@ export default defineComponent({
   setup(props, { slots, attrs, emit }) {
     const defaultTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
     const state = reactive({
-      calendarDate: null,
+      calendarDate: null as any,
       selectTimeStatus: false,
       selectTime: null as any,
       useTimer: [] as any, // 可排课时间段
@@ -45,18 +45,20 @@ export default defineComponent({
     }
 
     const onFilter = (type: any, option: any) => {
-      // console.log(type, option)
       // 时间
       if (type === 'hour') {
         const hour: any = []
         option.forEach((o: any) => {
           state.useTimerFormat.forEach((time: any) => {
-            if (o.value >= time.startHour && o.value <= time.endHour) {
+            if (
+              o.value >= time.startHour &&
+              o.value <= time.endHour &&
+              o.value != hour[hour.length - 1]?.value
+            ) {
               hour.push(o)
             }
           })
         })
-        // console.log(hour, 'hour')
         return hour
       }
       return option
@@ -109,13 +111,13 @@ export default defineComponent({
       // console.log(dayjs(tempDate).format('YYYY-MM-DD HH:mm:ss'), 'first', dayjs(tempDate).minute())
       // 时间加上每节课的时间,
       const lastDate = dayjs(tempDate).minute(props.times + dayjs(tempDate).minute())
-      console.log(dayjs(lastDate).format('YYYY-MM-DD HH:mm:ss'), 'second')
-      console.log(
-        dayjs(tempDate).format('YYYY-MM-DD HH:mm:ss'),
-        tempDate.toDate(),
-        'second tempDate',
-        state.useTimer
-      )
+      // console.log(dayjs(lastDate).format('YYYY-MM-DD HH:mm:ss'), 'second')
+      // console.log(
+      //   dayjs(tempDate).format('YYYY-MM-DD HH:mm:ss'),
+      //   tempDate.toDate(),
+      //   'second tempDate',
+      //   state.useTimer
+      // )
 
       let isActive = false
       state.useTimer.forEach((item: any) => {
@@ -152,6 +154,7 @@ export default defineComponent({
       console.log(props.timerList, 'timerList')
       state.calendarDate = props.timerList?.calendarDate
       const timeDetailList = props.timerList?.timeDetailList || []
+
       const useTimer: any = [] // 可排课时间段
       const usedTimer: any = [] // 不可排课时间段
 
@@ -167,8 +170,8 @@ export default defineComponent({
       const useFormat = onFormatTimer(useTimer)
       state.useTimerFormat = useFormat
       state.usedTimer = [...usedTimer]
-      // console.log(onFormatTimer(useTimer), 'onFormatTimer')
-      // console.log(state.useTimer, state.usedTimer, 'onUseTimer')
+      console.log(onFormatTimer(useTimer), 'onFormatTimer')
+      console.log(state.useTimer, state.usedTimer, 'onUseTimer')
 
       // 判断有可排课数据
       if (useFormat.length > 0) {

二进制
src/student/download/images/manage_bg.png


二进制
src/student/download/images/student_bg.png


二进制
src/student/download/images/teacher_bg.png


+ 22 - 3
src/student/download/index.module.less

@@ -4,10 +4,29 @@
   background-size: cover;
 }
 
+.teacher {
+  background: url('./images/teacher_bg.png') no-repeat top center;
+  min-height: 100vh;
+  background-size: cover;
+}
+
+.manage {
+  background: url('./images/manage_bg.png') no-repeat top center;
+  min-height: 100vh;
+  background-size: cover;
+}
+
+.buttonGroup {
+  text-align: center;
+}
 .btn {
-  // style={{ width: '60%', margin: '0 20%' }}
-  width: 60%;
-  margin: 85vh 20% 0;
+  // position: absolute;
+  // bottom: 53px;
+  // left: 50%;
+  // margin-left: -98px;
+  width: 194px;
+  height: 44px;
+  line-height: 44px;
 }
 
 .wxpopup {

+ 23 - 5
src/student/download/index.tsx

@@ -1,9 +1,13 @@
-import { Button, showToast } from 'vant'
+import { Button, showToast, Image } from 'vant'
 import { defineComponent, onMounted, reactive } from 'vue'
 import { useRoute } from 'vue-router'
 import styles from './index.module.less'
 import wxBg from './images/wx_bg.png'
 import { browser } from '@/helpers/utils'
+import student from './images/student_bg.png'
+import teacher from './images/teacher_bg.png'
+import manage from './images/manage_bg.png'
+import OSticky from '@/components/o-sticky'
 
 // 唤起前缀
 // BandMusicTeam:// 管乐团
@@ -75,10 +79,24 @@ export default defineComponent({
       document.title = state.buttonText
     })
     return () => (
-      <div class={[styles.student]}>
-        <Button round size="large" color="#FF8057" class={styles.btn} onClick={onDownload}>
-          {state.buttonText}
-        </Button>
+      <div
+        class={[
+          styles.student,
+          state.type === 'teacher' && styles.teacher,
+          state.type === 'manage' && styles.manage
+        ]}
+      >
+        {/* {state.type !== 'teacher' && state.type !== 'manage' && <Image src={student} />}
+        {state.type === 'teacher' && <Image src={teacher} />}
+        {state.type === 'manage' && <Image src={manage} />} */}
+
+        <OSticky background="white" position="bottom">
+          <div class={[styles.buttonGroup, 'btnGroup']}>
+            <Button round size="large" color="#FF8057" class={styles.btn} onClick={onDownload}>
+              {state.buttonText}
+            </Button>
+          </div>
+        </OSticky>
 
         {state.wxStatus && (
           <div

+ 8 - 4
src/student/download/transfer.tsx

@@ -4,6 +4,7 @@ import { defineComponent } from 'vue'
 import styles from './index.module.less'
 import wxBg from './images/wx_bg.png'
 import qs from 'query-string'
+import OSticky from '@/components/o-sticky'
 
 export default defineComponent({
   name: 'download-transfer',
@@ -92,10 +93,13 @@ export default defineComponent({
   render() {
     return (
       <div class={[styles.student]}>
-        <Button round size="large" color="#FF8057" class={styles.btn} onClick={this.onDownload}>
-          {this.buttonText}
-        </Button>
-
+        <OSticky background="white" position="bottom">
+          <div class={[styles.buttonGroup, 'btnGroup']}>
+            <Button round size="large" color="#FF8057" class={styles.btn} onClick={this.onDownload}>
+              {this.buttonText}
+            </Button>
+          </div>
+        </OSticky>
         {this.wxStatus && (
           <div
             class={styles.wxpopup}

+ 1 - 1
src/student/home/index.tsx

@@ -9,7 +9,7 @@ export default defineComponent({
     return (
       <>
         <div class={styles.title}>标题</div>
-        <Button>首页</Button>
+        {/* <Button>首页</Button> */}
       </>
     )
   }

+ 2 - 1
src/student/music-group/layout/index.module.less

@@ -33,7 +33,8 @@
 
 .login {
   min-height: 100vh;
-  background: url('../../../views/layout/images/top_bg.png') no-repeat top center;
+  background: url('../../../views/layout/images/top_bg.png') no-repeat top center,
+    url('../../../views/layout/images/bottom_student_bg.png') no-repeat bottom center;
   background-color: #fff;
   background-size: 100%;
 

+ 6 - 10
src/student/music-group/pre-apply/component/apply.tsx

@@ -282,7 +282,8 @@ export default defineComponent({
                 }
 
                 // 切换订单时判断是否有支付中和已支付的订单,并且已注册过
-                if (props.registerInfo?.orderNumber > 0 && props.registerInfo?.register) {
+                // 判断学生所在乐团状态,如果在读则不允许更换声部(只能退团重新报名)
+                if (props.registerInfo?.registerStatus === 'LEARNING') {
                   state.subjectChangeStatus = true
                   return
                 }
@@ -374,20 +375,15 @@ export default defineComponent({
 
         <Dialog
           v-model:show={state.subjectChangeStatus}
-          message={'您修改了乐团声部,若确认更换,请先将原声部订单退款,完成后修改声部重新缴费。'}
-          messageAlign="left"
-          confirmButtonText="去退款"
-          cancelButtonText="选错了"
-          showCancelButton
-          onConfirm={() => {
-            emit('next', 'order')
-          }}
+          message={'您已报名,不能更换声部'}
+          messageAlign="center"
+          confirmButtonText="确定"
         >
           {{
             title: () => (
               <div class={styles.dialogTitle}>
                 <i></i>
-                修改声部
+                提示
               </div>
             )
           }}

+ 13 - 15
src/student/music-group/pre-apply/component/payment.tsx

@@ -163,18 +163,18 @@ export default defineComponent({
             console.error(countUpRef.needPrice.error)
           }
         }
-        if (countUpRef.originalPrice) {
-          countUpRef.originalPrice.update(state.orderInfo.originalPrice)
-        } else {
-          countUpRef.originalPrice = new CountUp('originalPrice', state.orderInfo.originalPrice, {
-            decimalPlaces: 2
-          })
-          if (!countUpRef.originalPrice.error) {
-            countUpRef.originalPrice.start()
-          } else {
-            console.error(countUpRef.originalPrice.error)
-          }
-        }
+        // if (countUpRef.originalPrice) {
+        //   countUpRef.originalPrice.update(state.orderInfo.originalPrice)
+        // } else {
+        //   countUpRef.originalPrice = new CountUp('originalPrice', state.orderInfo.originalPrice, {
+        //     decimalPlaces: 2
+        //   })
+        //   if (!countUpRef.originalPrice.error) {
+        //     countUpRef.originalPrice.start()
+        //   } else {
+        //     console.error(countUpRef.originalPrice.error)
+        //   }
+        // }
       })
     }
 
@@ -491,9 +491,7 @@ export default defineComponent({
               </p>
               <p class={styles.allPrice}>
                 总原价:
-                <del>
-                  ¥<i style="font-style: normal" id="originalPrice"></i>
-                </del>
+                <del>¥{moneyFormat(state.orderInfo.originalPrice)}</del>
               </p>
             </div>
             <div class={styles.paymentBtn}>

+ 3 - 0
src/student/music-group/pre-apply/index.module.less

@@ -92,6 +92,8 @@
 
 .radioSection {
   position: relative;
+  min-width: 32px;
+  justify-content: center;
 
   .radioItem {
     position: absolute;
@@ -130,6 +132,7 @@
   align-items: center;
   justify-content: space-between;
   font-size: 14px;
+  padding: 15px 12px calc(15px + env(safe-area-inset-bottom)) 12px;
   .needPrice {
     display: flex;
     align-items: center;

+ 5 - 0
src/student/music-group/pre-apply/index.tsx

@@ -34,6 +34,8 @@ export default defineComponent({
 
       nextTick(() => {
         state.tabValue = name
+
+        window.scrollTo(0, 0)
       })
     }
 
@@ -150,6 +152,9 @@ export default defineComponent({
           //
         }
       }
+
+      // document.getElementById('app')?.scrollTo(0, 0)
+      window.scrollTo(0, 0)
     }
 
     const goAuth = (wxAppId: string) => {

+ 32 - 20
src/student/music-group/pre-apply/order-detail.tsx

@@ -255,16 +255,22 @@ export default defineComponent({
         console.log(data)
         state.pay_channel = data.paymentChannel
         if (data.status === 'PAID') {
-          showConfirmDialog({
-            message: '该订单已支付成功',
-            showCancelButton: false
-          }).then(() => {
-            router.replace({
-              path: '/payment-result',
-              query: {
-                orderNo: state.orderNo
-              }
-            })
+          // showConfirmDialog({
+          //   message: '该订单已支付成功',
+          //   showCancelButton: false
+          // }).then(() => {
+          //   router.replace({
+          //     path: '/payment-result',
+          //     query: {
+          //       orderNo: state.orderNo
+          //     }
+          //   })
+          // })
+          router.replace({
+            path: '/payment-result',
+            query: {
+              orderNo: state.orderNo
+            }
           })
         } else {
           callback && callback()
@@ -304,16 +310,22 @@ export default defineComponent({
         console.log(data)
         state.pay_channel = data.paymentChannel
         if (data.status === 'PAID') {
-          showConfirmDialog({
-            message: '该订单已支付成功',
-            showCancelButton: false
-          }).then(() => {
-            router.replace({
-              path: '/payment-result',
-              query: {
-                orderNo: state.orderNo
-              }
-            })
+          // showConfirmDialog({
+          //   message: '该订单已支付成功',
+          //   showCancelButton: false
+          // }).then(() => {
+          //   router.replace({
+          //     path: '/payment-result',
+          //     query: {
+          //       orderNo: state.orderNo
+          //     }
+          //   })
+          // })
+          router.replace({
+            path: '/payment-result',
+            query: {
+              orderNo: state.orderNo
+            }
           })
         } else {
           callback && callback()

+ 1 - 1
src/student/music-group/shop-address/address-operation.tsx

@@ -132,7 +132,7 @@ export default defineComponent({
           />
           <Field
             label="所在地区"
-            placeholder="省、市、区、街道"
+            placeholder="省/市/区/街道"
             readonly
             isLink
             modelValue={state.pcrStr}

+ 2 - 2
src/views/courseList/index.tsx

@@ -210,9 +210,9 @@ export default defineComponent({
             )
           })}
         </Grid>
-        <Button onClick={() => {
+        {/* <Button onClick={() => {
           location.href = 'http://192.168.3.114:1000/teacher.html#/coursewarePlay?id=1613426640725217281'
-        }}>胜强测试老师端</Button>
+        }}>胜强测试老师端</Button> */}
         {!data.loading && !data.list.length && <Empty description="空空如也" />}
       </div>
     )

+ 12 - 8
src/views/coursewarePlay/index.tsx

@@ -125,7 +125,7 @@ export default defineComponent({
         const res = await request.get(
           `${state.platformApi}/courseSchedule/detail/${route.query.courseId}`
         )
-        if (res?.data){
+        if (res?.data) {
           data.isCourse = res.data.status === 'COMPLETE' ? false : true
         }
       } catch (e) {
@@ -171,6 +171,7 @@ export default defineComponent({
             ...videoItem,
             iframeRef: null,
             tabName: item.name,
+            isLast: j === itemLength, // 当前知识点
             autoPlay: j === itemLength
           })
         }
@@ -354,17 +355,20 @@ export default defineComponent({
     //当前视频播放完
     const handleEnded = (m: any) => {
       // console.log(m)
-      // 自动播放下一个知识点
-      if (m.autoPlay) {
-        if (popupData.activeIndex != data.itemList.length - 1) {
-          popupData.activeIndex++
-          swipeRef.value?.next()
-          const nextItem = data.itemList[popupData.activeIndex]
+      if (popupData.activeIndex != data.itemList.length - 1) {
+        popupData.activeIndex++
+        swipeRef.value?.next()
+        const nextItem = data.itemList[popupData.activeIndex]
+        if (nextItem.type === 'VIDEO'){
           nextTick(() => {
+            // 自动播放下一个知识点
+            // if (m.autoPlay) {
+            // }
             nextItem.videoEle?.play()
           })
-          console.log('🚀 ~ nextItem', nextItem)
         }
+        
+        console.log('🚀 ~ nextItem', nextItem)
       }
     }
 

+ 3 - 1
src/views/exercise-after-class/index.tsx

@@ -126,8 +126,10 @@ export default defineComponent({
           videoData.training = JSON.parse(videoData?.lessonTrainingTemp?.trainingConfigJson)
         } catch (error) {}
         //请求本地缓存
-        if (browserInfo.isApp && ['VIDEO'].includes(videoData.type)) {
+        if (browserInfo.isApp && videoData.updateTime && ['VIDEO'].includes(videoData.type)) {
+          console.log('开始检查本地缓存')
           const localData = await getCacheFilePath(videoData)
+          console.log("🚀 ~ 本地缓存数据", localData)
           if (localData?.content?.localPath) {
             videoData.url = videoData.content
             videoData.content = localData.content.localPath

二进制
src/views/layout/images/bottom_manage_bg.png


二进制
src/views/layout/images/bottom_student_bg.png


二进制
src/views/layout/images/bottom_teacher_bg.png


二进制
src/views/layout/images/top_bg.png


+ 15 - 1
src/views/layout/login.module.less

@@ -1,9 +1,23 @@
 .login {
   min-height: 100vh;
-  background: url('./images/top_bg.png') no-repeat top center;
+  background: url('./images/top_bg.png') no-repeat top center,
+    url('./images/bottom_student_bg.png') no-repeat bottom center;
   background-color: #fff;
   background-size: 100%;
 
+  &.TEACHER {
+    background: url('./images/top_bg.png') no-repeat top center,
+      url('./images/bottom_teacher_bg.png') no-repeat bottom center;
+    background-color: #fff;
+    background-size: 100%;
+  }
+  &.SCHOOL {
+    background: url('./images/top_bg.png') no-repeat top center,
+      url('./images/bottom_manage_bg.png') no-repeat bottom center;
+    background-color: #fff;
+    background-size: 100%;
+  }
+
   .loginTitle {
     padding-top: 100px;
     font-size: 26px;

+ 1 - 1
src/views/layout/login.tsx

@@ -126,7 +126,7 @@ export default defineComponent({
   },
   render() {
     return (
-      <div class={styles.login}>
+      <div class={[styles.login, styles[state.platformType]]}>
         <div class={styles.loginTitle}>
           您好,
           <br /> 欢迎使用管乐团{this.appName}

+ 2 - 2
src/views/lessonCourseware/index.tsx

@@ -91,10 +91,10 @@ export default defineComponent({
             )
           })}
         </Grid>
-        <Button onClick={() => {
+        {/* <Button onClick={() => {
           location.href = 'http://192.168.3.114:1000/teacher.html#/courseList?id=1610595624868495362'
         }}>胜强测试</Button>
-        {!data.loading && !data.list.length && <OEmpty tips="没有课件" />}
+        {!data.loading && !data.list.length && <OEmpty tips="没有课件" />} */}
       </div>
     )
   }

二进制
src/views/unit-test/images/exam-fail-bg.png


二进制
src/views/unit-test/images/exam-left-btn.png


二进制
src/views/unit-test/images/exam-pass-bg.png


二进制
src/views/unit-test/images/exam-right-bg.png


二进制
src/views/unit-test/images/icon-error.png


二进制
src/views/unit-test/images/icon-song.png


二进制
src/views/unit-test/images/icon-tag.png


+ 9 - 1
src/views/unit-test/index.tsx

@@ -119,7 +119,15 @@ export default defineComponent({
                         <div class={styles.endTime}>截止时间:2022-10-24 21:00</div>
 
                         <div class={styles.unitBtnGroup}>
-                          <Button color="#FFF0E6" round block style={{ color: '#F67146' }}>
+                          <Button
+                            color="#FFF0E6"
+                            round
+                            block
+                            style={{ color: '#F67146' }}
+                            onClick={() => {
+                              router.push('/test-exercise')
+                            }}
+                          >
                             练习模式
                           </Button>
                           <Button

+ 9 - 3
src/views/unit-test/model/choice-question/index.module.less

@@ -6,9 +6,9 @@
   border-radius: 10px;
 }
 .unitSubjectTitle {
-  display: flex;
-  align-items: center;
-  flex-wrap: wrap;
+  // display: flex;
+  // align-items: center;
+  // flex-wrap: wrap;
   font-size: 16px;
   font-weight: 500;
   color: #333333;
@@ -16,6 +16,12 @@
   .unitScore {
     color: #777777;
   }
+  :global {
+    .van-tag {
+      vertical-align: middle;
+      margin-top: -3px;
+    }
+  }
 }
 .unitTitleImg {
   padding-top: 20px;

+ 5 - 0
src/views/unit-test/model/choice-question/index.tsx

@@ -18,6 +18,10 @@ export default defineComponent({
     answers: {
       type: Object,
       default: {}
+    },
+    readOnly: {
+      type: Boolean,
+      default: false
     }
   },
   emits: ['update:value'],
@@ -44,6 +48,7 @@ export default defineComponent({
     })
 
     const onSelect = (item: any) => {
+      if (props.readOnly) return
       if (props.type === 'checkbox') {
         // 判断是否已选过
         const value: any = props.value

+ 9 - 3
src/views/unit-test/model/drag-question/index.module.less

@@ -7,9 +7,9 @@
 }
 
 .unitSubjectTitle {
-  display: flex;
-  align-items: center;
-  flex-wrap: wrap;
+  // display: flex;
+  // align-items: center;
+  // flex-wrap: wrap;
   font-size: 16px;
   font-weight: 500;
   color: #333333;
@@ -17,6 +17,12 @@
   .unitScore {
     color: #777777;
   }
+  :global {
+    .van-tag {
+      vertical-align: middle;
+      margin-top: -3px;
+    }
+  }
 }
 .unitTitleSection {
   margin-top: 20px;

+ 2 - 2
src/views/unit-test/model/drag-question/index.tsx

@@ -25,7 +25,7 @@ export default defineComponent({
     /* 只读 */
     readOnly: {
       type: Boolean,
-      default: true
+      default: false
     }
   },
   emits: ['update:value'],
@@ -79,7 +79,7 @@ export default defineComponent({
       nextTick(() => {
         const el = document.getElementById(state.domId)
         state.sortable = Sortable.create(el, {
-          // disabled: true,
+          disabled: props.readOnly,
           animation: 150,
           sort: true,
           fallbackTolerance: 3,

+ 49 - 0
src/views/unit-test/model/error-mode/index.module.less

@@ -0,0 +1,49 @@
+.popupResult {
+  padding: 20px 20px 25px;
+  color: #333333;
+  font-size: 15px;
+
+  .resultTitle {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    text-align: center;
+    font-size: 18px;
+    font-weight: 600;
+    color: #333333;
+    .titleImg {
+      width: 18px;
+      height: 18px;
+      margin-right: 6px;
+    }
+  }
+
+  .result {
+    margin-top: 20px;
+    padding: 12px;
+    background: #f6f6f6;
+    border-radius: 4px;
+    font-size: 16px;
+    font-weight: 500;
+    color: #333333;
+
+    .yes {
+      color: #4593f4;
+      margin-right: 20px;
+    }
+    .no {
+      color: #f44541;
+    }
+  }
+
+  .resultContent {
+    padding: 20px 0 30px;
+    font-size: 15px;
+    color: #333333;
+    text-align: justify;
+    line-height: 23px;
+    span {
+      font-weight: 600;
+    }
+  }
+}

+ 42 - 0
src/views/unit-test/model/error-mode/index.tsx

@@ -0,0 +1,42 @@
+import { defineComponent } from 'vue'
+import { Image, Button } from 'vant'
+import iconError from '../../images/icon-error.png'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'result-mode',
+  emits: ['close', 'conform'],
+  setup(props, { emit }) {
+    return () => (
+      <div class={styles.popupResult}>
+        <div class={styles.resultTitle}>
+          <Image src={iconError} class={styles.titleImg} />
+          回答错误!
+        </div>
+
+        <div class={styles.result}>
+          正确答案: <span class={styles.yes}>A</span>
+          您选择: <span class={styles.no}>B</span>
+        </div>
+
+        <div class={styles.resultContent}>
+          <span>答案解析:</span>
+          八分音符,从拍号中可以了解这一段乐谱为4/4拍,每小节4拍,红框小节中共有8个音符共同组成4拍,每个音符半拍,所以是八分音符。
+        </div>
+
+        <Button
+          type="primary"
+          round
+          class={styles.btn}
+          block
+          onClick={() => {
+            emit('conform')
+            emit('close')
+          }}
+        >
+          我知道啦
+        </Button>
+      </div>
+    )
+  }
+})

+ 32 - 12
src/views/unit-test/model/keep-look-question/index.module.less

@@ -7,9 +7,9 @@
 }
 
 .unitSubjectTitle {
-  display: flex;
-  align-items: center;
-  flex-wrap: wrap;
+  // display: flex;
+  // align-items: center;
+  // flex-wrap: wrap;
   font-size: 16px;
   font-weight: 500;
   color: #333333;
@@ -17,6 +17,12 @@
   .unitScore {
     color: #777777;
   }
+  :global {
+    .van-tag {
+      vertical-align: middle;
+      margin-top: -3px;
+    }
+  }
 }
 
 .unitTitleImg {
@@ -28,15 +34,17 @@
 .unitAnswers {
   position: relative;
   padding-bottom: 20px;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
 
-  .leftSection,
-  .rightSection {
+  .answerItem {
     position: relative;
-    width: 95px;
     z-index: 2;
+    margin-bottom: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    &:last-child {
+      margin-bottom: 0;
+    }
   }
   .img {
     width: 95px;
@@ -50,8 +58,7 @@
     border: 2px solid #d5d5d5;
     overflow: hidden;
     height: 54px;
-    width: 100%;
-    margin-bottom: 15px;
+    width: 95px;
     font-size: 16px;
     font-weight: 500;
     color: #333333;
@@ -66,7 +73,20 @@
 
 .canvasSection {
   position: absolute;
-  background: #f9f9f9;
   top: 0;
   left: 0;
 }
+
+.resetBtnGroup {
+  text-align: right;
+  padding-bottom: 16px;
+  :global {
+    .van-button {
+      min-width: 84px;
+      line-height: 31px;
+      height: 31px;
+      font-size: 15px;
+      font-weight: 500;
+    }
+  }
+}

+ 239 - 61
src/views/unit-test/model/keep-look-question/index.tsx

@@ -1,9 +1,6 @@
 import { Tag, Image, Button } from 'vant'
-import { defineComponent, nextTick, onMounted, PropType, reactive } from 'vue'
-import { labelOptions } from '../../unit'
+import { defineComponent, nextTick, onMounted, PropType, reactive, ref } from 'vue'
 import styles from './index.module.less'
-import Sortable from 'sortablejs'
-import deepClone from '@/helpers/deep-clone'
 import { useRect } from '@vant/use'
 
 // 单选和多选题
@@ -25,18 +22,16 @@ export default defineComponent({
     /* 只读 */
     readOnly: {
       type: Boolean,
-      default: true
+      default: false
     }
   },
   emits: ['update:value'],
   setup(props, { emit }) {
+    const canvasRef = ref()
     const state = reactive({
       answerDomId: 'answer' + +new Date(),
-      answerObj: {
-        height: 100,
-        width: 100
-      },
-      canvasDomId: 'canvas' + +new Date(),
+      answerRect: {} as any,
+
       sortable: null as any,
       list: [] as any,
       options: [
@@ -45,70 +40,198 @@ export default defineComponent({
           value: 'Sol',
           left: false,
           right: false,
-          locked: false // 是否已经连线
+          leftLocked: false, // 是否已经连线
+          rightLocked: false // 是否已经连线
         },
         {
           index: 2,
           value: 'Sal',
           left: false,
           right: false,
-          locked: false
+          leftLocked: false, // 是否已经连线
+          rightLocked: false // 是否已经连线
         },
         {
           index: 3,
           value: 'La',
           left: false,
           right: false,
-          locked: false
+          leftLocked: false, // 是否已经连线
+          rightLocked: false // 是否已经连线
         },
         {
           index: 4,
           value: 'Si',
           left: false,
           right: false,
-          locked: false
+          leftLocked: false, // 是否已经连线
+          rightLocked: false // 是否已经连线
         }
       ],
+      drawLineList: [] as any,
       selectItem: [] as any
     })
-
+    /* 
+    drawLineList 下的对象 
+      {
+          startPoint: { x: 0, y: 0 },
+          endPoint: { x: 0, y: 0},
+          leftIndex: 1,
+          rightIndex: 2
+        }
+    */
     const onLeftClick = (e: any, item: any) => {
-      const obj = useRect(e.target)
-      console.log(e, obj)
-      state.options.forEach((item: any) => {
-        item.left = false
+      // 是否只读
+      if (props.readOnly) return
+      state.options.forEach((option: any) => {
+        if (!option.leftLocked && item.index !== option.index) {
+          option.left = false
+        }
       })
-      item.left = !item.left
 
-      // 为true时添加定位
-      if (item.left) {
-        state.selectItem[0] = obj
+      const selectd = isDrawLine(item.index, 'left')
+      // 判断当前元素是否已经连线了
+      if (selectd.status) {
+        state.options.forEach((option: any) => {
+          if (!option.rightLocked) {
+            option.right = false
+          }
+        })
+        state.selectItem = []
+        // 如果已经连线了则去掉
+        state.drawLineList.splice(selectd.selectIndex, 1)
+        // 重新绘制
+        renderDrawLine(canvasRef.value)
+
+        state.options.forEach((option: any) => {
+          if (selectd.selectOption.leftIndex === option.index) {
+            option.left = false
+            option.leftLocked = false
+          }
+          if (selectd.selectOption.rightIndex === option.index) {
+            option.right = false
+            option.rightLocked = false
+          }
+        })
+      } else {
+        item.left = !item.left
+        if (item.left) {
+          // 为true时添加定位
+          const obj: any = useRect(e.target)
+          obj.index = item.index
+          state.selectItem[0] = obj
+        } else {
+          state.selectItem[0] = null
+        }
+
+        // 判断是否有二个的关联
+        if (state.selectItem[0] && state.selectItem[1]) {
+          const postion = calcPoint()
+          state.drawLineList.push(postion)
+          state.selectItem = []
+
+          renderDrawLine(canvasRef.value)
+        }
       }
     }
 
     const onRightClick = (e: any, item: any) => {
-      const obj = useRect(e.target)
-      console.log(e, obj)
-      state.options.forEach((item: any) => {
-        item.right = false
+      // 是否只读
+      if (props.readOnly) return
+      // 去掉
+      state.options.forEach((option: any) => {
+        if (!option.rightLocked && item.index !== option.index) {
+          option.right = false
+        }
       })
-      item.right = !item.right
+      const selectd = isDrawLine(item.index, 'right')
+
+      // 判断当前元素是否已经连线了
+      if (selectd.status) {
+        state.options.forEach((option: any) => {
+          if (!option.leftLocked) {
+            option.left = false
+          }
+        })
+        state.selectItem = []
+        // console.log(
+        //   selectd,
+        //   'selected',
+        //   JSON.stringify(state.drawLineList),
+        //   state.drawLineList,
+        //   state.drawLineList.length
+        // )
+        // 如果已经连线了则去掉
+        state.drawLineList.splice(selectd.selectIndex, 1)
+        // 重新绘制
+        renderDrawLine(canvasRef.value)
 
-      // 为true时添加定位
-      if (item.right) {
-        state.selectItem[1] = obj
+        state.options.forEach((option: any) => {
+          if (selectd.selectOption.leftIndex === option.index) {
+            option.left = false
+            option.leftLocked = false
+          }
+          if (selectd.selectOption.rightIndex === option.index) {
+            option.right = false
+            option.rightLocked = false
+          }
+        })
+      } else {
+        item.right = !item.right
+
+        if (item.right) {
+          // 为true时添加定位
+          const obj: any = useRect(e.target)
+          obj.index = item.index
+          state.selectItem[1] = obj
+        } else {
+          state.selectItem[1] = null
+        }
 
-        if (state.selectItem.length >= 2) {
-          drawLine()
+        // 判断是否有二个的关联
+        if (state.selectItem[0] && state.selectItem[1]) {
+          const postion = calcPoint()
+          state.drawLineList.push(postion)
+          state.selectItem = []
+          renderDrawLine(canvasRef.value)
         }
       }
     }
 
-    const drawLine = () => {
-      const canvas: any = document.getElementById(state.canvasDomId)
-      const canvasPostion = useRect(canvas)
-      console.log(canvasPostion, 'canvasPostion')
+    /**
+     * @description 判断是否已连接
+     * @param key 选中的选项标识
+     * @returns status 状态,  selectIndex 索引, selectOption 选中选项
+     */
+    const isDrawLine = (key: number, type = 'left') => {
+      const drawLineList = state.drawLineList || []
+      let status = false // true 连,false 没连
+      let selectIndex = 0 // 连了所在的索引
+      let selectOption: any = {}
+      drawLineList.forEach((item: any, index: number) => {
+        if (item.leftIndex === key && type === 'left') {
+          selectOption = item
+          status = true
+          selectIndex = index
+        } else if (item.rightIndex === key && type === 'right') {
+          selectOption = item
+          status = true
+          selectIndex = index
+        }
+      })
+      return {
+        status,
+        selectIndex,
+        selectOption
+      }
+    }
 
+    /**
+     * @description 计算连线坐标位置及左右关联编号
+     * @returns 连线的坐标
+     */
+    const calcPoint = () => {
+      const canvasPostion = useRect(canvasRef.value)
       const firstPostion = state.selectItem[0]
       const secondPostion = state.selectItem[1]
 
@@ -122,9 +245,45 @@ export default defineComponent({
         y: secondPostion.top + secondPostion.height / 2 - canvasPostion.top
       }
 
-      const ctx = canvas.getContext('2d')
-      console.log(startPoint, endPoint, ctx)
+      state.options.forEach((item: any) => {
+        if (item.index === firstPostion.index) {
+          item.leftLocked = true
+        }
+        if (item.index === secondPostion.index) {
+          item.rightLocked = true
+        }
+      })
+      return {
+        startPoint,
+        endPoint,
+        leftIndex: firstPostion.index,
+        rightIndex: secondPostion.index
+      }
+    }
+
+    /**
+     * @description 绘制连线
+     * @param canvasRef 对象
+     */
+    const renderDrawLine = (canvasRef: any) => {
+      // 重新画线
+      if (canvasRef.getContext) {
+        const ctx = canvasRef.getContext('2d')
+        ctx.clearRect(0, 0, state.answerRect.width, state.answerRect.height)
+        state.drawLineList.forEach((item: any) => {
+          drawLine(ctx, item.startPoint, item.endPoint)
+        })
+      }
+    }
 
+    /**
+     * @description 连线
+     * @param ctx canvas 对象
+     * @param startPoint 开始坐标
+     * @param endPoint 结束坐标
+     * @returns void(0)
+     */
+    const drawLine = (ctx: any, startPoint: any, endPoint: any) => {
       ctx.beginPath()
       ctx.moveTo(startPoint.x, startPoint.y)
       ctx.lineTo(endPoint.x, endPoint.y)
@@ -150,13 +309,13 @@ export default defineComponent({
     // }
 
     onMounted(() => {
-      const answer: any = document.getElementById(state.answerDomId)
-      const { width, height } = useRect(answer)
-      console.log(width, height)
-      state.answerObj = {
-        height,
-        width
-      }
+      // 获取canvas 对象
+      nextTick(() => {
+        // 获取canvas元素定位
+        const answer: any = document.getElementById(state.answerDomId)
+        const answerRect = useRect(answer)
+        state.answerRect = answerRect
+      })
     })
 
     return () => (
@@ -172,8 +331,14 @@ export default defineComponent({
         />
 
         <div class={[styles.unitAnswers]} id={state.answerDomId}>
-          <div class={styles.leftSection}>
-            {state.options.map((item: any) => (
+          <canvas
+            ref={canvasRef}
+            class={styles.canvasSection}
+            width={state.answerRect.width || 0}
+            height={state.answerRect.height || 0}
+          ></canvas>
+          {state.options.map((item: any) => (
+            <div class={styles.answerItem}>
               <div
                 class={[styles.unitItem, item.left && styles.active]}
                 onClick={(e: any) => onLeftClick(e, item)}
@@ -183,25 +348,38 @@ export default defineComponent({
                   class={styles.img}
                 />
               </div>
-            ))}
-          </div>
-          <div class={styles.rightSection}>
-            {state.options.map((item: any) => (
               <div
                 class={[styles.unitItem, item.right && styles.active]}
                 onClick={(e: any) => onRightClick(e, item)}
               >
-                Re{item.index}
+                {item.value}
+                {item.index}
               </div>
-            ))}
-          </div>
+            </div>
+          ))}
+        </div>
+        <div class={styles.resetBtnGroup}>
+          <Button
+            round
+            type="primary"
+            disabled={props.readOnly}
+            onClick={() => {
+              state.drawLineList = []
+              renderDrawLine(canvasRef.value)
 
-          <canvas
-            id={state.canvasDomId}
-            class={styles.canvasSection}
-            width={state.answerObj.width}
-            height={state.answerObj.height}
-          ></canvas>
+              // 清除所有的选中状态
+              state.options.forEach((item: any) => {
+                item.left = false
+                item.right = false
+                item.leftLocked = false
+                item.rightLocked = false
+              })
+              // 清除所有的选择的内容
+              state.selectItem = []
+            }}
+          >
+            重置
+          </Button>
         </div>
       </div>
     )

+ 87 - 0
src/views/unit-test/model/play-question/index.module.less

@@ -0,0 +1,87 @@
+.unitSubject {
+  padding: 15px;
+  margin: 0 13px;
+  background-color: #fff;
+  // overflow: hidden;
+  border-radius: 10px;
+}
+
+.unitSubjectTitle {
+  // display: flex;
+  // align-items: center;
+  // flex-wrap: wrap;
+  font-size: 16px;
+  font-weight: 500;
+  color: #333333;
+  line-height: 26px;
+  .unitScore {
+    color: #777777;
+  }
+  :global {
+    .van-tag {
+      vertical-align: middle;
+      margin-top: -3px;
+    }
+  }
+}
+.unitTitleSection {
+  margin-top: 20px;
+  border-radius: 6px;
+  border: 1px solid #d5d5d5;
+  padding: 25px 5px;
+}
+.unitTitleImg {
+  width: 100%;
+}
+
+.unitAnswers {
+  padding-top: 20px;
+  padding-bottom: 30px;
+}
+
+.playSection {
+  padding: 15px 12px;
+  background: #ffebdd;
+  border-radius: 10px;
+  .img {
+    width: 45px;
+    height: 45px;
+    margin-right: 12px;
+  }
+
+  .playTitle {
+    font-size: 16px;
+    font-weight: 500;
+    color: #333333;
+  }
+  .playBtn {
+    flex-shrink: 0;
+    font-size: 15px;
+    font-weight: 500;
+    color: #ffffff;
+    height: 31px;
+    line-height: 31px;
+  }
+}
+
+.unitScoreNum {
+  margin-top: 25px;
+  padding-top: 20px;
+  text-align: center;
+  .score {
+    font-size: 32px;
+    font-weight: bold;
+    color: #f67146;
+  }
+  .scoreTitle {
+    font-size: 16px;
+    font-weight: 500;
+    color: #333333;
+    padding: 4px 0 10px;
+  }
+
+  .scoreTips {
+    font-size: 12px;
+    color: #aaaaaa;
+  }
+}

+ 82 - 0
src/views/unit-test/model/play-question/index.tsx

@@ -0,0 +1,82 @@
+import { Tag, Image, Button, Cell, Icon } from 'vant'
+import { defineComponent, nextTick, onMounted, PropType, reactive } from 'vue'
+import styles from './index.module.less'
+import deepClone from '@/helpers/deep-clone'
+import iconSong from '../../images/icon-song.png'
+
+// 单选和多选题
+export default defineComponent({
+  name: 'choice-question',
+  props: {
+    value: {
+      type: [String, Number, Array],
+      default: ''
+    },
+    answers: {
+      type: Object,
+      default: {}
+    },
+    /* 只读 */
+    readOnly: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['update:value'],
+  setup(props, { emit }) {
+    const state = reactive({
+      list: [] as any
+    })
+
+    // const onSelect = (item: any) => {
+    //   if (props.type === 'checkbox') {
+    //     // 判断是否已选过
+    //     const value: any = props.value
+    //     if (value.includes(item.index)) {
+    //       const index = value.findIndex((v: any) => v === item.index)
+    //       value.splice(index, 1)
+    //       emit('update:value', [...value])
+    //     } else {
+    //       emit('update:value', [item.index, ...value])
+    //     }
+    //   } else {
+    //     emit('update:value', item.index)
+    //   }
+    // }
+    return () => (
+      <div class={styles.unitSubject}>
+        <div class={styles.unitSubjectTitle}>
+          4、请点击以下曲目进行评测,评测分数达到80分合格
+          <span class={styles.unitScore}>(5分)</span>
+          <Tag type="primary">演奏题</Tag>
+        </div>
+        <div class={styles.unitTitleSection}>
+          <Image
+            class={styles.unitTitleImg}
+            src="https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/dbb27307d428424c8efb9f26032cfa1a_mergeImage.png"
+          />
+        </div>
+
+        <div class={[styles.unitAnswers]}>
+          <Cell class={styles.playSection} center titleClass={['van-ellipsis', styles.playTitle]}>
+            {{
+              icon: () => <Image class={styles.img} src={iconSong} />,
+              title: () => <>没开机三江源没开机三江源</>,
+              value: () => (
+                <Button round class={styles.playBtn} type="primary">
+                  点击评测
+                  <Icon name="play" />
+                </Button>
+              )
+            }}
+          </Cell>
+          <div class={['van-hairline--top', styles.unitScoreNum]}>
+            <div class={styles.score}>89</div>
+            <div class={styles.scoreTitle}>评测分数</div>
+            <div class={styles.scoreTips}>多次评测取完整评测的最高分数</div>
+          </div>
+        </div>
+      </div>
+    )
+  }
+})

+ 74 - 0
src/views/unit-test/model/result-finish/index.module.less

@@ -0,0 +1,74 @@
+.finishResult {
+  color: #333333;
+  font-size: 15px;
+
+  .finishContainer {
+    background: url('../../images/exam-pass-bg.png') no-repeat top center;
+    background-size: contain;
+    height: 413px;
+  }
+
+  .finishContent {
+    padding-top: 180px;
+    text-align: center;
+
+    .scoreNums {
+      line-height: 70px;
+      font-size: 60px;
+      font-weight: bold;
+      color: #f67146;
+      span {
+        font-size: 20px;
+        padding-left: 10px;
+      }
+    }
+
+    .scoreName {
+      font-size: 20px;
+      font-weight: 600;
+      color: #191919;
+      padding: 10px 0 20px;
+      max-width: 80%;
+      margin-left: 10%;
+      line-height: 28px;
+    }
+    .scoreResult {
+      font-size: 16px;
+      font-weight: 500;
+      color: #f44541;
+    }
+  }
+}
+.finishFail {
+  .finishContainer {
+    background: url('../../images/exam-fail-bg.png') no-repeat top center;
+    background-size: contain;
+    height: 413px;
+  }
+}
+
+.finishBtnGroup {
+  margin-top: 30px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  .finishLeft,
+  .finishRight {
+    text-align: center;
+    height: 50px;
+    line-height: 46px;
+    width: 100%;
+    font-size: 18px;
+    font-weight: 500;
+  }
+  .finishLeft {
+    color: #703a17;
+    background: url('../../images/exam-right-bg.png') no-repeat center center;
+    background-size: contain;
+  }
+  .finishRight {
+    color: #722b0f;
+    background: url('../../images/exam-left-btn.png') no-repeat center center;
+    background-size: contain;
+  }
+}

+ 51 - 0
src/views/unit-test/model/result-finish/index.tsx

@@ -0,0 +1,51 @@
+import { defineComponent, PropType } from 'vue'
+import { Image, Button } from 'vant'
+import iconError from '../../images/icon-error.png'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'result-mode',
+  props: {
+    confirmButtonText: {
+      type: String,
+      default: '去练习'
+    },
+    cancelButtonText: {
+      type: String,
+      default: '我知道了'
+    },
+    status: {
+      type: String as PropType<'SUCCESS' | 'FAIL'>,
+      default: 'SUCCESS'
+    },
+    resultString: {
+      type: String,
+      default: '恭喜你,测验通过!'
+    }
+  },
+  emits: ['close', 'conform'],
+  setup(props, { emit }) {
+    return () => (
+      <div class={[styles.finishResult, props.status === 'FAIL' && styles.finishFail]}>
+        <div class={styles.finishContainer}>
+          <div class={styles.finishContent}>
+            <div class={styles.scoreNums}>
+              98<span>分</span>
+            </div>
+            <div class={styles.scoreName}>《长笛level上册测验一》</div>
+            <div class={styles.scoreResult}>{props.resultString}</div>
+          </div>
+        </div>
+
+        <div class={styles.finishBtnGroup}>
+          <div class={styles.finishLeft} onClick={() => emit('conform')}>
+            {props.confirmButtonText}
+          </div>
+          <div class={styles.finishRight} onClick={() => emit('close')}>
+            {props.cancelButtonText}
+          </div>
+        </div>
+      </div>
+    )
+  }
+})

+ 46 - 0
src/views/unit-test/practice-mode/index.module.less

@@ -0,0 +1,46 @@
+.unitDetail {
+  overflow: hidden;
+}
+
+.unitSection {
+  margin: 12px 13px;
+  padding: 13px 15px;
+  width: auto;
+  overflow: hidden;
+  border-radius: 10px;
+
+  .unitTitle {
+    font-size: 16px;
+    font-weight: 500;
+    color: #333333;
+  }
+
+  .unitCount {
+    padding-top: 10px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+  .qNums {
+    display: flex;
+    align-items: center;
+    font-size: 14px;
+    color: #333333;
+    line-height: 20px;
+    .num {
+      color: #f67146;
+    }
+  }
+  .icon {
+    width: 14px;
+    height: 14px;
+    margin-right: 4px;
+  }
+}
+
+.wapList {
+  width: 44px;
+  height: 44px;
+  flex-shrink: 0;
+  margin-left: 18px;
+}

+ 216 - 0
src/views/unit-test/practice-mode/index.tsx

@@ -0,0 +1,216 @@
+import {
+  ActionSheet,
+  Button,
+  Cell,
+  CountDown,
+  Icon,
+  Image,
+  Popup,
+  Swipe,
+  SwipeItem,
+  Tag
+} from 'vant'
+import { defineComponent, onMounted, reactive, ref } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import styles from './index.module.less'
+import iconQuestionNums from '../images/icon-question-nums.png'
+import iconCountDown from '../images/icon-count-down.png'
+import iconButtonList from '../images/icon-button-list.png'
+
+import OSticky from '@/components/o-sticky'
+import ChoiceQuestion from '../model/choice-question'
+import AnswerList from '../model/answer-list'
+import ODialog from '@/components/o-dialog'
+import DragQuestion from '../model/drag-question'
+import KeepLookQuestion from '../model/keep-look-question'
+import PlayQuestion from '../model/play-question'
+import ErrorMode from '../model/error-mode'
+import ResultFinish from '../model/result-finish'
+
+export default defineComponent({
+  name: 'unit-detail',
+  setup() {
+    const route = useRoute()
+    const router = useRouter()
+    const countDownRef = ref()
+    const swipeRef = ref()
+    const state = reactive({
+      visiableError: false,
+      visiableAnswer: false,
+      visiableResult: false,
+      currentIndex: 0,
+      questionList: [1, 2, 3, 4, 5],
+      answerList: {},
+      time: 30 * 60 * 1000,
+      visiableSure: false,
+      childs: [
+        { name: 'John', id: 0 },
+        { name: 'Joao', id: 1 },
+        { name: 'Jean', id: 2 }
+      ]
+    })
+
+    return () => (
+      <div class={styles.unitDetail}>
+        <Cell center class={styles.unitSection}>
+          {{
+            title: () => <div class={styles.unitTitle}>长笛level1上册测验一</div>,
+            label: () => (
+              <div class={styles.unitCount}>
+                <div class={styles.qNums}>
+                  <Icon class={styles.icon} name={iconQuestionNums} />
+                  题目数量 <span class={styles.num}>1</span>/4
+                </div>
+                <div class={styles.qNums}>
+                  <Icon class={styles.icon} name={iconCountDown} />
+                  剩余时长:
+                  <CountDown
+                    ref={countDownRef}
+                    time={state.time}
+                    format={'mm:ss'}
+                    autoStart={false}
+                  />
+                </div>
+              </div>
+            )
+          }}
+        </Cell>
+
+        <Swipe
+          loop={false}
+          showIndicators={false}
+          ref={swipeRef}
+          duration={300}
+          touchable={false}
+          lazyRender
+          // initialSwipe={state.currentIndex}
+          onChange={(index: number) => {
+            state.currentIndex = index
+          }}
+        >
+          <SwipeItem>
+            <ChoiceQuestion v-model:value={state.answerList[0]} type="checkbox" />
+          </SwipeItem>
+          <SwipeItem>
+            <ChoiceQuestion v-model:value={state.answerList[1]} type="radio" />
+          </SwipeItem>
+          <SwipeItem>
+            <DragQuestion />
+          </SwipeItem>
+          <SwipeItem>
+            <KeepLookQuestion />
+          </SwipeItem>
+          <SwipeItem>
+            <PlayQuestion />
+          </SwipeItem>
+        </Swipe>
+
+        <OSticky position="bottom" background="white">
+          <div class={['btnGroup btnMore']}>
+            {state.currentIndex > 0 && (
+              <Button
+                round
+                block
+                type="primary"
+                plain
+                onClick={() => {
+                  swipeRef.value?.prev()
+                }}
+              >
+                上一题
+              </Button>
+            )}
+            <Button
+              block
+              round
+              type="primary"
+              onClick={() => {
+                if (state.questionList.length - 1 === state.currentIndex) {
+                  state.visiableSure = true
+                } else {
+                  // swipeRef.value?.next()
+                  state.visiableError = true
+                }
+              }}
+            >
+              {state.questionList.length - 1 === state.currentIndex ? '测验完成' : '下一题'}
+            </Button>
+            <Image
+              src={iconButtonList}
+              class={[styles.wapList, 'van-haptics-feedback']}
+              onClick={() => (state.visiableAnswer = true)}
+            />
+          </div>
+        </OSticky>
+
+        {/* 题目集合 */}
+        <ActionSheet v-model:show={state.visiableAnswer} title="题目列表" safeAreaInsetBottom>
+          <AnswerList
+            value={[1, 3, 4]}
+            statusList={[
+              {
+                text: '答对',
+                color: '#71B0FF'
+              },
+              {
+                text: '答错',
+                color: '#FF8486'
+              }
+            ]}
+            onSelect={(item: any) => {
+              // 跳转,并且跳过动画
+              swipeRef.value?.swipeTo(item, {
+                immediate: true
+              })
+              state.visiableAnswer = false
+            }}
+          />
+        </ActionSheet>
+
+        <Popup
+          v-model:show={state.visiableError}
+          style={{ width: '90%' }}
+          round
+          closeOnClickOverlay={false}
+        >
+          <ErrorMode
+            onClose={() => (state.visiableError = false)}
+            onConform={() => {
+              swipeRef.value?.next()
+            }}
+          />
+        </Popup>
+
+        <Popup
+          v-model:show={state.visiableResult}
+          closeOnClickOverlay={false}
+          style={{ background: 'transparent', width: '96%' }}
+        >
+          <ResultFinish
+            // status="FAIL"
+            confirmButtonText="继续练习本考点"
+            cancelButtonText="下一个考点"
+            onClose={() => (state.visiableResult = false)}
+            onConform={() => {
+              console.log('Success')
+              state.visiableResult = false
+            }}
+          />
+        </Popup>
+
+        <ODialog
+          v-model:show={state.visiableSure}
+          title="测验完成"
+          message="确认本次测验的题目都完成了吗?\n提交后不可修改哦"
+          messageAlign="left"
+          showCancelButton
+          cancelButtonText="再等等"
+          confirmButtonText="确认完成"
+          onConfirm={() => {
+            state.visiableResult = true
+          }}
+        />
+      </div>
+    )
+  }
+})

+ 86 - 0
src/views/unit-test/test-exercise/index.module.less

@@ -0,0 +1,86 @@
+.unitDetail {
+  overflow: hidden;
+
+  .cellGroup {
+    margin: 12px 13px;
+
+    .cellTitle {
+      padding: 8px 0 12px;
+      font-size: 16px;
+      font-weight: 500;
+      color: #333333;
+      i {
+        display: inline-block;
+        margin-right: 6px;
+        width: 4px;
+        height: 12px;
+        background: #ff8057;
+        border-radius: 2px;
+      }
+    }
+
+    :global {
+      .van-cell {
+        font-size: 16px;
+        font-weight: 500;
+        color: #333333;
+        border-radius: 10px;
+        overflow: hidden;
+        padding: 18px 15px;
+        margin-bottom: 10px;
+      }
+      .van-cell__title,
+      .van-cell__value {
+        flex: 1 auto;
+      }
+
+      .van-cell__value {
+        flex-shrink: 0;
+        margin-left: 12px;
+        color: #f67146;
+      }
+      .van-cell__right-icon {
+        color: #f67146;
+      }
+    }
+
+    .img {
+      width: 18px;
+      height: 18px;
+      margin-right: 6px;
+      flex-shrink: 0;
+    }
+  }
+}
+
+.unitSection {
+  margin: 12px 13px 0;
+  padding: 15px;
+  width: auto;
+  overflow: hidden;
+  border-radius: 10px;
+
+  .unitTitle {
+    display: table;
+    font-size: 16px;
+    font-weight: 500;
+    color: #333333;
+    :global {
+      .van-tag {
+        margin-left: 10px;
+        vertical-align: middle;
+        margin-top: -3px;
+      }
+    }
+  }
+
+  .examSite {
+    padding-top: 15px;
+    font-size: 14px;
+    color: #333333;
+
+    p {
+      margin-bottom: 10px;
+    }
+  }
+}

+ 58 - 0
src/views/unit-test/test-exercise/index.tsx

@@ -0,0 +1,58 @@
+import { Cell, CellGroup, Image, Tag } from 'vant'
+import { defineComponent, onMounted, reactive, ref } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import styles from './index.module.less'
+import iconTag from '../images/icon-tag.png'
+
+export default defineComponent({
+  name: 'unit-detail',
+  setup() {
+    const route = useRoute()
+    const router = useRouter()
+    const state = reactive({
+      visiableNotice: false
+    })
+
+    const onDetail = (item: any) => {
+      router.push({
+        path: '/practice-mode'
+      })
+    }
+
+    return () => (
+      <div class={styles.unitDetail}>
+        <Cell center class={styles.unitSection}>
+          {{
+            title: () => (
+              <div class={styles.unitTitle}>
+                长笛level1上册测验一长笛level1上册测验一册测验一
+                <Tag type="primary">长笛单技课</Tag>
+              </div>
+            ),
+            label: () => (
+              <div class={styles.examSite}>
+                <p>1、在练习模式中可以对本次测验相关考点进行反复练习;</p>
+                <p>2、为了测验达到理想成绩,充分练习后再进行正式测验吧!</p>
+              </div>
+            )
+          }}
+        </Cell>
+
+        <div class={styles.cellGroup}>
+          <div class={styles.cellTitle}>
+            <i></i>考点
+          </div>
+          {[1, 2, 3, 44, 5, 6, 7, 8].map((item: any) => (
+            <Cell center isLink titleClass={['van-ellipsis']} onClick={() => onDetail(item)}>
+              {{
+                icon: () => <Image src={iconTag} class={styles.img} />,
+                title: () => <>乐理知识乐理知识乐理知识乐理知识乐理知识乐理知识-音符</>,
+                value: () => <span>去练习</span>
+              }}
+            </Cell>
+          ))}
+        </div>
+      </div>
+    )
+  }
+})

+ 23 - 13
src/views/unit-test/unit-detail/index.tsx

@@ -23,6 +23,7 @@ import AnswerList from '../model/answer-list'
 import ODialog from '@/components/o-dialog'
 import DragQuestion from '../model/drag-question'
 import KeepLookQuestion from '../model/keep-look-question'
+import PlayQuestion from '../model/play-question'
 
 export default defineComponent({
   name: 'unit-detail',
@@ -84,13 +85,21 @@ export default defineComponent({
             state.currentIndex = index
           }}
         >
-          {state.questionList.map((item: any) => (
-            <SwipeItem>
-              {/* <ChoiceQuestion v-model:value={state.answerList[item]} type="checkbox" /> */}
-              {/* <DragQuestion /> */}
-              <KeepLookQuestion />
-            </SwipeItem>
-          ))}
+          <SwipeItem>
+            <ChoiceQuestion v-model:value={state.answerList[0]} type="checkbox" />
+          </SwipeItem>
+          <SwipeItem>
+            <ChoiceQuestion v-model:value={state.answerList[1]} type="radio" />
+          </SwipeItem>
+          <SwipeItem>
+            <DragQuestion />
+          </SwipeItem>
+          <SwipeItem>
+            <KeepLookQuestion />
+          </SwipeItem>
+          <SwipeItem>
+            <PlayQuestion />
+          </SwipeItem>
         </Swipe>
 
         <OSticky position="bottom" background="white">
@@ -113,14 +122,15 @@ export default defineComponent({
               round
               type="primary"
               onClick={() => {
-                if (state.questionList.length - 1 === state.currentIndex) {
-                  state.visiableSure = true
-                } else {
-                  swipeRef.value?.next()
-                }
+                // if (state.questionList.length - 1 === state.currentIndex) {
+                //   state.visiableSure = true
+                // } else {
+                swipeRef.value?.next()
+                // }
               }}
             >
-              {state.questionList.length === state.currentIndex + 1 ? '测试完成' : '下一题'}
+              下一题
+              {/* {state.questionList.length === state.currentIndex + 1 ? '测试完成' : '下一题'} */}
             </Button>
             <Image
               src={iconButtonList}