index.tsx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. import { Transition, defineComponent, onMounted, reactive, watch, defineAsyncComponent, computed, onUnmounted } from "vue";
  2. import { connectWebsocket, evaluatingData, handleEndBegin, handleStartBegin, handleStartEvaluat, handleViewReport, startCheckDelay, checkUseEarphone, handleCancelEvaluat, checkMinInterval, handleEndEvaluat } from "/src/view/evaluating";
  3. import Earphone from "./earphone";
  4. import styles from "./index.module.less";
  5. import SoundEffect from "./sound-effect";
  6. import state, { handleRessetState, resetPlaybackToStart, clearSelection, initSetPlayRate, resetBaseRate } from "/src/state";
  7. import { storeData } from "/src/store";
  8. import { browser } from "/src/utils";
  9. import { getNoteByMeasuresSlursStart } from "/src/helpers/formateMusic";
  10. import { Icon, Popup, showToast, closeToast, showLoadingToast } from "vant";
  11. import EvaluatResult from "./evaluat-result";
  12. import EvaluatAudio from "./evaluat-audio";
  13. import { api_getDeviceDelay, api_openAdjustRecording, api_proxyServiceMessage, api_videoUpdate, getEarphone, api_back, api_startDelayCheck, api_cancelDelayCheck, api_remove_cancelDelayCheck, api_closeDelayCheck, api_finishDelayCheck, api_retryEvaluating, api_remove_finishDelayCheck } from "/src/helpers/communication";
  14. import EvaluatShare from "./evaluat-share";
  15. import { Vue3Lottie } from "vue3-lottie";
  16. import startData from "./data/start.json";
  17. import startingData from "./data/starting.json";
  18. import iconTastBg from "./icons/task-bg.svg";
  19. import iconEvaluat from "./icons/evaluating.json";
  20. import { headImg } from "/src/page-instrument/header-top/image";
  21. import { api_musicPracticeRecordVideoUpload } from "../api";
  22. import { headTopData } from "../header-top/index";
  23. import { getQuery } from "/src/utils/queryString";
  24. import Countdown from "./countdown";
  25. import { IPostMessage } from "/src/utils/native-message";
  26. import tipErjiBg from "./icons/tip_erji.png"
  27. import tipErjiBtn from "./icons/tip_btn.png"
  28. // const DelayCheck = defineAsyncComponent(() =>
  29. // import('./delay-check')
  30. // )
  31. // frequency 频率, amplitude 振幅, decibels 分贝
  32. type TCriteria = "frequency" | "amplitude" | "decibels";
  33. /**
  34. * 节拍器时长
  35. * 评测模式时,应该传节拍器时长
  36. * 阶段评测时,判断是否从第一小节开始,并且曲子本身含有节拍器,需要传节拍器时长,否则传0
  37. */
  38. let actualBeatLength = 0;
  39. let calculateInfo: any = {};
  40. let checkErjiTimer: any = null
  41. export const reCheckDelay = () => {
  42. evaluatingData.onceErjiPopShow = false;
  43. evaluatingData.needCheckErjiStatus = true;
  44. headTopData.settingMode = false
  45. state.setting.soundEffect = false
  46. api_startDelayCheck({});
  47. }
  48. export default defineComponent({
  49. name: "evaluat-model",
  50. setup() {
  51. const query = getQuery();
  52. const evaluatModel = reactive({
  53. tips: true,
  54. evaluatUpdateAudio: false,
  55. isSaveVideo: state.setting.camera && state.setting.saveToAlbum,
  56. shareMode: false,
  57. isSkipDoing: false, // 跳转执行中
  58. });
  59. /**
  60. * 检测返回
  61. */
  62. const handleDelayBack = () => {
  63. if (query.workRecord) {
  64. evaluatingData.soundEffectMode = false;
  65. api_back();
  66. } else {
  67. evaluatingData.soundEffectMode = false;
  68. // handleRessetState();
  69. // headTopData.modeType = "init";
  70. }
  71. };
  72. /**
  73. * 执行检测
  74. */
  75. const handlePerformDetection = async () => {
  76. console.log(evaluatingData.checkStep, evaluatingData, "检测123");
  77. // 检测完成不检测了
  78. if (evaluatingData.checkEnd) return;
  79. // 延迟检测
  80. if (evaluatingData.checkStep === 0) {
  81. evaluatingData.checkStep = 10;
  82. // 没有设备延迟数据 或 开启了效音 显示检测组件,并持续检测耳机状态
  83. if (state.setting.soundEffect) {
  84. evaluatingData.soundEffectMode = true;
  85. return;
  86. }
  87. // 判断只有开始了设备检测之后才去调用api
  88. if (state.setting.soundEffect) {
  89. const delayData = await api_getDeviceDelay();
  90. // console.log("🚀 ~ delayTime:", delayData);
  91. if (delayData && delayData.content?.value < 0) {
  92. evaluatingData.soundEffectMode = true;
  93. return;
  94. }
  95. }
  96. handlePerformDetection();
  97. return;
  98. }
  99. // 效验完成
  100. if (evaluatingData.checkStep === 10) {
  101. const erji = await checkUseEarphone();
  102. if (!erji) {
  103. evaluatingData.earphoneMode = true;
  104. }
  105. evaluatingData.checkEnd = true;
  106. console.log("检测结束,生成数据");
  107. handleConnect();
  108. }
  109. };
  110. const browserInfo = browser();
  111. /** 是否是节奏练习 */
  112. const isRhythmicExercises = () => {
  113. const examSongName = state.examSongName || "";
  114. return examSongName.indexOf("节奏练习") > -1;
  115. };
  116. /** 获取评测标准 */
  117. const getEvaluationCriteria = () => {
  118. let criteria: TCriteria = "frequency";
  119. // 声部打击乐
  120. if ([23, 113, 121].includes(state.subjectId)) {
  121. criteria = "amplitude";
  122. } else if (isRhythmicExercises()) {
  123. // 分类为节奏练习
  124. criteria = "decibels";
  125. }
  126. return criteria;
  127. };
  128. /** 校验耳机状态 */
  129. const checkEarphoneStatus = async (type?: string) => {
  130. clearTimeout(checkErjiTimer);
  131. checkErjiTimer = null;
  132. if (type !== "start") {
  133. // const erji = await checkUseEarphone();
  134. const res = await getEarphone();
  135. const erji = res?.content?.checkIsWired || false;
  136. // 是否已经提示过耳机弹窗,重新进入评测页面,重置该状态为false,手动关掉耳机弹窗,改变该状态为true,本次评测都不在提示耳机状态弹窗
  137. if (!evaluatingData.onceErjiPopShow) {
  138. evaluatingData.earphoneMode = true;
  139. } else {
  140. clearTimeout(checkErjiTimer);
  141. checkErjiTimer = null;
  142. return;
  143. }
  144. evaluatingData.earPhoneType = res?.content?.type || "";
  145. if (evaluatingData.earPhoneType === "有线耳机") {
  146. clearTimeout(checkErjiTimer);
  147. checkErjiTimer = null;
  148. setTimeout(() => {
  149. evaluatingData.earphoneMode = false;
  150. }, 1500);
  151. } else {
  152. // 如果没有佩戴有限耳机,需要持续检测耳机状态
  153. checkErjiTimer = setTimeout(() => {
  154. checkEarphoneStatus();
  155. }, 1000);
  156. }
  157. }
  158. console.log("检测结束,生成数据", evaluatingData.websocketState, evaluatingData.startBegin, evaluatingData.checkEnd);
  159. handleConnect();
  160. };
  161. /** 生成评测曲谱数据 */
  162. const formatTimes = () => {
  163. console.log('评测111')
  164. let starTime = 0;
  165. let ListenMode = false;
  166. let dontEvaluatingMode = false;
  167. let skip = false;
  168. const datas = [];
  169. let selectTimes = state.times;
  170. let unitTestIdx = 0;
  171. let preTime = 0;
  172. let preTimes = [];
  173. // 系统节拍器时长
  174. actualBeatLength = Math.round((state.times[0].fixtime * 1000) / 1);
  175. // 如果是阶段评测,选取该阶段的times
  176. if (state.isSelectMeasureMode && state.section.length) {
  177. const startIndex = state.section[0].noteId ? state.times.findIndex((n: any) => n.noteId == state.section[0].noteId) : state.times.findIndex((n: any) => n.MeasureNumberXML == state.section[0].MeasureNumberXML);
  178. let endIndex = state.section[1].noteId ? state.times.findIndex((n: any) => n.noteId == state.section[1].noteId) : state.times.findIndex((n: any) => n.MeasureNumberXML == state.section[1].MeasureNumberXML);
  179. endIndex = endIndex < state.section[1].i ? state.section[1].i : endIndex;
  180. if (startIndex > 1) {
  181. // firstNoteTime应该取预备小节的第一个音符的开始播放的时间
  182. const idx = startIndex - 1 - state.times[startIndex - 1].si;
  183. preTime = state.times[idx] ? state.times[idx].time * 1000 : 0;
  184. }
  185. actualBeatLength = startIndex == 0 && state.isOpenMetronome ? actualBeatLength : 0;
  186. selectTimes = state.times.filter((n: any, index: number) => {
  187. return index >= startIndex && index <= endIndex;
  188. });
  189. preTimes = state.times.filter((n: any, index: number) => {
  190. return index < startIndex;
  191. });
  192. unitTestIdx = startIndex;
  193. starTime = selectTimes[0].sourceRelativeTime || selectTimes[0].relativeTime;
  194. }
  195. // 阶段评测beatLength需要加上预备小节的持续时长
  196. actualBeatLength = preTimes.length ? actualBeatLength + preTimes[preTimes.length - 1].relaMeasureLength * 1000 : actualBeatLength;
  197. // 如果是弱起,并且预备小节是第一节
  198. if (state.section.length && state.sectionFirst && state.sectionFirst.measureListIndex == 0) {
  199. // actualBeatLength = actualBeatLength < Math.round((state.times[0].fixtime * 1000) / 1) ? Math.round((state.times[0].fixtime * 1000) / 1) : actualBeatLength;
  200. }
  201. let firstNoteTime = unitTestIdx > 1 ? preTime : 0;
  202. let measureIndex = -1;
  203. let recordMeasure = -1;
  204. // 如果有mp3节拍器,并且预备小节是第一节,并且从0开始播放,actualBeatLength需要加上mp3节拍器时间
  205. if (state.section.length === 2 && firstNoteTime === 0 && state.section[0]?.MeasureNumberXML === state.firstMeasureNumber + 1 && state.times[0].fixtime) {
  206. actualBeatLength = actualBeatLength + Math.round((state.times[0].fixtime * 1000) / 1)
  207. }
  208. for (let index = 0; index < selectTimes.length; index++) {
  209. const item = selectTimes[index];
  210. const note = getNoteByMeasuresSlursStart(item);
  211. // #8701 bug: 评测模式,是以曲谱本身的速度进行评测,所以rate取1,不需要转换
  212. // const rate = state.speed / state.originSpeed;
  213. const rate = state.basePlayRate * state.originAudioPlayRate; // 播放倍率
  214. // const difftime = item.difftime;
  215. const difftime = 0;
  216. const start = difftime + (item.sourceRelativeTime || item.relativeTime) - starTime;
  217. const end = difftime + (item.sourceRelaEndtime || item.relaEndtime) - starTime;
  218. const isStaccato = note.noteElement.voiceEntry.isStaccato();
  219. const noteRate = isStaccato ? 0.5 : 1;
  220. if (note.formatLyricsEntries.contains("Play") || note.formatLyricsEntries.contains("Play...")) {
  221. ListenMode = false;
  222. }
  223. if (note.formatLyricsEntries.contains("Listen")) {
  224. ListenMode = true;
  225. }
  226. if (note.formatLyricsEntries.contains("纯律结束")) {
  227. dontEvaluatingMode = false;
  228. }
  229. if (note.formatLyricsEntries.contains("纯律")) {
  230. dontEvaluatingMode = true;
  231. }
  232. const nextNote = selectTimes[index + 1];
  233. // console.log("noteinfo", note.noteElement.isRestFlag && !!note.stave && !!nextNote)
  234. if (skip && (note.stave || !item.noteElement.isRestFlag || (nextNote && !nextNote.noteElement.isRestFlag))) {
  235. skip = false;
  236. }
  237. if (note.noteElement.isRestFlag && !!note.stave && !!nextNote && nextNote.noteElement.isRestFlag) {
  238. skip = true;
  239. }
  240. // console.log(note.measureOpenIndex, item.measureOpenIndex, note);
  241. // console.log("skip", skip)
  242. // console.log(end,start,rate,noteRate, '评测')
  243. if (note.measureOpenIndex != recordMeasure) {
  244. measureIndex++;
  245. recordMeasure = note.measureOpenIndex;
  246. }
  247. // 是否是需要延续、不停顿演奏的音符
  248. let isTenutoSound = false;
  249. if (item?.noteElement?.tie && item.noteElement.tie?.StartNote) {
  250. const startId = item.noteElement.tie?.StartNote?.NoteToGraphicalNoteObjectId
  251. isTenutoSound = item.NoteToGraphicalNoteObjectId === startId ? false : true
  252. }
  253. // 音符是否不需要评测
  254. let noteNeedEvaluat = item.hasGraceNote || ListenMode || dontEvaluatingMode || !!item?.voiceEntry?.ornamentContainer || !!item.noteElement?.speedInfo?.startWord?.includes('rit.') || item.skipMode
  255. noteNeedEvaluat = noteNeedEvaluat == true ? true : false;
  256. const data = {
  257. timeStamp: (start * 1000) / rate,
  258. duration: ((end * 1000) / rate - (start * 1000) / rate) * noteRate,
  259. frequency: item.frequency,
  260. nextFrequency: item.nextFrequency,
  261. prevFrequency: item.prevFrequency,
  262. // 重复的情况index会自然累加,render的index是谱面渲染的index
  263. measureIndex: measureIndex,
  264. measureRenderIndex: item.measureListIndex,
  265. // item.MeasureNumberXML >= 1 ? item.MeasureNumberXML - 1 : note.noteElement.sourceMeasure.measureListIndex,
  266. dontEvaluating: noteNeedEvaluat,
  267. musicalNotesIndex: index,
  268. denominator: note.noteElement?.Length.denominator,
  269. // isOrnament: !!note?.voiceEntry?.ornamentContainer,
  270. isTenutoSound,
  271. isStaccato: item?.voiceEntry?.isStaccato ? true : false, // 是否是重音
  272. frequencyList: item.frequencyList, // 如果是和弦音符,需要添加多个音符的频率,用于评测
  273. };
  274. datas.push(data);
  275. }
  276. return {
  277. datas,
  278. firstNoteTime,
  279. };
  280. };
  281. /** 连接websocket */
  282. const handleConnect = async () => {
  283. const behaviorId = localStorage.getItem("behaviorId") || localStorage.getItem("BEHAVIORID") || undefined;
  284. // let rate = state.speed / state.originSpeed;
  285. const rate = state.basePlayRate * state.originAudioPlayRate; // 播放倍率
  286. // rate = parseFloat(rate.toFixed(2));
  287. // console.log("速度比例", rate, "速度", state.speed);
  288. calculateInfo = formatTimes();
  289. // 评测的速度,如果是选段,则选选段开头小节的速度
  290. const evaluatSpeed = state.sectionStatus && state.section.length === 2 && state.section[0].measureSpeed ? state.section[0].measureSpeed * state.basePlayRate : state.speed;
  291. evaluatingData.evaluatSpeed = evaluatSpeed;
  292. const content = {
  293. musicXmlInfos: calculateInfo.datas,
  294. subjectId: state.musicalCode,
  295. detailId: state.detailId,
  296. examSongId: state.examSongId,
  297. xmlUrl: state.xmlUrl,
  298. partIndex: state.partIndex,
  299. behaviorId,
  300. platform: browserInfo.ios ? "IOS" : browserInfo.android ? "ANDROID" : "WEB",
  301. clientId: storeData.platformType === "STUDENT" ? "student" : storeData.platformType === "TEACHER" ? "teacher" : "education",
  302. hertz: state.setting.frequency,
  303. reactionTimeMs: state.setting.reactionTimeMs ? Number(state.setting.reactionTimeMs) : 0,
  304. speed: evaluatSpeed,
  305. heardLevel: state.setting.evaluationDifficulty,
  306. // beatLength: Math.round((state.fixtime * 1000) / rate),
  307. beatLength: actualBeatLength / rate,
  308. evaluationCriteria: state.evaluationStandard,
  309. speedRate: parseFloat(rate.toFixed(2)), // 播放倍率
  310. };
  311. await connectWebsocket(content);
  312. // state.playSource = "music";
  313. };
  314. /** 评测结果按钮处理 */
  315. const handleEvaluatResult = (type: "practise" | "tryagain" | "look" | "share" | "update" | "selfCancel") => {
  316. if (evaluatModel.isSkipDoing) {
  317. return;
  318. }
  319. evaluatModel.isSkipDoing = true;
  320. setTimeout(() => {
  321. evaluatModel.isSkipDoing = false;
  322. }, 1000);
  323. if (type === "update") {
  324. if (state.isAppPlay) {
  325. evaluatModel.evaluatUpdateAudio = true;
  326. resetPlaybackToStart();
  327. return;
  328. } else if (evaluatingData.resultData?.recordIdStr || evaluatingData.resultData?.recordId) {
  329. const rate = state.basePlayRate * state.originAudioPlayRate; // 播放倍率
  330. // 上传云端
  331. // evaluatModel.evaluatUpdateAudio = true;
  332. api_openAdjustRecording({
  333. recordId: evaluatingData.resultData?.recordIdStr || evaluatingData.resultData?.recordId,
  334. title: state.examSongName || "曲谱演奏",
  335. coverImg: state.coverImg,
  336. speedRate: parseFloat(rate.toFixed(2)), // 播放倍率
  337. musicRenderType: state.musicRenderType,
  338. musicSheetId: state.examSongId,
  339. 'part-index': state.partIndex
  340. });
  341. return;
  342. }
  343. } else if (type === "share") {
  344. // 分享
  345. evaluatModel.shareMode = true;
  346. return;
  347. } else if (type === "look") {
  348. // 跳转
  349. handleViewReport("recordId", "instrument");
  350. return;
  351. } else if (type === "practise") {
  352. // 去练习
  353. handleStartEvaluat();
  354. } else if (type === "tryagain") {
  355. startBtnHandle();
  356. } else if (type === "selfCancel") {
  357. // 再来一次,需要手动取消评测,不生成评测记录,不显示评测结果弹窗
  358. evaluatingData.oneselfCancleEvaluating = true;
  359. // handleCancelEvaluat();
  360. handleEndEvaluat(false, 'selfCancel');
  361. // evaluatingData.isBeginMask = true;
  362. evaluatingData.evaluatings = {};
  363. state.playState = "paused";
  364. }
  365. resetPlaybackToStart();
  366. evaluatingData.resulstMode = false;
  367. };
  368. /** 上传音视频 */
  369. const hanldeUpdateVideoAndAudio = async (update = false) => {
  370. if (!update) {
  371. evaluatModel.evaluatUpdateAudio = false;
  372. return;
  373. }
  374. if (state.setting.camera && state.setting.saveToAlbum) {
  375. evaluatModel.evaluatUpdateAudio = false;
  376. api_videoUpdate((res: any) => {
  377. if (res) {
  378. if (res?.content?.type === "success") {
  379. handleSaveResult({
  380. id: evaluatingData.resultData?.recordId,
  381. videoFilePath: res?.content?.filePath,
  382. });
  383. } else if (res?.content?.type === "error") {
  384. showToast({
  385. message: res.content?.message || "上传失败",
  386. });
  387. }
  388. }
  389. });
  390. return;
  391. }
  392. evaluatModel.evaluatUpdateAudio = false;
  393. showToast("上传成功");
  394. };
  395. const handleSaveResult = async (_body: any) => {
  396. await api_musicPracticeRecordVideoUpload(_body);
  397. showToast("上传成功");
  398. };
  399. const startBtnHandle = async () => {
  400. // 如果打开了延迟检测开关,需要先发送开始检测的消息
  401. const delayData = await api_getDeviceDelay();
  402. console.log('设备的延迟值',delayData.content?.value)
  403. if (delayData && delayData.content?.value <= 0) {
  404. await api_startDelayCheck({});
  405. return;
  406. }
  407. evaluatingData.needReplayEvaluat = false;
  408. // 选段未完成时,清除选段状态
  409. if (state.sectionStatus && state.section.length < 2) {
  410. clearSelection();
  411. }
  412. // 如果是异常状态,先等待500ms再执行后续流程
  413. if (evaluatingData.isErrorState && !state.setting.soundEffect) {
  414. // console.log('异常流程1')
  415. // showLoadingToast({
  416. // message: "处理中",
  417. // duration: 1000,
  418. // overlay: true,
  419. // overlayClass: styles.scoreMode,
  420. // });
  421. state.loadingText = "处理中…";
  422. state.isLoading = true;
  423. await new Promise<void>((resolve) => {
  424. setTimeout(() => {
  425. // closeToast();
  426. state.isLoading = false;
  427. evaluatingData.isErrorState = false;
  428. // console.log('异常流程2')
  429. resolve();
  430. }, 1000);
  431. });
  432. }
  433. // console.log('异常流程3')
  434. // 非选段状态,从头开始评测,重置速度
  435. if (!state.sectionStatus && state.section.length === 0) {
  436. state.activeNoteIndex = 0;
  437. state.activeMeasureIndex = state.firstMeasureNumber === 0 ? 0 : 1;
  438. state.speed = state.times[0].measureSpeed * state.basePlayRate
  439. }
  440. initSetPlayRate();
  441. // 检测APP端socket状态
  442. const res: any = await startCheckDelay();
  443. if (res?.checked) {
  444. handleConnect();
  445. handleStartBegin(calculateInfo.firstNoteTime);
  446. if (evaluatingData.isErrorState) {
  447. evaluatingData.isErrorState = false;
  448. evaluatingData.resulstMode = false;
  449. }
  450. }
  451. };
  452. // 监听到APP取消延迟检测
  453. const handleCancelDelayCheck = async (res?: IPostMessage) => {
  454. console.log("监听取消延迟检测", res);
  455. if (res?.content) {
  456. // 关闭延迟检测页面,并返回到模式选择页面
  457. // await api_closeDelayCheck({});
  458. handleDelayBack();
  459. }
  460. };
  461. // 监听APP延迟成功的回调
  462. const handleFinishDelayCheck = async (res?: IPostMessage) => {
  463. console.log("监听延迟检测成功", res);
  464. evaluatingData.socketErrorPop = false;
  465. if (res?.content) {
  466. evaluatingData.checkEnd = true;
  467. state.setting.soundEffect = false;
  468. evaluatingData.tipErjiShow = true;
  469. }
  470. };
  471. // 监听重复评测消息
  472. const handRetryEvaluating = () => {
  473. handleEvaluatResult("tryagain");
  474. };
  475. const earPhonePopShow = computed(() => {
  476. return evaluatingData.earphoneMode && !state.isLoading && !state.hasDriverPop;
  477. });
  478. const tipErjiPopShow = computed(() => {
  479. return evaluatingData.tipErjiShow && !state.isLoading && !state.hasDriverPop;
  480. });
  481. // watch(
  482. // () => state.setting.soundEffect,
  483. // (val) => {
  484. // if (val) {
  485. // headTopData.settingMode = false
  486. // api_startDelayCheck({});
  487. // state.setting.soundEffect = false
  488. // }
  489. // }
  490. // );
  491. // 手动取消评测,需要自动再次评测
  492. // watch(
  493. // () => evaluatingData.needReplayEvaluat,
  494. // (val) => {
  495. // if (val && evaluatingData.oneselfCancleEvaluating) {
  496. // setTimeout(() => {
  497. // startBtnHandle();
  498. // }, 500);
  499. // }
  500. // }
  501. // );
  502. onMounted(async () => {
  503. // 如果打开了延迟检测开关,需要先发送开始检测的消息
  504. const delayData = await api_getDeviceDelay();
  505. console.log('设备的延迟值',delayData.content?.value)
  506. if (delayData && delayData.content?.value <= 0 && state.paymentType !== "LOCK") {
  507. await api_startDelayCheck({});
  508. } else {
  509. evaluatingData.checkEnd = true;
  510. // 点击评测模式进入评测模块的需要检测耳机状态,通过返回按钮进入评测模块的,不检测耳机状态
  511. if (evaluatingData.needCheckErjiStatus) {
  512. checkEarphoneStatus();
  513. }
  514. }
  515. evaluatingData.isDisabledPlayMusic = true;
  516. // handlePerformDetection();
  517. api_cancelDelayCheck(handleCancelDelayCheck);
  518. api_finishDelayCheck(handleFinishDelayCheck);
  519. api_retryEvaluating(handRetryEvaluating);
  520. });
  521. onUnmounted(() => {
  522. api_remove_finishDelayCheck(handleFinishDelayCheck);
  523. api_remove_cancelDelayCheck(handleCancelDelayCheck);
  524. clearTimeout(checkErjiTimer);
  525. checkErjiTimer = null;
  526. });
  527. // 资源类型
  528. const isPad = navigator?.userAgent?.includes("UAWEIVRD-W09") || browserInfo?.iPad || browserInfo.isTablet;
  529. return () => (
  530. <div>
  531. <div class={styles.operatingBtn}>
  532. {!evaluatingData.startBegin && (
  533. <img
  534. class={[styles.iconBtn, "evaluting-1"]}
  535. src={headImg("icon_play.png")}
  536. onClick={() => {
  537. startBtnHandle();
  538. }}
  539. />
  540. )}
  541. {evaluatingData.startBegin && (
  542. <>
  543. <img class={styles.iconBtn} src={headImg("icon_reset.png")} onClick={() => {
  544. // 校验评测最小间隔时间
  545. const currentTime = +new Date();
  546. // 开始评测和结束评测的间隔时间小于800毫秒,则不处理
  547. if (currentTime - evaluatingData.recordingTime < 800) {
  548. return;
  549. }
  550. handleEvaluatResult("selfCancel")
  551. }} />
  552. <img class={styles.iconBtn} src={headImg("submit.png")} onClick={() => {
  553. // 校验评测最小间隔时间
  554. const currentTime = +new Date();
  555. // 开始评测和结束评测的间隔时间小于800毫秒,则不处理
  556. if (currentTime - evaluatingData.recordingTime < 800) {
  557. return;
  558. }
  559. handleEndBegin()
  560. }} />
  561. </>
  562. )}
  563. </div>
  564. {/* {evaluatingData.soundEffectMode && (
  565. <DelayCheck
  566. onClose={() => {
  567. evaluatingData.soundEffectMode = false;
  568. handlePerformDetection();
  569. }}
  570. onBack={() => handleDelayBack()}
  571. />
  572. )} */}
  573. {/* 倒计时 */}
  574. <Countdown />
  575. {/* 遮罩 */}
  576. {
  577. evaluatingData.isBeginMask && <div class={styles.beginMask}></div>
  578. }
  579. <Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale"]} transition="van-scale" v-model:show={tipErjiPopShow.value}>
  580. <div class={[styles.earphoneBox, isPad && styles.ipadEarphoneBox]}>
  581. <img class={styles.earphoneBg} src={tipErjiBg} />
  582. <img class={styles.earphoneBtn} src={tipErjiBtn} onClick={() => {
  583. evaluatingData.tipErjiShow = false;
  584. checkEarphoneStatus();
  585. }} />
  586. </div>
  587. </Popup>
  588. <Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale"]} transition="van-scale" v-model:show={earPhonePopShow.value}>
  589. <Earphone
  590. earphoneType={evaluatingData.earPhoneType}
  591. onClose={() => {
  592. evaluatingData.onceErjiPopShow = true;
  593. clearTimeout(checkErjiTimer);
  594. checkErjiTimer = null;
  595. // #11035,可能刚好关闭耳机弹窗的时候,第二次又出现了弹窗
  596. setTimeout(() => {
  597. evaluatingData.earphoneMode = false;
  598. }, 300);
  599. // handlePerformDetection();
  600. checkEarphoneStatus("start");
  601. }}
  602. />
  603. </Popup>
  604. {/* 评测作业,非完整评测不显示评测结果弹窗 */}
  605. {
  606. evaluatingData.resulstMode &&
  607. <>
  608. {evaluatingData.hideResultModal ? (
  609. <EvaluatResult onClose={handleEvaluatResult} />
  610. ) : (
  611. <Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale"]} transition="van-scale" v-model:show={evaluatingData.resulstMode}>
  612. <EvaluatResult onClose={handleEvaluatResult} />
  613. </Popup>
  614. )}
  615. </>
  616. }
  617. <Popup teleport="body" closeOnClickOverlay={false} class={["popup-custom", "van-scale"]} transition="van-scale" v-model:show={evaluatModel.evaluatUpdateAudio}>
  618. <EvaluatAudio onClose={hanldeUpdateVideoAndAudio} />
  619. </Popup>
  620. <Popup teleport="body" class={["popup-custom", "van-scale"]} transition="van-scale" v-model:show={evaluatModel.shareMode}>
  621. <EvaluatShare onClose={() => (evaluatModel.shareMode = false)} />
  622. </Popup>
  623. </div>
  624. );
  625. },
  626. });