浏览代码

酷乐秀

liushengqiang 2 年之前
父节点
当前提交
18871911ab
共有 78 个文件被更改,包括 2422 次插入5 次删除
  1. 48 0
      src/page-colexiu/App.tsx
  2. 35 0
      src/page-colexiu/api.ts
  3. 53 0
      src/page-colexiu/detail/index.module.less
  4. 259 0
      src/page-colexiu/detail/index.tsx
  5. 54 0
      src/page-colexiu/evaluat-model/earphone/index.module.less
  6. 23 0
      src/page-colexiu/evaluat-model/earphone/index.tsx
  7. 59 0
      src/page-colexiu/evaluat-model/evaluat-audio/index.module.less
  8. 27 0
      src/page-colexiu/evaluat-model/evaluat-audio/index.tsx
  9. 193 0
      src/page-colexiu/evaluat-model/evaluat-result/index.module.less
  10. 102 0
      src/page-colexiu/evaluat-model/evaluat-result/index.tsx
  11. 30 0
      src/page-colexiu/evaluat-model/evaluat-share/index.module.less
  12. 73 0
      src/page-colexiu/evaluat-model/evaluat-share/index.tsx
  13. 二进制
      src/page-colexiu/evaluat-model/icons/1.png
  14. 二进制
      src/page-colexiu/evaluat-model/icons/2.png
  15. 二进制
      src/page-colexiu/evaluat-model/icons/3.png
  16. 二进制
      src/page-colexiu/evaluat-model/icons/4.png
  17. 二进制
      src/page-colexiu/evaluat-model/icons/5.png
  18. 14 0
      src/page-colexiu/evaluat-model/icons/arrow-left-background.svg
  19. 二进制
      src/page-colexiu/evaluat-model/icons/bad.png
  20. 15 0
      src/page-colexiu/evaluat-model/icons/close.svg
  21. 12 0
      src/page-colexiu/evaluat-model/icons/close2.svg
  22. 二进制
      src/page-colexiu/evaluat-model/icons/erji.png
  23. 二进制
      src/page-colexiu/evaluat-model/icons/good.png
  24. 二进制
      src/page-colexiu/evaluat-model/icons/great.png
  25. 1 0
      src/page-colexiu/evaluat-model/icons/index.json
  26. 二进制
      src/page-colexiu/evaluat-model/icons/left-bg.png
  27. 二进制
      src/page-colexiu/evaluat-model/icons/perfect.png
  28. 二进制
      src/page-colexiu/evaluat-model/icons/student.png
  29. 二进制
      src/page-colexiu/evaluat-model/icons/title.png
  30. 78 0
      src/page-colexiu/evaluat-model/index.module.less
  31. 298 0
      src/page-colexiu/evaluat-model/index.tsx
  32. 54 0
      src/page-colexiu/evaluat-model/sound-effect/data.ts
  33. 二进制
      src/page-colexiu/evaluat-model/sound-effect/icons/bg-note.png
  34. 二进制
      src/page-colexiu/evaluat-model/sound-effect/icons/bg.png
  35. 二进制
      src/page-colexiu/evaluat-model/sound-effect/icons/child.png
  36. 二进制
      src/page-colexiu/evaluat-model/sound-effect/icons/content-bg.png
  37. 二进制
      src/page-colexiu/evaluat-model/sound-effect/icons/dot-active.png
  38. 二进制
      src/page-colexiu/evaluat-model/sound-effect/icons/dot-error.png
  39. 二进制
      src/page-colexiu/evaluat-model/sound-effect/icons/dot.png
  40. 11 0
      src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_120.svg
  41. 11 0
      src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_12_4.svg
  42. 10 0
      src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_13.svg
  43. 11 0
      src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_14_15.svg
  44. 11 0
      src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_5_6.svg
  45. 11 0
      src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_default.svg
  46. 二进制
      src/page-colexiu/evaluat-model/sound-effect/icons/notes.png
  47. 98 0
      src/page-colexiu/evaluat-model/sound-effect/index.module.less
  48. 84 0
      src/page-colexiu/evaluat-model/sound-effect/index.tsx
  49. 9 0
      src/page-colexiu/header-top/image/arrow.svg
  50. 12 0
      src/page-colexiu/header-top/image/close2.svg
  51. 3 0
      src/page-colexiu/header-top/image/headerTop.json
  52. 11 0
      src/page-colexiu/header-top/image/icon-back.svg
  53. 二进制
      src/page-colexiu/header-top/image/iconStep.png
  54. 5 0
      src/page-colexiu/header-top/image/index.ts
  55. 14 0
      src/page-colexiu/header-top/image/menu.svg
  56. 二进制
      src/page-colexiu/header-top/image/minus.png
  57. 二进制
      src/page-colexiu/header-top/image/music.png
  58. 二进制
      src/page-colexiu/header-top/image/plus.png
  59. 15 0
      src/page-colexiu/header-top/image/replay.svg
  60. 15 0
      src/page-colexiu/header-top/image/section0.svg
  61. 15 0
      src/page-colexiu/header-top/image/section1.svg
  62. 15 0
      src/page-colexiu/header-top/image/section2.svg
  63. 21 0
      src/page-colexiu/header-top/image/speed.svg
  64. 二进制
      src/page-colexiu/header-top/image/tickoff.png
  65. 二进制
      src/page-colexiu/header-top/image/tickon.png
  66. 75 0
      src/page-colexiu/header-top/index.module.less
  67. 123 0
      src/page-colexiu/header-top/index.tsx
  68. 83 0
      src/page-colexiu/header-top/settting/index.module.less
  69. 115 0
      src/page-colexiu/header-top/settting/index.tsx
  70. 35 0
      src/page-colexiu/header-top/speed/index.module.less
  71. 45 0
      src/page-colexiu/header-top/speed/index.tsx
  72. 25 0
      src/page-colexiu/header-top/title/index.module.less
  73. 39 0
      src/page-colexiu/header-top/title/index.tsx
  74. 22 0
      src/page-colexiu/main.ts
  75. 24 0
      src/page-colexiu/router.ts
  76. 8 0
      src/page-colexiu/theme.css
  77. 12 4
      src/page-gym/detail/index.tsx
  78. 6 1
      src/page-gym/header-top/settting/index.tsx

+ 48 - 0
src/page-colexiu/App.tsx

@@ -0,0 +1,48 @@
+import request from "umi-request";
+import { computed, defineComponent, onBeforeMount, onMounted } from "vue";
+import { RouterView } from "vue-router";
+import TheError from "../components/The-error";
+import { setUserInfo, storeData } from "../store";
+import { getRandomKey, setToken } from "../utils";
+import { getQuery } from "../utils/queryString";
+import Notfind from "../view/notfind";
+import { employeeQueryUserInfo, studentQueryUserInfo, teacherQueryUserInfo } from "./api";
+
+export default defineComponent({
+	name: "App",
+	setup() {
+		const query: any = getQuery()
+		/** 获取用户信息 */
+		const getUserInfo = async () => {
+			// const a = await request.get(`/student/queryUserInfo`)
+			// console.log(a)
+			if (storeData.platformType === "WEB") {
+				return await employeeQueryUserInfo();
+			} else if (storeData.platformType === "TEACHER") {
+				return await teacherQueryUserInfo();
+			}
+			return await studentQueryUserInfo();
+		};
+		const setUser = async () => {
+			const res = await getUserInfo();
+			const { student } = res?.data || {};
+			setUserInfo(student);
+			// console.log("🚀 ~ res:", res);
+		};
+		onBeforeMount(() => {
+			if (query.Authorization) {
+				setToken(query.Authorization);
+			}
+			setUser();
+			localStorage.setItem("behaviorId", getRandomKey());
+		});
+		onMounted(() => {
+			document.getElementById("loading")!.className = "";
+		})
+
+		const inited = computed(() => {
+			return storeData.status === "login";
+		});
+		return () => <>{storeData.status === "error" ? <TheError /> : inited.value ? <RouterView /> : null}</>;
+	},
+});

+ 35 - 0
src/page-colexiu/api.ts

@@ -0,0 +1,35 @@
+import request from "../utils/request";
+
+/** 获取学生信息 */
+export const studentQueryUserInfo = async () => {
+	return await request.get(`/student/queryUserInfo`);
+};
+/** 获取老师信息 */
+export const teacherQueryUserInfo = () => {
+	return request.get(`/student/queryUserInfo`);
+};
+/** 后台用户信息 */
+export const employeeQueryUserInfo = () => {
+	return request.get(`/employee/queryUserInfo`);
+};
+
+/** 获取曲谱信息 */
+export const sysMusicScoreAccompanimentQueryPage = (sysMusicScoreId: string) => {
+	return request.get("/sysMusicScoreAccompaniment/queryPage", {
+		params: {
+			clientType: "SMART_PRACTICE",
+			sysMusicScoreId,
+		},
+	});
+};
+
+/** 获取曲谱分类 */
+export const sysMusicScoreCategoriesQueryTree = (enable = false) => {
+	return request.get(`/sysMusicScoreCategories/queryTree`, {
+		params: {
+			parentId: 0,
+			// 后台详情忽略是否启用分类
+			enable,
+		},
+	});
+};

+ 53 - 0
src/page-colexiu/detail/index.module.less

@@ -0,0 +1,53 @@
+.skeleton {
+    position: fixed;
+    left: 0;
+    top: 0;
+    width: 100vw;
+    height: 100vh;
+    padding: 20px 30px;
+    background-color: #fff;
+    z-index: 10;
+    --van-skeleton-paragraph-height: .8rem;
+}
+
+.detail {
+    width: 100vw;
+    height: 100vh;
+    overflow: hidden;
+    background-color: var(--van-primary-color);
+    --header-height: 62px;
+    &.opencamera {
+        opacity: .7;
+    }
+
+    .headHeight {
+        height: var(--header-height);
+    }
+
+    .container {
+        position: relative;
+        height: calc(100vh - 18px - var(--header-height));
+        margin: 0 18px;
+        background: var(--container-background);
+        border-radius: 10px;
+        overflow: hidden;
+    }
+}
+
+.plugins {
+    display: none;
+}
+
+:global {
+    #cursorImg-0 {
+        min-height: 58PX;
+        content: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
+        margin-top: -14PX;
+        border-radius: 2px;
+        background-image: var(--cursor-color);
+        background-repeat: repeat-y;
+        background-position-x: 50%;
+        opacity: var(--corsor-opacity);
+    }
+
+}

