liushengqiang 2 năm trước cách đây
mục cha
commit
e316db922c
47 tập tin đã thay đổi với 1629 bổ sung158 xóa
  1. 14 0
      public/helpers/html-to-image.js
  2. 0 0
      public/helpers/plyr.css
  3. 0 0
      public/helpers/plyr.min.js
  4. 6 0
      public/helpers/qs.min.js
  5. 4 0
      public/helpers/rem-fit.min.js
  6. BIN
      public/share-evaluating/icons/avatar.png
  7. BIN
      public/share-evaluating/icons/dot.png
  8. BIN
      public/share-evaluating/icons/header.png
  9. BIN
      public/share-evaluating/icons/qrcode.png
  10. BIN
      public/share-evaluating/icons/tag.png
  11. BIN
      public/share-evaluating/icons/videobg.png
  12. 258 0
      public/share-evaluating/index.css
  13. 102 0
      public/share-evaluating/index.html
  14. 10 0
      public/share-evaluating/index.js
  15. BIN
      public/share/icons/avatar.png
  16. BIN
      public/share/icons/dot.png
  17. BIN
      public/share/icons/header.png
  18. BIN
      public/share/icons/qrcode.png
  19. BIN
      public/share/icons/tag.png
  20. BIN
      public/share/icons/videobg.png
  21. 211 0
      public/share/index.css
  22. 73 0
      public/share/index.html
  23. 10 0
      public/share/index.js
  24. 4 2
      src/components/The-error/index.tsx
  25. 32 8
      src/helpers/communication.ts
  26. 6 1
      src/helpers/formateMusic.ts
  27. 2 0
      src/page-gym/detail/index.module.less
  28. 7 4
      src/page-gym/detail/index.tsx
  29. 59 0
      src/page-gym/evaluat-model/evaluat-audio/index.module.less
  30. 27 0
      src/page-gym/evaluat-model/evaluat-audio/index.tsx
  31. 193 0
      src/page-gym/evaluat-model/evaluat-result/index.module.less
  32. 102 0
      src/page-gym/evaluat-model/evaluat-result/index.tsx
  33. 30 0
      src/page-gym/evaluat-model/evaluat-share/index.module.less
  34. 73 0
      src/page-gym/evaluat-model/evaluat-share/index.tsx
  35. 4 7
      src/page-gym/evaluat-model/icons/close2.svg
  36. BIN
      src/page-gym/evaluat-model/icons/student.png
  37. 53 2
      src/page-gym/evaluat-model/index.module.less
  38. 107 15
      src/page-gym/evaluat-model/index.tsx
  39. 5 0
      src/page-gym/header-top/index.module.less
  40. 10 9
      src/page-gym/header-top/index.tsx
  41. 2 0
      src/page-gym/main.ts
  42. 36 34
      src/state.ts
  43. 4 0
      src/store.ts
  44. 1 0
      src/view/evaluating/evaluatResult.ts
  45. 122 45
      src/view/evaluating/index.tsx
  46. 28 3
      src/view/selection/index.module.less
  47. 34 28
      src/view/selection/index.tsx

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 14 - 0
public/helpers/html-to-image.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
public/helpers/plyr.css


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
public/helpers/plyr.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 6 - 0
public/helpers/qs.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 4 - 0
public/helpers/rem-fit.min.js


BIN
public/share-evaluating/icons/avatar.png


BIN
public/share-evaluating/icons/dot.png


BIN
public/share-evaluating/icons/header.png


BIN
public/share-evaluating/icons/qrcode.png


BIN
public/share-evaluating/icons/tag.png


BIN
public/share-evaluating/icons/videobg.png


+ 258 - 0
public/share-evaluating/index.css

