lex 11 месяцев назад
Родитель
Сommit
41814cdf80

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


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

@@ -307,4 +307,107 @@
       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;
+    }
+  }
 }

+ 335 - 1
src/page-instrument/custom-plugins/guide-driver/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, nextTick, ref } from "vue";
+import { 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";
@@ -210,6 +210,16 @@ export const PractiseDriver = defineComponent({
 
     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 getAllGuidance = async () => {
       try {
@@ -224,6 +234,7 @@ export const PractiseDriver = defineComponent({
           }
         }
         if (!(guideInfo.value && guideInfo.value.practiseDriver)) {
+          document.addEventListener("click", handleClickOutside, true);
           driverObj = driver(driverOptions);
           nextTick(() => {
             driverObj.drive(0);
@@ -245,6 +256,7 @@ export const PractiseDriver = defineComponent({
       }
       endGuide(guideInfo.value);
       driverObj.destroy();
+      document.removeEventListener("click", handleClickOutside);
     };
 
     return () => <div></div>;
@@ -338,6 +350,15 @@ export const FollowDriver = defineComponent({
 
     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 getAllGuidance = async () => {
       try {
@@ -352,6 +373,7 @@ export const FollowDriver = defineComponent({
           }
         }
         if (!(guideInfo.value && guideInfo.value.followDriver)) {
+          document.addEventListener("click", handleClickOutside, true);
           driverObj = driver(driverOptions);
           nextTick(() => {
             driverObj.drive(0);
@@ -373,6 +395,7 @@ export const FollowDriver = defineComponent({
       }
       endGuide(guideInfo.value);
       driverObj.destroy();
+      document.removeEventListener("click", handleClickOutside);
     };
 
     return () => <div></div>;
@@ -465,6 +488,16 @@ export const EvaluatingDriver = defineComponent({
     };
     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 getAllGuidance = async () => {
       try {
@@ -479,6 +512,7 @@ export const EvaluatingDriver = defineComponent({
           }
         }
         if (!(guideInfo.value && guideInfo.value.evaluatingDriver)) {
+          document.addEventListener("click", handleClickOutside, true);
           driverObj = driver(driverOptions);
           nextTick(() => {
             driverObj.drive(0);
@@ -502,6 +536,7 @@ export const EvaluatingDriver = defineComponent({
       }
       endGuide(guideInfo.value);
       driverObj?.destroy();
+      document.removeEventListener("click", handleClickOutside);
     };
 
     return () => <div></div>;
@@ -617,6 +652,15 @@ export const EvaluatingResultDriver = defineComponent({
     };
     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 getAllGuidance = async () => {
       try {
@@ -631,6 +675,7 @@ export const EvaluatingResultDriver = defineComponent({
           }
         }
         if (!(guideInfo.value && guideInfo.value.evaluatingResultDriver)) {
+          document.addEventListener("click", handleClickOutside, true);
           driverObj = driver(driverOptions);
           nextTick(() => {
             driverObj.drive(0);
@@ -652,6 +697,295 @@ export const EvaluatingResultDriver = defineComponent({
       }
       endGuide(guideInfo.value);
       driverObj.destroy();
+      document.removeEventListener("click", handleClickOutside);
+    };
+
+    return () => <div></div>;
+  },
+});
+
+// 评测报告
+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", "close"],
+              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", "close"],
+              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", "close"],
+              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", "close"],
+            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", "close"],
+            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", "close"],
+              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", "close"],
+              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", "close"],
+            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 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();
+          });
+        }
+      } 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.removeEventListener("click", handleClickOutside);
     };
 
     return () => <div></div>;

+ 64 - 28
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,29 +319,34 @@
             }
         }
     }
-    .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;
@@ -325,11 +355,12 @@
                 bottom: 0;
             }
         }
-        .audioVisualizer{
+
+        .audioVisualizer {
             position: absolute;
             top: 50%;
             left: 50%;
-            transform: translate(-50%,-50%);
+            transform: translate(-50%, -50%);
             width: 370px;
             height: 66px;
         }
@@ -363,6 +394,7 @@
             margin-left: 4px;
             color: #fff;
         }
+
         &>i {
             width: 12px;
             height: 12px;
@@ -389,6 +421,7 @@
     max-width: 460px;
     padding: 10px;
     position: relative;
+
     &::before {
         content: "";
         position: absolute;
@@ -399,6 +432,7 @@
         background: #D5E8FF;
         border-radius: 14px;
     }
+
     .shiyiTop {
         position: absolute;
         width: 154px;
@@ -451,10 +485,12 @@
         &:nth-child(2n) {
             transform: translateX(20px);
         }
+
         span {
             font-size: 12px;
             font-weight: 400;
         }
+
         &>i {
             width: 12px;
             height: 12px;

+ 459 - 488
src/page-instrument/view-evaluat-report/component/share-top/index.tsx

@@ -20,236 +20,232 @@ 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;
-			if (shareData.isInitPlyr) return;
-			nextTick(() => {
-				const id = mediaType.value === "audio" ? "#audioSrc" : "#videoSrc";
-				shareData._plrl = new Plyr(id, {
-					controls: ["play-large", "play", "progress", "current-time", "duration"],
-					fullscreen: { enabled: false },
-				});
-				// 创建音波数据
-				if(mediaType.value === "audio"){
-					setTimeout(() => {
-						const audioDom = document.querySelector("#audioSrc") as HTMLAudioElement
-						const canvasDom = document.querySelector("#audioVisualizer") as HTMLCanvasElement
-						const { pauseVisualDraw, playVisualDraw } = audioVisualDraw(audioDom, canvasDom)
-						shareData._plrl.on('play', () => {
-							lottieDom.value.play()
-							lottieDom1.value.play()
-							lottieDom2.value.play()
-							playVisualDraw()
-						});
-						shareData._plrl.on('pause', () => {
-							lottieDom.value.pause()
-							lottieDom1.value.pause()
-							lottieDom2.value.pause()
-							pauseVisualDraw()
-						});
-					}, 300); // 弹窗动画是0.25秒 这里用定时器 确保canvas 能获取到宽高
-				}
-				shareData.isInitPlyr = true;
-			});
-		};
-		/**
-		 * 音频可视化
-		 * @param audioDom 
-		 * @param canvasDom 
-		 * @param fftSize  2的幂数,最小为32
-		 */
-		function audioVisualDraw(audioDom: HTMLAudioElement, canvasDom: HTMLCanvasElement, fftSize = 128) {
-			type propsType = { canvWidth: number; canvHeight: number; canvFillColor: string; lineColor: string; lineGap: number }
-			// canvas
-			const canvasCtx = canvasDom.getContext("2d")!
-			const { width, height } = canvasDom.getBoundingClientRect()
-			canvasDom.width = width
-			canvasDom.height = height
-			// audio
-			const audioCtx = new AudioContext()
-			const analyser = audioCtx.createAnalyser()
-			const source = audioCtx.createMediaElementSource(audioDom)
-			analyser.fftSize = fftSize
-			source.connect(analyser)
-			analyser.connect(audioCtx.destination)
-			const dataArray = new Uint8Array(analyser.frequencyBinCount)
-			const draw = (data: Uint8Array, ctx: CanvasRenderingContext2D, { lineGap, canvWidth, canvHeight, canvFillColor, lineColor }: propsType) => {
-				if (!ctx) return
-				const w = canvWidth
-				const h = canvHeight
-				fillCanvasBackground(ctx, w, h, canvFillColor)
-			   // 可视化
-				const dataLen = data.length
-				const step = (w / 2 - lineGap * dataLen) / dataLen
-				const midX = w / 2
-				const midY = h / 2
-				let xLeft = midX
-				for (let i = 0; i < dataLen; i++) {
-					const value = data[i]
-					const percent = value / 255 // 最大值为255
-					const barHeight = percent * midY
-					canvasCtx.fillStyle = lineColor
-					// 中间加间隙
-					if (i === 0) {
-						xLeft -= lineGap / 2
-					}
-					canvasCtx.fillRect(xLeft - step, midY - barHeight, step, barHeight)
-					canvasCtx.fillRect(xLeft - step, midY, step, barHeight)
-					xLeft -= step + lineGap
-				}
-				let xRight = midX
-				for (let i = 0; i < dataLen; i++) {
-					const value = data[i]
-					const percent = value / 255 // 最大值为255
-					const barHeight = percent * midY
-					canvasCtx.fillStyle = lineColor
-					if (i === 0) {
-						xRight += lineGap / 2
-					}
-					canvasCtx.fillRect(xRight, midY - barHeight, step, barHeight)
-					canvasCtx.fillRect(xRight, midY, step, barHeight)
-					xRight += step + lineGap
-				}
-			}
-			const fillCanvasBackground = (ctx: CanvasRenderingContext2D, w: number, h: number, colors: string) => {
-				ctx.clearRect(0, 0, w, h)
-				ctx.fillStyle = colors
-				ctx.fillRect(0, 0, w, h)
-			}
-			const requestAnimationFrameFun = () => {
-				requestAnimationFrame(() => {
-					analyser.getByteFrequencyData(dataArray)
-					draw(dataArray, canvasCtx, {
-						lineGap: 2,
-						canvWidth: width,
-						canvHeight: height,
-						canvFillColor: "transparent",
-						lineColor: "rgba(255, 255, 255, 0.3)"
-					})
-					if (!isPause) {
-						requestAnimationFrameFun()
-					}
-				})
-			}
-			let isPause = true
-			const playVisualDraw = () => {
-				isPause = false
-				audioCtx.resume()
-				requestAnimationFrameFun()
-			}
-			const pauseVisualDraw = () => {
-				isPause = true
-				audioCtx.suspend()
-			}
-			return {
-				playVisualDraw,
-				pauseVisualDraw
-			}
-		}
-		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>
+    const openAudioAndVideo = () => {
+      shareData.show = true;
+      if (shareData.isInitPlyr) return;
+      nextTick(() => {
+        const id = mediaType.value === "audio" ? "#audioSrc" : "#videoSrc";
+        shareData._plrl = new Plyr(id, {
+          controls: ["play-large", "play", "progress", "current-time", "duration"],
+          fullscreen: { enabled: false },
+        });
+        // 创建音波数据
+        if (mediaType.value === "audio") {
+          setTimeout(() => {
+            const audioDom = document.querySelector("#audioSrc") as HTMLAudioElement;
+            const canvasDom = document.querySelector("#audioVisualizer") as HTMLCanvasElement;
+            const { pauseVisualDraw, playVisualDraw } = audioVisualDraw(audioDom, canvasDom);
+            shareData._plrl.on("play", () => {
+              lottieDom.value.play();
+              lottieDom1.value.play();
+              lottieDom2.value.play();
+              playVisualDraw();
+            });
+            shareData._plrl.on("pause", () => {
+              lottieDom.value.pause();
+              lottieDom1.value.pause();
+              lottieDom2.value.pause();
+              pauseVisualDraw();
+            });
+          }, 300); // 弹窗动画是0.25秒 这里用定时器 确保canvas 能获取到宽高
+        }
+        shareData.isInitPlyr = true;
+      });
+    };
+    /**
+     * 音频可视化
+     * @param audioDom
+     * @param canvasDom
+     * @param fftSize  2的幂数,最小为32
+     */
+    function audioVisualDraw(audioDom: HTMLAudioElement, canvasDom: HTMLCanvasElement, fftSize = 128) {
+      type propsType = { canvWidth: number; canvHeight: number; canvFillColor: string; lineColor: string; lineGap: number };
+      // canvas
+      const canvasCtx = canvasDom.getContext("2d")!;
+      const { width, height } = canvasDom.getBoundingClientRect();
+      canvasDom.width = width;
+      canvasDom.height = height;
+      // audio
+      const audioCtx = new AudioContext();
+      const analyser = audioCtx.createAnalyser();
+      const source = audioCtx.createMediaElementSource(audioDom);
+      analyser.fftSize = fftSize;
+      source.connect(analyser);
+      analyser.connect(audioCtx.destination);
+      const dataArray = new Uint8Array(analyser.frequencyBinCount);
+      const draw = (data: Uint8Array, ctx: CanvasRenderingContext2D, { lineGap, canvWidth, canvHeight, canvFillColor, lineColor }: propsType) => {
+        if (!ctx) return;
+        const w = canvWidth;
+        const h = canvHeight;
+        fillCanvasBackground(ctx, w, h, canvFillColor);
+        // 可视化
+        const dataLen = data.length;
+        const step = (w / 2 - lineGap * dataLen) / dataLen;
+        const midX = w / 2;
+        const midY = h / 2;
+        let xLeft = midX;
+        for (let i = 0; i < dataLen; i++) {
+          const value = data[i];
+          const percent = value / 255; // 最大值为255
+          const barHeight = percent * midY;
+          canvasCtx.fillStyle = lineColor;
+          // 中间加间隙
+          if (i === 0) {
+            xLeft -= lineGap / 2;
+          }
+          canvasCtx.fillRect(xLeft - step, midY - barHeight, step, barHeight);
+          canvasCtx.fillRect(xLeft - step, midY, step, barHeight);
+          xLeft -= step + lineGap;
+        }
+        let xRight = midX;
+        for (let i = 0; i < dataLen; i++) {
+          const value = data[i];
+          const percent = value / 255; // 最大值为255
+          const barHeight = percent * midY;
+          canvasCtx.fillStyle = lineColor;
+          if (i === 0) {
+            xRight += lineGap / 2;
+          }
+          canvasCtx.fillRect(xRight, midY - barHeight, step, barHeight);
+          canvasCtx.fillRect(xRight, midY, step, barHeight);
+          xRight += step + lineGap;
+        }
+      };
+      const fillCanvasBackground = (ctx: CanvasRenderingContext2D, w: number, h: number, colors: string) => {
+        ctx.clearRect(0, 0, w, h);
+        ctx.fillStyle = colors;
+        ctx.fillRect(0, 0, w, h);
+      };
+      const requestAnimationFrameFun = () => {
+        requestAnimationFrame(() => {
+          analyser.getByteFrequencyData(dataArray);
+          draw(dataArray, canvasCtx, {
+            lineGap: 2,
+            canvWidth: width,
+            canvHeight: height,
+            canvFillColor: "transparent",
+            lineColor: "rgba(255, 255, 255, 0.3)",
+          });
+          if (!isPause) {
+            requestAnimationFrameFun();
+          }
+        });
+      };
+      let isPause = true;
+      const playVisualDraw = () => {
+        isPause = false;
+        audioCtx.resume();
+        requestAnimationFrameFun();
+      };
+      const pauseVisualDraw = () => {
+        isPause = true;
+        audioCtx.suspend();
+      };
+      return {
+        playVisualDraw,
+        pauseVisualDraw,
+      };
+    }
 
-				{/* 音准、节奏、完整度纬度 */}
-				
+    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.center}>
+          <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.cItem}>
 						<div>{level[scoreData.value.heardLevel]}</div>
 						<div>难度</div>
@@ -285,279 +281,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>
+          </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>							
-								</>
-							)}
+          {/* 五线谱,简谱类型提示  */}
+          {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>
+                    </>
+                  )}
 
-							{(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} />
-					
-					{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>
+    );
+  },
+});