فهرست منبع

Merge branch 'feature-wxl-newVersion' into kt-dev

lex 10 ماه پیش
والد
کامیت
74fcab7366
53فایلهای تغییر یافته به همراه4412 افزوده شده و 2891 حذف شده
  1. 1 1
      osmd-extended
  2. 16 10
      package-lock.json
  3. 1 0
      package.json
  4. 1 0
      src/hooks/useDrag/useDragGuidance.ts
  5. 3 2
      src/page-instrument/component/mode-type-mode/index.tsx
  6. BIN
      src/page-instrument/custom-plugins/guide-driver/images/btn-close.png
  7. BIN
      src/page-instrument/custom-plugins/guide-driver/images/btn-finsh.png
  8. BIN
      src/page-instrument/custom-plugins/guide-driver/images/btn-next.png
  9. BIN
      src/page-instrument/custom-plugins/guide-driver/images/evaluating/e1.png
  10. BIN
      src/page-instrument/custom-plugins/guide-driver/images/evaluating/e2.png
  11. BIN
      src/page-instrument/custom-plugins/guide-driver/images/evaluating/e3.png
  12. BIN
      src/page-instrument/custom-plugins/guide-driver/images/evaluating/r1.png
  13. BIN
      src/page-instrument/custom-plugins/guide-driver/images/evaluating/r2.png
  14. BIN
      src/page-instrument/custom-plugins/guide-driver/images/evaluating/r3.png
  15. BIN
      src/page-instrument/custom-plugins/guide-driver/images/evaluating/r4.png
  16. BIN
      src/page-instrument/custom-plugins/guide-driver/images/follow/f1.png
  17. BIN
      src/page-instrument/custom-plugins/guide-driver/images/follow/f2.png
  18. BIN
      src/page-instrument/custom-plugins/guide-driver/images/follow/f3.png
  19. BIN
      src/page-instrument/custom-plugins/guide-driver/images/practise/d1.png
  20. BIN
      src/page-instrument/custom-plugins/guide-driver/images/practise/d2.png
  21. BIN
      src/page-instrument/custom-plugins/guide-driver/images/practise/d3.png
  22. BIN
      src/page-instrument/custom-plugins/guide-driver/images/practise/d4.png
  23. BIN
      src/page-instrument/custom-plugins/guide-driver/images/practise/d5.png
  24. BIN
      src/page-instrument/custom-plugins/guide-driver/images/practise/d6.png
  25. BIN
      src/page-instrument/custom-plugins/guide-driver/images/practise/d7-1.png
  26. BIN
      src/page-instrument/custom-plugins/guide-driver/images/practise/d7.png
  27. BIN
      src/page-instrument/custom-plugins/guide-driver/images/practise/d8.png
  28. BIN
      src/page-instrument/custom-plugins/guide-driver/images/practise/d9.png
  29. BIN
      src/page-instrument/custom-plugins/guide-driver/images/report/r1.png
  30. BIN
      src/page-instrument/custom-plugins/guide-driver/images/report/r2.png
  31. BIN
      src/page-instrument/custom-plugins/guide-driver/images/report/r3.png
  32. BIN
      src/page-instrument/custom-plugins/guide-driver/images/report/r4.png
  33. BIN
      src/page-instrument/custom-plugins/guide-driver/images/report/r5.png
  34. 420 0
      src/page-instrument/custom-plugins/guide-driver/index.less
  35. 0 0
      src/page-instrument/custom-plugins/guide-driver/index.module.less
  36. 1112 0
      src/page-instrument/custom-plugins/guide-driver/index.tsx
  37. 37 16
      src/page-instrument/evaluat-model/evaluat-result/index.module.less
  38. 161 176
      src/page-instrument/evaluat-model/evaluat-result/index.tsx
  39. 80 93
      src/page-instrument/evaluat-model/index.tsx
  40. 34 37
      src/page-instrument/follow-model/index.tsx
  41. 60 26
      src/page-instrument/header-top/index.module.less
  42. 222 210
      src/page-instrument/header-top/index.tsx
  43. 101 110
      src/page-instrument/header-top/modeView.tsx
  44. 7 6
      src/page-instrument/main.ts
  45. 66 29
      src/page-instrument/view-evaluat-report/component/share-top/index.module.less
  46. 333 350
      src/page-instrument/view-evaluat-report/component/share-top/index.tsx
  47. 375 395
      src/page-instrument/view-evaluat-report/index.tsx
  48. 494 507
      src/page-instrument/view-figner/index.tsx
  49. 63 61
      src/state.ts
  50. 52 50
      src/style.css
  51. 41 43
      src/view/abnormal-pop/index.tsx
  52. 661 679
      src/view/evaluating/index.tsx
  53. 71 90
      src/view/fingering/index.tsx

+ 1 - 1
osmd-extended

@@ -1 +1 @@
-Subproject commit 331c4861934bbdc6b63c210308fc18f92a80c178
+Subproject commit e96ef0a67e70a21b7ea9a2a5a7925f8a9360a396

+ 16 - 10
package-lock.json

@@ -14,6 +14,7 @@
         "consola": "^2.15.3",
         "cos-js-sdk-v5": "^1.4.20",
         "dayjs": "^1.11.7",
+        "driver.js": "^1.3.1",
         "eventemitter3": "^5.0.0",
         "hammerjs": "^2.0.8",
         "howler": "^2.2.3",
@@ -3449,6 +3450,11 @@
         "tslib": "^2.0.3"
       }
     },
+    "node_modules/driver.js": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/driver.js/-/driver.js-1.3.1.tgz",
+      "integrity": "sha512-MvUdXbqSgEsgS/H9KyWb5Rxy0aE6BhOVT4cssi2x2XjmXea6qQfgdx32XKVLLSqTaIw7q/uxU5Xl3NV7+cN6FQ=="
+    },
     "node_modules/electron-to-chromium": {
       "version": "1.4.342",
       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.342.tgz",
@@ -7148,8 +7154,7 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
       "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
-      "dev": true,
-      "requires": {}
+      "dev": true
     },
     "@octokit/plugin-rest-endpoint-methods": {
       "version": "7.1.2",
@@ -7307,8 +7312,7 @@
     "@vant/use": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/@vant/use/-/use-1.5.1.tgz",
-      "integrity": "sha512-Zxd7lDz/LliVYEQi3PR9a8CQa/kGCVzF0u9hqDMaTlgXlbG0wHMFPllrcG0ThR6bfs8xrYVuSFM9pJn6HSoUGQ==",
-      "requires": {}
+      "integrity": "sha512-Zxd7lDz/LliVYEQi3PR9a8CQa/kGCVzF0u9hqDMaTlgXlbG0wHMFPllrcG0ThR6bfs8xrYVuSFM9pJn6HSoUGQ=="
     },
     "@varlet/icons": {
       "version": "2.9.5",
@@ -7371,8 +7375,7 @@
       "version": "4.3.4",
       "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz",
       "integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==",
-      "dev": true,
-      "requires": {}
+      "dev": true
     },
     "@vitejs/plugin-vue-jsx": {
       "version": "3.0.1",
@@ -8096,6 +8099,11 @@
         "tslib": "^2.0.3"
       }
     },
+    "driver.js": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/driver.js/-/driver.js-1.3.1.tgz",
+      "integrity": "sha512-MvUdXbqSgEsgS/H9KyWb5Rxy0aE6BhOVT4cssi2x2XjmXea6qQfgdx32XKVLLSqTaIw7q/uxU5Xl3NV7+cN6FQ=="
+    },
     "electron-to-chromium": {
       "version": "1.4.342",
       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.342.tgz",
@@ -8872,8 +8880,7 @@
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/postcss-pxtorem/-/postcss-pxtorem-6.0.0.tgz",
       "integrity": "sha512-ZRXrD7MLLjLk2RNGV6UA4f5Y7gy+a/j1EqjAfp9NdcNYVjUMvg5HTYduTjSkKBkRkfqbg/iKrjMO70V4g1LZeg==",
-      "dev": true,
-      "requires": {}
+      "dev": true
     },
     "proxy-from-env": {
       "version": "1.1.0",
@@ -9682,8 +9689,7 @@
     "ws": {
       "version": "8.13.0",
       "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
-      "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
-      "requires": {}
+      "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA=="
     },
     "y18n": {
       "version": "5.0.8",

+ 1 - 0
package.json

@@ -14,6 +14,7 @@
     "consola": "^2.15.3",
     "cos-js-sdk-v5": "^1.4.20",
     "dayjs": "^1.11.7",
+    "driver.js": "^1.3.1",
     "eventemitter3": "^5.0.0",
     "hammerjs": "^2.0.8",
     "howler": "^2.2.3",

+ 1 - 0
src/hooks/useDrag/useDragGuidance.ts

@@ -34,6 +34,7 @@ export default function useDragGuidance() {
    }
    return {
       guidanceShow,
+      guideInfoData,
       setGuidanceShow
    }
 }

+ 3 - 2
src/page-instrument/component/mode-type-mode/index.tsx

@@ -36,6 +36,7 @@ export default defineComponent({
         } else {
           // vip
           data.showVip = true;
+          state.isVip = true;
         }
       }
     };