+ 259 - 0
src/page-colexiu/detail/index.tsx

@@ -0,0 +1,259 @@
+import { Skeleton } from "vant";
+import { computed, defineComponent, nextTick, onBeforeMount, onMounted, reactive, Transition, watch, watchEffect } from "vue";
+import { useRoute } from "vue-router";
+import { formateTimes } from "../../helpers/formateMusic";
+import Metronome, { metronomeData } from "../../helpers/metronome";
+import state, { isRhythmicExercises } from "../../state";
+import { storeData } from "../../store";
+import { setGlobalData } from "../../utils";
+import AudioList from "../../view/audio-list";
+import MusicScore, { resetMusicScore } from "../../view/music-score";
+import { sysMusicScoreAccompanimentQueryPage, sysMusicScoreCategoriesQueryTree } from "../api";
+import EvaluatModel from "../evaluat-model";
+import HeaderTop from "../header-top";
+import styles from "./index.module.less";
+import { api_openCamera, api_setStatusBarVisibility, isSpecialShapedScreen } from "/src/helpers/communication";
+import { getQuery } from "/src/utils/queryString";
+import Evaluating, { evaluatingData } from "/src/view/evaluating";
+import MeasureSpeed from "/src/view/plugins/measure-speed";
+import { mappingVoicePart, subjectFingering } from "/src/view/fingering/fingering-config";
+import Fingering from "/src/view/fingering";
+import store from 'store'
+
+//特殊教材分类id
+const classids = [1, 30, 97]; // [大雅金唐, 竖笛教程, 声部训练]
+export default defineComponent({
+	name: "music-list",
+	setup() {
+		const route = useRoute();
+		const query: any = {
+			...getQuery(),
+			...route.query,
+		};
+
+		const paramsId = route.params.id as string;
+		const detailData = reactive({
+			isLoading: true,
+			paddingLeft: "",
+		});
+		const getAPPData = async () => {
+			const screenData = await isSpecialShapedScreen();
+			if (screenData?.content) {
+				// console.log("🚀 ~ screenData:", screenData.content);
+				const { isSpecialShapedScreen, notchHeight } = screenData.content;
+				if (isSpecialShapedScreen) {
+					detailData.paddingLeft = 25 + "px";
+				}
+			}
+		};
+		onBeforeMount(() => {
+			getAPPData();
+		});
+		// console.log(route.params, query)
+		/** 获取曲谱数据 */
+		const getMusicInfo = (res: any) => {
+			const index = query["part-index"] ? parseInt(query["part-index"] as string) : 0;
+			const musicInfo = res.data[index];
+			// console.log("🚀 ~ musicInfo:", musicInfo);
+			setState(musicInfo, index);
+			setCustom();
+			detailData.isLoading = false;
+		};
+		const getCategorId = (arr: any[], key = "sysMusicScoreCategoriesList"): any[] => {
+			const list = [];
+			if (!Array.isArray(arr)) return [];
+			for (let i = 0; i < arr.length; i++) {
+				list.push(arr[i].id);
+				if (Array.isArray(arr[i][key])) {
+					list.push(...getCategorId(arr[i][key]));
+				}
+			}
+			return list;
+		};
+		const getIds = (arr: any[], key = "sysMusicScoreCategoriesList"): any[] => {
+			if (!Array.isArray(arr)) return [];
+			const ids = [];
+			for (let i = 0; i < arr.length; i++) {
+				if (classids.includes(arr[i].id)) {
+					ids.push(arr[i].id, ...getCategorId(arr[i][key]));
+				} else {
+					ids.push(...getIds(arr[i][key]));
+				}
+			}
+			return ids;
+		};
+		/** 获取分类数据 */
+		const getCategory = (res: any) => {
+			console.log("特殊曲谱分类ids:");
+			console.log(getIds(res.data).toString());
+		};
+
+		const setState = (data: any, index: number) => {
+			state.detailId = data.id;
+			state.xmlUrl = data.xmlUrl;
+			state.partIndex = index;
+			state.subjectId = data.subjectId;
+			state.categoriesId = data.categoriesId;
+			state.categoriesName = data.categoriesName;
+			state.enableEvaluation = data.enableEvaluation;
+			state.examSongId = data.examSongId + "";
+			state.examSongName = data.examSongName;
+			// 解析扩展字段
+			if (data.extConfigJson) {
+				try {
+					state.extConfigJson = JSON.parse(data.extConfigJson as string);
+				} catch (error) {
+					console.error("解析扩展字段错误:", error);
+				}
+			}
+			state.isOpenMetronome = !data.isOpenMetronome;
+			state.needTick = data.isOpenMetronome;
+			state.isShowFingering = data.isShowFingering;
+			state.music = data.isOpenMetronome ? data.mp3Url : data.metronomeMp3Url;
+			state.accompany = data.isOpenMetronome ? data.url : data.metronomeUrl;
+			state.midiUrl = data.midiUrl;
+			state.parentCategoriesId = data.parentCategoriesId;
+			state.playMode = data.playMode;
+			state.originSpeed = state.speed = data.speed;
+			state.track = data.track;
+			state.isOpenPrepare = true;
+
+			// 映射声部ID
+			state.subjectId = mappingVoicePart(state.subjectId, "GYM");
+			// 是否打击乐
+			state.isPercussion = state.subjectId == 23 || state.subjectId == 113 || state.subjectId == 121 || isRhythmicExercises();
+
+			// 设置指法
+			state.fingeringInfo = subjectFingering(state.subjectId);
+		};
+
+		const setCustom = () => {
+			if (state.extConfigJson.multitrack) {
+				setGlobalData("multitrack", state.extConfigJson.multitrack);
+			}
+		};
+
+		onBeforeMount(() => {
+			api_setStatusBarVisibility();
+			const settting = store.get('musicscoresetting')
+			if (settting){
+				state.setting = settting
+				if (state.setting.camera){
+					api_openCamera();
+				}
+				// console.log("🚀 ~ settting:", settting)
+			}
+		});
+
+		onMounted(() => {
+			(window as any).appName = "gym";
+			Promise.all([sysMusicScoreCategoriesQueryTree(storeData.platformType === "WEB"), sysMusicScoreAccompanimentQueryPage(paramsId)]).then((values) => {
+				getCategory(values[0]);
+				getMusicInfo(values[1]);
+			});
+		});
+
+		/** 渲染完成 */
+		const handleRendered = (osmd: any) => {
+			state.musicRendered = true;
+			state.osmd = osmd;
+			state.times = formateTimes(osmd);
+			console.log("🚀 ~ state.times:", state.times);
+			try {
+				metronomeData.metro = new Metronome();
+				metronomeData.metro.init(state.times);
+			} catch (error) {}
+		};
+		/** 指法配置 */
+		const fingerConfig = computed<any>(() => {
+			if (state.setting.displayFingering && state.fingeringInfo?.name) {
+				if (state.fingeringInfo.direction === "transverse") {
+					return {
+						container: {
+							paddingBottom: state.fingeringInfo.height,
+						},
+						fingerBox: {
+							height: state.fingeringInfo.height,
+						},
+					};
+				} else {
+					return {
+						container: {
+							paddingRight: state.fingeringInfo.width,
+						},
+						fingerBox: {
+							position: "absolute",
+							width: state.fingeringInfo.width,
+							height: "100%",
+							right: 0,
+							top: 0,
+						},
+					};
+				}
+			}
+			return {
+				container: {},
+				fingerBox: {},
+			};
+		});
+
+		// 监听指法显示
+		watch(
+			() => state.setting.displayFingering,
+			() => {
+				if (state.fingeringInfo.direction === "vertical") {
+					if (state.setting.displayFingering) {
+						document.getElementById("musicAndSelection")?.style.removeProperty("--music-zoom");
+					} else {
+						nextTick(() => {
+							resetMusicScore();
+						});
+					}
+				}
+			}
+		);
+		return () => (
+			<div class={[styles.detail, state.setting.camera && styles.opencamera]} style={{ paddingLeft: detailData.paddingLeft }}>
+				{!state.musicRendered && (
+					<div class={styles.skeleton}>
+						<Skeleton class={styles.skeleton} row={8} />
+					</div>
+				)}
+				<div class={styles.headHeight}>
+					<Transition name="van-slide-down">{state.musicRendered && <HeaderTop />}</Transition>
+				</div>
+				<div
+					style={{ ...fingerConfig.value.container }}
+					class={[styles.container, state.setting.eyeProtection && "eyeProtection", !state.setting.displayCursor && "hideCursor"]}
+				>
+					{/* 曲谱渲染 */}
+					{!detailData.isLoading && <MusicScore onRendered={handleRendered} />}
+					{/* 播放 */}
+					{!detailData.isLoading && <AudioList />}
+					{/* 评测 */}
+					{state.modeType === "evaluating" && (
+						<>
+							<Evaluating />
+							{evaluatingData.rendered && <EvaluatModel />}
+						</>
+					)}
+					{/* 指法 */}
+					{state.setting.displayFingering && state.fingeringInfo?.name && (
+						<div style={{ ...fingerConfig.value.fingerBox }}>
+							<Fingering />
+						</div>
+					)}
+				</div>
+
+				{/* 公用的插件 */}
+				<div class={styles.plugins}>
+					{state.musicRendered && (
+						<>
+							<MeasureSpeed />
+						</>
+					)}
+				</div>
+			</div>
+		);
+	},
+});

