liushengqiang 1 year ago
parent
commit
5abca87a11

+ 1 - 1
image2code.js

@@ -4,7 +4,7 @@ const path = require('path')
 // 指法文件夹位置
 // const filesDir = path.join(__dirname, './fingering')
 // const filesDir = path.join(__dirname, './pages/detail')
-const filesDir = path.join(__dirname, './src/page-gym/evaluat-model/icons')
+const filesDir = path.join(__dirname, './iconss')
 console.log("🚀 ~ filesDir:", filesDir, path.join(filesDir, 'index.json'))
 
 // 需要处理的文件后缀

+ 5 - 1
src/helpers/communication.ts

@@ -1,5 +1,5 @@
 import { browser } from "../utils";
-import { CallBack, IPostMessage, listenerMessage, postMessage, promisefiyPostMessage } from "../utils/native-message";
+import { CallBack, IPostMessage, listenerMessage, postMessage, promisefiyPostMessage, removeListenerMessage } from "../utils/native-message";
 
 let isApp = (): boolean => {
 	const browserInfo = browser();
@@ -29,6 +29,10 @@ export const startSoundCheck = () => {
 export const sendResult = (callback: CallBack) => {
 	listenerMessage("sendResult", callback);
 };
+/** 取消监听录音返回 */
+export const removeResult = (callback: CallBack) => {
+   removeListenerMessage("sendResult", callback)
+}
 
 /** 结束录音 */
 export const endSoundCheck = () => {

+ 13 - 7
src/state.ts

@@ -111,6 +111,7 @@ const state = reactive({
 	},
 	/** 节拍器的时间 */
 	fixtime: 0,
+	
 
 	repeatedBeats: 0,
 
@@ -124,7 +125,7 @@ const state = reactive({
 	notchHeight: 0,
 	fixedKey: 0,
 	renderLoading: false,
-	evaluatings: {} as any,
+	
 	isPauseRecording: false,
 	feeShow: false,
 	vipShow: false,
@@ -188,7 +189,7 @@ export const onTimeupdate = (evt: Event) => {};
 export const onEnded = () => {
 	handleStopPlay();
 	if (state.modeType === 'evaluating'){
-		handleEndBegin()
+		handleEndBegin(true)
 	}
 };
 
@@ -225,11 +226,16 @@ const handlePlaying = (_item?: any) => {
 	}
 };
 /** 跳转到指定音符开始播放 */
-export const skipNotePlay = (itemIndex: number) => {
+export const skipNotePlay = (itemIndex: number, isStart = false) => {
 	const item = state.times[itemIndex];
+	let itemTime = item.time
+	if (isStart) {
+		itemTime = 0
+	}
+	console.log("🚀 ~ itemTime:", itemTime)
 	if (item) {
-		state.songEl && (state.songEl.currentTime = item.time);
-		state.backgroundEl && (state.backgroundEl.currentTime = item.time);
+		state.songEl && (state.songEl.currentTime = itemTime);
+		state.backgroundEl && (state.backgroundEl.currentTime = itemTime);
 		handlePlaying(item);
 	}
 };
@@ -257,7 +263,7 @@ export const handleStopPlay = () => {
 	state.playState = "paused";
 	state.songEl?.pause();
 	state.backgroundEl?.pause();
-	skipNotePlay(0);
+	skipNotePlay(0, true);
 	// 重复自动播放如果为开启,自动开始播放, 且是练习模式
 	if (state.setting.repeatAutoPlay && state.modeType === "practise") {
 		scrollViewNote();
@@ -326,7 +332,7 @@ export const getNote = (currentTime: number) => {
 
 /** 重播 */
 export const handleResetPlay = () => {
-	skipNotePlay(0);
+	skipNotePlay(0, true);
 };
 /** 设置速度 */
 export const handleSetSpeed = (speed: number) => {

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

@@ -0,0 +1,102 @@
+import Image1 from "./icons/5.png";
+import Image2 from "./icons/4.png";
+import Image3 from "./icons/3.png";
+import Image4 from "./icons/2.png";
+import Image5 from "./icons/1.png";
+
+import scoreIcon from "./scoreIcon.json";
+
+export interface IScoreItem {
+  color: string
+  icon: string
+  score: string | number
+  leve: number | number
+  measureIndex?: number
+  measureRenderIndex?: number
+}
+export interface IEvaluatings {
+  [_key: number] : IScoreItem
+}
+
+export const leveByScoreMeasureIcons = [
+	{
+		icon: scoreIcon.bad,
+		text: "bad",
+    color: '#EE4C6A'
+	},
+	{
+		icon: scoreIcon.good,
+		text: "good",
+    color: '#FF958B'
+	},
+	{
+		icon: scoreIcon.great,
+		text: "great",
+    color: '#FF8E5A'
+	},
+	{
+		icon: scoreIcon.perfect,
+		text: "perfect",
+    color: '#516AFF'
+	},
+];
+
+const icons = [
+	{
+		img: Image1,
+		tips: "你的演奏不太好,再练一练吧~",
+		mome: "敢于尝试",
+	},
+	{
+		img: Image2,
+		tips: "你的演奏还不熟练,加紧训练才能有好成绩哦~",
+		mome: "还要加油哦~",
+	},
+	{
+		img: Image3,
+		tips: "你的演奏还不流畅,科学的练习才能更完美哦~",
+		mome: "突破自我",
+	},
+	{
+		img: Image4,
+		tips: "你的演奏还不错,继续加油吧,离完美就差一步啦~",
+		mome: "崭露头角",
+	},
+	{
+		img: Image5,
+		tips: "你的演奏完美无缺,继续努力吧~",
+		mome: "你很棒",
+	},
+];
+
+export const getLeveByScore = (score?: string | number) => {
+	if (!score && typeof score !== "number") {
+		return {};
+	}
+	let leve: any = 1;
+	if (score > 20 && score <= 40) {
+		leve = 2;
+	} else if (score > 40 && score <= 60) {
+		leve = 3;
+	} else if (score > 60 && score <= 80) {
+		leve = 4;
+	} else if (score > 80) {
+		leve = 5;
+	}
+	return icons[leve];
+};
+
+export const getLeveByScoreMeasure = (score?: number) => {
+	if (!score && typeof score !== "number") {
+		return {};
+	}
+	let leve: any = 0;
+	if (score >= 40 && score < 70) {
+		leve = 1;
+	} else if (score >= 70 && score < 90) {
+		leve = 2;
+	} else if (score >= 90) {
+		leve = 3;
+	}
+	return leve
+};

BIN
src/view/evaluating/icons/1.png


BIN
src/view/evaluating/icons/2.png


BIN
src/view/evaluating/icons/3.png


BIN
src/view/evaluating/icons/4.png


BIN
src/view/evaluating/icons/5.png


+ 56 - 31
src/view/evaluating/index.tsx

@@ -1,10 +1,12 @@
-import { showToast } from "vant";
+import { closeToast, showLoadingToast, showToast, Toast } from "vant";
 import { defineComponent, onBeforeUnmount, onMounted, reactive, ref } from "vue";
+import { getLeveByScoreMeasure, IEvaluatings } from "./evaluatResult";
 import {
 	endEvaluating,
 	endSoundCheck,
 	getEarphone,
 	proxyServiceMessage,
+	removeResult,
 	sendResult,
 	startEvaluating,
 	startRecording,
@@ -12,6 +14,7 @@ import {
 } 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({
 	earphone: false, // 是否插入耳机
@@ -24,17 +27,16 @@ export const evaluatingData = reactive({
 	websocketState: false, // websocket连接状态
 	startBegin: false, // 开始
 	backtime: 0, // 延迟时间
-	measureIndex: -1,
-	measureRenderIndex: -1,
-	score: 0, // 评测分数
+	/** 已经评测的数据 */
+	evaluatings: {} as IEvaluatings,
 });
 
 /** 开始播放发送延迟时间 */
 export const sendEvaluatingOffsetTime = (currentTime: number) => {
 	const nowTime = Date.now();
-	console.log("第一次播放时间", nowTime);
-	console.log("已播放时长: ", currentTime * 1000);
-	console.log("不减掉已播放时间: ", nowTime - evaluatingData.backtime);
+	// console.log("第一次播放时间", nowTime);
+	// console.log("已播放时长: ", currentTime,  currentTime * 1000);
+	// console.log("不减掉已播放时间: ", nowTime - evaluatingData.backtime);
 	const delayTime = nowTime - evaluatingData.backtime - currentTime * 1000;
 	console.log("真正播放延迟", delayTime);
 	// 蓝牙耳机延迟一点发送消息确保在录音后面
@@ -45,7 +47,7 @@ export const sendEvaluatingOffsetTime = (currentTime: number) => {
 				type: "SOUND_COMPARE",
 			},
 			body: {
-				offsetTime: delayTime,
+				offsetTime: delayTime < 0 ? 0 : delayTime,
 			},
 		});
 		evaluatingData.backtime = 0;
@@ -63,24 +65,26 @@ const checkSoundEffect = () => {
 	return state.setting.soundEffect;
 };
 
+const handleSoundEffect = (res?: IPostMessage) => {
+    if (res?.content) {
+        const { header, body } = res.content;
+        if (header.commond === "checking") {
+            evaluatingData.soundEffectFrequency = body.frequency;
+        }
+    }
+}
+
 /**
  * 开始录音
  */
-const useSoundEffect = () => {
-	handleEndSoundCheck();
-	sendResult((res) => {
-		if (res?.content) {
-			const { header, body } = res.content;
-			if (header.commond === "checking") {
-				evaluatingData.soundEffectFrequency = body.frequency;
-			}
-		}
-	});
-	startSoundCheck();
-};
+const handleStartSoundCheck = () => {
+    sendResult(handleSoundEffect)
+    startSoundCheck();
+}
 /** 结束录音 */
 export const handleEndSoundCheck = () => {
 	endSoundCheck();
+    removeResult(handleSoundEffect);
 };
 
 /** 连接websocket */
@@ -114,7 +118,7 @@ export const handlePerformDetection = async () => {
 		// 是否需要开启效音
 		if (checkSoundEffect()) {
 			evaluatingData.soundEffectMode = true;
-			useSoundEffect();
+			handleStartSoundCheck();
 		}
 		evaluatingData.checkStep = 10;
 		return;
@@ -125,19 +129,34 @@ export const handlePerformDetection = async () => {
 	}
 };
 
+/** 记录小节分数 */
+const addMeasureScore = (measureScore: any) => {
+    evaluatingData.evaluatings[measureScore.measureRenderIndex] = {
+        ...measureScore,
+        leve: getLeveByScoreMeasure(measureScore.score)
+    }
+    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()
+        }
+    }
+}
+
 /** 开始评测 */
 export const handleStartBegin = async () => {
 	evaluatingData.startBegin = true;
-	sendResult((res) => {
-		if (res?.content) {
-            // console.log("🚀 ~ 评测返回:", res);
-			const { header } = res.content;
-			if (["measureScore", "overall"].includes(header.commond)) {
-                // console.log('评测返回1', new Date().toLocaleString())
-                _event.emit(EventEnum.sendResultScore, res.content)
-			}
-		}
-	});
+	sendResult(handleScoreResult);
 	await startRecording();
 	evaluatingData.backtime = Date.now();
 	togglePlay("play");
@@ -152,6 +171,12 @@ export const handleEndBegin = (isEnd = false) => {
 	endEvaluating({
 		musicScoreId: state.examSongId,
 	});
+    showLoadingToast({
+        message: '评分中',
+        duration: 0,
+        forbidClick: true,
+    })
+    
     if (isEnd) return
 	handleStopPlay();
 };

File diff suppressed because it is too large
+ 1 - 0
src/view/evaluating/scoreIcon.json


+ 52 - 27
src/view/selection/index.module.less

@@ -1,22 +1,28 @@
-.selectionContainer{
+.selectionContainer {
     position: absolute;
     left: 0;
     top: 0;
     right: 0;
+    --active-stave-box: rgba(1, 193, 181, 0.2);
 }
-.position{
+
+.position {
     position: absolute;
 
 }
-.note{
+
+.note {
     // background-color: rgba(0, 0, 0, .3);
 }
-.staveBox{
-    background-color: rgba(1, 193, 181, 0.2);
+
+.staveBox {
+    background-color: var(--active-stave-box);
 }
-.leftStaveBox{
-    background-color: rgba(1, 193, 181, 0.2);
-    &::before{
+
+.leftStaveBox {
+    background-color: var(--active-stave-box);
+
+    &::before {
         content: '';
         position: absolute;
         left: -5px;
@@ -28,9 +34,11 @@
         border-bottom: 5px solid var(--van-primary-color);
     }
 }
-.rightStaveBox{
-    background-color: rgba(1, 193, 181, 0.2);
-    &::after{
+
+.rightStaveBox {
+    background-color: var(--active-stave-box);
+
+    &::after {
         content: '';
         position: absolute;
         right: -5px;
@@ -42,9 +50,11 @@
         border-bottom: 5px solid var(--van-primary-color);
     }
 }
-.centerStaveBox{
-    background-color: rgba(1, 193, 181, 0.2);
-    &::before{
+
+.centerStaveBox {
+    background-color: var(--active-stave-box);
+
+    &::before {
         content: '';
         position: absolute;
         left: -5px;
@@ -55,7 +65,8 @@
         border-left: 5px solid var(--van-primary-color);
         border-bottom: 5px solid var(--van-primary-color);
     }
-    &::after{
+
+    &::after {
         content: '';
         position: absolute;
         right: -5px;
@@ -67,27 +78,41 @@
         border-bottom: 5px solid var(--van-primary-color);
     }
 }
-.prepareStaveBox{
+
+.prepareStaveBox {
     background-color: rgba(255, 98, 37, 0.18);
 }
-.disable{
+
+.disable {
     pointer-events: none;
 }
 
 .line {
-	position: absolute;
-	top: -20%;
-	height: 120%;
-	width: 4px;
-	background-color: #ff8d2960;
+    position: absolute;
+    top: -20%;
+    height: 120%;
+    width: 4px;
+    background-color: #ff8d2960;
 }
-:global{
-    .lineHide{
+
+:global {
+    .lineHide {
         opacity: 0;
     }
 }
-.scoreItem{
+
+.scoreItem {
     position: absolute;
-    right: 0;
-    top: -100%;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    font-size: 16px;
+    font-family: "Roboto", sans-serif;
+    font-weight: bold;
+    display: flex;
+    align-items: center;
+    pointer-events: none;
+    img {
+        height: 30px;
+    }
 }

+ 36 - 9
src/view/selection/index.tsx

@@ -2,7 +2,8 @@ import { computed, defineComponent, onMounted, reactive } from "vue";
 import state, { handleSelection, skipNotePlay } from "/src/state";
 import styles from "./index.module.less";
 import { metronomeData } from "/src/helpers/metronome";
-// import scoreIocn from './scoreIcon.json'
+import { evaluatingData } from "../evaluating";
+import { leveByScoreMeasureIcons } from "../evaluating/evaluatResult";
 
 export default defineComponent({
 	name: "selection",
@@ -75,8 +76,8 @@ export default defineComponent({
 					}
 					if (state.section.length === 2) {
 						// 选段预备拍背景
-						if (state.sectionFirst && item.MeasureNumberXML === state.sectionFirst.MeasureNumberXML){
-							return styles.prepareStaveBox
+						if (state.sectionFirst && item.MeasureNumberXML === state.sectionFirst.MeasureNumberXML) {
+							return styles.prepareStaveBox;
 						}
 						if (item.MeasureNumberXML >= state.section[0].MeasureNumberXML && item.MeasureNumberXML <= state.section[1].MeasureNumberXML) {
 							if (item.MeasureNumberXML == state.section[0].MeasureNumberXML && item.MeasureNumberXML == state.section[1].MeasureNumberXML) {
@@ -94,32 +95,58 @@ 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);
 		});
 		return () => (
 			<div class={styles.selectionContainer}>
 				{selectData.staves.map((item: any) => {
+					const scoreItem = evaluatingData.evaluatings[item.measureListIndex];
 					return (
 						<>
 							{item.staveBox && (
 								<div class={[styles.position, showClass.value(item)]} 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>}
-									{/* <div class={styles.scoreItem}>
-										<img src={scoreIocn.perfect} />
-										98
-									</div> */}
+									{!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>
+									)}
 								</div>
 							)}
 						</>
 					);
 				})}
 				{selectData.notes.map((item: any) => {
-					return <div class={[styles.position, state.sectionStatus && styles.disable, styles.note]} style={item.bbox} onClick={() => skipNotePlay(item.index)}></div>;
+					return (
+						<div class={[styles.position, state.sectionStatus && styles.disable, styles.note]} style={item.bbox} onClick={() => skipNotePlay(item.index)}></div>
+					);
 				})}
 			</div>
 		);

File diff suppressed because it is too large
+ 0 - 1
src/view/selection/scoreIcon.json


Some files were not shown because too many files changed in this diff