Browse Source

Merge branch 'master' into jenkins

lex 2 years ago
parent
commit
7f147722b5
41 changed files with 713 additions and 304 deletions
  1. 22 0
      package-lock.json
  2. 2 0
      package.json
  3. 3 0
      public/project/css/initiation.css
  4. 3 0
      public/project/css/preRegister.css
  5. 26 33
      public/project/initiation.html
  6. 24 28
      public/project/preRegister.html
  7. 4 4
      src/school/companion-teacher/companion-teacher-register.tsx
  8. 4 4
      src/school/manage-teacher/manage-teacher-register.tsx
  9. 2 2
      src/school/school-detail/eidt-school.tsx
  10. BIN
      src/student/member-center/images/member-1.png
  11. BIN
      src/student/member-center/images/member-2.png
  12. BIN
      src/student/member-center/images/member-3.png
  13. 9 3
      src/student/member-center/index.module.less
  14. 10 2
      src/student/member-center/index.tsx
  15. 17 2
      src/student/music-group/goods-detail/index.module.less
  16. 11 0
      src/student/music-group/goods-detail/index.tsx
  17. 4 2
      src/student/music-group/member-bao/index.tsx
  18. 76 32
      src/student/music-group/pre-apply/component/payment.tsx
  19. BIN
      src/student/music-group/pre-apply/images/member_bao-1.png
  20. BIN
      src/student/music-group/pre-apply/images/member_bao-2.png
  21. BIN
      src/student/music-group/pre-apply/images/member_bao-3.png
  22. 1 0
      src/student/music-group/pre-apply/index.module.less
  23. 53 28
      src/student/music-group/pre-apply/index.tsx
  24. 35 48
      src/student/music-group/pre-apply/order-detail.tsx
  25. 2 11
      src/student/music-group/shop-address/address-operation.tsx
  26. 30 8
      src/student/my-orchestra/apply-withdrawal.tsx
  27. 1 1
      src/student/payment-result/index.tsx
  28. 42 5
      src/views/coursewarePlay/index.module.less
  29. 2 0
      src/views/coursewarePlay/index.tsx
  30. 73 0
      src/views/coursewarePlay/playRecordTime.tsx
  31. 14 0
      src/views/unit-test/model/choice-question/index.module.less
  32. 18 9
      src/views/unit-test/model/choice-question/index.tsx
  33. 14 6
      src/views/unit-test/model/play-question/index.tsx
  34. BIN
      src/views/unit-test/model/unit-audio/images/icon-audio.png
  35. 41 0
      src/views/unit-test/model/unit-audio/index.module.less
  36. 80 0
      src/views/unit-test/model/unit-audio/index.tsx
  37. 69 71
      src/views/unit-test/unit-detail/index.tsx
  38. 5 0
      src/views/unit-test/unit-list/index.module.less
  39. 1 1
      src/views/unit-test/unit-list/index.tsx
  40. 9 3
      src/views/unit-test/unit-list/unitDetail.tsx
  41. 6 1
      src/views/unit-test/unit.ts

+ 22 - 0
package-lock.json

@@ -18,6 +18,8 @@
         "countup.js": "^2.3.2",
         "dayjs": "^1.10.7",
         "echarts": "^5.3.3",
+        "eventemitter3": "^5.0.0",
+        "howler": "^2.2.3",
         "html-to-image": "^1.9.0",
         "html2canvas": "^1.4.1",
         "loaders.css": "^0.1.2",
@@ -4750,6 +4752,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/eventemitter3": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.0.tgz",
+      "integrity": "sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg=="
+    },
     "node_modules/execa": {
       "version": "5.1.1",
       "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz",
@@ -5366,6 +5373,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/howler": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmmirror.com/howler/-/howler-2.2.3.tgz",
+      "integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg=="
+    },
     "node_modules/html-tags": {
       "version": "3.1.0",
       "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-3.1.0.tgz",
@@ -13091,6 +13103,11 @@
       "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
       "dev": true
     },
+    "eventemitter3": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.0.tgz",
+      "integrity": "sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg=="
+    },
     "execa": {
       "version": "5.1.1",
       "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz",
@@ -13517,6 +13534,11 @@
         "parse-passwd": "^1.0.0"
       }
     },