+ 54 - 0
src/page-colexiu/evaluat-model/earphone/index.module.less

@@ -0,0 +1,54 @@
+.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;
+}
+
+.btn {
+    width: 106px;
+    height: 31px;
+    margin: 11px 0 17px 0;
+    line-height: 31px;
+    text-align: center;
+    background: #01C1B5;
+    border-radius: 36px;
+    font-size: 13px;
+}

+ 23 - 0
src/page-colexiu/evaluat-model/earphone/index.tsx

@@ -0,0 +1,23 @@
+import { defineComponent } from "vue";
+import styles from "./index.module.less";
+import icons from "../icons/index.json";
+
+export default defineComponent({
+	name: "earphone",
+	emits: ["close"],
+	setup(props, { emit }) {
+		return () => (
+			<div class={styles.fraction}>
+				<div class={styles.title}>
+					<img src={icons.title} />
+					<div class={styles.titleDes}>提示</div>
+				</div>
+				<img class={styles.erji} src={icons.erji} />
+				<div class={styles.tip}>请佩戴耳机以保证测评准确率~</div>
+				<div class={styles.btn} onClick={() => emit("close")}>
+					确定
+				</div>
+			</div>
+		);
+	},
+});

+ 59 - 0
src/page-colexiu/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-colexiu/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-colexiu/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-colexiu/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-colexiu/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-colexiu/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>
+		);
+	},
+});

二进制
src/page-colexiu/evaluat-model/icons/1.png


二进制
src/page-colexiu/evaluat-model/icons/2.png


二进制
src/page-colexiu/evaluat-model/icons/3.png


二进制
src/page-colexiu/evaluat-model/icons/4.png


二进制
src/page-colexiu/evaluat-model/icons/5.png


+ 14 - 0
src/page-colexiu/evaluat-model/icons/arrow-left-background.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>箭头</title>
+    <g id="云教练2版" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="画板" transform="translate(-21.000000, -19.000000)">
+            <g id="箭头" transform="translate(21.000000, 19.000000)">
+                <circle id="椭圆形" fill="#01C1B5" cx="16" cy="16" r="16"></circle>
+                <g id="编组" transform="translate(16.100000, 16.533333) scale(-1, 1) translate(-16.100000, -16.533333) translate(6.600000, 8.533333)" fill="#FFFFFF" fill-rule="nonzero">
+                    <path d="M11.3783644,15.2991467 L17.9125501,8.76496106 C18.157653,8.51993004 18.2953564,8.1875535 18.2953564,7.84097646 C18.2953564,7.49439943 18.157653,7.16202289 17.9125501,6.91699186 L11.3783644,0.382806222 C11.1333334,0.137703307 10.8009569,-1.22046857e-07 10.4543798,-1.22046857e-07 C10.1078028,-1.22046857e-07 9.77542625,0.137703307 9.53039523,0.382806222 C9.02019826,0.893028709 9.02019826,1.72023574 9.53039523,2.23045823 L13.8337591,6.53445653 L1.30683713,6.53445653 C0.960216585,6.53437236 0.627768546,6.67202957 0.382670797,6.91712731 C0.137573047,7.16222506 -3.85698459e-08,7.4946731 -3.85698459e-08,7.84129365 C-3.85698459e-08,8.1879142 0.137573041,8.52036224 0.382670791,8.76545999 C0.627768541,9.01055774 0.960216582,9.14821494 1.30683713,9.14813078 L13.8337591,9.14813078 L9.53039523,13.4514947 C9.20028733,13.7816026 9.07136551,14.2627453 9.19219339,14.7136811 C9.31302127,15.1646169 9.66524221,15.5168378 10.116178,15.6376657 C10.5671138,15.7584936 11.0482565,15.6295718 11.3783644,15.2994639 L11.3783644,15.2991467 Z" id="路径"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

二进制
src/page-colexiu/evaluat-model/icons/bad.png


+ 15 - 0
src/page-colexiu/evaluat-model/icons/close.svg

@@ -0,0 +1,15 @@
+<?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="#DBDBDB" fill-rule="nonzero">
+            <g id="弹窗" transform="translate(143.000000, 40.000000)">
+                <g id="编组" 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>
+        </g>
+    </g>
+</svg>

+ 12 - 0
src/page-colexiu/evaluat-model/icons/close2.svg

@@ -0,0 +1,12 @@
+<?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">
+    <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>
+        </g>
+    </g>
+</svg>

二进制
src/page-colexiu/evaluat-model/icons/erji.png


二进制
src/page-colexiu/evaluat-model/icons/good.png


二进制
src/page-colexiu/evaluat-model/icons/great.png


文件差异内容过多而无法显示
+ 1 - 0
src/page-colexiu/evaluat-model/icons/index.json


二进制
src/page-colexiu/evaluat-model/icons/left-bg.png


二进制
src/page-colexiu/evaluat-model/icons/perfect.png


二进制
src/page-colexiu/evaluat-model/icons/student.png


二进制
src/page-colexiu/evaluat-model/icons/title.png


+ 78 - 0
src/page-colexiu/evaluat-model/index.module.less

@@ -0,0 +1,78 @@
+:global {
+    .var-popup .var-popup--center {
+        overflow: initial;
+    }
+}
+
+.btn {
+    position: fixed;
+    bottom: 33px;
+    left: 50%;
+    transform: translateX(-50%);
+    width: 115px;
+    height: 38px;
+    line-height: 38px;
+    background: var(--van-primary-color);
+    border-radius: 20px;
+    font-size: 16px;
+    font-weight: 500;
+    color: #FFFFFF;
+    text-align: center;
+    z-index: 10;
+}
+
+.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;
+        }
+    }
+}

+ 298 - 0
src/page-colexiu/evaluat-model/index.tsx

@@ -0,0 +1,298 @@
+import { Popup, Snackbar } from "@varlet/ui";
+import { defineComponent, onMounted, reactive, watch } from "vue";
+import {
+	connectWebsocket,
+	evaluatingData,
+	handleEndBegin,
+	handleEndSoundCheck,
+	handlePerformDetection,
+	handleStartBegin,
+	handleStartEvaluat,
+	handleViewReport,
+} from "/src/view/evaluating";
+import Earphone from "./earphone";
+import styles from "./index.module.less";
+import SoundEffect from "./sound-effect";
+import state from "/src/state";
+import { storeData } from "/src/store";
+import { browser } from "/src/utils";
+import { getNoteByMeasuresSlursStart } from "/src/helpers/formateMusic";
+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";
+
+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测评要放原音音频
+		 */
+		const getMusicMode = () => {
+			const muguan = [2, 4, 5, 6];
+			const tongguan = [12, 13, 14, 15, 17];
+			if (muguan.includes(state.subjectId) && (state.examSongName || "").search(/[^\u0000-\u00FF](1-2|1-3|1-6)/gi) > -1) {
+				return "music";
+			}
+			if (tongguan.includes(state.subjectId) && (state.examSongName || "").search(/[^\u0000-\u00FF](1-1-1|1-1-2|1-5|1-6)/gi) > -1) {
+				return "music";
+			}
+			if ([23, 113, 121].includes(state.subjectId)) {
+				return "music";
+			}
+			return "background";
+		};
+		const browserInfo = browser();
+		/** 是否是节奏练习 */
+		const isRhythmicExercises = () => {
+			const examSongName = state.examSongName || "";
+			return examSongName.indexOf("节奏练习") > -1;
+		};
+
+		/** 获取评测标准 */
+		const getEvaluationCriteria = () => {
+			let criteria: TCriteria = "frequency";
+			// 声部打击乐
+			if ([23, 113, 121].includes(state.subjectId)) {
+				criteria = "amplitude";
+			} else if (isRhythmicExercises()) {
+				// 分类为节奏练习
+				criteria = "decibels";
+			}
+			return criteria;
+		};
+
+		/** 生成评测曲谱数据 */
+		const formatTimes = () => {
+			let ListenMode = false;
+			let dontEvaluatingMode = false;
+			let skip = false;
+			const datas = [];
+			for (let index = 0; index < state.times.length; index++) {
+				const item = state.times[index];
+				const note = getNoteByMeasuresSlursStart(item);
+				const rate = state.speed / state.originSpeed;
+				const difftime = item.difftime;
+				const start = difftime + (item.sourceRelativeTime || item.relativeTime);
+				const end = difftime + (item.sourceRelaEndtime || item.relaEndtime);
+				const isStaccato = note.noteElement.voiceEntry.isStaccato();
+				const noteRate = isStaccato ? 0.5 : 1;
+				if (note.formatLyricsEntries.contains("Play") || note.formatLyricsEntries.contains("Play...")) {
+					ListenMode = false;
+				}
+				if (note.formatLyricsEntries.contains("Listen")) {
+					ListenMode = true;
+				}
+				if (note.formatLyricsEntries.contains("纯律结束")) {
+					dontEvaluatingMode = false;
+				}
+				if (note.formatLyricsEntries.contains("纯律")) {
+					dontEvaluatingMode = true;
+				}
+				const nextNote = state.times[index + 1];
+				// console.log("noteinfo", note.noteElement.isRestFlag && !!note.stave && !!nextNote)
+				if (skip && (note.stave || !item.noteElement.isRestFlag || (nextNote && !nextNote.noteElement.isRestFlag))) {
+					skip = false;
+				}
+				if (note.noteElement.isRestFlag && !!note.stave && !!nextNote && nextNote.noteElement.isRestFlag) {
+					skip = true;
+				}
+				// console.log(note.measureOpenIndex, item.measureOpenIndex, note);
+				// console.log("skip", skip)
+				const data = {
+					timeStamp: (start * 1000) / rate,
+					duration: ((end * 1000) / rate - (start * 1000) / rate) * noteRate,
+					frequency: item.frequency,
+					nextFrequency: item.nextFrequency,
+					prevFrequency: item.prevFrequency,
+					// 重复的情况index会自然累加,render的index是谱面渲染的index
+					measureIndex: note.measureOpenIndex,
+					measureRenderIndex: item.measureListIndex,
+					dontEvaluating: ListenMode || dontEvaluatingMode,
+					musicalNotesIndex: item.i,
+					denominator: note.noteElement?.Length.denominator,
+				};
+				datas.push(data);
+			}
+			return datas;
+		};
+		/** 连接websocket */
+		const handleConnect = async () => {
+			const behaviorId = localStorage.getItem("behaviorId") || undefined;
+			const rate = state.speed / state.originSpeed;
+			const content = {
+				musicXmlInfos: formatTimes(),
+				id: state.examSongId,
+				subjectId: state.subjectId,
+				detailId: state.detailId,
+				examSongId: state.examSongId,
+				xmlUrl: state.xmlUrl,
+				partIndex: state.partIndex,
+				behaviorId,
+				tenantId: storeData.user.tenantId,
+				platform: browserInfo.ios ? "IOS" : browserInfo.android ? "ANDROID" : "WEB",
+				clientId: storeData.platformType === "STUDENT" ? "student" : storeData.platformType === "TEACHER" ? "teacher" : "education",
+				speed: state.speed,
+				heardLevel: state.setting.evaluationDifficulty,
+				beatLength: Math.round((state.fixtime * 1000) / rate),
+				campId: sessionStorage.getItem("campId") || "",
+				evaluationCriteria: getEvaluationCriteria(),
+			};
+			const result = await connectWebsocket(content);
+			state.playSource = getMusicMode();
+		};
+
+		/** 评测结果按钮处理 */
+		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();
+		});
+		watch(
+			() => evaluatingData.checkEnd,
+			() => {
+				if (evaluatingData.checkEnd) {
+					console.log("检测结束,连接websocket");
+					handleConnect();
+				}
+			}
+		);
+		watch(
+			() => evaluatingData.startBegin,
+			() => {
+				if (evaluatingData.startBegin) {
+					evaluatModel.tips = false;
+				}
+			}
+		);
+		return () => (
+			<div>
+				{evaluatingData.websocketState && (
+					<>
+						{!evaluatingData.startBegin && (
+							<div class={styles.btn} onClick={handleStartBegin}>
+								开始演奏
+							</div>
+						)}
+						{evaluatingData.startBegin && (
+							<div class={[styles.btn, styles.endBtn]} onClick={() => handleEndBegin(false)}>
+								<Icon name="success" />
+								<span>结束演奏</span>
+							</div>
+						)}
+					</>
+				)}
+				{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;
+							handlePerformDetection();
+						}}
+					/>
+				</Popup>
+				<Popup teleport="body" closeOnClickOverlay={false} defaultStyle={false} v-model:show={evaluatingData.soundEffectMode}>
+					<SoundEffect
+						onClose={(value: any) => {
+							evaluatingData.soundEffectMode = false;
+							if (value) {
+								state.setting.soundEffect = false;
+							}
+							handleEndSoundCheck();
+							handlePerformDetection();
+						}}
+					/>
+				</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>
+		);
+	},
+});

