index.tsx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785
  1. import styles from "./index.module.less";
  2. import { Snackbar } from "@varlet/ui";
  3. import { closeToast, showLoadingToast, showToast, Popup } from "vant";
  4. import { defineComponent, onMounted, onUnmounted, reactive, ref, watch } from "vue";
  5. import { getLeveByScore, getLeveByScoreMeasure, IEvaluatings } from "./evaluatResult";
  6. import {
  7. cancelEvaluating,
  8. endEvaluating,
  9. endSoundCheck,
  10. getEarphone,
  11. api_proxyServiceMessage,
  12. removeResult,
  13. sendResult,
  14. startEvaluating,
  15. startSoundCheck,
  16. api_openWebView,
  17. api_startRecording,
  18. api_startRecordingCb,
  19. api_stopRecording,
  20. api_recordStartTime,
  21. api_remove_recordStartTime,
  22. api_startCapture,
  23. api_endCapture,
  24. api_getDeviceDelay,
  25. hideComplexButton,
  26. api_checkSocketStatus,
  27. addAccompanyError,
  28. removeAccompanyError,
  29. addSocketStatus,
  30. removeSocketStatus,
  31. api_disconnectSocket,
  32. api_midiMicDelay,
  33. api_cloudSetCurrentTime,
  34. api_cloudChangeSpeed,
  35. api_startDelayCheck,
  36. api_closeDelayCheck,
  37. } from "/src/helpers/communication";
  38. import state, { IPlayState, clearSelection, handleStopPlay, onPlay, resetPlaybackToStart, togglePlay, initSetPlayRate } from "/src/state";
  39. import { IPostMessage } from "/src/utils/native-message";
  40. import { usePageVisibility } from "@vant/use";
  41. import { browser } from "/src/utils";
  42. import { getAudioCurrentTime, toggleMutePlayAudio, audioListStart, audioData } from "../audio-list";
  43. import { handleStartTick, closeTick } from "../tick";
  44. import AbnormalPop from "../abnormal-pop";
  45. import { storeData } from "../../store";
  46. import icon_bg from "../abnormal-pop/icon_bg.svg";
  47. import icon_close from "../abnormal-pop/icon_close.svg";
  48. import icon_btn from "../abnormal-pop/icon_btn.svg";
  49. import icon_success from "../abnormal-pop/icon_success.svg";
  50. import { data } from "../../page-instrument/custom-plugins/work-index";
  51. import { startCountdown } from "/src/page-instrument/evaluat-model/countdown";
  52. const browserInfo = browser();
  53. let socketStartTime = 0;
  54. export const popImgs = {
  55. icon_bg,
  56. icon_close,
  57. icon_btn,
  58. icon_success,
  59. };
  60. export const evaluatingData = reactive({
  61. /** 评测数据 */
  62. contentData: {} as any,
  63. /** 评测模块是否加载完成 */
  64. rendered: false,
  65. earphone: false, // 是否插入耳机
  66. soundEffect: false, // 是否效音
  67. soundEffectFrequency: 0, // 效音频率
  68. checkStep: 0, // 执行步骤
  69. checkEnd: false, // 检测结束
  70. earphoneMode: false, // 耳机弹窗
  71. earPhoneType: "" as "" | "有线耳机" | "蓝牙耳机", // 耳机类型
  72. soundEffectMode: false, // 效音弹窗
  73. websocketState: false, // websocket连接状态
  74. /**是否开始播放 */
  75. startBegin: false, // 开始
  76. backtime: 0, // 延迟时间
  77. /** 已经评测的数据 */
  78. evaluatings: {} as IEvaluatings,
  79. /** 评测结果 */
  80. resultData: {} as any,
  81. /** 评测结果弹窗 */
  82. resulstMode: false,
  83. /** 是否是完整评测 */
  84. isComplete: false,
  85. /** */
  86. isDisabledPlayMusic: false,
  87. /** socket异常状态弹窗 */
  88. socketErrorPop: false,
  89. /** 异常提示 */
  90. errorContents: '',
  91. /** socket异常状态弹窗的状态值 */
  92. socketErrorStatus: 0,
  93. /** 延迟检测,socket状态异常 */
  94. delayCheckSocketError: false,
  95. /** 异常状态,不生成评测记录,不调用保存接口 */
  96. isErrorState: false,
  97. /** accompanyError,错误类型 */
  98. accompanyErrorType: '',
  99. /** app播放结束状态,重新评测需要重置为 */
  100. isAudioPlayEnd: false,
  101. preloadJson: true, // 预加载延迟检测的资源
  102. jsonLoading: true, // 延迟检测的资源加载中状态
  103. jsonLoadDone: true, // 延迟检测的动画dom加载完成状态
  104. hideResultModal: false, // 评测作业,如果不是完整评测,需要隐藏评测结果弹窗
  105. oneselfCancleEvaluating: false, // 是否是自主取消评测,自主取消评测,不生产评测记录
  106. isBeginMask: false // 倒计时和系统节拍器时候的遮罩,防止用户点击
  107. });
  108. const sendOffsetTime = async (offsetTime: number) => {
  109. const delayData = await api_getDeviceDelay();
  110. api_midiMicDelay({
  111. header: {
  112. commond: "audioPlayStart",
  113. type: "SOUND_COMPARE",
  114. },
  115. body: {
  116. offsetTime,
  117. micDelay: delayData?.content?.value,
  118. },
  119. });
  120. };
  121. /** 点击开始评测按钮 */
  122. export const handleStartEvaluat = async () => {
  123. if (state.modeType === "evaluating") {
  124. handleCancelEvaluat();
  125. } else {
  126. if (state.platform !== "PC") {
  127. // 评测前先检查APP端的websocket状态
  128. const res = await api_checkSocketStatus();
  129. if (res?.content?.status === "connected") {
  130. handleStopPlay();
  131. } else {
  132. // socket未连接
  133. // evaluatingData.socketErrorPop = true
  134. }
  135. } else {
  136. handleStopPlay();
  137. }
  138. }
  139. state.modeType = state.modeType === "evaluating" ? "practise" : "evaluating";
  140. if (state.modeType !== "evaluating") {
  141. // 切换到练习模式,卸载评测模块
  142. evaluatingData.rendered = false;
  143. }
  144. };
  145. /** 开始评测 & 延迟检测开始按钮 */
  146. export const startCheckDelay = async () => {
  147. // 评测前先检查APP端的websocket状态
  148. const res = await api_checkSocketStatus();
  149. if (res?.content?.status === "connected") {
  150. //
  151. return new Promise((resolve) => {
  152. resolve({ checked: true });
  153. });
  154. } else {
  155. /**
  156. * socket未连接,记录此时的时间,以便于和收到socket成功链接,进行对比,对比时间小于500ms时,则连接中的状态默认显示500ms持续时间
  157. *
  158. * */
  159. socketStartTime = +new Date();
  160. evaluatingData.socketErrorPop = true;
  161. evaluatingData.socketErrorStatus = 1;
  162. return new Promise((resolve) => {
  163. resolve({ checked: false });
  164. });
  165. }
  166. };
  167. const check_currentTime = () => {
  168. let preTime = 0;
  169. // 选段评测模式
  170. if (state.isSelectMeasureMode) {
  171. preTime = state.section[0].time * 1000;
  172. }
  173. const currentTime = getAudioCurrentTime() * 1000 - preTime;
  174. // console.log('播放进度music', currentTime, 'preTime:' + preTime)
  175. if (currentTime >= 500) {
  176. sendEvaluatingOffsetTime(500);
  177. return;
  178. }
  179. setTimeout(() => {
  180. check_currentTime();
  181. }, 10);
  182. };
  183. /** 开始播放发送延迟时间 */
  184. export const sendEvaluatingOffsetTime = async (currentTime: number) => {
  185. // 没有开始时间点, 不处理
  186. if (!evaluatingData.backtime) return;
  187. const nowTime = Date.now();
  188. const delayTime = nowTime - evaluatingData.backtime - currentTime;
  189. console.error("真正播放延迟", delayTime, "currentTime:", currentTime);
  190. await api_proxyServiceMessage({
  191. header: {
  192. commond: "audioPlayStart",
  193. type: "SOUND_COMPARE",
  194. },
  195. body: {
  196. offsetTime: delayTime < 0 ? 0 : delayTime,
  197. micDelay: 0,
  198. },
  199. });
  200. };
  201. /** 检测耳机 */
  202. export const checkUseEarphone = async () => {
  203. const res = await getEarphone();
  204. return res?.content?.checkIsWired || false;
  205. };
  206. /**
  207. * 开始录音
  208. */
  209. const handleStartSoundCheck = () => {
  210. startSoundCheck();
  211. };
  212. /** 结束录音 */
  213. export const handleEndSoundCheck = () => {
  214. endSoundCheck();
  215. };
  216. /** 连接websocket */
  217. export const connectWebsocket = async (content: any) => {
  218. evaluatingData.contentData = content;
  219. evaluatingData.websocketState = true;
  220. };
  221. /**
  222. * 执行检测
  223. */
  224. export const handlePerformDetection = async () => {
  225. // 检测完成不检测了
  226. if (evaluatingData.checkEnd) return;
  227. // 延迟检测
  228. if (evaluatingData.checkStep === 0) {
  229. evaluatingData.checkStep = 5;
  230. // 没有设备延迟数据 或 开启了效音 显示检测组件,并持续检测耳机状态
  231. if (state.setting.soundEffect) {
  232. evaluatingData.soundEffectMode = true;
  233. return;
  234. }
  235. const delayTime = await api_getDeviceDelay();
  236. console.log("🚀 ~ delayTime:", delayTime);
  237. if (!delayTime) {
  238. evaluatingData.soundEffectMode = true;
  239. return;
  240. }
  241. handlePerformDetection();
  242. return;
  243. }
  244. // 检测耳机
  245. if ((evaluatingData.checkStep = 5)) {
  246. evaluatingData.checkStep = 10;
  247. const erji = await checkUseEarphone();
  248. if (!erji) {
  249. evaluatingData.earphoneMode = true;
  250. return;
  251. }
  252. handlePerformDetection();
  253. return;
  254. }
  255. // 效音
  256. // if (evaluatingData.checkStep === 7) {
  257. // // 是否需要开启效音
  258. // evaluatingData.checkStep = 10;
  259. // if (state.setting.soundEffect && !state.isPercussion) {
  260. // evaluatingData.soundEffectMode = true;
  261. // handleStartSoundCheck();
  262. // return
  263. // }
  264. // handlePerformDetection();
  265. // return;
  266. // }
  267. // 效验完成
  268. if (evaluatingData.checkStep === 10) {
  269. evaluatingData.checkEnd = true;
  270. }
  271. };
  272. /** 记录小节分数 */
  273. export const addMeasureScore = (measureScore: any, show = true) => {
  274. // #8720 bug修复
  275. for (let idx in evaluatingData.evaluatings) {
  276. evaluatingData.evaluatings[idx].show = false;
  277. }
  278. evaluatingData.evaluatings[measureScore.measureRenderIndex] = {
  279. ...measureScore,
  280. leve: getLeveByScoreMeasure(measureScore.score),
  281. show,
  282. };
  283. // console.log("🚀 ~ measureScore:", evaluatingData.evaluatings)
  284. };
  285. const handleScoreResult = (res?: IPostMessage) => {
  286. console.log("返回", res, evaluatingData.oneselfCancleEvaluating);
  287. // 如果是手动取消评测,不生成评测记录
  288. if (evaluatingData.oneselfCancleEvaluating) {
  289. return;
  290. }
  291. if (res?.content) {
  292. const { header, body } = res.content;
  293. // 效音返回
  294. if (header.commond === "checking") {
  295. evaluatingData.soundEffectFrequency = body.frequency;
  296. }
  297. // 小节评分返回
  298. if (header?.commond === "measureScore") {
  299. console.log("🚀 ~ 评测返回:", res);
  300. addMeasureScore(body);
  301. }
  302. // 评测结束返回
  303. if (header?.commond === "overall") {
  304. console.log("🚀 ~ 评测返回:", res);
  305. console.log("评测结束", body);
  306. state.isHideEvaluatReportSaveBtn = false;
  307. setTimeout(() => {
  308. // 评测作业,如果不是完整评测,不展示评测弹窗
  309. if (data.trainingType === "EVALUATION" && !evaluatingData.isComplete) {
  310. evaluatingData.hideResultModal = true;
  311. } else {
  312. evaluatingData.hideResultModal = false;
  313. }
  314. evaluatingData.resulstMode = evaluatingData.isErrorState ? false : true;
  315. }, 200);
  316. evaluatingData.resultData = {
  317. ...body,
  318. ...getLeveByScore(body.score),
  319. };
  320. // console.log("🚀 ~ evaluatingData.resultData:", evaluatingData.resultData)
  321. closeToast();
  322. }
  323. }
  324. };
  325. /** 开始评测 */
  326. export const handleStartBegin = async (preTimes?: number) => {
  327. if (state.isAppPlay) {
  328. await api_cloudSetCurrentTime({
  329. currentTime: 0,
  330. songID: state.examSongId,
  331. })
  332. }
  333. evaluatingData.isComplete = false;
  334. evaluatingData.evaluatings = {};
  335. evaluatingData.resultData = {};
  336. evaluatingData.backtime = 0;
  337. evaluatingData.isAudioPlayEnd = false;
  338. const res = await startEvaluating(evaluatingData.contentData);
  339. if (res?.api !== "startEvaluating") {
  340. Snackbar.error("请在APP端进行评测");
  341. evaluatingData.startBegin = false;
  342. return;
  343. }
  344. if (res?.content?.reson) {
  345. showToast(res.content?.des);
  346. evaluatingData.startBegin = false;
  347. return;
  348. }
  349. initSetPlayRate();
  350. resetPlaybackToStart();
  351. evaluatingData.startBegin = true;
  352. if (evaluatingData.isDisabledPlayMusic) {
  353. evaluatingData.isBeginMask = true
  354. // 先播放倒计时
  355. await startCountdown()
  356. state.playState = state.playState === "paused" ? "play" : "paused";
  357. // 设置为开始播放时, 如果需要节拍,先播放节拍器
  358. if (state.playState === "play" && (state.playType==="play"&&state.needTick)||(state.playType==="sing"&&state.needSingTick)) {
  359. // 如果是系统节拍器 等系统节拍器播完了再播,如果是mp3节拍器 直接播
  360. if((state.playType==="play" && !state.isOpenMetronome)||(state.playType==="sing" && !state.isSingOpenMetronome)){
  361. const tickend = await handleStartTick();
  362. console.log("🚀 ~ tickend:", tickend)
  363. // 节拍器返回false, 取消播放
  364. if (!tickend) {
  365. state.playState = "paused";
  366. evaluatingData.startBegin = false;
  367. evaluatingData.isBeginMask = false
  368. return;
  369. }
  370. }else{
  371. handleStartTick()
  372. }
  373. }
  374. evaluatingData.isBeginMask = false
  375. onPlay();
  376. }
  377. if (evaluatingData.isErrorState) return
  378. //开始录音
  379. // await api_startRecording({
  380. // accompanimentState: state.setting.enableAccompaniment ? 1 : 0,
  381. // firstNoteTime: preTimes || 0,
  382. // });
  383. let rate = state.speed / state.originSpeed;
  384. rate = parseFloat(rate.toFixed(2));
  385. await api_startRecordingCb({
  386. // accompanimentState: state.setting.enableAccompaniment ? 1 : 0,
  387. accompanimentState: !state.accompany ? 0 : 1, // 评测没有伴奏时,静音播放
  388. firstNoteTime: preTimes || 0,
  389. speedRate: rate, // 播放倍率
  390. }, () => {
  391. if (state.isAppPlay) {
  392. setTimeout(() => {
  393. sendOffsetTime(0)
  394. }, 300);
  395. }
  396. })
  397. // 如果开启了摄像头, 开启录制视频
  398. if (state.setting.camera) {
  399. console.log("开始录制视频");
  400. await api_startCapture();
  401. }
  402. // 如果是midi音频评测,需要调用cloudPlay
  403. if (state.isAppPlay) {
  404. await api_cloudChangeSpeed({
  405. speed: state.originSpeed,
  406. originalSpeed: state.originSpeed,
  407. songID: state.examSongId,
  408. });
  409. audioData.progress = 0
  410. audioListStart(state.playState);
  411. }
  412. evaluatingData.oneselfCancleEvaluating = false;
  413. };
  414. /** 播放音乐 */
  415. const playMusic = async () => {
  416. const playState = await togglePlay("play");
  417. // 取消播放,停止播放
  418. if (!playState) {
  419. evaluatingData.startBegin = false;
  420. handleCancelEvaluat();
  421. return;
  422. }
  423. // 检测播放进度, 计算延迟
  424. check_currentTime();
  425. // 如果开启了摄像头, 开启录制视频
  426. if (state.setting.camera) {
  427. console.log("开始录制视频");
  428. api_startCapture();
  429. }
  430. };
  431. let _audio: HTMLAudioElement;
  432. /** 录音开始,记录开始时间点 */
  433. const recordStartTimePoint = async (res?: IPostMessage) => {
  434. console.error("开始录音");
  435. // 没有开始评测,不处理
  436. if (!evaluatingData.startBegin) return;
  437. let inteveral = res?.content?.inteveral || 0;
  438. if (browserInfo.ios) {
  439. inteveral *= 1000;
  440. }
  441. evaluatingData.backtime = inteveral || Date.now();
  442. console.log("🚀 ~ 开始时间点:", evaluatingData.backtime, "已经录的时间:", Date.now() - inteveral, "记录时间点:", Date.now());
  443. // 是否禁播
  444. if (evaluatingData.isDisabledPlayMusic) {
  445. return;
  446. }
  447. // 开始播放
  448. playMusic();
  449. };
  450. /**
  451. * 结束评测
  452. * @param isComplete 是否完整评测
  453. * @returns
  454. */
  455. export const handleEndEvaluat = (isComplete = false) => {
  456. // 没有开始评测 , 不是评测模式 , 不评分
  457. if (!evaluatingData.startBegin || state.modeType !== "evaluating") return;
  458. // 结束录音
  459. // api_stopRecording();
  460. // 结束评测
  461. console.log("评测结束1");
  462. endEvaluating({
  463. musicScoreId: state.examSongId,
  464. });
  465. // 评测作业如果不是完整评测,给出提示
  466. if (!isComplete && data.trainingType === "EVALUATION") {
  467. showToast({
  468. message: "完整演奏结束才算作业分数!",
  469. });
  470. } else {
  471. showLoadingToast({
  472. message: "评分中",
  473. duration: 0,
  474. overlay: true,
  475. overlayClass: styles.scoreMode,
  476. });
  477. }
  478. setTimeout(() => {
  479. evaluatingData.startBegin = false;
  480. }, 500);
  481. evaluatingData.isComplete = isComplete;
  482. // 如果开启了摄像头, 结束录制视频
  483. if (state.setting.camera) {
  484. console.log("结束录制视频");
  485. api_endCapture();
  486. }
  487. };
  488. /**
  489. * 结束评测(手动结束评测)
  490. */
  491. export const handleEndBegin = () => {
  492. handleEndEvaluat();
  493. handleStopPlay();
  494. };
  495. /**
  496. * 取消评测
  497. */
  498. export const handleCancelEvaluat = (cancelType?: string) => {
  499. evaluatingData.evaluatings = {};
  500. evaluatingData.startBegin = false;
  501. // 关闭提示
  502. closeToast();
  503. // 取消记录
  504. api_proxyServiceMessage({
  505. header: {
  506. commond: "recordCancel",
  507. type: "SOUND_COMPARE",
  508. status: 200,
  509. },
  510. });
  511. /**
  512. * 异常状态是取消评测(cancelEvaluating),正常结束时结束评测(endEvaluating)
  513. */
  514. // if (cancelType === "cancel") {
  515. // // 取消评测
  516. // cancelEvaluating();
  517. // } else {
  518. // endEvaluating({
  519. // musicScoreId: state.examSongId,
  520. // });
  521. // }
  522. cancelEvaluating();
  523. // 停止播放
  524. handleStopPlay();
  525. console.log("评测结束2");
  526. endEvaluating({
  527. musicScoreId: state.examSongId,
  528. });
  529. // 如果开启了摄像头, 结束录制视频
  530. if (state.setting.camera) {
  531. console.log("结束录制视频");
  532. api_endCapture();
  533. }
  534. };
  535. /** 查看报告 */
  536. export const handleViewReport = (key: "recordId" | "recordIdStr", type: "gym" | "colexiu" | "orchestra" | "instrument") => {
  537. const id = evaluatingData.resultData?.[key] || "";
  538. let url = "";
  539. switch (type) {
  540. case "gym":
  541. url = location.origin + location.pathname + "#/report/" + id;
  542. break;
  543. case "orchestra":
  544. url = location.origin + location.pathname + "report-share.html?id=" + id;
  545. break;
  546. case "instrument":
  547. url = location.origin + location.pathname + "#/evaluat-report?id=" + id + "&musicRenderType=" + state.musicRenderType;
  548. break;
  549. default:
  550. url = location.origin + location.pathname + "report-share.html?id=" + id;
  551. break;
  552. }
  553. api_openWebView({
  554. url,
  555. orientation: 0,
  556. isHideTitle: true, // 此处兼容安卓,意思为隐藏全部头部
  557. statusBarTextColor: false,
  558. isOpenLight: true,
  559. c_orientation: 0,
  560. });
  561. };
  562. // 隐藏存演奏按钮
  563. const handleComplexButton = (res?: IPostMessage) => {
  564. console.log("监听是否隐藏保存按钮", res);
  565. if (res?.content) {
  566. const { header, body } = res.content;
  567. state.isHideEvaluatReportSaveBtn = true;
  568. }
  569. };
  570. // 检测到APP发送的异常信息
  571. const handleAccompanyError = (res?: IPostMessage) => {
  572. console.log("异常信息返回", res);
  573. if (res?.content) {
  574. const { type, reson } = res.content;
  575. switch (type) {
  576. case "enterBackground":
  577. // App退到后台
  578. case "playError":
  579. // 播放异常
  580. case "socketError":
  581. // socket连接断开,评测中,则取消评测
  582. // 延迟检测中
  583. if (evaluatingData.soundEffectMode) {
  584. evaluatingData.socketErrorStatus = 0;
  585. evaluatingData.delayCheckSocketError = true;
  586. evaluatingData.socketErrorPop = type === "socketError" ? true : false;
  587. evaluatingData.accompanyErrorType = type;
  588. // api_checkSocketStatus()
  589. return;
  590. }
  591. // 评测中
  592. if (state.modeType === "evaluating" && evaluatingData.startBegin) {
  593. handleCancelEvaluat("cancel");
  594. }
  595. // 关闭节拍器
  596. closeTick();
  597. evaluatingData.socketErrorStatus = 0;
  598. evaluatingData.socketErrorPop = type === "socketError" ? true : false;
  599. evaluatingData.isErrorState = true;
  600. evaluatingData.accompanyErrorType = type;
  601. resetPlaybackToStart();
  602. break;
  603. case "recordError":
  604. // 录音异常
  605. break;
  606. default:
  607. break;
  608. }
  609. }
  610. };
  611. // 监测socket状态,是否已经成功连接
  612. const handleSocketStatus = (res?: IPostMessage) => {
  613. if (res?.content?.status === "connected") {
  614. const currentTime = +new Date();
  615. evaluatingData.delayCheckSocketError = false;
  616. const diffTime = currentTime - socketStartTime;
  617. if (diffTime < 1000) {
  618. const remainingTime = 1000 - diffTime;
  619. console.log(remainingTime, 99999);
  620. setTimeout(() => {
  621. evaluatingData.socketErrorStatus = 2;
  622. }, remainingTime);
  623. }
  624. }
  625. };
  626. // 评测出现异常,再试一次
  627. const hanldeConfirmPop = async () => {
  628. api_checkSocketStatus();
  629. evaluatingData.socketErrorStatus = 1;
  630. socketStartTime = +new Date();
  631. };
  632. // 关闭异常弹窗
  633. const hanldeClosePop = () => {
  634. evaluatingData.socketErrorPop = false;
  635. evaluatingData.socketErrorStatus = 0;
  636. };
  637. export default defineComponent({
  638. name: "evaluating",
  639. setup() {
  640. const pageVisibility = usePageVisibility();
  641. // 需要记录的数据
  642. const record_old_data = reactive({
  643. /** 指法 */
  644. finger: false,
  645. /** 原音伴奏 */
  646. play_mode: "" as IPlayState,
  647. /** 评测是否要伴奏 */
  648. enableAccompaniment: true,
  649. });
  650. /** 记录状态 */
  651. const hanlde_record = () => {
  652. // 取消指法
  653. record_old_data.finger = state.setting.displayFingering;
  654. state.setting.displayFingering = false;
  655. // 切换为伴奏
  656. record_old_data.play_mode = state.playSource;
  657. record_old_data.enableAccompaniment = state.setting.enableAccompaniment;
  658. // 如果关闭伴奏,评测静音
  659. if (!record_old_data.enableAccompaniment) {
  660. console.log("关闭伴奏");
  661. toggleMutePlayAudio(record_old_data.play_mode === "music" ? "music" : record_old_data.play_mode === "background" ? "background" : "mingSong", true);
  662. }
  663. };
  664. /** 还原状态 */
  665. const handle_reduction = () => {
  666. // 还原指法
  667. state.setting.displayFingering = record_old_data.finger;
  668. state.playSource = record_old_data.play_mode;
  669. // 如果关闭伴奏, 结束评测取消静音
  670. if (!record_old_data.enableAccompaniment) {
  671. toggleMutePlayAudio(record_old_data.play_mode === "music" ? "music" : record_old_data.play_mode === "background" ? "background" : "mingSong", false);
  672. }
  673. };
  674. watch(pageVisibility, (value) => {
  675. if (value == "hidden" && evaluatingData.startBegin) {
  676. // handleEndBegin();
  677. }
  678. });
  679. watch(
  680. () => evaluatingData.socketErrorStatus,
  681. () => {
  682. if (evaluatingData.socketErrorStatus === 2) {
  683. setTimeout(() => {
  684. evaluatingData.socketErrorPop = false;
  685. // evaluatingData.socketErrorStatus = 0
  686. }, 1000);
  687. }
  688. }
  689. );
  690. watch(
  691. () => evaluatingData.socketErrorPop,
  692. () => {
  693. if (evaluatingData.socketErrorPop && state.setting.soundEffect) {
  694. // 监听到socket状态异常,需要关闭延迟检测
  695. api_closeDelayCheck({});
  696. }
  697. }
  698. );
  699. onMounted(() => {
  700. resetPlaybackToStart();
  701. hanlde_record();
  702. evaluatingData.resultData = {};
  703. // evaluatingData.resulstMode = true;
  704. // evaluatingData.resultData = {...getLeveByScore(10), score: 10, intonation: 10, cadence: 30, integrity: 40}
  705. // console.log("🚀 ~ evaluatingData.resultData:", evaluatingData.resultData)
  706. evaluatingData.evaluatings = {};
  707. evaluatingData.soundEffectFrequency = 0;
  708. evaluatingData.checkStep = 0;
  709. evaluatingData.rendered = true;
  710. sendResult(handleScoreResult);
  711. hideComplexButton(handleComplexButton, true);
  712. api_recordStartTime(recordStartTimePoint);
  713. addAccompanyError(handleAccompanyError);
  714. addSocketStatus(handleSocketStatus);
  715. // 不是选段模式评测, 就清空已选段
  716. if (!state.isSelectMeasureMode) {
  717. clearSelection();
  718. }
  719. console.log("加载评测模块成功");
  720. });
  721. onUnmounted(() => {
  722. evaluatingData.checkEnd = false;
  723. evaluatingData.rendered = false;
  724. resetPlaybackToStart();
  725. removeResult(handleScoreResult);
  726. hideComplexButton(() => {}, false);
  727. api_remove_recordStartTime(recordStartTimePoint);
  728. handle_reduction();
  729. removeAccompanyError(handleAccompanyError);
  730. removeSocketStatus(handleSocketStatus);
  731. api_disconnectSocket();
  732. console.log("卸载评测模块成功");
  733. });
  734. return () => (
  735. <div>
  736. {/** 预加载一下断网需要用到的图片 */}
  737. <div class={styles.hiddenPop}>
  738. <img src={popImgs.icon_bg} />
  739. <img src={popImgs.icon_btn} />
  740. <img src={popImgs.icon_success} />
  741. <img src={popImgs.icon_close} />
  742. </div>
  743. <Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale"]} transition="van-scale" v-model:show={evaluatingData.socketErrorPop}>
  744. <AbnormalPop onConfirm={hanldeConfirmPop} onClose={hanldeClosePop} />
  745. </Popup>
  746. </div>
  747. );
  748. },
  749. });