@@ -86,8 +87,8 @@ export default defineComponent({
             <img id="modeType-1" style={{ cursor: state.isPercussion ? "not-allowed" : "pointer" }} onClick={() => headTopData.handleChangeModeType("follow")} src={state.isPercussion ? icons.icon_5 : icons.icon_2} />
             <img id="modeType-2" style={{ cursor: state.enableEvaluation ? "pointer" : "not-allowed" }} onClick={() => headTopData.handleChangeModeType("evaluating")} src={state.enableEvaluation ? icons.icon_3 : icons.icon_4} />
           </div>
-          {data.showPC && data.showTip && !query.isCbs ? <TeacherBootom></TeacherBootom> : null}
-          {data.showStudent && data.showTip && !query.isCbs ? <StudentBottom></StudentBottom> : null}
+          {/* {data.showPC && data.showTip && !query.isCbs ? <TeacherBootom></TeacherBootom> : null}
+          {data.showStudent && data.showTip && !query.isCbs ? <StudentBottom></StudentBottom> : null} */}
           {data.showVip && <TheVip />}
         </div>
         {headTopData.modeType && headTopData.modeType !== "init" && state.modeType == "practise" && state.fingeringInfo?.name && state.setting.displayFingering && <GuideIndex list={["detail"]} />}

BIN
src/page-instrument/custom-plugins/guide-driver/images/btn-close.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/btn-finsh.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/btn-next.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/evaluating/e1.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/evaluating/e2.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/evaluating/e3.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/evaluating/r1.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/evaluating/r2.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/evaluating/r3.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/evaluating/r4.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/follow/f1.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/follow/f2.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/follow/f3.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/practise/d1.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/practise/d2.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/practise/d3.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/practise/d4.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/practise/d5.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/practise/d6.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/practise/d7-1.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/practise/d7.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/practise/d8.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/practise/d9.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/report/r1.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/report/r2.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/report/r3.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/report/r4.png


BIN
src/page-instrument/custom-plugins/guide-driver/images/report/r5.png


+ 420 - 0
src/page-instrument/custom-plugins/guide-driver/index.less

@@ -0,0 +1,420 @@
+.driver-overlay,
+.driver-popover {
+  pointer-events: auto !important;
+}
+
+.popoverClass .driver-popover-next-btn {
+  width: 100px;
+  height: 34px;
+  text-shadow: none;
+  border: none;
+  font-weight: 600;
+  font-size: 13px;
+  color: #006ed1;
+  text-align: center;
+  position: absolute;
+  background: url("./images/btn-next.png") no-repeat center transparent;
+  background-size: contain;
+  background-color: transparent !important;
+}
+
+.popoverClass .driver-popover-next-btn:hover,
+.popoverClass .driver-popover-prev-btn:hover,
+.popoverClass .driver-popover-prev-btn:focus {
+  background-color: transparent;
+}
+
+.driver-popover-arrow {
+  display: none;
+}
+
+.driver-popover-close-btn,
+.driver-popover-close-btn-custom {
+  position: fixed;
+  left: 20px;
+  top: 20px;
+  width: 48px;
+  height: 24px;
+  z-index: 99999;
+  background: url('./images/btn-close.png') no-repeat center;
+  background-size: contain;
+  color: transparent;
+
+  &:hover,
+  &:focus {
+    color: transparent;
+  }
+}
+
+.driver-popover-close-btn-custom {
+  pointer-events: auto !important;
+}
+
+.popoverClass {
+  box-shadow: none;
+  padding: 0;
+}
+
+.popoverClass1 {
+  width: 257px;
+  height: 247px;
+  background: url("./images/practise/d1.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    bottom: 17px;
+    left: 16px;
+  }
+}
+
+.popoverClass2 {
+  width: 264px;
+  height: 228px;
+  background: url("./images/practise/d2.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    bottom: 26px;
+    right: 24px;
+  }
+}
+
+.popoverClass3 {
+  width: 264px;
+  height: 245px;
+  background: url("./images/practise/d3.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    right: 24px;
+    bottom: 23px;
+  }
+}
+
+.popoverClass4 {
+  width: 265px;
+  height: 245px;
+  background: url("./images/practise/d4.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    right: 25px;
+    bottom: 23px;
+  }
+}
+
+.popoverClass5 {
+  width: 264px;
+  height: 245px;
+  background: url("./images/practise/d5.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    right: 24px;
+    bottom: 23px;
+  }
+}
+
+.popoverClass6 {
+  width: 264px;
+  height: 245px;
+  background: url("./images/practise/d6.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    right: 24px;
+    bottom: 23px;
+  }
+
+  &.popoverClose {
+
+    .driver-popover-navigation-btns {
+      position: absolute;
+      bottom: 23px;
+      left: 0;
+      right: 15px;
+      justify-content: flex-start;
+    }
+
+    .driver-popover-next-btn {
+      // right: 14px;
+      // bottom: 18px;
+      position: relative;
+      top: 0;
+      right: 0;
+    }
+
+    .driver-popover-prev-btn {
+      margin-left: 14px;
+    }
+  }
+}
+
+.popoverClass7 {
+  width: 267px;
+  height: 221px;
+  background: url("./images/practise/d7.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    right: 14px;
+    bottom: 18px;
+  }
+}
+
+.popoverClass7-1 {
+  width: 267px;
+  height: 221px;
+  background: url("./images/practise/d7-1.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    right: 14px;
+    bottom: 18px;
+  }
+}
+
+.popoverClass8 {
+  width: 277px;
+  height: 152px;
+  background: url("./images/practise/d8.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    right: 24px;
+    bottom: -48px;
+  }
+}
+
+.popoverClass9 {
+  width: 270px;
+  height: 192px;
+  background: url("./images/practise/d9.png") no-repeat center;
+  background-size: contain;
+}
+
+.popoverClose {
+  .driver-popover-navigation-btns {
+    position: absolute;
+    bottom: -48px;
+    left: 0;
+    right: 0;
+    align-items: center;
+    justify-content: center;
+    flex-direction: row-reverse;
+  }
+
+  .driver-popover-next-btn {
+    position: relative;
+    // right: 14px;
+    // bottom: 18px;
+    width: 82px;
+    height: 33px;
+    background: url('./images/btn-finsh.png') no-repeat center;
+    background-size: contain;
+  }
+
+  .driver-popover-prev-btn {
+    border: 1px solid #fff;
+    border-radius: 100px;
+    color: #fff;
+    background-color: transparent;
+    font-weight: 400;
+    text-shadow: none;
+    width: 82px;
+    height: 33px;
+    text-align: center;
+    margin-left: 14px;
+  }
+}
+
+
+.popoverClassF1 {
+  width: 257px;
+  height: 247px;
+  background: url("./images/follow/f1.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    bottom: 17px;
+    left: 16px;
+  }
+}
+
+.popoverClassE1 {
+  width: 257px;
+  height: 247px;
+  background: url("./images/evaluating/e1.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    bottom: 17px;
+    left: 16px;
+  }
+}
+
+.popoverClassER1 {
+  width: 257px;
+  height: 178px;
+  background: url("./images/evaluating/r1.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    bottom: -27px;
+    right: 16px;
+  }
+}
+
+.popoverClassER2 {
+  width: 261px;
+  height: 226px;
+  background: url("./images/evaluating/r2.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    bottom: 14px;
+    right: 15px;
+  }
+}
+
+.popoverClassER3 {
+  width: 261px;
+  height: 249px;
+  background: url("./images/evaluating/r3.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    bottom: 15px;
+    left: 17px;
+  }
+}
+
+.popoverClassER4 {
+  width: 327px;
+  height: 246px;
+  background: url("./images/evaluating/r4.png") no-repeat center;
+  background-size: contain;
+
+  &.popoverClose {
+
+    .driver-popover-navigation-btns {
+      position: absolute;
+      bottom: 15px;
+      left: 17px;
+      justify-content: flex-end;
+    }
+
+    .driver-popover-next-btn {
+      position: relative;
+      top: 0;
+      right: 0;
+    }
+
+    .driver-popover-prev-btn {
+      margin-left: 14px;
+    }
+  }
+}
+
+.popoverClassReport1 {
+  width: 270px;
+  height: 143px;
+  background: url("./images/report/r1.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    bottom: -51px;
+    right: 17px;
+  }
+}
+
+.popoverClassReport2 {
+  width: 270px;
+  height: 143px;
+  background: url("./images/report/r2.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    bottom: -51px;
+    right: 17px;
+  }
+
+  &.popoverClose {
+    .driver-popover-navigation-btns {
+      position: absolute;
+      bottom: -51px;
+      left: 0;
+    }
+
+    .driver-popover-next-btn {
+      position: relative;
+      top: 0;
+      bottom: 0;
+      right: 0;
+    }
+
+    .driver-popover-prev-btn {
+      margin-left: 14px;
+    }
+  }
+}
+
+.popoverClassReport3 {
+  width: 270px;
+  height: 143px;
+  background: url("./images/report/r3.png") no-repeat center;
+  background-size: contain;
+
+  .driver-popover-next-btn {
+    bottom: -51px;
+    right: 17px;
+  }
+
+  &.popoverClose {
+    .driver-popover-navigation-btns {
+      position: absolute;
+      bottom: -51px;
+      left: 0;
+    }
+
+    .driver-popover-next-btn {
+      position: relative;
+      top: 0;
+      bottom: 0;
+      right: 0;
+    }
+
+    .driver-popover-prev-btn {
+      margin-left: 14px;
+    }
+  }
+}
+
+.popoverClassReport4 {
+  width: 270px;
+  height: 143px;
+  background: url("./images/report/r5.png") no-repeat center;
+  background-size: contain;
+
+  // .driver-popover-next-btn {
+  //   bottom: -51px;
+  //   right: 17px;
+  // }
+  &.popoverClose {
+    .driver-popover-navigation-btns {
+      position: absolute;
+      bottom: -51px;
+      left: 0;
+    }
+
+    .driver-popover-next-btn {
+      position: relative;
+      top: 0;
+      right: 0;
+    }
+
+    .driver-popover-prev-btn {
+      margin-left: 14px;
+    }
+  }
+}

+ 0 - 0
src/page-instrument/custom-plugins/guide-driver/index.module.less


+ 1112 - 0
src/page-instrument/custom-plugins/guide-driver/index.tsx

@@ -0,0 +1,1112 @@
+import { Teleport, defineComponent, nextTick, onMounted, ref } from "vue";
+import { Config, DriveStep, PopoverDOM, State, driver } from "driver.js";
+import "driver.js/dist/driver.css";
+import state from "/src/state";
+import { getGuidance, setGuidance } from "../guide-page/api";
+
+const endGuide = (guideInfo: any) => {
+  try {
+    setGuidance({ guideTag: "guideInfo", guideValue: JSON.stringify(guideInfo) });
+  } catch (e) {
+    console.log(e);
+  }
+};
+
+/** 练习模式 */
+export const PractiseDriver = defineComponent({
+  name: "PractiseDriver",
+  setup() {
+    // 初始化部分引导位置
+    const driverInitialPosition = (popover: PopoverDOM, options: { config: Config; state: State }) => {
+      options.config.stageRadius = 5;
+      options.config.stagePadding = 8;
+      try {
+        const rect = options.state.activeElement?.getBoundingClientRect();
+        popover.wrapper.style.marginLeft = (rect?.width || 0) / 2 + 4 + "px";
+      } catch {}
+    };
+
+    const driverOptions = (): Config => {
+      let length = state.setting.displayFingering ? 9 : 8;
+
+      // 乐器方向不一样引导位置不一样
+      const instrumentDirection: DriveStep = {
+        element: ".driver-7",
+        popover: {
+          title: "",
+          description: "",
+          popoverClass: `popoverClass ${state.fingeringInfo.direction === "transverse" ? "popoverClass7" : "popoverClass7-1"}`,
+          align: state.fingeringInfo.direction === "transverse" ? "start" : "center",
+          side: state.fingeringInfo.direction === "transverse" ? "top" : "left",
+          nextBtnText: "下一步7/" + length,
+          showButtons: ["next"],
+          onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+            if (state.fingeringInfo.direction === "transverse") driverInitialPosition(popover, options);
+          },
+          onCloseClick: () => {
+            onDriverClose();
+          },
+        },
+      };
+      let options: Config = {
+        showProgress: false,
+        allowClose: false,
+        popoverOffset: 3,
+        disableActiveInteraction: true,
+        onCloseClick: () => {
+          onDriverClose();
+        },
+        steps: [
+          {
+            element: ".driver-1",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClass1",
+              align: "end",
+              side: "top",
+              nextBtnText: "下一步1/" + length,
+              showButtons: ["next"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                options.config.stageRadius = 1000;
+                options.config.stagePadding = 0;
+              },
+            },
+          },
+          {
+            element: ".driver-2",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClass2",
+              align: "start",
+              side: "top",
+              nextBtnText: "下一步2/" + length,
+              showButtons: ["next"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                driverInitialPosition(popover, options);
+              },
+            },
+          },
+          {
+            element: ".driver-3",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClass3",
+              align: "start",
+              side: "top",
+              nextBtnText: "下一步3/" + length,
+              showButtons: ["next"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                driverInitialPosition(popover, options);
+              },
+            },
+          },
+          {
+            element: ".driver-4",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClass4",
+              align: "start",
+              side: "top",
+              nextBtnText: "下一步4/" + length,
+              showButtons: ["next"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                driverInitialPosition(popover, options);
+              },
+            },
+          },
+          {
+            element: ".driver-5",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClass5",
+              align: "start",
+              side: "top",
+              nextBtnText: "下一步5/" + length,
+              showButtons: ["next"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                driverInitialPosition(popover, options);
+              },
+            },
+          },
+          {
+            element: ".driver-6",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClass6",
+              align: "start",
+              side: "top",
+              nextBtnText: "下一步6/" + length,
+              showButtons: ["next"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                driverInitialPosition(popover, options);
+              },
+            },
+          },
+        ] as DriveStep[],
+      };
+      // 是否有指法图
+      if (state.setting.displayFingering) {
+        options.steps?.push(
+          instrumentDirection,
+          {
+            element: ".driver-8",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClass8",
+              align: "start",
+              side: "bottom",
+              nextBtnText: "下一步8/" + length,
+              showButtons: ["next"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                options.config.stageRadius = 1000;
+                options.config.stagePadding = 0;
+                try {
+                  const rect = options.state.activeElement?.getBoundingClientRect();
+                  popover.wrapper.style.marginLeft = (rect?.width || 0) / 2 - 4 + "px";
+                } catch {}
+              },
+            },
+          },
+          {
+            element: ".driver/" + length,
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClass9 popoverClose",
+              align: "end",
+              side: "bottom",
+              prevBtnText: "再看一遍",
+              doneBtnText: "完成",
+              showButtons: ["next", "previous"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                options.config.stageRadius = 1000;
+                options.config.stagePadding = 0;
+                try {
+                  const rect = options.state.activeElement?.getBoundingClientRect();
+                  popover.wrapper.style.marginLeft = -((rect?.width || 0) / 2 - 8) + "px";
+                } catch {}
+              },
+              onPrevClick: () => {
+                driverObj.drive(0);
+              },
+              onNextClick: () => {
+                onDriverClose();
+              },
+            },
+          }
+        );
+      } else {
+        options.steps?.push(
+          {
+            element: ".driver-8",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClass8",
+              align: "start",
+              side: "bottom",
+              nextBtnText: "下一步8/9",
+              showButtons: ["next"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                options.config.stageRadius = 1000;
+                options.config.stagePadding = 0;
+                try {
+                  const rect = options.state.activeElement?.getBoundingClientRect();
+                  popover.wrapper.style.marginLeft = (rect?.width || 0) / 2 - 4 + "px";
+                } catch {}
+              },
+            },
+          },
+          {
+            element: ".driver-9",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClass9 popoverClose",
+              align: "end",
+              side: "bottom",
+              prevBtnText: "再看一遍",
+              doneBtnText: "完成",
+              showButtons: ["next", "previous"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                options.config.stageRadius = 1000;
+                options.config.stagePadding = 0;
+                try {
+                  const rect = options.state.activeElement?.getBoundingClientRect();
+                  popover.wrapper.style.marginLeft = -((rect?.width || 0) / 2 - 8) + "px";
+                } catch {}
+              },
+              onPrevClick: () => {
+                driverObj.drive(0);
+              },
+              onNextClick: () => {
+                onDriverClose();
+              },
+            },
+          }
+        );
+      }
+      return options;
+    };
+
+    let driverObj: any;
+
+    const handleClickOutside = (event: any) => {
+      if (driverObj.isActive() && (event.target.nodeName === "path" || event.target.classList.contains("driver-popover") || event.target.classList.contains("driver-overlay"))) {
+        if (driverObj.isLastStep()) {
+          onDriverClose();
+        } else {
+          driverObj.moveNext(); // 跳转到下一步
+        }
+      }
+    };
+
+    const guideInfo = ref({} as any);
+    const showCloseBtn = ref(false);
+    const getAllGuidance = async () => {
+      try {
+        if (state.guideInfo) {
+          guideInfo.value = state.guideInfo;
+        } else {
+          const res = await getGuidance({ guideTag: "guideInfo" });
+          if (res.data) {
+            guideInfo.value = JSON.parse(res.data?.guideValue) || null;
+          } else {
+            guideInfo.value = {};
+          }
+        }
+
+        if (!(guideInfo.value && guideInfo.value.practiseDriver)) {
+          document.addEventListener("click", handleClickOutside, true);
+          driverObj = driver(driverOptions());
+          nextTick(() => {
+            driverObj.drive(0);
+            showCloseBtn.value = true;
+          });
+        }
+      } catch (e) {
+        console.log(e);
+      }
+    };
+
+    getAllGuidance();
+
+    // 结束关闭弹窗
+    const onDriverClose = () => {
+      if (!guideInfo.value) {
+        guideInfo.value = { practiseDriver: true };
+      } else {
+        guideInfo.value.practiseDriver = true;
+      }
+      endGuide(guideInfo.value);
+      driverObj.destroy();
+      document.querySelector(".driver-popover-close-btn-custom")?.remove();
+      document.removeEventListener("click", handleClickOutside);
+    };
+
+    return () => (
+      <Teleport to="body">
+        {showCloseBtn.value && (
+          <div
+            class="driver-popover-close-btn-custom"
+            onClick={(e: any) => {
+              onDriverClose();
+            }}
+          ></div>
+        )}
+      </Teleport>
+    );
+  },
+});
+
+/** 跟练模式 */
+export const FollowDriver = defineComponent({
+  name: "FollowDriver",
+  setup() {
+    // 初始化部分引导位置
+    const driverInitialPosition = (popover: PopoverDOM, options: { config: Config; state: State }) => {
+      options.config.stageRadius = 5;
+      options.config.stagePadding = 8;
+      try {
+        const rect = options.state.activeElement?.getBoundingClientRect();
+        popover.wrapper.style.marginLeft = (rect?.width || 0) / 2 + 4 + "px";
+      } catch {}
+    };
+
+    const driverOptions: Config = {
+      showProgress: false,
+      allowClose: false,
+      popoverOffset: 3,
+      disableActiveInteraction: true,
+      steps: [
+        {
+          element: ".follow-1",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClassF1",
+            align: "end",
+            side: "top",
+            nextBtnText: "下一步1/3",
+            showButtons: ["next"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              options.config.stageRadius = 1000;
+              options.config.stagePadding = 0;
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+        {
+          element: ".driver-5",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClass5",
+            align: "start",
+            side: "top",
+            nextBtnText: "下一步2/3",
+            showButtons: ["next"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              driverInitialPosition(popover, options);
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+        {
+          element: ".driver-6",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClass6 popoverClose",
+            align: "start",
+            side: "top",
+            prevBtnText: "再看一遍",
+            doneBtnText: "完成",
+            showButtons: ["next", "previous"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              driverInitialPosition(popover, options);
+            },
+            onPrevClick: () => {
+              driverObj.drive(0);
+            },
+            onNextClick: () => {
+              onDriverClose();
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+      ],
+    };
+
+    let driverObj: any;
+
+    const handleClickOutside = (event: any) => {
+      if (driverObj.isActive() && (event.target.nodeName === "path" || event.target.classList.contains("driver-popover") || event.target.classList.contains("driver-overlay"))) {
+        if (driverObj.isLastStep()) {
+          onDriverClose();
+        } else {
+          driverObj.moveNext(); // 跳转到下一步
+        }
+      }
+    };
+    const guideInfo = ref({} as any);
+    const showCloseBtn = ref(false);
+    const getAllGuidance = async () => {
+      try {
+        if (state.guideInfo) {
+          guideInfo.value = state.guideInfo;
+        } else {
+          const res = await getGuidance({ guideTag: "guideInfo" });
+          if (res.data) {
+            guideInfo.value = JSON.parse(res.data?.guideValue) || null;
+          } else {
+            guideInfo.value = {};
+          }
+        }
+        if (!(guideInfo.value && guideInfo.value.followDriver)) {
+          document.addEventListener("click", handleClickOutside, true);
+          driverObj = driver(driverOptions);
+          nextTick(() => {
+            driverObj.drive(0);
+            showCloseBtn.value = true;
+          });
+        }
+      } catch (e) {
+        console.log(e);
+      }
+    };
+
+    getAllGuidance();
+
+    // 结束关闭弹窗
+    const onDriverClose = () => {
+      if (!guideInfo.value) {
+        guideInfo.value = { followDriver: true };
+      } else {
+        guideInfo.value.followDriver = true;
+      }
+      endGuide(guideInfo.value);
+      driverObj.destroy();
+      document.querySelector(".driver-popover-close-btn-custom")?.remove();
+      document.removeEventListener("click", handleClickOutside);
+    };
+
+    return () => (
+      <Teleport to="body">
+        {showCloseBtn.value && (
+          <div
+            class="driver-popover-close-btn-custom"
+            onClick={(e: any) => {
+              onDriverClose();
+            }}
+          ></div>
+        )}
+      </Teleport>
+    );
+  },
+});
+
+// 评测模式
+export const EvaluatingDriver = defineComponent({
+  name: "EvaluatingDriver",
+  setup() {
+    // 初始化部分引导位置
+    const driverInitialPosition = (popover: PopoverDOM, options: { config: Config; state: State }) => {
+      options.config.stageRadius = 5;
+      options.config.stagePadding = 8;
+      try {
+        const rect = options.state.activeElement?.getBoundingClientRect();
+        popover.wrapper.style.marginLeft = (rect?.width || 0) / 2 + 4 + "px";
+      } catch {}
+    };
+
+    const driverOptions: Config = {
+      showProgress: false,
+      allowClose: false,
+      popoverOffset: 3,
+      disableActiveInteraction: true,
+      steps: [
+        {
+          element: ".evaluting-1",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClassE1",
+            align: "end",
+            side: "top",
+            nextBtnText: "下一步1/3",
+            showButtons: ["next"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              options.config.stageRadius = 1000;
+              options.config.stagePadding = 0;
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+        {
+          element: ".driver-5",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClass5",
+            align: "start",
+            side: "top",
+            nextBtnText: "下一步2/3",
+            showButtons: ["next"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              driverInitialPosition(popover, options);
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+        {
+          element: ".driver-6",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClass6 popoverClose",
+            align: "start",
+            side: "top",
+            prevBtnText: "再看一遍",
+            doneBtnText: "完成",
+            showButtons: ["next", "previous"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              driverInitialPosition(popover, options);
+            },
+            onPrevClick: () => {
+              driverObj.drive(0);
+            },
+            onNextClick: () => {
+              onDriverClose();
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+      ],
+    };
+    let driverObj: any;
+
+    const handleClickOutside = (event: any) => {
+      if (driverObj.isActive() && (event.target.nodeName === "path" || event.target.classList.contains("driver-popover") || event.target.classList.contains("driver-overlay"))) {
+        if (driverObj.isLastStep()) {
+          onDriverClose();
+        } else {
+          driverObj.moveNext(); // 跳转到下一步
+        }
+      }
+    };
+
+    const guideInfo = ref({} as any);
+    const showCloseBtn = ref(false);
+    const getAllGuidance = async () => {
+      try {
+        if (state.guideInfo) {
+          guideInfo.value = state.guideInfo;
+        } else {
+          const res = await getGuidance({ guideTag: "guideInfo" });
+          if (res.data) {
+            guideInfo.value = JSON.parse(res.data?.guideValue) || null;
+          } else {
+            guideInfo.value = {};
+          }
+        }
+        if (!(guideInfo.value && guideInfo.value.evaluatingDriver)) {
+          document.addEventListener("click", handleClickOutside, true);
+          driverObj = driver(driverOptions);
+          nextTick(() => {
+            driverObj.drive(0);
+            showCloseBtn.value = true;
+          });
+        } else {
+          driverObj.destroy();
+        }
+      } catch (e) {
+        console.log(e);
+      }
+    };
+
+    getAllGuidance();
+
+    // 结束关闭弹窗
+    const onDriverClose = () => {
+      if (!guideInfo.value) {
+        guideInfo.value = { evaluatingDriver: true };
+      } else {
+        guideInfo.value.evaluatingDriver = true;
+      }
+      endGuide(guideInfo.value);
+      driverObj?.destroy();
+      document.removeEventListener("click", handleClickOutside);
+    };
+
+    return () => (
+      <Teleport to="body">
+        {showCloseBtn.value && (
+          <div
+            class="driver-popover-close-btn-custom"
+            onClick={(e: any) => {
+              onDriverClose();
+            }}
+          ></div>
+        )}
+      </Teleport>
+    );
+  },
+});
+
+// 评测模式 - 结果弹窗
+export const EvaluatingResultDriver = defineComponent({
+  name: "EvaluatingResultDriver",
+  setup() {
+    // 初始化部分引导位置
+    const driverInitialPosition = (popover: PopoverDOM, options: { config: Config; state: State }, position = 1) => {
+      options.config.stageRadius = 1000;
+      options.config.stagePadding = 0;
+      try {
+        const rect = options.state.activeElement?.getBoundingClientRect();
+        popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * position + 4 + "px";
+      } catch {}
+    };
+
+    const driverOptions: Config = {
+      showProgress: false,
+      allowClose: false,
+      popoverOffset: 3,
+      disableActiveInteraction: true,
+      steps: [
+        {
+          element: ".evaluting-result-1",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClassER1",
+            align: "start",
+            side: "right",
+            nextBtnText: "下一步1/4",
+            showButtons: ["next"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              options.config.stageRadius = 12;
+              options.config.stagePadding = 10;
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+        {
+          element: ".evaluting-result-2",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClassER2",
+            align: "start",
+            side: "top",
+            nextBtnText: "下一步2/4",
+            showButtons: ["next"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              options.config.stageRadius = 1000;
+              options.config.stagePadding = 0;
+              try {
+                const rect = options.state.activeElement?.getBoundingClientRect();
+                popover.wrapper.style.marginLeft = (rect?.width || 0) / 2 - 4 + "px";
+              } catch {}
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+        {
+          element: ".evaluting-result-3",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClassER3",
+            align: "end",
+            side: "top",
+            nextBtnText: "下一步3/4",
+            showButtons: ["next"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              driverInitialPosition(popover, options, -1);
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+        {
+          element: ".evaluting-result-4",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClassER4 popoverClose",
+            align: "end",
+            side: "top",
+            prevBtnText: "再看一遍",
+            doneBtnText: "完成",
+            showButtons: ["next", "previous"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              driverInitialPosition(popover, options, -1);
+            },
+            onPrevClick: () => {
+              driverObj.drive(0);
+            },
+            onNextClick: () => {
+              onDriverClose();
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+      ],
+    };
+    let driverObj: any;
+
+    const handleClickOutside = (event: any) => {
+      if (driverObj.isActive() && (event.target.nodeName === "path" || event.target.classList.contains("driver-popover") || event.target.classList.contains("driver-overlay"))) {
+        if (driverObj.isLastStep()) {
+          onDriverClose();
+        } else {
+          driverObj.moveNext(); // 跳转到下一步
+        }
+      }
+    };
+    const guideInfo = ref({} as any);
+    const showCloseBtn = ref(false);
+    const getAllGuidance = async () => {
+      try {
+        if (state.guideInfo) {
+          guideInfo.value = state.guideInfo;
+        } else {
+          const res = await getGuidance({ guideTag: "guideInfo" });
+          if (res.data) {
+            guideInfo.value = JSON.parse(res.data?.guideValue) || null;
+          } else {
+            guideInfo.value = {};
+          }
+        }
+        if (!(guideInfo.value && guideInfo.value.evaluatingResultDriver)) {
+          document.addEventListener("click", handleClickOutside, true);
+          driverObj = driver(driverOptions);
+          nextTick(() => {
+            driverObj.drive(0);
+            showCloseBtn.value = true;
+          });
+        }
+      } catch (e) {
+        console.log(e);
+      }
+    };
+
+    getAllGuidance();
+
+    // 结束关闭弹窗
+    const onDriverClose = () => {
+      if (!guideInfo.value) {
+        guideInfo.value = { evaluatingResultDriver: true };
+      } else {
+        guideInfo.value.evaluatingResultDriver = true;
+      }
+      endGuide(guideInfo.value);
+      driverObj.destroy();
+      document.querySelector(".driver-popover-close-btn-custom")?.remove();
+      document.removeEventListener("click", handleClickOutside);
+    };
+
+    return () => (
+      <Teleport to="body">
+        {showCloseBtn.value && (
+          <div
+            class="driver-popover-close-btn-custom"
+            onClick={(e: any) => {
+              onDriverClose();
+            }}
+          ></div>
+        )}
+      </Teleport>
+    );
+  },
+});
+
+// 评测报告
+export const EvaluatingReportDriver = defineComponent({
+  name: "EvaluatingReportDriver",
+  props: {
+    /** 视屏地址 */
+    videoFilePath: {
+      type: String,
+      default: "",
+    },
+  },
+  setup(props) {
+    // state.isPercussion 是否为打击乐
+    // 初始化部分引导位置
+    const driverInitialPosition = (popover: PopoverDOM, options: { config: Config; state: State }, position = 1) => {
+      options.config.stageRadius = 12;
+      options.config.stagePadding = 0;
+      try {
+        const rect = options.state.activeElement?.getBoundingClientRect();
+        popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * position - 4 + "px";
+      } catch {}
+    };
+
+    // 判断是否为打击乐
+    let steps: DriveStep[] = [];
+    if (state.isPercussion) {
+      if (props.videoFilePath) {
+        steps = [
+          {
+            element: ".evaluting-report-2",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClassReport2",
+              align: "start",
+              side: "bottom",
+              nextBtnText: "下一步1/2",
+              showButtons: ["next"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                driverInitialPosition(popover, options);
+              },
+              onCloseClick: () => {
+                onDriverClose();
+              },
+            },
+          },
+          {
+            element: ".evaluting-report-4",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClassReport4 popoverClose",
+              align: "end",
+              side: "bottom",
+              prevBtnText: "再看一遍",
+              doneBtnText: "完成",
+              showButtons: ["next", "previous"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                options.config.stageRadius = 8;
+                options.config.stagePadding = 5;
+                try {
+                  const rect = options.state.activeElement?.getBoundingClientRect();
+                  popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 4 + "px";
+                } catch {}
+              },
+              onPrevClick: () => {
+                driverObj.drive(0);
+              },
+              onNextClick: () => {
+                onDriverClose();
+              },
+              onCloseClick: () => {
+                onDriverClose();
+              },
+            },
+          },
+        ];
+      } else {
+        steps = [
+          {
+            element: ".evaluting-report-2",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClassReport2 popoverClose",
+              align: "start",
+              side: "bottom",
+              doneBtnText: "完成",
+              showButtons: ["next"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                driverInitialPosition(popover, options);
+              },
+              onPrevClick: () => {
+                driverObj.drive(0);
+              },
+              onNextClick: () => {
+                onDriverClose();
+              },
+              onCloseClick: () => {
+                onDriverClose();
+              },
+            },
+          },
+        ];
+      }
+    } else {
+      const count = props.videoFilePath ? 4 : 3;
+      steps = [
+        {
+          element: ".evaluting-report-1",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClassReport1",
+            align: "start",
+            side: "bottom",
+            nextBtnText: "下一步1/" + count,
+            showButtons: ["next"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              driverInitialPosition(popover, options);
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+        {
+          element: ".evaluting-report-2",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClassReport2",
+            align: "start",
+            side: "bottom",
+            nextBtnText: "下一步2/" + count,
+            showButtons: ["next"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              driverInitialPosition(popover, options);
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        },
+      ];
+      if (props.videoFilePath) {
+        steps.push(
+          {
+            element: ".evaluting-report-3",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClassReport3",
+              align: "start",
+              side: "bottom",
+              nextBtnText: "下一步3/4",
+              showButtons: ["next"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                driverInitialPosition(popover, options);
+              },
+              onCloseClick: () => {
+                onDriverClose();
+              },
+            },
+          },
+          {
+            element: ".evaluting-report-4",
+            popover: {
+              title: "",
+              description: "",
+              popoverClass: "popoverClass popoverClassReport4 popoverClose",
+              align: "end",
+              side: "bottom",
+              prevBtnText: "再看一遍",
+              doneBtnText: "完成",
+              showButtons: ["next", "previous"],
+              onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+                options.config.stageRadius = 8;
+                options.config.stagePadding = 5;
+                try {
+                  const rect = options.state.activeElement?.getBoundingClientRect();
+                  popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 4 + "px";
+                } catch {}
+              },
+              onPrevClick: () => {
+                driverObj.drive(0);
+              },
+              onNextClick: () => {
+                onDriverClose();
+              },
+              onCloseClick: () => {
+                onDriverClose();
+              },
+            },
+          }
+        );
+      } else {
+        steps.push({
+          element: ".evaluting-report-3",
+          popover: {
+            title: "",
+            description: "",
+            popoverClass: "popoverClass popoverClassReport3 popoverClose",
+            align: "start",
+            side: "bottom",
+            prevBtnText: "再看一遍",
+            doneBtnText: "完成",
+            showButtons: ["next", "previous"],
+            onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
+              driverInitialPosition(popover, options);
+            },
+            onPrevClick: () => {
+              driverObj.drive(0);
+            },
+            onNextClick: () => {
+              onDriverClose();
+            },
+            onCloseClick: () => {
+              onDriverClose();
+            },
+          },
+        });
+      }
+    }
+
+    const driverOptions: Config = {
+      showProgress: false,
+      allowClose: false,
+      popoverOffset: 3,
+      disableActiveInteraction: true,
+      steps: steps,
+    };
+
+    let driverObj: any;
+
+    const guideInfo = ref({} as any);
+
+    const handleClickOutside = (event: any) => {
+      if (driverObj.isActive() && (event.target.nodeName === "path" || event.target.classList.contains("driver-popover") || event.target.classList.contains("driver-overlay"))) {
+        if (driverObj.isLastStep()) {
+          onDriverClose();
+        } else {
+          driverObj.moveNext(); // 跳转到下一步
+        }
+      }
+    };
+    const showCloseBtn = ref(false);
+    const getAllGuidance = async () => {
+      try {
+        if (state.guideInfo) {
+          guideInfo.value = state.guideInfo;
+        } else {
+          const res = await getGuidance({ guideTag: "guideInfo" });
+          if (res.data) {
+            guideInfo.value = JSON.parse(res.data?.guideValue) || null;
+          } else {
+            guideInfo.value = {};
+          }
+        }
+        if (!(guideInfo.value && guideInfo.value.evaluatingReportDriver)) {
+          // 监听点击事件以实现点击空白区域跳转到下一步
+          document.addEventListener("click", handleClickOutside, true);
+          driverObj = driver(driverOptions);
+          nextTick(() => {
+            driverObj.drive();
+
+            showCloseBtn.value = true;
+          });
+        }
+      } catch (e) {
+        console.log(e);
+      }
+    };
+
+    getAllGuidance();
+
+    // 结束关闭弹窗
+    const onDriverClose = () => {
+      if (!guideInfo.value) {
+        guideInfo.value = { evaluatingReportDriver: true };
+      } else {
+        guideInfo.value.evaluatingReportDriver = true;
+      }
+      endGuide(guideInfo.value);
+      driverObj.destroy();
+      document.querySelector(".driver-popover-close-btn-custom")?.remove();
+      document.removeEventListener("click", handleClickOutside);
+    };
+
+    return () => (
+      <Teleport to="body">
+        {showCloseBtn.value && (
+          <div
+            class="driver-popover-close-btn-custom"
+            onClick={(e: any) => {
+              onDriverClose();
+            }}
+          ></div>
+        )}
+      </Teleport>
+    );
+  },
+});

+ 37 - 16
src/page-instrument/evaluat-model/evaluat-result/index.module.less

@@ -66,9 +66,11 @@
     flex-direction: column;
     justify-content: space-between;
     margin-top: 59px;
-    &.fractionPercussion{
+
+    &.fractionPercussion {
         height: 195px;
     }
+
     .bg {
         position: absolute;
         left: 0;
@@ -93,6 +95,11 @@
             margin-top: -11px;
         }
 
+        .scoreSection {
+            display: flex;
+            align-items: flex-end;
+        }
+
         .text {
             margin-left: 32px;
             position: relative;
@@ -107,10 +114,12 @@
             z-index: 1;
             line-height: 1;
             white-space: nowrap;
+
             &.badgeText {
                 margin-left: 6px;
             }
-            .level{
+
+            .level {
                 margin-left: 4px;
                 padding: 0 6px;
                 display: flex;
@@ -122,7 +131,8 @@
                 font-weight: 400;
                 font-size: 12px;
                 color: #8A541E;
-                & > span{
+
+                &>span {
                     margin: -2px 4px 0;
                 }
             }
@@ -151,35 +161,40 @@
     padding: 0 30px;
     font-weight: 400;
     font-size: 14px;
-    color: rgba(0,0,0,0.5);
+    color: rgba(0, 0, 0, 0.5);
     line-height: 20px;
 }
 
 .ctrls {
     display: flex;
-    justify-content:space-between;
+    justify-content: space-between;
     align-items: center;
     margin: 0 27px 14px;
+
     .ctrlsBtn {
         width: 107px;
         height: 39px;
     }
 }
-:global{
+
+:global {
     .savePopoverClose {
-        &.van-popover{
-            --van-popover-dark-background:rgba(0,0,0,0.7);
+        &.van-popover {
+            --van-popover-dark-background: rgba(0, 0, 0, 0.7);
         }
+
         .popoverClose {
             display: flex;
             align-items: center;
             padding: 9px 12px;
-            >div{
+
+            >div {
                 font-weight: 500;
                 font-size: 14px;
                 color: #FFFFFF;
                 line-height: 20px;
             }
+
             >img {
                 margin-left: 16px;
                 width: 12px;
@@ -188,6 +203,7 @@
         }
     }
 }
+
 .detail {
     display: flex;
     align-items: center;
@@ -195,7 +211,7 @@
     box-shadow: 0px 1px 5px 0px #EFE3C2;
     border-radius: 14px;
     margin: 0 20px;
-    padding: 12px 0;    
+    padding: 12px 0;
     padding: 10px 0;
 }
 
@@ -205,26 +221,31 @@
     flex-direction: column;
     justify-content: center;
     align-items: center;
-    &:nth-child(2){
+
+    &:nth-child(2) {
         border-left: 1px solid #F2F2F2;
         border-right: 1px solid #F2F2F2;
     }
-    &>div:nth-child(1){
+
+    &>div:nth-child(1) {
         display: flex;
         align-items: center;
-        >span{
+
+        >span {
             font-weight: 500;
             font-size: 14px;
             color: #1A1A1A;
             line-height: 20px;
             margin-left: 4px;
         }
-        >img{
+
+        >img {
             width: 16px;
             height: 16px;
         }
     }
-    &>div{
+
+    &>div {
         margin-top: 6px;
         font-weight: 500;
         font-size: 18px;
@@ -256,7 +277,7 @@
     }
 }
 
-.disablued{
+.disablued {
     pointer-events: none;
     opacity: .5;
 }

+ 161 - 176
src/page-instrument/evaluat-model/evaluat-result/index.tsx

@@ -1,17 +1,17 @@
 import { defineComponent, onMounted, reactive, watch } from "vue";
-import { Popover } from "vant"
+import { Popover } from "vant";
 import styles from "./index.module.less";
 import state from "/src/state";
 import icon1 from "../icons/1.png";
 import { storeData } from "/src/store";
 import { evaluatingData } from "/src/view/evaluating";
 import iconBack from "/src/page-instrument/header-top/image/icon-back.png";
-import bg1Img from "./img/bg1.png"
-import bg2Img from "./img/bg2.png"
-import ckzpImg from "./img/ckzp.png"
-import bczpImg from "./img/bczp.png"
-import bczpJzImg from "./img/bczpJz.png"
-import zlycImg from "./img/zlyc.png"
+import bg1Img from "./img/bg1.png";
+import bg2Img from "./img/bg2.png";
+import ckzpImg from "./img/ckzp.png";
+import bczpImg from "./img/bczp.png";
+import bczpJzImg from "./img/bczpJz.png";
+import zlycImg from "./img/zlyc.png";
 import iconBadge from "./img/icon-badge.png";
 import icon_expression0 from "./img/icon_expression0.png";
 import icon_expression1 from "./img/icon_expression1.png";
@@ -26,178 +26,163 @@ import { getQuery } from "/src/utils/queryString";
 import { browser, getBehaviorId } from "/src/utils";
 import { api_musicPracticeRecordSave } from "../../api";
 import { getAudioDuration } from "/src/view/audio-list";
-import { debounce } from "/src/utils"
+import { debounce } from "/src/utils";
 
 export default defineComponent({
-	name: "evaluatResult",
-	emits: ["close"],
-	setup(props, { emit }) {
-		const query = getQuery();
-		const data = reactive({
-			saveLoading: true,
-			showPopover: true
-		});
-		const level: any = {
-			BEGINNER: "入门级",
-			ADVANCED: "进阶级",
-			PERFORMER: "大师级",
-		}; 
-		/** 添加评测记录 */
-		const handleAddRecord = async () => {
-			console.log("结束", evaluatingData.resultData);
-			/** 生成评测记录的时候,记录当前评测的谱面类型,用于评测报告默认展示的谱面类型 */
-			evaluatingData.resultData.scoreData.musicType = state.musicRenderType;
-			const body = {
-				deviceType: browser().android ? "ANDROID" : "IOS", // 设备类型
-				intonation: evaluatingData.resultData.intonation, // 音准
-				cadence: evaluatingData.resultData.cadence, // 节奏
-				integrity: evaluatingData.resultData.integrity, // 完成度
-				scoreData: JSON.stringify(evaluatingData.resultData.scoreData), // 评测数据
-				behaviorId: getBehaviorId(), // 行为id
-				sourceTime: getAudioDuration(), // 音频时长
-				partIndex: state.partIndex, // 音轨
-				speed: state.speed, // 速度
-				practiceSource: query.workRecord ? "LESSON_TRAINING" : "EVALUATION", // 练习来源
-				score: evaluatingData.resultData.score, // 分数
-				clientType: storeData.user.clientType, // 客户端类型
-				musicSheetId: state.examSongId, // 乐谱id
-				feature: "EVALUATION", // 特征
-				playTime: evaluatingData.resultData.playTime / 1000, // 播放时长
-				heardLevel: state.setting.evaluationDifficulty, // 听力等级
-				recordFilePath: evaluatingData.resultData.url, // 录音文件路径
-			};
-			data.saveLoading = true;
-			const res = await api_musicPracticeRecordSave(body);
-			if (res?.code === 200){
-				evaluatingData.resultData.recordId = res.data
-			}
-			data.saveLoading = false;
-		};
+  name: "evaluatResult",
+  emits: ["close"],
+  setup(props, { emit }) {
+    const query = getQuery();
+    const data = reactive({
+      saveLoading: true,
+      showPopover: true,
+    });
+    const level: any = {
+      BEGINNER: "入门级",
+      ADVANCED: "进阶级",
+      PERFORMER: "大师级",
+    };
+    /** 添加评测记录 */
+    const handleAddRecord = async () => {
+      console.log("结束", evaluatingData.resultData);
+      /** 生成评测记录的时候,记录当前评测的谱面类型,用于评测报告默认展示的谱面类型 */
+      evaluatingData.resultData.scoreData.musicType = state.musicRenderType;
+      const body = {
+        deviceType: browser().android ? "ANDROID" : "IOS", // 设备类型
+        intonation: evaluatingData.resultData.intonation, // 音准
+        cadence: evaluatingData.resultData.cadence, // 节奏
+        integrity: evaluatingData.resultData.integrity, // 完成度
+        scoreData: JSON.stringify(evaluatingData.resultData.scoreData), // 评测数据
+        behaviorId: getBehaviorId(), // 行为id
+        sourceTime: getAudioDuration(), // 音频时长
+        partIndex: state.partIndex, // 音轨
+        speed: state.speed, // 速度
+        practiceSource: query.workRecord ? "LESSON_TRAINING" : "EVALUATION", // 练习来源
+        score: evaluatingData.resultData.score, // 分数
+        clientType: storeData.user.clientType, // 客户端类型
+        musicSheetId: state.examSongId, // 乐谱id
+        feature: "EVALUATION", // 特征
+        playTime: evaluatingData.resultData.playTime / 1000, // 播放时长
+        heardLevel: state.setting.evaluationDifficulty, // 听力等级
+        recordFilePath: evaluatingData.resultData.url, // 录音文件路径
+      };
+      data.saveLoading = true;
+      const res = await api_musicPracticeRecordSave(body);
+      if (res?.code === 200) {
+        evaluatingData.resultData.recordId = res.data;
+      }
+      data.saveLoading = false;
+    };
 
-		const saveResult = () => {
-			emit("close", "update")
-		}
+    const saveResult = () => {
+      emit("close", "update");
+    };
 
-		onMounted(() => {
-			if (!evaluatingData.isErrorState) {
-				handleAddRecord();
-			}
-			// console.log('评测等级',evaluatingData.resultData.leve)
-		});
+    onMounted(() => {
+      if (!evaluatingData.isErrorState) {
+        handleAddRecord();
+      }
+      // console.log('评测等级',evaluatingData.resultData.leve)
+    });
 
-		watch(() => evaluatingData.resulstMode, (val) => {
-			// # 9402,评测异常操作:都改为不生成评测记录
-			if (val) {
-				setTimeout(() => {
-					if (!evaluatingData.isErrorState) {
-						handleAddRecord();
-					}
-				}, 0);
-			}
-		})
-		return () => (
-			<>
-			{
-				!evaluatingData.hideResultModal && 
-				<div class={styles.evaluatResult}>
-					<div class={styles.closeBtn} onClick={() => emit("close")}>
-						<img src={iconBack} />
-					</div>
-					<div class={[styles.fraction, state.isPercussion && styles.fractionPercussion]}>
-						<img class={styles.bg} src={state.isPercussion ? bg2Img : bg1Img} />
-						<div class={styles.top}>
-							{evaluatingData.resultData.score > 79 && (
-								<img class={styles.badge} src={iconBadge} />
-							)}
-							<div class={[styles.text, evaluatingData.resultData.score > 79 && styles.badgeText]}>
-								<div class={styles.num}>{evaluatingData.resultData.score}</div>
-								<div class={styles.score}>分</div>
-								<div class={styles.level}>
-									<div>{level[evaluatingData.resultData.heardLevel]}</div>
-									<span>|</span>
-									<div>速度{evaluatingData.resultData.speed}</div>
-								</div>
-							</div>
-							<img
-								style={{ display: evaluatingData.resultData.leve === 0 ? "" : "none" }}
-								class={styles.rightBadge}
-								src={icon_expression0}
-							/>
-							<img
-								style={{ display: evaluatingData.resultData.leve === 1 ? "" : "none" }}
-								class={styles.rightBadge}
-								src={icon_expression1}
-							/>
-							<img
-								style={{ display: evaluatingData.resultData.leve === 2 ? "" : "none" }}
-								class={styles.rightBadge}
-								src={icon_expression2}
-							/>
-							<img
-								style={{ display: evaluatingData.resultData.leve === 3 ? "" : "none" }}
-								class={styles.rightBadge}
-								src={icon_expression3}
-							/>
-							<img
-								style={{ display: evaluatingData.resultData.leve === 4 ? "" : "none" }}
-								class={styles.rightBadge}
-								src={icon_expression4}
-							/>
-						</div>
-						{!state.isPercussion && <div class={styles.detail}>
-								<div class={styles.progressitem}>
-									<div>
-										<img src={yzImg} />
-										<span>音准</span>
-									</div>
-									<div>{evaluatingData.resultData.intonation}分</div>
-								</div>
-								<div class={styles.progressitem}>
-									<div>
-										<img src={jzImg} />
-										<span>节奏</span>
-									</div>
-									<div>{evaluatingData.resultData.cadence}分</div>
-								</div>
-								<div class={styles.progressitem}>
-									<div>
-										<img src={wzxImg} />
-										<span>完成度</span>
-									</div>
-									<div>{evaluatingData.resultData.integrity}分</div>
-								</div>
-							</div>
-						}
-						<div class={styles.tips}>{evaluatingData.resultData.clxtip}</div>
-						<div class={styles.ctrls}>
-							<img
-								src={zlycImg}
-								class={styles.ctrlsBtn}
-								onClick={() => emit("close", "tryagain")}
-							/>
-							{
-								!state.isHideEvaluatReportSaveBtn && evaluatingData.resultData.recordId ? <img src={bczpImg} class={styles.ctrlsBtn} onClick={debounce(saveResult,300)} /> :  
-								<Popover class={"savePopoverClose"} placement={"top"} v-model:show={data.showPopover} v-slots={{
-										reference : ()=> <img src={bczpJzImg} class={styles.ctrlsBtn} />
-									}} theme="dark">
-									<div class={"popoverClose"}>
-										<div>该曲目暂不支持保存作品噢~</div>
-										<img src={closeImg} onClick={() => {
-											data.showPopover = false
-										}} />
-									</div>
-								</Popover>
-							}
-							<img
-								src={ckzpImg}
-								class={[styles.ctrlsBtn, data.saveLoading ? styles.disablued : ""]}
-								onClick={() => emit("close", "look")}
-							/>
-						</div>
-					</div>
-				</div>				
-			}
-			</>
-		);
-	},
+    watch(
+      () => evaluatingData.resulstMode,
+      (val) => {
+        // # 9402,评测异常操作:都改为不生成评测记录
+        if (val) {
+          setTimeout(() => {
+            if (!evaluatingData.isErrorState) {
+              handleAddRecord();
+            }
+          }, 0);
+        }
+      }
+    );
+    return () => (
+      <>
+        {!evaluatingData.hideResultModal && (
+          <div class={styles.evaluatResult}>
+            <div class={styles.closeBtn} onClick={() => emit("close")}>
+              <img src={iconBack} />
+            </div>
+            <div class={[styles.fraction, state.isPercussion && styles.fractionPercussion]}>
+              <img class={styles.bg} src={state.isPercussion ? bg2Img : bg1Img} />
+              <div class={styles.top}>
+                {evaluatingData.resultData.score > 79 && <img class={styles.badge} src={iconBadge} />}
+                <div class={[styles.text, evaluatingData.resultData.score > 79 && styles.badgeText]}>
+                  <div class={[styles.scoreSection, "evaluting-result-1"]}>
+                    <div class={styles.num}>{evaluatingData.resultData.score}00</div>
+                    <div class={styles.score}>分</div>
+                    <div class={styles.level}>
+                      <div>{level[evaluatingData.resultData.heardLevel]}</div>
+                      <span>|</span>
+                      <div>速度{evaluatingData.resultData.speed}</div>
+                    </div>
+                  </div>
+                </div>
+                <img style={{ display: evaluatingData.resultData.leve === 0 ? "" : "none" }} class={styles.rightBadge} src={icon_expression0} />
+                <img style={{ display: evaluatingData.resultData.leve === 1 ? "" : "none" }} class={styles.rightBadge} src={icon_expression1} />
+                <img style={{ display: evaluatingData.resultData.leve === 2 ? "" : "none" }} class={styles.rightBadge} src={icon_expression2} />
+                <img style={{ display: evaluatingData.resultData.leve === 3 ? "" : "none" }} class={styles.rightBadge} src={icon_expression3} />
+                <img style={{ display: evaluatingData.resultData.leve === 4 ? "" : "none" }} class={styles.rightBadge} src={icon_expression4} />
+              </div>
+              {!state.isPercussion && (
+                <div class={styles.detail}>
+                  <div class={styles.progressitem}>
+                    <div>
+                      <img src={yzImg} />
+                      <span>音准</span>
+                    </div>
+                    <div>{evaluatingData.resultData.intonation}分</div>
+                  </div>
+                  <div class={styles.progressitem}>
+                    <div>
+                      <img src={jzImg} />
+                      <span>节奏</span>
+                    </div>
+                    <div>{evaluatingData.resultData.cadence}分</div>
+                  </div>
+                  <div class={styles.progressitem}>
+                    <div>
+                      <img src={wzxImg} />
+                      <span>完成度</span>
+                    </div>
+                    <div>{evaluatingData.resultData.integrity}分</div>
+                  </div>
+                </div>
+              )}
+              <div class={styles.tips}>{evaluatingData.resultData.clxtip}</div>
+              <div class={styles.ctrls}>
+                <img src={zlycImg} class={[styles.ctrlsBtn, "evaluting-result-2"]} onClick={() => emit("close", "tryagain")} />
+                {!state.isHideEvaluatReportSaveBtn && evaluatingData.resultData.recordId ? (
+                  <img src={bczpImg} class={[styles.ctrlsBtn, "evaluting-result-3"]} onClick={debounce(saveResult, 300)} />
+                ) : (
+                  <Popover
+                    class={"savePopoverClose"}
+                    placement={"top"}
+                    v-model:show={data.showPopover}
+                    v-slots={{
+                      reference: () => <img src={bczpJzImg} class={[styles.ctrlsBtn, "evaluting-result-3"]} />,
+                    }}
+                    theme="dark"
+                  >
+                    <div class={"popoverClose"}>
+                      <div>该曲目暂不支持保存作品噢~</div>
+                      <img
+                        src={closeImg}
+                        onClick={() => {
+                          data.showPopover = false;
+                        }}
+                      />
+                    </div>
+                  </Popover>
+                )}
+                <img src={ckzpImg} class={[styles.ctrlsBtn, "evaluting-result-4", data.saveLoading ? styles.disablued : ""]} onClick={() => emit("close", "look")} />
+              </div>
+            </div>
+          </div>
+        )}
+      </>
+    );
+  },
 });

+ 80 - 93
src/page-instrument/evaluat-model/index.tsx

@@ -10,19 +10,7 @@ import { getNoteByMeasuresSlursStart } from "/src/helpers/formateMusic";
 import { Icon, Popup, showToast, closeToast, showLoadingToast } from "vant";
 import EvaluatResult from "./evaluat-result";
 import EvaluatAudio from "./evaluat-audio";
-import { 
-  api_getDeviceDelay, 
-  api_openAdjustRecording, 
-  api_proxyServiceMessage, 
-  api_videoUpdate, 
-  getEarphone, 
-  api_back, 
-  api_startDelayCheck,
-  api_cancelDelayCheck,
-  api_closeDelayCheck,
-  api_finishDelayCheck,
-  api_retryEvaluating
- } from "/src/helpers/communication";
+import { api_getDeviceDelay, api_openAdjustRecording, api_proxyServiceMessage, api_videoUpdate, getEarphone, api_back, api_startDelayCheck, api_cancelDelayCheck, api_closeDelayCheck, api_finishDelayCheck, api_retryEvaluating } from "/src/helpers/communication";
 import EvaluatShare from "./evaluat-share";
 import { Vue3Lottie } from "vue3-lottie";
 import startData from "./data/start.json";
@@ -33,7 +21,7 @@ import { headImg } from "/src/page-instrument/header-top/image";
 import { api_musicPracticeRecordVideoUpload } from "../api";
 import { headTopData } from "../header-top/index";
 import { getQuery } from "/src/utils/queryString";
-import Countdown from "./countdown"
+import Countdown from "./countdown";
 import { IPostMessage } from "/src/utils/native-message";
 
 // const DelayCheck = defineAsyncComponent(() =>
@@ -48,8 +36,8 @@ type TCriteria = "frequency" | "amplitude" | "decibels";
  * 评测模式时,应该传节拍器时长
  * 阶段评测时,判断是否从第一小节开始,并且曲子本身含有节拍器,需要传节拍器时长,否则传0
  */
-let actualBeatLength = 0
-let calculateInfo: any = {}
+let actualBeatLength = 0;
+let calculateInfo: any = {};
 
 export default defineComponent({
   name: "evaluat-model",
@@ -73,7 +61,7 @@ export default defineComponent({
         handleRessetState();
         headTopData.modeType = "init";
       }
-    }
+    };
     /**
      * 执行检测
      */
@@ -137,11 +125,11 @@ export default defineComponent({
 
     /** 校验耳机状态 */
     const checkEarphoneStatus = async (type?: string) => {
-      if (type !== 'start') {
+      if (type !== "start") {
         // const erji = await checkUseEarphone();
         const res = await getEarphone();
         const erji = res?.content?.checkIsWired || false;
-        console.log('耳机状态111',res)
+        console.log("耳机状态111", res);
         evaluatingData.earphoneMode = true;
         evaluatingData.earPhoneType = res?.content?.type || "";
         if (evaluatingData.earPhoneType === "有线耳机") {
@@ -150,52 +138,48 @@ export default defineComponent({
           }, 3000);
         }
       }
-      console.log("检测结束,生成数据",evaluatingData.websocketState , evaluatingData.startBegin , evaluatingData.checkEnd);
+      console.log("检测结束,生成数据", evaluatingData.websocketState, evaluatingData.startBegin, evaluatingData.checkEnd);
       handleConnect();
-    }
+    };
 
     /** 生成评测曲谱数据 */
     const formatTimes = () => {
-      let starTime = 0
+      let starTime = 0;
       let ListenMode = false;
       let dontEvaluatingMode = false;
       let skip = false;
       const datas = [];
-      let selectTimes = state.times
-      let unitTestIdx = 0
-			let preTime = 0
-			let preTimes = []
+      let selectTimes = state.times;
+      let unitTestIdx = 0;
+      let preTime = 0;
+      let preTimes = [];
       // 系统节拍器时长
-      actualBeatLength = Math.round(state.times[0].fixtime * 1000 / 1)
+      actualBeatLength = Math.round((state.times[0].fixtime * 1000) / 1);
       // 如果是阶段评测,选取该阶段的times
-			if (state.isSelectMeasureMode && state.section.length) {
-				const startIndex = state.times.findIndex(
-				  (n: any) => n.noteId == state.section[0].noteId
-				)
-				let endIndex = state.times.findIndex(
-				  (n: any) => n.noteId == state.section[1].noteId
-				)
-        endIndex = endIndex < state.section[1].i ? state.section[1].i : endIndex
+      if (state.isSelectMeasureMode && state.section.length) {
+        const startIndex = state.times.findIndex((n: any) => n.noteId == state.section[0].noteId);
+        let endIndex = state.times.findIndex((n: any) => n.noteId == state.section[1].noteId);
+        endIndex = endIndex < state.section[1].i ? state.section[1].i : endIndex;
         if (startIndex > 1) {
-					// firstNoteTime应该取预备小节的第一个音符的开始播放的时间
-					const idx = startIndex - 1 - (state.times[startIndex-1].si)
-					preTime = state.times[idx] ? state.times[idx].time * 1000 : 0
-				}
-        actualBeatLength = startIndex == 0 && state.isOpenMetronome ? actualBeatLength : 0
-				selectTimes = state.times.filter((n: any, index: number) => {
-				  return index >= startIndex && index <= endIndex
-				})
+          // firstNoteTime应该取预备小节的第一个音符的开始播放的时间
+          const idx = startIndex - 1 - state.times[startIndex - 1].si;
+          preTime = state.times[idx] ? state.times[idx].time * 1000 : 0;
+        }
+        actualBeatLength = startIndex == 0 && state.isOpenMetronome ? actualBeatLength : 0;
+        selectTimes = state.times.filter((n: any, index: number) => {
+          return index >= startIndex && index <= endIndex;
+        });
         preTimes = state.times.filter((n: any, index: number) => {
-					return index < startIndex
-				})				
-				unitTestIdx = startIndex
-        starTime = selectTimes[0].sourceRelativeTime || selectTimes[0].relativeTime
-			}	
-			// 阶段评测beatLength需要加上预备小节的持续时长
-			actualBeatLength = preTimes.length ? actualBeatLength + preTimes[preTimes.length - 1].relaMeasureLength * 1000 : actualBeatLength				
-			let firstNoteTime = unitTestIdx > 1 ? preTime : 0
-			let measureIndex = -1
-			let recordMeasure = -1
+          return index < startIndex;
+        });
+        unitTestIdx = startIndex;
+        starTime = selectTimes[0].sourceRelativeTime || selectTimes[0].relativeTime;
+      }
+      // 阶段评测beatLength需要加上预备小节的持续时长
+      actualBeatLength = preTimes.length ? actualBeatLength + preTimes[preTimes.length - 1].relaMeasureLength * 1000 : actualBeatLength;
+      let firstNoteTime = unitTestIdx > 1 ? preTime : 0;
+      let measureIndex = -1;
+      let recordMeasure = -1;
 
       for (let index = 0; index < selectTimes.length; index++) {
         const item = selectTimes[index];
@@ -232,9 +216,9 @@ export default defineComponent({
         // console.log("skip", skip)
         // console.log(end,start,rate,noteRate, '评测')
         if (note.measureOpenIndex != recordMeasure) {
-					measureIndex++
-					recordMeasure = note.measureOpenIndex
-				}
+          measureIndex++;
+          recordMeasure = note.measureOpenIndex;
+        }
         const data = {
           timeStamp: (start * 1000) / rate,
           duration: ((end * 1000) / rate - (start * 1000) / rate) * noteRate,
@@ -251,18 +235,18 @@ export default defineComponent({
         };
         datas.push(data);
       }
-			return {
-				datas,
-				firstNoteTime
-			}
+      return {
+        datas,
+        firstNoteTime,
+      };
     };
     /** 连接websocket */
     const handleConnect = async () => {
       const behaviorId = localStorage.getItem("behaviorId") || localStorage.getItem("BEHAVIORID") || undefined;
       let rate = state.speed / state.originSpeed;
       rate = parseFloat(rate.toFixed(2));
-      console.log('速度比例',rate,'速度',state.speed)
-      calculateInfo = formatTimes()
+      console.log("速度比例", rate, "速度", state.speed);
+      calculateInfo = formatTimes();
       const content = {
         musicXmlInfos: calculateInfo.datas,
         subjectId: state.musicalCode,
@@ -291,7 +275,7 @@ export default defineComponent({
       if (type === "update") {
         if (state.isAppPlay) {
           evaluatModel.evaluatUpdateAudio = true;
-          resetPlaybackToStart()
+          resetPlaybackToStart();
           return;
         } else if (evaluatingData.resultData?.recordIdStr || evaluatingData.resultData?.recordId) {
           let rate = state.speed / state.originSpeed;
@@ -320,14 +304,14 @@ export default defineComponent({
         // 去练习
         handleStartEvaluat();
       } else if (type === "tryagain") {
-        startBtnHandle()
+        startBtnHandle();
       } else if (type === "selfCancel") {
         // 再来一次,需要手动取消评测,不生成评测记录,不显示评测结果弹窗
         evaluatingData.oneselfCancleEvaluating = true;
         handleCancelEvaluat();
-        startBtnHandle()
+        startBtnHandle();
       }
-      resetPlaybackToStart()
+      resetPlaybackToStart();
       evaluatingData.resulstMode = false;
     };
 
@@ -376,11 +360,11 @@ export default defineComponent({
         await new Promise<void>((resolve) => {
           setTimeout(() => {
             closeToast();
-            evaluatingData.isErrorState =false
+            evaluatingData.isErrorState = false;
             // console.log('异常流程2')
-            resolve()
+            resolve();
           }, 1000);
-        })
+        });
       }
       // console.log('异常流程3')
       // 检测APP端socket状态
@@ -392,41 +376,40 @@ export default defineComponent({
           evaluatingData.isErrorState = false;
           evaluatingData.resulstMode = false;
         }
-        
       }
-    }
+    };
 
     // 监听到APP取消延迟检测
     const handleCancelDelayCheck = async (res?: IPostMessage) => {
-      console.log('监听取消延迟检测', res)
+      console.log("监听取消延迟检测", res);
       if (res?.content) {
         // 关闭延迟检测页面,并返回到模式选择页面
         // await api_closeDelayCheck({});
-        handleDelayBack()
+        handleDelayBack();
       }
     };
 
     // 监听APP延迟成功的回调
     const handleFinishDelayCheck = async (res?: IPostMessage) => {
-      console.log('监听延迟检测成功', res)
+      console.log("监听延迟检测成功", res);
       if (res?.content) {
-        evaluatingData.checkEnd = true
-        checkEarphoneStatus()
+        evaluatingData.checkEnd = true;
+        checkEarphoneStatus();
       }
     };
 
     // 监听重复评测消息
     const handRetryEvaluating = () => {
-      handleEvaluatResult('tryagain')
-    }
+      handleEvaluatResult("tryagain");
+    };
 
     onMounted(async () => {
       // 如果打开了延迟检测开关,需要先发送开始检测的消息
       if (state.setting.soundEffect) {
         await api_startDelayCheck({});
       } else {
-        evaluatingData.checkEnd = true
-        checkEarphoneStatus()
+        evaluatingData.checkEnd = true;
+        checkEarphoneStatus();
       }
       evaluatingData.isDisabledPlayMusic = true;
       // handlePerformDetection();
@@ -438,17 +421,20 @@ export default defineComponent({
       <div>
         <div class={styles.operatingBtn}>
           {evaluatingData.websocketState && !evaluatingData.startBegin && evaluatingData.checkEnd && (
-              <img class={styles.iconBtn} src={headImg("icon_play.png")} 
+            <img
+              class={[styles.iconBtn, "evaluting-1"]}
+              src={headImg("icon_play.png")}
               onClick={() => {
-                startBtnHandle()
-              }} />
-            )}
+                startBtnHandle();
+              }}
+            />
+          )}
           {evaluatingData.websocketState && evaluatingData.startBegin && (
             <>
-              <img class={styles.iconBtn} src={headImg("icon_reset.png")} onClick={()=>handleEvaluatResult("selfCancel")} />  
-              <img class={styles.iconBtn} src={headImg("submit.png")} onClick={() => handleEndBegin()}/> 
+              <img class={styles.iconBtn} src={headImg("icon_reset.png")} onClick={() => handleEvaluatResult("selfCancel")} />
+              <img class={styles.iconBtn} src={headImg("submit.png")} onClick={() => handleEndBegin()} />
             </>
-          )} 
+          )}
         </div>
 
         {/* {evaluatingData.soundEffectMode && (
@@ -460,7 +446,7 @@ export default defineComponent({
             onBack={() => handleDelayBack()}
           />
         )} */}
-        
+
         {/* 倒计时 */}
         <Countdown />
         {/* 遮罩 */}
@@ -473,18 +459,19 @@ export default defineComponent({
             onClose={() => {
               evaluatingData.earphoneMode = false;
               // handlePerformDetection();
-              checkEarphoneStatus('start');
+              checkEarphoneStatus("start");
             }}
           />
         </Popup>
 
         {/* 评测作业,非完整评测不显示评测结果弹窗 */}
-        {
-          evaluatingData.hideResultModal ? <EvaluatResult onClose={handleEvaluatResult} /> :
+        {evaluatingData.hideResultModal ? (
+          <EvaluatResult onClose={handleEvaluatResult} />
+        ) : (
           <Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale"]} transition="van-scale" v-model:show={evaluatingData.resulstMode}>
             <EvaluatResult onClose={handleEvaluatResult} />
-          </Popup>    
-        }
+          </Popup>
+        )}
 
         <Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale"]} transition="van-scale" v-model:show={evaluatModel.evaluatUpdateAudio}>
           <EvaluatAudio onClose={hanldeUpdateVideoAndAudio} />

+ 34 - 37
src/page-instrument/follow-model/index.tsx

@@ -5,15 +5,15 @@ import { followData, handleFollowEnd, handleFollowStart } from "/src/view/follow
 import { Popup } from "vant";
 import Microphone from "./microphone";
 import state, { IPlatform } from "/src/state";
-import PcEndIcon from "../header-top/image/pc_end_icon.png"
+import PcEndIcon from "../header-top/image/pc_end_icon.png";
 import { headImg } from "/src/page-instrument/header-top/image";
 
 export default defineComponent({
-	name: "follow-model",
-	setup() {
-		return () => (
-			<>
-				{/* <Transition name="pop-center">
+  name: "follow-model",
+  setup() {
+    return () => (
+      <>
+        {/* <Transition name="pop-center">
 					{!followData.start && (
 						<div class={styles.startBtn} key="start">
 							<img
@@ -37,40 +37,37 @@ export default defineComponent({
 						</div>
 					)}
 				</Transition> */}
-				<div class={styles.operatingBtn}>
-					{!followData.start && (
-						<img class={styles.iconBtn} src={headImg("icon_play.png")} 
-						onClick={() => {
-							handleFollowStart()
-						}} />
-						)}
-					{followData.start && (
-						<>
-						<img class={styles.iconBtn} src={headImg("icon_reset.png")} onClick={()=>handleFollowEnd()} />  
-						<img class={styles.iconBtn} src={headImg("submit.png")} onClick={() => handleFollowEnd()}/> 
-						</>
-					)} 
-				</div>				
-				{/* <div style={{ display: followData.start ? "" : "none" }} class={styles.noteState}>
+        <div class={styles.operatingBtn}>
+          {!followData.start && (
+            <img
+              class={[styles.iconBtn, "follow-1"]}
+              src={headImg("icon_play.png")}
+              onClick={() => {
+                handleFollowStart();
+              }}
+            />
+          )}
+          {followData.start && (
+            <>
+              <img class={styles.iconBtn} src={headImg("icon_reset.png")} onClick={() => handleFollowEnd()} />
+              <img class={styles.iconBtn} src={headImg("submit.png")} onClick={() => handleFollowEnd()} />
+            </>
+          )}
+        </div>
+        {/* <div style={{ display: followData.start ? "" : "none" }} class={styles.noteState}>
 					<span style={{ background: "#ffca67" }} class={styles.dot}></span>
 					<span>低</span>
 					<span style={{ background: "rgb(255, 0, 0)" }} class={styles.dot}></span>
 					<span>高</span>
 				</div> */}
-				<Popup
-					teleport="body"
-					closeOnClickOverlay={false}
-					class={["popup-custom", "van-scale"]}
-					transition="van-scale"
-					v-model:show={followData.earphone}
-				>
-					<Microphone
-						onClose={() => {
-							followData.earphone = false;
-						}}
-					/>
-				</Popup>
-			</>
-		);
-	},
+        <Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale"]} transition="van-scale" v-model:show={followData.earphone}>
+          <Microphone
+            onClose={() => {
+              followData.earphone = false;
+            }}
+          />
+        </Popup>
+      </>
+    );
+  },
 });

+ 60 - 26
src/page-instrument/header-top/index.module.less

@@ -5,26 +5,31 @@
     height: 100%;
     flex-shrink: 0;
     padding: 0 30px;
-    background: linear-gradient( 180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.6) 100%);
+    background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 100%);
 }
-.headTopLeftBox{
+
+.headTopLeftBox {
     position: fixed;
     top: 20px;
     left: 30px;
     display: flex;
     align-items: center;
-    .img{
+
+    .img {
         width: 32px;
         height: 32px;
-        &:first-child{
+
+        &:first-child {
             margin-right: 10px;
         }
     }
-    .title{
+
+    .title {
         width: 216px;
-        &.isMusicList{
-            :global{
-                .van-notice-bar .van-notice-bar__content::after{
+
+        &.isMusicList {
+            :global {
+                .van-notice-bar .van-notice-bar__content::after {
                     position: absolute;
                     top: 50%;
                     right: 0;
@@ -33,31 +38,35 @@
                     width: 11px;
                     height: 6px;
                     background: url("./image/sj.png") no-repeat;
-                    background-size: 100% 100%;                         
+                    background-size: 100% 100%;
                 }
             }
         }
-        :global{
-            .van-notice-bar{
+
+        :global {
+            .van-notice-bar {
                 height: 30px;
                 line-height: 30px;
                 padding: 0;
                 font-weight: 600;
                 font-size: 18px;
                 color: #FFFFFF;
-                .van-notice-bar__content{
+
+                .van-notice-bar__content {
                     position: relative;
                     padding-right: 16px;
                 }
             }
         }
     }
+
     .hidenBack {
         opacity: 0;
         pointer-events: none;
     }
 }
-.modeChangeBox{
+
+.modeChangeBox {
     position: fixed;
     top: 20px;
     right: 30px;
@@ -67,35 +76,47 @@
     display: flex;
     align-items: center;
     padding: 0 10px;
-    .img{
+
+    .img {
         width: 18px;
         height: 18px;
     }
-    .title{
+
+    .title {
         margin-left: 6px;
         font-weight: 500;
         font-size: 14px;
         color: #FFFFFF;
     }
 }
+
 .headRight {
     display: flex;
     align-items: center;
     height: 100%;
+
     .btn {
         position: relative;
         display: flex;
         flex-direction: column;
         align-items: center;
         cursor: pointer;
-        margin-right: 30px;
-        &:last-child{
+        padding: 0 8px;
+        margin-right: 14px;
+
+        &:first-child {
+            margin-left: -8px;
+        }
+
+        &:last-child {
             margin-right: 0;
         }
+
         .iconBtn {
             width: 24px;
             height: 24px;
         }
+
         span {
             margin-top: 3px;
             font-weight: 500;
@@ -104,9 +125,11 @@
             line-height: 17px;
         }
     }
-    .metronomeBtn{
+
+    .metronomeBtn {
         position: relative;
-        .speedCon{
+
+        .speedCon {
             transform: scale(0.83);
             transform-origin: left bottom;
             padding: 2px;
@@ -118,11 +141,13 @@
             background: #FFC121;
             border-radius: 120px 120px 120px 1px;
             border: 1px solid #FFFFFF;
-            >img{
+
+            >img {
                 width: 15px;
                 height: 11px;
             }
-            >div{
+
+            >div {
                 margin-left: 1px;
                 font-weight: 600;
                 font-size: 12px;
@@ -143,6 +168,7 @@
     right: 30px;
     bottom: 12px;
     transition: bottom .2s ease;
+
     .btnWrap {
         width: 50px;
         height: 50px;
@@ -153,6 +179,7 @@
             height: 100%;
         }
     }
+
     &.playLeftButton {
         left: 46px !important;
         right: auto !important;
@@ -164,7 +191,7 @@
         left: auto !important;
         bottom: 12px !important;
     }
-    
+
     .progress {
         position: absolute;
         left: 50%;
@@ -226,11 +253,13 @@
     background: url(./image/bg.png) no-repeat;
     background-size: 100% 100%;
     transition: all .3s;
-    &.hidden{
+
+    &.hidden {
         opacity: 0;
         transform: translateY(100%);
         pointer-events: none;
     }
+
     .back {
         position: absolute;
         width: 38px;
@@ -239,6 +268,7 @@
         top: 17px;
         cursor: pointer;
     }
+
     .name {
         position: absolute;
         left: 50%;
@@ -247,19 +277,23 @@
         width: 87px;
         height: 21px;
     }
+
     .modeBox {
         width: 100%;
         margin-top: 90px;
         display: flex;
         justify-content: space-between;
         padding: 0 36px;
-        &.twoModeBox{
+
+        &.twoModeBox {
             justify-content: center;
-            > .modeImg + .modeImg{
+
+            >.modeImg+.modeImg {
                 margin-left: 150px;
             }
         }
-        > .modeImg {
+
+        >.modeImg {
             width: calc((100% - 2*40px)/3);
             max-width: 220px;
         }

+ 222 - 210
src/page-instrument/header-top/index.tsx

@@ -1,4 +1,4 @@
-import { Transition, computed, defineComponent, onMounted, onUnmounted, reactive, ref, watch, toRef,ComputedRef } from "vue";
+import { Transition, computed, defineComponent, onMounted, onUnmounted, reactive, ref, watch, toRef, ComputedRef } from "vue";
 import styles from "./index.module.less";
 
 import iconBack from "./image/icon-back.png";
@@ -28,9 +28,10 @@ import { toggleMusicSheet } from "/src/view/plugins/toggleMusicSheet";
 import useDrag from "/src/view/plugins/useDrag/index";
 import Dragbom from "/src/view/plugins/useDrag/dragbom";
 import { getGuidance, setGuidance } from "../custom-plugins/guide-page/api";
-import ModeView from "./modeView"
-import { smoothAnimationState } from "../view-detail/smoothAnimation"
+import ModeView from "./modeView";
+import { smoothAnimationState } from "../view-detail/smoothAnimation";
 import { isMusicList, musicListShow } from "../component/the-music-list";
+import { EvaluatingDriver, EvaluatingResultDriver, FollowDriver, PractiseDriver } from "../custom-plugins/guide-driver";
 
 /** 头部数据和方法 */
 export const headTopData = reactive({
@@ -57,7 +58,7 @@ export const headTopData = reactive({
       // 如果延迟检测资源还在加载中,给出提示
       if (!evaluatingData.jsonLoadDone) {
         evaluatingData.jsonLoading = true;
-        state.audioDone && showToast("资源加载中,请稍后");  //音频资源加载完之后才提示
+        state.audioDone && showToast("资源加载中,请稍后"); //音频资源加载完之后才提示
         return;
       }
       // 如果是pc端, 评测模式暂不可用
@@ -72,20 +73,20 @@ export const headTopData = reactive({
       }
       // 评测模式,只有一行谱模式
       if (!state.isSingleLine) {
-        state.isSingleLine = true
-        refreshMusicSvg()
+        state.isSingleLine = true;
+        refreshMusicSvg();
       }
       state.playIngSpeed = state.originSpeed;
       handleStartEvaluat();
       // 开发模式,把此处打开
-      // state.modeType = "evaluating"
+      state.modeType = "evaluating";
       // evaluatingData.rendered = true;
       // evaluatingData.soundEffectMode = true;
     } else if (value === "follow") {
       // 跟练模式,只有一行谱模式
       if (!state.isSingleLine) {
-        state.isSingleLine = true
-        refreshMusicSvg()
+        state.isSingleLine = true;
+        refreshMusicSvg();
       }
       smoothAnimationState.isShow.value = false;
       toggleFollow();
@@ -99,143 +100,143 @@ export const headData = reactive({
   musicTypeShow: false,
 });
 
-let resetBtn:ComputedRef<{
+let resetBtn: ComputedRef<{
   display: boolean;
   disabled: boolean;
-}>
+}>;
 /**
  * 处理模式切换
  * @param oldPlayType   没改变之前的播放模式
  * @param oldPlaySource  没改变之前的播放类型
  * @param isforceReset   是否强制刷新播放状态 模式times时值改变时候也刷新
  */
-export function handlerModeChange(oldPlayType:"play"|"sing", oldPlaySource:IPlayState,isforceReset?:boolean) {
-    const isModeChange = modeChangeHandleTimes(oldPlayType, oldPlaySource)
-    // 没有切换的时候 不处理下面的
-    if(isModeChange){
-      try {
-        metronomeData.metro.calculation(state.times);
-      } catch (error) {}
-      console.log("重新之后的times", state.times, state.fixtime)
-    }
-    if(isModeChange||isforceReset){
-      // 重置播放状态
-      handleRessetState()
-      // 隐藏重播按钮
-      resetBtn && (resetBtn.value.display = false)
-    }
+export function handlerModeChange(oldPlayType: "play" | "sing", oldPlaySource: IPlayState, isforceReset?: boolean) {
+  const isModeChange = modeChangeHandleTimes(oldPlayType, oldPlaySource);
+  // 没有切换的时候 不处理下面的
+  if (isModeChange) {
+    try {
+      metronomeData.metro.calculation(state.times);
+    } catch (error) {}
+    console.log("重新之后的times", state.times, state.fixtime);
+  }
+  if (isModeChange || isforceReset) {
+    // 重置播放状态
+    handleRessetState();
+    // 隐藏重播按钮
+    resetBtn && (resetBtn.value.display = false);
+  }
 }
 // 模式切换之后重新给times赋值
-function modeChangeHandleTimes(oldPlayType:"play"|"sing", oldPlaySource:IPlayState){
-  const playType = state.playType
-  const playSource = state.playSource
-  const {notBeatFixtime, xmlMp3BeatFixTime, difftime} = state.times[0]
-  const { isOpenMetronome, isSingOpenMetronome } = state 
+function modeChangeHandleTimes(oldPlayType: "play" | "sing", oldPlaySource: IPlayState) {
+  const playType = state.playType;
+  const playSource = state.playSource;
+  const { notBeatFixtime, xmlMp3BeatFixTime, difftime } = state.times[0];
+  const { isOpenMetronome, isSingOpenMetronome } = state;
   // 演奏向演唱切
-  if(oldPlayType === "play"&&playType === "sing"){
-    if(playSource === "mingSong"){
+  if (oldPlayType === "play" && playType === "sing") {
+    if (playSource === "mingSong") {
       // 唱名文件也要加上弱起时间  他们制作曲子加了弱起时间
-      state.fixtime = difftime
-      state.times.map(item => {
-        item.time = item.xmlNoteTime + difftime
-        item.endtime = item.xmlNoteEndTime + difftime
-        item.fixtime = difftime
-      })
-      return true
-    }else{
+      state.fixtime = difftime;
+      state.times.map((item) => {
+        item.time = item.xmlNoteTime + difftime;
+        item.endtime = item.xmlNoteEndTime + difftime;
+        item.fixtime = difftime;
+      });
+      return true;
+    } else {
       //演奏开了节拍器,演唱没开节拍器
-      if(isOpenMetronome&&!isSingOpenMetronome){
-        state.fixtime = notBeatFixtime
-        state.times.map(item => {
-          item.time = item.notBeatTime
-          item.endtime = item.notBeatEndTime
-          item.fixtime = notBeatFixtime
-        })
-        return true
-      }else if(!isOpenMetronome&&isSingOpenMetronome){
-        state.fixtime = notBeatFixtime + xmlMp3BeatFixTime
-        state.times.map(item => {
-          item.time = item.notBeatTime + xmlMp3BeatFixTime
-          item.endtime = item.notBeatEndTime + xmlMp3BeatFixTime
-          item.fixtime = notBeatFixtime + xmlMp3BeatFixTime
-        })
-        return true
+      if (isOpenMetronome && !isSingOpenMetronome) {
+        state.fixtime = notBeatFixtime;
+        state.times.map((item) => {
+          item.time = item.notBeatTime;
+          item.endtime = item.notBeatEndTime;
+          item.fixtime = notBeatFixtime;
+        });
+        return true;
+      } else if (!isOpenMetronome && isSingOpenMetronome) {
+        state.fixtime = notBeatFixtime + xmlMp3BeatFixTime;
+        state.times.map((item) => {
+          item.time = item.notBeatTime + xmlMp3BeatFixTime;
+          item.endtime = item.notBeatEndTime + xmlMp3BeatFixTime;
+          item.fixtime = notBeatFixtime + xmlMp3BeatFixTime;
+        });
+        return true;
       }
     }
-  }else if(oldPlayType === "sing"&&playType === "play"){
+  } else if (oldPlayType === "sing" && playType === "play") {
     // 演唱向演奏切
-    if(oldPlaySource === "mingSong"){
+    if (oldPlaySource === "mingSong") {
       // 有节拍器
-      if(isOpenMetronome){
-        state.fixtime = notBeatFixtime + xmlMp3BeatFixTime
-        state.times.map(item => {
-          item.time = item.notBeatTime + xmlMp3BeatFixTime
-          item.endtime = item.notBeatEndTime + xmlMp3BeatFixTime
-          item.fixtime = notBeatFixtime + xmlMp3BeatFixTime
-        })
-        return true
-      }else{
-        state.fixtime = notBeatFixtime
-        state.times.map(item => {
-          item.time = item.notBeatTime
-          item.endtime = item.notBeatEndTime
-          item.fixtime = notBeatFixtime
-        })
-        return true
+      if (isOpenMetronome) {
+        state.fixtime = notBeatFixtime + xmlMp3BeatFixTime;
+        state.times.map((item) => {
+          item.time = item.notBeatTime + xmlMp3BeatFixTime;
+          item.endtime = item.notBeatEndTime + xmlMp3BeatFixTime;
+          item.fixtime = notBeatFixtime + xmlMp3BeatFixTime;
+        });
+        return true;
+      } else {
+        state.fixtime = notBeatFixtime;
+        state.times.map((item) => {
+          item.time = item.notBeatTime;
+          item.endtime = item.notBeatEndTime;
+          item.fixtime = notBeatFixtime;
+        });
+        return true;
       }
     }
     // 演奏开了节拍器,演唱没开节拍器
-    if(isOpenMetronome&&!isSingOpenMetronome){
-      state.fixtime = notBeatFixtime + xmlMp3BeatFixTime
-      state.times.map(item => {
-        item.time = item.notBeatTime + xmlMp3BeatFixTime
-        item.endtime = item.notBeatEndTime + xmlMp3BeatFixTime
-        item.fixtime = notBeatFixtime + xmlMp3BeatFixTime
-      })
-      return true
-    }else if(!isOpenMetronome&&isSingOpenMetronome){
-      state.fixtime = notBeatFixtime
-      state.times.map(item => {
-        item.time = item.notBeatTime
-        item.endtime = item.notBeatEndTime
-        item.fixtime = notBeatFixtime
-      })
-      return true
+    if (isOpenMetronome && !isSingOpenMetronome) {
+      state.fixtime = notBeatFixtime + xmlMp3BeatFixTime;
+      state.times.map((item) => {
+        item.time = item.notBeatTime + xmlMp3BeatFixTime;
+        item.endtime = item.notBeatEndTime + xmlMp3BeatFixTime;
+        item.fixtime = notBeatFixtime + xmlMp3BeatFixTime;
+      });
+      return true;
+    } else if (!isOpenMetronome && isSingOpenMetronome) {
+      state.fixtime = notBeatFixtime;
+      state.times.map((item) => {
+        item.time = item.notBeatTime;
+        item.endtime = item.notBeatEndTime;
+        item.fixtime = notBeatFixtime;
+      });
+      return true;
     }
-  }else if(oldPlayType === "sing"&&playType === "sing"){
-    // 演唱之间切换  
+  } else if (oldPlayType === "sing" && playType === "sing") {
+    // 演唱之间切换
     // 切到唱名时候
-    if(playSource === "mingSong"){
+    if (playSource === "mingSong") {
       // 唱名文件也要加上弱起时间  他们制作曲子加了弱起时间
-      state.fixtime = difftime
-      state.times.map(item => {
-        item.time = item.xmlNoteTime + difftime
-        item.endtime = item.xmlNoteEndTime + difftime
-        item.fixtime = difftime
-      })
-      return true
-    }else if(oldPlaySource === "mingSong"){
+      state.fixtime = difftime;
+      state.times.map((item) => {
+        item.time = item.xmlNoteTime + difftime;
+        item.endtime = item.xmlNoteEndTime + difftime;
+        item.fixtime = difftime;
+      });
+      return true;
+    } else if (oldPlaySource === "mingSong") {
       // 有节拍器
-      if(isSingOpenMetronome){
-        state.fixtime = notBeatFixtime + xmlMp3BeatFixTime
-        state.times.map(item => {
-          item.time = item.notBeatTime + xmlMp3BeatFixTime
-          item.endtime = item.notBeatEndTime + xmlMp3BeatFixTime
-          item.fixtime = notBeatFixtime + xmlMp3BeatFixTime
-        })
-        return true
-      }else{
-        state.fixtime = notBeatFixtime
-        state.times.map(item => {
-          item.time = item.notBeatTime
-          item.endtime = item.notBeatEndTime
-          item.fixtime = notBeatFixtime
-        })
-        return true
+      if (isSingOpenMetronome) {
+        state.fixtime = notBeatFixtime + xmlMp3BeatFixTime;
+        state.times.map((item) => {
+          item.time = item.notBeatTime + xmlMp3BeatFixTime;
+          item.endtime = item.notBeatEndTime + xmlMp3BeatFixTime;
+          item.fixtime = notBeatFixtime + xmlMp3BeatFixTime;
+        });
+        return true;
+      } else {
+        state.fixtime = notBeatFixtime;
+        state.times.map((item) => {
+          item.time = item.notBeatTime;
+          item.endtime = item.notBeatEndTime;
+          item.fixtime = notBeatFixtime;
+        });
+        return true;
       }
     }
   }
-  return false
+  return false;
 }
 
 export default defineComponent({
@@ -345,20 +346,20 @@ export default defineComponent({
       // 评测开始 禁用
       if (state.modeType === "evaluating") return { display: false, disabled: true };
       if (!state.isAppPlay) {
-        if(state.playType === "play"){
-            // 原声, 伴奏 少一个,就不能切换
-            if (state.music && state.accompany) return { display: true, disabled: false };
+        if (state.playType === "play") {
+          // 原声, 伴奏 少一个,就不能切换
+          if (state.music && state.accompany) return { display: true, disabled: false };
         } else {
           // 播放过程中不能切换
-          if (state.playState === "play"){
+          if (state.playState === "play") {
             return { display: true, disabled: true };
           }
-          // 范唱 
-          let index = 0
-          state.fanSong && index++
-          state.banSong && index++
-          state.mingSong && index++
-          if(index > 1) {
+          // 范唱
+          let index = 0;
+          state.fanSong && index++;
+          state.banSong && index++;
+          state.mingSong && index++;
+          if (index > 1) {
             return { display: true, disabled: false };
           }
         }
@@ -377,23 +378,23 @@ export default defineComponent({
       // 音频播放中 禁用
       if (state.playState === "play") return { display: true, disabled: true };
       if (!state.isAppPlay) {
-          let index = 0
-          state.music && index++
-          state.accompany && index++
-          let songIndex = 0
-          state.fanSong && songIndex++
-          state.banSong && songIndex++
-          state.mingSong && songIndex++
-          // 演唱和演奏 都有数据的时间不禁用
-          if(songIndex>0&&index>0) {
-            return { display: true, disabled: false };
-          }
+        let index = 0;
+        state.music && index++;
+        state.accompany && index++;
+        let songIndex = 0;
+        state.fanSong && songIndex++;
+        state.banSong && songIndex++;
+        state.mingSong && songIndex++;
+        // 演唱和演奏 都有数据的时间不禁用
+        if (songIndex > 0 && index > 0) {
+          return { display: true, disabled: false };
+        }
       }
       return {
         disabled: true,
         display: true,
       };
-    })
+    });
     /** 模式切换按钮 */
     const toggleBtn = computed(() => {
       // 选择模式, url设置模式 不显示
@@ -603,39 +604,43 @@ export default defineComponent({
         >
           {/* 返回和标题 */}
           <div class={styles.headTopLeftBox}>
-            <img src={iconBack} class={['headTopBackBtn', styles.img, !headTopData.showBack && styles.hidenBack]} onClick={handleBack} />
-            {
-              state.modeType === "practise" && smoothAnimationState.isShow.value ?
-                <div class={[styles.title,isMusicList.value && styles.isMusicList]} onClick={()=>{
-                  isMusicList.value && (musicListShow.value = true)
-                }}>
-                  <NoticeBar
-                    text={state.examSongName}
-                    background="none"
-                  />
-              </div> :
-                isMusicList.value &&
-                <img src={listImg} class={[styles.img]} onClick={()=>{
-                  musicListShow.value = true
-                }} />
-            }
-          </div>
-          {/* 模式切换 */}
-            { 
-            state.playType === "play" &&
-              <div 
-                id={state.platform === IPlatform.PC ? "teacherTop-0" : "studnetT-0"}
-                style={{ display: toggleBtn.value.display ? "" : "none" }}
-                class={[styles.modeChangeBox, toggleBtn.value.disabled && styles.disabled]} 
+            <img src={iconBack} class={["headTopBackBtn", styles.img, !headTopData.showBack && styles.hidenBack]} onClick={handleBack} />
+            {state.modeType === "practise" && smoothAnimationState.isShow.value ? (
+              <div
+                class={[styles.title, "driver-8", isMusicList.value && styles.isMusicList]}
                 onClick={() => {
-                    handleRessetState();
-                    headTopData.modeType = "init";
+                  isMusicList.value && (musicListShow.value = true);
                 }}
               >
-                <img class={styles.img} src={iconMode} />
-                <div class={styles.title}>{state.modeType==="practise" ? '练习模式' : state.modeType==="follow" ? "跟练模式" : state.modeType==="evaluating" ? "评测模式" : ""}</div>
+                <NoticeBar text={state.examSongName} background="none" />
               </div>
-          }
+            ) : (
+              isMusicList.value && (
+                <img
+                  src={listImg}
+                  class={[styles.img, "driver-8"]}
+                  onClick={() => {
+                    musicListShow.value = true;
+                  }}
+                />
+              )
+            )}
+          </div>
+          {/* 模式切换 */}
+          {state.playType === "play" && (
+            <div
+              id={state.platform === IPlatform.PC ? "teacherTop-0" : "studnetT-0"}
+              style={{ display: toggleBtn.value.display ? "" : "none" }}
+              class={["driver-9", styles.modeChangeBox, toggleBtn.value.disabled && styles.disabled]}
+              onClick={() => {
+                handleRessetState();
+                headTopData.modeType = "init";
+              }}
+            >
+              <img class={styles.img} src={iconMode} />
+              <div class={styles.title}>{state.modeType === "practise" ? "练习模式" : state.modeType === "follow" ? "跟练模式" : state.modeType === "evaluating" ? "评测模式" : ""}</div>
+            </div>
+          )}
           {/* 功能按钮 */}
           <div
             class={[styles.headRight]}
@@ -672,18 +677,18 @@ export default defineComponent({
             ) : null} */}
             <div
               style={{ display: playTypeBtn.value.display ? "" : "none" }}
-              class={[styles.btn, playTypeBtn.value.disabled && styles.disabled]}
+              class={["driver-2", styles.btn, playTypeBtn.value.disabled && styles.disabled]}
               onClick={() => {
-                const oldPlayType = state.playType
-                const oldPlaySource = state.playSource
-                if(state.playType === "play"){
-                  state.playType = "sing"
-                  state.playSource = state.fanSong?"music":state.banSong?"background":"mingSong"
+                const oldPlayType = state.playType;
+                const oldPlaySource = state.playSource;
+                if (state.playType === "play") {
+                  state.playType = "sing";
+                  state.playSource = state.fanSong ? "music" : state.banSong ? "background" : "mingSong";
                 } else {
-                  state.playType = "play"
-                  state.playSource = state.music?"music":"background"
+                  state.playType = "play";
+                  state.playSource = state.music ? "music" : "background";
                 }
-                handlerModeChange(oldPlayType, oldPlaySource, true)
+                handlerModeChange(oldPlayType, oldPlaySource, true);
               }}
             >
               <img style={{ display: state.playType === "play" ? "" : "none" }} class={styles.iconBtn} src={headImg(`perform.png`)} />
@@ -693,40 +698,40 @@ export default defineComponent({
             <div
               id={state.platform === IPlatform.PC ? "teacherTop-1" : "studnetT-1"}
               style={{ display: originBtn.value.display ? "" : "none" }}
-              class={[styles.btn, originBtn.value.disabled && styles.disabled]}
+              class={["driver-3", styles.btn, originBtn.value.disabled && styles.disabled]}
               onClick={() => {
-                const oldPlayType = state.playType
-                const oldPlaySource = state.playSource
-                if(state.playType === 'play'){
+                const oldPlayType = state.playType;
+                const oldPlaySource = state.playSource;
+                if (state.playType === "play") {
                   state.playSource = state.playSource === "music" ? "background" : "music";
-                }else{
-                  if(state.playSource === "music"){
-                    state.playSource = state.banSong ? "background" :"mingSong"
-                  }else if(state.playSource === "background"){
-                    state.playSource = state.mingSong ? "mingSong" :"music"
-                  }else {
-                    state.playSource = state.fanSong ? "music" :"background"
+                } else {
+                  if (state.playSource === "music") {
+                    state.playSource = state.banSong ? "background" : "mingSong";
+                  } else if (state.playSource === "background") {
+                    state.playSource = state.mingSong ? "mingSong" : "music";
+                  } else {
+                    state.playSource = state.fanSong ? "music" : "background";
                   }
                 }
-                handlerModeChange(oldPlayType, oldPlaySource)
+                handlerModeChange(oldPlayType, oldPlaySource);
               }}
             >
-              <img style={{ display: state.playSource === "music" ? "" : "none" }} class={styles.iconBtn} src={state.playType === 'play'?headImg(`music.png`):headImg(`music1.png`)} />
-              <img style={{ display: state.playSource === "background" ? "" : "none" }} class={styles.iconBtn} src={state.playType === 'play'?headImg(`background.png`):headImg(`background1.png`)} />
+              <img style={{ display: state.playSource === "music" ? "" : "none" }} class={styles.iconBtn} src={state.playType === "play" ? headImg(`music.png`) : headImg(`music1.png`)} />
+              <img style={{ display: state.playSource === "background" ? "" : "none" }} class={styles.iconBtn} src={state.playType === "play" ? headImg(`background.png`) : headImg(`background1.png`)} />
               <img style={{ display: state.playSource === "mingSong" ? "" : "none" }} class={styles.iconBtn} src={headImg(`mingsong.png`)} />
-              <span>{state.playSource === "music" ? (state.playType ==="play" ? "原声" : "范唱") : state.playSource === "background" ? (state.playType ==="play" ? "伴奏" : "伴唱") : "唱名"}</span>
+              <span>{state.playSource === "music" ? (state.playType === "play" ? "原声" : "范唱") : state.playSource === "background" ? (state.playType === "play" ? "伴奏" : "伴唱") : "唱名"}</span>
             </div>
-            <div id={state.platform === IPlatform.PC ? "teacherTop-2" : "studnetT-2"} style={{ display: selectBtn.value.display ? "" : "none" }} class={[styles.btn, selectBtn.value.disabled && styles.disabled]} onClick={() => handleChangeSection()}>
+            <div id={state.platform === IPlatform.PC ? "teacherTop-2" : "studnetT-2"} style={{ display: selectBtn.value.display ? "" : "none" }} class={["driver-4", styles.btn, selectBtn.value.disabled && styles.disabled]} onClick={() => handleChangeSection()}>
               <img style={{ display: state.section.length === 0 ? "" : "none" }} class={styles.iconBtn} src={headImg(`section0.png`)} />
               <img style={{ display: state.section.length === 1 ? "" : "none" }} class={styles.iconBtn} src={headImg(`section1.png`)} />
               <img style={{ display: state.section.length === 2 ? "" : "none" }} class={styles.iconBtn} src={headImg(`section2.png`)} />
               <span>选段</span>
             </div>
-            {(
+            {
               <>
                 <div
                   style={{ display: metronomeBtn.value.display ? "" : "none" }}
-                  class={[styles.btn, styles.metronomeBtn, metronomeBtn.value.disabled && styles.disabled]}
+                  class={["driver-5", styles.btn, styles.metronomeBtn, metronomeBtn.value.disabled && styles.disabled]}
                   onClick={async () => {
                     headData.speedShow = !headData.speedShow;
                   }}
@@ -738,15 +743,15 @@ export default defineComponent({
                     <img src={headImg("speed.png")} />
                     <div>{state.speed}</div>
                   </div>
-                </div>          
+                </div>
                 {
-                  <Popup v-model:show={headData.speedShow} class="popup-custom van-scale center-closeBtn settingBoxClass_drag" transition="van-scale" teleport="body" style={positionInfo.styleDrag.value} overlay-style={{background:'rgba(0, 0, 0, 0.3)'}}>
+                  <Popup v-model:show={headData.speedShow} class="popup-custom van-scale center-closeBtn settingBoxClass_drag" transition="van-scale" teleport="body" style={positionInfo.styleDrag.value} overlay-style={{ background: "rgba(0, 0, 0, 0.3)" }}>
                     <Speed />
                     {state.platform === IPlatform.PC && <Dragbom showGuide={!state.guideInfo?.teacherDrag} onGuideDone={handleGuide} />}
                   </Popup>
                 }
-              </>             
-            )}
+              </>
+            }
             {/* {state.enableNotation ? (
               <Popover trigger="manual" v-model:show={headData.musicTypeShow} class={state.platform === IPlatform.PC && styles.pcTransPop} placement={state.platform === IPlatform.PC ? "top-end" : "bottom-end"} overlay={false} offset={state.platform === IPlatform.PC ? [0, 40] : [0, 8]}>
                 {{
@@ -779,7 +784,7 @@ export default defineComponent({
                 <span>声轨</span>
               </div>
             )}
-            <div id={state.platform === IPlatform.PC ? "teacherTop-6" : "studnetT-6"} style={{ display: settingBtn.value.display ? "" : "none" }} class={[styles.btn, settingBtn.value.disabled && styles.disabled]} onClick={() => (headTopData.settingMode = true)}>
+            <div id={state.platform === IPlatform.PC ? "teacherTop-6" : "studnetT-6"} style={{ display: settingBtn.value.display ? "" : "none" }} class={["driver-6", styles.btn, settingBtn.value.disabled && styles.disabled]} onClick={() => (headTopData.settingMode = true)}>
               <img class={styles.iconBtn} src={headImg("icon_menu.png")} />
               <span>设置</span>
             </div>
@@ -791,6 +796,8 @@ export default defineComponent({
           id="studnetT-7"
           style={{ display: playBtn.value.display ? "" : "none" }}
           class={[
+            // 引导使用的类
+            "driver-1",
             styles.playBtn,
             playBtn.value.disabled && styles.disabled,
             state.platform === IPlatform.PC && state.musicScoreBtnDirection === "left" ? styles.playLeftButton : state.platform === IPlatform.PC && state.musicScoreBtnDirection === "right" ? styles.playRightButton : "",
@@ -808,17 +815,13 @@ export default defineComponent({
         <div
           id="tips-step-9"
           style={{ display: resetBtn.value.display ? "" : "none" }}
-          class={[
-            styles.resetBtn,
-            resetBtn.value.disabled && styles.disabled,
-            state.platform === IPlatform.PC && state.musicScoreBtnDirection === "left" ? styles.pauseLeftButton : state.platform === IPlatform.PC && state.musicScoreBtnDirection === "right" ? styles.pauseRightButton : "",
-          ]}
+          class={[styles.resetBtn, resetBtn.value.disabled && styles.disabled, state.platform === IPlatform.PC && state.musicScoreBtnDirection === "left" ? styles.pauseLeftButton : state.platform === IPlatform.PC && state.musicScoreBtnDirection === "right" ? styles.pauseRightButton : ""]}
           onClick={() => handleResetPlay()}
         >
           <img class={styles.iconBtn} src={headImg("icon_reset.png")} />
         </div>
 
-        <Popup v-model:show={headTopData.settingMode} class="popup-custom van-scale center-closeBtn settingBoxClass_drag" transition="van-scale" teleport="body" style={positionInfo.styleDrag.value} overlay-style={{background:'rgba(0, 0, 0, 0.3)'}}>
+        <Popup v-model:show={headTopData.settingMode} class="popup-custom van-scale center-closeBtn settingBoxClass_drag" transition="van-scale" teleport="body" style={positionInfo.styleDrag.value} overlay-style={{ background: "rgba(0, 0, 0, 0.3)" }}>
           <Settting />
           {state.platform === IPlatform.PC && <Dragbom showGuide={!state.guideInfo?.teacherDrag} onGuideDone={handleGuide} />}
         </Popup>
@@ -828,8 +831,17 @@ export default defineComponent({
         <ModeView></ModeView>
 
         {/* isAllBtns */}
-        {isAllBtns.value && !query.isCbs && showGuideIndex.value && <TeacherTop></TeacherTop>}
-        {isAllBtnsStudent.value && !query.isCbs && showGuideIndex.value && <StudentTop></StudentTop>}
+        {/* {isAllBtns.value && !query.isCbs && showGuideIndex.value && <TeacherTop></TeacherTop>}
+        {isAllBtnsStudent.value && !query.isCbs && showGuideIndex.value && <StudentTop></StudentTop>} */}
+
+        {/* 练习模式功能引导 加载音频完成 不是会员 */}
+        {state.modeType === "practise" && !query.isCbs && state.audioDone && !state.isVip && <PractiseDriver />}
+        {/* 跟练模式功能引导 加载音频完成 不是会员 */}
+        {state.modeType === "follow" && !query.isCbs && state.audioDone && !state.isVip && <FollowDriver />}
+        {/* 评测模式功能引导 加载音频完成 不是会员 */}
+        {state.modeType === "evaluating" && !evaluatingData.earphoneMode && !query.isCbs && state.audioDone && !state.isVip && <EvaluatingDriver />}
+        {/* 评测模式-结果弹窗 功能引导 加载音频完成 不是会员 */}
+        {state.modeType === "evaluating" && evaluatingData.resulstMode && !evaluatingData.earphoneMode && !query.isCbs && state.audioDone && !state.isVip && <EvaluatingResultDriver />}
       </>
     );
   },

+ 101 - 110
src/page-instrument/header-top/modeView.tsx

@@ -1,116 +1,107 @@
-import { defineComponent, onMounted, watch, reactive, ref, nextTick } from "vue"
-import styles from "./index.module.less"
-import backImg from "./image/back.png"
-import nameImg from "./image/zt.png"
-import lxMode from "./image/lxMode.json"
-import glMode from "./image/glMode.json"
-import pcMode from "./image/pcMode.json"
-import { headTopData } from "./index"
-import TheVip from "../custom-plugins/the-vip"
-import { getQuery } from "/src/utils/queryString"
-import { storeData } from "/src/store"
-import state from "/src/state"
-import { studentQueryUserInfo } from "../api"
-import { usePageVisibility } from "@vant/use"
+import { defineComponent, onMounted, watch, reactive, ref, nextTick } from "vue";
+import styles from "./index.module.less";
+import backImg from "./image/back.png";
+import nameImg from "./image/zt.png";
+import lxMode from "./image/lxMode.json";
+import glMode from "./image/glMode.json";
+import pcMode from "./image/pcMode.json";
+import { headTopData } from "./index";
+import TheVip from "../custom-plugins/the-vip";
+import { getQuery } from "/src/utils/queryString";
+import { storeData } from "/src/store";
+import state from "/src/state";
+import { studentQueryUserInfo } from "../api";
+import { usePageVisibility } from "@vant/use";
 import { Vue3Lottie } from "vue3-lottie";
 
 export default defineComponent({
-   name: "modeView",
-   setup() {
-      const query = getQuery()
-      const data = reactive({
-         showPC: false,
-         showStudent: false,
-         showVip: false
-      })
-      const modeImgDom1 = ref()
-      const modeImgDom2 = ref()
-      const modeImgDom3 = ref()
-      const openGuid = () => {
-         // 加载后 判断 端口号 加载对应的引导
-         if (storeData.platformType !== "STUDENT" || storeData.user.clientType !== "STUDENT") {
-            // PC
-            data.showPC = true
-         } else {
-            // 从课堂乐器学生端课件预览默认不显示会员
-            if (storeData.user.vipMember || state.paymentType === "FREE" || query.showCourseMember === "true") {
-               // 学生端
-               data.showStudent = true
-            } else {
-               // vip
-               data.showVip = true
-            }
-         }
+  name: "modeView",
+  setup() {
+    const query = getQuery();
+    const data = reactive({
+      showPC: false,
+      showStudent: false,
+      showVip: false,
+    });
+    const modeImgDom1 = ref();
+    const modeImgDom2 = ref();
+    const modeImgDom3 = ref();
+    const openGuid = () => {
+      // 加载后 判断 端口号 加载对应的引导
+      if (storeData.platformType !== "STUDENT" || storeData.user.clientType !== "STUDENT") {
+        // PC
+        data.showPC = true;
+      } else {
+        // 从课堂乐器学生端课件预览默认不显示会员
+        if (storeData.user.vipMember || state.paymentType === "FREE" || query.showCourseMember === "true") {
+          // 学生端
+          data.showStudent = true;
+        } else {
+          // vip
+          data.showVip = true;
+        }
       }
+    };
+    const getUserInfo = async () => {
+      const res = await studentQueryUserInfo();
+      const student = res?.data || {};
+      storeData.user.vipMember = student.vipMember;
+      // console.log("🚀 ~ student:", student);
+      if (storeData.user.vipMember) {
+        data.showVip = false;
+        openGuid();
+      }
+    };
 
-      const getUserInfo = async () => {
-         const res = await studentQueryUserInfo()
-         const student = res?.data || {}
-         storeData.user.vipMember = student.vipMember
-         // console.log("🚀 ~ student:", student);
-         if (storeData.user.vipMember) {
-            data.showVip = false
-            openGuid()
-         }
+    const pageVisible = usePageVisibility();
+    watch(
+      () => pageVisible.value,
+      (val) => {
+        if (val === "visible") {
+          if (storeData.user.vipMember) return;
+          console.log("页面显示");
+          getUserInfo();
+        }
+      }
+    );
+    watch(
+      () => headTopData.modeType,
+      (value, oldValue) => {
+        // headTopData.modeType 值 刚开始是 ""  所以 第一次切换时候不触发播放动画
+        if (!oldValue) return;
+        nextTick(() => {
+          if (value === "show") {
+            modeImgDom1.value?.pause();
+            modeImgDom2.value?.pause();
+            modeImgDom3.value?.pause();
+          } else if (value === "init") {
+            modeImgDom1.value?.play();
+            modeImgDom2.value?.play();
+            modeImgDom3.value?.play();
+          }
+        });
       }
-      const pageVisible = usePageVisibility()
-      watch(
-         () => pageVisible.value,
-         val => {
-            if (val === "visible") {
-               if (storeData.user.vipMember) return
-               console.log("页面显示")
-               getUserInfo()
-            }
-         }
-      )
-      watch(() => headTopData.modeType, (value,oldValue) => {
-         // headTopData.modeType 值 刚开始是 ""  所以 第一次切换时候不触发播放动画
-         if(!oldValue) return
-         nextTick(()=>{
-            if(value === "show"){
-               modeImgDom1.value?.pause()
-               modeImgDom2.value?.pause()
-               modeImgDom3.value?.pause()
-            }else if(value === "init"){
-               modeImgDom1.value?.play()
-               modeImgDom2.value?.play()
-               modeImgDom3.value?.play()
-            }
-         })
-      })
-      onMounted(() => {
-         openGuid()
-      })
-      return () => (
-         <div class={[styles.modeView, headTopData.modeType !== "init" && styles.hidden]}>
-            <img
-               src={backImg}
-               class={styles.back}
-               onClick={() => {
-                  headTopData.modeType = "show"
-               }}
-            />
-            <img src={nameImg} class={styles.name} />
-            <div
-               class={[
-                  styles.modeBox,
-                  ((!state.isPercussion && !state.enableEvaluation) ||
-                     (state.isPercussion && state.enableEvaluation) ||
-                     (state.isPercussion && !state.enableEvaluation)) &&
-                     styles.twoModeBox
-               ]}
-            >
-               <Vue3Lottie ref={modeImgDom1} class={styles.modeImg} animationData={lxMode} autoPlay={false} loop={true} onClick={() => headTopData.handleChangeModeType("practise")}></Vue3Lottie>
-               {
-                  !state.isPercussion && <Vue3Lottie ref={modeImgDom2} class={styles.modeImg} animationData={glMode} autoPlay={false} loop={true} onClick={() => headTopData.handleChangeModeType("follow")}></Vue3Lottie>
-               }
-               {
-                  state.enableEvaluation && <Vue3Lottie ref={modeImgDom3} class={styles.modeImg} animationData={pcMode} autoPlay={false} loop={true} onClick={() => headTopData.handleChangeModeType("evaluating")}></Vue3Lottie>
-               }
-            </div>
-            {data.showVip && <TheVip />}
-         </div>
-      )
-   }
-})
+    );
+    onMounted(() => {
+      openGuid();
+    });
+    return () => (
+      <div class={[styles.modeView, headTopData.modeType !== "init" && styles.hidden]}>
+        <img
+          src={backImg}
+          class={styles.back}
+          onClick={() => {
+            headTopData.modeType = "show";
+          }}
+        />
+        <img src={nameImg} class={styles.name} />
+        <div class={[styles.modeBox, ((!state.isPercussion && !state.enableEvaluation) || (state.isPercussion && state.enableEvaluation) || (state.isPercussion && !state.enableEvaluation)) && styles.twoModeBox]}>
+          <Vue3Lottie ref={modeImgDom1} class={styles.modeImg} animationData={lxMode} autoPlay={false} loop={true} onClick={() => headTopData.handleChangeModeType("practise")}></Vue3Lottie>
+          {!state.isPercussion && <Vue3Lottie ref={modeImgDom2} class={styles.modeImg} animationData={glMode} autoPlay={false} loop={true} onClick={() => headTopData.handleChangeModeType("follow")}></Vue3Lottie>}
+          {state.enableEvaluation && <Vue3Lottie ref={modeImgDom3} class={styles.modeImg} animationData={pcMode} autoPlay={false} loop={true} onClick={() => headTopData.handleChangeModeType("evaluating")}></Vue3Lottie>}
+        </div>
+        {data.showVip && <TheVip />}
+      </div>
+    );
+  },
+});

+ 7 - 6
src/page-instrument/main.ts

@@ -10,18 +10,19 @@ import "../style.css";
 import App from "./App";
 import router from "./router";
 import "./theme.css";
+import "./custom-plugins/guide-driver/index.less"
 import { getQuery } from "/src/utils/queryString";
 
 (function () {
 	const query = getQuery();
 	const u = navigator.userAgent;
 	const instance: any =
-	(window as any).DAYA ||
-	(window as any).webkit?.messageHandlers?.DAYA ||
-	(window as any).COLEXIU ||
-	(window as any).webkit?.messageHandlers?.COLEXIU ||
-	(window as any).ORCHESTRA ||
-	(window as any).webkit?.messageHandlers?.ORCHESTRA;
+		(window as any).DAYA ||
+		(window as any).webkit?.messageHandlers?.DAYA ||
+		(window as any).COLEXIU ||
+		(window as any).webkit?.messageHandlers?.COLEXIU ||
+		(window as any).ORCHESTRA ||
+		(window as any).webkit?.messageHandlers?.ORCHESTRA;
 	const apiPrefix = query.isCbs ? "/cbs-app" : u.includes("COLEXIUSTUDENT") ? "/edu-app" : "/edu-app"
 	setStoreData({
 		isApp: instance ? true : false,

+ 66 - 29
src/page-instrument/view-evaluat-report/component/share-top/index.module.less

@@ -53,6 +53,7 @@
                     line-height: 20px;
                     padding: 0 !important;
                 }
+
                 .van-notice-bar__content {
                     color: #fff;
                 }
@@ -77,7 +78,7 @@
         text-align: center;
         padding: 0 12px;
         white-space: nowrap;
-        
+
         &>div:first-child {
             font-size: 12px;
             color: #333333;
@@ -146,7 +147,7 @@
     }
 
     .active {
-        background: linear-gradient( 270deg, rgba(27, 210, 255, 0.36) 0%, rgba(26, 173, 255, 0.36) 100%);
+        background: linear-gradient(270deg, rgba(27, 210, 255, 0.36) 0%, rgba(26, 173, 255, 0.36) 100%);
         border-radius: 8px;
 
         .mScore,
@@ -170,10 +171,14 @@
         font-size: 10px;
         line-height: 14px;
         font-weight: 400;
-        padding: 0 10px;
+        padding: 0 5px;
         color: #fff;
         cursor: pointer;
 
+        &+.btn {
+            margin-left: 10px;
+        }
+
         .iconBtn {
             display: block;
             width: 33px;
@@ -205,88 +210,108 @@
             right: -48px;
             background: url("./image/closeImg.png") no-repeat;
             background-size: 100% 100%;
-            &::before{
+
+            &::before {
                 display: none;
             }
         }
     }
 }
-.playerBox{
+
+.playerBox {
     width: 537px;
-    height: 314px; 
+    height: 314px;
     background: #FFF8F8;
     box-shadow: inset 4px -3px 6px 0px #B2E8FF;
     border-radius: 20px;
     padding: 14px;
+
     :global {
         .plyr {
             border-radius: 16px;
             width: 100%;
             height: 100%;
-            .plyr__control.plyr__control--overlaid{
+
+            .plyr__control.plyr__control--overlaid {
                 width: 48px;
                 height: 48px;
                 background: url("./image/midPlay.png") no-repeat;
                 background-size: 100% 100%;
-                .plyr__sr-only, svg{
+
+                .plyr__sr-only,
+                svg {
                     display: none;
                 }
             }
-            .plyr__controls{
+
+            .plyr__controls {
                 background: initial;
                 padding: 0 20px 13px;
-                .plyr__controls__item.plyr__control{
+
+                .plyr__controls__item.plyr__control {
                     padding: 0;
                     width: 18px;
                     height: 18px;
-                    &:hover{
+
+                    &:hover {
                         background: initial;
                     }
-                    .icon--pressed{
+
+                    .icon--pressed {
                         width: 100%;
                         height: 100%;
                         background: url("./image//pause.png") no-repeat;
                         background-size: 100% 100%;
-                        use{
+
+                        use {
                             display: none;
                         }
                     }
-                    .icon--not-pressed{
+
+                    .icon--not-pressed {
                         width: 100%;
                         height: 100%;
                         background: url("./image/play.png") no-repeat;
                         background-size: 100% 100%;
-                        use{
+
+                        use {
                             display: none;
                         }
                     }
                 }
-                .plyr__controls__item.plyr__progress__container{
+
+                .plyr__controls__item.plyr__progress__container {
                     margin-left: 9px;
-                    input[type=range]{
+
+                    input[type=range] {
                         color: #73C1FF;
                         height: 20px;
                     }
+
                     input[type="range"]::-webkit-slider-runnable-track {
                         height: 4px;
                     }
+
                     input[type="range"]::-webkit-slider-thumb {
                         width: 12px;
                         height: 12px;
                         margin-top: -4px;
                     }
-                    .plyr__progress__buffer{
+
+                    .plyr__progress__buffer {
                         height: 4px;
-                        color: rgba(115,193,255,0.8);
+                        color: rgba(115, 193, 255, 0.8);
                         background-color: #fff;
                         margin-top: -2px;
                     }
                 }
-                .plyr__controls__item.plyr__time{
+
+                .plyr__controls__item.plyr__time {
                     font-weight: 500;
                     font-size: 14px;
                     color: #FFFFFF;
-                    &.plyr__time--current{
+
+                    &.plyr__time--current {
                         margin-left: 9px;
                     }
                 }
@@ -294,33 +319,39 @@
             }
         }
     }
-    .videoBox{
+
+    .videoBox {
         width: 100%;
         height: 100%;
     }
-    .audioBox{
+
+    .audioBox {
         width: 100%;
         height: 100%;
         background: url("./image/audioBg.png") no-repeat;
         background-size: 100% 100%;
         position: relative;
         border-radius: 16px;
-        .audioBga{
+
+        .audioBga {
             width: 100%;
             height: 88%;
         }
-        .audioBga1{
+
+        .audioBga1 {
             position: absolute;
             left: 0;
             top: 8px;
             width: 146px;
-        }        
-        .audioBga2{
+        }
+
+        .audioBga2 {
             width: 268px;
             position: absolute;
             right: -24px;
             top: -8px;
         }
+
         :global {
             .plyr {
                 position: absolute;
@@ -329,11 +360,12 @@
                 bottom: 0;
             }
         }
-        .audioVisualizer{
+
+        .audioVisualizer {
             position: absolute;
             top: 50%;
             left: 50%;
-            transform: translate(-50%,-50%);
+            transform: translate(-50%, -50%);
             width: 370px;
             height: 66px;
         }
@@ -367,6 +399,7 @@
             margin-left: 4px;
             color: #fff;
         }
+
         &>i {
             width: 12px;
             height: 12px;
@@ -393,6 +426,7 @@
     max-width: 460px;
     padding: 10px;
     position: relative;
+
     &::before {
         content: "";
         position: absolute;
@@ -403,6 +437,7 @@
         background: #D5E8FF;
         border-radius: 14px;
     }
+
     .shiyiTop {
         position: absolute;
         width: 154px;
@@ -456,10 +491,12 @@
         &:nth-child(2n) {
             transform: translateX(20px);
         }
+
         span {
             font-size: 12px;
             font-weight: 400;
         }
+
         &>i {
             width: 12px;
             height: 12px;

+ 333 - 350
src/page-instrument/view-evaluat-report/component/share-top/index.tsx

@@ -20,65 +20,66 @@ import { Vue3Lottie } from "vue3-lottie";
 import audioBga from "./image/audioBga.json";
 import audioBga1 from "./image/leftCloud.json";
 import audioBga2 from "./image/rightCloud.json";
+import { EvaluatingReportDriver } from "/src/page-instrument/custom-plugins/guide-driver";
 
 type IItemType = "intonation" | "cadence" | "integrity";
 
 export default defineComponent({
-	name: "header-top",
-	props: {
-		scoreData: {
-			type: Object,
-			default: () => ({}),
-		},
-	},
-	setup(props, {expose}) {
-		const browserInfo = browser();
-		const { scoreData } = toRefs(props);
-		const shareData = reactive({
-			show: false,
-			shiyiShow: false,
-			isInitPlyr: false,
-			_plrl: null as any,
-		});
-		const lottieDom = ref<any>()
-		const lottieDom1 = ref<any>()
-		const lottieDom2 = ref<any>()
-		const level: any = {
-			BEGINNER: "入门级",
-			ADVANCED: "进阶级",
-			PERFORMER: "大师级",
-		};
-		// 颜色配置
-		const bgColors = {
-			high: '#FF66A6',
-			low: '#FFB900',
-			right: '#65FFAE',
-			wrong: '#DA3736',
-			lack: '#A5CBFF',
-			not: '#FFFFFF',
-			fast: '#B366FF',
-			slow: '#FF7B00'
-		}
-		// console.log("🚀 ~ scoreData:", scoreData.value)
-		const itemType = ref<IItemType>("intonation");
-		/** 返回 */
-		const handleBack = () => {
-			api_back();
-		};
+  name: "header-top",
+  props: {
+    scoreData: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
+  setup(props, { expose }) {
+    const browserInfo = browser();
+    const { scoreData } = toRefs(props);
+    const shareData = reactive({
+      show: false,
+      shiyiShow: false,
+      isInitPlyr: false,
+      _plrl: null as any,
+    });
+    const lottieDom = ref<any>();
+    const lottieDom1 = ref<any>();
+    const lottieDom2 = ref<any>();
+    const level: any = {
+      BEGINNER: "入门级",
+      ADVANCED: "进阶级",
+      PERFORMER: "大师级",
+    };
+    // 颜色配置
+    const bgColors = {
+      high: "#FF66A6",
+      low: "#FFB900",
+      right: "#65FFAE",
+      wrong: "#DA3736",
+      lack: "#A5CBFF",
+      not: "#FFFFFF",
+      fast: "#B366FF",
+      slow: "#FF7B00",
+    };
+    // console.log("🚀 ~ scoreData:", scoreData.value)
+    const itemType = ref<IItemType>("intonation");
+    /** 返回 */
+    const handleBack = () => {
+      api_back();
+    };
 
-		const handleChange = (type: IItemType) => {
-			itemType.value = type;
-			scoreData.value.itemType = type
-		};
+    const handleChange = (type: IItemType) => {
+      itemType.value = type;
+      scoreData.value.itemType = type;
+    };
 
-		// 资源类型
-		const mediaType = computed((): "audio" | "video" => {
-			const subfix = (scoreData.value.videoFilePath || "").split(".").pop();
-			if (subfix === "wav" || subfix === "mp3" || subfix === "m4a") {
-				return "audio";
-			}
-			return "video";
-		});
+    // 资源类型
+    const mediaType = computed((): "audio" | "video" => {
+      const subfix = (scoreData.value.videoFilePath || "").split(".").pop();
+      if (subfix === "wav" || subfix === "mp3" || subfix === "m4a") {
+        return "audio";
+      }
+      return "video";
+    });
 
 		const openAudioAndVideo = () => {
 			shareData.show = true;
@@ -227,37 +228,44 @@ export default defineComponent({
 					</div>
 				</div>
 
-				{/* 音准、节奏、完整度纬度 */}
-				
+    return () => (
+      <>
+        <div class={[styles.headerTop, browserInfo.android && styles.android]}>
+          <div class={styles.left}>
+            <div class={[styles.back, !storeData.isApp && styles.disabled]} onClick={handleBack}>
+              <img src={iconBack} />
+            </div>
+            <div class={styles.leftContent}>
+              {/* <div class={styles.lcName}>{state.examSongName}</div> */}
+              <Title class={styles.lcName} text={state.examSongName} rightView={false} />
+              <div class={styles.lcScore}>
+                {level[scoreData.value.heardLevel]}|综合分数:{scoreData.value.score}分
+              </div>
+            </div>
+          </div>
 
-				<div class={styles.middle}>
-					{
-					 	state.isPercussion ? null : 
-						<div 
-							onClick={() => handleChange("intonation")}
-							class={[styles.cItem, itemType.value === "intonation" && styles.active]}>
-							<span class={styles.mScore}>{scoreData.value.intonation}分</span>
-							<span class={styles.mLabel}>音准</span>
-						</div>					 
-					}
-					<div
-						onClick={() => handleChange("cadence")}
-						class={[styles.cItem, itemType.value === "cadence" && styles.active]}>
-						<span class={styles.mScore}>{scoreData.value.cadence}分</span>
-						<span class={styles.mLabel}>节奏</span>
-					</div>
-					{
-						state.isPercussion ? null : 
-						<div						
-							onClick={() => handleChange("integrity")}
-							class={[styles.cItem, itemType.value === "integrity" && styles.active]}>
-							<span class={styles.mScore}>{scoreData.value.integrity}分</span>
-							<span class={styles.mLabel}>完成度</span>
-						</div>
-					}
-				</div>
+          {/* 音准、节奏、完整度纬度 */}
+
+          <div class={styles.middle}>
+            {state.isPercussion ? null : (
+              <div onClick={() => handleChange("intonation")} class={[styles.cItem, "evaluting-report-1", itemType.value === "intonation" && styles.active]}>
+                <span class={styles.mScore}>{scoreData.value.intonation}分</span>
+                <span class={styles.mLabel}>音准</span>
+              </div>
+            )}
+            <div onClick={() => handleChange("cadence")} class={[styles.cItem, "evaluting-report-2", itemType.value === "cadence" && styles.active]}>
+              <span class={styles.mScore}>{scoreData.value.cadence}分</span>
+              <span class={styles.mLabel}>节奏</span>
+            </div>
+            {state.isPercussion ? null : (
+              <div onClick={() => handleChange("integrity")} class={[styles.cItem, "evaluting-report-3", itemType.value === "integrity" && styles.active]}>
+                <span class={styles.mScore}>{scoreData.value.integrity}分</span>
+                <span class={styles.mLabel}>完成度</span>
+              </div>
+            )}
+          </div>
 
-				{/* <div class={styles.center}>
+          {/* <div class={styles.center}>
 					<div class={styles.cItem}>
 						<div>{level[scoreData.value.heardLevel]}</div>
 						<div>难度</div>
@@ -293,279 +301,254 @@ export default defineComponent({
 					)}
 				</div> */}
 
-				<div class={styles.right}>
-					<div
-						style={{ display: scoreData.value.videoFilePath ? "" : "none" }}
-						class={styles.btn}
-						onClick={openAudioAndVideo}
-					>
-						<img class={styles.iconBtn} src={iconhuifang} />
-						<span>回放</span>
-					</div>
-					<div class={styles.btn} onClick={() => (shareData.shiyiShow = true)}>
-						<img class={styles.iconBtn} src={iconShiyi} />
-						<span>释义</span>
-					</div>
-					{/* <div class={styles.btn}>
+          <div class={styles.right}>
+            <div style={{ display: scoreData.value.videoFilePath ? "" : "none" }} class={[styles.btn, "evaluting-report-4"]} onClick={openAudioAndVideo}>
+              <img class={styles.iconBtn} src={iconhuifang} />
+              <span>回放</span>
+            </div>
+            <div class={styles.btn} onClick={() => (shareData.shiyiShow = true)}>
+              <img class={styles.iconBtn} src={iconShiyi} />
+              <span>释义</span>
+            </div>
+            {/* <div class={styles.btn}>
 						<img class={styles.iconBtn} src={iconhuifang} />
 						<span>再来一遍</span>
 					</div> */}
-				</div>
-
-				{/* 五线谱,简谱类型提示  */}
-				{
-					scoreData.value.musicType === 'staff' ? 
-					<>
-					{state.isPercussion ? null : (
-						<div class={styles.demos}>
-							{itemType.value === "intonation" && (
-								<>
-									<div>
-										{/* <Note fill="rgba(255, 102, 166, 1)" shadowFill="#FFAB25" shadow x={-2} y={0} /> */}
-										<Note fill="#FF66A6" />
-										<span>演奏偏高</span>
-									</div>
-									<div>
-										<Note fill="#FFB900" />
-										<span>演奏偏低</span>
-									</div>
-								</>
-							)}
-							{itemType.value === "cadence" && (
-								<>
-									<div>
-										<Note fill="#B366FF" />
-										<span>节奏偏快</span>
-									</div>
-									<div>
-										<Note fill="#FF7B00" />
-										<span>节奏偏慢</span>
-									</div>
-								</>
-							)}		
-							{(itemType.value === "intonation" || itemType.value === "cadence") && (
-								<>										
-									<div>
-										<Note fill="#65FFAE" />
-										<span>演奏正确</span>
-									</div>
-									<div>
-										<Note fill="#DA3736" />
-										<span>演奏错误</span>
-									</div>							
-								</>
-							)}
+          </div>
 
-							{(itemType.value === "intonation" || itemType.value === "integrity") && (
-								<div>
-									<Note fill="#A5CBFF" />
-									<span>时值不足</span>
-								</div>
-							)}
-							{
-								itemType.value === "integrity" && 
-								<div>
-									<Note fill="#65FFAE" />
-									<span>时值正确</span>
-								</div>								
-							}
-							<div>
-								<Note fill="#FFFFFF" />
-								<span>未演奏</span>
-							</div>
-						</div>
-					)}
-					</> : 
-					<>
-					{state.isPercussion ? null : (
-						<div class={styles.demos}>
-							{itemType.value === "intonation" && (
-								<>
-									<div>
-										{/* <img class={styles.firstIcon1} src={firstTop} /> */}
-										<i style={{ background: bgColors.high }}></i>
-										<span>演奏偏高</span>
-									</div>
-									<div>
-										<i style={{ background: bgColors.low }}></i>
-										<span>演奏偏低</span>
-									</div>
-								</>
-							)}	
-							{itemType.value === "cadence" && (
-								<>
-									<div>
-										<i style={{ background: bgColors.fast }}></i>
-										<span>节奏偏快</span>
-									</div>
-									<div>
-										<i style={{ background: bgColors.slow }}></i>
-										<span>节奏偏慢</span>
-									</div>
-								</>
-							)}								
-							{(itemType.value === "intonation" || itemType.value === "cadence") && (	
-								<>			
-									<div>
-										<i style={{ background: bgColors.right }}></i>
-										<span>演奏正确</span>
-									</div>
-									<div>
-										<i style={{ background: bgColors.wrong }}></i>
-										<span>演奏错误</span>
-									</div>							
-								</>
-							)}
-							{(itemType.value === "intonation" || itemType.value === "integrity") && (
-								<div>
-									<i style={{ background: bgColors.lack }}></i>
-									<span>时值不足</span>
-								</div>
-							)}
-							{
-								itemType.value === "integrity" && 
-								<div>
-									<i style={{ background: bgColors.right }}></i>
-									<span>时值正确</span>
-								</div>								
-							}							
-							<div>
-								<i style={{ background: bgColors.not }}></i>
-								<span>未演奏</span>
-							</div>
-						</div>
-					)}
-					</>							
-				}	
-				<Popup
-					teleport="body"
-					class={["popup-custom", "van-scale", styles.popup]}
-					transition="van-scale"
-					v-model:show={shareData.show}
-					closeable
-					onClose={() => {
-						shareData._plrl?.pause();
-					}}
-				>
-					<div class={styles.playerBox}>
-						{
-							mediaType.value === "audio" ? 
-							<div class={styles.audioBox}>
-								<canvas class={styles.audioVisualizer} id="audioVisualizer"></canvas>
-								<Vue3Lottie ref={lottieDom} class={styles.audioBga} animationData={audioBga} autoPlay={false} loop={true}></Vue3Lottie>
-								<Vue3Lottie ref={lottieDom1} class={styles.audioBga1} animationData={audioBga1} autoPlay={false} loop={true}></Vue3Lottie>
-								<Vue3Lottie ref={lottieDom2} class={styles.audioBga2} animationData={audioBga2} autoPlay={false} loop={true}></Vue3Lottie>
-								<audio
-									crossorigin="anonymous"
-									id="audioSrc"
-									src={scoreData.value.videoFilePath}
-									controls="false"
-									preload="metadata"
-									playsinline
-								/>
-							</div> : 
-							<video
-								id="videoSrc"
-								class={styles.videoBox}
-								src={scoreData.value.videoFilePath}
-								data-poster={videobg}
-								preload="metadata"
-								playsinline
-							/>
-						}
-					</div>
-				</Popup>
+          {/* 五线谱,简谱类型提示  */}
+          {scoreData.value.musicType === "staff" ? (
+            <>
+              {state.isPercussion ? null : (
+                <div class={styles.demos}>
+                  {itemType.value === "intonation" && (
+                    <>
+                      <div>
+                        {/* <Note fill="rgba(255, 102, 166, 1)" shadowFill="#FFAB25" shadow x={-2} y={0} /> */}
+                        <Note fill="#FF66A6" />
+                        <span>演奏偏高</span>
+                      </div>
+                      <div>
+                        <Note fill="#FFB900" />
+                        <span>演奏偏低</span>
+                      </div>
+                    </>
+                  )}
+                  {itemType.value === "cadence" && (
+                    <>
+                      <div>
+                        <Note fill="#B366FF" />
+                        <span>节奏偏快</span>
+                      </div>
+                      <div>
+                        <Note fill="#FF7B00" />
+                        <span>节奏偏慢</span>
+                      </div>
+                    </>
+                  )}
+                  {(itemType.value === "intonation" || itemType.value === "cadence") && (
+                    <>
+                      <div>
+                        <Note fill="#65FFAE" />
+                        <span>演奏正确</span>
+                      </div>
+                      <div>
+                        <Note fill="#DA3736" />
+                        <span>演奏错误</span>
+                      </div>
+                    </>
+                  )}
 
-				<Popup
-					v-model:show={shareData.shiyiShow}
-					class="popup-custom van-scale center-closeBtn shiyiBox"
-					transition="van-scale"
-					teleport="body"
-					closeable
-				>
-					<img onClick={() => shareData.shiyiShow = false }  class={styles.shiyiClose} src={shiyiClose} />
-					
-					{scoreData.value.musicType === 'staff' ?
-						<div class={styles.shiyiPopup}>
-							<img class={styles.shiyiTop} src={shiyiTop} />
-							<div class={styles.items}>
-								<div class={styles.item}>
-									{/* <Note fill="rgba(42, 188, 111, 1)" shadowFill="#FFAB25" shadow x={-2} y={0} /> */}
-									<Note fill="#FF66A6" />
-									<span>玫红色音符:演奏偏高</span>
-								</div>
-								<div class={styles.item}>
-									<Note fill="#4BED98" />
-									<span>绿色音符:演奏/时值正确</span>
-								</div>
-								<div class={styles.item}>
-									<Note fill="#FFB900" />
-									<span>黄色音符:演奏偏低</span>
-								</div>
-								<div class={styles.item}>
-									<Note fill="#DA3736" />
-									<span>红色音符:演奏错误</span>
-								</div>
-								<div class={styles.item}>
-									<Note fill="#B366FF" />
-									<span>紫色音符:节奏偏快</span>
-								</div>
-								<div class={styles.item}>
-									<Note fill="#A5CBFF" />
-									<span>浅蓝色音符:时值不足</span>
-								</div>
-								<div class={styles.item}>
-									<Note fill="#FF7B00" />
-									<span>橙色音符:节奏偏慢</span>
-								</div>
-								<div class={styles.item}>
-									<Note fill="#FFFFFF" />
-									<span>白色音符:未演奏</span>
-								</div>
-							</div>
-						</div> : 
-						<div class={styles.shiyiPopup}>
-							<img class={styles.shiyiTop} src={shiyiTop} />
-							<div class={styles.items}>
-								<div class={styles.itemTone}>
-									<i style={{ background: bgColors.high }}></i>
-									<span>玫红色音符:演奏偏高</span>
-								</div>
-								<div class={styles.itemTone}>
-									<i style={{ background: bgColors.right }}></i>
-									<span>绿色音符:演奏/时值正确</span>
-								</div>
-								<div class={styles.itemTone}>
-									<i style={{ background: bgColors.low }}></i>
-									<span>黄色音符:演奏偏低</span>
-								</div>
-								<div class={styles.itemTone}>
-									<i style={{ background: bgColors.wrong }}></i>
-									<span>红色音符:演奏错误</span>
-								</div>
-								<div class={styles.itemTone}>
-									<i style={{ background: bgColors.fast }}></i>
-									<span>紫色音符:节奏偏快</span>
-								</div>
-								<div class={styles.itemTone}>
-									<i style={{ background: bgColors.lack }}></i>
-									<span>浅蓝色音符:时值不足</span>
-								</div>
-								<div class={styles.itemTone}>
-									<i style={{ background: bgColors.slow }}></i>
-									<span>橙色音符:节奏偏慢</span>
-								</div>
-								<div class={styles.itemTone}>
-									<i style={{ background: bgColors.not }}></i>
-									<span>白色音符:未演奏</span>
-								</div>
-							</div>
-						</div>											
-					}
+                  {(itemType.value === "intonation" || itemType.value === "integrity") && (
+                    <div>
+                      <Note fill="#A5CBFF" />
+                      <span>时值不足</span>
+                    </div>
+                  )}
+                  {itemType.value === "integrity" && (
+                    <div>
+                      <Note fill="#65FFAE" />
+                      <span>时值正确</span>
+                    </div>
+                  )}
+                  <div>
+                    <Note fill="#FFFFFF" />
+                    <span>未演奏</span>
+                  </div>
+                </div>
+              )}
+            </>
+          ) : (
+            <>
+              {state.isPercussion ? null : (
+                <div class={styles.demos}>
+                  {itemType.value === "intonation" && (
+                    <>
+                      <div>
+                        {/* <img class={styles.firstIcon1} src={firstTop} /> */}
+                        <i style={{ background: bgColors.high }}></i>
+                        <span>演奏偏高</span>
+                      </div>
+                      <div>
+                        <i style={{ background: bgColors.low }}></i>
+                        <span>演奏偏低</span>
+                      </div>
+                    </>
+                  )}
+                  {itemType.value === "cadence" && (
+                    <>
+                      <div>
+                        <i style={{ background: bgColors.fast }}></i>
+                        <span>节奏偏快</span>
+                      </div>
+                      <div>
+                        <i style={{ background: bgColors.slow }}></i>
+                        <span>节奏偏慢</span>
+                      </div>
+                    </>
+                  )}
+                  {(itemType.value === "intonation" || itemType.value === "cadence") && (
+                    <>
+                      <div>
+                        <i style={{ background: bgColors.right }}></i>
+                        <span>演奏正确</span>
+                      </div>
+                      <div>
+                        <i style={{ background: bgColors.wrong }}></i>
+                        <span>演奏错误</span>
+                      </div>
+                    </>
+                  )}
+                  {(itemType.value === "intonation" || itemType.value === "integrity") && (
+                    <div>
+                      <i style={{ background: bgColors.lack }}></i>
+                      <span>时值不足</span>
+                    </div>
+                  )}
+                  {itemType.value === "integrity" && (
+                    <div>
+                      <i style={{ background: bgColors.right }}></i>
+                      <span>时值正确</span>
+                    </div>
+                  )}
+                  <div>
+                    <i style={{ background: bgColors.not }}></i>
+                    <span>未演奏</span>
+                  </div>
+                </div>
+              )}
+            </>
+          )}
+          <Popup
+            teleport="body"
+            class={["popup-custom", "van-scale", styles.popup]}
+            transition="van-scale"
+            v-model:show={shareData.show}
+            closeable
+            onClose={() => {
+              shareData._plrl?.pause();
+            }}
+          >
+            <div class={styles.playerBox}>
+              {mediaType.value === "audio" ? (
+                <div class={styles.audioBox}>
+                  <canvas class={styles.audioVisualizer} id="audioVisualizer"></canvas>
+                  <Vue3Lottie ref={lottieDom} class={styles.audioBga} animationData={audioBga} autoPlay={false} loop={true}></Vue3Lottie>
+                  <Vue3Lottie ref={lottieDom1} class={styles.audioBga1} animationData={audioBga1} autoPlay={false} loop={true}></Vue3Lottie>
+                  <Vue3Lottie ref={lottieDom2} class={styles.audioBga2} animationData={audioBga2} autoPlay={false} loop={true}></Vue3Lottie>
+                  <audio crossorigin="anonymous" id="audioSrc" src={scoreData.value.videoFilePath} controls="false" preload="metadata" playsinline />
+                </div>
+              ) : (
+                <video id="videoSrc" class={styles.videoBox} src={scoreData.value.videoFilePath} data-poster={videobg} preload="metadata" playsinline />
+              )}
+            </div>
+          </Popup>
 
+          <Popup v-model:show={shareData.shiyiShow} class="popup-custom van-scale center-closeBtn shiyiBox" transition="van-scale" teleport="body" closeable>
+            <img onClick={() => (shareData.shiyiShow = false)} class={styles.shiyiClose} src={shiyiClose} />
 
-				</Popup>
-			</div>
-		);
-	},
+            {scoreData.value.musicType === "staff" ? (
+              <div class={styles.shiyiPopup}>
+                <img class={styles.shiyiTop} src={shiyiTop} />
+                <div class={styles.items}>
+                  <div class={styles.item}>
+                    {/* <Note fill="rgba(42, 188, 111, 1)" shadowFill="#FFAB25" shadow x={-2} y={0} /> */}
+                    <Note fill="#FF66A6" />
+                    <span>玫红色音符:演奏偏高</span>
+                  </div>
+                  <div class={styles.item}>
+                    <Note fill="#4BED98" />
+                    <span>绿色音符:演奏/时值正确</span>
+                  </div>
+                  <div class={styles.item}>
+                    <Note fill="#FFB900" />
+                    <span>黄色音符:演奏偏低</span>
+                  </div>
+                  <div class={styles.item}>
+                    <Note fill="#DA3736" />
+                    <span>红色音符:演奏错误</span>
+                  </div>
+                  <div class={styles.item}>
+                    <Note fill="#B366FF" />
+                    <span>紫色音符:节奏偏快</span>
+                  </div>
+                  <div class={styles.item}>
+                    <Note fill="#A5CBFF" />
+                    <span>浅蓝色音符:时值不足</span>
+                  </div>
+                  <div class={styles.item}>
+                    <Note fill="#FF7B00" />
+                    <span>橙色音符:节奏偏慢</span>
+                  </div>
+                  <div class={styles.item}>
+                    <Note fill="#FFFFFF" />
+                    <span>白色音符:未演奏</span>
+                  </div>
+                </div>
+              </div>
+            ) : (
+              <div class={styles.shiyiPopup}>
+                <img class={styles.shiyiTop} src={shiyiTop} />
+                <div class={styles.items}>
+                  <div class={styles.itemTone}>
+                    <i style={{ background: bgColors.high }}></i>
+                    <span>玫红色音符:演奏偏高</span>
+                  </div>
+                  <div class={styles.itemTone}>
+                    <i style={{ background: bgColors.right }}></i>
+                    <span>绿色音符:演奏/时值正确</span>
+                  </div>
+                  <div class={styles.itemTone}>
+                    <i style={{ background: bgColors.low }}></i>
+                    <span>黄色音符:演奏偏低</span>
+                  </div>
+                  <div class={styles.itemTone}>
+                    <i style={{ background: bgColors.wrong }}></i>
+                    <span>红色音符:演奏错误</span>
+                  </div>
+                  <div class={styles.itemTone}>
+                    <i style={{ background: bgColors.fast }}></i>
+                    <span>紫色音符:节奏偏快</span>
+                  </div>
+                  <div class={styles.itemTone}>
+                    <i style={{ background: bgColors.lack }}></i>
+                    <span>浅蓝色音符:时值不足</span>
+                  </div>
+                  <div class={styles.itemTone}>
+                    <i style={{ background: bgColors.slow }}></i>
+                    <span>橙色音符:节奏偏慢</span>
+                  </div>
+                  <div class={styles.itemTone}>
+                    <i style={{ background: bgColors.not }}></i>
+                    <span>白色音符:未演奏</span>
+                  </div>
+                </div>
+              </div>
+            )}
+          </Popup>
+        </div>
+        <EvaluatingReportDriver videoFilePath={scoreData.value.videoFilePath} />
+      </>
+    );
+  },
 });

+ 375 - 395
src/page-instrument/view-evaluat-report/index.tsx

@@ -5,15 +5,11 @@ import state, { isRhythmicExercises, getMusicDetail, EnumMusicRenderType } from
 import { setGlobalData } from "../../utils";
 import MusicScore from "../../view/music-score";
 import styles from "./index.module.less";
-import {
-	api_cloudLoading,
-	api_setStatusBarVisibility,
-	isSpecialShapedScreen,
-} from "/src/helpers/communication";
+import { api_cloudLoading, api_setStatusBarVisibility, isSpecialShapedScreen } from "/src/helpers/communication";
 import { getQuery } from "/src/utils/queryString";
 import { mappingVoicePart, subjectFingering } from "/src/view/fingering/fingering-config";
 import { api_musicPracticeRecordDetail, sysMusicScoreAccompanimentQueryPage } from "../api";
-import { getMusicSheetDetail } from "/src/utils/baseApi"
+import { getMusicSheetDetail } from "/src/utils/baseApi";
 import ShareTop from "./component/share-top";
 import { addMeasureScore } from "/src/view/evaluating";
 import TopArrow from "./component/note/topArrow";
@@ -22,16 +18,15 @@ import LeftArrow from "./component/note/leftArrow";
 import RightArrow from "./component/note/rightArrow";
 
 const colorsClass: any = {
-	RIGHT: styles.right, // 正确
-	WRONG: styles.wrong, // 错误
-	NOT_PLAYED: styles.notPlay, // 未演奏
-	EARLY: styles.cadence_fast, // 节奏快
-	LATE: styles.cadence_slow, // 节奏慢
-	HIGH: styles.intonation_high, // 音准高
-	LOW: styles.intonation_low, // 音准低
-	DURATION_INSUFFICIENT: styles.integrity_wrong // 完整性(时值)不足
-}
-
+  RIGHT: styles.right, // 正确
+  WRONG: styles.wrong, // 错误
+  NOT_PLAYED: styles.notPlay, // 未演奏
+  EARLY: styles.cadence_fast, // 节奏快
+  LATE: styles.cadence_slow, // 节奏慢
+  HIGH: styles.intonation_high, // 音准高
+  LOW: styles.intonation_low, // 音准低
+  DURATION_INSUFFICIENT: styles.integrity_wrong, // 完整性(时值)不足
+};
 
 // const colorsClass: any = {
 // 	/** 音准 */
@@ -65,393 +60,378 @@ const colorsClass: any = {
 // }
 
 export default defineComponent({
-	name: "music-list",
-	setup() {
-		const query: any = getQuery();
-		const useedid = ref<string[]>([])
-		const scoreData = reactive({
-			videoFilePath: "", // 回放视频路径
-			cadence: 0,
-			integrity: 0,
-			intonation: 0,
-			score: 0,
-			heardLevel: "",
-			itemType: "intonation",
-			musicType: 'staff',
-		});
+  name: "music-list",
+  setup() {
+    const query: any = getQuery();
+    const useedid = ref<string[]>([]);
+    const scoreData = reactive({
+      videoFilePath: "", // 回放视频路径
+      cadence: 0,
+      integrity: 0,
+      intonation: 0,
+      score: 0,
+      heardLevel: "",
+      itemType: "intonation",
+      musicType: "staff",
+    });
+
+    const detailData = reactive({
+      isLoading: true,
+      paddingLeft: "",
+      headerHide: false,
+      musicalNotesPlayStats: [] as any[],
+      userMeasureScore: {} as any,
+    });
+    const getAPPData = async () => {
+      const screenData = await isSpecialShapedScreen();
+      if (screenData?.content) {
+        const { isSpecialShapedScreen, notchHeight } = screenData.content;
+        if (isSpecialShapedScreen) {
+          detailData.paddingLeft = 25 + "px";
+        }
+      }
+      // 普通webview 没有获取异性屏的方法
+      detailData.paddingLeft = 20 + "px";
+    };
+    onBeforeMount(() => {
+      getAPPData();
+      api_setStatusBarVisibility();
+    });
+    // console.log(route.params, query)
+    /** 获取曲谱数据 */
+    const getMusicInfo = (res: any) => {
+      const index = state.partIndex;
+      const musicInfo = {
+        ...res.data,
+        ...res.data.background[index],
+      };
+      // console.log("🚀 ~ musicInfo:", musicInfo);
+      setState(musicInfo, index);
+      setCustom();
+      detailData.isLoading = false;
+    };
+
+    const setState = (data: any, index: number) => {
+      // console.log("🚀 ~ data:", data)
+      state.scrollContainer = "scrollContainer";
+      state.detailId = data.id;
+      state.xmlUrl = data.xmlFileUrl;
+      state.partIndex = index;
+      state.subjectId = data.musicSubject;
+      state.categoriesId = data.categoriesId;
+      state.categoriesName = data.musicTagNames;
+      state.enableEvaluation = data.canEvaluate ? true : false;
+      state.examSongId = data.id + "";
+      state.examSongName = data.musicSheetName;
+      // 解析扩展字段
+      if (data.extConfigJson) {
+        try {
+          state.extConfigJson = JSON.parse(data.extConfigJson as string);
+        } catch (error) {
+          console.error("解析扩展字段错误:", error);
+        }
+      }
+      state.isOpenMetronome = data.mp3Type === "MP3_METRONOME" ? true : false;
+      state.needTick = data.isOpenMetronome;
+      state.isShowFingering = data.showFingering ? true : false;
+      state.music = data.audioFileUrl;
+      state.accompany = data.metronomeUrl || data.metronomeUrl;
+      state.midiUrl = data.midiUrl;
+      state.parentCategoriesId = data.musicTag;
+      state.playMode = data.audioType === "MP3" ? "MP3" : "MIDI";
+      state.originSpeed = state.speed = data.speed;
+      state.track = data.track;
+      state.enableNotation = data.notation ? true : false;
+
+      // 映射声部ID
+      state.subjectId = mappingVoicePart(state.subjectId as any, "ORCHESTRA");
+      // console.log("🚀 ~ state.subjectId:", state.subjectId);
+      // 是否打击乐
+      state.isPercussion = state.subjectId == 23 || state.subjectId == 113 || state.subjectId == 121 || isRhythmicExercises();
+
+      // 设置指法
+      state.fingeringInfo = subjectFingering(state.subjectId);
+      // console.log("🚀 ~ state.fingeringInfo:", state.fingeringInfo, state.subjectId, state.track)
+      // state.isOpenPrepare = true
+    };
+
+    const setCustom = () => {
+      if (state.extConfigJson.multitrack) {
+        setGlobalData("multitrack", state.extConfigJson.multitrack);
+      }
+    };
 
-		const detailData = reactive({
-			isLoading: true,
-			paddingLeft: "",
-			headerHide: false,
-			musicalNotesPlayStats: [] as any[],
-			userMeasureScore: {} as any,
-		});
-		const getAPPData = async () => {
-			const screenData = await isSpecialShapedScreen();
-			if (screenData?.content) {
-				const { isSpecialShapedScreen, notchHeight } = screenData.content;
-				if (isSpecialShapedScreen) {
-					detailData.paddingLeft = 25 + "px";
-				}
-			}
-			// 普通webview 没有获取异性屏的方法
-			detailData.paddingLeft = 20 + "px";
-		};
-		onBeforeMount(() => {
-			getAPPData();
-			api_setStatusBarVisibility();
-		});
-		// console.log(route.params, query)
-		/** 获取曲谱数据 */
-		const getMusicInfo = (res: any) => {
-			const index = state.partIndex;
-			const musicInfo = {
-				...res.data,
-				...res.data.background[index],
-			};
-			// console.log("🚀 ~ musicInfo:", musicInfo);
-			setState(musicInfo, index);
-			setCustom();
-			detailData.isLoading = false;
-		};
+    onMounted(async () => {
+      const res = await api_musicPracticeRecordDetail(query.id);
+      state.partIndex = Number(res?.data?.partIndex);
+      let resultData = {} as any;
+      try {
+        resultData = JSON.parse(res?.data?.scoreData);
+      } catch (error) {
+        console.error("解析评测结果:", error);
+      }
+      // console.log("🚀 ~ resultData:", resultData);
+      // @ts-ignore
+      // resultData.musicalNotesPlayStats?.notesData.forEach((item) => item.rhythmicAssessment.result = 'EARLY')
+      detailData.musicalNotesPlayStats = resultData.musicalNotesPlayStats?.notesData || [];
+      detailData.userMeasureScore = resultData.userMeasureScore || {};
 
-		const setState = (data: any, index: number) => {
-			// console.log("🚀 ~ data:", data)
-			state.scrollContainer = "scrollContainer";
-			state.detailId = data.id;
-			state.xmlUrl = data.xmlFileUrl;
-			state.partIndex = index;
-			state.subjectId = data.musicSubject;
-			state.categoriesId = data.categoriesId;
-			state.categoriesName = data.musicTagNames;
-			state.enableEvaluation = data.canEvaluate ? true : false;
-			state.examSongId = data.id + "";
-			state.examSongName = data.musicSheetName;
-			// 解析扩展字段
-			if (data.extConfigJson) {
-				try {
-					state.extConfigJson = JSON.parse(data.extConfigJson as string);
-				} catch (error) {
-					console.error("解析扩展字段错误:", error);
-				}
-			}
-			state.isOpenMetronome = data.mp3Type === "MP3_METRONOME" ? true : false;
-			state.needTick = data.isOpenMetronome;
-			state.isShowFingering = data.showFingering ? true : false;
-			state.music = data.audioFileUrl;
-			state.accompany = data.metronomeUrl || data.metronomeUrl;
-			state.midiUrl = data.midiUrl;
-			state.parentCategoriesId = data.musicTag;
-			state.playMode = data.audioType === "MP3" ? "MP3" : "MIDI";
-			state.originSpeed = state.speed = data.speed;
-			state.track = data.track;
-			state.enableNotation = data.notation ? true : false;
+      scoreData.heardLevel = res.data?.heardLevel;
+      scoreData.cadence = res.data?.cadence;
+      scoreData.integrity = res.data?.integrity;
+      scoreData.intonation = res.data?.intonation;
+      scoreData.score = res.data?.score;
+      scoreData.videoFilePath = res.data?.videoFilePath || res.data?.recordFilePath;
+      state.isEvaluatReport = true;
+      await getMusicDetail(resultData.musicalNotesPlayStats?.examSongId);
+      // 从练习记录进入评测报告,默认显示五线谱
+      // if (!query.musicRenderType) {
+      // 	state.musicRenderType = EnumMusicRenderType.staff
+      // }
+      // 评测报告展示什么类型的谱面
+      scoreData.musicType = query.musicRenderType ? query.musicRenderType : resultData.musicType ? resultData.musicType : state.musicRenderType;
+      detailData.isLoading = false;
+      // Promise.all([
+      // 	getMusicSheetDetail(resultData.musicalNotesPlayStats?.examSongId),
+      // ]).then((values) => {
+      // 	getMusicInfo(values[0]);
+      // });
+    });
 
-			// 映射声部ID
-			state.subjectId = mappingVoicePart(state.subjectId as any, "ORCHESTRA");
-			// console.log("🚀 ~ state.subjectId:", state.subjectId);
-			// 是否打击乐
-			state.isPercussion =
-				state.subjectId == 23 ||
-				state.subjectId == 113 ||
-				state.subjectId == 121 ||
-				isRhythmicExercises();
+    const getOffsetPosition = (type: keyof typeof colorsClass): string => {
+      // 五线谱
+      if (scoreData.musicType === "staff") {
+        switch (type) {
+          case "EARLY":
+            return "translateX(-3px)";
+          case "LATE":
+            return "translateX(3px)";
+          case "HIGH":
+            return "translateY(-2px)";
+          case "LOW":
+            return "translateY(2px)";
+          default:
+            return "";
+        }
+      } else {
+        switch (type) {
+          case "EARLY":
+            return "translateX(-3px)";
+          case "LATE":
+            return "translateX(3px)";
+          case "HIGH":
+            return "translateY(-2px)";
+          case "LOW":
+            return "translateY(-10px)";
+          default:
+            return "";
+        }
+      }
+    };
 
-			// 设置指法
-			state.fingeringInfo = subjectFingering(state.subjectId);
-			// console.log("🚀 ~ state.fingeringInfo:", state.fingeringInfo, state.subjectId, state.track)
-			// state.isOpenPrepare = true
-		};
+    const filterNotes = () => {
+      let include = ["RIGHT", "WRONG", "NOT_PLAYED"];
+      if (scoreData.itemType === "intonation") {
+        // 音准
+        include.push(...["HIGH", "LOW", "DURATION_INSUFFICIENT"]);
+      } else if (scoreData.itemType === "cadence") {
+        // 节奏
+        include.push(...["EARLY", "LATE"]);
+      } else if (scoreData.itemType === "integrity") {
+        // 完整性
+        include = ["DURATION_INSUFFICIENT", "RIGHT", "NOT_PLAYED"];
+      }
+      if (scoreData.itemType === "cadence") {
+        return detailData.musicalNotesPlayStats.filter((item: any) => include.includes(item.rhythmicAssessment.result));
+      } else {
+        return detailData.musicalNotesPlayStats.filter((item: any) => {
+          let result = item.pitchAssessment.result;
+          if (scoreData.itemType === "integrity") {
+            result = result === "HIGH" || result === "LOW" || result === "WRONG" ? "RIGHT" : result;
+          }
+          return include.includes(result);
+        });
+      }
+    };
 
-		const setCustom = () => {
-			if (state.extConfigJson.multitrack) {
-				setGlobalData("multitrack", state.extConfigJson.multitrack);
-			}
-		};
+    const setViewColor = () => {
+      clearViewColor();
+      const notes = filterNotes();
+      // console.log(1111,notes)
+      for (const note of notes) {
+        const active = state.times[note.index];
+        setTimeout(() => {
+          if (useedid.value.includes(active.id)) {
+            return;
+          }
+          useedid.value.push(active.id);
+          const svgEl = document.getElementById("vf-" + active.id);
+          const stemEl = document.getElementById("vf-" + active.id + "-stem");
+          let errType = scoreData.itemType === "cadence" ? note.rhythmicAssessment.result : note.pitchAssessment.result;
+          // console.log(1111222,errType)
+          const isNeedCopyElement = scoreData.itemType === "integrity" ? false : ["HIGH", "LOW", "EARLY", "LATE"].includes(errType);
+          if (scoreData.itemType === "integrity") {
+            errType = errType = note.pitchAssessment.result === "HIGH" || note.pitchAssessment.result === "LOW" || note.pitchAssessment.result === "WRONG" ? "RIGHT" : errType;
+          }
+          stemEl?.classList.add(colorsClass[errType]);
+          svgEl?.classList.add(colorsClass[errType]);
+          // console.log(123456,'添加颜色',errType)
+          // 评测过的音符,需要给小节添加背景色
+          if (errType !== "NOT_PLAYED") {
+            const staveNote = svgEl?.parentNode?.parentNode?.querySelector(".vf-stave");
+            if (staveNote) {
+              staveNote.querySelector(".vf-custom-bg")?.setAttribute("fill", "#132D4C");
+              staveNote.querySelector(".vf-custom-bot")?.setAttribute("fill", "#040D1E");
+            }
+          }
+          if (svgEl && isNeedCopyElement) {
+            stemEl?.classList.remove(colorsClass[errType]);
+            svgEl?.classList.remove(colorsClass[errType]);
+            let copySvg: any = null;
+            // 五线谱
+            if (scoreData.musicType === "staff") {
+              stemEl?.classList.add(colorsClass.RIGHT);
+              svgEl?.classList.add(colorsClass.RIGHT);
+              copySvg = svgEl.querySelector(".vf-notehead")!.cloneNode(true) as SVGSVGElement;
+            } else {
+              //copySvg = svgEl.querySelector('.vf-numbered-note-head')!.cloneNode(true) as SVGSVGElement
 
-		onMounted(async () => {
-			const res = await api_musicPracticeRecordDetail(query.id);
-			state.partIndex = Number(res?.data?.partIndex);
-			let resultData = {} as any;
-			try {
-				resultData = JSON.parse(res?.data?.scoreData);
-			} catch (error) {
-				console.error("解析评测结果:", error);
-			}
-			// console.log("🚀 ~ resultData:", resultData);
-			// @ts-ignore
-			// resultData.musicalNotesPlayStats?.notesData.forEach((item) => item.rhythmicAssessment.result = 'EARLY')
-			detailData.musicalNotesPlayStats = resultData.musicalNotesPlayStats?.notesData || [];
-			detailData.userMeasureScore = resultData.userMeasureScore || {};
+              if (isNeedCopyElement) {
+                svgEl?.classList.add(styles.inaccuracy);
+                const targetId = errType === "HIGH" ? "topSvg" : errType === "LOW" ? "bottomSvg" : errType === "EARLY" ? "leftSvg" : errType === "LATE" ? "rightSvg" : "";
+                copySvg = document.getElementById(targetId)!.cloneNode(true) as SVGSVGElement;
+                const { width, height } = svgEl.getBoundingClientRect() || {};
+                // @ts-ignore
+                let { x, y } = svgEl?.getBBox() || {};
+                x = errType === "HIGH" ? x + (width - 15) / 2 + 2 : errType === "LOW" ? x + (width - 15) / 2 + 2 : errType === "EARLY" ? x - Math.abs((width - 15) / 2) - 12 : errType === "LATE" ? x + width + 6 : x;
+                y = errType === "HIGH" ? y - Math.abs((height - 10) / 2) - 10 : errType === "LOW" ? y + height + 8 : errType === "EARLY" ? y + (height - 10) / 2 : errType === "LATE" ? y + (height - 10) / 2 : y;
+                copySvg.setAttribute("x", x);
+                copySvg.setAttribute("y", y);
+              }
 
-			scoreData.heardLevel = res.data?.heardLevel;
-			scoreData.cadence = res.data?.cadence;
-			scoreData.integrity = res.data?.integrity;
-			scoreData.intonation = res.data?.intonation;
-			scoreData.score = res.data?.score;
-			scoreData.videoFilePath = res.data?.videoFilePath || res.data?.recordFilePath;
-			state.isEvaluatReport = true;
-			await getMusicDetail(resultData.musicalNotesPlayStats?.examSongId);
-			// 从练习记录进入评测报告,默认显示五线谱
-			// if (!query.musicRenderType) {
-			// 	state.musicRenderType = EnumMusicRenderType.staff
-			// }
-			// 评测报告展示什么类型的谱面
-			scoreData.musicType = query.musicRenderType ? query.musicRenderType : resultData.musicType ? resultData.musicType : state.musicRenderType;
-			detailData.isLoading = false;
-			// Promise.all([
-			// 	getMusicSheetDetail(resultData.musicalNotesPlayStats?.examSongId),
-			// ]).then((values) => {
-			// 	getMusicInfo(values[0]);
-			// });
-		});
+              // console.log(x,y,copySvg.getBoundingClientRect())
+              // const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
+              // rect.setAttribute("x", 0 +'px');
+              // rect.setAttribute("y", 0+'px');
+              // rect.setAttribute("width", `50`);
+              // rect.setAttribute("height", `50`);
+              // rect.setAttribute("fill", "#FF4444");
+              // svgEl.prepend(rect);
+            }
+            if (scoreData.musicType === "staff") {
+              copySvg.style.transform = getOffsetPosition(errType);
+              //svgEl.style.opacity = '.7'
+              if (stemEl) {
+                //stemEl.style.opacity = '.7'
+              }
+            }
+            copySvg.id = "vf-" + active.id + "-copy";
+            copySvg?.classList.add(colorsClass[errType]);
+            // stemEl?.classList.add(colorsClass.RIGHT)
+            // @ts-ignore
+            state.osmd?.container.querySelector("svg")!.insertAdjacentElement("afterbegin", copySvg);
+            // svgEl?.parentElement?.appendChild(copySvg)
+          }
+        }, 300);
+      }
+    };
 
+    const removeClass = (el?: HTMLElement | null) => {
+      if (!el) return;
+      const classList = el.classList.values();
+      for (const val of classList) {
+        if (val?.indexOf("vf-") !== 0) {
+          el.classList.remove(val);
+        }
+      }
+    };
 
-		const getOffsetPosition = (type: keyof typeof colorsClass): string => {
-			// 五线谱
-			if (scoreData.musicType === 'staff') {
-				switch (type) {
-					case 'EARLY':
-					  return 'translateX(-3px)'
-					case 'LATE':
-					  return 'translateX(3px)'
-					case 'HIGH':
-					  return 'translateY(-2px)'
-					case 'LOW':
-					  return 'translateY(2px)'
-					default:
-					  return ''
-				  }
-			} else {
-				switch (type) {
-					case 'EARLY':
-					  return 'translateX(-3px)'
-					case 'LATE':
-					  return 'translateX(3px)'
-					case 'HIGH':
-					  return 'translateY(-2px)'
-					case 'LOW':
-					  return 'translateY(-10px)'
-					default:
-					  return ''
-				  }
-			}
-		  }
-	  
-		  const filterNotes = () => {
-			let include = ['RIGHT', 'WRONG', 'NOT_PLAYED']
-			if (scoreData.itemType === 'intonation') { // 音准
-			  include.push(...['HIGH', 'LOW', 'DURATION_INSUFFICIENT'])
-			} else if (scoreData.itemType === 'cadence') { // 节奏
-			  include.push(...['EARLY', 'LATE'])
-			} else if (scoreData.itemType === 'integrity') { // 完整性
-			  include = ['DURATION_INSUFFICIENT', 'RIGHT', 'NOT_PLAYED']
-			}
-			if (scoreData.itemType === 'cadence') {
-				return detailData.musicalNotesPlayStats.filter((item: any) => include.includes(item.rhythmicAssessment.result))
-			} else {
-				return detailData.musicalNotesPlayStats.filter((item: any) => {
-					let result = item.pitchAssessment.result
-					if (scoreData.itemType === 'integrity') {
-						result = (result === 'HIGH' || result === 'LOW' || result === 'WRONG') ? 'RIGHT' : result
-					}
-					return include.includes(result)
-				})
-			}
-		  }
-	  
-		  const setViewColor = () => {
-			clearViewColor()
-			const notes = filterNotes()
-			// console.log(1111,notes)
-			for (const note of notes) {
-			  const active = state.times[note.index]
-			  setTimeout(() => {
-				if (useedid.value.includes(active.id)) {
-				  return
-				}
-				useedid.value.push(active.id)
-				const svgEl = document.getElementById('vf-' + active.id)
-				const stemEl = document.getElementById('vf-' + active.id + '-stem')
-				let errType = scoreData.itemType === 'cadence' ? note.rhythmicAssessment.result : note.pitchAssessment.result
-				// console.log(1111222,errType)
-				const isNeedCopyElement = scoreData.itemType === 'integrity' ? false : ['HIGH', 'LOW', 'EARLY', 'LATE'].includes(
-				  errType
-				)
-				if (scoreData.itemType === 'integrity') {
-					errType = errType = (note.pitchAssessment.result === 'HIGH' || note.pitchAssessment.result === 'LOW' || note.pitchAssessment.result === 'WRONG') ? 'RIGHT' : errType
-				}
-				stemEl?.classList.add(colorsClass[errType])
-				svgEl?.classList.add(colorsClass[errType])
-				// console.log(123456,'添加颜色',errType)
-				// 评测过的音符,需要给小节添加背景色
-				if (errType !== 'NOT_PLAYED') {
-					const staveNote = svgEl?.parentNode?.parentNode?.querySelector('.vf-stave')
-					if (staveNote) {
-						staveNote.querySelector('.vf-custom-bg')?.setAttribute("fill", "#132D4C")
-						staveNote.querySelector('.vf-custom-bot')?.setAttribute("fill", "#040D1E")
-					}
-				}
-				if (svgEl && isNeedCopyElement) {
-				  stemEl?.classList.remove(colorsClass[errType])
-				  svgEl?.classList.remove(colorsClass[errType])
-				  let copySvg: any = null;
-				  // 五线谱
-				  if (scoreData.musicType === 'staff') {
-					stemEl?.classList.add(colorsClass.RIGHT)
-					svgEl?.classList.add(colorsClass.RIGHT)
-					copySvg = svgEl.querySelector('.vf-notehead')!.cloneNode(true) as SVGSVGElement
-				  } else {
-					//copySvg = svgEl.querySelector('.vf-numbered-note-head')!.cloneNode(true) as SVGSVGElement
-					
-					if (isNeedCopyElement) {
-						svgEl?.classList.add(styles.inaccuracy)
-						const targetId = errType === 'HIGH' ? 'topSvg' : errType === 'LOW' ? 'bottomSvg' : errType === 'EARLY' ? 'leftSvg' : errType === 'LATE' ? 'rightSvg' : ''
-						copySvg = document.getElementById(targetId)!.cloneNode(true) as SVGSVGElement
-						const { width, height } = svgEl.getBoundingClientRect() || {}
-						// @ts-ignore
-						let { x, y } = svgEl?.getBBox() || {}
-						x = errType === 'HIGH' ? x + (width - 15)/2 + 2 : errType === 'LOW' ? x + (width - 15)/2 + 2 : errType === 'EARLY' ? x - Math.abs((width - 15)/2) - 12  : errType === 'LATE' ? x + width + 6 : x
-						y = errType === 'HIGH' ? y - Math.abs((height-10)/2) - 10 : errType === 'LOW' ? y + height + 8 : errType === 'EARLY' ? y + (height - 10)/2 : errType === 'LATE' ? y + (height - 10)/2 : y
-						copySvg.setAttribute("x", x)
-						copySvg.setAttribute("y", y)
-					}
+    const clearViewColor = () => {
+      for (const id of useedid.value) {
+        removeClass(document.getElementById("vf-" + id));
+        removeClass(document.getElementById("vf-" + id + "-stem"));
+        const qid = "vf-" + id + "-copy";
+        const copyEl = document.getElementById(qid);
+        if (copyEl) {
+          copyEl.remove();
+        }
+      }
+      useedid.value = [];
+    };
 
-					// console.log(x,y,copySvg.getBoundingClientRect())
-					// const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
-					// rect.setAttribute("x", 0 +'px');
-					// rect.setAttribute("y", 0+'px');
-					// rect.setAttribute("width", `50`);
-					// rect.setAttribute("height", `50`);
-					// rect.setAttribute("fill", "#FF4444");
-					// svgEl.prepend(rect);			
-				  }
-				  if (scoreData.musicType === 'staff') {
-					copySvg.style.transform = getOffsetPosition(errType)
-					//svgEl.style.opacity = '.7'
-					if (stemEl) {
-					  //stemEl.style.opacity = '.7'
-					}
-				  }
-				  copySvg.id = 'vf-' + active.id + '-copy'
-				  copySvg?.classList.add(colorsClass[errType])
-				  // stemEl?.classList.add(colorsClass.RIGHT)
-				  // @ts-ignore
-				  state.osmd?.container.querySelector('svg')!.insertAdjacentElement('afterbegin', copySvg)
-				  // svgEl?.parentElement?.appendChild(copySvg)
-				}
-			  }, 300)
-			}
-		  }
-	  
-		  const removeClass = (el?: HTMLElement | null) => {
-			if (!el) return
-			const classList = el.classList.values()
-			for (const val of classList) {
-			  if (val?.indexOf('vf-') !== 0) {
-				el.classList.remove(val)
-			  }
-			}
-		  }
-	  
-		  const clearViewColor = () => {
-			for (const id of useedid.value) {
-			  removeClass(document.getElementById('vf-' + id))
-			  removeClass(document.getElementById('vf-' + id + '-stem'))
-			  const qid = 'vf-' + id + '-copy'
-			  const copyEl = document.getElementById(qid)
-			  if (copyEl) {
-				copyEl.remove()
-			  }
-			}
-			useedid.value = []
-		  }
-		
-		const setPathColor = () => {
-			console.log(11111,detailData.musicalNotesPlayStats,scoreData.itemType)
-			for (const note of detailData.musicalNotesPlayStats) {
-				const active = state.times[note.index];
-				const svgEl = active?.id ? document.getElementById("vf-" + active?.id) : null;
-				switch (scoreData.itemType) {
-					case "intonation":
-						svgEl?.classList.add(colorsClass.pitch[note.pitchAssessment.result]);
-						break;
-					case "cadence":
-						svgEl?.classList.add(colorsClass.rhythmic[note.rhythmicAssessment.result]);
-						break;	
-					case "integrity":
-						svgEl?.classList.add(colorsClass.pitch[note.pitchAssessment.result]);
-						break;								
-					default:
-						break;
-				}
-			}
-		};
-		const setMearureColor = () => {
-			for (let key in detailData.userMeasureScore) {
-				addMeasureScore(detailData.userMeasureScore[key], false);
-			}
-		};
+    const setPathColor = () => {
+      console.log(11111, detailData.musicalNotesPlayStats, scoreData.itemType);
+      for (const note of detailData.musicalNotesPlayStats) {
+        const active = state.times[note.index];
+        const svgEl = active?.id ? document.getElementById("vf-" + active?.id) : null;
+        switch (scoreData.itemType) {
+          case "intonation":
+            svgEl?.classList.add(colorsClass.pitch[note.pitchAssessment.result]);
+            break;
+          case "cadence":
+            svgEl?.classList.add(colorsClass.rhythmic[note.rhythmicAssessment.result]);
+            break;
+          case "integrity":
+            svgEl?.classList.add(colorsClass.pitch[note.pitchAssessment.result]);
+            break;
+          default:
+            break;
+        }
+      }
+    };
+    const setMearureColor = () => {
+      for (let key in detailData.userMeasureScore) {
+        addMeasureScore(detailData.userMeasureScore[key], false);
+      }
+    };
 
-		/** 渲染完成 */
-		const handleRendered = (osmd: any) => {
-			state.musicRendered = true;
-			state.osmd = osmd;
-			state.times = formateTimes(osmd);
-			console.log("🚀 ~ state.times:", state.times);
-			// @ts-ignore
-			const beams =  Array.from(new Set(document.getElementsByClassName('vf-beam')))
-			beams.forEach((item: any) => {
-				item.classList.add(styles.beam)
-			})
-			//setPathColor();
-			setViewColor();
-			// setMearureColor();
-			api_cloudLoading();
-		};
-		watch(
-			() => scoreData.itemType,
-			() => {
-				setViewColor();
-			}
-		);
-		return () => (
-			<div
-				class={[styles.detail, state.setting.eyeProtection && "eyeProtection", styles.shareBox]}
-				style={{ paddingLeft: detailData.paddingLeft }}
-			>
-				<Transition name="van-fade">
-					{!state.musicRendered && (
-						<div class={styles.skeleton}>
-							<Skeleton class={styles.skeleton} row={8} />
-						</div>
-					)}
-				</Transition>
-				<div
-					class={[styles.headHeight, detailData.headerHide && styles.headHide]}
-					onClick={(e: Event) => e.stopPropagation()}
-				>
-					<Transition name="van-slide-down">
-						{state.musicRendered && <ShareTop scoreData={scoreData} />}
-					</Transition>
-				</div>
-				<div
-					id="scrollContainer"
-					class={[styles.container, !state.setting.displayCursor && "hideCursor"]}
-				>
-					{/* 曲谱渲染 */}
-					{!detailData.isLoading && <MusicScore musicColor={'#FFFFFF'} onRendered={handleRendered} />}
-					{
-						<div class={styles.arrowSvg}>
-							<TopArrow />
-							<BottomArrow />
-							<LeftArrow />
-							<RightArrow />
-						</div>
-					}
-				</div>
-			</div>
-		);
-	},
-});
+    /** 渲染完成 */
+    const handleRendered = (osmd: any) => {
+      state.musicRendered = true;
+      state.osmd = osmd;
+      state.times = formateTimes(osmd);
+      console.log("🚀 ~ state.times:", state.times);
+      // @ts-ignore
+      const beams = Array.from(new Set(document.getElementsByClassName("vf-beam")));
+      beams.forEach((item: any) => {
+        item.classList.add(styles.beam);
+      });
+      //setPathColor();
+      setViewColor();
+      // setMearureColor();
+      api_cloudLoading();
+    };
+    watch(
+      () => scoreData.itemType,
+      () => {
+        setViewColor();
+      }
+    );
+    return () => (
+      <div class={[styles.detail, state.setting.eyeProtection && "eyeProtection", styles.shareBox]} style={{ paddingLeft: detailData.paddingLeft }}>
+        <Transition name="van-fade">
+          {!state.musicRendered && (
+            <div class={styles.skeleton}>
+              <Skeleton class={styles.skeleton} row={8} />
+            </div>
+          )}
+        </Transition>
+        <div class={[styles.headHeight, detailData.headerHide && styles.headHide]} onClick={(e: Event) => e.stopPropagation()}>
+          <Transition name="van-slide-down">{state.musicRendered && <ShareTop scoreData={scoreData} />}</Transition>
+        </div>
+        <div id="scrollContainer" class={[styles.container, !state.setting.displayCursor && "hideCursor"]}>
+          {/* 曲谱渲染 */}
+          {!detailData.isLoading && <MusicScore musicColor={"#FFFFFF"} onRendered={handleRendered} />}
+          {
+            <div class={styles.arrowSvg}>
+              <TopArrow />
+              <BottomArrow />
+              <LeftArrow />
+              <RightArrow />
+            </div>
+          }
+        </div>
+      </div>
+    );
+  },
+});

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 494 - 507
src/page-instrument/view-figner/index.tsx


+ 63 - 61
src/state.ts

@@ -15,7 +15,7 @@ import { getMusicSheetDetail } from "./utils/baseApi"
 import { getQuery } from "/src/utils/queryString";
 import { followData, skipNotePractice } from "/src/view/follow-practice/index"
 import { changeSongSourceByBate } from "/src/view/audio-list"
-import { moveSmoothAnimation, smoothAnimationState, moveSmoothAnimationByPlayTime, moveTranslateXNum, destroySmoothAnimation} from "/src/page-instrument/view-detail/smoothAnimation"
+import { moveSmoothAnimation, smoothAnimationState, moveSmoothAnimationByPlayTime, moveTranslateXNum, destroySmoothAnimation } from "/src/page-instrument/view-detail/smoothAnimation"
 import { storeData } from "/src/store";
 import { downloadXmlStr } from "./view/music-score"
 import { musicScoreRef } from "/src/page-instrument/view-detail/index"
@@ -268,6 +268,7 @@ const state = reactive({
   /** 声部ID */
   subjectId: 0 as number,
   trackId: 0 as string | number,
+  isVip: false, // 是否会员
   /** 分类ID */
   categoriesId: 0,
   /** 分类名称 */
@@ -289,7 +290,7 @@ const state = reactive({
   /** 扩展样式字段 */
   extStyleConfigJson: {} as any,
   /** 是否开启节拍器(mp3节拍器) */
-  isOpenMetronome: false,  
+  isOpenMetronome: false,
   /** 演唱模式是否开启节拍器(mp3节拍器) */
   isSingOpenMetronome: false,
   /** 是否显示指法 */
@@ -303,7 +304,7 @@ const state = reactive({
   /** 伴唱 */
   banSong: "",
   /** 唱名 */
-  mingSong: "", 
+  mingSong: "",
   /** 节拍器音乐资源 */
   beatSong: {
     music: "",
@@ -333,7 +334,7 @@ const state = reactive({
   /** 当前显示声部索引 */
   partIndex: 0,
   /** 演奏是否需要节拍器 */
-  needTick: false, 
+  needTick: false,
   /** 演唱模式是否需要节拍器 */
   needSingTick: false,
   /** 曲谱实例 */
@@ -538,7 +539,7 @@ export const setStep = () => {
 };
 /** 开始播放 */
 export const onPlay = () => {
-  console.log("开始播放",'音频总时长:',getAudioDuration());
+  console.log("开始播放", '音频总时长:', getAudioDuration());
   state.playEnd = false;
   // offset_duration = browserInfo.xiaomi ? 0.2 : 0.08;
   offset_duration = 0.2;
@@ -567,7 +568,7 @@ export const onEnded = () => {
   if (state.playEnd) {
     console.log('音频播放结束,无需再次执行')
     return
-  }  
+  }
   // 修改状态为结束
   state.playEnd = true;
   state.playState = "paused";
@@ -699,7 +700,7 @@ export const togglePlay = async (playState?: "play" | "paused", sourceType?: str
     return
   }
   // 播放之前  当为评测模式和不为MIDI时候按  是否禁用节拍器  切换音源
-  if ((playState ? playState : state.playState === "paused" ? "play" : "paused") ==='play' && state.modeType === "practise" && state.playMode !== "MIDI") {
+  if ((playState ? playState : state.playState === "paused" ? "play" : "paused") === 'play' && state.modeType === "practise" && state.playMode !== "MIDI") {
     console.log("设置音源")
     changeSongSourceByBate(metronomeData.disable)
   }
@@ -727,14 +728,14 @@ export const togglePlay = async (playState?: "play" | "paused", sourceType?: str
   if (state.playState === "play" && state.sectionStatus && state.section.length == 2 && state.playProgress === 0) {
     resetPlaybackToStart();
   }
-   // 当在节拍器播放期间暂停的话 就暂停节拍器
-  if(state.playState === "paused") {
+  // 当在节拍器播放期间暂停的话 就暂停节拍器
+  if (state.playState === "paused") {
     closeTick()
   }
   // 设置为开始播放时, 如果需要节拍,先播放节拍器   只有在当前播放时间不为0的时候开启节拍器
-  if (state.playState === "play" && getAudioCurrentTime()===0 && ((state.playType==="play"&&state.needTick)||(state.playType==="sing"&&state.needSingTick))) {
+  if (state.playState === "play" && getAudioCurrentTime() === 0 && ((state.playType === "play" && state.needTick) || (state.playType === "sing" && state.needSingTick))) {
     // 如果是系统节拍器 等系统节拍器播完了再播,如果是mp3节拍器 直接播
-    if((state.playType==="play" && !state.isOpenMetronome)||(state.playType==="sing" && !state.isSingOpenMetronome)){
+    if ((state.playType === "play" && !state.isOpenMetronome) || (state.playType === "sing" && !state.isSingOpenMetronome)) {
       const tickend = await handleStartTick();
       // console.log("🚀 ~ tickend:", tickend)
       // 节拍器返回false, 取消播放
@@ -742,7 +743,7 @@ export const togglePlay = async (playState?: "play" | "paused", sourceType?: str
         state.playState = "paused";
         return false;
       }
-    }else{
+    } else {
       handleStartTick()
     }
   }
@@ -1230,48 +1231,48 @@ const getMusicInfo = async (res: any) => {
 function xmlToTracks(xmlString: string) {
   const xmlParse = new DOMParser().parseFromString(xmlString, "text/xml");
   const partNames = Array.from(xmlParse.getElementsByTagName('part-name'));
-	return partNames.reduce((arr:string[], item)=>{
+  return partNames.reduce((arr: string[], item) => {
     const textContent = item?.textContent?.trim()
-    if(textContent !== "COMMON" && textContent){
+    if (textContent !== "COMMON" && textContent) {
       arr.push(textContent)
     }
     return arr
-  },[]);
+  }, []);
 }
 // 设置音源
-function initMusicSource (data: any, track?:string) {
+function initMusicSource(data: any, track?: string) {
   const { instrumentId } = storeData.user
   let { musicSheetType, isAllSubject, musicSheetSoundList, musicSheetAccompanimentList } = data
   musicSheetSoundList || (musicSheetSoundList = [])
   musicSheetAccompanimentList || (musicSheetAccompanimentList = [])
-  let musicObj,accompanyObj,fanSongObj,banSongObj
+  let musicObj, accompanyObj, fanSongObj, banSongObj
   // 独奏
-  if(musicSheetType === "SINGLE") {
+  if (musicSheetType === "SINGLE") {
     // 适用声部(instrumentId)为true 时候没有乐器只有一个原音;当前用户有乐器就匹配  不然取第一个原音
-    musicObj = musicSheetSoundList.find((item:any) => {
-      return (isAllSubject||!instrumentId)?item.audioPlayType === "PLAY":(item.audioPlayType === "PLAY"&&item.musicalInstrumentId ==instrumentId)
+    musicObj = musicSheetSoundList.find((item: any) => {
+      return (isAllSubject || !instrumentId) ? item.audioPlayType === "PLAY" : (item.audioPlayType === "PLAY" && item.musicalInstrumentId == instrumentId)
     })
     // 因为可能根据学生的乐器id也找不到曲目所以尝试取第一个
-    musicObj || (musicObj = musicSheetSoundList.find((item:any) => {
+    musicObj || (musicObj = musicSheetSoundList.find((item: any) => {
       return item.audioPlayType === "PLAY"
     }))
-    fanSongObj = musicSheetSoundList.find((item:any) => {
+    fanSongObj = musicSheetSoundList.find((item: any) => {
       return item.audioPlayType === "SING"
     })
-    banSongObj = musicSheetAccompanimentList.find((item:any) => {
+    banSongObj = musicSheetAccompanimentList.find((item: any) => {
       return item.audioPlayType === "SING"
     })
   } else {
     // 合奏  合奏根据声轨来区分原音
-    musicObj = track?musicSheetSoundList.find((item:any) => {
+    musicObj = track ? musicSheetSoundList.find((item: any) => {
       return item.track === track
-    }):musicSheetSoundList[0]
+    }) : musicSheetSoundList[0]
   }
-  accompanyObj = musicSheetAccompanimentList.find((item:any) => {
+  accompanyObj = musicSheetAccompanimentList.find((item: any) => {
     return item.audioPlayType === "PLAY"
   })
   // 当没有任何曲目的时候报错
-  if(!musicObj?.audioFileUrl&&!accompanyObj?.audioFileUrl&&!fanSongObj?.audioFileUrl&&!banSongObj?.audioFileUrl&&!fanSongObj?.solmizationFileUrl){
+  if (!musicObj?.audioFileUrl && !accompanyObj?.audioFileUrl && !fanSongObj?.audioFileUrl && !banSongObj?.audioFileUrl && !fanSongObj?.solmizationFileUrl) {
     throw new Error("该曲目无任何音源");
   }
   Object.assign(state, {
@@ -1280,7 +1281,7 @@ function initMusicSource (data: any, track?:string) {
     fanSong: fanSongObj?.audioFileUrl,
     banSong: banSongObj?.audioFileUrl,
     mingSong: fanSongObj?.solmizationFileUrl
-  })  
+  })
   Object.assign(state.beatSong, {
     music: musicObj?.audioBeatMixUrl,
     accompany: accompanyObj?.audioBeatMixUrl,
@@ -1289,7 +1290,7 @@ function initMusicSource (data: any, track?:string) {
     mingSong: fanSongObj?.solmizationBeatUrl
   })
   return musicObj
-}  
+}
 const setState = (data: any, index: number) => {
   state.appName = "COLEXIU";
   state.detailId = data.bizId;
@@ -1349,11 +1350,12 @@ const setState = (data: any, index: number) => {
   const track = data.code || data.track;
   state.track = track ? track.replace(/ /g, "").toLocaleLowerCase() : "";
   // 能否评测,根据当前声轨有无伴奏判断
-  if (state.isAppPlay) {
-    state.enableEvaluation = state.midiUrl ? true : false
-  } else {
-    state.enableEvaluation = state.accompany ? true : false
-  }
+  // if (state.isAppPlay) {
+  //   state.enableEvaluation = state.midiUrl ? true : false
+  // } else {
+  //   state.enableEvaluation = state.accompany ? true : false
+  // }
+  state.enableEvaluation = true
   state.isConcert = data.musicSheetType === "CONCERT" ? true : false;
   // multiTracksSelection 返回为空,默认代表全部分轨
   state.canSelectTracks = data.multiTracksSelection === "null" || data.multiTracksSelection === "" || data.multiTracksSelection === null ? [] : data.multiTracksSelection?.split(',');
@@ -1476,10 +1478,10 @@ export const followBeatPaly = () => {
 // 音符添加bbox
 export const addNoteBBox = (list: any[]) => {
   const musicContainer = document.getElementById("musicAndSelection")?.getBoundingClientRect() || {
-		x: 0,
-		y: 0,
-	};
-	const parentLeft = musicContainer.x || 0;
+    x: 0,
+    y: 0,
+  };
+  const parentLeft = musicContainer.x || 0;
   let voicesBBox: any = null;
   for (let i = 0; i < list.length; i++) {
     const note = list[i];
@@ -1562,7 +1564,7 @@ const fillWordColor = () => {
     if (index === currentNote.repeatIdx) {
       lyric?.classList.add('lyricActive')
     }
-  })  
+  })
 }
 
 /** 跳动svgdom */
@@ -1588,9 +1590,9 @@ export const moveSvgDom = (skipNote?: boolean) => {
 watch(
   () => state.playState,
   () => {
-    if(state.isSingleLine){
+    if (state.isSingleLine) {
       // 当在播放中暂停 执行这个方法
-      if(!state.playEnd && state.playState === "paused") {
+      if (!state.playEnd && state.playState === "paused") {
         moveTranslateXNum(0)
         const scrollLeft = smoothAnimationState.osdmScrollDom!.scrollLeft
         smoothAnimationState.osdmScrollDom!.scrollLeft = scrollLeft + smoothAnimationState.translateXNum
@@ -1602,10 +1604,10 @@ watch(
 
 
 /* 根据当前的小节获取前面有多少需要去除的合并小节 */
-function getNeedReduceMultipleRestNum(currMeasureIndex:number){
+function getNeedReduceMultipleRestNum(currMeasureIndex: number) {
   let needReduceMultipleRestNum = 0;
-  for(let noteIndex = 0; noteIndex < state.times.length; noteIndex++){
-    const note =  state.times[noteIndex];
+  for (let noteIndex = 0; noteIndex < state.times.length; noteIndex++) {
+    const note = state.times[noteIndex];
     if (note.MeasureNumberXML > currMeasureIndex) {
       break;
     }
@@ -1617,58 +1619,58 @@ function getNeedReduceMultipleRestNum(currMeasureIndex:number){
 }
 
 watch(
-	() => state.activeMeasureIndex,
-	() => {
+  () => state.activeMeasureIndex,
+  () => {
     // 需要减去的合并小节数
     const needReduceMultipleRestNum = getNeedReduceMultipleRestNum(state.activeMeasureIndex)
     const matchMeasureNum = state.activeMeasureIndex - needReduceMultipleRestNum - 1
-    console.log('选中的小节',matchMeasureNum,'需要减去的小节',needReduceMultipleRestNum,'当前的小节',state.activeMeasureIndex)
+    console.log('选中的小节', matchMeasureNum, '需要减去的小节', needReduceMultipleRestNum, '当前的小节', state.activeMeasureIndex)
     state.vfmeasures.forEach((item: any, idx: number) => {
       if (idx === matchMeasureNum) {
         item.querySelector('.vf-custom-bg')?.setAttribute("fill", "#132D4C")
         item.querySelector('.vf-custom-bot')?.setAttribute("fill", "#040D1E")
       } else {
         // 有选段只清除选段处的
-        if(state.section.length === 2){
+        if (state.section.length === 2) {
           // 把中间置灰
           let leftMeasureNumberXML = state.section[0].MeasureNumberXML
           let rightMeasureNumberXML = state.section[1].MeasureNumberXML
-          if(leftMeasureNumberXML > rightMeasureNumberXML){
+          if (leftMeasureNumberXML > rightMeasureNumberXML) {
             leftMeasureNumberXML = state.section[1].MeasureNumberXML
             rightMeasureNumberXML = state.section[0].MeasureNumberXML
           }
           const leftVfmeasuresIndex = leftMeasureNumberXML - getNeedReduceMultipleRestNum(leftMeasureNumberXML) - 1
           const rightVfmeasuresIndex = rightMeasureNumberXML - getNeedReduceMultipleRestNum(rightMeasureNumberXML) - 1
-          if(idx >= leftVfmeasuresIndex && idx <= rightVfmeasuresIndex){
+          if (idx >= leftVfmeasuresIndex && idx <= rightVfmeasuresIndex) {
             item.querySelector('.vf-custom-bg')?.setAttribute("fill", "#609FCF")
             item.querySelector('.vf-custom-bot')?.setAttribute("fill", "#2B70A5")
           }
           // 预备小节
-          if(state.sectionFirst){
+          if (state.sectionFirst) {
             const sectionFirstVfmeasuresIndex = state.sectionFirst.MeasureNumberXML - getNeedReduceMultipleRestNum(state.sectionFirst.MeasureNumberXML) - 1
             const sectionFirstDom = state.vfmeasures[sectionFirstVfmeasuresIndex]
             sectionFirstDom?.querySelector('.vf-custom-bg')?.setAttribute("fill", "#71B8BD")
             sectionFirstDom?.querySelector('.vf-custom-bot')?.setAttribute("fill", "#448F9C")
           }
-        }else{
+        } else {
           item.querySelector('.vf-custom-bg')?.setAttribute("fill", "#609FCF")
           item.querySelector('.vf-custom-bot')?.setAttribute("fill", "#2B70A5")
         }
       }
     })
-	}
-);    
+  }
+);
 
 
 
 watch(
   () => state.section,
   () => {
-    if(state.section.length === 2){
+    if (state.section.length === 2) {
       // 把前面和 后面置灰
       let leftMeasureNumberXML = state.section[0].MeasureNumberXML
       let rightMeasureNumberXML = state.section[1].MeasureNumberXML
-      if(leftMeasureNumberXML > rightMeasureNumberXML){
+      if (leftMeasureNumberXML > rightMeasureNumberXML) {
         leftMeasureNumberXML = state.section[1].MeasureNumberXML
         rightMeasureNumberXML = state.section[0].MeasureNumberXML
       }
@@ -1681,19 +1683,19 @@ watch(
           item.querySelector('.vf-custom-bot')?.setAttribute("fill", "rgba(43,112,165,0.5)")
         }
         // 大于选中置灰
-        if(idx > rightVfmeasuresIndex){
+        if (idx > rightVfmeasuresIndex) {
           item.querySelector('.vf-custom-bg')?.setAttribute("fill", "rgba(96,159,207,0.5)")
           item.querySelector('.vf-custom-bot')?.setAttribute("fill", "rgba(43,112,165,0.5)")
         }
       })
       // 预备小节
-      if(state.sectionFirst){
+      if (state.sectionFirst) {
         const sectionFirstVfmeasuresIndex = state.sectionFirst.MeasureNumberXML - getNeedReduceMultipleRestNum(state.sectionFirst.MeasureNumberXML) - 1
         const sectionFirstDom = state.vfmeasures[sectionFirstVfmeasuresIndex]
         sectionFirstDom?.querySelector('.vf-custom-bg')?.setAttribute("fill", "#71B8BD")
         sectionFirstDom?.querySelector('.vf-custom-bot')?.setAttribute("fill", "#448F9C")
       }
-    }else{
+    } else {
       // 恢复选段前
       const matchMeasureNum = state.activeMeasureIndex - getNeedReduceMultipleRestNum(state.activeMeasureIndex) - 1
       state.vfmeasures.forEach((item: any, idx: number) => {
@@ -1712,7 +1714,7 @@ watch(
 /** 刷新谱面 */
 export const refreshMusicSvg = () => {
   state.loadingText = '正在加载中,请稍等…'
-  if(state.isSingleLine){
+  if (state.isSingleLine) {
     // 销毁旋律线
     destroySmoothAnimation()
   }
@@ -1723,7 +1725,7 @@ export const refreshMusicSvg = () => {
 watch(
   () => state.setting.displayFingering,
   () => {
-    nextTick(()=>{
+    nextTick(() => {
       if (smoothAnimationState.osdmScrollDom) {
         smoothAnimationState.osdmScrollDomWith = smoothAnimationState.osdmScrollDom.offsetWidth | 0
       }

+ 52 - 50
src/style.css

@@ -1,23 +1,25 @@
 * {
-  margin    : 0;
-  padding   : 0;
+  margin: 0;
+  padding: 0;
   box-sizing: border-box;
 }
-img { -webkit-touch-callout: none; }
-body{
+img {
+  -webkit-touch-callout: none;
+}
+body {
   user-select: none;
 }
 
 :root {
-  --cursor-color        : url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABRJREFUCB1jZDy49T8DEDCBCBAAACTkAnq23WmtAAAAAElFTkSuQmCC);
+  --cursor-color: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABRJREFUCB1jZDy49T8DEDCBCBAAACTkAnq23WmtAAAAAElFTkSuQmCC);
   --container-background: #fff;
-  --active-stave-box    : rgba(1, 193, 181, 0.2);
-  --corsor-opacity      : 1;
+  --active-stave-box: rgba(1, 193, 181, 0.2);
+  --corsor-opacity: 1;
 }
 
 .eyeProtection {
-  --cursor-color            : url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABZJREFUCB1j/N8sv5UBCJhABMN/hv8AKQgEWNQffFMAAAAASUVORK5CYII=);
-  --container-background    : #fff4e1;
+  --cursor-color: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABZJREFUCB1j/N8sv5UBCJhABMN/hv8AKQgEWNQffFMAAAAASUVORK5CYII=);
+  --container-background: #fff4e1;
   --active-measur-backgorund: rgba(255, 98, 37, 0.18);
 }
 
@@ -41,7 +43,7 @@ body{
 
 /* vant 弹窗优化 */
 :root {
-  --van-duration-base: .25s;
+  --van-duration-base: 0.25s;
 }
 
 .van-overlay {
@@ -51,7 +53,7 @@ body{
 .popup-custom {
   transition: all 0.25s;
   background: transparent;
-  overflow  : initial;
+  overflow: initial;
   max-width: 100%;
 }
 
@@ -62,7 +64,7 @@ body{
 /* 缩放动画 */
 .van-scale-enter-from,
 .van-scale-leave-to {
-  opacity  : 0;
+  opacity: 0;
   transform: scale(0.3);
 }
 
@@ -72,13 +74,13 @@ body{
 }
 
 .custom-close-btn .van-popup__close-icon {
-  top             : 0;
-  right           : -30px;
+  top: 0;
+  right: -30px;
   background-color: #fff;
-  border-radius   : 50%;
-  padding         : 4px;
-  font-size       : 16px;
-  color           : var(--van-primary-color);
+  border-radius: 50%;
+  padding: 4px;
+  font-size: 16px;
+  color: var(--van-primary-color);
 }
 
 .top_drag {
@@ -116,7 +118,7 @@ body{
 /* 引导动画 */
 @keyframes guideKeyframes {
   0% {
-    transform: scale(.9);
+    transform: scale(0.9);
   }
 
   50% {
@@ -124,64 +126,64 @@ body{
   }
 
   100% {
-    transform: scale(.9);
+    transform: scale(0.9);
   }
 }
 
 .guideAnimate {
-  animation-duration       : 1.5s;
-  animation-name           : guideKeyframes;
+  animation-duration: 1.5s;
+  animation-name: guideKeyframes;
   animation-iteration-count: infinite;
 }
 
-@keyframes cnimate{
-  0%{
-      opacity: 0;
+@keyframes cnimate {
+  0% {
+    opacity: 0;
   }
-  50%{
-      opacity: 0.5;
+  50% {
+    opacity: 0.5;
   }
-  100%{
-      opacity: 1;
+  100% {
+    opacity: 1;
   }
 }
 
-@keyframes noteAnimate{
-  0%{
-      scale: 0.4;
+@keyframes noteAnimate {
+  0% {
+    scale: 0.4;
   }
-  10%{
+  10% {
     scale: 0.6;
   }
-  20%{
+  20% {
     scale: 0.8;
   }
-  30%{
+  30% {
     scale: 1;
   }
-  40%{
+  40% {
     scale: 1.2;
   }
-  50%{
-      opacity: 1.4;
+  50% {
+    opacity: 1.4;
   }
-  60%{
-      opacity: 1.6;
+  60% {
+    opacity: 1.6;
   }
-  70%{
+  70% {
     opacity: 1.4;
   }
-  80%{
+  80% {
     opacity: 1.2;
-  }  
-  90%{
-      opacity: 1.1;
-  }  
-  100%{
-      opacity: 1;
+  }
+  90% {
+    opacity: 1.1;
+  }
+  100% {
+    opacity: 1;
   }
 }
 
 html {
-  font-size: 64PX;
-}
+  font-size: 64px;
+}

+ 41 - 43
src/view/abnormal-pop/index.tsx

@@ -1,53 +1,51 @@
 import { defineComponent } from "vue";
 import styles from "./index.module.less";
 import state from "/src/state";
-import { popImgs } from '/src/view/evaluating'
+import { popImgs } from "/src/view/evaluating";
 import { evaluatingData } from "/src/view/evaluating";
 import { Vue3Lottie } from "vue3-lottie";
 import loading from "./loading.json";
 import animBg from "../audio-list/img/refresh_anim.json";
 
 export default defineComponent({
-	name: "abnormal-pop",
-	props:{},
-	emits: ["close", "confirm"],
-	setup(props, { emit }) {
-		return () => (
-			<>
-				{
-					evaluatingData.socketErrorStatus === 0 && 
-					<div class={styles.fraction}>
-						<img class={styles.close} src={popImgs.icon_close} onClick={() => emit("close")} />
-						<div class={styles.content}>
-							<div class={styles.title}>网络连接失败</div>
-							<div class={styles.desc}>请确保网络正常后重新连接</div>
-						</div>
-						<div>
-							<img src={popImgs.icon_btn} class={styles.btn} onClick={() => emit("confirm", true)} />
-						</div>
-					</div>				
-				}
-				{
-					evaluatingData.socketErrorStatus === 1 && 
-					// <div class={styles.loadColumn}>
-					// 	<Vue3Lottie class={styles.loadIcon} animationData={loading} loop={true}></Vue3Lottie>
-					// 	<img class={styles.close} src={popImgs.icon_close} onClick={() => emit("close")} />
-					// 	<p>正在连接服务器,请稍后…</p>
-					// </div>
-					<div class={styles.loadingPop}>
-						<Vue3Lottie animationData={animBg} class={styles.loadingIcon}></Vue3Lottie>
-						<div class={styles.loadingTip}>正在连接中,请稍等…</div>
-						<div class={styles.loadingClose} onClick={() => emit("close")}>取消连接</div>
-					</div>
-				}
-				{
-					evaluatingData.socketErrorStatus === 2 && 
-					<div class={styles.loadColumn}>
-						<img class={styles.successIcon} src={popImgs.icon_success} />
-						<p>网络连接成功</p>
-					</div>
-				}
-			</>
-		);
-	},
+  name: "abnormal-pop",
+  emits: ["close", "confirm"],
+  setup(props, { emit }) {
+    return () => (
+      <>
+        {evaluatingData.socketErrorStatus === 0 && (
+          <div class={styles.fraction}>
+            <img class={styles.close} src={popImgs.icon_close} onClick={() => emit("close")} />
+            <div class={styles.content}>
+              <div class={styles.title}>网络连接失败</div>
+              <div class={styles.desc}>请确保网络正常后重新连接</div>
+            </div>
+            <div>
+              <img src={popImgs.icon_btn} class={styles.btn} onClick={() => emit("confirm", true)} />
+            </div>
+          </div>
+        )}
+        {evaluatingData.socketErrorStatus === 1 && (
+          // <div class={styles.loadColumn}>
+          // 	<Vue3Lottie class={styles.loadIcon} animationData={loading} loop={true}></Vue3Lottie>
+          // 	<img class={styles.close} src={popImgs.icon_close} onClick={() => emit("close")} />
+          // 	<p>正在连接服务器,请稍后…</p>
+          // </div>
+          <div class={styles.loadingPop}>
+            <Vue3Lottie animationData={animBg} class={styles.loadingIcon}></Vue3Lottie>
+            <div class={styles.loadingTip}>正在连接中,请稍等…</div>
+            <div class={styles.loadingClose} onClick={() => emit("close")}>
+              取消连接
+            </div>
+          </div>
+        )}
+        {evaluatingData.socketErrorStatus === 2 && (
+          <div class={styles.loadColumn}>
+            <img class={styles.successIcon} src={popImgs.icon_success} />
+            <p>网络连接成功</p>
+          </div>
+        )}
+      </>
+    );
+  },
 });

+ 661 - 679
src/view/evaluating/index.tsx

@@ -4,45 +4,38 @@ import { closeToast, showLoadingToast, showToast, Popup } from "vant";
 import { defineComponent, onMounted, onUnmounted, reactive, ref, watch } from "vue";
 import { getLeveByScore, getLeveByScoreMeasure, IEvaluatings } from "./evaluatResult";
 import {
-	cancelEvaluating,
-	endEvaluating,
-	endSoundCheck,
-	getEarphone,
-	api_proxyServiceMessage,
-	removeResult,
-	sendResult,
-	startEvaluating,
-	startSoundCheck,
-	api_openWebView,
-	api_startRecording,
-	api_startRecordingCb,
-	api_stopRecording,
-	api_recordStartTime,
-	api_remove_recordStartTime,
-	api_startCapture,
-	api_endCapture,
-	api_getDeviceDelay,
-	hideComplexButton,
-	api_checkSocketStatus,
-	addAccompanyError,
-	removeAccompanyError,
-	addSocketStatus,
-	removeSocketStatus,
-	api_disconnectSocket,
-	api_midiMicDelay,
-	api_cloudSetCurrentTime,
-	api_cloudChangeSpeed,
-	api_startDelayCheck,
-	api_closeDelayCheck,
+  cancelEvaluating,
+  endEvaluating,
+  endSoundCheck,
+  getEarphone,
+  api_proxyServiceMessage,
+  removeResult,
+  sendResult,
+  startEvaluating,
+  startSoundCheck,
+  api_openWebView,
+  api_startRecording,
+  api_startRecordingCb,
+  api_stopRecording,
+  api_recordStartTime,
+  api_remove_recordStartTime,
+  api_startCapture,
+  api_endCapture,
+  api_getDeviceDelay,
+  hideComplexButton,
+  api_checkSocketStatus,
+  addAccompanyError,
+  removeAccompanyError,
+  addSocketStatus,
+  removeSocketStatus,
+  api_disconnectSocket,
+  api_midiMicDelay,
+  api_cloudSetCurrentTime,
+  api_cloudChangeSpeed,
+  api_startDelayCheck,
+  api_closeDelayCheck,
 } from "/src/helpers/communication";
-import state, {
-	IPlayState,
-	clearSelection,
-	handleStopPlay,
-	onPlay,
-	resetPlaybackToStart,
-	togglePlay,
-} from "/src/state";
+import state, { IPlayState, clearSelection, handleStopPlay, onPlay, resetPlaybackToStart, togglePlay } from "/src/state";
 import { IPostMessage } from "/src/utils/native-message";
 import { usePageVisibility } from "@vant/use";
 import { browser } from "/src/utils";
@@ -50,435 +43,431 @@ import { getAudioCurrentTime, toggleMutePlayAudio, audioListStart, audioData } f
 import { handleStartTick, closeTick } from "../tick";
 import AbnormalPop from "../abnormal-pop";
 import { storeData } from "../../store";
-import icon_bg from '../abnormal-pop/icon_bg.svg'
-import icon_close from '../abnormal-pop/icon_close.svg'
-import icon_btn from '../abnormal-pop/icon_btn.svg'
-import icon_success from '../abnormal-pop/icon_success.svg'
-import { data } from '../../page-instrument/custom-plugins/work-index'
-import { startCountdown } from "/src/page-instrument/evaluat-model/countdown"
+import icon_bg from "../abnormal-pop/icon_bg.svg";
+import icon_close from "../abnormal-pop/icon_close.svg";
+import icon_btn from "../abnormal-pop/icon_btn.svg";
+import icon_success from "../abnormal-pop/icon_success.svg";
+import { data } from "../../page-instrument/custom-plugins/work-index";
+import { startCountdown } from "/src/page-instrument/evaluat-model/countdown";
 
 const browserInfo = browser();
 
-let socketStartTime = 0
+let socketStartTime = 0;
 
 export const popImgs = {
-	icon_bg,
-	icon_close,
-	icon_btn,
-	icon_success
-}
+  icon_bg,
+  icon_close,
+  icon_btn,
+  icon_success,
+};
 
 export const evaluatingData = reactive({
-	/** 评测数据 */
-	contentData: {} as any,
-	/** 评测模块是否加载完成 */
-	rendered: false,
-	earphone: false, // 是否插入耳机
-	soundEffect: false, // 是否效音
-	soundEffectFrequency: 0, // 效音频率
-	checkStep: 0, // 执行步骤
-	checkEnd: false, // 检测结束
-	earphoneMode: false, // 耳机弹窗
-	earPhoneType: "" as "" | "有线耳机" | "蓝牙耳机", // 耳机类型
-	soundEffectMode: false, // 效音弹窗
-	websocketState: false, // websocket连接状态
-	/**是否开始播放 */
-	startBegin: false, // 开始
-	backtime: 0, // 延迟时间
-	/** 已经评测的数据 */
-	evaluatings: {} as IEvaluatings,
-	/** 评测结果 */
-	resultData: {} as any,
-	/** 评测结果弹窗 */
-	resulstMode: false,
-	/** 是否是完整评测 */
-	isComplete: false,
-	/**  */
-	isDisabledPlayMusic: false,
-	/** socket异常状态弹窗 */
-	socketErrorPop: false,
-	/** 异常提示 */
-	errorContents: '',
-	/** socket异常状态弹窗的状态值 */
-	socketErrorStatus: 0,
-	/** 延迟检测,socket状态异常 */
-	delayCheckSocketError: false,
-	/** 异常状态,不生成评测记录,不调用保存接口 */
-	isErrorState: false,
-	/** accompanyError,错误类型 */
-	accompanyErrorType: '',	
-	/** app播放结束状态,重新评测需要重置为 */
-	isAudioPlayEnd: false,
-	preloadJson: true, // 预加载延迟检测的资源
-	jsonLoading: true, // 延迟检测的资源加载中状态
-	jsonLoadDone: true, // 延迟检测的动画dom加载完成状态
-	hideResultModal: false, // 评测作业,如果不是完整评测,需要隐藏评测结果弹窗
-	oneselfCancleEvaluating: false, // 是否是自主取消评测,自主取消评测,不生产评测记录
-	isBeginMask: false // 倒计时和系统节拍器时候的遮罩,防止用户点击
+  /** 评测数据 */
+  contentData: {} as any,
+  /** 评测模块是否加载完成 */
+  rendered: false,
+  earphone: false, // 是否插入耳机
+  soundEffect: false, // 是否效音
+  soundEffectFrequency: 0, // 效音频率
+  checkStep: 0, // 执行步骤
+  checkEnd: false, // 检测结束
+  earphoneMode: false, // 耳机弹窗
+  earPhoneType: "" as "" | "有线耳机" | "蓝牙耳机", // 耳机类型
+  soundEffectMode: false, // 效音弹窗
+  websocketState: false, // websocket连接状态
+  /**是否开始播放 */
+  startBegin: false, // 开始
+  backtime: 0, // 延迟时间
+  /** 已经评测的数据 */
+  evaluatings: {} as IEvaluatings,
+  /** 评测结果 */
+  resultData: {} as any,
+  /** 评测结果弹窗 */
+  resulstMode: false,
+  /** 是否是完整评测 */
+  isComplete: false,
+  /**  */
+  isDisabledPlayMusic: false,
+  /** socket异常状态弹窗 */
+  socketErrorPop: false,
+  /** 异常提示 */
+  errorContents: "",
+  /** socket异常状态弹窗的状态值 */
+  socketErrorStatus: 0,
+  /** 延迟检测,socket状态异常 */
+  delayCheckSocketError: false,
+  /** 异常状态,不生成评测记录,不调用保存接口 */
+  isErrorState: false,
+  /** accompanyError,错误类型 */
+  accompanyErrorType: "",
+  /** app播放结束状态,重新评测需要重置为 */
+  isAudioPlayEnd: false,
+  preloadJson: true, // 预加载延迟检测的资源
+  jsonLoading: true, // 延迟检测的资源加载中状态
+  jsonLoadDone: true, // 延迟检测的动画dom加载完成状态
+  hideResultModal: false, // 评测作业,如果不是完整评测,需要隐藏评测结果弹窗
+  oneselfCancleEvaluating: false, // 是否是自主取消评测,自主取消评测,不生产评测记录
+  isBeginMask: false, // 倒计时和系统节拍器时候的遮罩,防止用户点击
 });
 
 const sendOffsetTime = async (offsetTime: number) => {
-	const delayData = await api_getDeviceDelay();
-	api_midiMicDelay({
-		header: {
-			commond: 'audioPlayStart',
-			type: 'SOUND_COMPARE',
-		  },
-		  body: {
-			offsetTime,
-			micDelay: delayData?.content?.value
-		  },
-	})
-  }
+  const delayData = await api_getDeviceDelay();
+  api_midiMicDelay({
+    header: {
+      commond: "audioPlayStart",
+      type: "SOUND_COMPARE",
+    },
+    body: {
+      offsetTime,
+      micDelay: delayData?.content?.value,
+    },
+  });
+};
 
 /** 点击开始评测按钮 */
 export const handleStartEvaluat = async () => {
-	if (state.modeType === "evaluating") {
-		handleCancelEvaluat();
-	} else {
-		if (state.platform !== 'PC') {
-			// 评测前先检查APP端的websocket状态
-			const res = await api_checkSocketStatus();
-			if (res?.content?.status === "connected") {
-				handleStopPlay();
-			} else {
-				// socket未连接
-				// evaluatingData.socketErrorPop = true
-			}
-		} else {
-			handleStopPlay();
-		}
-	}
-	state.modeType = state.modeType === "evaluating" ? "practise" : "evaluating";
-	if (state.modeType !== "evaluating") {
-		// 切换到练习模式,卸载评测模块
-		evaluatingData.rendered = false;
-	}
+  if (state.modeType === "evaluating") {
+    handleCancelEvaluat();
+  } else {
+    if (state.platform !== "PC") {
+      // 评测前先检查APP端的websocket状态
+      const res = await api_checkSocketStatus();
+      if (res?.content?.status === "connected") {
+        handleStopPlay();
+      } else {
+        // socket未连接
+        // evaluatingData.socketErrorPop = true
+      }
+    } else {
+      handleStopPlay();
+    }
+  }
+  state.modeType = state.modeType === "evaluating" ? "practise" : "evaluating";
+  if (state.modeType !== "evaluating") {
+    // 切换到练习模式,卸载评测模块
+    evaluatingData.rendered = false;
+  }
 };
 
 /** 开始评测 & 延迟检测开始按钮 */
 export const startCheckDelay = async () => {
-	// 评测前先检查APP端的websocket状态
-	const res = await api_checkSocketStatus();
-	if (res?.content?.status === "connected") {
-		// 
-		return new Promise((resolve) => {
-			resolve({checked: true})
-		});
-	} else {
-		/** 
-		 * socket未连接,记录此时的时间,以便于和收到socket成功链接,进行对比,对比时间小于500ms时,则连接中的状态默认显示500ms持续时间
-		 * 
-		 * */ 
-		socketStartTime = +new Date()
-		evaluatingData.socketErrorPop = true
-		evaluatingData.socketErrorStatus = 1
-		return new Promise((resolve) => {
-			resolve({checked: false})
-		});
-	}
-}
+  // 评测前先检查APP端的websocket状态
+  const res = await api_checkSocketStatus();
+  if (res?.content?.status === "connected") {
+    //
+    return new Promise((resolve) => {
+      resolve({ checked: true });
+    });
+  } else {
+    /**
+     * socket未连接,记录此时的时间,以便于和收到socket成功链接,进行对比,对比时间小于500ms时,则连接中的状态默认显示500ms持续时间
+     *
+     * */
+    socketStartTime = +new Date();
+    evaluatingData.socketErrorPop = true;
+    evaluatingData.socketErrorStatus = 1;
+    return new Promise((resolve) => {
+      resolve({ checked: false });
+    });
+  }
+};
 
 const check_currentTime = () => {
-	let preTime = 0;
-	// 选段评测模式
-	if (state.isSelectMeasureMode) {
-		preTime = state.section[0].time * 1000;
-	}
-	const currentTime = getAudioCurrentTime() * 1000 - preTime;
-	// console.log('播放进度music', currentTime, 'preTime:' + preTime)
-
-	if (currentTime >= 500) {
-		sendEvaluatingOffsetTime(500);
-		return;
-	}
-	setTimeout(() => {
-		check_currentTime();
-	}, 10);
+  let preTime = 0;
+  // 选段评测模式
+  if (state.isSelectMeasureMode) {
+    preTime = state.section[0].time * 1000;
+  }
+  const currentTime = getAudioCurrentTime() * 1000 - preTime;
+  // console.log('播放进度music', currentTime, 'preTime:' + preTime)
+
+  if (currentTime >= 500) {
+    sendEvaluatingOffsetTime(500);
+    return;
+  }
+  setTimeout(() => {
+    check_currentTime();
+  }, 10);
 };
 
 /** 开始播放发送延迟时间 */
 export const sendEvaluatingOffsetTime = async (currentTime: number) => {
-	// 没有开始时间点, 不处理
-	if (!evaluatingData.backtime) return;
-	const nowTime = Date.now();
-	const delayTime = nowTime - evaluatingData.backtime - currentTime;
-	console.error("真正播放延迟", delayTime, "currentTime:", currentTime);
-	await api_proxyServiceMessage({
-		header: {
-			commond: "audioPlayStart",
-			type: "SOUND_COMPARE",
-		},
-		body: {
-			offsetTime: delayTime < 0 ? 0 : delayTime,
-			micDelay: 0,
-		},
-	});
+  // 没有开始时间点, 不处理
+  if (!evaluatingData.backtime) return;
+  const nowTime = Date.now();
+  const delayTime = nowTime - evaluatingData.backtime - currentTime;
+  console.error("真正播放延迟", delayTime, "currentTime:", currentTime);
+  await api_proxyServiceMessage({
+    header: {
+      commond: "audioPlayStart",
+      type: "SOUND_COMPARE",
+    },
+    body: {
+      offsetTime: delayTime < 0 ? 0 : delayTime,
+      micDelay: 0,
+    },
+  });
 };
 
 /** 检测耳机 */
 export const checkUseEarphone = async () => {
-	const res = await getEarphone();
-	return res?.content?.checkIsWired || false;
+  const res = await getEarphone();
+  return res?.content?.checkIsWired || false;
 };
 
 /**
  * 开始录音
  */
 const handleStartSoundCheck = () => {
-	startSoundCheck();
+  startSoundCheck();
 };
 /** 结束录音 */
 export const handleEndSoundCheck = () => {
-	endSoundCheck();
+  endSoundCheck();
 };
 
 /** 连接websocket */
 export const connectWebsocket = async (content: any) => {
-	evaluatingData.contentData = content;
-	evaluatingData.websocketState = true;
+  evaluatingData.contentData = content;
+  evaluatingData.websocketState = true;
 };
 
 /**
  * 执行检测
  */
 export const handlePerformDetection = async () => {
-	// 检测完成不检测了
-	if (evaluatingData.checkEnd) return;
-	// 延迟检测
-	if (evaluatingData.checkStep === 0) {
-		evaluatingData.checkStep = 5;
-		// 没有设备延迟数据 或 开启了效音 显示检测组件,并持续检测耳机状态
-		if (state.setting.soundEffect) {
-			evaluatingData.soundEffectMode = true;
-			return;
-		}
-		const delayTime = await api_getDeviceDelay();
-		console.log("🚀 ~ delayTime:", delayTime);
-		if (!delayTime) {
-			evaluatingData.soundEffectMode = true;
-			return;
-		}
-		handlePerformDetection();
-		return;
-	}
-	// 检测耳机
-	if ((evaluatingData.checkStep = 5)) {
-		evaluatingData.checkStep = 10;
-		const erji = await checkUseEarphone();
-		if (!erji) {
-			evaluatingData.earphoneMode = true;
-			return;
-		}
-		handlePerformDetection();
-		return;
-	}
-	// 效音
-	// if (evaluatingData.checkStep === 7) {
-	// 	// 是否需要开启效音
-	// 	evaluatingData.checkStep = 10;
-	// 	if (state.setting.soundEffect && !state.isPercussion) {
-	// 		evaluatingData.soundEffectMode = true;
-	// 		handleStartSoundCheck();
-	// 		return
-	// 	}
-	// 	handlePerformDetection();
-	// 	return;
-	// }
-	// 效验完成
-	if (evaluatingData.checkStep === 10) {
-		evaluatingData.checkEnd = true;
-	}
+  // 检测完成不检测了
+  if (evaluatingData.checkEnd) return;
+  // 延迟检测
+  if (evaluatingData.checkStep === 0) {
+    evaluatingData.checkStep = 5;
+    // 没有设备延迟数据 或 开启了效音 显示检测组件,并持续检测耳机状态
+    if (state.setting.soundEffect) {
+      evaluatingData.soundEffectMode = true;
+      return;
+    }
+    const delayTime = await api_getDeviceDelay();
+    console.log("🚀 ~ delayTime:", delayTime);
+    if (!delayTime) {
+      evaluatingData.soundEffectMode = true;
+      return;
+    }
+    handlePerformDetection();
+    return;
+  }
+  // 检测耳机
+  if ((evaluatingData.checkStep = 5)) {
+    evaluatingData.checkStep = 10;
+    const erji = await checkUseEarphone();
+    if (!erji) {
+      evaluatingData.earphoneMode = true;
+      return;
+    }
+    handlePerformDetection();
+    return;
+  }
+  // 效音
+  // if (evaluatingData.checkStep === 7) {
+  // 	// 是否需要开启效音
+  // 	evaluatingData.checkStep = 10;
+  // 	if (state.setting.soundEffect && !state.isPercussion) {
+  // 		evaluatingData.soundEffectMode = true;
+  // 		handleStartSoundCheck();
+  // 		return
+  // 	}
+  // 	handlePerformDetection();
+  // 	return;
+  // }
+  // 效验完成
+  if (evaluatingData.checkStep === 10) {
+    evaluatingData.checkEnd = true;
+  }
 };
 
 /** 记录小节分数 */
 export const addMeasureScore = (measureScore: any, show = true) => {
-	// #8720 bug修复
-	for(let idx in evaluatingData.evaluatings) {
-		evaluatingData.evaluatings[idx].show = false
-	}
-	evaluatingData.evaluatings[measureScore.measureRenderIndex] = {
-		...measureScore,
-		leve: getLeveByScoreMeasure(measureScore.score),
-		show,
-	};
-	// console.log("🚀 ~ measureScore:", evaluatingData.evaluatings)
+  // #8720 bug修复
+  for (let idx in evaluatingData.evaluatings) {
+    evaluatingData.evaluatings[idx].show = false;
+  }
+  evaluatingData.evaluatings[measureScore.measureRenderIndex] = {
+    ...measureScore,
+    leve: getLeveByScoreMeasure(measureScore.score),
+    show,
+  };
+  // console.log("🚀 ~ measureScore:", evaluatingData.evaluatings)
 };
 
 const handleScoreResult = (res?: IPostMessage) => {
-	console.log('返回', res,evaluatingData.oneselfCancleEvaluating)
-	// 如果是手动取消评测,不生成评测记录
-	if (evaluatingData.oneselfCancleEvaluating) {
-		return;
-	}
-	if (res?.content) {
-		const { header, body } = res.content;
-		// 效音返回
-		if (header.commond === "checking") {
-			evaluatingData.soundEffectFrequency = body.frequency;
-		}
-		// 小节评分返回
-		if (header?.commond === "measureScore") {
-			console.log("🚀 ~ 评测返回:", res);
-			addMeasureScore(body);
-		}
-		// 评测结束返回
-		if (header?.commond === "overall") {
-			console.log("🚀 ~ 评测返回:", res);
-			console.log("评测结束", body);
-			state.isHideEvaluatReportSaveBtn = false;
-			setTimeout(() => {
-				// 评测作业,如果不是完整评测,不展示评测弹窗
-				if (data.trainingType === 'EVALUATION' && !evaluatingData.isComplete) {
-					evaluatingData.hideResultModal = true;
-				} else {
-					evaluatingData.hideResultModal = false;
-				}
-				evaluatingData.resulstMode = evaluatingData.isErrorState ? false : true
-			}, 200);
-			evaluatingData.resultData = {
-				...body,
-				...getLeveByScore(body.score),
-			};
-			// console.log("🚀 ~ evaluatingData.resultData:", evaluatingData.resultData)
-			closeToast();
-		}
-	}
+  console.log("返回", res, evaluatingData.oneselfCancleEvaluating);
+  // 如果是手动取消评测,不生成评测记录
+  if (evaluatingData.oneselfCancleEvaluating) {
+    return;
+  }
+  if (res?.content) {
+    const { header, body } = res.content;
+    // 效音返回
+    if (header.commond === "checking") {
+      evaluatingData.soundEffectFrequency = body.frequency;
+    }
+    // 小节评分返回
+    if (header?.commond === "measureScore") {
+      console.log("🚀 ~ 评测返回:", res);
+      addMeasureScore(body);
+    }
+    // 评测结束返回
+    if (header?.commond === "overall") {
+      console.log("🚀 ~ 评测返回:", res);
+      console.log("评测结束", body);
+      state.isHideEvaluatReportSaveBtn = false;
+      setTimeout(() => {
+        // 评测作业,如果不是完整评测,不展示评测弹窗
+        if (data.trainingType === "EVALUATION" && !evaluatingData.isComplete) {
+          evaluatingData.hideResultModal = true;
+        } else {
+          evaluatingData.hideResultModal = false;
+        }
+        evaluatingData.resulstMode = evaluatingData.isErrorState ? false : true;
+      }, 200);
+      evaluatingData.resultData = {
+        ...body,
+        ...getLeveByScore(body.score),
+      };
+      // console.log("🚀 ~ evaluatingData.resultData:", evaluatingData.resultData)
+      closeToast();
+    }
+  }
 };
 
 /** 开始评测 */
 export const handleStartBegin = async (preTimes?: number) => {
-	if (state.isAppPlay) {
-		await api_cloudSetCurrentTime({
-			currentTime: 0,
-			songID: state.examSongId,
-		})
-	}
-	evaluatingData.isComplete = false;
-	evaluatingData.evaluatings = {};
-	evaluatingData.resultData = {};
-	evaluatingData.backtime = 0;
-	resetPlaybackToStart();
-	evaluatingData.isAudioPlayEnd = false;
-	const res = await startEvaluating(evaluatingData.contentData);
-	if (res?.api !== "startEvaluating") {
-		Snackbar.error("请在APP端进行评测");
-		evaluatingData.startBegin = false;
-		return;
-	}
-	if (res?.content?.reson) {
-		showToast(res.content?.des);
-		evaluatingData.startBegin = false;
-		return;
-	}
-	evaluatingData.startBegin = true;
-	if (evaluatingData.isDisabledPlayMusic) {
-		evaluatingData.isBeginMask = true
-		// 先播放倒计时
-		await startCountdown()
-		state.playState = state.playState === "paused" ? "play" : "paused";
-		// 设置为开始播放时, 如果需要节拍,先播放节拍器
-		if (state.playState === "play" && (state.playType==="play"&&state.needTick)||(state.playType==="sing"&&state.needSingTick)) {
-			// 如果是系统节拍器 等系统节拍器播完了再播,如果是mp3节拍器 直接播
-			if((state.playType==="play" && !state.isOpenMetronome)||(state.playType==="sing" && !state.isSingOpenMetronome)){
-				const tickend = await handleStartTick();
-				console.log("🚀 ~ tickend:", tickend)
-				// 节拍器返回false, 取消播放
-				if (!tickend) {
-					state.playState = "paused";
-					evaluatingData.startBegin = false;
-					evaluatingData.isBeginMask = false
-					return;
-				}
-			}else{
-				handleStartTick()
-			}
-		}
-		evaluatingData.isBeginMask = false
-		onPlay();
-	}
-	if (evaluatingData.isErrorState) return
-	//开始录音
-	// await api_startRecording({
-	// 	accompanimentState: state.setting.enableAccompaniment ? 1 : 0,
-	// 	firstNoteTime: preTimes || 0,
-	// });
-	let rate = state.speed / state.originSpeed;
-	rate = parseFloat(rate.toFixed(2));
-	await api_startRecordingCb({
-		accompanimentState: state.setting.enableAccompaniment ? 1 : 0,
-		firstNoteTime: preTimes || 0,
-		speedRate: rate, // 播放倍率
-	}, () => {
-		if (state.isAppPlay) {
-			setTimeout(() => {
-				sendOffsetTime(0)
-			}, 300);
-		}
-	})
-	// 如果开启了摄像头, 开启录制视频
-	if (state.setting.camera) {
-		console.log("开始录制视频");
-		await api_startCapture();
-	}
-	// 如果是midi音频评测,需要调用cloudPlay
-	if (state.isAppPlay) {
-		await api_cloudChangeSpeed({
-			speed: state.originSpeed,
-			originalSpeed: state.originSpeed,
-			songID: state.examSongId,
-		});
-		audioData.progress = 0
-		audioListStart(state.playState);
-	}
-	evaluatingData.oneselfCancleEvaluating = false;
+  if (state.isAppPlay) {
+    await api_cloudSetCurrentTime({
+      currentTime: 0,
+      songID: state.examSongId,
+    });
+  }
+  evaluatingData.isComplete = false;
+  evaluatingData.evaluatings = {};
+  evaluatingData.resultData = {};
+  evaluatingData.backtime = 0;
+  resetPlaybackToStart();
+  evaluatingData.isAudioPlayEnd = false;
+  const res = await startEvaluating(evaluatingData.contentData);
+  if (res?.api !== "startEvaluating") {
+    Snackbar.error("请在APP端进行评测");
+    evaluatingData.startBegin = false;
+    return;
+  }
+  if (res?.content?.reson) {
+    showToast(res.content?.des);
+    evaluatingData.startBegin = false;
+    return;
+  }
+  evaluatingData.startBegin = true;
+  if (evaluatingData.isDisabledPlayMusic) {
+    evaluatingData.isBeginMask = true;
+    // 先播放倒计时
+    await startCountdown();
+    state.playState = state.playState === "paused" ? "play" : "paused";
+    // 设置为开始播放时, 如果需要节拍,先播放节拍器
+    if ((state.playState === "play" && state.playType === "play" && state.needTick) || (state.playType === "sing" && state.needSingTick)) {
+      // 如果是系统节拍器 等系统节拍器播完了再播,如果是mp3节拍器 直接播
+      if ((state.playType === "play" && !state.isOpenMetronome) || (state.playType === "sing" && !state.isSingOpenMetronome)) {
+        const tickend = await handleStartTick();
+        console.log("🚀 ~ tickend:", tickend);
+        // 节拍器返回false, 取消播放
+        if (!tickend) {
+          state.playState = "paused";
+          evaluatingData.startBegin = false;
+          evaluatingData.isBeginMask = false;
+          return;
+        }
+      } else {
+        handleStartTick();
+      }
+    }
+    evaluatingData.isBeginMask = false;
+    onPlay();
+  }
+  if (evaluatingData.isErrorState) return;
+  //开始录音
+  // await api_startRecording({
+  // 	accompanimentState: state.setting.enableAccompaniment ? 1 : 0,
+  // 	firstNoteTime: preTimes || 0,
+  // });
+  let rate = state.speed / state.originSpeed;
+  rate = parseFloat(rate.toFixed(2));
+  await api_startRecordingCb(
+    {
+      accompanimentState: state.setting.enableAccompaniment ? 1 : 0,
+      firstNoteTime: preTimes || 0,
+      speedRate: rate, // 播放倍率
+    },
+    () => {
+      if (state.isAppPlay) {
+        setTimeout(() => {
+          sendOffsetTime(0);
+        }, 300);
+      }
+    }
+  );
+  // 如果开启了摄像头, 开启录制视频
+  if (state.setting.camera) {
+    console.log("开始录制视频");
+    await api_startCapture();
+  }
+  // 如果是midi音频评测,需要调用cloudPlay
+  if (state.isAppPlay) {
+    await api_cloudChangeSpeed({
+      speed: state.originSpeed,
+      originalSpeed: state.originSpeed,
+      songID: state.examSongId,
+    });
+    audioData.progress = 0;
+    audioListStart(state.playState);
+  }
+  evaluatingData.oneselfCancleEvaluating = false;
 };
 
 /** 播放音乐 */
 const playMusic = async () => {
-	const playState = await togglePlay("play");
-	// 取消播放,停止播放
-	if (!playState) {
-		evaluatingData.startBegin = false;
-		handleCancelEvaluat();
-		return;
-	}
-	// 检测播放进度, 计算延迟
-	check_currentTime();
-
-	// 如果开启了摄像头, 开启录制视频
-	if (state.setting.camera) {
-		console.log("开始录制视频");
-		api_startCapture();
-	}
+  const playState = await togglePlay("play");
+  // 取消播放,停止播放
+  if (!playState) {
+    evaluatingData.startBegin = false;
+    handleCancelEvaluat();
+    return;
+  }
+  // 检测播放进度, 计算延迟
+  check_currentTime();
+
+  // 如果开启了摄像头, 开启录制视频
+  if (state.setting.camera) {
+    console.log("开始录制视频");
+    api_startCapture();
+  }
 };
 
 let _audio: HTMLAudioElement;
 
 /** 录音开始,记录开始时间点 */
 const recordStartTimePoint = async (res?: IPostMessage) => {
-	console.error("开始录音");
-	// 没有开始评测,不处理
-	if (!evaluatingData.startBegin) return;
-	let inteveral = res?.content?.inteveral || 0;
-	if (browserInfo.ios) {
-		inteveral *= 1000;
-	}
-	evaluatingData.backtime = inteveral || Date.now();
-	console.log(
-		"🚀 ~ 开始时间点:",
-		evaluatingData.backtime,
-		"已经录的时间:",
-		Date.now() - inteveral,
-		"记录时间点:",
-		Date.now()
-	);
-	// 是否禁播
-	if (evaluatingData.isDisabledPlayMusic) {
-		return;
-	}
-	// 开始播放
-	playMusic();
+  console.error("开始录音");
+  // 没有开始评测,不处理
+  if (!evaluatingData.startBegin) return;
+  let inteveral = res?.content?.inteveral || 0;
+  if (browserInfo.ios) {
+    inteveral *= 1000;
+  }
+  evaluatingData.backtime = inteveral || Date.now();
+  console.log("🚀 ~ 开始时间点:", evaluatingData.backtime, "已经录的时间:", Date.now() - inteveral, "记录时间点:", Date.now());
+  // 是否禁播
+  if (evaluatingData.isDisabledPlayMusic) {
+    return;
+  }
+  // 开始播放
+  playMusic();
 };
 
 /**
@@ -487,318 +476,311 @@ const recordStartTimePoint = async (res?: IPostMessage) => {
  * @returns
  */
 export const handleEndEvaluat = (isComplete = false) => {
-	// 没有开始评测 , 不是评测模式 , 不评分
-	if (!evaluatingData.startBegin || state.modeType !== "evaluating") return;
-	// 结束录音
-	// api_stopRecording();
-	// 结束评测
-	console.log('评测结束1')
-	endEvaluating({
-		musicScoreId: state.examSongId,
-	});
-	// 评测作业如果不是完整评测,给出提示
-    if (!isComplete && data.trainingType === 'EVALUATION') {
-		showToast({
-			message: "完整演奏结束才算作业分数!",
-		});
-	} else {
-		showLoadingToast({
-			message: "评分中",
-			duration: 0,
-			overlay: true,
-			overlayClass: styles.scoreMode,
-		});
-	}
-	setTimeout(() => {
-		evaluatingData.startBegin = false;
-	}, 500);
-	evaluatingData.isComplete = isComplete;
-	// 如果开启了摄像头, 结束录制视频
-	if (state.setting.camera) {
-		console.log("结束录制视频");
-		api_endCapture();
-	}
+  // 没有开始评测 , 不是评测模式 , 不评分
+  if (!evaluatingData.startBegin || state.modeType !== "evaluating") return;
+  // 结束录音
+  // api_stopRecording();
+  // 结束评测
+  console.log("评测结束1");
+  endEvaluating({
+    musicScoreId: state.examSongId,
+  });
+  // 评测作业如果不是完整评测,给出提示
+  if (!isComplete && data.trainingType === "EVALUATION") {
+    showToast({
+      message: "完整演奏结束才算作业分数!",
+    });
+  } else {
+    showLoadingToast({
+      message: "评分中",
+      duration: 0,
+      overlay: true,
+      overlayClass: styles.scoreMode,
+    });
+  }
+  setTimeout(() => {
+    evaluatingData.startBegin = false;
+  }, 500);
+  evaluatingData.isComplete = isComplete;
+  // 如果开启了摄像头, 结束录制视频
+  if (state.setting.camera) {
+    console.log("结束录制视频");
+    api_endCapture();
+  }
 };
 
 /**
  * 结束评测(手动结束评测)
  */
 export const handleEndBegin = () => {
-	handleEndEvaluat();
-	handleStopPlay();
+  handleEndEvaluat();
+  handleStopPlay();
 };
 
 /**
  * 取消评测
  */
 export const handleCancelEvaluat = (cancelType?: string) => {
-	evaluatingData.evaluatings = {};
-	evaluatingData.startBegin = false;
-	// 关闭提示
-	closeToast();
-	// 取消记录
-	api_proxyServiceMessage({
-		header: {
-			commond: "recordCancel",
-			type: "SOUND_COMPARE",
-			status: 200,
-		},
-	});
-	/**
-	 * 异常状态是取消评测(cancelEvaluating),正常结束时结束评测(endEvaluating)
-	 */
-	// if (cancelType === "cancel") {
-	// 	// 取消评测
-	// 	cancelEvaluating();
-	// } else {
-	// 	endEvaluating({
-	// 		musicScoreId: state.examSongId,
-	// 	});
-	// }
-	
-	cancelEvaluating();
-	// 停止播放
-	handleStopPlay();
-	console.log('评测结束2')
-	endEvaluating({
-		musicScoreId: state.examSongId,
-	});
-	// 如果开启了摄像头, 结束录制视频
-	if (state.setting.camera) {
-		console.log("结束录制视频");
-		api_endCapture();
-	}
+  evaluatingData.evaluatings = {};
+  evaluatingData.startBegin = false;
+  // 关闭提示
+  closeToast();
+  // 取消记录
+  api_proxyServiceMessage({
+    header: {
+      commond: "recordCancel",
+      type: "SOUND_COMPARE",
+      status: 200,
+    },
+  });
+  /**
+   * 异常状态是取消评测(cancelEvaluating),正常结束时结束评测(endEvaluating)
+   */
+  // if (cancelType === "cancel") {
+  // 	// 取消评测
+  // 	cancelEvaluating();
+  // } else {
+  // 	endEvaluating({
+  // 		musicScoreId: state.examSongId,
+  // 	});
+  // }
+
+  cancelEvaluating();
+  // 停止播放
+  handleStopPlay();
+  console.log("评测结束2");
+  endEvaluating({
+    musicScoreId: state.examSongId,
+  });
+  // 如果开启了摄像头, 结束录制视频
+  if (state.setting.camera) {
+    console.log("结束录制视频");
+    api_endCapture();
+  }
 };
 
 /** 查看报告 */
-export const handleViewReport = (
-	key: "recordId" | "recordIdStr",
-	type: "gym" | "colexiu" | "orchestra" | "instrument"
-) => {
-	const id = evaluatingData.resultData?.[key] || "";
-	let url = "";
-	switch (type) {
-		case "gym":
-			url = location.origin + location.pathname + "#/report/" + id;
-			break;
-		case "orchestra":
-			url = location.origin + location.pathname + "report-share.html?id=" + id;
-			break;
-		case "instrument":
-			url = location.origin + location.pathname + "#/evaluat-report?id=" + id + "&musicRenderType=" + state.musicRenderType;
-			break;
-		default:
-			url = location.origin + location.pathname + "report-share.html?id=" + id;
-			break;
-	}
-	api_openWebView({
-		url,
-		orientation: 0,
-		isHideTitle: true, // 此处兼容安卓,意思为隐藏全部头部
-		statusBarTextColor: false,
-		isOpenLight: true,
-		c_orientation: 0,
-	});
+export const handleViewReport = (key: "recordId" | "recordIdStr", type: "gym" | "colexiu" | "orchestra" | "instrument") => {
+  const id = evaluatingData.resultData?.[key] || "";
+  let url = "";
+  switch (type) {
+    case "gym":
+      url = location.origin + location.pathname + "#/report/" + id;
+      break;
+    case "orchestra":
+      url = location.origin + location.pathname + "report-share.html?id=" + id;
+      break;
+    case "instrument":
+      url = location.origin + location.pathname + "#/evaluat-report?id=" + id + "&musicRenderType=" + state.musicRenderType;
+      break;
+    default:
+      url = location.origin + location.pathname + "report-share.html?id=" + id;
+      break;
+  }
+  api_openWebView({
+    url,
+    orientation: 0,
+    isHideTitle: true, // 此处兼容安卓,意思为隐藏全部头部
+    statusBarTextColor: false,
+    isOpenLight: true,
+    c_orientation: 0,
+  });
 };
 
 // 隐藏存演奏按钮
 const handleComplexButton = (res?: IPostMessage) => {
-	console.log('监听是否隐藏保存按钮', res)
-	if (res?.content) {
-		const { header, body } = res.content;
-		state.isHideEvaluatReportSaveBtn = true
-	}
+  console.log("监听是否隐藏保存按钮", res);
+  if (res?.content) {
+    const { header, body } = res.content;
+    state.isHideEvaluatReportSaveBtn = true;
+  }
 };
 
 // 检测到APP发送的异常信息
 const handleAccompanyError = (res?: IPostMessage) => {
-	console.log('异常信息返回', res)
-	if (res?.content) {
-		const { type, reson } = res.content;
-		switch (type) {
-			case "enterBackground":
-				// App退到后台
-			case "playError":
-				// 播放异常
-			case "socketError":
-				// socket连接断开,评测中,则取消评测
-				// 延迟检测中
-				if (evaluatingData.soundEffectMode) {
-					evaluatingData.socketErrorStatus = 0
-					evaluatingData.delayCheckSocketError = true
-					evaluatingData.socketErrorPop = type === "socketError" ? true : false
-					evaluatingData.accompanyErrorType = type
-					// api_checkSocketStatus()
-					return
-				}
-				// 评测中
-				if (state.modeType === "evaluating" && evaluatingData.startBegin) {
-					handleCancelEvaluat('cancel');
-				}
-				// 关闭节拍器
-				closeTick()
-				evaluatingData.socketErrorStatus = 0
-				evaluatingData.socketErrorPop = type === "socketError" ? true : false
-				evaluatingData.isErrorState = true
-				evaluatingData.accompanyErrorType = type
-				resetPlaybackToStart();
-				break;	
-			case "recordError":
-				// 录音异常
-				break;											
-			default:
-				break;
-		}
-	}
+  console.log("异常信息返回", res);
+  if (res?.content) {
+    const { type, reson } = res.content;
+    switch (type) {
+      case "enterBackground":
+      // App退到后台
+      case "playError":
+      // 播放异常
+      case "socketError":
+        // socket连接断开,评测中,则取消评测
+        // 延迟检测中
+        if (evaluatingData.soundEffectMode) {
+          evaluatingData.socketErrorStatus = 0;
+          evaluatingData.delayCheckSocketError = true;
+          evaluatingData.socketErrorPop = type === "socketError" ? true : false;
+          evaluatingData.accompanyErrorType = type;
+          // api_checkSocketStatus()
+          return;
+        }
+        // 评测中
+        if (state.modeType === "evaluating" && evaluatingData.startBegin) {
+          handleCancelEvaluat("cancel");
+        }
+        // 关闭节拍器
+        closeTick();
+        evaluatingData.socketErrorStatus = 0;
+        evaluatingData.socketErrorPop = type === "socketError" ? true : false;
+        evaluatingData.isErrorState = true;
+        evaluatingData.accompanyErrorType = type;
+        resetPlaybackToStart();
+        break;
+      case "recordError":
+        // 录音异常
+        break;
+      default:
+        break;
+    }
+  }
 };
 
 // 监测socket状态,是否已经成功连接
 const handleSocketStatus = (res?: IPostMessage) => {
-	if (res?.content?.status === "connected") {
-		const currentTime = +new Date()
-		evaluatingData.delayCheckSocketError = false
-		const diffTime = currentTime - socketStartTime
-		if (diffTime < 1000) {
-			const remainingTime = 1000 - diffTime
-			console.log(remainingTime,99999)
-			setTimeout(() => {
-				evaluatingData.socketErrorStatus = 2
-			}, remainingTime);
-		}
-	}
-}
+  if (res?.content?.status === "connected") {
+    const currentTime = +new Date();
+    evaluatingData.delayCheckSocketError = false;
+    const diffTime = currentTime - socketStartTime;
+    if (diffTime < 1000) {
+      const remainingTime = 1000 - diffTime;
+      console.log(remainingTime, 99999);
+      setTimeout(() => {
+        evaluatingData.socketErrorStatus = 2;
+      }, remainingTime);
+    }
+  }
+};
 
 // 评测出现异常,再试一次
 const hanldeConfirmPop = async () => {
-	api_checkSocketStatus();
-	evaluatingData.socketErrorStatus = 1
-	socketStartTime = +new Date()
-}
+  api_checkSocketStatus();
+  evaluatingData.socketErrorStatus = 1;
+  socketStartTime = +new Date();
+};
 
 // 关闭异常弹窗
 const hanldeClosePop = () => {
-	evaluatingData.socketErrorPop = false
-	evaluatingData.socketErrorStatus = 0
-}
+  evaluatingData.socketErrorPop = false;
+  evaluatingData.socketErrorStatus = 0;
+};
 
 export default defineComponent({
-	name: "evaluating",
-	setup() {
-		const pageVisibility = usePageVisibility();
-		// 需要记录的数据
-		const record_old_data = reactive({
-			/** 指法 */
-			finger: false,
-			/** 原音伴奏 */
-			play_mode: "" as IPlayState,
-			/** 评测是否要伴奏 */
-			enableAccompaniment: true,
-		});
-		/** 记录状态 */
-		const hanlde_record = () => {
-			// 取消指法
-			record_old_data.finger = state.setting.displayFingering;
-			state.setting.displayFingering = false;
-			// 切换为伴奏
-			record_old_data.play_mode = state.playSource;
-			record_old_data.enableAccompaniment = state.setting.enableAccompaniment;
-			// 如果关闭伴奏,评测静音
-			if (!record_old_data.enableAccompaniment) {
-				console.log("关闭伴奏");
-				toggleMutePlayAudio(record_old_data.play_mode === "music" ? "music" : record_old_data.play_mode === "background" ? "background" : "mingSong", true);
-			}
-		};
-		/** 还原状态 */
-		const handle_reduction = () => {
-			// 还原指法
-			state.setting.displayFingering = record_old_data.finger;
-			state.playSource = record_old_data.play_mode;
-
-			// 如果关闭伴奏, 结束评测取消静音
-			if (!record_old_data.enableAccompaniment) {
-				toggleMutePlayAudio(record_old_data.play_mode === "music" ? "music" : record_old_data.play_mode === "background" ? "background" : "mingSong", false);
-			}
-		};
-
-		watch(pageVisibility, (value) => {
-			if (value == "hidden" && evaluatingData.startBegin) {
-				// handleEndBegin();
-			}
-		});
-		watch(
-			() => evaluatingData.socketErrorStatus,
-			() => {
-				if (evaluatingData.socketErrorStatus === 2) {
-					setTimeout(() => {
-						evaluatingData.socketErrorPop = false
-						// evaluatingData.socketErrorStatus = 0
-					}, 1000);
-				}
-			}
-		);
-		watch(
-			() => evaluatingData.socketErrorPop,
-			() => {
-				if (evaluatingData.socketErrorPop && state.setting.soundEffect) {
-					// 监听到socket状态异常,需要关闭延迟检测
-					api_closeDelayCheck({});
-				}	
-			}
-		);
-		onMounted(() => {
-			resetPlaybackToStart();
-			hanlde_record();
-			evaluatingData.resultData = {};
-
-			// evaluatingData.resulstMode = true;
-			// evaluatingData.resultData = {...getLeveByScore(10), score: 10, intonation: 10, cadence: 30, integrity: 40}
-			// console.log("🚀 ~ evaluatingData.resultData:", evaluatingData.resultData)
-
-			evaluatingData.evaluatings = {};
-			evaluatingData.soundEffectFrequency = 0;
-			evaluatingData.checkStep = 0;
-			evaluatingData.rendered = true;
-			sendResult(handleScoreResult);
-			hideComplexButton(handleComplexButton, true);
-			api_recordStartTime(recordStartTimePoint);
-			addAccompanyError(handleAccompanyError);
-			addSocketStatus(handleSocketStatus);
-			// 不是选段模式评测, 就清空已选段
-			if (!state.isSelectMeasureMode) {
-				clearSelection();
-			}
-			console.log("加载评测模块成功");
-		});
-		onUnmounted(() => {
-			evaluatingData.checkEnd = false;
-			evaluatingData.rendered = false;
-			resetPlaybackToStart();
-			removeResult(handleScoreResult);
-			hideComplexButton(() => {}, false);
-			api_remove_recordStartTime(recordStartTimePoint);
-			handle_reduction();
-			removeAccompanyError(handleAccompanyError);
-			removeSocketStatus(handleSocketStatus);
-			api_disconnectSocket();
-			console.log("卸载评测模块成功");
-		});
-		return () => (
-			<div>
-				{/** 预加载一下断网需要用到的图片 */}
-				<div class={styles.hiddenPop}>
-					<img src={popImgs.icon_bg} />
-					<img src={popImgs.icon_btn} />
-					<img src={popImgs.icon_success} />
-					<img src={popImgs.icon_close} />
-				</div>
-				<Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale"]} transition="van-scale" v-model:show={evaluatingData.socketErrorPop}>
-					<AbnormalPop 
-						onConfirm={hanldeConfirmPop}
-						onClose={hanldeClosePop} 
-					/>
-				</Popup>
-
-			</div>
-		);
-	},
+  name: "evaluating",
+  setup() {
+    const pageVisibility = usePageVisibility();
+    // 需要记录的数据
+    const record_old_data = reactive({
+      /** 指法 */
+      finger: false,
+      /** 原音伴奏 */
+      play_mode: "" as IPlayState,
+      /** 评测是否要伴奏 */
+      enableAccompaniment: true,
+    });
+    /** 记录状态 */
+    const hanlde_record = () => {
+      // 取消指法
+      record_old_data.finger = state.setting.displayFingering;
+      state.setting.displayFingering = false;
+      // 切换为伴奏
+      record_old_data.play_mode = state.playSource;
+      record_old_data.enableAccompaniment = state.setting.enableAccompaniment;
+      // 如果关闭伴奏,评测静音
+      if (!record_old_data.enableAccompaniment) {
+        console.log("关闭伴奏");
+        toggleMutePlayAudio(record_old_data.play_mode === "music" ? "music" : record_old_data.play_mode === "background" ? "background" : "mingSong", true);
+      }
+    };
+    /** 还原状态 */
+    const handle_reduction = () => {
+      // 还原指法
+      state.setting.displayFingering = record_old_data.finger;
+      state.playSource = record_old_data.play_mode;
+
+      // 如果关闭伴奏, 结束评测取消静音
+      if (!record_old_data.enableAccompaniment) {
+        toggleMutePlayAudio(record_old_data.play_mode === "music" ? "music" : record_old_data.play_mode === "background" ? "background" : "mingSong", false);
+      }
+    };
+
+    watch(pageVisibility, (value) => {
+      if (value == "hidden" && evaluatingData.startBegin) {
+        // handleEndBegin();
+      }
+    });
+    watch(
+      () => evaluatingData.socketErrorStatus,
+      () => {
+        if (evaluatingData.socketErrorStatus === 2) {
+          setTimeout(() => {
+            evaluatingData.socketErrorPop = false;
+            // evaluatingData.socketErrorStatus = 0
+          }, 1000);
+        }
+      }
+    );
+    watch(
+      () => evaluatingData.socketErrorPop,
+      () => {
+        if (evaluatingData.socketErrorPop && state.setting.soundEffect) {
+          // 监听到socket状态异常,需要关闭延迟检测
+          api_closeDelayCheck({});
+        }
+      }
+    );
+    onMounted(() => {
+      resetPlaybackToStart();
+      hanlde_record();
+      evaluatingData.resultData = {};
+
+      // evaluatingData.resulstMode = true;
+      // evaluatingData.resultData = {...getLeveByScore(10), score: 10, intonation: 10, cadence: 30, integrity: 40}
+      // console.log("🚀 ~ evaluatingData.resultData:", evaluatingData.resultData)
+
+      evaluatingData.evaluatings = {};
+      evaluatingData.soundEffectFrequency = 0;
+      evaluatingData.checkStep = 0;
+      evaluatingData.rendered = true;
+      sendResult(handleScoreResult);
+      hideComplexButton(handleComplexButton, true);
+      api_recordStartTime(recordStartTimePoint);
+      addAccompanyError(handleAccompanyError);
+      addSocketStatus(handleSocketStatus);
+      // 不是选段模式评测, 就清空已选段
+      if (!state.isSelectMeasureMode) {
+        clearSelection();
+      }
+      console.log("加载评测模块成功");
+    });
+    onUnmounted(() => {
+      evaluatingData.checkEnd = false;
+      evaluatingData.rendered = false;
+      resetPlaybackToStart();
+      removeResult(handleScoreResult);
+      hideComplexButton(() => {}, false);
+      api_remove_recordStartTime(recordStartTimePoint);
+      handle_reduction();
+      removeAccompanyError(handleAccompanyError);
+      removeSocketStatus(handleSocketStatus);
+      api_disconnectSocket();
+      console.log("卸载评测模块成功");
+    });
+    return () => (
+      <div>
+        {/** 预加载一下断网需要用到的图片 */}
+        <div class={styles.hiddenPop}>
+          <img src={popImgs.icon_bg} />
+          <img src={popImgs.icon_btn} />
+          <img src={popImgs.icon_success} />
+          <img src={popImgs.icon_close} />
+        </div>
+        <Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale"]} transition="van-scale" v-model:show={evaluatingData.socketErrorPop}>
+          <AbnormalPop onConfirm={hanldeConfirmPop} onClose={hanldeClosePop} />
+        </Popup>
+      </div>
+    );
+  },
 });

+ 71 - 90
src/view/fingering/index.tsx

@@ -4,98 +4,79 @@ import state from "/src/state";
 import { getFingeringConfig, ITypeFingering } from "./fingering-config";
 
 export default defineComponent({
-	name: "fingering",
-	emits: ["open"],
-	setup(props, { emit }) {
-		const fingerData = reactive({
-			relationshipIndex: 0,
-			subject: null as unknown as ITypeFingering,
-			delay: 0,
-		});
-		const getFingeringData = async () => {
-			fingerData.subject = await getFingeringConfig(state.fingeringInfo.name);
-			console.log("🚀 ~ fingerData.subject:", fingerData.subject)
-		};
-		onBeforeMount(() => {
-			getFingeringData();
-		});
+  name: "fingering",
+  emits: ["open"],
+  setup(props, { emit }) {
+    const fingerData = reactive({
+      relationshipIndex: 0,
+      subject: null as unknown as ITypeFingering,
+      delay: 0,
+    });
+    const getFingeringData = async () => {
+      fingerData.subject = await getFingeringConfig(state.fingeringInfo.name);
+      console.log("🚀 ~ fingerData.subject:", fingerData.subject);
+    };
+    onBeforeMount(() => {
+      getFingeringData();
+    });
 
-		/** 当前音域 */
-		const realKey = computed(() => {
-			return state.times[state.activeNoteIndex]?.realKey || -1;
-		});
+    /** 当前音域 */
+    const realKey = computed(() => {
+      return state.times[state.activeNoteIndex]?.realKey || -1;
+    });
 
-		const doubeClick = () => {
-			const nowTime = Date.now();
-			if (nowTime - fingerData.delay < 300) {
-				emit("open");
-				return;
-			}
-			fingerData.delay = nowTime;
-		};
-		return () => {
-			// console.log("音高", realKey.value);
-			const relationship = fingerData.subject?.relationship?.[realKey.value] || [];
-			const rs: number[] = Array.isArray(relationship[1])
-				? relationship[fingerData.relationshipIndex]
-				: relationship;
-			const canTizhi = Array.isArray(relationship[1]);
-			return (
-				<>
-					{state.fingeringInfo.direction === "transverse" ? (
-						<div onClick={() => doubeClick()} class={[styles.fingeringContainer]}>
+    const doubeClick = () => {
+      const nowTime = Date.now();
+      if (nowTime - fingerData.delay < 300) {
+        emit("open");
+        return;
+      }
+      fingerData.delay = nowTime;
+    };
+    return () => {
+      // console.log("音高", realKey.value);
+      const relationship = fingerData.subject?.relationship?.[realKey.value] || [];
+      const rs: number[] = Array.isArray(relationship[1]) ? relationship[fingerData.relationshipIndex] : relationship;
+      const canTizhi = Array.isArray(relationship[1]);
+      return (
+        <>
+          {state.fingeringInfo.direction === "transverse" ? (
+            <div onClick={() => doubeClick()} class={[styles.fingeringContainer]}>
+              <div class={styles.imgs}>
+                <img class="driver-7" src={fingerData.subject?.json?.full} />
+                {rs.map((key: number | string, index: number) => {
+                  const nk: string = typeof key === "string" ? key.replace("active-", "") : String(key);
+                  return <img data-index={nk} src={fingerData.subject?.json?.[nk]} />;
+                })}
+              </div>
 
-							<div class={styles.imgs}>
-								<img src={fingerData.subject?.json?.full} />
-								{rs.map((key: number | string, index: number) => {
-									const nk: string = typeof key === "string" ? key.replace("active-", "") : String(key);
-									return <img data-index={nk} src={fingerData.subject?.json?.[nk]} />;
-								})}
-							</div>
+              {state.fingeringInfo.hasTizhi && (
+                <div class={[styles.tizhi, canTizhi && styles.canDisplay]} onClick={() => (fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)}>
+                  替指
+                </div>
+              )}
+            </div>
+          ) : (
+            <div onClick={() => doubeClick()} class={[styles.fingeringContainer, styles.vertical, state.fingeringInfo.name]}>
+              <div class={styles.imgs}>
+                <img class="driver-7" src={fingerData.subject?.json?.full} />
+                {rs.map((key: number | string, index: number) => {
+                  const nk: string = typeof key === "string" ? key.replace("active-", "") : String(key);
+                  return <img data-index={nk} src={fingerData.subject?.json?.[nk]} />;
+                })}
+              </div>
 
-							{state.fingeringInfo.hasTizhi && (
-								<div
-									class={[styles.tizhi, canTizhi && styles.canDisplay]}
-									onClick={() =>
-										(fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)
-									}
-								>
-									替指
-								</div>
-							)}
-						</div>
-					) : (
-						<div
-							onClick={() => doubeClick()}
-							class={[styles.fingeringContainer, styles.vertical, state.fingeringInfo.name]}
-						>
-							<div class={styles.imgs}>
-								<img src={fingerData.subject?.json?.full} />
-								{rs.map((key: number | string, index: number) => {
-									const nk: string = typeof key === "string" ? key.replace("active-", "") : String(key);
-									return <img data-index={nk} src={fingerData.subject?.json?.[nk]} />;
-								})}
-							</div>
-
-							{state.fingeringInfo.hasTizhi && (
-								<div
-									style={{ display: state.fingeringInfo.disabledFinger ? "none" : "" }}
-									class={styles.rightContent}
-								>
-									<div
-										class={[styles.tizhi, canTizhi && styles.canDisplay]}
-										onClick={() =>
-											(fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)
-										}
-									>
-										替指
-									</div>
-								</div>
-							)}
-						</div>
-					)}
-				</>
-			);
-		};
-	},
+              {state.fingeringInfo.hasTizhi && (
+                <div style={{ display: state.fingeringInfo.disabledFinger ? "none" : "" }} class={styles.rightContent}>
+                  <div class={[styles.tizhi, canTizhi && styles.canDisplay]} onClick={() => (fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)}>
+                    替指
+                  </div>
+                </div>
+              )}
+            </div>
+          )}
+        </>
+      );
+    };
+  },
 });

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است