Browse Source

曲谱列表

liushengqiang 2 years ago
parent
commit
02199d5ab9

+ 8 - 0
src/helpers/communication.ts

@@ -142,3 +142,11 @@ export const api_cloudLoading = () => {
 		},
 	});
 };
+
+
+/** 销毁云教练 */
+export const api_cloudDestroy = () =>{
+	postMessage({
+		api: 'cloudDestroy'
+	})
+}

+ 21 - 17
src/helpers/formateMusic.ts

@@ -22,6 +22,7 @@ export const getFixTime = (speed: number) => {
 	// 	// 音频制作问题仅2拍不重复
 	// 	numerator = numerator === 2 ? 4 : numerator;
 	// }
+	// console.log('state.isOpenMetronome', state.isOpenMetronome)
 	return state.isOpenMetronome ? (60 / speed) * formatBeatUnit(beatUnit) * (numerator / denominator) : 0;
 };
 
@@ -650,11 +651,12 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 	const _notes = [] as any[];
 
 	if (state.gradualTimes) {
-		if (["12280"].includes(detailId) && ["24"].includes(partIndex)) {
-			state.gradualTimes["8"] = "00:25:63";
-			state.gradualTimes["66"] = "01:53:35";
-			state.gradualTimes["90"] = "02:41:40";
-		}
+		// 管乐迷
+		// if (["12280"].includes(detailId) && ["24"].includes(partIndex)) {
+		// 	state.gradualTimes["8"] = "00:25:63";
+		// 	state.gradualTimes["66"] = "01:53:35";
+		// 	state.gradualTimes["90"] = "02:41:40";
+		// }
 		console.log("合奏速度", state.gradual, state.gradualTimes);
 	}
 
@@ -774,9 +776,10 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			// console.log({...iterator}, i)
 			const { RealValue: vRealValue, Denominator: vDenominator } = formatDuration(iterator.currentMeasure.activeTimeSignature, iterator.currentMeasure.duration);
 			let { wholeValue, numerator, denominator, realValue: NoteRealValue } = note.length;
-			if (i === 0 && ["2670"].includes(detailId)) {
-				NoteRealValue = 0.03125;
-			}
+			// 管乐迷
+			// if (i === 0 && ["2670"].includes(detailId)) {
+			// 	NoteRealValue = 0.03125;
+			// }
 			if (isDouble && currentTime > 0) {
 				if (currentTime != NoteRealValue) {
 					console.log(`小节 ${note.sourceMeasure.MeasureNumberXML} 替换: noteLength: ${NoteRealValue}, 最小: ${currentTime}`);
@@ -784,15 +787,16 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				}
 			}
 			// note.sourceMeasure.MeasureNumberXML === 8 && console.error(`小节 ${note.sourceMeasure.MeasureNumberXML}`, NoteRealValue)
-			if (["12667", "12673"].includes(detailId)) {
-				if (isMutileSubject && currentTimes[i + 1] > 0 && NoteRealValue > currentTimes[i + 1]) {
-					NoteRealValue = currentTimes[i + 1];
-				}
-			}
-			if (["12673"].includes(detailId) && ["22"].includes(partIndex) && i == 208) {
-				console.log(note.sourceMeasure.MeasureNumberXML, i);
-				NoteRealValue = 0.125;
-			}
+			// 管乐迷
+			// if (["12667", "12673"].includes(detailId)) {
+			// 	if (isMutileSubject && currentTimes[i + 1] > 0 && NoteRealValue > currentTimes[i + 1]) {
+			// 		NoteRealValue = currentTimes[i + 1];
+			// 	}
+			// }
+			// if (["12673"].includes(detailId) && ["22"].includes(partIndex) && i == 208) {
+			// 	console.log(note.sourceMeasure.MeasureNumberXML, i);
+			// 	NoteRealValue = 0.125;
+			// }
 			let relativeTime = usetime;
 			// 速度不能为0 此处的速度应该是按照设置的速度而不是校准后的速度,否则mp3速度不对
 			let beatSpeed = (state.isSpecialBookCategory ? measureSpeed : baseSpeed) || 1;