+ 54 - 0
src/page-colexiu/evaluat-model/sound-effect/data.ts

@@ -0,0 +1,54 @@
+import iconSound_12_4 from "./icons/icon-sound_12_4.svg";
+import iconsound_5_6 from "./icons/icon-sound_5_6.svg";
+import iconsound_13 from "./icons/icon-sound_13.svg";
+import iconsound_14_15 from "./icons/icon-sound_14_15.svg";
+import iconsound_120 from "./icons/icon-sound_120.svg";
+import iconsound_default from "./icons/icon-sound_default.svg";
+export const getScoreData = (subjectId: number) => {
+	// 小号、单簧管
+	if (subjectId == 12 || subjectId == 4) {
+		return {
+			src: iconSound_12_4,
+			text: "",
+			frequency: 525.6295448312027,
+		};
+	}
+	// 萨克斯
+	if (subjectId == 5 || subjectId == 6) {
+		return {
+			src: iconsound_5_6,
+			text: "C",
+			frequency: 525.6295448312027,
+		};
+	}
+	// 圆号
+	if (subjectId == 13) {
+		return {
+			src: iconsound_13,
+			text: "F",
+			frequency: 350.8156324849721,
+		};
+	}
+	// 长号 上低音号
+	if (subjectId == 14 || subjectId == 15) {
+		return {
+			src: iconsound_14_15,
+			text: "S",
+			frequency: 117.07067192670213,
+		};
+	}
+	// 长号 上低音号
+	if (subjectId == 120) {
+		return {
+			src: iconsound_120,
+			text: "A",
+			frequency: 884,
+		};
+	}
+	// 剩余声部
+	return {
+		src: iconsound_default,
+		text: "Bb",
+		frequency: 468.28268770680853,
+	};
+};

二进制
src/page-colexiu/evaluat-model/sound-effect/icons/bg-note.png


二进制
src/page-colexiu/evaluat-model/sound-effect/icons/bg.png


二进制
src/page-colexiu/evaluat-model/sound-effect/icons/child.png


二进制
src/page-colexiu/evaluat-model/sound-effect/icons/content-bg.png


二进制
src/page-colexiu/evaluat-model/sound-effect/icons/dot-active.png


二进制
src/page-colexiu/evaluat-model/sound-effect/icons/dot-error.png


二进制
src/page-colexiu/evaluat-model/sound-effect/icons/dot.png


文件差异内容过多而无法显示
+ 11 - 0
src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_120.svg


文件差异内容过多而无法显示
+ 11 - 0
src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_12_4.svg


文件差异内容过多而无法显示
+ 10 - 0
src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_13.svg


文件差异内容过多而无法显示
+ 11 - 0
src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_14_15.svg


文件差异内容过多而无法显示
+ 11 - 0
src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_5_6.svg


文件差异内容过多而无法显示
+ 11 - 0
src/page-colexiu/evaluat-model/sound-effect/icons/icon-sound_default.svg


二进制
src/page-colexiu/evaluat-model/sound-effect/icons/notes.png


+ 98 - 0
src/page-colexiu/evaluat-model/sound-effect/index.module.less

@@ -0,0 +1,98 @@
+.sound-effect {
+  position: relative;
+  width: 100vw;
+  height: 100vh;
+  background-image: url('./icons/bg.png');
+  background-size: 100% 100%;
+  background-repeat: no-repeat;
+  background-position: center;
+
+}
+
+.top {
+  display: flex;
+  justify-content: space-between;
+  padding: 20px 30px 10px 20px;
+}
+
+.back {
+  width: 30px;
+  height: 30px;
+
+  img {
+    width: 100%;
+    height: 100%;
+    display: block;
+  }
+}
+
+.skibtns {
+  z-index: 9999 !important;
+  :global {
+    --van-popover-action-width: 100px;
+    --van-popover-action-font-size: 14px;
+    --van-popover-light-text-color: #999999;
+  }
+}
+
+.rightSkipBtn {
+  font-size: 17px;
+
+  .tran {
+    margin-left: 6px;
+    transform: rotate(90deg);
+  }
+}
+
+.content {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background-image: url('./icons/bg-note.png');
+  background-repeat: no-repeat;
+  background-size: 94%;
+  background-position: center 30%;
+}
+
+.heiban {
+  position: relative;
+  width: 50%;
+  height: 75vh;
+  background-image: url('./icons/content-bg.png');
+  background-repeat: no-repeat;
+  background-size: contain;
+  background-position: center;
+  max-height: 370PX;
+  min-height: 260PX;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
+
+.iconChild {
+  position: absolute;
+  bottom: 0;
+  left: 8%;
+  width: 8vw;
+  max-width: 95PX;
+}
+.scoreContent{
+  text-align: center;
+}
+.tips{
+  text-align: center;
+  color: #fff;
+  font-size: 13px;
+  padding: 4px 0;
+}
+.steps{
+  margin: 0 auto;
+  padding: 10px 0;
+  width: 40%;
+  display: flex;
+  justify-content: space-evenly;
+  & > img {
+    width: 20px;
+    height: 20px;
+  }
+}

+ 84 - 0
src/page-colexiu/evaluat-model/sound-effect/index.tsx

@@ -0,0 +1,84 @@
+import { defineComponent, reactive, ref, watch } from "vue";
+import { Popover, Icon } from "vant";
+import icons from "../icons/index.json";
+import iconChild from "./icons/child.png";
+import DotIcon from "./icons/dot.png";
+import DotActiveIcon from "./icons/dot-active.png";
+import DotErrorIcon from "./icons/dot-error.png";
+
+import styles from "./index.module.less";
+import state from "/src/state";
+import { evaluatingData } from "/src/view/evaluating";
+import { getScoreData } from "./data";
+
+export default defineComponent({
+	name: "sound-effect",
+	emits: ["close"],
+	setup(props, { emit }) {
+		const scoreData = getScoreData(state.subjectId);
+		const soundEffectData = reactive({
+			step: 0,
+			tips: ["左边红灯表示吹奏的音过低", "吹奏时请保持中间绿灯亮起", "右边红灯表示吹奏的音过高"],
+			time: 1,
+		});
+		watch(
+			() => evaluatingData.soundEffectFrequency,
+			() => {
+        // console.log('吹奏',evaluatingData.soundEffectFrequency , scoreData.frequency)
+				const trend =
+					Math.abs(evaluatingData.soundEffectFrequency - scoreData.frequency) <= 10 ? 1 : evaluatingData.soundEffectFrequency > scoreData.frequency ? 2 : 0;
+				soundEffectData.step = trend;
+				if (trend !== 1) {
+					soundEffectData.time = Date.now();
+				}
+        // 持续时间达到3秒钟,效音成功
+				if (Date.now() - soundEffectData.time > 3000) {
+					// console.log("效音完成");
+          emit('close')
+				}
+			}
+		);
+
+		/** 跳过本次 */
+		const handleSelect = (e: {text: string}) => {
+      if (e.text === '关闭校音'){
+        emit('close', true)
+        return
+      } 
+      emit('close')
+		};
+		return () => (
+			<div class={styles["sound-effect"]}>
+				<div class={styles.top}>
+					<div class={styles.back} onClick={() => emit('close')}>
+						<img src={icons["arrow-left-background"]} />
+					</div>
+					<Popover trigger="click" class={styles.skibtns} actions={[{ text: "跳过本次" }, { text: "关闭校音" }]} onSelect={handleSelect}>
+						{{
+							reference: () => (
+								<div class={styles.rightSkipBtn}>
+									<span>跳过本次</span>
+									<Icon name="play" color="var(--van-primary-color)" class={styles.tran}/>
+								</div>
+							),
+						}}
+					</Popover>
+				</div>
+				<div class={styles.content}>
+					<div class={styles.heiban}>
+						<img class={styles.iconChild} src={iconChild} />
+						<div class={styles.scoreContent}>
+							<img src={scoreData.src} />
+						</div>
+						<div class={styles.tips}>{soundEffectData.tips[soundEffectData.step]}</div>
+						<div class={styles.steps}>
+							<img src={soundEffectData.step === 0 ? DotErrorIcon : DotIcon} />
+							<img src={soundEffectData.step === 1 ? DotActiveIcon : DotIcon} />
+							<img src={soundEffectData.step === 2 ? DotErrorIcon : DotIcon} />
+						</div>
+					</div>
+				</div>
+			</div>
+		);
+	},
+});

文件差异内容过多而无法显示
+ 9 - 0
src/page-colexiu/header-top/image/arrow.svg


+ 12 - 0
src/page-colexiu/header-top/image/close2.svg

@@ -0,0 +1,12 @@
+<?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">
+    <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>
+        </g>
+    </g>
+</svg>

文件差异内容过多而无法显示
+ 3 - 0
src/page-colexiu/header-top/image/headerTop.json


+ 11 - 0
src/page-colexiu/header-top/image/icon-back.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <g id="云教练2版" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="画板" transform="translate(-27.000000, -123.000000)">
+            <g id="返回箭头备份" transform="translate(43.000000, 139.000000) scale(-1, 1) translate(-43.000000, -139.000000) translate(27.000000, 123.000000)">
+                <circle id="椭圆形" fill="#FFFFFF" cx="16" cy="16" r="16"></circle>
+                <path d="M17.3783645,23.2991468 L23.9125501,16.7649612 C24.157653,16.5199302 24.2953565,16.1875536 24.2953565,15.8409766 C24.2953565,15.4943995 24.157653,15.162023 23.9125501,14.916992 L17.3783645,8.38280634 C17.1333334,8.13770343 16.8009569,8 16.4543799,8 C16.1078028,8 15.7754263,8.13770343 15.5303953,8.38280634 C15.0201983,8.89302883 15.0201983,9.72023586 15.5303953,10.2304583 L19.8337592,14.5344566 L7.30683717,14.5344566 C6.96021662,14.5343725 6.62776858,14.6720297 6.38267084,14.9171274 C6.13757309,15.1622252 6,15.4946732 6,15.8412938 C6,16.1879143 6.13757308,16.5203624 6.38267083,16.7654601 C6.62776858,17.0105579 6.96021662,17.1482151 7.30683717,17.1481309 L19.8337592,17.1481309 L15.5303953,21.4514948 C15.2002874,21.7816027 15.0713656,22.2627455 15.1921934,22.7136812 C15.3130213,23.164617 15.6652423,23.516838 16.116178,23.6376658 C16.5671138,23.7584937 17.0482566,23.6295719 17.3783645,23.299464 L17.3783645,23.2991468 Z" id="路径" fill="#2DC7AA" fill-rule="nonzero"></path>
+            </g>
+        </g>
+    </g>
+</svg>

二进制
src/page-colexiu/header-top/image/iconStep.png


+ 5 - 0
src/page-colexiu/header-top/image/index.ts

@@ -0,0 +1,5 @@
+const icons = import.meta.glob("./*", { import: "default", eager: true });
+export const headImg = (name: string) => {
+    const src = icons[`./${name}`] as unknown as string || ''
+	return src;
+};

+ 14 - 0
src/page-colexiu/header-top/image/menu.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="34px" height="34px" viewBox="0 0 34 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 5备份 4</title>
+    <g id="云教练2版" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="画板" transform="translate(-587.000000, -55.000000)">
+            <g id="编组-5备份-4" transform="translate(589.000000, 57.000000)">
+                <rect id="矩形" stroke="#8EE1DC" stroke-width="2" fill="#FFFFFF" x="-1" y="-1" width="32" height="32" rx="12"></rect>
+                <g id="编组" transform="translate(5.000000, 6.000000)" fill="#01C1B5" fill-rule="nonzero">
+                    <path d="M13.4777127,0.0182290708 C14.4044052,0.0179861139 15.2701896,0.47987939 15.786046,1.24971788 L15.8832682,1.40711806 L19.4443793,7.57378472 C19.9055877,8.37249457 19.9402825,9.34803984 19.5369792,10.1774957 L19.4443793,10.3515625 L15.8832682,16.5182291 C15.4201933,17.3208032 14.5874276,17.8396355 13.6629123,17.9015625 L13.4777127,17.9071182 L6.35735677,17.9071182 C5.43066421,17.9073611 4.56487987,17.4454678 4.04902344,16.6756293 L3.95180122,16.5182291 L0.389756944,10.3515625 C-0.0714513871,9.55285265 -0.106146222,8.57730739 0.297157118,7.74785156 L0.389756944,7.57378472 L3.95086806,1.40711806 C4.41394723,0.604537909 5.24672247,0.085704652 6.17124566,0.0237847222 L6.35642361,0.0182290708 L13.4768012,0.0182290708 L13.4777127,0.0182290708 Z M13.4777127,1.87007376 L6.35735677,1.87007376 C6.06542361,1.87007376 5.79055442,2.00761662 5.6156901,2.24138455 L5.55549045,2.33305122 L1.99437934,8.49971788 C1.85122226,8.74833447 1.83139307,9.04926681 1.9406901,9.31451823 L1.99437934,9.42562934 L5.55549045,15.592296 C5.70147524,15.8451441 5.95816545,16.0143272 6.24809028,16.0487847 L6.35735677,16.0552735 L13.4777127,16.0552735 C13.7696458,16.0552735 14.044515,15.9177306 14.2193793,15.6839627 L14.279579,15.592296 L17.8406901,9.42562934 C17.9838472,9.17701275 18.0036764,8.87608041 17.8943793,8.61082899 L17.8406901,8.49971788 L14.279579,2.33305122 C14.1335942,2.0802031 13.876904,1.91102001 13.5869792,1.8765625 L13.4777127,1.87007376 L13.4777127,1.87007376 Z M10.1,5.66 C12.0329966,5.66 13.6,7.22700338 13.6,9.16 C13.6,11.0929966 12.0329966,12.66 10.1,12.66 C8.16700338,12.66 6.6,11.0929966 6.6,9.16 C6.6,7.22700338 8.16700338,5.66 10.1,5.66 Z" id="形状"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

二进制
src/page-colexiu/header-top/image/minus.png


二进制
src/page-colexiu/header-top/image/music.png


二进制
src/page-colexiu/header-top/image/plus.png


+ 15 - 0
src/page-colexiu/header-top/image/replay.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="34px" height="34px" viewBox="0 0 34 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 5备份 4</title>
+    <g id="云教练2版" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="画板" transform="translate(-499.000000, -55.000000)">
+            <g id="编组-5备份-4" transform="translate(501.000000, 57.000000)">
+                <rect id="矩形" stroke="#8EE1DC" stroke-width="2" fill="#FFFFFF" x="-1" y="-1" width="32" height="32" rx="12"></rect>
+                <g id="编组" transform="translate(15.000000, 15.500000) scale(-1, 1) translate(-15.000000, -15.500000) translate(4.000000, 5.000000)" fill="#01C1B5" fill-rule="nonzero">
+                    <path d="M21.8359743,5.91618045 C21.4133965,7.0257019 21.0615062,8.16089287 20.7824176,9.31493233 C20.5279569,10.3734135 19.9595125,10.6413835 19.1023816,10.1813684 C19.1023816,10.1813684 16.8062829,8.88915789 15.8866529,8.47231579 C14.8132553,7.98401503 14.9714872,7.47437594 16.3613486,6.9433985 C16.7720572,6.78708271 17.1663969,6.59057144 17.5726412,6.4238346 C16.1878095,3.53041761 13.2777299,1.67769054 10.0712582,1.64801504 C5.30782736,1.72874517 1.51067257,5.65548936 1.58774614,10.4210526 C1.5098371,15.1866217 5.30633371,19.1140403 10.0697701,19.1955789 C12.4640261,19.1829371 14.7366908,18.1386081 16.3062899,16.3297895 C16.5067122,16.1698604 16.7415656,16.0587535 16.9922922,16.0052481 C17.3774369,16.0098332 17.6998373,16.2986565 17.7467459,16.6811278 C17.7563723,16.870475 17.7141837,17.0588621 17.6247238,17.226 C17.5872265,17.2811669 17.5427057,17.331212 17.4922852,17.3748722 C15.6413244,19.5566109 12.9315942,20.8226441 10.0712582,20.8421053 C4.41465343,20.7450836 -0.0930331504,16.0801605 0.0014588411,10.4210526 C0.0139187466,9.29275203 0.205800339,8.17362578 0.569903248,7.10566917 C0.580968335,7.01624856 0.606605274,6.92924621 0.645795047,6.8481203 C2.01644926,2.79485654 5.79595676,0.0492687634 10.0727463,0 C13.931717,0.0337531718 17.4260937,2.28823581 19.048811,5.79112781 C19.5205306,5.58270676 20.0041547,5.40406014 20.4714101,5.18075188 C21.8166293,4.53861654 22.2739642,4.78375939 21.8434147,5.91618045 L21.8359743,5.91618045 Z" id="路径"></path>
+                    <path d="M8.18320392,13.7071392 C7.50450737,14.1305274 6.94736842,13.8126149 6.94736842,13.0014923 L6.94736842,7.84061301 C6.94736842,7.02949037 7.50306026,6.71157784 8.18320392,7.13496603 L12.2264409,9.65152602 C12.5343515,9.77570179 12.7368421,10.0807 12.7368421,10.4203098 C12.7368421,10.7599196 12.5343515,11.0649179 12.2264409,11.1890937 L8.18320392,13.7071392 Z" id="路径" transform="translate(9.842105, 10.421053) scale(-1, 1) translate(-9.842105, -10.421053) "></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 15 - 0
src/page-colexiu/header-top/image/section0.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="34px" height="34px" viewBox="0 0 34 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 5备份</title>
+    <g id="云教练2版" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="画板" transform="translate(-176.000000, -55.000000)">
+            <g id="编组-5备份" transform="translate(178.000000, 57.000000)">
+                <rect id="矩形" stroke="#8EE1DC" stroke-width="2" fill="#FFFFFF" x="-1" y="-1" width="32" height="32" rx="12"></rect>
+                <g id="编组" transform="translate(15.500000, 15.000000) rotate(-90.000000) translate(-15.500000, -15.000000) translate(6.500000, 6.000000)" fill="#01C1B5" fill-rule="nonzero">
+                    <path d="M1.3031071,7.71434066 L5.04076678,7.71434066 L5.04076678,16.6618347 C5.04076678,17.3741595 5.58990162,17.9822416 6.31617672,17.9996154 C7.06016586,18.0169891 7.66244277,17.4436546 7.66244277,16.713956 L7.66244277,1.66826702 C7.66244277,0.747456955 6.90073961,0.00038463772 5.96189619,0.00038463772 C5.39504732,0.00038463772 4.86362652,0.278365035 4.54477403,0.747456955 L0.647688105,6.48080265 C0.27569354,7.00201589 0.647688105,7.71434066 1.3031071,7.71434066 Z" id="路径"></path>
+                    <path d="M12.9057947,10.2856593 L12.9057947,1.3381653 C12.9057947,0.625840532 12.3566599,0.0177584126 11.6303848,0.00038463772 C10.8863957,-0.0169891371 10.2841188,0.556345432 10.2841188,1.28604398 L10.2841188,16.331733 C10.2841188,17.252543 11.0458219,17.9996154 11.9846653,17.9996154 C12.5515142,17.9996154 13.082935,17.721635 13.4017875,17.252543 L17.3697295,11.4323285 C17.706296,10.9458628 17.3520155,10.2856593 16.7497386,10.2856593 L12.9057947,10.2856593 Z" id="路径"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 15 - 0
src/page-colexiu/header-top/image/section1.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="34px" height="34px" viewBox="0 0 34 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 5备份 3</title>
+    <g id="云教练2版" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="画板" transform="translate(-217.000000, -55.000000)">
+            <g id="编组-5备份-3" transform="translate(219.000000, 57.000000)">
+                <rect id="矩形" stroke="#8EE1DC" stroke-width="2" fill="#FFFFFF" x="-1" y="-1" width="32" height="32" rx="12"></rect>
+                <g id="编组" transform="translate(15.500000, 15.000000) rotate(-90.000000) translate(-15.500000, -15.000000) translate(7.000000, 6.000000)" fill-rule="nonzero">
+                    <path d="M0.803107102,7.71434066 L4.54076678,7.71434066 L4.54076678,16.6618347 C4.54076678,17.3741595 5.08990162,17.9822416 5.81617672,17.9996154 C6.56016586,18.0169891 7.16244277,17.4436546 7.16244277,16.713956 L7.16244277,1.66826702 C7.16244277,0.747456955 6.40073961,0.00038463772 5.46189619,0.00038463772 C4.89504732,0.00038463772 4.36362652,0.278365035 4.04477403,0.747456955 L0.147688105,6.48080265 C-0.22430646,7.00201589 0.147688105,7.71434066 0.803107102,7.71434066 Z" id="路径" fill="#01C1B5"></path>
+                    <path d="M12.4057947,10.2856593 L12.4057947,1.3381653 C12.4057947,0.625840532 11.8566599,0.0177584126 11.1303848,0.00038463772 C10.3863957,-0.0169891371 9.78411876,0.556345432 9.78411876,1.28604398 L9.78411876,16.331733 C9.78411876,17.252543 10.5458219,17.9996154 11.4846653,17.9996154 C12.0515142,17.9996154 12.582935,17.721635 12.9017875,17.252543 L16.8697295,11.4323285 C17.206296,10.9458628 16.8520155,10.2856593 16.2497386,10.2856593 L12.4057947,10.2856593 Z" id="路径" fill="#FFC830"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 15 - 0
src/page-colexiu/header-top/image/section2.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="34px" height="34px" viewBox="0 0 34 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 5备份 5</title>
+    <g id="云教练2版" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="画板" transform="translate(-258.000000, -55.000000)">
+            <g id="编组-5备份-5" transform="translate(260.000000, 57.000000)">
+                <rect id="矩形" stroke="#8EE1DC" stroke-width="2" fill="#FFFFFF" x="-1" y="-1" width="32" height="32" rx="12"></rect>
+                <g id="编组" transform="translate(15.500000, 15.000000) rotate(-90.000000) translate(-15.500000, -15.000000) translate(7.000000, 6.000000)" fill="#FFC830" fill-rule="nonzero">
+                    <path d="M0.803107102,7.71434066 L4.54076678,7.71434066 L4.54076678,16.6618347 C4.54076678,17.3741595 5.08990162,17.9822416 5.81617672,17.9996154 C6.56016586,18.0169891 7.16244277,17.4436546 7.16244277,16.713956 L7.16244277,1.66826702 C7.16244277,0.747456955 6.40073961,0.00038463772 5.46189619,0.00038463772 C4.89504732,0.00038463772 4.36362652,0.278365035 4.04477403,0.747456955 L0.147688105,6.48080265 C-0.22430646,7.00201589 0.147688105,7.71434066 0.803107102,7.71434066 Z" id="路径"></path>
+                    <path d="M12.4057947,10.2856593 L12.4057947,1.3381653 C12.4057947,0.625840532 11.8566599,0.0177584126 11.1303848,0.00038463772 C10.3863957,-0.0169891371 9.78411876,0.556345432 9.78411876,1.28604398 L9.78411876,16.331733 C9.78411876,17.252543 10.5458219,17.9996154 11.4846653,17.9996154 C12.0515142,17.9996154 12.582935,17.721635 12.9017875,17.252543 L16.8697295,11.4323285 C17.206296,10.9458628 16.8520155,10.2856593 16.2497386,10.2856593 L12.4057947,10.2856593 Z" id="路径"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 21 - 0
src/page-colexiu/header-top/image/speed.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="34px" height="34px" viewBox="0 0 34 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 5备份 4</title>
+    <g id="云教练2版" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="画板" transform="translate(-549.000000, -55.000000)">
+            <g id="编组-5备份-4" transform="translate(551.000000, 57.000000)">
+                <rect id="矩形" stroke="#8EE1DC" stroke-width="2" fill="#FFFFFF" x="-1" y="-1" width="32" height="32" rx="12"></rect>
+                <g id="编组-2" transform="translate(4.000000, 6.000000)" fill="#01C1B5" fill-rule="nonzero">
+                    <g id="编组">
+                        <path d="M17.1788934,2.65984165 L21.2637262,2.66129281 C21.6441871,2.6595274 21.9627029,2.95663144 21.9969824,3.34525683 C22.0312619,3.73388223 21.7699052,4.08478959 21.3953125,4.15308163 L21.2637262,4.16614204 L17.1788934,4.16614204 C17.3474571,3.67892357 17.3474571,3.14706012 17.1788934,2.65984165 L17.1788934,2.65984165 Z" id="形状"></path>
+                        <path d="M12.7368421,0 C14.6553049,0 16.2105263,1.5552214 16.2105263,3.47368421 C16.2105263,5.39214703 14.6553049,6.94736842 12.7368421,6.94736842 C11.0558023,6.94736842 9.65365999,5.75326639 9.33235683,4.16700399 L0.73627375,4.16614204 C0.355812911,4.16790744 0.0372970883,3.8708034 0.00301760378,3.48217801 C-0.0312618807,3.09355262 0.230094793,2.74264526 0.604687516,2.67435322 L0.73627375,2.66129281 L9.35922942,2.65897043 C9.72584461,1.13357143 11.0989635,0 12.7368421,0 Z" id="形状结合"></path>
+                    </g>
+                    <g id="编组备份-3" transform="translate(11.000000, 15.078947) scale(-1, 1) translate(-11.000000, -15.078947) translate(0.000000, 11.578947)">
+                        <path d="M17.1788934,2.65984165 L21.2637262,2.66129281 C21.6441871,2.6595274 21.9627029,2.95663144 21.9969824,3.34525683 C22.0312619,3.73388223 21.7699052,4.08478959 21.3953125,4.15308163 L21.2637262,4.16614204 L17.1788934,4.16614204 C17.3474571,3.67892357 17.3474571,3.14706012 17.1788934,2.65984165 L17.1788934,2.65984165 Z" id="形状"></path>
+                        <path d="M12.7368421,0 C14.6553049,0 16.2105263,1.5552214 16.2105263,3.47368421 C16.2105263,5.39214703 14.6553049,6.94736842 12.7368421,6.94736842 C11.0558023,6.94736842 9.65365999,5.75326639 9.33235683,4.16700399 L0.73627375,4.16614204 C0.355812911,4.16790744 0.0372970883,3.8708034 0.00301760378,3.48217801 C-0.0312618807,3.09355262 0.230094793,2.74264526 0.604687516,2.67435322 L0.73627375,2.66129281 L9.35922942,2.65897043 C9.72584461,1.13357143 11.0989635,0 12.7368421,0 Z" id="形状结合"></path>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

二进制
src/page-colexiu/header-top/image/tickoff.png


二进制
src/page-colexiu/header-top/image/tickon.png


+ 75 - 0
src/page-colexiu/header-top/index.module.less

@@ -0,0 +1,75 @@
+.headerTop{
+    display: flex;
+    align-items: center;
+    width: 100%;
+    height: 100%;
+    flex-shrink: 0;
+    padding: 8px 10px;
+}
+.back{
+    display: flex;
+    align-items: center;
+    height: 100%;
+    padding: 0 11px 0 6px;
+    img{
+        display: block;
+        width: 24px;
+        height: 24px;
+    }
+}
+.headRight{
+    display: flex;
+    align-items: center;
+    margin-left: auto;
+    .btn{
+        position: relative;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        color: #fff;
+        font-size: 11px;
+        line-height: 16px;
+        font-weight: 400;
+        padding: 0 6px;
+        .iconBtn{
+            display: block;
+            width: 25px;
+            height: 25px;
+        }
+        span{
+            white-space: nowrap;
+        }
+        .btnWrap{
+            position: relative;
+            width: 25px;
+            height: 25px;
+        }
+        .progress{
+            position: absolute;
+            left: 50%;
+            top: 50%;
+            transform: translate(-50%, -50%);
+            width: 20px;
+            height: 20px;
+        }
+        &.disabled{
+            opacity: .8;
+        }
+    }
+    .badge{
+        :global{
+            .van-badge{
+                border: none;
+                color: #005e58;
+                background-color: #d7fffc;
+                font-weight: 400;
+            }
+        }
+    }
+}
+
+.disable{
+    pointer-events: none;
+    opacity: .6;
+}

+ 123 - 0
src/page-colexiu/header-top/index.tsx

@@ -0,0 +1,123 @@
+import { computed, defineComponent, onMounted, reactive, ref } from "vue";
+import styles from "./index.module.less";
+
+import iconBack from "./image/icon-back.svg";
+import Title from "./title";
+import state, { handleChangeSection, handleResetPlay, togglePlay } from "../../state";
+import { headImg } from "./image";
+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";
+import { Popup } from "@varlet/ui";
+import Settting from "./settting";
+
+export const headData = reactive({
+	speedShow: false,
+});
+
+export default defineComponent({
+	name: "header-top",
+	setup() {
+		const headerData = reactive({
+			settingMode: false,
+		});
+		const headRef = ref();
+
+		const toggleEvaluat = () => {
+			handleStartEvaluat();
+		};
+		const disabledList = ["evaluating"];
+		return () => (
+			<div ref={headRef} class={styles.headerTop}>
+				<div class={styles.back}>
+					<img src={iconBack} />
+				</div>
+				<Title text={state.examSongName} />
+
+				<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} />
+						<span>评测</span>
+					</div>
+					<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, 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" />
+						</div>
+						<span>{state.playState === "play" ? "暂停" : "播放"}</span>
+					</div>
+					<div
+						class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]}
+						id="tips-step-6"
+						onClick={() => {
+							state.playSource = state.playSource === "music" ? "background" : "music";
+						}}
+					>
+						<img class={styles.iconBtn} src={state.playSource === "music" ? icons.music : icons.background} />
+						<span>{state.playSource === "music" ? "原声" : "伴奏"}</span>
+					</div>
+					<div
+						class={[styles.btn]}
+						onClick={async () => {
+							metronomeData.lineShow = !metronomeData.lineShow;
+						}}
+					>
+						<img class={styles.iconBtn} src={headImg("iconStep.png")} />
+						<span>{metronomeData.lineShow ? "高级" : "初级"}</span>
+					</div>
+					<div
+						class={styles.btn}
+						onClick={async () => {
+							metronomeData.disable = !metronomeData.disable;
+							metronomeData.metro?.initPlayer();
+						}}
+					>
+						<img style={{ display: metronomeData.disable ? "block" : "none" }} class={styles.iconBtn} src={headImg("tickoff.png")} />
+						<img style={{ display: !metronomeData.disable ? "block" : "none" }} class={styles.iconBtn} src={headImg("tickon.png")} />
+						<span style={{ whiteSpace: "nowrap" }}>节拍器</span>
+					</div>
+					<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>
+
+					<Popover trigger="manual" v-model:show={headData.speedShow} placement="bottom" overlay={false}>
+						{{
+							reference: () => (
+								<div
+									id="tips-step-8"
+									class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]}
+									onClick={(e: Event) => {
+										e.stopPropagation();
+										headData.speedShow = !headData.speedShow;
+									}}
+								>
+									<Badge class={styles.badge} content={state.speed}>
+										<img class={styles.iconBtn} src={headImg("speed.svg")} />
+									</Badge>
+									<span>速度</span>
+								</div>
+							),
+							default: () => <Speed />,
+						}}
+					</Popover>
+					<div class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]} onClick={() => (headerData.settingMode = true)}>
+						<img class={styles.iconBtn} src={headImg("menu.svg")} />
+						<span>设置</span>
+					</div>
+				</div>
+
+				<Popup teleport="body" defaultStyle={false} v-model:show={headerData.settingMode}>
+					<Settting onClose={() => headerData.settingMode = false} />
+				</Popup>
+			</div>
+		);
+	},
+});

+ 83 - 0
src/page-colexiu/header-top/settting/index.module.less

@@ -0,0 +1,83 @@
+.header-settting {
+    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;
+    }
+}
+
+.content {
+    position: relative;
+    overflow: hidden;
+    border-radius: 18px;
+    width: 300px;
+    height: 86vh;
+    background-color: #fff;
+    max-height: 310px;
+    --van-tabs-line-height: 50px;
+    --van-tab-active-text-color: var(--van-primary-color);
+    :global{
+        .van-tab__panel{
+            height: calc(86vh - 50px);
+            overflow-y: auto;
+            padding: 0 10px 10px 10px;
+            &::-webkit-scrollbar {
+                width: 0;
+                display: none;
+            }
+        }
+        .van-tabs__nav--line{
+            padding-bottom: 0;
+        }
+        .van-tabs__line{
+            bottom: 0;
+        }
+    }
+}
+
+.tags {
+    display: flex;
+    justify-content: space-between;
+    text-align: center;
+    padding: 0 var(--cell-padding);
+    &.tagsbig {
+        >span {
+            width: 47.5%;
+            text-align: center;
+        }
+    }
+
+    >span {
+        border-radius: 3PX;
+        display: block;
+        width: 31%;
+        font-size: 12PX;
+        padding: 6PX 0;
+        background-color: #F8F8F8;
+        color: #999999;
+        border: 1PX solid #F8F8F8;
+        &.active {
+            color: var(--van-primary-color);
+            border-color: var(--van-primary-color);
+            background-color: #E2FFF9;
+        }
+    }
+}