+    "howler": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmmirror.com/howler/-/howler-2.2.3.tgz",
+      "integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg=="
+    },
     "html-tags": {
       "version": "3.1.0",
       "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-3.1.0.tgz",

+ 2 - 0
package.json

@@ -30,6 +30,8 @@
     "countup.js": "^2.3.2",
     "dayjs": "^1.10.7",
     "echarts": "^5.3.3",
+    "eventemitter3": "^5.0.0",
+    "howler": "^2.2.3",
     "html-to-image": "^1.9.0",
     "html2canvas": "^1.4.1",
     "loaders.css": "^0.1.2",

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

@@ -37,6 +37,9 @@ span {
   font-size: 16px;
   font-weight: 500;
 }
+.van-field--disabled .van-field__label {
+  color: #333;
+}
 
 .van-cell--required::before {
   left: 15px;

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

@@ -37,6 +37,9 @@ span {
   font-size: 16px;
   font-weight: 500;
 }
+.van-field--disabled .van-field__label {
+  color: #333;
+}
 
 .van-cell--required::before {
   left: 15px;

+ 26 - 33
public/project/initiation.html

@@ -192,30 +192,30 @@
           v-model="stu.phone" name="phone" :rules="[{ pattern, message: '输入监护人手机号码有误' }]" placeholder="请输入监护人手机号码">
         </van-field>
         <van-field label="学员姓名" :rules="[{ validator, message }]" name="username" v-model="stu.username"
-          placeholder="请填写学员真实姓名" :disabled='checkPhone'></van-field>
-        <van-field label="性别" name="sex" :disabled='checkPhone' :rules="[{ required: true, message: '请选择性别' }]">
+          placeholder="请填写学员真实姓名"></van-field>
+        <van-field label="性别" name="sex" :rules="[{ required: true, message: '请选择性别' }]">
           <template #input>
-            <van-radio-group v-model="stu.sex" :disabled='checkPhone' checked-color="#FF8057" direction="horizontal">
-              <van-tag size="large" type="primary" :color="!(stu.sex === 1) || checkPhone ? '#EAEAEA' : '#FF8057'"
-                :text-color="!(stu.sex === 1) || checkPhone ? '#AAA' : '#FFF'" class="radioSection" round>
+            <van-radio-group v-model="stu.sex" checked-color="#FF8057" direction="horizontal">
+              <van-tag size="large" type="primary" :color="!(stu.sex === 1) ? '#EAEAEA' : '#FF8057'"
+                :text-color="!(stu.sex === 1) ? '#AAA' : '#FFF'" class="radioSection" round>
                 <van-radio class="radioItem" :name="1"></van-radio>男生
               </van-tag>
-              <van-tag size="large" type="primary" :color="!(stu.sex === 0) || checkPhone ? '#EAEAEA' : '#FF8057'"
-                :text-color="!(stu.sex === 0) || checkPhone ? '#AAA' : '#FFF'" class="radioSection" round>
+              <van-tag size="large" type="primary" :color="!(stu.sex === 0) ? '#EAEAEA' : '#FF8057'"
+                :text-color="!(stu.sex === 0) ? '#AAA' : '#FFF'" class="radioSection" round>
                 <van-radio class="radioItem" :name="0"></van-radio>女生
               </van-tag>
             </van-radio-group>
           </template>
         </van-field>
-        <van-field label="在读年级" :disabled='checkPhone' v-model="stu.currentGrade" readonly name="currentGrade"
+        <van-field label="在读年级" v-model="stu.currentGrade" readonly name="currentGrade"
           :rules="[{ required: true, message: '请选择在读年级', trigger: 'onChange' }]" @click="() => showPicker = true"
-          placeholder="请选择在读年级" clickable>
+          placeholder="请选择在读年级">
           <template #right-icon>
             <van-icon name="arrow" :color="checkPhone ? '#aaa' : '#323233'" size="16"></van-icon>
           </template>
         </van-field>
         <van-field v-show="false" v-model="stu.currentGradeNum" name="currentGradeNum" readonly></van-field>
-        <van-field label="所在班级" :disabled='checkPhone' v-model="stu.currentClass" readonly name="currentClass"
+        <van-field label="所在班级" v-model="stu.currentClass" readonly name="currentClass"
           :rules="[{ required: true, message: '请选择所在班级', trigger: 'onChange' }]" @click="() => classPicker = true"
           placeholder="请选择所在班级">
           <template #right-icon>
@@ -223,48 +223,41 @@
           </template>
         </van-field>
 
-        <van-field label="孩子是否有学习过乐器" :disabled='checkPhone' name="musicalInstrumentsLearning"
+        <van-field label="孩子是否有学习过乐器" name="musicalInstrumentsLearning"
           :rules="[{ required: true, message: '请选择孩子是否有学习过乐器' }]">
           <template #input>
-            <van-radio-group v-model="stu.hasLearningExperience" :disabled='checkPhone' checked-color="#FF8057"
-              direction="horizontal">
-              <van-tag size="large" type="primary"
-                :color="!(stu.hasLearningExperience === 1) || checkPhone ? '#EAEAEA' : '#FF8057'"
-                :text-color="!(stu.hasLearningExperience === 1) || checkPhone ? '#AAA' : '#FFF'" class="radioSection"
-                round>
+            <van-radio-group v-model="stu.hasLearningExperience" checked-color="#FF8057" direction="horizontal">
+              <van-tag size="large" type="primary" :color="!(stu.hasLearningExperience === 1) ? '#EAEAEA' : '#FF8057'"
+                :text-color="!(stu.hasLearningExperience === 1) ? '#AAA' : '#FFF'" class="radioSection" round>
                 <van-radio class="radioItem" :name="1"></van-radio>是
               </van-tag>
-              <van-tag size="large" type="primary"
-                :color="!(stu.hasLearningExperience === 0) || checkPhone ? '#EAEAEA' : '#FF8057'"
-                :text-color="!(stu.hasLearningExperience === 0) || checkPhone ? '#AAA' : '#FFF'" class="radioSection"
+              <van-tag size="large" type="primary" :color="!(stu.hasLearningExperience === 0) ? '#EAEAEA' : '#FF8057'"
+                :text-color="!(stu.hasLearningExperience === 0) ? '#AAA' : '#FFF'" class="radioSection"
                 @click="() => stu.learningSubjectName = ''" round>
                 <van-radio class="radioItem" :name="0"></van-radio>否
               </van-tag>
             </van-radio-group>
           </template>
         </van-field>
-        <van-field label="所学乐器" :disabled='checkPhone' v-if="stu.hasLearningExperience==1" name="learningSubjectName"
+        <van-field label="所学乐器" v-if="stu.hasLearningExperience==1" name="learningSubjectName"
           v-model="stu.learningSubjectName" placeholder="(非必填)请选择所学乐器"></van-field>
-        <van-field label="是否报名参加管乐团家长会了解相关情况" :disabled='checkPhone' name="joinParentMeeting"
+        <van-field label="是否报名参加管乐团家长会了解相关情况" name="joinParentMeeting"
           :rules="[{ required: true, message: '请选择是否报名参加管乐团家长会了解相关情况' }]">
           <template #input>
-            <van-radio-group v-model="stu.joinParentMeeting" :disabled='checkPhone' checked-color="#FF8057"
-              direction="horizontal">
-              <van-tag size="large" type="primary"
-                :color="!(stu.joinParentMeeting === 1) || checkPhone ? '#EAEAEA' : '#FF8057'"
-                :text-color="!(stu.joinParentMeeting === 1) || checkPhone ? '#AAA' : '#FFF'" class="radioSection" round>
+            <van-radio-group v-model="stu.joinParentMeeting" checked-color="#FF8057" direction="horizontal">
+              <van-tag size="large" type="primary" :color="!(stu.joinParentMeeting === 1) ? '#EAEAEA' : '#FF8057'"
+                :text-color="!(stu.joinParentMeeting === 1) ? '#AAA' : '#FFF'" class="radioSection" round>
                 <van-radio class="radioItem" :name="1"></van-radio>是
               </van-tag>
-              <van-tag size="large" type="primary"
-                :color="!(stu.joinParentMeeting === 0) || checkPhone ? '#EAEAEA' : '#FF8057'"
-                :text-color="!(stu.joinParentMeeting === 0) || checkPhone ? '#AAA' : '#FFF'" class="radioSection" round>
+              <van-tag size="large" type="primary" :color="!(stu.joinParentMeeting === 0) ? '#EAEAEA' : '#FF8057'"
+                :text-color="!(stu.joinParentMeeting === 0) ? '#AAA' : '#FFF'" class="radioSection" round>
                 <van-radio class="radioItem" :name="0"></van-radio>否
               </van-tag>
             </van-radio-group>
           </template>
         </van-field>
-        <van-field :border="true" label="您对校管乐团的组建有什么建议" :disabled='checkPhone' type="textarea" rows="3"
-          name="personalSuggestion" v-model="stu.personalSuggestion" placeholder="(非必填)请填写您的建议内容">
+        <van-field :border="true" label="您对校管乐团的组建有什么建议" type="textarea" rows="3" name="personalSuggestion"
+          v-model="stu.personalSuggestion" placeholder="(非必填)请填写您的建议内容">
         </van-field>
       </van-cell-group>
 
@@ -324,7 +317,7 @@
           pickerType: null, // 下拉类型
           stu: {
             username: null, // 姓名
-            sex: 0, // 性别
+            sex: 1, // 性别
             phone: null, // 电话
             currentGrade: '', // 年级
             currentGradeNum: null, // 年级编号

+ 24 - 28
public/project/preRegister.html

@@ -192,29 +192,29 @@
           v-model="stu.phone" name="phone" :rules="[{ pattern, message: '输入监护人手机号码有误' }]" placeholder="请输入监护人手机号码">
         </van-field>
         <van-field label="学员姓名" :rules="[{ validator, message }]" name="username" v-model="stu.username"
-          placeholder="请填写学员真实姓名" :disabled='checkPhone'></van-field>
-        <van-field label="性别" name="sex" :disabled='checkPhone' :rules="[{ required: true, message: '请选择性别' }]">
+          placeholder="请填写学员真实姓名"></van-field>
+        <van-field label="性别" name="sex" :rules="[{ required: true, message: '请选择性别' }]">
           <template #input>
-            <van-radio-group v-model="stu.sex" :disabled='checkPhone' checked-color="#9A64FF" direction="horizontal">
-              <van-tag size="large" type="primary" :color="!(stu.sex === 1) || checkPhone ? '#EAEAEA' : '#64A9FF'"
-                :text-color="!(stu.sex === 1) || checkPhone ? '#AAA' : '#FFF'" class="radioSection" round>
+            <van-radio-group v-model="stu.sex" checked-color="#9A64FF" direction="horizontal">
+              <van-tag size="large" type="primary" :color="!(stu.sex === 1) ? '#EAEAEA' : '#64A9FF'"
+                :text-color="!(stu.sex === 1) ? '#AAA' : '#FFF'" class="radioSection" round>
                 <van-radio class="radioItem" :name="1"></van-radio>男生
               </van-tag>
-              <van-tag size="large" type="primary" :color="!(stu.sex === 0)  || checkPhone? '#EAEAEA' : '#64A9FF'"
-                :text-color="!(stu.sex === 0)  || checkPhone? '#AAA' : '#FFF'" class="radioSection" round>
+              <van-tag size="large" type="primary" :color="!(stu.sex === 0) ? '#EAEAEA' : '#64A9FF'"
+                :text-color="!(stu.sex === 0) ? '#AAA' : '#FFF'" class="radioSection" round>
                 <van-radio class="radioItem" :name="0"></van-radio>女生
               </van-tag>
             </van-radio-group>
           </template>
         </van-field>
-        <van-field label="在读年级" :disabled='checkPhone' v-model="stu.currentGrade" readonly name="currentGrade"
+        <van-field label="在读年级" v-model="stu.currentGrade" readonly name="currentGrade"
           :rules="[{ required: true, message: '请选择在读年级', trigger: 'onChange' }]" @click="pickerChange"
-          placeholder="请选择在读年级" clickable>
+          placeholder="请选择在读年级">
           <template #right-icon>
             <van-icon name="arrow" :color="checkPhone ? '#aaa' : '#323233'" size="16"></van-icon>
           </template>
         </van-field>
-        <van-field label="所在班级" :disabled='checkPhone' v-model="stu.currentClass" readonly name="currentClass"
+        <van-field label="所在班级" v-model="stu.currentClass" readonly name="currentClass"
           :rules="[{ required: true, message: '请选择所在班级', trigger: 'onChange' }]" @click="pickerChange1"
           placeholder="请选择所在班级">
           <template #right-icon>
@@ -224,7 +224,7 @@
       </van-cell-group>
 
       <van-cell-group inset class="cell-group">
-        <van-field label="选报声部" :disabled='checkPhone' v-model="stu.registerSubjectId" readonly name="registerSubjectId"
+        <van-field label="选报声部" v-model="stu.registerSubjectId" readonly name="registerSubjectId"
           :rules="[{ required: true, message: '请选择选报声部', trigger: 'onChange' }]" @click="pickerChange2"
           placeholder="请选择选报声部">
           <template #right-icon>
@@ -232,42 +232,38 @@
           </template>
         </van-field>
 
-        <van-field label="乐器准备方式" :disabled='checkPhone' name="instrumentsPrepareMode"
-          :rules="[{ required: true, message: '请选择乐器准备方式' }]">
+        <van-field label="乐器准备方式" name="instrumentsPrepareMode" :rules="[{ required: true, message: '请选择乐器准备方式' }]">
           <template #input>
-            <van-radio-group v-model="stu.instrumentsPrepareMode" :disabled='checkPhone' checked-color="#9A64FF"
-              direction="horizontal">
+            <van-radio-group v-model="stu.instrumentsPrepareMode" checked-color="#9A64FF" direction="horizontal">
               <van-tag size="large" type="primary"
-                :color="!(stu.instrumentsPrepareMode === 'GROUP_PURCHASE')  || checkPhone ? '#EAEAEA' : '#64A9FF'"
-                :text-color="!(stu.instrumentsPrepareMode === 'GROUP_PURCHASE') || checkPhone ? '#AAA' : '#FFF'"
-                class="radioSection" round>
+                :color="!(stu.instrumentsPrepareMode === 'GROUP_PURCHASE')  ? '#EAEAEA' : '#64A9FF'"
+                :text-color="!(stu.instrumentsPrepareMode === 'GROUP_PURCHASE') ? '#AAA' : '#FFF'" class="radioSection"
+                round>
                 <van-radio class="radioItem" name="GROUP_PURCHASE"></van-radio>团购
               </van-tag>
               <van-tag size="large" type="primary"
-                :color="!(stu.instrumentsPrepareMode === 'ONESELF') || checkPhone ? '#EAEAEA' : '#64A9FF'"
-                :text-color="!(stu.instrumentsPrepareMode === 'ONESELF') || checkPhone ? '#AAA' : '#FFF'"
-                class="radioSection" round>
+                :color="!(stu.instrumentsPrepareMode === 'ONESELF') ? '#EAEAEA' : '#64A9FF'"
+                :text-color="!(stu.instrumentsPrepareMode === 'ONESELF') ? '#AAA' : '#FFF'" class="radioSection" round>
                 <van-radio class="radioItem" name="ONESELF"></van-radio>自备
               </van-tag>
             </van-radio-group>
           </template>
         </van-field>
 
-        <van-field label="乐团学习系统准备方式" :disabled='checkPhone' name="learningSystemPrepareMode"
+        <van-field label="乐团学习系统准备方式" name="learningSystemPrepareMode"
           :rules="[{ required: true, message: '请选择乐团学习系统准备方式' }]">
           <template #input>
-            <van-radio-group v-model="stu.learningSystemPrepareMode" :disabled='checkPhone' checked-color="#9A64FF"
-              direction="horizontal">
+            <van-radio-group v-model="stu.learningSystemPrepareMode" checked-color="#9A64FF" direction="horizontal">
               <!-- :color="checkPhone ? '#ccc': '#9A64FF'" -->
               <van-tag size="large" type="primary"
-                :color="!(stu.learningSystemPrepareMode === 'GROUP_PURCHASE') || checkPhone ? '#EAEAEA' : '#64A9FF'"
-                :text-color="!(stu.learningSystemPrepareMode === 'GROUP_PURCHASE') || checkPhone ? '#AAA' : '#FFF'"
+                :color="!(stu.learningSystemPrepareMode === 'GROUP_PURCHASE') ? '#EAEAEA' : '#64A9FF'"
+                :text-color="!(stu.learningSystemPrepareMode === 'GROUP_PURCHASE') ? '#AAA' : '#FFF'"
                 class="radioSection" round>
                 <van-radio class="radioItem" name="GROUP_PURCHASE"></van-radio>团购
               </van-tag>
               <van-tag size="large" type="primary"
-                :color="!(stu.learningSystemPrepareMode === 'ONESELF') || checkPhone ? '#EAEAEA' : '#64A9FF'"
-                :text-color="!(stu.learningSystemPrepareMode === 'ONESELF') || checkPhone ? '#AAA' : '#FFF'"
+                :color="!(stu.learningSystemPrepareMode === 'ONESELF') ? '#EAEAEA' : '#64A9FF'"
+                :text-color="!(stu.learningSystemPrepareMode === 'ONESELF') ? '#AAA' : '#FFF'"
                 :color="checkPhone ? '#ccc': '#9A64FF'" class="radioSection" round>
                 <van-radio class="radioItem" name="ONESELF"></van-radio>自备
               </van-tag>

+ 4 - 4
src/school/companion-teacher/companion-teacher-register.tsx

@@ -369,6 +369,10 @@ export default defineComponent({
               onBlur={checkchangePhone}
               type="tel"
             ></Field>
+            <div class={styles.phoneTips}>
+              <Icon name="warning" size="16" />
+              提示:手机号码将成为您管乐团老师端登录账号
+            </div>
             <Field
               label="真实姓名"
               v-model={state.forms.realName}
@@ -601,10 +605,6 @@ export default defineComponent({
                   )
               }}
             </Field>
-            <div class={styles.phoneTips}>
-              <Icon name="warning" size="16" />
-              提示:手机号码将成为您管乐团老师端登录账号
-            </div>
           </CellGroup>
 
           <div class={styles.protocol}>

+ 4 - 4
src/school/manage-teacher/manage-teacher-register.tsx

@@ -238,6 +238,10 @@ export default defineComponent({
               maxlength={11}
               type="tel"
             ></Field>
+            <div class={styles.phoneTips}>
+              <Icon name="warning" size="16" />
+              提示:手机号码将成为您管乐团管理端登录账号
+            </div>
 
             <Field
               label="身份证号码"
@@ -315,10 +319,6 @@ export default defineComponent({
                   )
               }}
             </Field>
-            <div class={styles.phoneTips}>
-              <Icon name="warning" size="16" />
-              提示:手机号码将成为您管乐团管理端登录账号
-            </div>
           </CellGroup>
 
           <div class={styles.protocol}>

+ 2 - 2
src/school/school-detail/eidt-school.tsx

@@ -202,7 +202,7 @@ export default defineComponent({
                 rows={3}
                 v-model={forms.email}
                 maxlength={50}
-                placeholder="邮箱"
+                placeholder="请输入邮箱"
               >
                 {{ label: () => <p class={styles.addP}>邮箱</p> }}
               </Field>
@@ -211,7 +211,7 @@ export default defineComponent({
                 rows={3}
                 v-model={forms.emergencyContact}
                 maxlength={50}
-                placeholder="负责人"
+                placeholder="请输入负责人姓名"
               >
                 {{ label: () => <p class={styles.addP}>负责人</p> }}
               </Field>

BIN
src/student/member-center/images/member-1.png


BIN
src/student/member-center/images/member-2.png


BIN
src/student/member-center/images/member-3.png


+ 9 - 3
src/student/member-center/index.module.less

@@ -86,6 +86,13 @@
     }
   }
 
+  .memberImgs {
+    img {
+      margin-bottom: 6px;
+      width: 100%;
+    }
+  }
+
   .intro {
     background: url('./images/tip_bg.png') no-repeat center;
     background-size: contain;
@@ -156,7 +163,6 @@
     box-sizing: content-box;
     padding-top: 16px;
     padding-bottom: 10px;
-    margin-bottom: 10px;
   }
   .system-item {
     display: flex;
@@ -169,7 +175,7 @@
     box-sizing: border-box;
     background: #ffffff;
     border-radius: 12px;
-    border: 1px solid #e5e5e5;
+    // border: 1px solid #e5e5e5;
     .title {
       font-weight: 500;
       font-size: 14px;
@@ -211,7 +217,7 @@
       // background: linear-gradient(215deg, #ffe7c4 0%, rgba(250, 211, 156, 0.21) 100%);
       background: url('./images/vip_bg.png') no-repeat center center;
       background-size: cover;
-      border: 1px solid #b1652e;
+      // border: 1px solid #b1652e;
       position: relative;
       .title {
         color: #814014;

+ 10 - 2
src/student/member-center/index.tsx

@@ -12,6 +12,9 @@ import dayjs from 'dayjs'
 import { memberType } from '@/constant'
 import { moneyFormat } from '@/helpers/utils'
 import OHeader from '@/components/o-header'
+import member1 from './images/member-1.png'
+import member2 from './images/member-2.png'
+import member3 from './images/member-3.png'
 
 export const getAssetsHomeFile = (fileName: string) => {
   const path = `./images/${fileName}`
@@ -252,8 +255,13 @@ export default defineComponent({
               </div>
             </div>
           </div>
+          <div class={styles.memberImgs}>
+            <img src={member1} />
+            <img src={member2} />
+            <img src={member3} />
+          </div>
 
-          <div class={[styles.intro]}>
+          {/* <div class={[styles.intro]}>
             <p>
               团练宝会员使用包括平台提供教材的所有训练乐谱,并专享“乐器练习云教练”八大核心功能,孩子在家就能轻松完成乐器自主规范练习。
             </p>
@@ -270,7 +278,7 @@ export default defineComponent({
                 </div>
               ))}
             </div>
-          </div>
+          </div> */}
         </div>
         <div class={styles.btnGroup}>
           <div class={styles.priceSection}>

+ 17 - 2
src/student/music-group/goods-detail/index.module.less

@@ -19,10 +19,25 @@
   padding: 8px 0;
   :global {
     .van-cell {
-      padding-top: 5px;
-      padding-bottom: 5px;
+      padding: 5px 16px;
     }
   }
+
+  .sectionTips {
+    margin: 6px 16px;
+    display: flex;
+    align-items: center;
+    font-size: 14px;
+    color: #ff4e19;
+    padding: 5px 6px;
+    background: #ffebdd;
+    border-radius: 6px;
+  }
+  .iconGives {
+    width: 32px;
+    height: 18px;
+    margin-right: 8px;
+  }
 }
 
 .priceGroup {

+ 11 - 0
src/student/music-group/goods-detail/index.tsx

@@ -3,6 +3,7 @@ import { moneyFormat } from '@/helpers/utils'
 import { Swipe, SwipeItem, Image, CellGroup, Cell, showImagePreview } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './index.module.less'
+import iconGives from '../pre-apply/images/icon-gives.png'
 
 export default defineComponent({
   name: 'goods-detail',
@@ -11,6 +12,10 @@ export default defineComponent({
       type: Number,
       default: 0
     },
+    goodsInfo: {
+      type: Object,
+      default: () => ({})
+    },
     id: {
       type: String,
       defualt: ''
@@ -200,6 +205,12 @@ export default defineComponent({
             title={product.name}
             titleClass={[styles.goodsName, 'van-ellipsis']}
           />
+          {this.goodsInfo.showFree && (
+            <div class={styles.sectionTips}>
+              <Image src={iconGives} class={styles.iconGives} />
+              赠价值{this.goodsInfo.originalPrice}元乐器维保服务一年
+            </div>
+          )}
         </CellGroup>
 
         {this.detailMobileHtml && (

+ 4 - 2
src/student/music-group/member-bao/index.tsx

@@ -2,15 +2,17 @@ import { Image } from 'vant'
 import { defineComponent } from 'vue'
 import bg1 from '../pre-apply/images/member_bao-1.png'
 import bg2 from '../pre-apply/images/member_bao-2.png'
+import bg3 from '../pre-apply/images/member_bao-3.png'
 
 export default defineComponent({
   name: 'member-bao',
   setup() {
     return () => (
-      <>
+      <div style={{ lineHeight: '0' }}>
         <Image src={bg1} />
         <Image src={bg2} />
-      </>
+        <Image src={bg3} />
+      </div>
     )
   }
 })

+ 76 - 32
src/student/music-group/pre-apply/component/payment.tsx

@@ -23,6 +23,7 @@ import { CountUp } from 'countup.js'
 import OPopup from '@/components/o-popup'
 import MemberBao from '../../member-bao'
 import GoodsDetail from '../../goods-detail'
+import ODialog from '@/components/o-dialog'
 
 export default defineComponent({
   name: 'payment',
@@ -46,7 +47,11 @@ export default defineComponent({
       memberBaoStatus: false, // 团练宝详情状态
       goodsStatus: false, //
       selectGoodsId: null as any,
-      currentPrice: 0
+      currentPrice: 0,
+      selectGoodsInfo: {} as any,
+      dialogStatus: false,
+      dialogMessage: '',
+      dialogConfig: {} as any
     })
 
     // 查询未支付订单
@@ -55,29 +60,9 @@ export default defineComponent({
         const { data } = await request.get('/api-student/userPaymentOrder/unpaid')
         // 判断是否有待支付订单
         if (data.id) {
-          showConfirmDialog({
-            message: '您有待支付的订单,是否继续支付',
-            cancelButtonText: '取消订单',
-            confirmButtonText: '继续支付'
-          })
-            .then(() => {
-              const paymentConfig = data.paymentConfig
-              router.push({
-                path: '/orderDetail',
-                query: {
-                  pm: 1, // h5乐团报名
-                  config: JSON.stringify(paymentConfig.paymentConfig),
-                  orderNo: paymentConfig.orderNo
-                }
-              })
-            })
-            .catch(async () => {
-              try {
-                await request.post('/api-student/userPaymentOrder/cancelPayment/' + data.orderNo)
-              } catch {
-                //
-              }
-            })
+          state.dialogMessage = '您有待支付的订单,是否继续支付'
+          state.dialogStatus = true
+          state.dialogConfig = data
         }
       } catch {
         //
@@ -285,7 +270,6 @@ export default defineComponent({
             2、所有参与乐团的学生免费赠送选报声部教材,教材随乐器一同发放,若您自备乐器,则需承担教材运费。
           </p>
         </div>
-
         <CheckboxGroup
           v-model={state.check}
           style={{ paddingBottom: '20px' }}
@@ -331,11 +315,24 @@ export default defineComponent({
                             e.stopPropagation()
                             state.selectGoodsId = state.goodsInfo.goodsId
                             state.currentPrice = state.goodsInfo.currentPrice
+                            state.selectGoodsInfo = {
+                              showFree: true,
+                              originalPrice: state.repaireInfo.originalPrice
+                            }
                             state.goodsStatus = true
                           }}
                         />
                         <div class={styles.sectionContent}>
-                          <h2>{state.goodsInfo.goodsName}</h2>
+                          <h2
+                            onClick={(e: any) => {
+                              e.stopPropagation()
+                              state.selectGoodsId = state.goodsInfo.goodsId
+                              state.currentPrice = state.goodsInfo.currentPrice
+                              state.goodsStatus = true
+                            }}
+                          >
+                            {state.goodsInfo.goodsName}
+                          </h2>
                           <Tag
                             color="linear-gradient(135deg, #FF8C4A 0%, #FF531C 100%)"
                             textColor="#fff"
@@ -428,11 +425,22 @@ export default defineComponent({
                             e.stopPropagation()
                             state.selectGoodsId = state.textBookInfo.goodsId
                             state.currentPrice = state.textBookInfo.currentPrice
+                            state.selectGoodsInfo = {}
                             state.goodsStatus = true
                           }}
                         />
                         <div class={styles.sectionContent}>
-                          <h2>{state.textBookInfo.goodsName}</h2>
+                          <h2
+                            onClick={(e: any) => {
+                              e.stopPropagation()
+                              state.selectGoodsId = state.textBookInfo.goodsId
+                              state.currentPrice = state.textBookInfo.currentPrice
+                              state.selectGoodsInfo = {}
+                              state.goodsStatus = true
+                            }}
+                          >
+                            {state.textBookInfo.goodsName}
+                          </h2>
                           <Tag
                             color="linear-gradient(135deg, #FF8C4A 0%, #FF531C 100%)"
                             textColor="#fff"
@@ -522,7 +530,14 @@ export default defineComponent({
                           }}
                         />
                         <div class={styles.sectionContent}>
-                          <h2>{state.vipInfo.goodsName}</h2>
+                          <h2
+                            onClick={(e: any) => {
+                              e.stopPropagation()
+                              state.memberBaoStatus = true
+                            }}
+                          >
+                            {state.vipInfo.goodsName}
+                          </h2>
                           <Tag
                             color="linear-gradient(135deg, #FF8C4A 0%, #FF531C 100%)"
                             textColor="#fff"
@@ -563,7 +578,6 @@ export default defineComponent({
             </>
           )}
         </CheckboxGroup>
-
         <OSticky position="bottom" background="white">
           <div class={styles.paymentContainer}>
             <div class={styles.payemntPrice}>
@@ -591,16 +605,46 @@ export default defineComponent({
             </div>
           </div>
         </OSticky>
-
         <OPopup v-model:modelValue={state.memberBaoStatus} position="right">
           <MemberBao />
         </OPopup>
-
         <OPopup v-model:modelValue={state.goodsStatus} position="right" destroy>
           {state.goodsStatus && (
-            <GoodsDetail id={state.selectGoodsId} groupPrice={state.currentPrice} />
+            <GoodsDetail
+              id={state.selectGoodsId}
+              groupPrice={state.currentPrice}
+              goodsInfo={state.selectGoodsInfo}
+            />
           )}
         </OPopup>
+        <ODialog
+          title="提示"
+          v-model:show={state.dialogStatus}
+          message={state.dialogMessage}
+          confirmButtonText="继续支付"
+          cancelButtonText="取消订单"
+          showCancelButton
+          onConfirm={() => {
+            const paymentConfig = state.dialogConfig.paymentConfig
+            router.push({
+              path: '/orderDetail',
+              query: {
+                pm: 1, // h5乐团报名
+                config: JSON.stringify(paymentConfig.paymentConfig),
+                orderNo: paymentConfig.orderNo
+              }
+            })
+          }}
+          onCancel={async () => {
+            try {
+              await request.post(
+                '/api-student/userPaymentOrder/cancelPayment/' + state.dialogConfig.orderNo
+              )
+            } catch {
+              //
+            }
+          }}
+        />
       </>
     )
   }

BIN
src/student/music-group/pre-apply/images/member_bao-1.png


BIN
src/student/music-group/pre-apply/images/member_bao-2.png


BIN
src/student/music-group/pre-apply/images/member_bao-3.png


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

@@ -112,6 +112,7 @@
   background: #ffffff;
   border-radius: 10px;
   padding: 12px 14px;
+  margin-bottom: 12px;
   font-size: 14px;
   color: #777;
   line-height: 20px;

+ 53 - 28
src/student/music-group/pre-apply/index.tsx

@@ -10,6 +10,7 @@ import { useRoute, useRouter } from 'vue-router'
 import { setLogout } from '@/state'
 import request from '@/helpers/request'
 import { browser, getUrlCode } from '@/helpers/utils'
+import ODialog from '@/components/o-dialog'
 
 export default defineComponent({
   name: 'pre-apply',
@@ -24,6 +25,8 @@ export default defineComponent({
       register: true, // 是否注册
       // showPopup: false,
       code: '' as any,
+      dialogStatus: false,
+      dialogMessage: '',
 
       // 是否开启微信登录(测试使用)默认为false
       testIsWeixin: false
@@ -73,40 +76,44 @@ export default defineComponent({
         // 判断乐团报名,只有 乐团报名 乐团交付 已交付才可以报名
         const oStatus = data.status // 乐团状态
         if (oStatus !== 'REGISTER' && oStatus !== 'DOING' && oStatus !== 'DONE') {
-          showDialog({
-            title: '提示',
-            message: '乐团建设中,请稍等'
-          }).then(() => {
-            setLogout()
+          // showDialog({
+          //   title: '提示',
+          //   message: '乐团建设中,请稍等'
+          // }).then(() => {
+          //   setLogout()
 
-            const query = {
-              returnUrl: route.path,
-              ...route.query
-            } as any
-            router.replace({
-              path: '/loginMusic',
-              query: query
-            })
-          })
+          //   const query = {
+          //     returnUrl: route.path,
+          //     ...route.query
+          //   } as any
+          //   router.replace({
+          //     path: '/loginMusic',
+          //     query: query
+          //   })
+          // })
+          state.dialogMessage = '乐团建设中,请稍等'
+          state.dialogStatus = true
           return
         }
 
         // 判断乐团
         if (data.registerOrchestra >= 1) {
-          showDialog({
-            title: '提示',
-            message: '您已在其它乐团'
-          }).then(() => {
-            setLogout()
-            const query = {
-              returnUrl: route.path,
-              ...route.query
-            } as any
-            router.replace({
-              path: '/loginMusic',
-              query: query
-            })
-          })
+          // showDialog({
+          //   title: '提示',
+          //   message: '您已在其它乐团'
+          // }).then(() => {
+          //   setLogout()
+          //   const query = {
+          //     returnUrl: route.path,
+          //     ...route.query
+          //   } as any
+          //   router.replace({
+          //     path: '/loginMusic',
+          //     query: query
+          //   })
+          // })
+          state.dialogMessage = '您已在其它乐团'
+          state.dialogStatus = true
           return
         }
 
@@ -242,6 +249,24 @@ export default defineComponent({
             <p class={styles.popupTips}>请使用微信打开</p>
           </div>
         </Popup> */}
+
+        <ODialog
+          title="提示"
+          v-model:show={state.dialogStatus}
+          message={state.dialogMessage}
+          confirmButtonText="确定"
+          onConfirm={() => {
+            setLogout()
+            const query = {
+              returnUrl: route.path,
+              ...route.query
+            } as any
+            router.replace({
+              path: '/loginMusic',
+              query: query
+            })
+          }}
+        />
       </div>
     )
   }

+ 35 - 48
src/student/music-group/pre-apply/order-detail.tsx

@@ -16,6 +16,8 @@ import UserAuth from './component/user-auth'
 import qs from 'query-string'
 import MemberBao from '../member-bao'
 import GoodsDetail from '../goods-detail'
+import ODialog from '@/components/o-dialog'
+import { orderStatus } from '@/constant'
 
 export default defineComponent({
   name: 'order-detail',
@@ -40,7 +42,9 @@ export default defineComponent({
       memberBaoStatus: false, // 团练宝详情状态
       goodsStatus: false, //
       selectGoodsId: null as any,
-      currentPrice: 0
+      currentPrice: 0,
+      dialogStatus: false,
+      dialogMessage: ''
     })
 
     const orderType = computed(() => {
@@ -92,17 +96,19 @@ export default defineComponent({
         // REFUNDED: '已退款'
         if (data.status !== 'WAIT_PAY' && data.status !== 'PAYING') {
           // status
-          showConfirmDialog({
-            message: '订单处理中,请稍等',
-            showCancelButton: false
-          }).then(() => {
-            router.replace({
-              path: '/payment-result',
-              query: {
-                orderNo: state.orderNo
-              }
-            })
-          })
+          state.dialogStatus = true
+          state.dialogMessage = '订单' + orderStatus[data.status]
+          // showConfirmDialog({
+          //   message: '订单处理中,请稍等',
+          //   showCancelButton: false
+          // }).then(() => {
+          //   router.replace({
+          //     path: '/payment-result',
+          //     query: {
+          //       orderNo: state.orderNo
+          //     }
+          //   })
+          // })
         }
       } catch {
         //
@@ -113,18 +119,6 @@ export default defineComponent({
       const config: any = state.config
       state.pay_channel = val.pay_channel
       if (val.payCode === 'payResult') {
-        // router.push({
-        //   path: '/payResult',
-        //   query: {
-        //     pay_channel: val.pay_channel,
-        //     wxAppId: config.wxAppId,
-        //     body: config.body,
-        //     price: config.price,
-        //     orderNo: config.merOrderNo,
-        //     userId: config.userId
-        //   }
-        // })
-
         window.location.href =
           window.location.origin +
           '/orchestra-student/#/payResult?' +
@@ -260,18 +254,7 @@ export default defineComponent({
         const { data } = await request.get('/api-student/userPaymentOrder/detail/' + state.orderNo)
         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
-          //     }
-          //   })
-          // })
+        if (data.status !== 'WAIT_PAY' && data.status !== 'PAYING') {
           router.replace({
             path: '/payment-result',
             query: {
@@ -315,18 +298,7 @@ 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
-          //     }
-          //   })
-          // })
+        if (data.status !== 'WAIT_PAY' && data.status !== 'PAYING') {
           router.replace({
             path: '/payment-result',
             query: {
@@ -559,6 +531,21 @@ export default defineComponent({
             <GoodsDetail id={state.selectGoodsId} groupPrice={state.currentPrice} />
           )}
         </OPopup>
+
+        <ODialog
+          title="提示"
+          v-model:show={state.dialogStatus}
+          message={state.dialogMessage}
+          confirmButtonText="确定"
+          onConfirm={() => {
+            router.replace({
+              path: '/payment-result',
+              query: {
+                orderNo: state.orderNo
+              }
+            })
+          }}
+        />
       </>
     )
   }

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

@@ -69,28 +69,19 @@ export default defineComponent({
               ...params
             }
           })
-          setTimeout(() => {
-            showToast('修改成功')
-          }, 100)
         } else {
           await request.post('/api-student/userReceiveAddress/save', {
             data: {
               ...params
             }
           })
-          setTimeout(() => {
-            showToast('添加成功')
-          }, 100)
         }
 
-        setTimeout(() => {
-          state.isClick = false
-          router.back()
-        }, 1100)
+        state.isClick = false
+        router.back()
       } catch (e: any) {
         //
         state.isClick = false
-        console.log(e)
       }
     }
 

+ 30 - 8
src/student/my-orchestra/apply-withdrawal.tsx

@@ -20,6 +20,7 @@ export default defineComponent({
     })
     const forms = reactive({
       status: false,
+      statusMember: false,
       reason: '',
       id: route.query.id,
       dataInfo: {} as any,
@@ -40,7 +41,22 @@ export default defineComponent({
         showToast('请输入退团原因')
         return
       }
-      forms.status = true
+
+      try {
+        const { data } = await request.post('/api-student/student/hasWaitActivateVipOrder', {
+          requestType: 'form',
+          data: {
+            orchestraId: forms.id
+          }
+        })
+        if (data) {
+          forms.statusMember = true
+        } else {
+          forms.status = true
+        }
+      } catch {
+        //
+      }
     }
 
     //
@@ -56,12 +72,7 @@ export default defineComponent({
         })
 
         forms.isClick = false
-        setTimeout(() => {
-          showToast('申请成功')
-        }, 100)
-        setTimeout(() => {
-          router.replace('/my-orchestra')
-        }, 1100)
+        router.replace('/my-orchestra')
       } catch {
         //
         forms.isClick = false
@@ -128,7 +139,18 @@ export default defineComponent({
           confirmButtonText="确定"
           cancelButtonText="取消"
           showCancelButton
-          onConfirm={() => onConfirm()}
+          onConfirm={onConfirm}
+        ></ODialog>
+
+        <ODialog
+          v-model:show={forms.statusMember}
+          title="提示"
+          message={'您有待激活的团练宝订单,是否继续退团?'}
+          messageAlign="left"
+          confirmButtonText="继续"
+          cancelButtonText="取消"
+          showCancelButton
+          onConfirm={onConfirm}
         ></ODialog>
       </div>
     )

+ 1 - 1
src/student/payment-result/index.tsx

@@ -166,7 +166,7 @@ export default defineComponent({
                     </Tag>
                   </div>
                 ),
-                value: () => <span>x 1</span>
+                value: () => <span>{goods.goodsType === 'VIP' ? '6个月' : 'x 1'}</span>
               }}
             </Cell>
           ))}

+ 42 - 5
src/views/coursewarePlay/index.module.less

@@ -36,8 +36,8 @@
   align-items: center;
   z-index: 10;
   padding: 4px 10px 4px 15px;
-  :global{
-    .van-icon{
+  :global {
+    .van-icon {
       margin-right: 8px;
     }
   }
@@ -214,14 +214,51 @@
   }
 }
 
-.loadWrap{
+.loadWrap {
   position: absolute;
   left: 0;
   top: 0;
   right: 0;
   bottom: 0;
-  background: linear-gradient(45deg, #21232A, #111218);
+  background: linear-gradient(45deg, #21232a, #111218);
   display: flex;
   justify-content: center;
   align-items: center;
-}
+}
+.playRecordTimeWrap{
+  position: fixed;
+  top: 0;
+}
+.playRecordTime {
+  position: fixed;
+  top: 33px;
+  left: 16px;
+  background: rgba(0, 0, 0, 0.4);
+  border-radius: 20px;
+  font-size: 6px;
+  padding: 6px;
+  display: flex;
+  align-items: center;
+  color: #fff;
+  .timeLoad {
+    width: 5px;
+    height: 5px;
+    background: #ff4e19;
+    border: .5px solid #ffffff;
+    border-radius: 50%;
+    margin-right: 3px;
+    animation: loadFade 1s ease-in-out infinite;
+  }
+}
+
+@keyframes loadFade {
+  0%{
+    opacity: 0;
+  }
+  50%{
+    opacity: .5;
+  }
+  100%{
+    opacity: 1;
+  }
+}

+ 2 - 0
src/views/coursewarePlay/index.tsx

@@ -41,6 +41,7 @@ import { browser, getSecondRPM } from '@/helpers/utils'
 import { Vue3Lottie } from 'vue3-lottie'
 import playLoadData from './datas/data.json'
 import { usePageVisibility } from '@vant/use'
+import PlayRecordTime from './playRecordTime'
 
 export default defineComponent({
   name: 'CoursewarePlay',
@@ -692,6 +693,7 @@ export default defineComponent({
             />
           </Popup>
         </div>
+        {/* <PlayRecordTime list={data.itemList} /> */}
       </div>
     )
   }

+ 73 - 0
src/views/coursewarePlay/playRecordTime.tsx

@@ -0,0 +1,73 @@
+import request from '@/helpers/request'
+import { getSecondRPM } from '@/helpers/utils'
+import { state } from '@/state'
+import { computed, defineComponent, onMounted, onUnmounted, reactive, ref } from 'vue'
+import { useRoute } from 'vue-router'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'playRecordTime',
+  props: {
+    list: {
+      type: Array,
+      default: () => []
+    }
+  },
+  setup(props) {
+    const route = useRoute()
+    const saveModel = reactive({
+      startTime: Date.now(),
+      timer: null as any,
+      totalTime: 0,
+      loading: false
+    })
+    const total = computed(() => {
+      const _total = props.list.reduce(
+        (_total: number, item: any) => _total + item.adviseStudyTimeSecond,
+        0
+      )
+      return _total
+    })
+
+    const handleRecord = async () => {
+      if (saveModel.loading) return
+      saveModel.loading = true
+      try {
+        const res: any = await request.post(
+          `${state.platformApi}/courseSchedule/coursewarePlayTime`,
+          {
+            params: {
+              courseScheduleId: route.query.courseId,
+              playTime: saveModel.totalTime
+            }
+          }
+        )
+      } catch (error) {}
+    }
+    const timerRecord = ref()
+    onMounted(() => {
+      timerRecord.value = setInterval(() => {
+        if (saveModel.totalTime > total.value){
+          handleRecord()
+        }
+        saveModel.totalTime = Math.floor((Date.now() - saveModel.startTime) / 1000)
+      }, 1000)
+    })
+    onUnmounted(() => {
+      clearInterval(timerRecord.value)
+    })
+    return () => (
+      <div class={styles.playRecordTimeWrap}>
+        <div
+          class={styles.playRecordTime}
+          style={{ display: saveModel.totalTime > total.value ? 'none' : '' }}
+        >
+          <div class={styles.timeLoad}></div>
+          <div>
+            {getSecondRPM(saveModel.totalTime)} / {getSecondRPM(total.value)}
+          </div>
+        </div>
+      </div>
+    )
+  }
+})

+ 14 - 0
src/views/unit-test/model/choice-question/index.module.less

@@ -18,6 +18,7 @@
     padding: 15px 12px;
     display: flex;
     align-items: center;
+    justify-content: space-between;
     font-size: 16px;
     font-weight: 500;
     color: #333333;
@@ -32,9 +33,22 @@
         }
       }
     }
+    .valueAudio {
+      width: 170px;
+    }
   }
   .active {
     background-color: #ffebdd;
     color: #f67146;
   }
+
+  .answerContent {
+    display: flex;
+    align-items: center;
+  }
+  .answerChoice {
+    font-size: 16px;
+    font-weight: 500;
+    line-height: 26px;
+  }
 }

+ 18 - 9
src/views/unit-test/model/choice-question/index.tsx

@@ -3,6 +3,7 @@ import { computed, defineComponent, PropType, reactive } from 'vue'
 import { AnswerType, labelOptions, QuestionType } from '../../unit'
 import AnserTitle from '../anser-title'
 import AnswerAnalysis from '../answer-analysis'
+import UnitAudio from '../unit-audio'
 import styles from './index.module.less'
 
 // 单选题 - 多选题
@@ -108,15 +109,23 @@ export default defineComponent({
                 class={[styles.unitAnswer, item.checked && styles.active]}
                 onClick={() => onSelect(item)}
               >
-                <span class={styles.option}>{labelOptions[index + 1]}.</span>
-                {item.questionAnswerTypeCode === AnswerType.IMAGE && (
-                  <div class={styles.value}>
-                    <Image src={item.questionAnswer} />
-                  </div>
-                )}
-                {item.questionAnswerTypeCode === AnswerType.TXT && (
-                  <div class={styles.value}>{item.questionAnswer}</div>
-                )}
+                <div class={styles.answerContent}>
+                  <span class={styles.option}>{labelOptions[index + 1]}.</span>
+                  {item.questionAnswerTypeCode === AnswerType.IMAGE && (
+                    <div class={styles.value}>
+                      <Image src={item.questionAnswer} />
+                    </div>
+                  )}
+                  {item.questionAnswerTypeCode === AnswerType.TXT && (
+                    <div class={styles.value}>{item.questionAnswer}</div>
+                  )}
+                  {item.questionAnswerTypeCode === AnswerType.AUDIO && (
+                    <div class={styles.value}>
+                      <UnitAudio src={item.questionAnswer} class={styles.valueAudio} />
+                    </div>
+                  )}
+                </div>
+                {props.showRate && <div class={styles.answerChoice}>{item.selectRate}%人选择</div>}
               </div>
             ))}
           </div>

+ 14 - 6
src/views/unit-test/model/play-question/index.tsx

@@ -154,7 +154,13 @@ export default defineComponent({
                   icon: () => <Image class={styles.img} src={iconSong} />,
                   title: () => <>{questionExtendsInfo.value.musicName}</>,
                   value: () => (
-                    <Button round class={styles.playBtn} type="primary" onClick={onEvaluation}>
+                    <Button
+                      round
+                      class={styles.playBtn}
+                      type="primary"
+                      onClick={onEvaluation}
+                      disabled={props.readOnly}
+                    >
                       点击评测
                       <Icon name="play" />
                     </Button>
@@ -163,11 +169,13 @@ export default defineComponent({
               </Cell>
             )}
 
-            <div class={['van-hairline--top', styles.unitScoreNum]}>
-              <div class={styles.score}>{state.score}</div>
-              <div class={styles.scoreTitle}>评测分数</div>
-              <div class={styles.scoreTips}>多次评测取完整评测的最高分数</div>
-            </div>
+            {!props.readOnly && (
+              <div class={['van-hairline--top', styles.unitScoreNum]}>
+                <div class={styles.score}>{state.score}</div>
+                <div class={styles.scoreTitle}>评测分数</div>
+                <div class={styles.scoreTips}>多次评测取完整评测的最高分数</div>
+              </div>
+            )}
           </div>
         </div>
         {props.showAnalysis && (

BIN
src/views/unit-test/model/unit-audio/images/icon-audio.png


+ 41 - 0
src/views/unit-test/model/unit-audio/index.module.less

@@ -0,0 +1,41 @@
+.unitAudio {
+  position: relative;
+  display: flex;
+}
+
+.nativeAudio {
+  width: 100%;
+  height: 40px;
+}
+
+.htmlAudio {
+  // position: absolute;
+  width: 100%;
+  height: 40px;
+  box-sizing: border-box;
+  background: #ff8057;
+  border-radius: 20px;
+  padding: 0 12px;
+  z-index: 9;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  color: #fff;
+  .htmlTimes {
+    display: flex;
+    align-items: center;
+    color: #fff;
+    font-size: 16px;
+    font-weight: 500;
+    color: #ffffff;
+    line-height: 22px;
+  }
+  .iconAudio {
+    margin-right: 6px;
+    font-size: 18px;
+  }
+
+  .audioStatus {
+    font-size: 18px;
+  }
+}

+ 80 - 0
src/views/unit-test/model/unit-audio/index.tsx

@@ -0,0 +1,80 @@
+import { Icon } from 'vant'
+import { defineComponent, reactive } from 'vue'
+import styles from './index.module.less'
+import { Howl } from 'howler'
+import iconAudio from './images/icon-audio.png'
+import { eventUnit } from '../../unit'
+
+// 音频播放
+export default defineComponent({
+  name: 'unit-audio',
+  props: {
+    src: {
+      type: String,
+      default: ''
+    }
+  },
+  setup(props) {
+    const state = reactive({
+      audioStatus: 'stopped',
+      duration: 0
+    })
+    // https://github.com/Tonejs/Tone.js/issues/784 部分ios上可能存在问题
+    const htick: any = new Howl({
+      src: props.src,
+      format: ['mp3']
+      // sprite: {
+      //   blast: [0, 3000],
+      //   laser: [4000, 1000],
+      //   winner: [6000, 5000]
+      // }
+    })
+    console.log(htick)
+
+    htick.on('load', (val) => {
+      console.log('111')
+      console.log(htick.duration())
+      state.duration = Math.ceil(htick.duration())
+    })
+    // 播放结束
+    htick.on('end', () => {
+      console.log('1')
+    })
+
+    eventUnit.on('unitAudioStop', () => {
+      htick.stop()
+      state.audioStatus = 'stopped'
+    })
+
+    return () => (
+      <div class={styles.unitAudio}>
+        <div class={styles.htmlAudio}>
+          <div class={styles.htmlTimes}>
+            <Icon class={styles.iconAudio} name={iconAudio} />
+            <span>{state.duration}"</span>
+          </div>
+
+          {/* pause-circle */}
+          <Icon
+            name={state.audioStatus === 'stopped' ? 'play-circle' : 'pause-circle'}
+            class={styles.audioStatus}
+            onClick={() => {
+              if (state.audioStatus === 'stopped') {
+                eventUnit.emit('unitAudioStop')
+                htick.play()
+                state.audioStatus = 'paused'
+              } else if (state.audioStatus === 'paused') {
+                htick.pause()
+                state.audioStatus = 'stopped'
+              }
+            }}
+          />
+        </div>
+        {/* <audio controls class={styles.nativeAudio} id={state.id}>
+          <source src={props.src} type="audio/mpeg" />
+          您的浏览器不支持该音频格式。
+        </audio> */}
+      </div>
+    )
+  }
+})

+ 69 - 71
src/views/unit-test/unit-detail/index.tsx

@@ -223,39 +223,10 @@ export default defineComponent({
             resizeSwipeItemHeight()
           }}
         >
-          {state.questionList.map((item: any, index: number) => (
-            // item.questionTypeCode === QuestionType.CHECKBOX && (
-            //   <SwipeItem>
-            //     <ChoiceQuestion
-            //       v-model:value={item.userAnswer}
-            //       index={index + 1}
-            //       data={item}
-            //       readOnly
-            //       type="radio"
-            //       showAnalysis
-            //       analysis={{
-            //         message: item.answerAnalysis,
-            //         topic: true, // 是否显示结果
-            //         userResult: formatUserResult(item.id) // 用户答题对错
-            //       }}
-            //     />
-            //   </SwipeItem>
-            // )
-            <SwipeItem>
-              <div class="swipe-item-question">
-                {item.questionTypeCode === QuestionType.RADIO && (
-                  <ChoiceQuestion
-                    v-model:value={item.userAnswer}
-                    index={index + 1}
-                    data={item}
-                    readOnly
-                    type="radio"
-                    showRate={item.showRate}
-                    showAnalysis={item.showAnalysis}
-                    analysis={item.analysis}
-                  />
-                )}
-                {item.questionTypeCode === QuestionType.CHECKBOX && (
+          {state.questionList.map(
+            (item: any, index: number) =>
+              item.questionTypeCode === QuestionType.CHECKBOX && (
+                <SwipeItem>
                   <ChoiceQuestion
                     v-model:value={item.userAnswer}
                     index={index + 1}
@@ -266,44 +237,71 @@ export default defineComponent({
                     showAnalysis={item.showAnalysis}
                     analysis={item.analysis}
                   />
-                )}
-                {item.questionTypeCode === QuestionType.SORT && (
-                  <DragQuestion
-                    readOnly
-                    v-model:value={item.userAnswer}
-                    data={item}
-                    index={index + 1}
-                    showRate={item.showRate}
-                    showAnalysis={item.showAnalysis}
-                    analysis={item.analysis}
-                  />
-                )}
-                {item.questionTypeCode === QuestionType.LINK && (
-                  <KeepLookQuestion
-                    readOnly
-                    v-model:value={item.userAnswer}
-                    data={item}
-                    index={index + 1}
-                    showRate={item.showRate}
-                    showAnalysis={item.showAnalysis}
-                    analysis={item.analysis}
-                  />
-                )}
-                {item.questionTypeCode === QuestionType.PLAY && (
-                  <PlayQuestion
-                    readOnly
-                    v-model:value={item.userAnswer}
-                    data={item}
-                    index={index + 1}
-                    unitId={state.id as any}
-                    showRate={item.showRate}
-                    showAnalysis={item.showAnalysis}
-                    analysis={item.analysis}
-                  />
-                )}
-              </div>
-            </SwipeItem>
-          ))}
+                </SwipeItem>
+              )
+            // <SwipeItem>
+            //   <div class="swipe-item-question">
+            //     {item.questionTypeCode === QuestionType.RADIO && (
+            //       <ChoiceQuestion
+            //         v-model:value={item.userAnswer}
+            //         index={index + 1}
+            //         data={item}
+            //         readOnly
+            //         type="radio"
+            //         showRate={item.showRate}
+            //         showAnalysis={item.showAnalysis}
+            //         analysis={item.analysis}
+            //       />
+            //     )}
+            //     {item.questionTypeCode === QuestionType.CHECKBOX && (
+            //       <ChoiceQuestion
+            //         v-model:value={item.userAnswer}
+            //         index={index + 1}
+            //         data={item}
+            //         readOnly
+            //         type="checkbox"
+            //         showRate={item.showRate}
+            //         showAnalysis={item.showAnalysis}
+            //         analysis={item.analysis}
+            //       />
+            //     )}
+            //     {item.questionTypeCode === QuestionType.SORT && (
+            //       <DragQuestion
+            //         readOnly
+            //         v-model:value={item.userAnswer}
+            //         data={item}
+            //         index={index + 1}
+            //         showRate={item.showRate}
+            //         showAnalysis={item.showAnalysis}
+            //         analysis={item.analysis}
+            //       />
+            //     )}
+            //     {item.questionTypeCode === QuestionType.LINK && (
+            //       <KeepLookQuestion
+            //         readOnly
+            //         v-model:value={item.userAnswer}
+            //         data={item}
+            //         index={index + 1}
+            //         showRate={item.showRate}
+            //         showAnalysis={item.showAnalysis}
+            //         analysis={item.analysis}
+            //       />
+            //     )}
+            //     {item.questionTypeCode === QuestionType.PLAY && (
+            //       <PlayQuestion
+            //         readOnly
+            //         v-model:value={item.userAnswer}
+            //         data={item}
+            //         index={index + 1}
+            //         unitId={state.id as any}
+            //         showRate={item.showRate}
+            //         showAnalysis={item.showAnalysis}
+            //         analysis={item.analysis}
+            //       />
+            //     )}
+            //   </div>
+            // </SwipeItem>
+          )}
         </Swipe>
 
         <OSticky position="bottom" background="white">

+ 5 - 0
src/views/unit-test/unit-list/index.module.less

@@ -109,6 +109,11 @@
       font-size: 16px;
       line-height: 22px;
       color: #333;
+      span {
+        width: 80px;
+        display: inline-block;
+        text-align: right;
+      }
     }
   }
   :global {

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

@@ -95,7 +95,7 @@ export default defineComponent({
       <div class={styles.unitTest}>
         <OSticky position="top">
           {/* */}
-          <OHeader isBack>
+          <OHeader>
             {{
               right: () =>
                 platformApi == '/api-teacher' ? (

+ 9 - 3
src/views/unit-test/unit-list/unitDetail.tsx

@@ -140,9 +140,15 @@ export default defineComponent({
                   根据学生入团的批次对不同训练阶段的学生进行分类,不同训练阶段的学生可布置不同标准的课后训练和单元测验内容。
                 </p>
                 <br />
-                <p>I类学生:最新进入本乐团的学员</p>
-                <p>II类学生:较早进入本乐团的学员</p>
-                <p>III类学生:最早进入本乐团的学员</p>
+                <p>
+                  <span>I类学生:</span>最新进入本乐团的学员
+                </p>
+                <p>
+                  <span>II类学生:</span>较早进入本乐团的学员
+                </p>
+                <p>
+                  <span>III类学生:</span>最早进入本乐团的学员
+                </p>
               </div>
             )
           }}

+ 6 - 1
src/views/unit-test/unit.ts

@@ -1,3 +1,8 @@
+import EventEmitter from "eventemitter3"
+
+export const eventUnit = new EventEmitter()
+
+
 export const labelOptions = {
   1: 'A',
   2: 'B',
@@ -30,5 +35,5 @@ export const QuestionTypeName = {
 export const AnswerType = {
   IMAGE: 'IMAGE',
   TXT: 'TXT',
-  RADIO: 'RADIO',
+  AUDIO: 'AUDIO',
 }