+ 5 - 0
src/page-gym/api.ts

@@ -33,3 +33,8 @@ export const sysMusicScoreCategoriesQueryTree = (enable = false) => {
 		},
 	});
 };
+
+/** 获取曲谱列表 */
+export const sysMusicScoreQueryPage2 = (params: any) => {
+	return request.get(`/sysMusicScore/queryPage2`, {params})
+}

+ 27 - 17
src/page-gym/detail/index.tsx

@@ -18,10 +18,11 @@ 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'
+import store from "store";
 
 //特殊教材分类id
-const classids = [1, 30, 97]; // [大雅金唐, 竖笛教程, 声部训练]
+const classIds = [1, 30, 97]; // [大雅金唐, 竖笛教程, 声部训练]
+const classKey = "sysMusicScoreCategoriesList";
 export default defineComponent({
 	name: "music-list",
 	setup() {
@@ -35,6 +36,7 @@ export default defineComponent({
 		const detailData = reactive({
 			isLoading: true,
 			paddingLeft: "",
+			classList: [],
 		});
 		const getAPPData = async () => {
 			const screenData = await isSpecialShapedScreen();
@@ -53,31 +55,33 @@ export default defineComponent({
 		/** 获取曲谱数据 */
 		const getMusicInfo = (res: any) => {
 			const index = query["part-index"] ? parseInt(query["part-index"] as string) : 0;
-			const musicInfo = res.data[index];
+			const musicInfo = res.data[index] ? res.data[index] : res.data[0];
 			// console.log("🚀 ~ musicInfo:", musicInfo);
 			setState(musicInfo, index);
 			setCustom();
 			detailData.isLoading = false;
+			state.partListNames = res.data.map((n: any) => n.track);
 		};
-		const getCategorId = (arr: any[], key = "sysMusicScoreCategoriesList"): any[] => {
+		const getCategorId = (arr: any[]): 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]));
+				if (Array.isArray(arr[i][classKey])) {
+					list.push(...getCategorId(arr[i][classKey]));
 				}
 			}
 			return list;
 		};
-		const getIds = (arr: any[], key = "sysMusicScoreCategoriesList"): any[] => {
+		const getIds = (arr: any[], _classids?: any[]): any[] => {
+			const classids = _classids ? _classids : classIds;
 			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]));
+					ids.push(arr[i].id, ...getCategorId(arr[i][classKey]));
 				} else {
-					ids.push(...getIds(arr[i][key]));
+					ids.push(...getIds(arr[i][classKey], classids));
 				}
 			}
 			return ids;
@@ -85,6 +89,7 @@ export default defineComponent({
 		/** 获取分类数据 */
 		const getCategory = (res: any) => {
 			console.log("特殊曲谱分类ids:");
+			detailData.classList = res.data;
 			console.log(getIds(res.data).toString());
 		};
 
@@ -125,6 +130,11 @@ export default defineComponent({
 
 			// 设置指法
 			state.fingeringInfo = subjectFingering(state.subjectId);
+
+			//小曲目 不需要计算音乐的节拍器
+			if (getIds(detailData.classList, [41]).toString().includes(data.parentCategoriesId)) {
+				state.isOpenMetronome = false;
+			}
 		};
 
 		const setCustom = () => {
@@ -135,10 +145,10 @@ export default defineComponent({
 
 		onBeforeMount(() => {
 			api_setStatusBarVisibility();
-			const settting = store.get('musicscoresetting')
-			if (settting){
-				state.setting = settting
-				if (state.setting.camera){
+			const settting = store.get("musicscoresetting");
+			if (settting) {
+				state.setting = settting;
+				if (state.setting.camera) {
 					api_openCamera();
 				}
 				// console.log("🚀 ~ settting:", settting)
@@ -213,11 +223,11 @@ export default defineComponent({
 			}
 		);
 		onMounted(() => {
-			window.addEventListener('resize', resetMusicScore)
-		})
+			window.addEventListener("resize", resetMusicScore);
+		});
 		onBeforeUnmount(() => {
-			window.removeEventListener('resize', resetMusicScore)
-		})
+			window.removeEventListener("resize", resetMusicScore);
+		});
 		return () => (
 			<div class={[styles.detail, state.setting.camera && styles.opencamera]} style={{ paddingLeft: detailData.paddingLeft }}>
 				{!state.musicRendered && (

BIN
src/page-gym/header-top/image/icon-vip.png


+ 8 - 3
src/page-gym/header-top/index.tsx

@@ -12,6 +12,7 @@ import Speed from "./speed";
 import { evaluatingData, handleStartEvaluat } from "/src/view/evaluating";
 import { Popup } from "@varlet/ui";
 import Settting from "./settting";
+import MusciList from "../musci-list";
 
 export const headData = reactive({
 	speedShow: false,
@@ -22,6 +23,7 @@ export default defineComponent({
 	setup() {
 		const headerData = reactive({
 			settingMode: false,
+			listShow: false,
 		});
 		const headRef = ref();
 
@@ -34,7 +36,7 @@ export default defineComponent({
 				<div class={styles.back}>
 					<img src={iconBack} />
 				</div>
-				<Title text={state.examSongName} />
+				<Title onClick={() => (headerData.listShow = true)} text={state.examSongName} />
 
 				<div class={styles.headRight}>
 					<div class={styles.btn} id="tips-step-2" onClick={toggleEvaluat}>
@@ -114,8 +116,11 @@ export default defineComponent({
 					</div>
 				</div>
 
-				<Popup teleport="body" defaultStyle={false} v-model:show={headerData.settingMode}>
-					<Settting onClose={() => headerData.settingMode = false} />
+				<Popup v-model:show={headerData.settingMode} teleport="body" defaultStyle={false} >
+					<Settting onClose={() => (headerData.settingMode = false)} />
+				</Popup>
+				<Popup class="musicListPopup" v-model:show={headerData.listShow} teleport="body" defaultStyle={false} position="left">
+					<MusciList onClose={() => (headerData.listShow = false)} />
 				</Popup>
 			</div>
 		);

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

@@ -22,7 +22,7 @@ export default defineComponent({
   },
   render() {
     return (
-      <div class={styles.container}>
+      <div class={styles.container} onClick={this.$props.onClick}>
         <img class={styles.icon} src={MusicIcon}/>
         <NoticeBar
           text={this.text}

+ 53 - 0
src/page-gym/musci-list/choosePartName/index.module.less

@@ -0,0 +1,53 @@
+.container {
+  min-width: 260px;
+  width: 50vw;
+  max-height: 500px;
+  border-radius: 8px;
+  padding: 10px;
+  background-color: #fff;
+
+  .top {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-size: 16px;
+
+    .title {
+      position: relative;
+      padding-left: 8px;
+
+      &::before {
+        content: '';
+        position: absolute;
+        left: 0;
+        top: 10%;
+        height: 80%;
+        width: 4px;
+        background-color: var(--van-primary-color);
+        border-radius: 4px;
+      }
+    }
+  }
+
+  .picker {
+    padding: 20px 0;
+
+    :global {
+      .van-picker-column__item {
+        font-size: 16px;
+      }
+    }
+  }
+
+  .button {
+    width: 50%;
+    height: 40px;
+    margin: 0 auto;
+
+    :global {
+      .van-button__text {
+        font-size: 16px;
+      }
+    }
+  }
+}

+ 39 - 0
src/page-gym/musci-list/choosePartName/index.tsx

@@ -0,0 +1,39 @@
+import { computed, defineComponent, ref } from "vue";
+import { Picker, Button, PickerOption, Icon } from "vant";
+import styles from "./index.module.less";
+import state from "/src/state";
+
+export default defineComponent({
+	name: "choosePartName",
+	emits: ["close"],
+	setup(props, { emit }) {
+		const selectIndex = ref(0);
+		const columns = computed(() => {
+			return state.partListNames.map((n: any, index: number) => {
+				return {
+					text: n,
+					value: index,
+				};
+			});
+		});
+		return () => (
+			<div class={styles.container}>
+				<div class={styles.top}>
+					<div class={styles.title}>请选择您练习的分谱</div>
+					<Icon name="cross" size={24} onClick={() => emit("close")} />
+				</div>
+				<Picker
+					class={styles.picker}
+					showToolbar={false}
+					columns={columns.value}
+					onChange={(row) => {
+						selectIndex.value = row.selectedValues[0];
+					}}
+				/>
+				<Button class={styles.button} type="primary" round block onClick={() => emit("close", selectIndex.value)}>
+					确定
+				</Button>
+			</div>
+		);
+	},
+});

BIN
src/page-gym/musci-list/icon-back.png


+ 68 - 0
src/page-gym/musci-list/index.module.less

@@ -0,0 +1,68 @@
+:global{
+  .musicListPopup{
+    overflow: initial !important;
+  }
+}
+.wrap {
+  position: relative;
+  min-width: 269px;
+  width: 40vw;
+  height: 100vh;
+  background-color: #fff;
+  border-radius: 0 16px 16px 0;
+  overflow-x: hidden;
+  overflow-y: auto;
+
+  &::-webkit-scrollbar {
+    width: 0;
+    display: none;
+  }
+}
+.closeBtn{
+  position: absolute;
+  right: -30px;
+  top: 50%;
+  margin-top: -20px;
+  width: 30px;
+  height: 40px;
+}
+
+.item {
+  display: flex;
+  align-items: center;
+  height: 46px;
+  border-bottom: 1px solid #F1F1F1;
+  padding-left: 40px;
+  padding-right: 16px;
+  font-size: 14px;
+  &:active{
+    background-color: rgba(0, 0, 0, .1);
+  }
+  &.active{
+    pointer-events: none;
+    :global{
+      .van-notice-bar{
+        color: var(--van-primary-color);
+      }
+    }
+  }
+  .icon {
+    width: 20px;
+    flex-shrink: 0;
+  }
+
+  .vip {
+    height: 14px;
+    margin-left: 6px;
+    flex-shrink: 0;
+  }
+  :global{
+    .van-notice-bar{
+      margin-left: 6px;
+      background-color: transparent;
+      padding: 0;
+      flex: 1;
+      color: #444444;
+    }
+  }
+}

+ 123 - 0
src/page-gym/musci-list/index.tsx

@@ -0,0 +1,123 @@
+import { List, NoticeBar } from "vant";
+import { defineComponent, reactive } from "vue";
+import styles from "./index.module.less";
+import iconMusic from "../header-top/image/music.png";
+import iconVip from "../header-top/image/icon-vip.png";
+import { sysMusicScoreQueryPage2 } from "../api";
+import state from "/src/state";
+import { api_cloudDestroy } from "/src/helpers/communication";
+import queryString from "query-string";
+import { storeData } from "/src/store";
+import { Popup } from "@varlet/ui";
+import ChoosePartName from "./choosePartName";
+import { useRoute } from "vue-router";
+import iconBack from "./icon-back.png";
+
+export default defineComponent({
+	name: "detail-list",
+	emits: ["close"],
+	setup(props, { emit }) {
+		const route = useRoute();
+		const musicData = reactive({
+			loading: false,
+			finish: false,
+			list: [] as any[],
+			pages: {
+				page: 1,
+				rows: 20,
+			},
+			row: {} as any,
+			selectedPartIndex: 0,
+			vipShow: false,
+			partShow: false,
+		});
+		//获取曲谱列表
+		const getData = async () => {
+			musicData.loading = true;
+			try {
+				const res = await sysMusicScoreQueryPage2({
+					...musicData.pages,
+					clientType: "SMART_PRACTICE",
+					subjectId: state.subjectId === 0 ? undefined : state.subjectId,
+					categoriesId: state.categoriesId,
+					search: undefined,
+				});
+				if (Array.isArray(res?.data?.rows) && res.data.rows.length) {
+					musicData.list = musicData.list.concat(res.data.rows);
+					musicData.pages.page = musicData.pages.page + 1;
+					if (res.data.rows.length < musicData.pages.rows) {
+						musicData.finish = true;
+					}
+				} else {
+					musicData.finish = true;
+				}
+			} catch (error) {}
+			musicData.loading = false;
+		};
+
+		/**  */
+		const handleClick = (item: any) => {
+			musicData.row = { ...item };
+			// 选择声部
+			if (state.partListNames.length > 1) {
+				musicData.partShow = true;
+				return;
+			}
+			openWevView();
+		};
+		const openWevView = () => {
+			const needIds = musicData.row.rankIds?.split(",")?.filter(Boolean) || [];
+			const sid = String(storeData.user?.memberRankSettingId);
+			if (needIds.length && !needIds.includes(sid) && storeData.platformType === "STUDENT") {
+				musicData.vipShow = true;
+				return;
+			}
+			// 销毁云教练
+			api_cloudDestroy();
+
+			const searchStr = queryString.stringify({
+				...queryString.parse(location.search),
+				_t: new Date().getTime(),
+			});
+			const hashSearchStr = queryString.stringify({
+				...route.query,
+				"part-index": musicData.selectedPartIndex,
+			});
+			const nextUrl = `${location.origin + location.pathname}?${searchStr}#/detail/${musicData.row.id}?${hashSearchStr}`;
+			location.replace(nextUrl);
+		};
+		return () => (
+			<>
+				<img class={styles.closeBtn} src={iconBack} onClick={() => emit('close')} />
+				<div class={styles.wrap}>
+					<List
+						loading={musicData.loading}
+						finished={musicData.finish}
+						onLoad={() => {
+							getData();
+						}}
+					>
+						{musicData.list.map((item) => (
+							<div class={[styles.item, state.examSongId == item.id + "" && styles.active]} onClick={() => handleClick(item)}>
+								<img class={styles.icon} src={iconMusic} />
+								<img class={styles.vip} style={{ display: item.rankIds ? "" : "none" }} src={iconVip} />
+								<NoticeBar text={item.name} />
+							</div>
+						))}
+					</List>
+					<Popup v-model:show={musicData.partShow} teleport="body" defaultStyle={false}>
+						<ChoosePartName
+							onClose={(index: number) => {
+								if (index) {
+									musicData.selectedPartIndex = index;
+									openWevView();
+								}
+								musicData.partShow = false;
+							}}
+						/>
+					</Popup>
+				</div>
+			</>
+		);
+	},
+});

+ 1 - 0
src/store.ts

@@ -9,6 +9,7 @@ type IUser = {
 	subjectNames?: string
 	/** 头像 */
 	avatar?: string
+	memberRankSettingId?: number
 };
 type IStatus = "init" | "login" | "logout" | "error";
 type IPlatformType = "STUDENT" | "TEACHER" | "WEB";

+ 6 - 6
src/view/fingering/fingering-config.ts

@@ -123,12 +123,12 @@ export const subjectFingering = (subjectId: number): IFingering => {
 				direction: "vertical",
 				width: "3rem",
 			};
-		case 23: // 打击乐
-			return {
-				name: "small-drum",
-				direction: "transverse",
-				height: "1.6rem",
-			};
+		// case 23: // 打击乐
+		// 	return {
+		// 		name: "small-drum",
+		// 		direction: "transverse",
+		// 		height: "1.6rem",
+		// 	};
 		default:
 			return {};
 	}

+ 2 - 1
vite.config.ts

@@ -50,7 +50,8 @@ export default defineConfig({
 		port: 3000,
 		proxy: {
 			"^/gym/.*": {
-				target: "https://mstutest.dayaedu.com",
+				// target: "https://mstutest.dayaedu.com",
+				target: "https://online.dayaedu.com",
 				changeOrigin: true,
 				rewrite: (path) => path.replace(/^\/gym/, ""),
 			},