+ 115 - 0
src/page-colexiu/header-top/settting/index.tsx

@@ -0,0 +1,115 @@
+import { defineComponent, onBeforeMount, watch } from "vue";
+import styles from "./index.module.less";
+import iconClose from "../image/close2.svg";
+import { Tab, Tabs } from "vant";
+import { Cell, Switch } from "@varlet/ui";
+import state from "/src/state";
+import { api_closeCamera, api_openCamera } from "/src/helpers/communication";
+import store from 'store'
+
+export default defineComponent({
+	name: "header-settting",
+	emits: ["close"],
+	setup(props, { emit }) {
+		// 设置改变触发
+		watch(state.setting, () => {
+			store.set('musicscoresetting', state.setting)
+		})
+		return () => (
+			<div class={styles["header-settting"]}>
+				<div class={styles.closeBtn} onClick={() => emit("close")}>
+					<img src={iconClose} />
+				</div>
+				<div class={styles.content}>
+					<Tabs border animated>
+						<Tab title="设置">
+							<Cell title="护眼模式">
+								{{
+									extra: () => <Switch v-model={state.setting.eyeProtection}></Switch>,
+								}}
+							</Cell>
+							<Cell title="校音提醒">
+								{{
+									extra: () => <Switch v-model={state.setting.soundEffect}></Switch>,
+								}}
+							</Cell>
+							<Cell title="摄像头">
+								{{
+									extra: () => (
+										<Switch
+											v-model={state.setting.camera}
+											onChange={(value) => {
+												if (value) {
+													api_openCamera();
+												} else {
+													api_closeCamera();
+												}
+											}}
+										></Switch>
+									),
+								}}
+							</Cell>
+							<Cell title="循环播放">
+								{{
+									extra: () => <Switch v-model={state.setting.repeatAutoPlay}></Switch>,
+								}}
+							</Cell>
+							<Cell title="显示指法">
+								{{
+									extra: () => <Switch v-model={state.setting.displayFingering}></Switch>,
+								}}
+							</Cell>
+							<Cell title="显示光标">
+								{{
+									extra: () => <Switch v-model={state.setting.displayCursor}></Switch>,
+								}}
+							</Cell>
+							<Cell title="选择调率"></Cell>
+							<div class={[styles.tags, styles.tagsbig]}>
+								<span class={[styles.tag, state.setting.frequency === 440 && styles.active]} onClick={() => (state.setting.frequency = 440)}>
+									440Hz
+								</span>
+								<span class={[styles.tag, state.setting.frequency === 442 && styles.active]} onClick={() => (state.setting.frequency = 442)}>
+									442Hz
+								</span>
+							</div>
+						</Tab>
+						<Tab title="评测">
+							<Cell title="选择评测难度"></Cell>
+							<div class={styles.tags}>
+								<span
+									class={[styles.tag, state.setting.evaluationDifficulty === "BEGINNER" && styles.active]}
+									onClick={() => (state.setting.evaluationDifficulty = "BEGINNER")}
+								>
+									入门级
+								</span>
+								<span
+									class={[styles.tag, state.setting.evaluationDifficulty === "ADVANCED" && styles.active]}
+									onClick={() => (state.setting.evaluationDifficulty = "ADVANCED")}
+								>
+									进阶级
+								</span>
+								<span
+									class={[styles.tag, state.setting.evaluationDifficulty === "PERFORMER" && styles.active]}
+									onClick={() => (state.setting.evaluationDifficulty = "PERFORMER")}
+								>
+									大师级
+								</span>
+							</div>
+							<Cell title="保存到相册">
+								{{
+									extra: () => <Switch v-model={state.setting.saveToAlbum}></Switch>,
+								}}
+							</Cell>
+							<Cell title="开启伴奏">
+								{{
+									extra: () => <Switch v-model={state.setting.enableAccompaniment}></Switch>,
+								}}
+							</Cell>
+						</Tab>
+					</Tabs>
+				</div>
+			</div>
+		);
+	},
+});