@@ -0,0 +1,258 @@
+html, body{
+  margin: 0;
+  background: rgba(1, 193, 181, 1);
+  min-height: 100vh;
+  background-image: url('./icons/header.png');
+  background-size: contain;
+  background-position: top center;
+  background-repeat: no-repeat;
+  font-family: PingFangSC-Regular, PingFang SC;
+  overflow-x: hidden;
+}
+
+:root{
+  --plyr-color-main: #01c1b5;
+  --plyr-font-size-base: 30px;
+  --plyr-control-icon-size: 36px;
+  --plyr-range-thumb-height: 26px;
+  --plyr-control-spacing: 20px;
+  --plyr-font-size-small: 26px;
+  --plyr-video-background: transparent;
+}
+
+body{
+  padding-top: 160rem;
+  box-sizing: border-box;
+  padding-bottom: 10rem;
+}
+
+.content{
+  box-sizing: border-box;
+  width: 95%;
+  background-color: #fff;
+  border-radius: 10rem;
+  padding: 22rem 24rem;
+  margin: 0 2.5%;
+  position: relative;
+  /* overflow: hidden;
+  border: none;
+  box-shadow: none; */
+}
+
+.content .tag{
+  position: absolute;
+  top: -1rem;
+  right: 23rem;
+  width: 13rem;
+  height: 22rem;
+  background: url('./icons/tag.png') no-repeat center;
+  background-size: contain;
+}
+
+.content .userinfo{
+  display: flex;
+}
+
+.content .userinfo > img {
+  display: block;
+  width: 60rem;
+  height: 60rem;
+  margin-right: 15rem;
+  border-radius: 50%;
+}
+
+.content .userinfo .cont {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
+
+.content .userinfo .cont > p {
+  color: rgba(80, 80, 80, 1);
+  font-size: 14rem;
+  margin: 0;
+}
+
+.content .userinfo .cont > .name{
+  font-size: 18rem;
+  color: rgba(26, 26, 26, 1);
+}
+
+.content div.desc {
+  color: rgba(128, 128, 128, 1);
+  font-size: 14rem;
+  line-height: 1.5;
+  position: relative;
+  margin: 0;
+  margin-top: 20rem;
+  margin-left: 20rem;
+}
+
+.content div.desc::before {
+  content: "";
+  background: url('./icons/dot.png') no-repeat center;
+  background-size: contain;
+  display: block;
+  width: 6rem;
+  height: 6rem;
+  margin-right: 10rem;
+  position: absolute;
+  top: 6rem;
+  left: -14rem;
+}
+
+.content .tit{
+  font-size: 16rem;
+  color: #000;
+}
+
+.content .info{
+  font-size: 14rem;
+  color: #808080;
+}
+
+.video-container{
+  border-radius: 20rem;
+  border: 4rem solid #F3F3F3;
+  overflow: hidden;
+  margin-top: 17rem;
+}
+
+.video{
+  max-width: 100%;
+  width: 289rem;
+  margian: 0 auto;
+  display: block;
+}
+
+.plyr__control--overlaid{
+  background-color: rgba(0, 0, 0, .5)!important;
+}
+
+.plyr--stopped.plyr__poster-enabled .plyr__poster{
+  background-color: #fff!important;
+  background-size: 70%;
+}
+
+.line{
+  margin-top: 21rem;
+  width: 300rem;
+  height: 2rem;
+  border-top: 3rem dotted #01C1B5;
+  position: relative;
+}
+
+.line::after,
+.line::before{
+  content: "";
+  display: block;
+  position: absolute;
+  width: 23rem;
+  height: 23rem;
+  border-radius: 100%;
+  background-color: rgba(1, 193, 181, 1);
+  z-index: 2;
+}
+
+.line::after{
+  right: -46rem;
+  top: -12rem;
+}
+
+.line::before{
+  left: -38rem;
+  top: -12rem;
+}
+
+.app{
+  margin-top: 18rem;
+  display: flex;
+  justify-content: ;pace-between;
+}
+
+.app-info {
+  padding: 10rem 0;
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  justify-content: space-around;
+}
+
+.app-title{
+  color: rgba(80, 80, 80, 1);
+  font-size: 18rem;
+  font-weight: bold;
+}
+
+.app-desc{
+  font-size: 16rem;
+  color: rgba(80, 80, 80, 1);
+}
+
+.app-desc > span{
+  color: rgba(1, 193, 181, 1);
+  font-weight: bold;
+}
+
+.app-subtitle{
+  font-size: 12rem;
+  color: #fff;
+  background: rgba(1, 193, 181, 1);
+  height: 20rem;
+  line-height: 20rem;
+  border-radius: 10rem;
+  padding-left: 5rem;
+  display: inline-block;
+  width: 170rem;
+  font-weight: bold;
+}
+
+.app > img{
+  width: 96rem;
+  height: 96rem;
+}
+
+.video,
+.plyr__video-wrapper,
+.plyr,
+.plyr__poster,
+.plyr__video-wrapper{
+  border-radius: 20rem;
+  overflow: hidden;
+}
+
+.fraction{
+  display: flex;
+  margin: 10rem 0;
+  margin-left: 20rem;
+}
+
+.fraction .item{
+  display: flex;
+  color: #01C1B5;
+  font-size: 10rem;
+  font-weight: bold;
+}
+
+.fraction .item .view{
+  flex: 1;
+  height: 16rem;
+  width: 137rem;
+  background: #F5F4F2;
+  border-radius: 4rem;
+  margin: 0 6rem;
+  position: relative;
+}
+
+.fraction .item .view>span{
+  display: block;
+  flex: 1;
+  height: 16rem;
+  width: 0%;
+  border-radius: 4rem;
+  background: linear-gradient(90deg, #02E2DB 0%, #01C1B5 100%);
+  position: absolute;
+  top: 0;
+  left: 0;
+}

+ 102 - 0
public/share-evaluating/index.html

@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html lang="zh">
+<head>
+  <meta charset="UTF-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <meta name="description" content="管乐迷APP,是国内首款专为管乐在线教学打造的专业级平台,丰富、便捷的学习工具,让管乐教学与学习更轻松。">
+  <title>管乐迷</title>
+  <link rel="stylesheet" href="index.css">
+  <script src="../helpers/rem-fit.min.js"></script>
+  <script src="../helpers/qs.min.js"></script>
+  <script src="../helpers/html-to-image.js"></script>
+  <script>
+    var remFit = new RemFit(375,true)
+    remFit.init()
+    window.addEventListener('resize', function () {
+      remFit.init()
+    })
+    var parseSearch = Qs.parse(window.location.search, { ignoreQueryPrefix: true })
+  </script>
+  <!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/plyr@3.6.8/dist/plyr.css"> -->
+</head>
+<body>
+  <div class="content">
+    <div class="tag"></div>
+    <div class="userinfo">
+      <img id="avatar" crossOrigin="anonymous"/>
+      <div class="cont">
+        <p class="name" id="name"></p>
+        <p class="sub" id="subjectName"></p>
+      </div>
+    </div>
+    <div class="desc">
+      <div class="tit">我在管乐迷智能评测获得了<span id="score"></span>分!</div>
+      <div class="info" id="examSongName"></div>
+    </div>
+    <div class="fraction" style="display: none;">
+      <div class="item" id="intonation">
+        <div class="decs">音准</div>
+        <div class="view"><span id="intonation-view"></span></div>
+        <div class="val" id="intonation-val"></div>
+      </div>
+    </div>
+    <div class="fraction" style="display: none;">
+      <div class="item" id="cadence">
+        <div class="decs">节奏</div>
+        <div class="view"><span id="cadence-view"></span></div>
+        <div class="val" id="cadence-val"></div>
+      </div>
+    </div>
+    <div class="fraction" style="display: none;">
+      <div class="item" id="integrity">
+        <div class="decs">节奏</div>
+        <div class="view"><span id="integrity-view"></span></div>
+        <div class="val" id="integrity-val"></div>
+      </div>
+    </div>
+    <div class="line"></div>
+    <div class="app">
+      <div class="app-info">
+        <div class="app-title">管乐迷</div>
+        <div class="app-desc">管乐学习的<span>不二选择</span></div>
+        <div class="app-subtitle">云教练见证你的成长!</div>
+      </div>
+      <img src="./icons/qrcode.png"/>
+    </div>
+  </div>
+  <script>
+    document.getElementById('avatar').src = (decodeURIComponent(parseSearch.avatar) || './icons/avatar.png') + '?v=' + parseInt(Math.random() * 10000000)
+    document.getElementById('name').innerText = parseSearch.name || ''
+    document.getElementById('subjectName').innerText = parseSearch.subjectName || ''
+    document.getElementById('examSongName').innerText = parseSearch.examSongName || ''
+    document.getElementById('score').innerText = parseSearch.score || ''
+    if (parseSearch.intonation) {
+      document.getElementById('intonation').parentElement.style.display = 'flex'
+      document.getElementById('intonation-val').innerText = parseSearch.intonation || ''
+      document.getElementById('intonation-view').style.width = parseSearch.intonation + '%' || ''
+    }
+    if (parseSearch.cadence) {
+      document.getElementById('cadence').parentElement.style.display = 'flex'
+      document.getElementById('cadence-val').innerText = parseSearch.cadence || ''
+      document.getElementById('cadence-view').style.width = parseSearch.cadence + '%' || ''
+    }
+    if (parseSearch.integrity) {
+      document.getElementById('integrity').parentElement.style.display = 'flex'
+      document.getElementById('integrity-val').innerText = parseSearch.integrity || ''
+      document.getElementById('integrity-view').style.width = parseSearch.integrity + '%' || ''
+    }
+
+    window.addEventListener('load', function () {
+      htmlToImage.toPng(document.body)
+      .then(() => htmlToImage.toPng(document.body))
+      .then(res => {
+        if (window.setPng) {
+          window.setPng(res)
+        }
+      })
+    })
+  </script>
+  <!-- <script src="./index.js"></script> -->
+</body>
+</html>

+ 10 - 0
public/share-evaluating/index.js

@@ -0,0 +1,10 @@
+;(function() {
+  new Plyr(document.querySelector('.video'), {
+    controls: ['play-large', 'play', 'progress', 'current-time', 'airplay', 'fullscreen',],
+    fullscreen: {
+      enabled: true,
+      fallback: true,
+      iosNative: true,
+    }
+  })
+})()

BIN
public/share/icons/avatar.png


BIN
public/share/icons/dot.png


BIN
public/share/icons/header.png


BIN
public/share/icons/qrcode.png


BIN
public/share/icons/tag.png


BIN
public/share/icons/videobg.png


+ 211 - 0
public/share/index.css

@@ -0,0 +1,211 @@
+html, body{
+  margin: 0;
+  background: rgba(1, 193, 181, 1);
+  min-height: 100vh;
+  background-image: url('./icons/header.png');
+  background-size: contain;
+  background-position: top center;
+  background-repeat: no-repeat;
+}
+
+:root{
+  --plyr-color-main: #01c1b5;
+  --plyr-font-size-base: 30px;
+  --plyr-control-icon-size: 36px;
+  --plyr-range-thumb-height: 26px;
+  --plyr-control-spacing: 20px;
+  --plyr-font-size-small: 26px;
+  --plyr-video-background: transparent;
+}
+
+body{
+  padding-top: 160rem;
+  box-sizing: border-box;
+  padding-bottom: 10rem;
+}
+
+.content{
+  box-sizing: border-box;
+  width: 347rem;
+  background-color: #fff;
+  border-radius: 10rem;
+  padding: 22rem 24rem;
+  margin: 0 auto;
+  position: relative;
+  /* overflow: hidden;
+  border: none;
+  box-shadow: none; */
+}
+
+.content .tag{
+  position: absolute;
+  top: -1rem;
+  right: 23rem;
+  width: 13rem;
+  height: 22rem;
+  background: url('./icons/tag.png') no-repeat center;
+  background-size: contain;
+}
+
+.content .userinfo{
+  display: flex;
+}
+
+.content .userinfo > img {
+  display: block;
+  width: 60rem;
+  height: 60rem;
+  margin-right: 15rem;
+  border-radius: 50%;
+}
+
+.content .userinfo .cont {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
+
+.content .userinfo .cont > p {
+  color: rgba(80, 80, 80, 1);
+  font-size: 14rem;
+  margin: 0;
+}
+
+.content .userinfo .cont > p.name{
+  font-size: 18rem;
+  color: rgba(26, 26, 26, 1);
+}
+
+.content p.desc {
+  color: rgba(128, 128, 128, 1);
+  font-size: 14rem;
+  line-height: 1.5;
+  position: relative;
+  margin: 0;
+  margin-top: 20rem;
+  margin-left: 20rem;
+}
+
+.content p.desc::before {
+  content: "";
+  background: url('./icons/dot.png') no-repeat center;
+  background-size: contain;
+  display: block;
+  width: 6rem;
+  height: 6rem;
+  margin-right: 10rem;
+  position: absolute;
+  top: 6rem;
+  left: -14rem;
+}
+
+.video-container{
+  border-radius: 20rem;
+  border: 4rem solid #F3F3F3;
+  overflow: hidden;
+  margin-top: 17rem;
+}
+
+.video{
+  max-width: 100%;
+  width: 289rem;
+  margian: 0 auto;
+  display: block;
+}
+
+.plyr__control--overlaid{
+  background-color: rgba(0, 0, 0, .5)!important;
+}
+
+.plyr--stopped.plyr__poster-enabled .plyr__poster{
+  background-color: #fff!important;
+  background-size: 70%;
+}
+
+.line{
+  margin-top: 21rem;
+  width: 300rem;
+  height: 2rem;
+  border-top: 3rem dotted #01C1B5;
+  position: relative;
+}
+
+.line::after,
+.line::before{
+  content: "";
+  display: block;
+  position: absolute;
+  width: 23rem;
+  height: 23rem;
+  border-radius: 100%;
+  background-color: rgba(1, 193, 181, 1);
+  z-index: 2;
+}
+
+.line::after{
+  right: -38rem;
+  top: -12rem;
+}
+
+.line::before{
+  left: -38rem;
+  top: -12rem;
+}
+
+.app{
+  margin-top: 18rem;
+  display: flex;
+  justify-content: ;pace-between;
+}
+
+.app-info {
+  padding: 10rem 0;
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  justify-content: space-around;
+}
+
+.app-title{
+  color: rgba(80, 80, 80, 1);
+  font-size: 18rem;
+  font-weight: bold;
+}
+
+.app-desc{
+  font-size: 16rem;
+  color: rgba(80, 80, 80, 1);
+}
+
+.app-desc > span{
+  color: rgba(1, 193, 181, 1);
+  font-weight: bold;
+}
+
+.app-subtitle{
+  font-size: 12rem;
+  color: #fff;
+  background: rgba(1, 193, 181, 1);
+  height: 20rem;
+  line-height: 20rem;
+  border-radius: 10rem;
+  padding-left: 5rem;
+  display: inline-block;
+  width: 170rem;
+  font-weight: bold;
+}
+
+.app > img{
+  width: 96rem;
+  height: 96rem;
+}
+
+.video,
+.plyr__video-wrapper,
+.plyr,
+.plyr__poster,
+.plyr__video-wrapper{
+  border-radius: 20rem;
+  overflow: hidden;
+}

+ 73 - 0
public/share/index.html

@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html lang="zh">
+<head>
+  <meta charset="UTF-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <meta name="description" content="管乐迷APP,是国内首款专为管乐在线教学打造的专业级平台,丰富、便捷的学习工具,让管乐教学与学习更轻松。">
+  <title>管乐迷</title>
+  <link rel="stylesheet" href="index.css">
+  <script src="../helpers/rem-fit.min.js"></script>
+  <script src="../helpers/qs.min.js"></script>
+  <script>
+    var remFit = new RemFit(375,true)
+    remFit.init()
+    window.addEventListener('resize', function () {
+      remFit.init()
+    })
+    var parseSearch = Qs.parse(window.location.search, { ignoreQueryPrefix: true })
+  </script>
+  <link rel="stylesheet" href="../helpers/plyr.css">
+</head>
+<body>
+  <div class="content">
+    <div class="tag"></div>
+    <div class="userinfo">
+      <img id="avatar" crossOrigin="anonymous"/>
+      <div class="cont">
+        <p class="name" id="name"></p>
+        <p class="sub" id="subjectName"></p>
+      </div>
+    </div>
+    <p class="desc">
+      管乐迷使用云教练可太好用了!演奏越来越好了,我的演奏快来看看吧!
+    </p>
+    <div class="video-container">
+      <video
+        id="video"
+        class="video"
+        src=""
+        data-poster="./icons/videobg.png"
+        controls="false"
+        preload="metadata"
+        playsinline
+      />
+    </div>
+    <div class="line"></div>
+    <div class="app">
+      <div class="app-info">
+        <div class="app-title">管乐迷</div>
+        <div class="app-desc">管乐学习的<span>不二选择</span></div>
+        <div class="app-subtitle">云教练见证你的成长!</div>
+      </div>
+      <img src="./icons/qrcode.png"/>
+    </div>
+  </div>
+  <script src="../helpers/plyr.min.js"></script>
+  <script>
+    new Plyr(document.querySelector('.video'), {
+      controls: ['play-large', 'play', 'progress', 'current-time', 'airplay', 'fullscreen',],
+      fullscreen: {
+        enabled: true,
+        fallback: true,
+        iosNative: true,
+      }
+    })
+    document.getElementById('avatar').src = (parseSearch.avatar || './icons/avatar.png') + '?v=' + parseInt(Math.random() * 10000000)
+    document.getElementById('name').innerText = parseSearch.name || ''
+    document.getElementById('subjectName').innerText = parseSearch.subjectName || ''
+    document.getElementById('video').src = parseSearch.src
+  </script>
+  <!-- <script src="./index.js"></script> -->
+</body>
+</html>

+ 10 - 0
public/share/index.js

@@ -0,0 +1,10 @@
+;(function() {
+  new Plyr(document.querySelector('.video'), {
+    controls: ['play-large', 'play', 'progress', 'current-time', 'airplay', 'fullscreen',],
+    fullscreen: {
+      enabled: true,
+      fallback: true,
+      iosNative: true,
+    }
+  })
+})()

+ 4 - 2
src/components/The-error/index.tsx

@@ -1,11 +1,13 @@
-import { Empty } from "vant";
+import { Button, Empty } from "vant";
 import { defineComponent } from "vue";
 
 export default defineComponent({
     name: 'The-error',
     setup(props, ctx) {
         return () => <div>
-            <Empty image="error" />
+            <Empty image="error" description="网络开小差,再刷新看看">
+                <Button type="primary" size="small" onClick={() => history.go(0)}>重新加载</Button>
+            </Empty>
         </div>
     },
 })

+ 32 - 8
src/helpers/communication.ts

@@ -31,8 +31,8 @@ export const sendResult = (callback: CallBack) => {
 };
 /** 取消监听录音返回 */
 export const removeResult = (callback: CallBack) => {
-   removeListenerMessage("sendResult", callback)
-}
+	removeListenerMessage("sendResult", callback);
+};
 
 /** 结束录音 */
 export const endSoundCheck = () => {
@@ -43,23 +43,47 @@ export const endSoundCheck = () => {
 
 /** 开始评测 */
 export const startEvaluating = (content: any): Promise<IPostMessage | undefined> => {
-   if (!isApp()) return Promise.resolve({} as any);
+	if (!isApp()) return Promise.resolve({} as any);
 	return promisefiyPostMessage({ api: "startEvaluating", content: content });
 };
 /** 结束评测 */
 export const endEvaluating = (content: any): Promise<IPostMessage | undefined> => {
-   if (!isApp()) return Promise.resolve({} as any);
+	if (!isApp()) return Promise.resolve({} as any);
 	return promisefiyPostMessage({ api: "endEvaluating", content: content });
 };
+/** 取消评测 */
+export const cancelEvaluating = () => {
+	postMessage({
+		api: "cancelEvaluating",
+	});
+};
 
 /** 评测开始录音 */
 export const startRecording = (): Promise<IPostMessage | undefined> => {
-   if (!isApp()) return Promise.resolve({} as any);
+	if (!isApp()) return Promise.resolve({} as any);
 	return promisefiyPostMessage({ api: "startRecording" });
 };
 
 /** 和websocket通信 */
-export const proxyServiceMessage = (content: any): Promise<IPostMessage | undefined> => {
-   if (!isApp()) return Promise.resolve({} as any);
+export const api_proxyServiceMessage = (content: any): Promise<IPostMessage | undefined> => {
+	if (!isApp()) return Promise.resolve({} as any);
 	return promisefiyPostMessage({ api: "proxyServiceMessage", content });
-}
+};
+
+/** 上传评测 音 视频 */
+export const api_videoUpdate = () => {
+	if (!isApp()) return Promise.resolve({} as any);
+	return promisefiyPostMessage({ api: "videoUpdate" });
+};
+
+/** 分享 */
+export const api_shareAchievements = (content: any): Promise<IPostMessage | undefined> => {
+	if (!isApp()) return Promise.resolve({} as any);
+	return promisefiyPostMessage({ api: "shareAchievements", content });
+};
+
+/** openwebview */
+export const api_openWebView = (content: any): Promise<IPostMessage | undefined> => {
+	if (!isApp()) return Promise.resolve({} as any);
+	return promisefiyPostMessage({ api: "openWebView", content });
+};

+ 6 - 1
src/helpers/formateMusic.ts

@@ -905,6 +905,11 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 					fixtime += difftime;
 				}
 			}
+			let stave = activeVerticalMeasureList[0]?.stave
+			if (!stave && note.isRestFlag && allNotes.length) {
+				// 全休止符, 公用stave
+				// stave = allNotes[allNotes.length - 1].stave
+			}
 			// console.log(note.tie)
 			const nodeDetail = {
 				isStaccato: note.voiceEntry.isStaccato(),
@@ -942,7 +947,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				realKey: 0,
 				duration: 0,
 				formatLyricsEntries: formatLyricsEntries(note),
-				stave: activeVerticalMeasureList[0]?.stave,
+				stave,
 				firstVerticalMeasure: activeVerticalMeasureList[0],
 				noteLength: 1,
 				osdmContext: osmd,

+ 2 - 0
src/page-gym/detail/index.module.less

@@ -6,6 +6,7 @@
     height: 100vh;
     padding: 20px 30px;
     background-color: #fff;
+    z-index: 10;
     --van-skeleton-paragraph-height: .8rem;
 }
 
@@ -22,6 +23,7 @@
     }
 
     .container {
+        position: relative;
         margin: 0 18px 18px 18px;
         flex: 1;
         overflow-y: auto;

+ 7 - 4
src/page-gym/detail/index.tsx

@@ -3,7 +3,7 @@ import { defineComponent, onBeforeMount, onMounted, reactive, Transition } from
 import { useRoute } from "vue-router";
 import { formateTimes } from "../../helpers/formateMusic";
 import Metronome, { metronomeData } from "../../helpers/metronome";
-import state from "../../state";
+import state, { isRhythmicExercises } from "../../state";
 import { storeData } from "../../store";
 import { setGlobalData } from "../../utils";
 import { getQuery } from "../../utils/queryString";
@@ -14,7 +14,7 @@ import EvaluatModel from "../evaluat-model";
 import HeaderTop from "../header-top";
 import styles from "./index.module.less";
 import { isSpecialShapedScreen } from "/src/helpers/communication";
-import Evaluating from "/src/view/evaluating";
+import Evaluating, { evaluatingData } from "/src/view/evaluating";
 import MeasureSpeed from "/src/view/plugins/measure-speed";
 import Selection from "/src/view/selection";
 
@@ -44,7 +44,7 @@ export default defineComponent({
 		};
 		onBeforeMount(() => {
 			getAPPData();
-		})
+		});
 		// console.log(route.params, query)
 		/** 获取曲谱数据 */
 		const getMusicInfo = (res: any) => {
@@ -113,6 +113,9 @@ export default defineComponent({
 			state.originSpeed = state.speed = data.speed;
 			state.track = data.track;
 			state.isOpenPrepare = true;
+
+			// 是否打击乐
+			state.isPercussion = state.subjectId == 23 || state.subjectId == 113 || state.subjectId == 121 || isRhythmicExercises();
 		};
 
 		const setCustom = () => {
@@ -155,7 +158,7 @@ export default defineComponent({
 					{state.modeType === "evaluating" && (
 						<>
 							<Evaluating />
-							<EvaluatModel />
+							{evaluatingData.rendered && <EvaluatModel />}
 						</>
 					)}
 					{!detailData.isLoading && (

+ 59 - 0
src/page-gym/evaluat-model/evaluat-audio/index.module.less

@@ -0,0 +1,59 @@
+.fraction {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    color: #fff;
+    background-color: #fff;
+    border-radius: 18px;
+    min-width: 244px;
+}
+
+.title {
+    position: relative;
+    width: 100px;
+    height: 30px;
+    top: -4.5px;
+
+    img {
+        display: block;
+        width: 100%;
+        height: 100%;
+    }
+
+    .titleDes {
+        position: absolute;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        top: 0;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 15px;
+    }
+}
+
+.erji {
+    width: 150px;
+}
+
+.tip {
+    font-size: 13px;
+    color: #808080;
+    padding: 20px 0;
+}
+.btns{
+    display: flex;
+    justify-content: space-evenly;
+    align-items: center;
+    width: 100%;
+}
+.btn {
+    width: 40%;
+    height: 31px;
+    margin: 11px 0 17px 0;
+    line-height: 31px;
+    text-align: center;
+    border-radius: 36px;
+    font-size: 13px;
+}

+ 27 - 0
src/page-gym/evaluat-model/evaluat-audio/index.tsx

@@ -0,0 +1,27 @@
+import { defineComponent } from "vue";
+import styles from "./index.module.less";
+import icons from "../icons/index.json";
+
+export default defineComponent({
+	name: "evaluat-audio",
+	emits: ["close"],
+	setup(props, { emit }) {
+		return () => (
+			<div class={styles.fraction}>
+				<div class={styles.title}>
+					<img src={icons.title} />
+					<div class={styles.titleDes}>提示</div>
+				</div>
+				<div class={styles.tip}>评测{false ? "音视频" : "音频"}是否上传到云端?</div>
+				<div class={styles.btns}>
+					<div style={{ background: "#F0F0F0", color: "var(--van-primary-color)" }} class={styles.btn} onClick={() => emit("close")}>
+						取消
+					</div>
+					<div style={{ background: "var(--van-primary-color)" }} class={styles.btn} onClick={() => emit("close", true)}>
+						确认
+					</div>
+				</div>
+			</div>
+		);
+	},
+});

+ 193 - 0
src/page-gym/evaluat-model/evaluat-result/index.module.less

@@ -0,0 +1,193 @@
+.evaluatResult {
+    position: relative;
+}
+
+.closeBtn {
+    position: absolute;
+    right: -34px;
+    top: -6px;
+    width: 24px;
+    height: 24px;
+    border-radius: 50%;
+    background-color: #fff;
+    overflow: hidden;
+    padding: 6px;
+
+    img {
+        width: 100%;
+        height: 100%;
+        display: block;
+    }
+
+    &:active {
+        opacity: .8;
+    }
+}
+
+.fraction {
+    position: relative;
+    overflow: hidden;
+    border-radius: 18px;
+    height: 86vh;
+    background-color: #fff;
+    display: flex;
+    max-height: 310px;
+}
+
+.leftContent {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-evenly;
+    align-items: center;
+    width: 168px;
+    flex-shrink: 0;
+    background: url(../icons/left-bg.png) no-repeat center;
+    background-color: var(--van-primary-color);
+    background-size: 100%;
+
+    .leftIcon {
+        width: 80%;
+    }
+
+    .scoreDes {
+        color: #fff;
+        font-size: 20px;
+        text-align: center;
+
+        .scoreNum {
+            font-size: 35px;
+            font-weight: 700;
+        }
+    }
+}
+
+.content {
+    width: 335px;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+
+    .headerButton {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 12px;
+    }
+
+    .leftTitle {
+        font-size: 17px;
+        font-weight: 500;
+        position: relative;
+        padding-left: 10px;
+
+        &::before {
+            content: '';
+            position: absolute;
+            left: 0;
+            top: 15%;
+            width: 4px;
+            height: 70%;
+            background-color: var(--van-primary-color);
+            border-radius: 2px;
+        }
+    }
+
+    .headBtn {
+        background-color: #EFEFEF;
+        border-radius: 12px;
+        height: 26px;
+        line-height: 26px;
+        font-size: 13px;
+        color: var(--van-primary-color);
+        padding: 0 10px;
+        min-width: 61px;
+        text-align: center;
+
+        &+.headBtn {
+            margin-left: 8px;
+        }
+
+        &:active {
+            opacity: .8;
+        }
+    }
+}
+
+.tips {
+    padding: 0 20px;
+    font-size: 13px;
+    color: #808080;
+    text-align: center;
+}
+
+.ctrls {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin: 0 20px 18px 20px;
+    padding-top: 16px;
+    border-top: 1px dashed #E5E5E5;
+
+    .ctrlsBtn {
+        width: 94px;
+        height: 30px;
+        line-height: 30px;
+        text-align: center;
+        font-size: 13px;
+        color: #fff;
+        border-radius: 36px;
+    }
+}
+
+.detail {
+    padding: 0 12px;
+}
+
+.progressitem {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    padding: 6px 0;
+
+    &>span {
+        flex: 1;
+        font-size: 12px;
+        font-weight: 600;
+        color: var(--van-primary-color);
+    }
+
+    &>span:first-child {
+        text-align: right;
+    }
+
+    :global {
+        .van-progress {
+            width: 165px;
+            margin: 0 10px;
+            flex-shrink: 0;
+            transform: skewX(-10deg);
+        }
+    }
+
+}
+
+.percussion {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+
+    &>img {
+        height: 74px;
+        margin-bottom: 10px;
+    }
+
+    .scoreDes {
+        color: var(--van-primary-color);
+        font-size: 18px;
+        .scoreNum{
+            font-weight: bold;
+            font-size: 28px;
+        }
+    }
+}

+ 102 - 0
src/page-gym/evaluat-model/evaluat-result/index.tsx

@@ -0,0 +1,102 @@
+import { Popup } from "@varlet/ui";
+import { defineComponent, ref } from "vue";
+import styles from "./index.module.less";
+import state from "/src/state";
+import iconClose from "../icons/close2.svg";
+import icon1 from "../icons/1.png";
+import { storeData } from "/src/store";
+import { Progress } from "vant";
+import { evaluatingData } from "/src/view/evaluating";
+
+export default defineComponent({
+	name: "evaluatResult",
+	emits: ["close"],
+	setup(props, { emit }) {
+		return () => (
+			<div class={styles.evaluatResult}>
+				<div class={styles.closeBtn} onClick={() => emit("close")}>
+					<img src={iconClose} />
+				</div>
+
+				<div class={styles.fraction}>
+					<div class={styles.leftContent} style={{ display: state.isPercussion ? "none" : "" }}>
+						<img class={styles.leftIcon} src={evaluatingData.resultData.img || icon1} />
+						<div class={styles.scoreDes}>
+							<span class={styles.scoreNum}>{evaluatingData.resultData.score}</span>分<br />
+							<span>{evaluatingData.resultData.mome}</span>
+						</div>
+					</div>
+
+					<div class={styles.content}>
+						<div class={styles.headerButton}>
+							<div class={styles.leftTitle}>智能评分</div>
+							<div style={{ display: "flex" }}>
+								<div class={styles.headBtn} onClick={() => emit("close", "update")}>
+									上传到云端
+								</div>
+								<div class={styles.headBtn} style={{ display: storeData.platformType === "STUDENT" ? "block" : "" }} onClick={() => emit("close", "share")}>
+									分享
+								</div>
+							</div>
+						</div>
+						{state.isPercussion ? (
+							<div class={styles.percussion}>
+								<img class={styles.leftIcon} src={evaluatingData.resultData.img || icon1} />
+								<div class={styles.scoreDes}>
+									<span class={styles.scoreNum}>{evaluatingData.resultData.score}</span>分
+									<span style={{marginLeft: '8px'}}>{evaluatingData.resultData.mome}</span>
+								</div>
+							</div>
+						) : (
+							<div class={styles.detail}>
+								<div class={styles.progressitem}>
+									<span>音准</span>
+									<Progress
+										percentage={evaluatingData.resultData.intonation}
+										strokeWidth={20}
+										showPivot={false}
+										color="linear-gradient(to right, #02E2DB, #01C1B5)"
+									/>
+									<span>{evaluatingData.resultData.intonation}</span>
+								</div>
+								<div class={styles.progressitem}>
+									<span>节奏</span>
+									<Progress
+										percentage={evaluatingData.resultData.cadence}
+										strokeWidth={20}
+										showPivot={false}
+										color="linear-gradient(to right, #02E2DB, #01C1B5)"
+									/>
+									<span>{evaluatingData.resultData.cadence}</span>
+								</div>
+								<div class={styles.progressitem}>
+									<span>完成度</span>
+									<Progress
+										percentage={evaluatingData.resultData.integrity}
+										strokeWidth={20}
+										showPivot={false}
+										color="linear-gradient(to right, #02E2DB, #01C1B5)"
+									/>
+									<span>{evaluatingData.resultData.integrity}</span>
+								</div>
+							</div>
+						)}
+
+						<div class={styles.tips}>{evaluatingData.resultData.tips}</div>
+						<div class={styles.ctrls}>
+							<div class={styles.ctrlsBtn} style={{ background: "#01C1B5" }} onClick={() => emit("close", "practise")}>
+								去练习
+							</div>
+							<div class={styles.ctrlsBtn} style={{ background: "#F79300" }} onClick={() => emit("close", "tryagain")}>
+								再试一次
+							</div>
+							<div class={styles.ctrlsBtn} style={{ background: "#F7B500" }} onClick={() => emit("close", "look")}>
+								查看报告
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+		);
+	},
+});

+ 30 - 0
src/page-gym/evaluat-model/evaluat-share/index.module.less

@@ -0,0 +1,30 @@
+.iframe {
+    border: none;
+    width: 500PX;
+    height: 80vh;
+}
+
+.btns {
+    display: flex;
+    font-size: 18PX;
+    align-items: center;
+    position: absolute;
+    right: 10PX;
+    top: 12PX;
+
+    >.sbtn {
+        background-color: rgba(0, 0, 0, .23);
+        width: 68PX;
+        height: 32PX;
+        border-radius: 19PX;
+        text-align: center;
+        line-height: 32PX;
+        color: #fff;
+        margin-left: 10PX;
+    }
+}
+
+.disabled {
+    opacity: .5;
+    pointer-events: none;
+}

+ 73 - 0
src/page-gym/evaluat-model/evaluat-share/index.tsx

@@ -0,0 +1,73 @@
+import { Snackbar } from "@varlet/ui";
+import { defineComponent, onMounted, ref } from "vue";
+import styles from "./index.module.less";
+import { api_shareAchievements } from "/src/helpers/communication";
+import state from "/src/state";
+import { storeData } from "/src/store";
+import { evaluatingData } from "/src/view/evaluating";
+import qs from 'query-string'
+
+export default defineComponent({
+	name: "evaluat-share",
+	emits: ["close"],
+	setup(props, { emit }) {
+		const src = ref("");
+		const shareLoadedPngData = ref("");
+		const shareDisabeled = ref(true);
+		const getShareUrl = () => {
+			const data: any = {
+				name: storeData.user?.username || "",
+				subjectName: (storeData.user?.subjectNames || "").split(",")[0] || "",
+				avatar: encodeURIComponent(storeData.user?.avatar || ""),
+				score: evaluatingData.resultData?.score || 0,
+				examSongName: state.examSongName || "",
+			};
+			if (!state.isPercussion) {
+				data.intonation = evaluatingData.resultData?.intonation;
+				data.cadence = evaluatingData.resultData?.cadence;
+				data.integrity = evaluatingData.resultData?.integrity;
+			}
+			// src.value = `${location.origin}/accompany/share-evaluating/index.html?${qs.stringify(data)}`;
+			src.value = `${location.origin}/share-evaluating/index.html?${qs.stringify(data)}`;
+		};
+		const shareLoaded = (evt: Event) => {
+			const el = evt.target as HTMLIFrameElement;
+			if (el) {
+				// @ts-ignore
+				el.contentWindow.setPng = (data: string) => {
+					shareLoadedPngData.value = data;
+                    shareDisabeled.value = false
+				};
+			}
+		};
+		const shareNext = async () => {
+			const res = await api_shareAchievements({
+				title: "分享我的乐器练习进度,一起见证我的成长!",
+				desc: "晒一下我的评测分数,快来“云教练”上和我PK一下吧!",
+				image: shareLoadedPngData.value,
+				video: "",
+				type: "image",
+			});
+			if (!res?.content?.status && res?.content?.message) {
+                Snackbar(res?.content?.message);
+			}
+            emit("close");
+		};
+		onMounted(() => {
+			getShareUrl();
+		});
+		return () => (
+			<div>
+				<div class={styles.btns}>
+					<div class={styles.sbtn} onClick={() => emit("close")}>
+						取消
+					</div>
+					<div class={[styles.sbtn, shareDisabeled.value && styles.disabled]} onClick={shareNext}>
+						继续
+					</div>
+				</div>
+				<iframe class={styles.iframe} src={src.value} onLoad={shareLoaded} />
+			</div>
+		);
+	},
+});

+ 4 - 7
src/page-gym/evaluat-model/icons/close2.svg

@@ -1,12 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 62 (91390) - https://sketch.com -->
-    <title>编组</title>
-    <desc>Created with Sketch.</desc>
-    <g id="智能打分" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="81-100备份" transform="translate(-493.000000, -57.000000)" fill="#01C1B5" fill-rule="nonzero">
-            <g id="弹窗" transform="translate(143.000000, 40.000000)">
-                <g id="编组" transform="translate(350.000000, 17.000000)">
+    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g transform="translate(-493.000000, -57.000000)" fill="#01C1B5" fill-rule="nonzero">
+            <g transform="translate(143.000000, 40.000000)">
+                <g transform="translate(350.000000, 17.000000)">
                     <path d="M1.05548433,1.07092551 L0.973890205,1.16134969 C0.666012474,1.54828454 0.694001358,2.11481906 1.05785686,2.46909941 L6.537,7.949 L1.05548433,13.4315873 C0.672191496,13.8148801 0.672191496,14.4441272 1.05548433,14.8274201 L1.08636669,14.8583024 L1.17896103,14.9399506 C1.56501142,15.2393273 2.12839072,15.2121112 2.48219949,14.8583024 L7.964,9.376 L13.4470285,14.8583024 C13.8303213,15.2415953 14.4595684,15.2415953 14.8428613,14.8583024 L14.8737436,14.8274201 L14.9553918,14.7348257 C15.2547684,14.3487753 15.2275524,13.785396 14.8737436,13.4315873 L9.391,7.949 L14.8737436,2.46675831 C15.2570364,2.08346548 15.2570364,1.45421835 14.8737436,1.07092551 L14.8428613,1.04004316 L14.7502669,0.958394979 C14.3642165,0.659018325 13.8008372,0.686234384 13.4470285,1.04004316 L7.964,6.53 L2.48231995,1.0401637 C2.09890665,0.65675032 1.46965952,0.65675032 1.08636669,1.04004316 L1.05548433,1.07092551 Z" id="路径"></path>
                 </g>
             </g>

BIN
src/page-gym/evaluat-model/icons/student.png


+ 53 - 2
src/page-gym/evaluat-model/index.module.less

@@ -1,5 +1,5 @@
 :global {
-    .var-popup--center {
+    .var-popup .var-popup--center {
         overflow: initial;
     }
 }
@@ -20,8 +20,59 @@
     text-align: center;
     z-index: 10;
 }
-.endBtn{
+
+.endBtn {
     display: flex;
     align-items: center;
     justify-content: space-evenly;
+}
+
+.notice {
+    position: absolute;
+    left: 0;
+    top: 0;
+    display: flex;
+    align-items: center;
+    width: 100%;
+    z-index: 2;
+
+    &>img {
+        width: 56px;
+        height: 92px;
+    }
+
+    :global {
+        .van-notice-bar {
+            height: 32PX !important;
+            width: 35vw;
+            min-width: 270PX;
+            margin-left: 2vw;
+            border-radius: 5PX;
+            border: 1PX solid #01C1B5;
+
+            &::before {
+                content: '';
+                display: block;
+                width: 6PX;
+                height: 6PX;
+                border-bottom: 1px solid #01C1B5;
+                border-left: 1px solid #01C1B5;
+                transform: rotate(45deg);
+                background-color: #fff;
+                margin-left: -22PX;
+            }
+        }
+
+        .van-swipe-item {
+            line-height: 32PX !important;
+            font-size: 12PX !important;
+            text-align: left;
+            padding-left: 10PX;
+        }
+
+        .van-notice-bar__left-icon,
+        .van-notice-bar__right-icon {
+            font-size: 14PX;
+        }
+    }
 }

+ 107 - 15
src/page-gym/evaluat-model/index.tsx

@@ -1,6 +1,5 @@
-import { Popup } from "@varlet/ui";
-import "@varlet/ui/es/popup/style/index";
-import { defineComponent, onBeforeUnmount, onMounted, onUnmounted, reactive, watch } from "vue";
+import { Popup, Snackbar } from "@varlet/ui";
+import { defineComponent, onMounted, reactive, watch } from "vue";
 import {
 	connectWebsocket,
 	evaluatingData,
@@ -8,6 +7,8 @@ import {
 	handleEndSoundCheck,
 	handlePerformDetection,
 	handleStartBegin,
+	handleStartEvaluat,
+	handleViewReport,
 } from "/src/view/evaluating";
 import Earphone from "./earphone";
 import styles from "./index.module.less";
@@ -16,8 +17,12 @@ import state from "/src/state";
 import { storeData } from "/src/store";
 import { browser } from "/src/utils";
 import { getNoteByMeasuresSlursStart } from "/src/helpers/formateMusic";
-import { Icon } from "vant";
-import _event, { EventEnum } from "/src/helpers/eventemitter";
+import { Icon, NoticeBar, showToast, Swipe, SwipeItem } from "vant";
+import iconStudent from "./icons/student.png";
+import EvaluatResult from "./evaluat-result";
+import EvaluatAudio from "./evaluat-audio";
+import { api_openWebView, api_proxyServiceMessage, api_videoUpdate } from "/src/helpers/communication";
+import EvaluatShare from "./evaluat-share";
 
 // frequency 频率, amplitude 振幅, decibels 分贝
 type TCriteria = "frequency" | "amplitude" | "decibels";
@@ -25,6 +30,12 @@ type TCriteria = "frequency" | "amplitude" | "decibels";
 export default defineComponent({
 	name: "evaluat-model",
 	setup() {
+		const evaluatModel = reactive({
+			tips: true,
+			evaluatUpdateAudio: false,
+			isSaveVideo: state.setting.camera && state.setting.saveToAlbum,
+			shareMode: false,
+		});
 		/**
 		 * 木管(长笛 萨克斯 单簧管)乐器一级的2、3、6测评要放原音音频
 		 * 铜管乐器一级的1a,1b,5,6测评要放原音音频
@@ -143,17 +154,57 @@ export default defineComponent({
 			state.playSource = getMusicMode();
 		};
 
-        /** 评测返回 */
-        const handleResult = (result: any) => {
-            console.log('评测返回2', result.body)
-        }
+		/** 评测结果按钮处理 */
+		const handleEvaluatResult = (type: "practise" | "tryagain" | "look" | "share" | "update") => {
+			if (type === "update") {
+				// 上传云端
+				evaluatModel.evaluatUpdateAudio = true;
+				return;
+			} else if (type === "share") {
+				// 分享
+				evaluatModel.shareMode = true;
+				return;
+			} else if (type === "look") {
+				// 跳转
+				handleViewReport();
+				return;
+			} else if (type === "practise") {
+				// 去练习
+				handleStartEvaluat();
+			} else if (type === "tryagain") {
+				// 再来一次
+				handleStartBegin();
+			}
+			evaluatingData.resulstMode = false;
+		};
+		/** 上传音视频 */
+		const hanldeUpdateVideoAndAudio = async (update = false) => {
+			if (!update) {
+				evaluatModel.evaluatUpdateAudio = false;
+				return;
+			}
+			let res = null;
+			if (evaluatModel.isSaveVideo) {
+				res = await api_videoUpdate();
+			}
+			api_proxyServiceMessage({
+				header: {
+					commond: "videoUpload",
+					status: 200,
+					type: "SOUND_COMPARE",
+				},
+				body: {
+					filePath: res?.content?.filePath,
+					recordId: res?.recordId,
+				},
+			});
+			Snackbar.success("上传成功");
+			evaluatModel.evaluatUpdateAudio = false;
+		};
+
 		onMounted(() => {
 			handlePerformDetection();
-            _event.on(EventEnum.sendResultScore, handleResult)
 		});
-        onBeforeUnmount(() => {
-            _event.off(EventEnum.sendResultScore)
-        })
 		watch(
 			() => evaluatingData.checkEnd,
 			() => {
@@ -163,6 +214,14 @@ export default defineComponent({
 				}
 			}
 		);
+		watch(
+			() => evaluatingData.startBegin,
+			() => {
+				if (evaluatingData.startBegin) {
+					evaluatModel.tips = false;
+				}
+			}
+		);
 		return () => (
 			<div>
 				{evaluatingData.websocketState && (
@@ -180,7 +239,30 @@ export default defineComponent({
 						)}
 					</>
 				)}
-				<Popup closeOnClickOverlay={false} defaultStyle={false} v-model:show={evaluatingData.earphoneMode}>
+				{evaluatModel.tips && (
+					<>
+						<div class={styles.notice}>
+							<img src={iconStudent} />
+							<NoticeBar
+								scrollable={false}
+								style="background: #fff;color: #01C1B5;"
+								mode="closeable"
+								onClose={() => {
+									evaluatModel.tips = false;
+								}}
+							>
+								<Swipe style="height: 32px;" show-indicators={false} autoplay={3000} vertical>
+									<SwipeItem>请在周围安静的环境下演奏,减少杂音</SwipeItem>
+									<SwipeItem>请选择稳定、良好的网络环境,避免信号中断</SwipeItem>
+									<SwipeItem>演奏前请调试好乐器,保证最佳演奏状态</SwipeItem>
+									<SwipeItem>演奏时请佩戴耳机,评测收音更精准</SwipeItem>
+								</Swipe>
+							</NoticeBar>
+						</div>
+						<div style={{ height: "40px" }}></div>
+					</>
+				)}
+				<Popup teleport="body" closeOnClickOverlay={false} defaultStyle={false} v-model:show={evaluatingData.earphoneMode}>
 					<Earphone
 						onClose={() => {
 							evaluatingData.earphoneMode = false;
@@ -188,7 +270,7 @@ export default defineComponent({
 						}}
 					/>
 				</Popup>
-				<Popup closeOnClickOverlay={false} defaultStyle={false} v-model:show={evaluatingData.soundEffectMode}>
+				<Popup teleport="body" closeOnClickOverlay={false} defaultStyle={false} v-model:show={evaluatingData.soundEffectMode}>
 					<SoundEffect
 						onClose={(value: any) => {
 							evaluatingData.soundEffectMode = false;
@@ -200,6 +282,16 @@ export default defineComponent({
 						}}
 					/>
 				</Popup>
+
+				<Popup teleport="body" closeOnClickOverlay={false} defaultStyle={false} v-model:show={evaluatingData.resulstMode}>
+					<EvaluatResult onClose={handleEvaluatResult} />
+				</Popup>
+				<Popup teleport="body" closeOnClickOverlay={false} defaultStyle={false} v-model:show={evaluatModel.evaluatUpdateAudio}>
+					<EvaluatAudio onClose={hanldeUpdateVideoAndAudio} />
+				</Popup>
+				<Popup teleport="body" closeOnClickOverlay={false} defaultStyle={false} v-model:show={evaluatModel.shareMode}>
+					<EvaluatShare onClose={() => (evaluatModel.shareMode = false)} />
+				</Popup>
 			</div>
 		);
 	},

+ 5 - 0
src/page-gym/header-top/index.module.less

@@ -67,4 +67,9 @@
             }
         }
     }
+}
+
+.disable{
+    pointer-events: none;
+    opacity: .6;
 }

+ 10 - 9
src/page-gym/header-top/index.tsx

@@ -9,6 +9,7 @@ import icons from './image/headerTop.json'
 import { Badge, Circle, Popover } from "vant";
 import { metronomeData } from "../../helpers/metronome";
 import Speed from "./speed";
+import { evaluatingData, handleStartEvaluat } from "/src/view/evaluating";
 
 export const headData = reactive({
 	speedShow: false,
@@ -20,8 +21,9 @@ export default defineComponent({
 		const headRef = ref();
 
 		const toggleEvaluat = () => {
-			state.modeType = state.modeType === 'evaluating' ? 'practise' : 'evaluating'
+			handleStartEvaluat()
 		}
+		const disabledList = [ "evaluating"]
 		return () => (
 			<div ref={headRef} class={styles.headerTop}>
 				<div class={styles.back}>
@@ -32,15 +34,14 @@ export default defineComponent({
 				<div class={styles.headRight}>
 					<div class={styles.btn} id="tips-step-2" onClick={toggleEvaluat}>
 						<img class={styles.iconBtn} src={state.modeType === 'evaluating' ? icons.evaluating2 : icons.evaluating} />
-						{/* <Button class={styles.button} icon={runtime.evaluatingStatus ? Icons.evaluating2 : Icons.evaluating} disabled={(this.isAppPlay ? !state.activeDetail?.midiUrl || state.midiPlayIniting : !(runtime.songs.background || runtime.songs.music)) || isHomework || !state.enableEvaluation} onClick={this.authBefore("evaluating", this.evaluating)} /> */}
 						<span>评测</span>
 					</div>
-					<div class={styles.btn} id="tips-step-4" onClick={() => handleChangeSection()}>
+					<div class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]} id="tips-step-4" onClick={() => handleChangeSection()}>
 						<img class={styles.iconBtn} src={headImg(`section${state.section.length}.svg`)} />
 						{/* <Button class={styles.button} icon={Icons["section" + state.section.length]} color="#01C1B5" disabled={runtime.isFirstPlay || runtime.evaluatingStatus || isHomework} onClick={this.authBefore("excerpts", RuntimeUtils.sectionChange)} /> */}
 						<span>选段</span>
 					</div>
-					<div class={styles.btn} id="tips-step-5" onClick={() => togglePlay()}>
+					<div class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]} id="tips-step-5" onClick={() => togglePlay()}>
 						<div class={styles.btnWrap}>
 							<img style={{ marginTop: "-1px" }} class={styles.iconBtn} src={ state.playState === "paused" ? icons.play : icons.pause} />
 							<Circle class={styles.progress} stroke-width={80} currentRate={state.playProgress} rate={100} layerColor="#01C1B5" color="#FFC830" />
@@ -48,7 +49,7 @@ export default defineComponent({
 						<span>{state.playState === "play" ? "暂停" : "播放"}</span>
 					</div>
 					<div
-						class={styles.btn}
+						class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]}
 						id="tips-step-6"
 						onClick={() => {
 							state.playSource = state.playSource === "music" ? "background" : "music";
@@ -58,7 +59,7 @@ export default defineComponent({
 						<span>{state.playSource === "music" ? "原声" : "伴奏"}</span>
 					</div>
 					<div
-						class={styles.btn}
+						class={[styles.btn]}
 						onClick={async () => {
 							metronomeData.lineShow = !metronomeData.lineShow;
 						}}
@@ -77,7 +78,7 @@ export default defineComponent({
 						<img style={{ display: !metronomeData.disable ? "block" : "none" }} class={styles.iconBtn} src={headImg("tickon.png")} />
 						<span style={{ whiteSpace: "nowrap" }}>节拍器</span>
 					</div>
-					<div class={styles.btn} id="tips-step-7" onClick={() => handleResetPlay()}>
+					<div class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]} id="tips-step-7" onClick={() => handleResetPlay()}>
 						<img class={styles.iconBtn} src={headImg("replay.svg")} />
 						<span>重播</span>
 					</div>
@@ -87,7 +88,7 @@ export default defineComponent({
 							reference: () => (
 								<div
 									id="tips-step-8"
-									class={[styles.btn]}
+									class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]}
 									onClick={(e: Event) => {
 										e.stopPropagation()
 										headData.speedShow = !headData.speedShow;
@@ -102,7 +103,7 @@ export default defineComponent({
 							default: () => <Speed />,
 						}}
 					</Popover>
-					<div class={styles.btn}>
+					<div class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]}>
 						<img class={styles.iconBtn} src={headImg("menu.svg")} />
 						<span>设置</span>
 					</div>

+ 2 - 0
src/page-gym/main.ts

@@ -1,4 +1,6 @@
 import 'vant/lib/index.css'
+import "@varlet/ui/es/popup/style/index";
+import "@varlet/ui/es/snackbar/style/index";
 import { createApp } from 'vue'
 import { getRequestHostname } from '../constant/whiteUrl'
 import { initStore } from '../store'

+ 36 - 34
src/state.ts

@@ -7,11 +7,11 @@ import { GradualNote, GradualTimes, GradualVersion, IMode } from "./type";
 import { handleEndBegin, sendEvaluatingOffsetTime } from "./view/evaluating";
 
 /** 入门 | 进阶 | 大师 */
-export type IDifficulty = 'BEGINNER' | 'ADVANCED' | 'PERFORMER'
+export type IDifficulty = "BEGINNER" | "ADVANCED" | "PERFORMER";
 
 const state = reactive({
 	/** 当前曲谱数据ID, 和曲谱ID不一致 */
-	detailId: '',
+	detailId: "",
 	/** 曲谱资源URL */
 	xmlUrl: "",
 	/** 声部ID */
@@ -101,17 +101,16 @@ const state = reactive({
 		/** 显示光标 */
 		displayCursor: true,
 		/** 频率 */
-		frequency: '442',
+		frequency: "442",
 		/** 评测难度 */
-		evaluationDifficulty: 'ADVANCED' as IDifficulty,
+		evaluationDifficulty: "ADVANCED" as IDifficulty,
 		/** 保存到相册 */
 		saveToAlbum: false,
 		/** 开启伴奏 */
-		enableAccompaniment: false
+		enableAccompaniment: false,
 	},
 	/** 节拍器的时间 */
 	fixtime: 0,
-	
 
 	repeatedBeats: 0,
 
@@ -125,7 +124,7 @@ const state = reactive({
 	notchHeight: 0,
 	fixedKey: 0,
 	renderLoading: false,
-	
+
 	isPauseRecording: false,
 	feeShow: false,
 	vipShow: false,
@@ -178,9 +177,9 @@ const setStep = () => {
 /** 开始播放 */
 export const onPlay = () => {
 	setStep();
-	if (state.modeType === 'evaluating'){
+	if (state.modeType === "evaluating") {
 		const currentTime = state.songEl?.currentTime || 0;
-		sendEvaluatingOffsetTime(currentTime)
+		sendEvaluatingOffsetTime(currentTime);
 	}
 };
 /** 播放中事件 */
@@ -188,8 +187,15 @@ export const onTimeupdate = (evt: Event) => {};
 /** 播放完成事件 */
 export const onEnded = () => {
 	handleStopPlay();
-	if (state.modeType === 'evaluating'){
-		handleEndBegin(true)
+	if (state.modeType === "evaluating") {
+		handleEndBegin(true);
+	}
+	// 重复自动播放如果为开启,自动开始播放, 且是练习模式
+	if (state.setting.repeatAutoPlay && state.modeType === "practise") {
+		scrollViewNote();
+		setTimeout(() => {
+			togglePlay("play");
+		}, 1000);
 	}
 };
 
@@ -205,19 +211,18 @@ const handlePlaying = (_item?: any) => {
 		if (item) {
 			// 选段状态下
 			if (state.sectionStatus && state.section.length === 2) {
-				let startItemIndex = state.section[0].index
-				let startXmlIndex = state.section[0].MeasureNumberXML
+				let startItemIndex = state.section[0].index;
+				let startXmlIndex = state.section[0].MeasureNumberXML;
 				// 开启预备拍
 				if (state.sectionFirst) {
-					startItemIndex = state.sectionFirst.i
-					startXmlIndex = state.sectionFirst.MeasureNumberXML
+					startItemIndex = state.sectionFirst.i;
+					startXmlIndex = state.sectionFirst.MeasureNumberXML;
 				}
 				if (item.MeasureNumberXML < startXmlIndex || item.MeasureNumberXML > state.section[1].MeasureNumberXML) {
 					// console.log('选段播放结束')
 					skipNotePlay(startItemIndex);
 					return;
 				}
-				
 			}
 			gotoNext(item);
 		}
@@ -228,11 +233,11 @@ const handlePlaying = (_item?: any) => {
 /** 跳转到指定音符开始播放 */
 export const skipNotePlay = (itemIndex: number, isStart = false) => {
 	const item = state.times[itemIndex];
-	let itemTime = item.time
+	let itemTime = item.time;
 	if (isStart) {
-		itemTime = 0
+		itemTime = 0;
 	}
-	console.log("🚀 ~ itemTime:", itemTime)
+	console.log("🚀 ~ itemTime:", itemTime);
 	if (item) {
 		state.songEl && (state.songEl.currentTime = itemTime);
 		state.backgroundEl && (state.backgroundEl.currentTime = itemTime);
@@ -264,13 +269,6 @@ export const handleStopPlay = () => {
 	state.songEl?.pause();
 	state.backgroundEl?.pause();
 	skipNotePlay(0, true);
-	// 重复自动播放如果为开启,自动开始播放, 且是练习模式
-	if (state.setting.repeatAutoPlay && state.modeType === "practise") {
-		scrollViewNote();
-		setTimeout(() => {
-			togglePlay("play");
-		}, 1000);
-	}
 };
 
 /** 跳转到指定音符 */
@@ -336,9 +334,9 @@ export const handleResetPlay = () => {
 };
 /** 设置速度 */
 export const handleSetSpeed = (speed: number) => {
-	state.speed = speed
-	const playbackRate = speed / state.originSpeed
-	console.log("🚀 ~ playbackRate:", speed , state.originSpeed)
+	state.speed = speed;
+	const playbackRate = speed / state.originSpeed;
+	console.log("🚀 ~ playbackRate:", speed, state.originSpeed);
 
 	// 按照比例设置速度
 	state.songEl && (state.songEl.playbackRate = playbackRate);
@@ -379,17 +377,16 @@ export const handleSelection = (item: any) => {
 		state.section.push(item);
 		if (state.section.length === 2) {
 			state.section = state.section.sort((a, b) => a.time - b.time);
-			let startItemINdex = state.section[0].index
+			let startItemINdex = state.section[0].index;
 			// 开启预备拍
 			if (state.isOpenPrepare) {
-				const startXmlIndex = state.section[0].MeasureNumberXML
-				state.sectionFirst = state.times.find((n: any) => (startXmlIndex - n.MeasureNumberXML) === 1)
-				startItemINdex = state.sectionFirst ? state.sectionFirst.i : startItemINdex
+				const startXmlIndex = state.section[0].MeasureNumberXML;
+				state.sectionFirst = state.times.find((n: any) => startXmlIndex - n.MeasureNumberXML === 1);
+				startItemINdex = state.sectionFirst ? state.sectionFirst.i : startItemINdex;
 			}
 			skipNotePlay(startItemINdex);
 			state.sectionToast?.close();
 			state.sectionToast = null;
-			
 		}
 	}
 	if (state.section.length === 1 && state.sectionToast) {
@@ -418,4 +415,9 @@ export const scrollViewNote = () => {
 		});
 	}
 };
+
+/** 检测是否是节奏练习 */
+export const isRhythmicExercises = () => {
+	return state.examSongName.indexOf("节奏练习") > -1;
+};
 export default state;

+ 4 - 0
src/store.ts

@@ -5,6 +5,10 @@ type IUser = {
     /** 会员结束时间 */
     membershipEndTime?: string
 	tenantId?: number
+	/** 声部名称 */
+	subjectNames?: string
+	/** 头像 */
+	avatar?: string
 };
 type IStatus = "init" | "login" | "logout" | "error";
 type IPlatformType = "STUDENT" | "TEACHER" | "WEB";

+ 1 - 0
src/view/evaluating/evaluatResult.ts

@@ -13,6 +13,7 @@ export interface IScoreItem {
   leve: number | number
   measureIndex?: number
   measureRenderIndex?: number
+  show: boolean
 }
 export interface IEvaluatings {
   [_key: number] : IScoreItem

+ 122 - 45
src/view/evaluating/index.tsx

@@ -1,22 +1,26 @@
-import { closeToast, showLoadingToast, showToast, Toast } from "vant";
-import { defineComponent, onBeforeUnmount, onMounted, reactive, ref } from "vue";
-import { getLeveByScoreMeasure, IEvaluatings } from "./evaluatResult";
+import { Snackbar } from "@varlet/ui";
+import { closeToast, showLoadingToast } from "vant";
+import { defineComponent, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref } from "vue";
+import { getLeveByScore, getLeveByScoreMeasure, IEvaluatings } from "./evaluatResult";
 import {
+	cancelEvaluating,
 	endEvaluating,
 	endSoundCheck,
 	getEarphone,
-	proxyServiceMessage,
+	api_proxyServiceMessage,
 	removeResult,
 	sendResult,
 	startEvaluating,
 	startRecording,
 	startSoundCheck,
+    api_openWebView,
 } from "/src/helpers/communication";
-import _event, { EventEnum } from "/src/helpers/eventemitter";
 import state, { handleStopPlay, togglePlay } from "/src/state";
 import { IPostMessage } from "/src/utils/native-message";
 
 export const evaluatingData = reactive({
+    /** 评测模块是否加载完成 */
+    rendered: false,
 	earphone: false, // 是否插入耳机
 	soundEffect: false, // 是否效音
 	soundEffectFrequency: 0, // 效音频率
@@ -29,8 +33,26 @@ export const evaluatingData = reactive({
 	backtime: 0, // 延迟时间
 	/** 已经评测的数据 */
 	evaluatings: {} as IEvaluatings,
+    /** 评测结果 */
+    resultData: {} as any,
+    /** 评测结果弹窗 */
+    resulstMode: false
 });
 
+/** 点击开始评测按钮 */
+export const handleStartEvaluat = () => {
+    if (state.modeType === 'evaluating') {
+        handleCancelEvaluat()
+    } else {
+        handleStopPlay()
+    }
+    state.modeType = state.modeType === 'evaluating' ? 'practise' : 'evaluating'
+    if (state.modeType !== 'evaluating') {
+        // 切换到练习模式,卸载评测模块
+        evaluatingData.rendered = false
+    }
+}
+
 /** 开始播放发送延迟时间 */
 export const sendEvaluatingOffsetTime = (currentTime: number) => {
 	const nowTime = Date.now();
@@ -41,7 +63,7 @@ export const sendEvaluatingOffsetTime = (currentTime: number) => {
 	console.log("真正播放延迟", delayTime);
 	// 蓝牙耳机延迟一点发送消息确保在录音后面
 	setTimeout(async () => {
-		await proxyServiceMessage({
+		await api_proxyServiceMessage({
 			header: {
 				commond: "audioPlayStart",
 				type: "SOUND_COMPARE",
@@ -66,25 +88,25 @@ const checkSoundEffect = () => {
 };
 
 const handleSoundEffect = (res?: IPostMessage) => {
-    if (res?.content) {
-        const { header, body } = res.content;
-        if (header.commond === "checking") {
-            evaluatingData.soundEffectFrequency = body.frequency;
-        }
-    }
-}
+	if (res?.content) {
+		const { header, body } = res.content;
+		if (header.commond === "checking") {
+			evaluatingData.soundEffectFrequency = body.frequency;
+		}
+	}
+};
 
 /**
  * 开始录音
  */
 const handleStartSoundCheck = () => {
-    sendResult(handleSoundEffect)
-    startSoundCheck();
-}
+	sendResult(handleSoundEffect);
+	startSoundCheck();
+};
 /** 结束录音 */
 export const handleEndSoundCheck = () => {
 	endSoundCheck();
-    removeResult(handleSoundEffect);
+	removeResult(handleSoundEffect);
 };
 
 /** 连接websocket */
@@ -97,7 +119,7 @@ export const connectWebsocket = async (content: any) => {
 	if (res?.api === "startEvaluating") {
 		evaluatingData.websocketState = true;
 	} else {
-		showToast("请在APP端进行评测");
+        Snackbar.error("请在APP端进行评测")
 	}
 };
 
@@ -109,8 +131,12 @@ export const handlePerformDetection = async () => {
 	if (evaluatingData.checkStep === 0) {
 		// 检测耳机
 		const erji = await checkUseEarphone();
-		evaluatingData.earphoneMode = !erji;
 		evaluatingData.checkStep = 1;
+		if (erji) {
+			handlePerformDetection();
+		} else {
+			evaluatingData.earphoneMode = true;
+		}
 		return;
 	}
 	if (evaluatingData.checkStep === 1) {
@@ -126,36 +152,46 @@ export const handlePerformDetection = async () => {
 	if (evaluatingData.checkStep === 10) {
 		// 连接websocket
 		evaluatingData.checkEnd = true;
+        evaluatingData.checkStep = 0
 	}
 };
 
 /** 记录小节分数 */
 const addMeasureScore = (measureScore: any) => {
-    evaluatingData.evaluatings[measureScore.measureRenderIndex] = {
-        ...measureScore,
-        leve: getLeveByScoreMeasure(measureScore.score)
-    }
-    console.log("🚀 ~ measureScore:", evaluatingData.evaluatings[measureScore.measureRenderIndex])
-}
+	evaluatingData.evaluatings[measureScore.measureRenderIndex] = {
+		...measureScore,
+		leve: getLeveByScoreMeasure(measureScore.score),
+		show: true,
+	};
+	// console.log("🚀 ~ measureScore:", evaluatingData.evaluatings[measureScore.measureRenderIndex])
+};
 
 const handleScoreResult = (res?: IPostMessage) => {
-    if (res?.content) {
-        // console.log("🚀 ~ 评测返回:", res);
-        const { header, body } = res.content;
-        if (header?.commond === 'measureScore'){
-            addMeasureScore(body)
-        }
-        if (header?.commond === 'overall'){
-            console.log('评测结束', body)
-            removeResult(handleSoundEffect);
-            closeToast()
-        }
-    }
-}
+	if (res?.content) {
+		// console.log("🚀 ~ 评测返回:", res);
+		const { header, body } = res.content;
+		if (header?.commond === "measureScore") {
+			addMeasureScore(body);
+		}
+		if (header?.commond === "overall") {
+			// console.log("评测结束", body);
+            evaluatingData.resulstMode = true
+            evaluatingData.resultData = {
+                ...body,
+                ...getLeveByScore(body.score)
+            }
+            console.log("🚀 ~ evaluatingData.resultData:", evaluatingData.resultData)
+			removeResult(handleScoreResult);
+			closeToast();
+		}
+	}
+};
 
 /** 开始评测 */
 export const handleStartBegin = async () => {
 	evaluatingData.startBegin = true;
+    evaluatingData.evaluatings = {};
+    evaluatingData.resultData = {};
 	sendResult(handleScoreResult);
 	await startRecording();
 	evaluatingData.backtime = Date.now();
@@ -171,18 +207,59 @@ export const handleEndBegin = (isEnd = false) => {
 	endEvaluating({
 		musicScoreId: state.examSongId,
 	});
-    showLoadingToast({
-        message: '评分中',
-        duration: 0,
-        forbidClick: true,
-    })
-    
-    if (isEnd) return
+	showLoadingToast({
+		message: "评分中",
+		duration: 0,
+		forbidClick: true,
+	});
+
+	if (isEnd) return;
 	handleStopPlay();
 };
+
+/**
+ * 取消评测
+ */
+export const handleCancelEvaluat = () => {
+	evaluatingData.evaluatings = {};
+    // 关闭提示
+    closeToast();
+    // 取消记录
+	api_proxyServiceMessage({
+		header: {
+			commond: "recordCancel",
+			type: "SOUND_COMPARE",
+			status: 200,
+		},
+	});
+    // 取消评测
+	cancelEvaluating();
+    // 卸载评测监听
+    removeResult(handleScoreResult);
+    // 停止播放
+    handleStopPlay();
+};
+
+/** 查看报告 */
+export const handleViewReport = () => {
+    api_openWebView({
+        url: location.origin + "/accompany/#/report/" + evaluatingData.resultData?.recordId || "",
+        orientation: 0,
+        isHideTitle: true, // 此处兼容安卓,意思为隐藏全部头部
+        statusBarTextColor: false,
+        isOpenLight: true,
+    });
+};
 export default defineComponent({
 	name: "evaluating",
 	setup() {
+        onBeforeMount(() => {
+            evaluatingData.resultData = {}
+            evaluatingData.evaluatings = {}
+            evaluatingData.soundEffectFrequency = 0
+            evaluatingData.checkStep = 0
+            evaluatingData.rendered = true
+        })
 		return () => <div></div>;
 	},
 });

+ 28 - 3
src/view/selection/index.module.less

@@ -16,7 +16,7 @@
 }
 
 .staveBox {
-    background-color: var(--active-stave-box);
+    background-color: var(--active-stave-box) !important;
 }
 
 .leftStaveBox {
@@ -104,15 +104,40 @@
 .scoreItem {
     position: absolute;
     left: 50%;
-    top: 50%;
-    transform: translate(-50%, -50%);
+    top: -90%;
+    transform: translateX(-50%);
     font-size: 16px;
     font-family: "Roboto", sans-serif;
     font-weight: bold;
     display: flex;
     align-items: center;
     pointer-events: none;
+    transition: all .8s;
     img {
         height: 30px;
     }
+}
+
+:global {
+    .scoreItemLeve0{
+        background-color: rgba(255, 142, 142, 0.32);
+    }
+    .scoreItemLeve1{
+        background-color: rgba(1, 193, 181, 0.2);
+    }
+    .scoreItemLeve2{
+        background-color: rgba(255, 178, 82, 0.37);
+    }
+    .scoreItemLeve3{
+        background-color: rgba(255, 220, 64, 0.4);
+    }
+    .centerTop-enter-active {
+        opacity: 1;
+    }
+    .centerTop-enter-from {
+        opacity: 0;
+        left: 50%;
+        top: 50%;
+        transform: translateX(-50%) translateY(-50%) scale(.3);
+    }
 }

+ 34 - 28
src/view/selection/index.tsx

@@ -1,9 +1,9 @@
-import { computed, defineComponent, onMounted, reactive } from "vue";
+import { computed, defineComponent, onMounted, reactive, Transition } from "vue";
 import state, { handleSelection, skipNotePlay } from "/src/state";
 import styles from "./index.module.less";
 import { metronomeData } from "/src/helpers/metronome";
 import { evaluatingData } from "../evaluating";
-import { leveByScoreMeasureIcons } from "../evaluating/evaluatResult";
+import { getLeveByScoreMeasure, leveByScoreMeasureIcons } from "../evaluating/evaluatResult";
 
 export default defineComponent({
 	name: "selection",
@@ -95,31 +95,30 @@ export default defineComponent({
 				} else {
 					if (state.activeMeasureIndex == item.MeasureNumberXML) {
 						return styles.staveBox;
-					} else if (state.modeType === "evaluating") {
-						const evaluatItem = evaluatingData.evaluatings[item.measureListIndex];
-						// 评测模式
-						if (evaluatItem) {
-							// return [styles.evaluatBox, styles.]
-						}
 					}
 				}
 			};
 		});
 		onMounted(() => {
 			calcNoteData();
-			let i = 0;
-			setInterval(() => {
-				if (i > state.times.length - 1) return
-				i++
-				evaluatingData.evaluatings[i] = {
-					leve: 1,
-					measureIndex: i,
-					measureRenderIndex: i,
-					score: '67',
-					color: '',
-					icon: leveByScoreMeasureIcons[0].icon
-				};
-			}, 2000);
+			// let i = 0;
+			// setInterval(() => {
+			// 	// if (i > 6) {
+			// 	// 	i = 0
+			// 	// };
+
+			// 	const s = 
+			// 	evaluatingData.evaluatings[i] = {
+			// 		leve: getLeveByScoreMeasure(Math.floor(Math.random() * 100)),
+			// 		measureIndex: i,
+			// 		measureRenderIndex: i,
+			// 		score: Math.floor(Math.random() * 100),
+			// 		color: "",
+			// 		icon: leveByScoreMeasureIcons[0].icon,
+			// 		show: true,
+			// 	};
+			// 	i++;
+			// }, 2000);
 		});
 		return () => (
 			<div class={styles.selectionContainer}>
@@ -128,16 +127,23 @@ export default defineComponent({
 					return (
 						<>
 							{item.staveBox && (
-								<div class={[styles.position, showClass.value(item)]} style={item.staveBox} onClick={() => handleSelection(item)}>
+								<div class={[styles.position, showClass.value(item), scoreItem ? `scoreItemLeve${scoreItem.leve}` : '']} style={item.staveBox} onClick={() => handleSelection(item)}>
 									{!item.isRestFlag && metronomeData.lineShow && item.MeasureNumberXML === metronomeData.activeMetro?.measureNumberXML && (
 										<div class={styles.line} style={{ left: metronomeData.activeMetro.left }}></div>
 									)}
-									{scoreItem && (
-										<div class={styles.scoreItem} style={{ color: leveByScoreMeasureIcons[scoreItem.leve]?.color || "" }}>
-											<img src={leveByScoreMeasureIcons[scoreItem.leve]?.icon} />
-											<span>{scoreItem.score}</span>
-										</div>
-									)}
+									<Transition
+										name="centerTop"
+										onAfterEnter={() => {
+											scoreItem.show = false
+										}}
+									>
+										{scoreItem?.show && (
+											<div class={styles.scoreItem} style={{ color: leveByScoreMeasureIcons[scoreItem.leve]?.color || "" }}>
+												<img src={leveByScoreMeasureIcons[scoreItem.leve]?.icon} />
+												<span>{scoreItem.score}</span>
+											</div>
+										)}
+									</Transition>
 								</div>
 							)}
 						</>

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác