index.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. import { computed, defineComponent, nextTick, reactive, ref, toRefs } from "vue";
  2. import styles from "./index.module.less";
  3. import { api_back } from "/src/helpers/communication";
  4. import state from "/src/state";
  5. import iconBack from "./image/icon-back.svg";
  6. import iconShiyi from "./image/icon-shiyi.svg";
  7. import iconhuifang from "./image/icon-huifang.svg";
  8. import { Grid, GridItem, Popup } from "vant";
  9. import videobg from "./image/videobg.png";
  10. import "plyr/dist/plyr.css";
  11. import Plyr from "plyr";
  12. import { browser } from "/src/utils";
  13. import Note from "../note";
  14. import { storeData } from "/src/store";
  15. type IItemType = "intonation" | "cadence" | "integrity";
  16. export default defineComponent({
  17. name: "header-top",
  18. props: {
  19. scoreData: {
  20. type: Object,
  21. default: () => ({}),
  22. },
  23. },
  24. setup(props, {expose}) {
  25. const browserInfo = browser();
  26. const { scoreData } = toRefs(props);
  27. const shareData = reactive({
  28. show: false,
  29. shiyiShow: false,
  30. isInitPlyr: false,
  31. _plrl: null as any,
  32. });
  33. const level: any = {
  34. BEGINNER: "入门级",
  35. ADVANCED: "进阶级",
  36. PERFORMER: "大师级",
  37. };
  38. // console.log("🚀 ~ scoreData:", scoreData.value)
  39. const itemType = ref<IItemType>("intonation");
  40. /** 返回 */
  41. const handleBack = () => {
  42. api_back();
  43. };
  44. const handleChange = (type: IItemType) => {
  45. itemType.value = type;
  46. scoreData.value.itemType = type
  47. };
  48. // 资源类型
  49. const mediaType = computed((): "audio" | "video" => {
  50. const subfix = (scoreData.value.videoFilePath || "").split(".").pop();
  51. if (subfix === "wav" || subfix === "mp3" || subfix === "m4a") {
  52. return "audio";
  53. }
  54. return "video";
  55. });
  56. const openAudioAndVideo = () => {
  57. shareData.show = true;
  58. if (shareData.isInitPlyr) return;
  59. nextTick(() => {
  60. const id = mediaType.value === "audio" ? "#audioSrc" : "#videoSrc";
  61. shareData._plrl = new Plyr(id, {
  62. controls: ["play-large", "play", "progress", "current-time"],
  63. fullscreen: { enabled: false },
  64. });
  65. shareData.isInitPlyr = true;
  66. });
  67. };
  68. return () => (
  69. <div class={[styles.headerTop, browserInfo.android && styles.android]}>
  70. <div class={[styles.back, !storeData.isApp && styles.disabled]} onClick={handleBack}>
  71. <img src={iconBack} />
  72. </div>
  73. <div class={styles.center}>
  74. <div class={styles.cItem}>
  75. <div>{level[scoreData.value.heardLevel]}</div>
  76. <div>难度</div>
  77. </div>
  78. <div class={styles.cItem}>
  79. <div>{scoreData.value.score}分</div>
  80. <div>评测分数</div>
  81. </div>
  82. {state.isPercussion ? null : (
  83. <>
  84. <div
  85. onClick={() => handleChange("intonation")}
  86. class={[styles.cItem, itemType.value === "intonation" && styles.active]}
  87. >
  88. <div style={{ color: "rgb(45, 199, 170)" }}>{scoreData.value.intonation}分</div>
  89. <div>音准</div>
  90. </div>
  91. <div
  92. onClick={() => handleChange("cadence")}
  93. class={[styles.cItem, itemType.value === "cadence" && styles.active]}
  94. >
  95. <div style={{ color: "#FF4E19" }}>{scoreData.value.cadence}分</div>
  96. <div>节奏</div>
  97. </div>
  98. <div
  99. onClick={() => handleChange("integrity")}
  100. class={[styles.cItem, itemType.value === "integrity" && styles.active]}
  101. >
  102. <div style={{ color: "rgb(255, 196, 89)" }}>{scoreData.value.integrity}分</div>
  103. <div>完成度</div>
  104. </div>
  105. </>
  106. )}
  107. </div>
  108. <div class={styles.right}>
  109. <div
  110. style={{ display: scoreData.value.videoFilePath ? "" : "none" }}
  111. class={styles.btn}
  112. onClick={openAudioAndVideo}
  113. >
  114. <img class={styles.iconBtn} src={iconhuifang} />
  115. <span>回放</span>
  116. </div>
  117. <div class={styles.btn} onClick={() => (shareData.shiyiShow = true)}>
  118. <img class={styles.iconBtn} src={iconShiyi} />
  119. <span>释义</span>
  120. </div>
  121. {/* <div class={styles.btn}>
  122. <img class={styles.iconBtn} src={iconhuifang} />
  123. <span>再来一遍</span>
  124. </div> */}
  125. </div>
  126. {state.isPercussion ? null : (
  127. <div class={styles.demos}>
  128. <div>
  129. <Note fill="#01C1B5" />
  130. <span>演奏正确</span>
  131. </div>
  132. {itemType.value === "intonation" && (
  133. <>
  134. <div>
  135. <Note fill="rgba(1, 193, 181, .8)" shadowFill="#FFAB25" shadow x={-2} y={0} />
  136. <span>音高了</span>
  137. </div>
  138. <div>
  139. <Note fill="rgba(1, 193, 181, .8)" shadowFill="#FFAB25" shadow x={-1} y={-3} />
  140. <span>音低了</span>
  141. </div>
  142. </>
  143. )}
  144. {itemType.value === "cadence" && (
  145. <>
  146. <div>
  147. <Note fill="rgba(1, 193, 181, .8)" shadowFill="#FF4444" shadow x={0.5} y={-1} />
  148. <span>节奏过快</span>
  149. </div>
  150. <div>
  151. <Note fill="rgba(1, 193, 181, .8)" shadowFill="#FF4444" shadow x={-3} y={-2.5} />
  152. <span>节奏慢了</span>
  153. </div>
  154. </>
  155. )}
  156. {itemType.value === "integrity" && (
  157. <div>
  158. <Note fill="#CC75FF" />
  159. <span>完成度不足</span>
  160. </div>
  161. )}
  162. <div>
  163. <Note fill="#AEAEAE" />
  164. <span>未演奏</span>
  165. </div>
  166. </div>
  167. )}
  168. <Popup
  169. teleport="body"
  170. class={["popup-custom", "van-scale", styles.popup]}
  171. transition="van-scale"
  172. v-model:show={shareData.show}
  173. closeable
  174. onClose={() => {
  175. shareData._plrl?.pause();
  176. }}
  177. >
  178. {mediaType.value === "audio" && (
  179. <div class={styles.audiobox}>
  180. <audio
  181. id="audioSrc"
  182. src={scoreData.value.videoFilePath}
  183. controls="false"
  184. preload="metadata"
  185. playsinline
  186. />
  187. </div>
  188. )}
  189. {mediaType.value === "video" && (
  190. <div class={styles.videobox}>
  191. <video
  192. id="videoSrc"
  193. class={styles.videoBox}
  194. src={scoreData.value.videoFilePath}
  195. data-poster={videobg}
  196. preload="metadata"
  197. playsinline
  198. />
  199. </div>
  200. )}
  201. </Popup>
  202. <Popup
  203. v-model:show={shareData.shiyiShow}
  204. class="popup-custom van-scale center-closeBtn"
  205. transition="van-scale"
  206. teleport="body"
  207. closeable
  208. >
  209. <div class={styles.shiyiPopup}>
  210. <div class={styles.shiyiTitle}>图标释义</div>
  211. <div class={styles.items}>
  212. <div class={styles.item}>
  213. <Note fill="#01C1B5" />
  214. <span>绿色音符:演奏正确</span>
  215. </div>
  216. <div class={styles.item}>
  217. <Note fill="#FF4444" />
  218. <span>红色音符:错音</span>
  219. </div>
  220. <div class={styles.item}>
  221. <Note fill="#CC75FF" />
  222. <span>紫色音符:完成度不足</span>
  223. </div>
  224. <div class={styles.item}>
  225. <Note fill="#AEAEAE" />
  226. <span>灰色音符:未演奏</span>
  227. </div>
  228. <div class={styles.item}>
  229. <Note fill="rgba(1, 193, 181, .8)" shadowFill="#FF4444" shadow x={0.5} y={-1} />
  230. <span>音符重影(红色在前):节奏过快</span>
  231. </div>
  232. <div class={styles.item}>
  233. <Note fill="rgba(1, 193, 181, .8)" shadowFill="#FF4444" shadow x={-3} y={-2.5} />
  234. <span>音符重影(红色在后):节奏慢了</span>
  235. </div>
  236. <div class={styles.item}>
  237. <Note fill="rgba(1, 193, 181, .8)" shadowFill="#FFAB25" shadow x={-2} y={0} />
  238. <span>音符重影(黄色在上):音高了</span>
  239. </div>
  240. <div class={styles.item}>
  241. <Note fill="rgba(1, 193, 181, .8)" shadowFill="#FFAB25" shadow x={-1} y={-3} />
  242. <span>音符重影(黄色在下):音低了</span>
  243. </div>
  244. </div>
  245. </div>
  246. </Popup>
  247. </div>
  248. );
  249. },
  250. });