+ 35 - 0
src/page-colexiu/header-top/speed/index.module.less

@@ -0,0 +1,35 @@
+.speedContainer {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  background-color: #fff;
+  padding: 4px;
+}
+
+.btn {
+  width: 30px;
+  height: 30px;
+  border: none;
+  background-color: transparent;
+  padding: 0;
+  font-size: 18px;
+}
+
+.slider {
+  height: 40vh;
+  margin: 10px 0;
+}
+
+.customButton {
+  width: 36px;
+  height: 23px;
+  font-size: 12px;
+  font-weight: bold;
+  border-radius: 14px;
+  box-shadow: 0 5px 10px rgba(0, 0, 0, .1);
+  background: #fff;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}

+ 45 - 0
src/page-colexiu/header-top/speed/index.tsx

@@ -0,0 +1,45 @@
+import { defineComponent, reactive, ref, watch } from "vue";
+import { Button, Slider } from "vant";
+import styles from "./index.module.less";
+import { headImg } from "../image";
+import state, { handleSetSpeed } from "../../../state";
+import { useClickAway } from "@vant/use";
+import { headData } from "..";
+
+export default defineComponent({
+	name: "speed",
+	setup(props) {
+		const speed = reactive({
+			value: state.speed,
+		});
+
+		const minusSpeed = () => {
+			speed.value = Math.max(speed.value - 1, 45);
+		};
+
+		const plusSpeed = () => {
+			speed.value = Math.min(speed.value + 1, 270);
+		};
+
+		watch(() => speed.value, () => {
+			handleSetSpeed(speed.value)
+		})
+
+		const speedRef = ref();
+		useClickAway(speedRef, () => {
+			headData.speedShow = false
+		});
+
+		return () => (
+			<div class={styles.speedContainer} ref={speedRef}>
+				<Button class={styles.btn} icon={headImg("plus.png")} disabled={state.speed == 270} onClick={plusSpeed} />
+				<Slider class={styles.slider} max={270} min={45} vertical v-model={speed.value} reverse>
+					{{
+						button: () => <div class={styles.customButton}>{speed.value}</div>,
+					}}
+				</Slider>
+				<Button class={styles.btn} icon={headImg("minus.png")} disabled={state.speed == 45} onClick={minusSpeed} />
+			</div>
+		);
+	}
+});

+ 25 - 0
src/page-colexiu/header-top/title/index.module.less

@@ -0,0 +1,25 @@
+.container {
+  width: 170px;
+  height: 31px;
+  background: #fff;
+  display: flex;
+  align-items: center;
+  border-radius: 18px;
+  padding: 6px;
+
+  .noticeBar {
+    flex: 1;
+    padding: 0 6px;
+  }
+}
+
+.icon {
+  width: 26px;
+  height: 26px;
+  flex-shrink: 0;
+}
+
+.status {
+  margin-left: auto;
+  flex-shrink: 0;
+}

+ 39 - 0
src/page-colexiu/header-top/title/index.tsx

@@ -0,0 +1,39 @@
+import { defineComponent } from 'vue'
+import { NoticeBar } from 'vant'
+import styles from './index.module.less'
+
+import MusicIcon from '../image/music.png'
+import ArrowIcon from '../image/arrow.svg'
+
+export default defineComponent({
+  name: 'detail-title',
+  props: {
+    text: {
+      type: String,
+      default: ''
+    },
+    rightView: {
+      type: Boolean,
+      default: true,
+    },
+    onClick: {
+      type: Function,
+    } as any
+  },
+  render() {
+    return (
+      <div class={styles.container}>
+        <img class={styles.icon} src={MusicIcon}/>
+        <NoticeBar
+          text={this.text}
+          color="#000"
+          class={styles.noticeBar}
+          background="none"
+        />
+        {this.rightView ? (
+          <img class={styles.status} src={ArrowIcon}/>
+        ) : null}
+      </div>
+    )
+  }
+})

+ 22 - 0
src/page-colexiu/main.ts

@@ -0,0 +1,22 @@
+import 'vant/lib/index.css'
+import "@varlet/ui/es/popup/style/index";
+import "@varlet/ui/es/snackbar/style/index";
+import "@varlet/ui/es/cell/style/index";
+import "@varlet/ui/es/switch/style/index";
+import { createApp } from 'vue'
+import { getRequestHostname } from '../constant/whiteUrl'
+import { initStore } from '../store'
+import '../style.css'
+import App from './App'
+import router from './router'
+import './theme.css'
+
+const platformApi = getRequestHostname()
+initStore({
+    platformApi: platformApi,
+    platformType: platformApi === '/api-student' ? 'STUDENT' : platformApi === '/api-teacher' ? 'TEACHER' : 'WEB',
+    proxy: import.meta.env.DEV ? '/gym' : '',
+})
+
+createApp(App).use(router)
+.mount('#app')

+ 24 - 0
src/page-colexiu/router.ts

@@ -0,0 +1,24 @@
+import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
+import Home from "./detail/index";
+import Notfind from "../view/notfind";
+
+const routes: RouteRecordRaw[] = [
+	{
+		path: "/detail/:id",
+		component: Home,
+	},
+	{
+		path: "/:pathMatch(.*)*",
+		component: Notfind,
+		meta: {
+			title: "404 Not Fund",
+		},
+	},
+];
+
+const router = createRouter({
+	history: createWebHashHistory(),
+	routes,
+});
+
+export default router;

+ 8 - 0
src/page-colexiu/theme.css

@@ -0,0 +1,8 @@
+:root {
+    --van-primary-color: #01c1b5;
+    --color-primary    : #01c1b5;
+}
+
+.vf-StaveSection {
+    display: none;
+}

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

@@ -1,6 +1,5 @@
-import queryString from "query-string";
 import { Skeleton } from "vant";
-import { computed, defineComponent, nextTick, onBeforeMount, onMounted, reactive, Transition, watch } from "vue";
+import { computed, defineComponent, nextTick, onBeforeMount, onMounted, reactive, Transition, watch, watchEffect } from "vue";
 import { useRoute } from "vue-router";
 import { formateTimes } from "../../helpers/formateMusic";
 import Metronome, { metronomeData } from "../../helpers/metronome";
@@ -13,13 +12,13 @@ import { sysMusicScoreAccompanimentQueryPage, sysMusicScoreCategoriesQueryTree }
 import EvaluatModel from "../evaluat-model";
 import HeaderTop from "../header-top";
 import styles from "./index.module.less";
-import { api_setStatusBarVisibility, isSpecialShapedScreen } from "/src/helpers/communication";
+import { api_openCamera, api_setStatusBarVisibility, isSpecialShapedScreen } from "/src/helpers/communication";
 import { getQuery } from "/src/utils/queryString";
 import Evaluating, { evaluatingData } from "/src/view/evaluating";
 import MeasureSpeed from "/src/view/plugins/measure-speed";
-import Selection from "/src/view/selection";
 import { mappingVoicePart, subjectFingering } from "/src/view/fingering/fingering-config";
 import Fingering from "/src/view/fingering";
+import store from 'store'
 
 //特殊教材分类id
 const classids = [1, 30, 97]; // [大雅金唐, 竖笛教程, 声部训练]
@@ -136,6 +135,14 @@ export default defineComponent({
 
 		onBeforeMount(() => {
 			api_setStatusBarVisibility();
+			const settting = store.get('musicscoresetting')
+			if (settting){
+				state.setting = settting
+				if (state.setting.camera){
+					api_openCamera();
+				}
+				// console.log("🚀 ~ settting:", settting)
+			}
 		});
 
 		onMounted(() => {
@@ -190,6 +197,7 @@ export default defineComponent({
 			};
 		});
 
+		// 监听指法显示
 		watch(
 			() => state.setting.displayFingering,
 			() => {

+ 6 - 1
src/page-gym/header-top/settting/index.tsx

@@ -1,15 +1,20 @@
-import { defineComponent } from "vue";
+import { defineComponent, onBeforeMount, watch } from "vue";
 import styles from "./index.module.less";
 import iconClose from "../image/close2.svg";
 import { Tab, Tabs } from "vant";
 import { Cell, Switch } from "@varlet/ui";
 import state from "/src/state";
 import { api_closeCamera, api_openCamera } from "/src/helpers/communication";
+import store from 'store'
 
 export default defineComponent({
 	name: "header-settting",
 	emits: ["close"],
 	setup(props, { emit }) {
+		// 设置改变触发
+		watch(state.setting, () => {
+			store.set('musicscoresetting', state.setting)
+		})
 		return () => (
 			<div class={styles["header-settting"]}>
 				<div class={styles.closeBtn} onClick={() => emit("close")}>

部分文件因为文件数量过多而无法显示