|  | @@ -1,35 +1,13 @@
 | 
	
		
			
				|  |  | -import {
 | 
	
		
			
				|  |  | -	PropType,
 | 
	
		
			
				|  |  | -	computed,
 | 
	
		
			
				|  |  | -	defineComponent,
 | 
	
		
			
				|  |  | -	nextTick,
 | 
	
		
			
				|  |  | -	onBeforeMount,
 | 
	
		
			
				|  |  | -	onMounted,
 | 
	
		
			
				|  |  | -	onUnmounted,
 | 
	
		
			
				|  |  | -	reactive,
 | 
	
		
			
				|  |  | -	ref,
 | 
	
		
			
				|  |  | -} from "vue";
 | 
	
		
			
				|  |  | +import { PropType, computed, defineComponent, nextTick, onBeforeMount, onMounted, onUnmounted, reactive, ref } from "vue";
 | 
	
		
			
				|  |  |  import styles from "./index.module.less";
 | 
	
		
			
				|  |  |  import icons from "./image/icons.json";
 | 
	
		
			
				|  |  | -import { FIGNER_INSTRUMENT_DATA, IFIGNER_INSTRUMENT_Note } from "/src/view/figner-preview";
 | 
	
		
			
				|  |  | -import {
 | 
	
		
			
				|  |  | -	ITypeFingering,
 | 
	
		
			
				|  |  | -	IVocals,
 | 
	
		
			
				|  |  | -	getFingeringConfig,
 | 
	
		
			
				|  |  | -	mappingVoicePart,
 | 
	
		
			
				|  |  | -	subjectFingering,
 | 
	
		
			
				|  |  | -} from "/src/view/fingering/fingering-config";
 | 
	
		
			
				|  |  | +import { FIGNER_INSTRUMENT_DATA, FIGNER_INSTRUMENT_REALKEY, IFIGNER_INSTRUMENT_Note } from "/src/view/figner-preview";
 | 
	
		
			
				|  |  | +import { ITypeFingering, IVocals, getFingeringConfig, mappingVoicePart, subjectFingering } from "/src/view/fingering/fingering-config";
 | 
	
		
			
				|  |  |  import { Howl } from "howler";
 | 
	
		
			
				|  |  |  import { storeData } from "/src/store";
 | 
	
		
			
				|  |  | -import {
 | 
	
		
			
				|  |  | -	api_back,
 | 
	
		
			
				|  |  | -	api_cloudLoading,
 | 
	
		
			
				|  |  | -	api_setRequestedOrientation,
 | 
	
		
			
				|  |  | -	api_setStatusBarVisibility,
 | 
	
		
			
				|  |  | -	isSpecialShapedScreen,
 | 
	
		
			
				|  |  | -} from "/src/helpers/communication";
 | 
	
		
			
				|  |  | +import { api_back, api_cloudLoading, api_setRequestedOrientation, api_setStatusBarVisibility, isSpecialShapedScreen } from "/src/helpers/communication";
 | 
	
		
			
				|  |  |  import Hammer from "hammerjs";
 | 
	
		
			
				|  |  | -import { Button, Icon, Loading, Popup, Progress, Space } from "vant";
 | 
	
		
			
				|  |  | +import { Button, Icon, Loading, Popover, Popup, Progress, Space } from "vant";
 | 
	
		
			
				|  |  |  import GuideIndex from "./guide/guide-index";
 | 
	
		
			
				|  |  |  import { getQuery } from "/src/utils/queryString";
 | 
	
		
			
				|  |  |  import { browser } from "/src/utils";
 | 
	
	
		
			
				|  | @@ -39,663 +17,1118 @@ import icon_loading_img from "./image/icon_loading_img.png";
 | 
	
		
			
				|  |  |  import state, { IPlatform } from "/src/state";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  export default defineComponent({
 | 
	
		
			
				|  |  | -	name: "viewFigner",
 | 
	
		
			
				|  |  | -	emits: ["close"],
 | 
	
		
			
				|  |  | -	props: {
 | 
	
		
			
				|  |  | -		show: {
 | 
	
		
			
				|  |  | -			type: Boolean,
 | 
	
		
			
				|  |  | -			default: true,
 | 
	
		
			
				|  |  | -		},
 | 
	
		
			
				|  |  | -		isComponent: {
 | 
	
		
			
				|  |  | -			type: Boolean,
 | 
	
		
			
				|  |  | -			default: false,
 | 
	
		
			
				|  |  | -		},
 | 
	
		
			
				|  |  | -		subject: {
 | 
	
		
			
				|  |  | -			type: String as PropType<IVocals>,
 | 
	
		
			
				|  |  | -			default: "",
 | 
	
		
			
				|  |  | -		},
 | 
	
		
			
				|  |  | -	},
 | 
	
		
			
				|  |  | -	setup(props, { emit }) {
 | 
	
		
			
				|  |  | -		const query = getQuery();
 | 
	
		
			
				|  |  | -		const browsInfo = browser();
 | 
	
		
			
				|  |  | -		const code = mappingVoicePart(query.code, "INSTRUMENT");
 | 
	
		
			
				|  |  | -		const subject = props.isComponent ? props.subject || "pan-flute" : code || "pan-flute";
 | 
	
		
			
				|  |  | -		const data = reactive({
 | 
	
		
			
				|  |  | -			loading: true,
 | 
	
		
			
				|  |  | -			subject: subject as any,
 | 
	
		
			
				|  |  | -			realKey: 0,
 | 
	
		
			
				|  |  | -			notes: [] as IFIGNER_INSTRUMENT_Note[],
 | 
	
		
			
				|  |  | -			tones: [] as IFIGNER_INSTRUMENT_Note[],
 | 
	
		
			
				|  |  | -			activeTone: {} as IFIGNER_INSTRUMENT_Note,
 | 
	
		
			
				|  |  | -			popupActiveTone: {} as IFIGNER_INSTRUMENT_Note,
 | 
	
		
			
				|  |  | -			activeToneName: "",
 | 
	
		
			
				|  |  | -			soundFonts: {} as any,
 | 
	
		
			
				|  |  | -			viewIndex: 0,
 | 
	
		
			
				|  |  | -			viewTotal: 1,
 | 
	
		
			
				|  |  | -			noteAudio: null as unknown as Howl,
 | 
	
		
			
				|  |  | -			transform: {
 | 
	
		
			
				|  |  | -				scale: 1,
 | 
	
		
			
				|  |  | -				x: 0,
 | 
	
		
			
				|  |  | -				y: 0,
 | 
	
		
			
				|  |  | -				startScale: 1,
 | 
	
		
			
				|  |  | -				startX: 0,
 | 
	
		
			
				|  |  | -				startY: 0,
 | 
	
		
			
				|  |  | -				transition: "",
 | 
	
		
			
				|  |  | -			},
 | 
	
		
			
				|  |  | -			tipShow: false,
 | 
	
		
			
				|  |  | -			tips: [] as IFIGNER_INSTRUMENT_Note[],
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -			tnoteShow: false,
 | 
	
		
			
				|  |  | -			loadingSoundFonts: true,
 | 
	
		
			
				|  |  | -			loadingSoundProgress: 0,
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -			huaweiPad: navigator?.userAgent?.includes("UAWEIVRD-W09") ? true : false,
 | 
	
		
			
				|  |  | -			paddingTop: "",
 | 
	
		
			
				|  |  | -			paddingLeft: "",
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | -		const fingerData = reactive({
 | 
	
		
			
				|  |  | -			relationshipIndex: 0,
 | 
	
		
			
				|  |  | -			subject: null as unknown as ITypeFingering,
 | 
	
		
			
				|  |  | -			fingeringInfo: subjectFingering(data.subject),
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | -		if (!props.isComponent) {
 | 
	
		
			
				|  |  | -			state.fingeringInfo = fingerData.fingeringInfo;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		const getAPPData = async (type: "top" | "left") => {
 | 
	
		
			
				|  |  | -			const screenData = await isSpecialShapedScreen();
 | 
	
		
			
				|  |  | -			if (screenData?.content) {
 | 
	
		
			
				|  |  | -				// console.log("🚀 ~ screenData:", screenData.content);
 | 
	
		
			
				|  |  | -				const { isSpecialShapedScreen, notchHeight } = screenData.content;
 | 
	
		
			
				|  |  | -				if (isSpecialShapedScreen) {
 | 
	
		
			
				|  |  | -					if (type === "top") {
 | 
	
		
			
				|  |  | -						data.paddingTop = 25 + "px";
 | 
	
		
			
				|  |  | -					}
 | 
	
		
			
				|  |  | -					if (type === "left") {
 | 
	
		
			
				|  |  | -						data.paddingLeft = 25 + "px";
 | 
	
		
			
				|  |  | -					}
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		const getHeadTop = () => {
 | 
	
		
			
				|  |  | -			if (!browsInfo.ios && fingerData.fingeringInfo.orientation === 1) {
 | 
	
		
			
				|  |  | -				getAPPData("top");
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			if (!browsInfo.ios && fingerData.fingeringInfo.orientation === 0) {
 | 
	
		
			
				|  |  | -				getAPPData("left");
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		const getNotes = () => {
 | 
	
		
			
				|  |  | -			const fignerData = FIGNER_INSTRUMENT_DATA[data.subject as keyof typeof FIGNER_INSTRUMENT_DATA];
 | 
	
		
			
				|  |  | -			if (fignerData) {
 | 
	
		
			
				|  |  | -				data.tones = fignerData.tones || [];
 | 
	
		
			
				|  |  | -				if (data.tones.length) {
 | 
	
		
			
				|  |  | -					data.activeTone = data.tones[0];
 | 
	
		
			
				|  |  | -					data.popupActiveTone = data.tones[0];
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -				data.tips = fignerData.tips || [];
 | 
	
		
			
				|  |  | -				setNotes();
 | 
	
		
			
				|  |  | -				setTimeout(() => {
 | 
	
		
			
				|  |  | -					data.loading = false;
 | 
	
		
			
				|  |  | -				}, 600);
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -		const setNotes = () => {
 | 
	
		
			
				|  |  | -			const fignerData = FIGNER_INSTRUMENT_DATA[data.subject as keyof typeof FIGNER_INSTRUMENT_DATA];
 | 
	
		
			
				|  |  | -			if (fignerData) {
 | 
	
		
			
				|  |  | -				data.notes = fignerData[`list${data.activeTone.realName || ""}`];
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -		const getFingeringData = async () => {
 | 
	
		
			
				|  |  | -			const subject: any = data.subject + (data.viewIndex === 0 ? "" : data.viewIndex);
 | 
	
		
			
				|  |  | -			console.log("🚀 ~ subject:", subject);
 | 
	
		
			
				|  |  | -			fingerData.subject = await getFingeringConfig(subject);
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -		const createAudio = (url: string) => {
 | 
	
		
			
				|  |  | -			return new Promise((resolve) => {
 | 
	
		
			
				|  |  | -				const noteAudio = new Howl({
 | 
	
		
			
				|  |  | -					src: url,
 | 
	
		
			
				|  |  | -					loop: true,
 | 
	
		
			
				|  |  | -					onload: () => {
 | 
	
		
			
				|  |  | -						resolve(noteAudio);
 | 
	
		
			
				|  |  | -					},
 | 
	
		
			
				|  |  | -				});
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -		const getSounFonts = async () => {
 | 
	
		
			
				|  |  | -			const pathname = /(192|localhost)/.test(location.origin) ? "/" : location.pathname;
 | 
	
		
			
				|  |  | -			data.loadingSoundFonts = true;
 | 
	
		
			
				|  |  | -			data.loadingSoundProgress = 0;
 | 
	
		
			
				|  |  | -			for (let i = 0; i < data.notes.length; i++) {
 | 
	
		
			
				|  |  | -				const note = data.notes[i];
 | 
	
		
			
				|  |  | -				// console.log("🚀 ~ note:", i);
 | 
	
		
			
				|  |  | -				let url = `${pathname}soundfonts/${data.subject}/`;
 | 
	
		
			
				|  |  | -				url += note.realName;
 | 
	
		
			
				|  |  | -				url += ".mp3";
 | 
	
		
			
				|  |  | -				data.soundFonts[note.realKey] = await createAudio(url);
 | 
	
		
			
				|  |  | -				data.loadingSoundProgress = Math.floor(((i + 1) / data.notes.length) * 100);
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			data.loadingSoundProgress = 100;
 | 
	
		
			
				|  |  | -			api_cloudLoading();
 | 
	
		
			
				|  |  | -			data.loadingSoundFonts = false;
 | 
	
		
			
				|  |  | -			// console.log("🚀 ~ data.soundFonts:", data.soundFonts);
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -		onBeforeMount(() => {
 | 
	
		
			
				|  |  | -			getNotes();
 | 
	
		
			
				|  |  | -			if (["pan-flute", "ocarina"].includes(data.subject)) {
 | 
	
		
			
				|  |  | -				data.viewIndex = 1;
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			const o: any = {
 | 
	
		
			
				|  |  | -				"pan-flute": 2,
 | 
	
		
			
				|  |  | -				ocarina: 2,
 | 
	
		
			
				|  |  | -				piccolo: 2,
 | 
	
		
			
				|  |  | -				"hulusi-flute": 2,
 | 
	
		
			
				|  |  | -			};
 | 
	
		
			
				|  |  | -			data.viewTotal = o[data.subject] || 1;
 | 
	
		
			
				|  |  | -			getFingeringData();
 | 
	
		
			
				|  |  | -			getSounFonts();
 | 
	
		
			
				|  |  | -			getHeadTop();
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		const noteClick = (item: IFIGNER_INSTRUMENT_Note) => {
 | 
	
		
			
				|  |  | -			if (data.noteAudio) {
 | 
	
		
			
				|  |  | -				data.noteAudio.stop();
 | 
	
		
			
				|  |  | -				if (data.realKey === item.realKey) {
 | 
	
		
			
				|  |  | -					data.realKey = 0;
 | 
	
		
			
				|  |  | -					data.noteAudio = null as unknown as Howl;
 | 
	
		
			
				|  |  | -					return;
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			data.realKey = item.realKey;
 | 
	
		
			
				|  |  | -			data.noteAudio = data.soundFonts[item.realKey];
 | 
	
		
			
				|  |  | -			data.noteAudio.play();
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -		const handleStop = () => {
 | 
	
		
			
				|  |  | -			if (data.noteAudio) {
 | 
	
		
			
				|  |  | -				data.noteAudio.stop();
 | 
	
		
			
				|  |  | -				data.realKey = 0;
 | 
	
		
			
				|  |  | -				data.noteAudio = null as unknown as Howl;
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		/** 返回 */
 | 
	
		
			
				|  |  | -		const handleBack = () => {
 | 
	
		
			
				|  |  | -			handleStop();
 | 
	
		
			
				|  |  | -			if (props.isComponent) {
 | 
	
		
			
				|  |  | -				console.log("关闭");
 | 
	
		
			
				|  |  | -				emit("close");
 | 
	
		
			
				|  |  | -				return;
 | 
	
		
			
				|  |  | -			} else {
 | 
	
		
			
				|  |  | -				// if (fingerData.fingeringInfo.orientation === 0) {
 | 
	
		
			
				|  |  | -				// 	api_setRequestedOrientation(1);
 | 
	
		
			
				|  |  | -				// }
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			// 不在APP中,
 | 
	
		
			
				|  |  | -			if (!storeData.isApp) {
 | 
	
		
			
				|  |  | -				window.close();
 | 
	
		
			
				|  |  | -				return;
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			api_back();
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		onMounted(() => {
 | 
	
		
			
				|  |  | -			loadElement();
 | 
	
		
			
				|  |  | -			api_setStatusBarVisibility();
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | -		const loadElement = () => {
 | 
	
		
			
				|  |  | -			const fingeringContainer = document.getElementById("fingeringContainer");
 | 
	
		
			
				|  |  | -			// console.log("🚀 ~ fingeringContainer:", fingeringContainer);
 | 
	
		
			
				|  |  | -			const mc = new Hammer.Manager(fingeringContainer as HTMLElement);
 | 
	
		
			
				|  |  | -			mc.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
 | 
	
		
			
				|  |  | -			mc.add(new Hammer.Pinch({ threshold: 0 })).recognizeWith([mc.get("pan")]);
 | 
	
		
			
				|  |  | -			// mc.get("pan").set({ direction: Hammer.DIRECTION_ALL });
 | 
	
		
			
				|  |  | -			// mc.get("pinch").set({ enable: true });
 | 
	
		
			
				|  |  | -			mc.on("panstart pinchstart", function (ev) {
 | 
	
		
			
				|  |  | -				data.transform.transition = "";
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | -			mc.on("panmove pinchmove", function (ev) {
 | 
	
		
			
				|  |  | -				if (ev.type === "pinchmove") {
 | 
	
		
			
				|  |  | -					// console.log("🚀 ~ ev:", ev.type, ev.scale, ev.deltaX, ev.deltaY);
 | 
	
		
			
				|  |  | -					data.transform.scale = ev.scale * data.transform.startScale;
 | 
	
		
			
				|  |  | -					data.transform.x = data.transform.startX + ev.deltaX;
 | 
	
		
			
				|  |  | -					data.transform.y = data.transform.startY + ev.deltaY;
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -				if (ev.type === "panmove") {
 | 
	
		
			
				|  |  | -					// console.log("🚀 ~ ev:", ev.type, ev.deltaX, ev.deltaY);
 | 
	
		
			
				|  |  | -					data.transform.x = data.transform.startX + ev.deltaX;
 | 
	
		
			
				|  |  | -					data.transform.y = data.transform.startY + ev.deltaY;
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | -			//
 | 
	
		
			
				|  |  | -			mc.on("hammer.input", function (ev) {
 | 
	
		
			
				|  |  | -				// console.log("🚀 ~ ev:", ev.type, ev.isFinal);
 | 
	
		
			
				|  |  | -				if (ev.isFinal) {
 | 
	
		
			
				|  |  | -					data.transform.startScale = data.transform.scale;
 | 
	
		
			
				|  |  | -					data.transform.startX = data.transform.x;
 | 
	
		
			
				|  |  | -					data.transform.startY = data.transform.y;
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -		const resetElement = () => {
 | 
	
		
			
				|  |  | -			data.transform.transition = "all 0.3s";
 | 
	
		
			
				|  |  | -			nextTick(() => {
 | 
	
		
			
				|  |  | -				data.transform.scale = 1;
 | 
	
		
			
				|  |  | -				data.transform.x = 0;
 | 
	
		
			
				|  |  | -				data.transform.y = 0;
 | 
	
		
			
				|  |  | -				data.transform.startScale = 1;
 | 
	
		
			
				|  |  | -				data.transform.startX = 0;
 | 
	
		
			
				|  |  | -				data.transform.startY = 0;
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		const pageVisible = usePageVisibility();
 | 
	
		
			
				|  |  | -		watch(
 | 
	
		
			
				|  |  | -			() => pageVisible.value,
 | 
	
		
			
				|  |  | -			(val) => {
 | 
	
		
			
				|  |  | -				if (val === "hidden") {
 | 
	
		
			
				|  |  | -					console.log("页面隐藏停止播放");
 | 
	
		
			
				|  |  | -					handleStop();
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		);
 | 
	
		
			
				|  |  | -		/** 课件播放 */
 | 
	
		
			
				|  |  | -		const changePlay = (res: any) => {
 | 
	
		
			
				|  |  | -			if (res?.data?.api === "setPlayState") {
 | 
	
		
			
				|  |  | -				handleStop();
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		const noteBoxRef = ref();
 | 
	
		
			
				|  |  | -		const scrollNoteBox = (type: "left" | "right") => {
 | 
	
		
			
				|  |  | -			const width = noteBoxRef.value.offsetWidth / 2;
 | 
	
		
			
				|  |  | -			(noteBoxRef.value as unknown as HTMLElement).scrollBy({
 | 
	
		
			
				|  |  | -				left: type === "left" ? -width : width,
 | 
	
		
			
				|  |  | -				behavior: "smooth",
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		/** 滚轮缩放 */
 | 
	
		
			
				|  |  | -		const handleWheel = (e: WheelEvent) => {
 | 
	
		
			
				|  |  | -			e.preventDefault();
 | 
	
		
			
				|  |  | -			if (e.deltaY > 0) {
 | 
	
		
			
				|  |  | -				data.transform.scale -= 0.1;
 | 
	
		
			
				|  |  | -				if (data.transform.scale <= 0.5) {
 | 
	
		
			
				|  |  | -					data.transform.scale = 0.5;
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			} else {
 | 
	
		
			
				|  |  | -				data.transform.scale += 0.1;
 | 
	
		
			
				|  |  | -				if (data.transform.scale >= 2) {
 | 
	
		
			
				|  |  | -					data.transform.scale = 2;
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		onMounted(() => {
 | 
	
		
			
				|  |  | -			window.addEventListener("message", changePlay);
 | 
	
		
			
				|  |  | -			const fingeringContainer = document.getElementById("fingeringContainer");
 | 
	
		
			
				|  |  | -			fingeringContainer?.addEventListener("wheel", handleWheel);
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		onUnmounted(() => {
 | 
	
		
			
				|  |  | -			window.removeEventListener("message", changePlay);
 | 
	
		
			
				|  |  | -			const fingeringContainer = document.getElementById("fingeringContainer");
 | 
	
		
			
				|  |  | -			fingeringContainer?.removeEventListener("wheel", handleWheel);
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		const containerBox = computed(() => {
 | 
	
		
			
				|  |  | -			if (state.platform === IPlatform.PC || query.modelType) {
 | 
	
		
			
				|  |  | -				return {
 | 
	
		
			
				|  |  | -					paddingTop: "1rem",
 | 
	
		
			
				|  |  | -					paddingBottom: "",
 | 
	
		
			
				|  |  | -				};
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			if (data.subject === "hulusi-flute") {
 | 
	
		
			
				|  |  | -				return {
 | 
	
		
			
				|  |  | -					paddingTop: "3.1rem",
 | 
	
		
			
				|  |  | -					paddingBottom: ".8rem",
 | 
	
		
			
				|  |  | -				};
 | 
	
		
			
				|  |  | -			} else if (data.subject === "piccolo") {
 | 
	
		
			
				|  |  | -				return {
 | 
	
		
			
				|  |  | -					paddingTop: "4rem",
 | 
	
		
			
				|  |  | -					paddingBottom: ".8rem",
 | 
	
		
			
				|  |  | -				};
 | 
	
		
			
				|  |  | -			} else if (data.subject === "pan-flute") {
 | 
	
		
			
				|  |  | -				return {
 | 
	
		
			
				|  |  | -					paddingTop: "0",
 | 
	
		
			
				|  |  | -					paddingBottom: "0",
 | 
	
		
			
				|  |  | -				};
 | 
	
		
			
				|  |  | -			} else if (data.subject === "ocarina") {
 | 
	
		
			
				|  |  | -				return {
 | 
	
		
			
				|  |  | -					paddingTop: "1.2rem",
 | 
	
		
			
				|  |  | -					paddingBottom: "0",
 | 
	
		
			
				|  |  | -				};
 | 
	
		
			
				|  |  | -			} else if (data.subject === "melodica") {
 | 
	
		
			
				|  |  | -				return {
 | 
	
		
			
				|  |  | -					paddingTop: "2.8rem",
 | 
	
		
			
				|  |  | -					paddingBottom: "1.8rem",
 | 
	
		
			
				|  |  | -				};
 | 
	
		
			
				|  |  | -			} else {
 | 
	
		
			
				|  |  | -				return {
 | 
	
		
			
				|  |  | -					paddingTop: "",
 | 
	
		
			
				|  |  | -					paddingBottom: "",
 | 
	
		
			
				|  |  | -				};
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | -		return () => {
 | 
	
		
			
				|  |  | -			const relationship = fingerData.subject?.relationship?.[data.realKey] || [];
 | 
	
		
			
				|  |  | -			const rs: number[] = Array.isArray(relationship[1])
 | 
	
		
			
				|  |  | -				? relationship[fingerData.relationshipIndex]
 | 
	
		
			
				|  |  | -				: relationship;
 | 
	
		
			
				|  |  | -			const canTizhi = Array.isArray(relationship[1]);
 | 
	
		
			
				|  |  | -			return (
 | 
	
		
			
				|  |  | -				<div
 | 
	
		
			
				|  |  | -					class={[
 | 
	
		
			
				|  |  | -						styles.fingerBox,
 | 
	
		
			
				|  |  | -						state.platform !== IPlatform.PC &&
 | 
	
		
			
				|  |  | -						!query.modelType &&
 | 
	
		
			
				|  |  | -						fingerData.fingeringInfo.orientation === 1
 | 
	
		
			
				|  |  | -							? styles.fingerBottom
 | 
	
		
			
				|  |  | -							: styles.fingerRight,
 | 
	
		
			
				|  |  | -					]}
 | 
	
		
			
				|  |  | -				>
 | 
	
		
			
				|  |  | -					<div
 | 
	
		
			
				|  |  | -						class={styles.head}
 | 
	
		
			
				|  |  | -						style={{
 | 
	
		
			
				|  |  | -							paddingTop: data.paddingTop ? data.paddingTop : "",
 | 
	
		
			
				|  |  | -							paddingLeft: data.paddingLeft ? data.paddingLeft : "",
 | 
	
		
			
				|  |  | -						}}
 | 
	
		
			
				|  |  | -					>
 | 
	
		
			
				|  |  | -						<div class={styles.left}>
 | 
	
		
			
				|  |  | -							<button class={[styles.backBtn]} onClick={() => handleBack()}>
 | 
	
		
			
				|  |  | -								<img src={icons.icon_back} />
 | 
	
		
			
				|  |  | -							</button>
 | 
	
		
			
				|  |  | -							{data.subject !== "melodica" && (
 | 
	
		
			
				|  |  | -								<div
 | 
	
		
			
				|  |  | -									class={styles.baseBtn}
 | 
	
		
			
				|  |  | -									onClick={() => {
 | 
	
		
			
				|  |  | -										data.viewIndex++;
 | 
	
		
			
				|  |  | -										if (data.viewIndex > data.viewTotal) {
 | 
	
		
			
				|  |  | -											if (["pan-flute", "ocarina"].includes(data.subject)) {
 | 
	
		
			
				|  |  | -												data.viewIndex = 1;
 | 
	
		
			
				|  |  | -											} else {
 | 
	
		
			
				|  |  | -												data.viewIndex = 0;
 | 
	
		
			
				|  |  | -											}
 | 
	
		
			
				|  |  | -										}
 | 
	
		
			
				|  |  | -										getFingeringData();
 | 
	
		
			
				|  |  | -									}}
 | 
	
		
			
				|  |  | -								>
 | 
	
		
			
				|  |  | -									<img src={icons.icon_toggle} />
 | 
	
		
			
				|  |  | -									<span>切换视图</span>
 | 
	
		
			
				|  |  | -								</div>
 | 
	
		
			
				|  |  | -							)}
 | 
	
		
			
				|  |  | -						</div>
 | 
	
		
			
				|  |  | -						<div class={styles.rightBtn}>
 | 
	
		
			
				|  |  | -							<div class={styles.baseBtn} onClick={() => resetElement()}>
 | 
	
		
			
				|  |  | -								<img src={icons.icon_2_0} />
 | 
	
		
			
				|  |  | -								<span>还原</span>
 | 
	
		
			
				|  |  | -							</div>
 | 
	
		
			
				|  |  | -							<div
 | 
	
		
			
				|  |  | -								class={styles.baseBtn}
 | 
	
		
			
				|  |  | -								onClick={() => {
 | 
	
		
			
				|  |  | -									resetElement();
 | 
	
		
			
				|  |  | -									data.tipShow = !data.tipShow;
 | 
	
		
			
				|  |  | -								}}
 | 
	
		
			
				|  |  | -							>
 | 
	
		
			
				|  |  | -								<img src={icons.icon_2_1} />
 | 
	
		
			
				|  |  | -								<span>使用说明</span>
 | 
	
		
			
				|  |  | -							</div>
 | 
	
		
			
				|  |  | -						</div>
 | 
	
		
			
				|  |  | -					</div>
 | 
	
		
			
				|  |  | -					<div class={styles.fingerContent}>
 | 
	
		
			
				|  |  | -						<div class={styles.wrapFinger}>
 | 
	
		
			
				|  |  | -							<div
 | 
	
		
			
				|  |  | -								id="fingeringContainer"
 | 
	
		
			
				|  |  | -								class={styles.boxFinger}
 | 
	
		
			
				|  |  | -								style={{
 | 
	
		
			
				|  |  | -									paddingTop: containerBox.value.paddingTop,
 | 
	
		
			
				|  |  | -									paddingBottom: containerBox.value.paddingBottom,
 | 
	
		
			
				|  |  | -								}}
 | 
	
		
			
				|  |  | -							>
 | 
	
		
			
				|  |  | -								<div
 | 
	
		
			
				|  |  | -									style={{
 | 
	
		
			
				|  |  | -										transform: `translate3d(${data.transform.x}px,${data.transform.y}px,0px) scale(${data.transform.scale})`,
 | 
	
		
			
				|  |  | -										transition: data.transform.transition,
 | 
	
		
			
				|  |  | -									}}
 | 
	
		
			
				|  |  | -									class={[styles.fingeringContainer]}
 | 
	
		
			
				|  |  | -								>
 | 
	
		
			
				|  |  | -									<div class={styles.imgs}>
 | 
	
		
			
				|  |  | -										<img src={fingerData.subject?.json?.full} />
 | 
	
		
			
				|  |  | -										{rs.map((key: number | string, index: number) => {
 | 
	
		
			
				|  |  | -											const nk: string =
 | 
	
		
			
				|  |  | -												typeof key === "string" ? key.replace("active-", "") : String(key);
 | 
	
		
			
				|  |  | -											return <img data-index={nk} src={fingerData.subject?.json?.[nk]} />;
 | 
	
		
			
				|  |  | -										})}
 | 
	
		
			
				|  |  | -										<div
 | 
	
		
			
				|  |  | -											style={{ left: data.viewIndex == 2 ? "0" : "64%" }}
 | 
	
		
			
				|  |  | -											class={[styles.tizhi, canTizhi && styles.canDisplay]}
 | 
	
		
			
				|  |  | -											onClick={() =>
 | 
	
		
			
				|  |  | -												(fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)
 | 
	
		
			
				|  |  | -											}
 | 
	
		
			
				|  |  | -										>
 | 
	
		
			
				|  |  | -											替指
 | 
	
		
			
				|  |  | -										</div>
 | 
	
		
			
				|  |  | -										<div
 | 
	
		
			
				|  |  | -											id="finger-note-2"
 | 
	
		
			
				|  |  | -											style={{ left: "50%", transform: "translateX(-50%)" }}
 | 
	
		
			
				|  |  | -											class={styles.tizhi}
 | 
	
		
			
				|  |  | -											onClick={() =>
 | 
	
		
			
				|  |  | -												(fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)
 | 
	
		
			
				|  |  | -											}
 | 
	
		
			
				|  |  | -										></div>
 | 
	
		
			
				|  |  | -									</div>
 | 
	
		
			
				|  |  | -								</div>
 | 
	
		
			
				|  |  | -							</div>
 | 
	
		
			
				|  |  | -							<div
 | 
	
		
			
				|  |  | -								class={styles.notes}
 | 
	
		
			
				|  |  | -								style={{
 | 
	
		
			
				|  |  | -									paddingLeft: data.paddingLeft ? data.paddingLeft : "",
 | 
	
		
			
				|  |  | -								}}
 | 
	
		
			
				|  |  | -							>
 | 
	
		
			
				|  |  | -								<Button class={styles.noteBtn} onClick={() => scrollNoteBox("left")}>
 | 
	
		
			
				|  |  | -									<Icon name="arrow-left" />
 | 
	
		
			
				|  |  | -								</Button>
 | 
	
		
			
				|  |  | -								<div
 | 
	
		
			
				|  |  | -									class={[
 | 
	
		
			
				|  |  | -										styles.noteContent,
 | 
	
		
			
				|  |  | -										browsInfo.ios ? "" : styles.noteContentWrap,
 | 
	
		
			
				|  |  | -										data.huaweiPad && styles.huaweiPad,
 | 
	
		
			
				|  |  | -									]}
 | 
	
		
			
				|  |  | -								>
 | 
	
		
			
				|  |  | -									<div ref={noteBoxRef} class={styles.noteBox}>
 | 
	
		
			
				|  |  | -										{data.notes.map((note: IFIGNER_INSTRUMENT_Note, index: number) => {
 | 
	
		
			
				|  |  | -											const steps = new Array(Math.abs(note.step)).fill(1);
 | 
	
		
			
				|  |  | -											return (
 | 
	
		
			
				|  |  | -												<div
 | 
	
		
			
				|  |  | -													id={index == 0 ? "finger-note-0" : ""}
 | 
	
		
			
				|  |  | -													draggable={false}
 | 
	
		
			
				|  |  | -													class={styles.note}
 | 
	
		
			
				|  |  | -													onClick={() => noteClick(note)}
 | 
	
		
			
				|  |  | -												>
 | 
	
		
			
				|  |  | -													{data.realKey === note.realKey ? (
 | 
	
		
			
				|  |  | -														<img draggable={false} src={icons.icon_btn_ylow} />
 | 
	
		
			
				|  |  | -													) : (
 | 
	
		
			
				|  |  | -														<img draggable={false} src={icons.icon_btn_blue} />
 | 
	
		
			
				|  |  | -													)}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -													<div
 | 
	
		
			
				|  |  | -														class={[styles.noteKey, data.realKey === note.realKey && styles.keyActive]}
 | 
	
		
			
				|  |  | -													>
 | 
	
		
			
				|  |  | -														{note.step > 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -														<div class={styles.noteName}>
 | 
	
		
			
				|  |  | -															<sup>{note.mark && (note.mark === "rise" ? "#" : "b")}</sup>
 | 
	
		
			
				|  |  | -															{note.key}
 | 
	
		
			
				|  |  | -														</div>
 | 
	
		
			
				|  |  | -														{note.step < 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
 | 
	
		
			
				|  |  | -													</div>
 | 
	
		
			
				|  |  | -												</div>
 | 
	
		
			
				|  |  | -											);
 | 
	
		
			
				|  |  | -										})}
 | 
	
		
			
				|  |  | -									</div>
 | 
	
		
			
				|  |  | -								</div>
 | 
	
		
			
				|  |  | -								<Button class={styles.noteBtn} onClick={() => scrollNoteBox("right")}>
 | 
	
		
			
				|  |  | -									<Icon name="arrow" />
 | 
	
		
			
				|  |  | -								</Button>
 | 
	
		
			
				|  |  | -							</div>
 | 
	
		
			
				|  |  | -						</div>
 | 
	
		
			
				|  |  | -						<div class={[styles.tips, data.tipShow ? "" : styles.tipHidden]}>
 | 
	
		
			
				|  |  | -							<div class={styles.tipTitle}>
 | 
	
		
			
				|  |  | -								<div class={styles.tipTitleName}>{fingerData.fingeringInfo.code}使用说明</div>
 | 
	
		
			
				|  |  | -								<Button class={styles.tipClose} onClick={() => (data.tipShow = false)}>
 | 
	
		
			
				|  |  | -									<Icon name="cross" size={19} color="#fff" />
 | 
	
		
			
				|  |  | -								</Button>
 | 
	
		
			
				|  |  | -							</div>
 | 
	
		
			
				|  |  | -							<div class={styles.iconBook}></div>
 | 
	
		
			
				|  |  | -							<div class={styles.tipContentbox}>
 | 
	
		
			
				|  |  | -								<div class={styles.tipContent}>
 | 
	
		
			
				|  |  | -									{data.tips.map((tip, tipIndex) => (
 | 
	
		
			
				|  |  | -										<div class={styles.tipItem}>
 | 
	
		
			
				|  |  | -											<div class={styles.iconWrap}>
 | 
	
		
			
				|  |  | -												<div class={styles.tipItemIcon}>{tipIndex + 1}</div>
 | 
	
		
			
				|  |  | -											</div>
 | 
	
		
			
				|  |  | -											<div>
 | 
	
		
			
				|  |  | -												{tip.name}: {tip.realName}
 | 
	
		
			
				|  |  | -											</div>
 | 
	
		
			
				|  |  | -										</div>
 | 
	
		
			
				|  |  | -									))}
 | 
	
		
			
				|  |  | -								</div>
 | 
	
		
			
				|  |  | -							</div>
 | 
	
		
			
				|  |  | -						</div>
 | 
	
		
			
				|  |  | -						{data.loadingSoundFonts && (
 | 
	
		
			
				|  |  | -							<div class={styles.loading}>
 | 
	
		
			
				|  |  | -								<div class={styles.loadingWrap}>
 | 
	
		
			
				|  |  | -									<img class={styles.loadingIcon} src={icon_loading_img} />
 | 
	
		
			
				|  |  | -									<Progress percentage={data.loadingSoundProgress} />
 | 
	
		
			
				|  |  | -									<div class={styles.loadingTip}>加载中,请稍后…</div>
 | 
	
		
			
				|  |  | -								</div>
 | 
	
		
			
				|  |  | -							</div>
 | 
	
		
			
				|  |  | -						)}
 | 
	
		
			
				|  |  | -					</div>
 | 
	
		
			
				|  |  | -					{!!data.tones.length && (
 | 
	
		
			
				|  |  | -						<>
 | 
	
		
			
				|  |  | -							{fingerData.fingeringInfo.name == "hulusi-flute" ? (
 | 
	
		
			
				|  |  | -								<div
 | 
	
		
			
				|  |  | -									id="finger-note-1"
 | 
	
		
			
				|  |  | -									class={[styles.toggleBtn, styles.toggleBtnhulusi]}
 | 
	
		
			
				|  |  | -									onClick={() => (data.tnoteShow = true)}
 | 
	
		
			
				|  |  | -								>
 | 
	
		
			
				|  |  | -									<div>
 | 
	
		
			
				|  |  | -										全按作
 | 
	
		
			
				|  |  | -										<div class={[styles.noteKey]}>
 | 
	
		
			
				|  |  | -											{data.activeTone.step > 0 ? <span class={styles.dot}></span> : null}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -											<div class={styles.noteName}>
 | 
	
		
			
				|  |  | -												<sup>
 | 
	
		
			
				|  |  | -													{data.activeTone.mark && (data.activeTone.mark === "rise" ? "#" : "b")}
 | 
	
		
			
				|  |  | -												</sup>
 | 
	
		
			
				|  |  | -												{data.activeTone.key}
 | 
	
		
			
				|  |  | -											</div>
 | 
	
		
			
				|  |  | -											{data.activeTone.step < 0 ? <span class={styles.dot}></span> : null}
 | 
	
		
			
				|  |  | -										</div>
 | 
	
		
			
				|  |  | -									</div>
 | 
	
		
			
				|  |  | -									<img src={icons.icon_arrow} />
 | 
	
		
			
				|  |  | -								</div>
 | 
	
		
			
				|  |  | -							) : (
 | 
	
		
			
				|  |  | -								<div id="finger-note-1" class={styles.toggleBtn} onClick={() => (data.tnoteShow = true)}>
 | 
	
		
			
				|  |  | -									<div style={{ marginTop: "-4px" }}>
 | 
	
		
			
				|  |  | -										<sup>{data.activeTone.mark && (data.activeTone.mark === "rise" ? "#" : "b")}</sup>
 | 
	
		
			
				|  |  | -										{data.activeTone.name}
 | 
	
		
			
				|  |  | -									</div>
 | 
	
		
			
				|  |  | -									调
 | 
	
		
			
				|  |  | -									<img src={icons.icon_arrow} />
 | 
	
		
			
				|  |  | -								</div>
 | 
	
		
			
				|  |  | -							)}
 | 
	
		
			
				|  |  | -						</>
 | 
	
		
			
				|  |  | -					)}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -					<Popup
 | 
	
		
			
				|  |  | -						class="tonePopup"
 | 
	
		
			
				|  |  | -						v-model:show={data.tnoteShow}
 | 
	
		
			
				|  |  | -						position={
 | 
	
		
			
				|  |  | -							state.platform !== IPlatform.PC &&
 | 
	
		
			
				|  |  | -							!query.modelType &&
 | 
	
		
			
				|  |  | -							fingerData.fingeringInfo.orientation === 1
 | 
	
		
			
				|  |  | -								? "bottom"
 | 
	
		
			
				|  |  | -								: "right"
 | 
	
		
			
				|  |  | -						}
 | 
	
		
			
				|  |  | -					>
 | 
	
		
			
				|  |  | -						<div class={styles.tones}>
 | 
	
		
			
				|  |  | -							<div class={styles.toneTitle}>
 | 
	
		
			
				|  |  | -								<div class={styles.tipTitleName}>移调</div>
 | 
	
		
			
				|  |  | -								<Button class={styles.tipClose} onClick={() => (data.tnoteShow = false)}>
 | 
	
		
			
				|  |  | -									<Icon name="cross" size={19} color="#fff" />
 | 
	
		
			
				|  |  | -								</Button>
 | 
	
		
			
				|  |  | -							</div>
 | 
	
		
			
				|  |  | -							<div class={styles.tipContentbox}>
 | 
	
		
			
				|  |  | -								<div class={styles.tipContent}>
 | 
	
		
			
				|  |  | -									<div class={styles.tipWrap}>
 | 
	
		
			
				|  |  | -										<Space size={0} class={styles.toneContent}>
 | 
	
		
			
				|  |  | -											{data.tones.map((tone: IFIGNER_INSTRUMENT_Note) => {
 | 
	
		
			
				|  |  | -												const steps = new Array(Math.abs(tone.step)).fill(1);
 | 
	
		
			
				|  |  | -												return (
 | 
	
		
			
				|  |  | -													<Button
 | 
	
		
			
				|  |  | -														class={[fingerData.fingeringInfo.name == "hulusi-flute" && styles.hulusiBtn]}
 | 
	
		
			
				|  |  | -														round
 | 
	
		
			
				|  |  | -														plain
 | 
	
		
			
				|  |  | -														type={
 | 
	
		
			
				|  |  | -															data.popupActiveTone.realName === tone.realName ? "primary" : "default"
 | 
	
		
			
				|  |  | -														}
 | 
	
		
			
				|  |  | -														onClick={() => {
 | 
	
		
			
				|  |  | -															data.popupActiveTone = tone;
 | 
	
		
			
				|  |  | -															setNotes();
 | 
	
		
			
				|  |  | -														}}
 | 
	
		
			
				|  |  | -													>
 | 
	
		
			
				|  |  | -														{fingerData.fingeringInfo.name == "hulusi-flute" ? (
 | 
	
		
			
				|  |  | -															<div style={{ display: "flex", alignItems: "center" }}>
 | 
	
		
			
				|  |  | -																全按作
 | 
	
		
			
				|  |  | -																<div class={[styles.noteKey, styles.hulusiNoteKey]}>
 | 
	
		
			
				|  |  | -																	{tone.step > 0 ? <span class={styles.dot}></span> : null}
 | 
	
		
			
				|  |  | -																	<div class={styles.noteName} style={{ fontSize: "0.25rem" }}>
 | 
	
		
			
				|  |  | -																		<sup>{tone.mark && (tone.mark === "rise" ? "#" : "b")}</sup>
 | 
	
		
			
				|  |  | -																		{tone.key}
 | 
	
		
			
				|  |  | -																	</div>
 | 
	
		
			
				|  |  | -																	{tone.step < 0 ? <span class={styles.dot}></span> : null}
 | 
	
		
			
				|  |  | -																</div>
 | 
	
		
			
				|  |  | -															</div>
 | 
	
		
			
				|  |  | -														) : (
 | 
	
		
			
				|  |  | -															<div class={styles.noteName}>
 | 
	
		
			
				|  |  | -																<sup>{tone.mark && (tone.mark === "rise" ? "#" : "b")}</sup>
 | 
	
		
			
				|  |  | -																{tone.name}
 | 
	
		
			
				|  |  | -															</div>
 | 
	
		
			
				|  |  | -														)}
 | 
	
		
			
				|  |  | -													</Button>
 | 
	
		
			
				|  |  | -												);
 | 
	
		
			
				|  |  | -											})}
 | 
	
		
			
				|  |  | -										</Space>
 | 
	
		
			
				|  |  | -									</div>
 | 
	
		
			
				|  |  | -									<div class={styles.toneAction}>
 | 
	
		
			
				|  |  | -										<img onClick={() => (data.tnoteShow = false)} src={icons.icon_action_cancel} />
 | 
	
		
			
				|  |  | -										<img
 | 
	
		
			
				|  |  | -											onClick={() => {
 | 
	
		
			
				|  |  | -												data.activeTone = data.popupActiveTone;
 | 
	
		
			
				|  |  | -												setNotes();
 | 
	
		
			
				|  |  | -												data.tnoteShow = false;
 | 
	
		
			
				|  |  | -											}}
 | 
	
		
			
				|  |  | -											src={icons.icon_action_confirm}
 | 
	
		
			
				|  |  | -										/>
 | 
	
		
			
				|  |  | -									</div>
 | 
	
		
			
				|  |  | -								</div>
 | 
	
		
			
				|  |  | -							</div>
 | 
	
		
			
				|  |  | -						</div>
 | 
	
		
			
				|  |  | -					</Popup>
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -					{props.show && !data.loading && !data.loadingSoundFonts && (
 | 
	
		
			
				|  |  | -						<GuideIndex showGuide={false} list={["finger"]} />
 | 
	
		
			
				|  |  | -					)}
 | 
	
		
			
				|  |  | -				</div>
 | 
	
		
			
				|  |  | -			);
 | 
	
		
			
				|  |  | -		};
 | 
	
		
			
				|  |  | -	},
 | 
	
		
			
				|  |  | +  name: "viewFigner",
 | 
	
		
			
				|  |  | +  emits: ["close"],
 | 
	
		
			
				|  |  | +  props: {
 | 
	
		
			
				|  |  | +    show: {
 | 
	
		
			
				|  |  | +      type: Boolean,
 | 
	
		
			
				|  |  | +      default: true,
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    isComponent: {
 | 
	
		
			
				|  |  | +      type: Boolean,
 | 
	
		
			
				|  |  | +      default: false,
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    subject: {
 | 
	
		
			
				|  |  | +      type: String as PropType<IVocals>,
 | 
	
		
			
				|  |  | +      default: "",
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  setup(props, { emit }) {
 | 
	
		
			
				|  |  | +    const query = getQuery();
 | 
	
		
			
				|  |  | +    const browsInfo = browser();
 | 
	
		
			
				|  |  | +    const code = mappingVoicePart(query.code, "INSTRUMENT");
 | 
	
		
			
				|  |  | +    const subject = props.isComponent ? props.subject || "pan-flute" : code || "pan-flute";
 | 
	
		
			
				|  |  | +    const data = reactive({
 | 
	
		
			
				|  |  | +      loading: true,
 | 
	
		
			
				|  |  | +      subject: subject as any,
 | 
	
		
			
				|  |  | +      realKey: 0,
 | 
	
		
			
				|  |  | +      notes: [] as IFIGNER_INSTRUMENT_Note[],
 | 
	
		
			
				|  |  | +      tones: [] as IFIGNER_INSTRUMENT_Note[],
 | 
	
		
			
				|  |  | +      activeTone: {} as IFIGNER_INSTRUMENT_Note,
 | 
	
		
			
				|  |  | +      popupActiveTone: {} as IFIGNER_INSTRUMENT_Note,
 | 
	
		
			
				|  |  | +      activeToneName: "",
 | 
	
		
			
				|  |  | +      soundFonts: {} as any,
 | 
	
		
			
				|  |  | +      viewIndex: 0,
 | 
	
		
			
				|  |  | +      viewTotal: 1,
 | 
	
		
			
				|  |  | +      noteAudio: null as unknown as Howl,
 | 
	
		
			
				|  |  | +      transform: {
 | 
	
		
			
				|  |  | +        scale: 1,
 | 
	
		
			
				|  |  | +        x: 0,
 | 
	
		
			
				|  |  | +        y: 0,
 | 
	
		
			
				|  |  | +        startScale: 1,
 | 
	
		
			
				|  |  | +        startX: 0,
 | 
	
		
			
				|  |  | +        startY: 0,
 | 
	
		
			
				|  |  | +        transition: "",
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      tipShow: false,
 | 
	
		
			
				|  |  | +      tips: [] as IFIGNER_INSTRUMENT_Note[],
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      tnoteShow: false,
 | 
	
		
			
				|  |  | +      loadingSoundFonts: true,
 | 
	
		
			
				|  |  | +      loadingSoundProgress: 0,
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      huaweiPad: navigator?.userAgent?.includes("UAWEIVRD-W09") ? true : false,
 | 
	
		
			
				|  |  | +      paddingTop: "",
 | 
	
		
			
				|  |  | +      paddingLeft: "",
 | 
	
		
			
				|  |  | +      subjects: [
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +          text: "排箫",
 | 
	
		
			
				|  |  | +          value: "pan-flute",
 | 
	
		
			
				|  |  | +          className: "",
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +          text: "陶笛",
 | 
	
		
			
				|  |  | +          value: "ocarina",
 | 
	
		
			
				|  |  | +          className: "",
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +          text: "葫芦丝",
 | 
	
		
			
				|  |  | +          value: "hulusi-flute",
 | 
	
		
			
				|  |  | +          className: "",
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +          text: "竖笛",
 | 
	
		
			
				|  |  | +          value: "piccolo",
 | 
	
		
			
				|  |  | +          className: "",
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +          text: "口风琴",
 | 
	
		
			
				|  |  | +          value: "melodica",
 | 
	
		
			
				|  |  | +          className: "",
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +      ],
 | 
	
		
			
				|  |  | +      fingeringModeList: [
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +          text: "指法模式",
 | 
	
		
			
				|  |  | +          value: "fingeringMode",
 | 
	
		
			
				|  |  | +          icon: icons.icon_click,
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +          text: "听音模式",
 | 
	
		
			
				|  |  | +          value: "listenMode",
 | 
	
		
			
				|  |  | +          icon: icons.icon_listen,
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +          text: "音阶模式",
 | 
	
		
			
				|  |  | +          value: "scaleMode",
 | 
	
		
			
				|  |  | +          icon: icons.icon_mode,
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +      ],
 | 
	
		
			
				|  |  | +      fingeringMode: query.listenMode || ("scaleMode" as "fingeringMode" | "listenMode" | "scaleMode"), // 模式
 | 
	
		
			
				|  |  | +      noteType: "all" as "#c" | "all", // 音调
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    const fingerData = reactive({
 | 
	
		
			
				|  |  | +      relationshipIndex: 0,
 | 
	
		
			
				|  |  | +      subject: null as unknown as ITypeFingering,
 | 
	
		
			
				|  |  | +      fingeringInfo: subjectFingering(data.subject),
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    if (!props.isComponent) {
 | 
	
		
			
				|  |  | +      state.fingeringInfo = fingerData.fingeringInfo;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const getAPPData = async (type: "top" | "left") => {
 | 
	
		
			
				|  |  | +      const screenData = await isSpecialShapedScreen();
 | 
	
		
			
				|  |  | +      if (screenData?.content) {
 | 
	
		
			
				|  |  | +        // console.log("🚀 ~ screenData:", screenData.content);
 | 
	
		
			
				|  |  | +        const { isSpecialShapedScreen, notchHeight } = screenData.content;
 | 
	
		
			
				|  |  | +        if (isSpecialShapedScreen) {
 | 
	
		
			
				|  |  | +          if (type === "top") {
 | 
	
		
			
				|  |  | +            data.paddingTop = 25 + "px";
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          if (type === "left") {
 | 
	
		
			
				|  |  | +            data.paddingLeft = 25 + "px";
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const getHeadTop = () => {
 | 
	
		
			
				|  |  | +      if (!browsInfo.ios && fingerData.fingeringInfo.orientation === 1) {
 | 
	
		
			
				|  |  | +        getAPPData("top");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (!browsInfo.ios && fingerData.fingeringInfo.orientation === 0) {
 | 
	
		
			
				|  |  | +        getAPPData("left");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const getNotes = () => {
 | 
	
		
			
				|  |  | +      const fignerData = FIGNER_INSTRUMENT_DATA[data.subject as keyof typeof FIGNER_INSTRUMENT_DATA];
 | 
	
		
			
				|  |  | +      if (fignerData) {
 | 
	
		
			
				|  |  | +        data.tones = fignerData.tones || [];
 | 
	
		
			
				|  |  | +        if (data.tones.length) {
 | 
	
		
			
				|  |  | +          data.activeTone = data.tones[0];
 | 
	
		
			
				|  |  | +          data.popupActiveTone = data.tones[0];
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        data.tips = fignerData.tips || [];
 | 
	
		
			
				|  |  | +        setNotes();
 | 
	
		
			
				|  |  | +        setTimeout(() => {
 | 
	
		
			
				|  |  | +          data.loading = false;
 | 
	
		
			
				|  |  | +        }, 600);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    const setNotes = () => {
 | 
	
		
			
				|  |  | +      const fignerData = FIGNER_INSTRUMENT_DATA[data.subject as keyof typeof FIGNER_INSTRUMENT_DATA];
 | 
	
		
			
				|  |  | +      if (fignerData) {
 | 
	
		
			
				|  |  | +        const tempNotes = fignerData[`list${data.activeTone.realName || ""}`];
 | 
	
		
			
				|  |  | +        const appendNote: any = [];
 | 
	
		
			
				|  |  | +        tempNotes.forEach((note: any) => {
 | 
	
		
			
				|  |  | +          note.steps = new Array(Math.abs(note.step)).fill(1);
 | 
	
		
			
				|  |  | +          if (FIGNER_INSTRUMENT_REALKEY.includes(note.realKey)) {
 | 
	
		
			
				|  |  | +            appendNote.push(note);
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +        // 判断是音符状态
 | 
	
		
			
				|  |  | +        data.notes = data.noteType === "#c" ? appendNote : tempNotes;
 | 
	
		
			
				|  |  | +        // data.notes = fignerData[`list${data.activeTone.realName || ""}`];
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    const getFingeringData = async () => {
 | 
	
		
			
				|  |  | +      const subject: any = data.subject + (data.viewIndex === 0 ? "" : data.viewIndex);
 | 
	
		
			
				|  |  | +      console.log("🚀 ~ subject:", subject);
 | 
	
		
			
				|  |  | +      fingerData.subject = await getFingeringConfig(subject);
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    const createAudio = (url: string) => {
 | 
	
		
			
				|  |  | +      return new Promise((resolve) => {
 | 
	
		
			
				|  |  | +        const noteAudio = new Howl({
 | 
	
		
			
				|  |  | +          src: url,
 | 
	
		
			
				|  |  | +          loop: true,
 | 
	
		
			
				|  |  | +          onload: () => {
 | 
	
		
			
				|  |  | +            resolve(noteAudio);
 | 
	
		
			
				|  |  | +          },
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    const getSounFonts = async () => {
 | 
	
		
			
				|  |  | +      const pathname = /(192|localhost)/.test(location.origin) ? "/" : location.pathname;
 | 
	
		
			
				|  |  | +      data.loadingSoundFonts = true;
 | 
	
		
			
				|  |  | +      data.loadingSoundProgress = 0;
 | 
	
		
			
				|  |  | +      for (let i = 0; i < data.notes.length; i++) {
 | 
	
		
			
				|  |  | +        const note = data.notes[i];
 | 
	
		
			
				|  |  | +        // console.log("🚀 ~ note:", i);
 | 
	
		
			
				|  |  | +        let url = `${pathname}soundfonts/${data.subject}/`;
 | 
	
		
			
				|  |  | +        url += note.realName;
 | 
	
		
			
				|  |  | +        url += ".mp3";
 | 
	
		
			
				|  |  | +        data.soundFonts[note.realKey] = await createAudio(url);
 | 
	
		
			
				|  |  | +        data.loadingSoundProgress = Math.floor(((i + 1) / data.notes.length) * 100);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      data.loadingSoundProgress = 100;
 | 
	
		
			
				|  |  | +      api_cloudLoading();
 | 
	
		
			
				|  |  | +      data.loadingSoundFonts = false;
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const selectSubjectType = (subject: string) => {
 | 
	
		
			
				|  |  | +      data.subjects.forEach((item: any) => {
 | 
	
		
			
				|  |  | +        if (item.value === subject) {
 | 
	
		
			
				|  |  | +          item.className = styles.selected;
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          item.className = "";
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 切换当前模式
 | 
	
		
			
				|  |  | +    const onChangeFingeringModel = () => {
 | 
	
		
			
				|  |  | +      //
 | 
	
		
			
				|  |  | +      if (playAction.listenLock) return;
 | 
	
		
			
				|  |  | +      if (playAction.showAnswerLoading) return;
 | 
	
		
			
				|  |  | +      if (data.fingeringMode === "scaleMode") {
 | 
	
		
			
				|  |  | +        data.fingeringMode = "listenMode";
 | 
	
		
			
				|  |  | +      } else if (data.fingeringMode === "listenMode") {
 | 
	
		
			
				|  |  | +        data.fingeringMode = "fingeringMode";
 | 
	
		
			
				|  |  | +      } else if (data.fingeringMode === "fingeringMode") {
 | 
	
		
			
				|  |  | +        data.fingeringMode = "scaleMode";
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      resetMode(true, 0);
 | 
	
		
			
				|  |  | +      __init();
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const __init = () => {
 | 
	
		
			
				|  |  | +      getNotes();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      selectSubjectType(data.subject);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if (data.fingeringMode === "fingeringMode") {
 | 
	
		
			
				|  |  | +        console.log(data.subject, "data.subject");
 | 
	
		
			
				|  |  | +        if (data.subject === "pan-flute") {
 | 
	
		
			
				|  |  | +          data.viewIndex = 3;
 | 
	
		
			
				|  |  | +        } else if (["pan-flute", "ocarina", "melodica"].includes(data.subject)) {
 | 
	
		
			
				|  |  | +          data.viewIndex = 1;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        if (["pan-flute", "ocarina"].includes(data.subject)) {
 | 
	
		
			
				|  |  | +          data.viewIndex = 1;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      const o: any = {
 | 
	
		
			
				|  |  | +        "pan-flute": 2,
 | 
	
		
			
				|  |  | +        ocarina: 2,
 | 
	
		
			
				|  |  | +        piccolo: 2,
 | 
	
		
			
				|  |  | +        "hulusi-flute": 2,
 | 
	
		
			
				|  |  | +      };
 | 
	
		
			
				|  |  | +      data.viewTotal = o[data.subject] || 1;
 | 
	
		
			
				|  |  | +      getFingeringData();
 | 
	
		
			
				|  |  | +      getSounFonts();
 | 
	
		
			
				|  |  | +      getHeadTop();
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    onBeforeMount(() => {
 | 
	
		
			
				|  |  | +      state.platform = query.platform?.toLocaleUpperCase() || "";
 | 
	
		
			
				|  |  | +      __init();
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 播放音频
 | 
	
		
			
				|  |  | +     * @param item 音频节点
 | 
	
		
			
				|  |  | +     * @param showNote 是否显示对应的指法
 | 
	
		
			
				|  |  | +     * @returns
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    const noteClick = (item: IFIGNER_INSTRUMENT_Note, showNote = true) => {
 | 
	
		
			
				|  |  | +      // console.log('音高', item.realKey)
 | 
	
		
			
				|  |  | +      if (data.noteAudio) {
 | 
	
		
			
				|  |  | +        data.noteAudio.stop();
 | 
	
		
			
				|  |  | +        if (data.realKey === item.realKey) {
 | 
	
		
			
				|  |  | +          data.realKey = 0;
 | 
	
		
			
				|  |  | +          data.noteAudio = null as unknown as Howl;
 | 
	
		
			
				|  |  | +          return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (showNote) {
 | 
	
		
			
				|  |  | +        data.realKey = item.realKey;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      data.noteAudio = data.soundFonts[item.realKey];
 | 
	
		
			
				|  |  | +      data.noteAudio.play();
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    const handleStop = () => {
 | 
	
		
			
				|  |  | +      if (data.noteAudio) {
 | 
	
		
			
				|  |  | +        data.noteAudio.stop();
 | 
	
		
			
				|  |  | +        data.realKey = 0;
 | 
	
		
			
				|  |  | +        data.noteAudio = null as unknown as Howl;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /** 返回 */
 | 
	
		
			
				|  |  | +    const handleBack = () => {
 | 
	
		
			
				|  |  | +      // platform: query.platform,
 | 
	
		
			
				|  |  | +      handleStop();
 | 
	
		
			
				|  |  | +      if (props.isComponent) {
 | 
	
		
			
				|  |  | +        console.log("关闭");
 | 
	
		
			
				|  |  | +        emit("close");
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +      } else if (state.platform === IPlatform.PC) {
 | 
	
		
			
				|  |  | +        // 老师端,首页
 | 
	
		
			
				|  |  | +        window.parent.postMessage(
 | 
	
		
			
				|  |  | +          {
 | 
	
		
			
				|  |  | +            api: "iframe_exit",
 | 
	
		
			
				|  |  | +          },
 | 
	
		
			
				|  |  | +          "*"
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +        // if (fingerData.fingeringInfo.orientation === 0) {
 | 
	
		
			
				|  |  | +        // 	api_setRequestedOrientation(1);
 | 
	
		
			
				|  |  | +        // }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      // 不在APP中,
 | 
	
		
			
				|  |  | +      if (!storeData.isApp) {
 | 
	
		
			
				|  |  | +        window.close();
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      api_back();
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    onMounted(() => {
 | 
	
		
			
				|  |  | +      loadElement();
 | 
	
		
			
				|  |  | +      api_setStatusBarVisibility();
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    const loadElement = () => {
 | 
	
		
			
				|  |  | +      const fingeringContainer = document.getElementById("fingeringContainer");
 | 
	
		
			
				|  |  | +      // console.log("🚀 ~ fingeringContainer:", fingeringContainer);
 | 
	
		
			
				|  |  | +      const mc = new Hammer.Manager(fingeringContainer as HTMLElement);
 | 
	
		
			
				|  |  | +      mc.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
 | 
	
		
			
				|  |  | +      mc.add(new Hammer.Pinch({ threshold: 0 })).recognizeWith([mc.get("pan")]);
 | 
	
		
			
				|  |  | +      // mc.get("pan").set({ direction: Hammer.DIRECTION_ALL });
 | 
	
		
			
				|  |  | +      // mc.get("pinch").set({ enable: true });
 | 
	
		
			
				|  |  | +      mc.on("panstart pinchstart", function (ev) {
 | 
	
		
			
				|  |  | +        data.transform.transition = "";
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +      mc.on("panmove pinchmove", function (ev) {
 | 
	
		
			
				|  |  | +        if (ev.type === "pinchmove") {
 | 
	
		
			
				|  |  | +          // console.log("🚀 ~ ev:", ev.type, ev.scale, ev.deltaX, ev.deltaY);
 | 
	
		
			
				|  |  | +          data.transform.scale = ev.scale * data.transform.startScale;
 | 
	
		
			
				|  |  | +          data.transform.x = data.transform.startX + ev.deltaX;
 | 
	
		
			
				|  |  | +          data.transform.y = data.transform.startY + ev.deltaY;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (ev.type === "panmove") {
 | 
	
		
			
				|  |  | +          // console.log("🚀 ~ ev:", ev.type, ev.deltaX, ev.deltaY);
 | 
	
		
			
				|  |  | +          data.transform.x = data.transform.startX + ev.deltaX;
 | 
	
		
			
				|  |  | +          data.transform.y = data.transform.startY + ev.deltaY;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +      //
 | 
	
		
			
				|  |  | +      mc.on("hammer.input", function (ev) {
 | 
	
		
			
				|  |  | +        // console.log("🚀 ~ ev:", ev.type, ev.isFinal);
 | 
	
		
			
				|  |  | +        if (ev.isFinal) {
 | 
	
		
			
				|  |  | +          data.transform.startScale = data.transform.scale;
 | 
	
		
			
				|  |  | +          data.transform.startX = data.transform.x;
 | 
	
		
			
				|  |  | +          data.transform.startY = data.transform.y;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    const resetElement = () => {
 | 
	
		
			
				|  |  | +      data.transform.transition = "all 0.3s";
 | 
	
		
			
				|  |  | +      nextTick(() => {
 | 
	
		
			
				|  |  | +        data.transform.scale = 1;
 | 
	
		
			
				|  |  | +        data.transform.x = 0;
 | 
	
		
			
				|  |  | +        data.transform.y = 0;
 | 
	
		
			
				|  |  | +        data.transform.startScale = 1;
 | 
	
		
			
				|  |  | +        data.transform.startX = 0;
 | 
	
		
			
				|  |  | +        data.transform.startY = 0;
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const pageVisible = usePageVisibility();
 | 
	
		
			
				|  |  | +    watch(
 | 
	
		
			
				|  |  | +      () => pageVisible.value,
 | 
	
		
			
				|  |  | +      (val) => {
 | 
	
		
			
				|  |  | +        if (val === "hidden") {
 | 
	
		
			
				|  |  | +          console.log("页面隐藏停止播放");
 | 
	
		
			
				|  |  | +          handleStop();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +    /** 课件播放 */
 | 
	
		
			
				|  |  | +    const changePlay = (res: any) => {
 | 
	
		
			
				|  |  | +      if (res?.data?.api === "setPlayState") {
 | 
	
		
			
				|  |  | +        handleStop();
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const noteBoxRef = ref();
 | 
	
		
			
				|  |  | +    const scrollNoteBox = (type: "left" | "right") => {
 | 
	
		
			
				|  |  | +      const width = noteBoxRef.value.offsetWidth / 2;
 | 
	
		
			
				|  |  | +      (noteBoxRef.value as unknown as HTMLElement).scrollBy({
 | 
	
		
			
				|  |  | +        left: type === "left" ? -width : width,
 | 
	
		
			
				|  |  | +        behavior: "smooth",
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const playStatus = reactive({
 | 
	
		
			
				|  |  | +      gamut: false, // 是否播放音阶
 | 
	
		
			
				|  |  | +      gamutTimer: null as any, // 播放音阶定时器
 | 
	
		
			
				|  |  | +      answer: false, // 是否显示答案
 | 
	
		
			
				|  |  | +      action: false, // 是否开始播放
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /** 音符切换 */
 | 
	
		
			
				|  |  | +    const noteChangeShow = () => {
 | 
	
		
			
				|  |  | +      // 播放音阶时不能切换
 | 
	
		
			
				|  |  | +      if (playStatus.gamut) return;
 | 
	
		
			
				|  |  | +      // 开始答题不能切换
 | 
	
		
			
				|  |  | +      if (playStatus.action) return;
 | 
	
		
			
				|  |  | +      if (data.noteType === "all") {
 | 
	
		
			
				|  |  | +        data.noteType = "#c";
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        data.noteType = "all";
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      getNotes();
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 开始播放音阶
 | 
	
		
			
				|  |  | +    const onGamutPlayOrPause = async () => {
 | 
	
		
			
				|  |  | +      if (playStatus.gamut) {
 | 
	
		
			
				|  |  | +        playStatus.gamut = false;
 | 
	
		
			
				|  |  | +        gaumntPause();
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        // 不管当前显示在哪个音老师滚动到开始位置
 | 
	
		
			
				|  |  | +        (noteBoxRef.value as unknown as HTMLElement).scroll({
 | 
	
		
			
				|  |  | +          left: 0,
 | 
	
		
			
				|  |  | +          top: 0,
 | 
	
		
			
				|  |  | +          behavior: "smooth",
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +        playStatus.gamut = true;
 | 
	
		
			
				|  |  | +        const notes = data.notes;
 | 
	
		
			
				|  |  | +        let scrollCount = 0;
 | 
	
		
			
				|  |  | +        for (let i = 0; i < notes.length; i++) {
 | 
	
		
			
				|  |  | +          if (!playStatus.gamut) return false;
 | 
	
		
			
				|  |  | +          const activeDom = document.querySelectorAll(".note-class")[i] as any;
 | 
	
		
			
				|  |  | +          if (activeDom.offsetLeft >= noteBoxRef.value.offsetWidth + (noteBoxRef.value.offsetWidth / 2) * scrollCount - activeDom.offsetWidth) {
 | 
	
		
			
				|  |  | +            scrollNoteBox("right");
 | 
	
		
			
				|  |  | +            scrollCount++;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          await gaumtPlay(notes[i]);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // // 处理播放到最后一个
 | 
	
		
			
				|  |  | +        setTimeout(() => {
 | 
	
		
			
				|  |  | +          playStatus.gamut = false;
 | 
	
		
			
				|  |  | +          gaumntPause();
 | 
	
		
			
				|  |  | +        }, 667);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    const gaumtPlay = (note: any, status?: boolean) => {
 | 
	
		
			
				|  |  | +      return new Promise((resolve) => {
 | 
	
		
			
				|  |  | +        playStatus.gamutTimer = setTimeout(() => {
 | 
	
		
			
				|  |  | +          if (playStatus.gamut || status) {
 | 
	
		
			
				|  |  | +            noteClick(note);
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          resolve(note);
 | 
	
		
			
				|  |  | +        }, 667);
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    const gaumntPause = () => {
 | 
	
		
			
				|  |  | +      clearTimeout(playStatus.gamutTimer);
 | 
	
		
			
				|  |  | +      if (data.noteAudio) {
 | 
	
		
			
				|  |  | +        data.noteAudio.stop();
 | 
	
		
			
				|  |  | +        data.realKey = 0;
 | 
	
		
			
				|  |  | +        data.noteAudio = null as unknown as Howl;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /** 开始播放 */
 | 
	
		
			
				|  |  | +    const playAction = reactive({
 | 
	
		
			
				|  |  | +      exampleAnser: {} as any, // 示例声音
 | 
	
		
			
				|  |  | +      standardAnswer: {} as any, // 标准答案key
 | 
	
		
			
				|  |  | +      showAnswerLoading: false, // 显示按答案中
 | 
	
		
			
				|  |  | +      listenModeStatus: false, // 是否开始了模式
 | 
	
		
			
				|  |  | +      listenLock: false,
 | 
	
		
			
				|  |  | +      listenTipsStatus: false, // 开始播放状态
 | 
	
		
			
				|  |  | +      /** 0: 未答,1: 答对,2: 答错 */
 | 
	
		
			
				|  |  | +      userAnswerStatus: 0 as 0 | 1 | 2, // 用户回答状态
 | 
	
		
			
				|  |  | +      userAnswer: {} as any, // 用户答的数据
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    const onActionPlay = async () => {
 | 
	
		
			
				|  |  | +      if (playAction.listenLock) return;
 | 
	
		
			
				|  |  | +      if (playAction.showAnswerLoading) return;
 | 
	
		
			
				|  |  | +      playStatus.action = true;
 | 
	
		
			
				|  |  | +      playStatus.answer = true;
 | 
	
		
			
				|  |  | +      // 先暂停播放声音
 | 
	
		
			
				|  |  | +      gaumntPause();
 | 
	
		
			
				|  |  | +      if (data.fingeringMode === "fingeringMode") {
 | 
	
		
			
				|  |  | +        onFingeringMode();
 | 
	
		
			
				|  |  | +      } else if (data.fingeringMode === "listenMode") {
 | 
	
		
			
				|  |  | +        if (playAction.listenModeStatus) {
 | 
	
		
			
				|  |  | +          playAction.listenLock = true;
 | 
	
		
			
				|  |  | +          await fingeringPlay(playAction.standardAnswer, 1500, false);
 | 
	
		
			
				|  |  | +          gaumntPause();
 | 
	
		
			
				|  |  | +          playAction.listenLock = false;
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          onListenMode();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 指法模式
 | 
	
		
			
				|  |  | +    const fingeringPlay = (note: any, timer = 1500, showNote = true) => {
 | 
	
		
			
				|  |  | +      return new Promise((resolve) => {
 | 
	
		
			
				|  |  | +        noteClick(note, showNote);
 | 
	
		
			
				|  |  | +        setTimeout(() => {
 | 
	
		
			
				|  |  | +          resolve(note);
 | 
	
		
			
				|  |  | +        }, timer);
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    const onFingeringMode = () => {
 | 
	
		
			
				|  |  | +      const randomIndex = Math.floor(Math.random() * data.notes.length);
 | 
	
		
			
				|  |  | +      playAction.standardAnswer = data.notes[randomIndex];
 | 
	
		
			
				|  |  | +      data.realKey = data.notes[randomIndex].realKey;
 | 
	
		
			
				|  |  | +      if (playAction.listenModeStatus) {
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      playAction.listenModeStatus = true; // 是否开始听音
 | 
	
		
			
				|  |  | +      playAction.listenLock = true; // 锁
 | 
	
		
			
				|  |  | +      playAction.listenTipsStatus = true;
 | 
	
		
			
				|  |  | +      setTimeout(() => {
 | 
	
		
			
				|  |  | +        playAction.listenTipsStatus = false;
 | 
	
		
			
				|  |  | +        playAction.listenLock = false; // 锁
 | 
	
		
			
				|  |  | +      }, 2000);
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    // 听音模式
 | 
	
		
			
				|  |  | +    const onListenMode = async () => {
 | 
	
		
			
				|  |  | +      playAction.listenModeStatus = true; // 是否开始听音
 | 
	
		
			
				|  |  | +      playAction.listenLock = true; // 锁
 | 
	
		
			
				|  |  | +      playAction.listenTipsStatus = true;
 | 
	
		
			
				|  |  | +      // 设置并保存示例数据
 | 
	
		
			
				|  |  | +      let randomIndex = Math.floor(Math.random() * data.notes.length);
 | 
	
		
			
				|  |  | +      playAction.exampleAnser = data.notes[randomIndex];
 | 
	
		
			
				|  |  | +      data.realKey = playAction.exampleAnser.realKey;
 | 
	
		
			
				|  |  | +      scrollAnswer(playAction.exampleAnser.realKey);
 | 
	
		
			
				|  |  | +      await fingeringPlay(playAction.exampleAnser);
 | 
	
		
			
				|  |  | +      data.realKey = 0;
 | 
	
		
			
				|  |  | +      playAction.exampleAnser = {};
 | 
	
		
			
				|  |  | +      gaumntPause();
 | 
	
		
			
				|  |  | +      setTimeout(async () => {
 | 
	
		
			
				|  |  | +        // 设置答题数据
 | 
	
		
			
				|  |  | +        randomIndex = Math.floor(Math.random() * data.notes.length);
 | 
	
		
			
				|  |  | +        playAction.standardAnswer = data.notes[randomIndex];
 | 
	
		
			
				|  |  | +        await fingeringPlay(data.notes[randomIndex], 1500, false);
 | 
	
		
			
				|  |  | +        gaumntPause();
 | 
	
		
			
				|  |  | +        playAction.listenLock = false;
 | 
	
		
			
				|  |  | +        playAction.listenTipsStatus = false;
 | 
	
		
			
				|  |  | +      }, 1000);
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 显示答案
 | 
	
		
			
				|  |  | +    const onShowAnswer = async () => {
 | 
	
		
			
				|  |  | +      if (playAction.listenLock) return;
 | 
	
		
			
				|  |  | +      playAction.showAnswerLoading = true;
 | 
	
		
			
				|  |  | +      scrollAnswer(playAction.standardAnswer.realKey);
 | 
	
		
			
				|  |  | +      await fingeringPlay(playAction.standardAnswer);
 | 
	
		
			
				|  |  | +      resetMode(true, 0);
 | 
	
		
			
				|  |  | +      // }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    // 滚动到对应答案位置
 | 
	
		
			
				|  |  | +    const scrollAnswer = (realKey?: any) => {
 | 
	
		
			
				|  |  | +      const tempRealKey = realKey || data.realKey;
 | 
	
		
			
				|  |  | +      const index = data.notes.findIndex((item: any) => item.realKey === tempRealKey);
 | 
	
		
			
				|  |  | +      const activeDom = document.querySelectorAll(".note-class")[index] as any;
 | 
	
		
			
				|  |  | +      if (activeDom) {
 | 
	
		
			
				|  |  | +        const aWidth = activeDom.offsetWidth;
 | 
	
		
			
				|  |  | +        const width = noteBoxRef.value.offsetWidth;
 | 
	
		
			
				|  |  | +        const aLeft = Math.max(activeDom?.offsetLeft - aWidth, 0);
 | 
	
		
			
				|  |  | +        (noteBoxRef.value as unknown as HTMLElement).scroll({
 | 
	
		
			
				|  |  | +          left: Math.max(aLeft - width / 2, 0),
 | 
	
		
			
				|  |  | +          top: 0,
 | 
	
		
			
				|  |  | +          behavior: "smooth",
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 重置播放状态
 | 
	
		
			
				|  |  | +     * @param status 是否全部重置
 | 
	
		
			
				|  |  | +     * @param timer 延时时长(默认2s)
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    const resetMode = (status = true, timer = 2000) => {
 | 
	
		
			
				|  |  | +      // 2秒钟后重置
 | 
	
		
			
				|  |  | +      setTimeout(() => {
 | 
	
		
			
				|  |  | +        gaumntPause();
 | 
	
		
			
				|  |  | +        if (status) {
 | 
	
		
			
				|  |  | +          playAction.standardAnswer = {};
 | 
	
		
			
				|  |  | +          playAction.showAnswerLoading = false;
 | 
	
		
			
				|  |  | +          playAction.userAnswerStatus = 0;
 | 
	
		
			
				|  |  | +          playAction.userAnswer = {};
 | 
	
		
			
				|  |  | +          playAction.listenModeStatus = false;
 | 
	
		
			
				|  |  | +          playStatus.action = false;
 | 
	
		
			
				|  |  | +          playStatus.answer = false;
 | 
	
		
			
				|  |  | +          playStatus.gamut = false;
 | 
	
		
			
				|  |  | +          data.realKey = 0;
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          playAction.userAnswerStatus = 0;
 | 
	
		
			
				|  |  | +          playAction.userAnswer = {};
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }, timer);
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /** 滚轮缩放 */
 | 
	
		
			
				|  |  | +    const handleWheel = (e: WheelEvent) => {
 | 
	
		
			
				|  |  | +      e.preventDefault();
 | 
	
		
			
				|  |  | +      if (e.deltaY > 0) {
 | 
	
		
			
				|  |  | +        data.transform.scale -= 0.1;
 | 
	
		
			
				|  |  | +        if (data.transform.scale <= 0.5) {
 | 
	
		
			
				|  |  | +          data.transform.scale = 0.5;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        data.transform.scale += 0.1;
 | 
	
		
			
				|  |  | +        if (data.transform.scale >= 2) {
 | 
	
		
			
				|  |  | +          data.transform.scale = 2;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    onMounted(() => {
 | 
	
		
			
				|  |  | +      window.addEventListener("message", changePlay);
 | 
	
		
			
				|  |  | +      const fingeringContainer = document.getElementById("fingeringContainer");
 | 
	
		
			
				|  |  | +      fingeringContainer?.addEventListener("wheel", handleWheel);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    onUnmounted(() => {
 | 
	
		
			
				|  |  | +      window.removeEventListener("message", changePlay);
 | 
	
		
			
				|  |  | +      const fingeringContainer = document.getElementById("fingeringContainer");
 | 
	
		
			
				|  |  | +      fingeringContainer?.removeEventListener("wheel", handleWheel);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const containerBox = computed(() => {
 | 
	
		
			
				|  |  | +      if (state.platform === IPlatform.PC || query.modelType) {
 | 
	
		
			
				|  |  | +        return {
 | 
	
		
			
				|  |  | +          paddingTop: "1rem",
 | 
	
		
			
				|  |  | +          paddingBottom: "",
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (data.subject === "hulusi-flute") {
 | 
	
		
			
				|  |  | +        return {
 | 
	
		
			
				|  |  | +          paddingTop: "3.1rem",
 | 
	
		
			
				|  |  | +          paddingBottom: ".8rem",
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +      } else if (data.subject === "piccolo") {
 | 
	
		
			
				|  |  | +        return {
 | 
	
		
			
				|  |  | +          paddingTop: "4rem",
 | 
	
		
			
				|  |  | +          paddingBottom: ".8rem",
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +      } else if (data.subject === "pan-flute") {
 | 
	
		
			
				|  |  | +        return {
 | 
	
		
			
				|  |  | +          paddingTop: "0",
 | 
	
		
			
				|  |  | +          paddingBottom: "0",
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +      } else if (data.subject === "ocarina") {
 | 
	
		
			
				|  |  | +        return {
 | 
	
		
			
				|  |  | +          paddingTop: "1.2rem",
 | 
	
		
			
				|  |  | +          paddingBottom: "0",
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +      } else if (data.subject === "melodica") {
 | 
	
		
			
				|  |  | +        return {
 | 
	
		
			
				|  |  | +          paddingTop: "2.8rem",
 | 
	
		
			
				|  |  | +          paddingBottom: "1.8rem",
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        return {
 | 
	
		
			
				|  |  | +          paddingTop: "",
 | 
	
		
			
				|  |  | +          paddingBottom: "",
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const listenText = computed(() => {
 | 
	
		
			
				|  |  | +      if (data.fingeringMode === "fingeringMode") {
 | 
	
		
			
				|  |  | +        if (playStatus.action) {
 | 
	
		
			
				|  |  | +          return "换一换";
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          return "开始练习";
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else if (data.fingeringMode === "listenMode") {
 | 
	
		
			
				|  |  | +        if (playStatus.action) {
 | 
	
		
			
				|  |  | +          return "再听一遍";
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          return "开始听音";
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      return "开始听音";
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const modeText = computed(() => {
 | 
	
		
			
				|  |  | +      let text = "";
 | 
	
		
			
				|  |  | +      let icon = icons.icon_mode;
 | 
	
		
			
				|  |  | +      data.fingeringModeList.forEach((item: any) => {
 | 
	
		
			
				|  |  | +        if (item.value === data.fingeringMode) {
 | 
	
		
			
				|  |  | +          text = item.text;
 | 
	
		
			
				|  |  | +          icon = item.icon;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +      return {
 | 
	
		
			
				|  |  | +        text,
 | 
	
		
			
				|  |  | +        icon,
 | 
	
		
			
				|  |  | +      };
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 屏幕方向 0 竖,1 横
 | 
	
		
			
				|  |  | +    const orientationDirection = computed(() => {
 | 
	
		
			
				|  |  | +      return ["hulusi-flute", "piccolo"].includes(data.subject) ? 1 : 0;
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const resultImg = (note: any) => {
 | 
	
		
			
				|  |  | +      if (data.realKey === note.realKey && !playStatus.action) {
 | 
	
		
			
				|  |  | +        return {
 | 
	
		
			
				|  |  | +          icon: icons.icon_btn_ylow,
 | 
	
		
			
				|  |  | +          status: false,
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +      } else if (playAction.exampleAnser.realKey === note.realKey) {
 | 
	
		
			
				|  |  | +        return {
 | 
	
		
			
				|  |  | +          icon: icons.icon_btn_ylow,
 | 
	
		
			
				|  |  | +          status: false,
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +      } else if (playAction.standardAnswer.realKey === note.realKey) {
 | 
	
		
			
				|  |  | +        // 没有开始答题
 | 
	
		
			
				|  |  | +        if (!playStatus.action) {
 | 
	
		
			
				|  |  | +          return {
 | 
	
		
			
				|  |  | +            icon: icons.icon_btn_ylow,
 | 
	
		
			
				|  |  | +            status: false,
 | 
	
		
			
				|  |  | +          };
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        // 显示答案中
 | 
	
		
			
				|  |  | +        if (playAction.showAnswerLoading) {
 | 
	
		
			
				|  |  | +          return {
 | 
	
		
			
				|  |  | +            icon: icons.icon_btn_green,
 | 
	
		
			
				|  |  | +            status: true,
 | 
	
		
			
				|  |  | +          };
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        // 用户答对
 | 
	
		
			
				|  |  | +        if (playAction.userAnswerStatus === 1) {
 | 
	
		
			
				|  |  | +          return {
 | 
	
		
			
				|  |  | +            icon: icons.icon_btn_green,
 | 
	
		
			
				|  |  | +            status: true,
 | 
	
		
			
				|  |  | +          };
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        // 用户答错
 | 
	
		
			
				|  |  | +        if (playAction.userAnswerStatus === 2 && playAction.userAnswer.realKey === note.realKey) {
 | 
	
		
			
				|  |  | +          return {
 | 
	
		
			
				|  |  | +            icon: icons.icon_btn_red,
 | 
	
		
			
				|  |  | +            status: true,
 | 
	
		
			
				|  |  | +          };
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      return {
 | 
	
		
			
				|  |  | +        icon: icons.icon_btn_blue,
 | 
	
		
			
				|  |  | +        status: true,
 | 
	
		
			
				|  |  | +      };
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    return () => {
 | 
	
		
			
				|  |  | +      const relationship = fingerData.subject?.relationship?.[data.realKey] || [];
 | 
	
		
			
				|  |  | +      const rs: number[] = Array.isArray(relationship[1]) ? relationship[fingerData.relationshipIndex] : relationship;
 | 
	
		
			
				|  |  | +      const canTizhi = Array.isArray(relationship[1]);
 | 
	
		
			
				|  |  | +      return (
 | 
	
		
			
				|  |  | +        <div class={[styles.fingerBox, state.platform !== IPlatform.PC && !query.modelType && fingerData.fingeringInfo.orientation === 1 ? styles.fingerBottom : styles.fingerRight]}>
 | 
	
		
			
				|  |  | +          <div
 | 
	
		
			
				|  |  | +            class={styles.head}
 | 
	
		
			
				|  |  | +            style={{
 | 
	
		
			
				|  |  | +              paddingTop: data.paddingTop ? data.paddingTop : "",
 | 
	
		
			
				|  |  | +              paddingLeft: data.paddingLeft ? data.paddingLeft : "",
 | 
	
		
			
				|  |  | +            }}
 | 
	
		
			
				|  |  | +          >
 | 
	
		
			
				|  |  | +            <div class={styles.left}>
 | 
	
		
			
				|  |  | +              <button class={[styles.backBtn]} onClick={() => handleBack()}>
 | 
	
		
			
				|  |  | +                <img src={icons.icon_back} />
 | 
	
		
			
				|  |  | +              </button>
 | 
	
		
			
				|  |  | +              <Popover
 | 
	
		
			
				|  |  | +                placement="bottom"
 | 
	
		
			
				|  |  | +                class={styles.popoverContainer}
 | 
	
		
			
				|  |  | +                actions={data.subjects}
 | 
	
		
			
				|  |  | +                onSelect={(val: any) => {
 | 
	
		
			
				|  |  | +                  if (data.subject === val.value) return;
 | 
	
		
			
				|  |  | +                  data.subject = val.value;
 | 
	
		
			
				|  |  | +                  data.viewIndex = 0;
 | 
	
		
			
				|  |  | +                  resetElement();
 | 
	
		
			
				|  |  | +                  resetMode(true, 0);
 | 
	
		
			
				|  |  | +                  // 设置屏幕方向
 | 
	
		
			
				|  |  | +                  __init();
 | 
	
		
			
				|  |  | +                  setTimeout(() => {
 | 
	
		
			
				|  |  | +                    api_setRequestedOrientation(orientationDirection.value);
 | 
	
		
			
				|  |  | +                  }, 100);
 | 
	
		
			
				|  |  | +                }}
 | 
	
		
			
				|  |  | +              >
 | 
	
		
			
				|  |  | +                {{
 | 
	
		
			
				|  |  | +                  reference: () => (
 | 
	
		
			
				|  |  | +                    <div
 | 
	
		
			
				|  |  | +                      class={styles.baseBtn}
 | 
	
		
			
				|  |  | +                      onClick={() => {
 | 
	
		
			
				|  |  | +                        //
 | 
	
		
			
				|  |  | +                      }}
 | 
	
		
			
				|  |  | +                    >
 | 
	
		
			
				|  |  | +                      <img src={icons.icon_change_instrument} />
 | 
	
		
			
				|  |  | +                      <span>切换乐器</span>
 | 
	
		
			
				|  |  | +                    </div>
 | 
	
		
			
				|  |  | +                  ),
 | 
	
		
			
				|  |  | +                }}
 | 
	
		
			
				|  |  | +              </Popover>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +              {data.subject !== "melodica" && data.fingeringMode === "scaleMode" && (
 | 
	
		
			
				|  |  | +                <div
 | 
	
		
			
				|  |  | +                  class={styles.baseBtn}
 | 
	
		
			
				|  |  | +                  onClick={() => {
 | 
	
		
			
				|  |  | +                    data.viewIndex++;
 | 
	
		
			
				|  |  | +                    if (data.viewIndex > data.viewTotal) {
 | 
	
		
			
				|  |  | +                      if (["pan-flute", "ocarina"].includes(data.subject)) {
 | 
	
		
			
				|  |  | +                        data.viewIndex = 1;
 | 
	
		
			
				|  |  | +                      } else {
 | 
	
		
			
				|  |  | +                        data.viewIndex = 0;
 | 
	
		
			
				|  |  | +                      }
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    getFingeringData();
 | 
	
		
			
				|  |  | +                  }}
 | 
	
		
			
				|  |  | +                >
 | 
	
		
			
				|  |  | +                  <img src={icons.icon_toggle} />
 | 
	
		
			
				|  |  | +                  <span>切换视图</span>
 | 
	
		
			
				|  |  | +                </div>
 | 
	
		
			
				|  |  | +              )}
 | 
	
		
			
				|  |  | +            </div>
 | 
	
		
			
				|  |  | +            <div class={styles.rightBtn}>
 | 
	
		
			
				|  |  | +              <div class={styles.baseBtn} onClick={onChangeFingeringModel}>
 | 
	
		
			
				|  |  | +                <img src={modeText.value.icon} />
 | 
	
		
			
				|  |  | +                <span>{modeText.value.text}</span>
 | 
	
		
			
				|  |  | +              </div>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +              <div class={styles.baseBtn} onClick={() => resetElement()}>
 | 
	
		
			
				|  |  | +                <img src={icons.icon_2_0} />
 | 
	
		
			
				|  |  | +                <span>还原</span>
 | 
	
		
			
				|  |  | +              </div>
 | 
	
		
			
				|  |  | +              <div
 | 
	
		
			
				|  |  | +                class={styles.baseBtn}
 | 
	
		
			
				|  |  | +                onClick={() => {
 | 
	
		
			
				|  |  | +                  resetElement();
 | 
	
		
			
				|  |  | +                  data.tipShow = !data.tipShow;
 | 
	
		
			
				|  |  | +                }}
 | 
	
		
			
				|  |  | +              >
 | 
	
		
			
				|  |  | +                <img src={icons.icon_2_1} />
 | 
	
		
			
				|  |  | +                <span>使用说明</span>
 | 
	
		
			
				|  |  | +              </div>
 | 
	
		
			
				|  |  | +            </div>
 | 
	
		
			
				|  |  | +          </div>
 | 
	
		
			
				|  |  | +          <div class={styles.fingerContent}>
 | 
	
		
			
				|  |  | +            <div class={styles.wrapFinger}>
 | 
	
		
			
				|  |  | +              <div
 | 
	
		
			
				|  |  | +                id="fingeringContainer"
 | 
	
		
			
				|  |  | +                class={styles.boxFinger}
 | 
	
		
			
				|  |  | +                style={{
 | 
	
		
			
				|  |  | +                  paddingTop: containerBox.value.paddingTop,
 | 
	
		
			
				|  |  | +                  paddingBottom: containerBox.value.paddingBottom,
 | 
	
		
			
				|  |  | +                }}
 | 
	
		
			
				|  |  | +              >
 | 
	
		
			
				|  |  | +                <div
 | 
	
		
			
				|  |  | +                  style={{
 | 
	
		
			
				|  |  | +                    transform: `translate3d(${data.transform.x}px,${data.transform.y}px,0px) scale(${data.transform.scale})`,
 | 
	
		
			
				|  |  | +                    transition: data.transform.transition,
 | 
	
		
			
				|  |  | +                  }}
 | 
	
		
			
				|  |  | +                  class={[styles.fingeringContainer]}
 | 
	
		
			
				|  |  | +                >
 | 
	
		
			
				|  |  | +                  <div class={styles.imgs}>
 | 
	
		
			
				|  |  | +                    <img src={data.fingeringMode === "scaleMode" ? fingerData.subject?.json?.full : fingerData.subject?.json?.full1} />
 | 
	
		
			
				|  |  | +                    {rs.map((key: number | string, index: number) => {
 | 
	
		
			
				|  |  | +                      const nk: string = typeof key === "string" ? key.replace("active-", "") : String(key);
 | 
	
		
			
				|  |  | +                      return <img data-index={nk} src={fingerData.subject?.json?.[nk]} />;
 | 
	
		
			
				|  |  | +                    })}
 | 
	
		
			
				|  |  | +                    <div style={{ left: data.viewIndex == 2 ? "0" : "64%" }} class={[styles.tizhi, canTizhi && styles.canDisplay]} onClick={() => (fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)}>
 | 
	
		
			
				|  |  | +                      替指
 | 
	
		
			
				|  |  | +                    </div>
 | 
	
		
			
				|  |  | +                    <div id="finger-note-2" style={{ left: "50%", transform: "translateX(-50%)" }} class={styles.tizhi} onClick={() => (fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)}></div>
 | 
	
		
			
				|  |  | +                  </div>
 | 
	
		
			
				|  |  | +                </div>
 | 
	
		
			
				|  |  | +              </div>
 | 
	
		
			
				|  |  | +              <div
 | 
	
		
			
				|  |  | +                class={styles.notes}
 | 
	
		
			
				|  |  | +                style={{
 | 
	
		
			
				|  |  | +                  paddingLeft: data.paddingLeft ? data.paddingLeft : "",
 | 
	
		
			
				|  |  | +                }}
 | 
	
		
			
				|  |  | +              >
 | 
	
		
			
				|  |  | +                {playAction.listenTipsStatus && <div class={[styles.tipsT, data.fingeringMode === "fingeringMode" ? styles.playTips2 : styles.playTips]}></div>}
 | 
	
		
			
				|  |  | +                {playAction.userAnswerStatus === 1 && <div class={[styles.tipsT, styles.playSuccess]}></div>}
 | 
	
		
			
				|  |  | +                {playAction.userAnswerStatus === 2 && <div class={[styles.tipsT, styles.playError]}></div>}
 | 
	
		
			
				|  |  | +                <Button class={styles.noteBtn} onClick={() => scrollNoteBox("left")}>
 | 
	
		
			
				|  |  | +                  <Icon name="arrow-left" />
 | 
	
		
			
				|  |  | +                </Button>
 | 
	
		
			
				|  |  | +                <div class={[styles.noteContent, data.fingeringMode !== "scaleMode" && orientationDirection.value === 0 && styles.noteContentOther, browsInfo.ios ? "" : styles.noteContentWrap, data.huaweiPad && styles.huaweiPad]}>
 | 
	
		
			
				|  |  | +                  {/* 判断是否为音阶模式 */}
 | 
	
		
			
				|  |  | +                  {data.fingeringMode !== "scaleMode" && (
 | 
	
		
			
				|  |  | +                    <div draggable={false} class={styles.note} onClick={noteChangeShow}>
 | 
	
		
			
				|  |  | +                      <img draggable={false} src={data.noteType === "all" ? icons.icon_btn_orange : icons.icon_btn_orange2} />
 | 
	
		
			
				|  |  | +                    </div>
 | 
	
		
			
				|  |  | +                  )}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                  {/* [styles.noteContent, browsInfo.ios ? "" : styles.noteContentWrap, data.huaweiPad && styles.huaweiPad] */}
 | 
	
		
			
				|  |  | +                  <div class={styles.lastNoteContent}>
 | 
	
		
			
				|  |  | +                    <div ref={noteBoxRef} class={styles.noteBox}>
 | 
	
		
			
				|  |  | +                      {data.notes.map((note: IFIGNER_INSTRUMENT_Note, index: number) => {
 | 
	
		
			
				|  |  | +                        const steps = new Array(Math.abs(note.step)).fill(1);
 | 
	
		
			
				|  |  | +                        return (
 | 
	
		
			
				|  |  | +                          <div
 | 
	
		
			
				|  |  | +                            id={index == 0 ? "finger-note-0" : ""}
 | 
	
		
			
				|  |  | +                            draggable={false}
 | 
	
		
			
				|  |  | +                            class={[styles.note, "note-class"]}
 | 
	
		
			
				|  |  | +                            key={note.realKey}
 | 
	
		
			
				|  |  | +                            onClick={async () => {
 | 
	
		
			
				|  |  | +                              // 判断是否在播放音阶
 | 
	
		
			
				|  |  | +                              if (playStatus.gamut) return;
 | 
	
		
			
				|  |  | +                              if (playAction.listenLock) return;
 | 
	
		
			
				|  |  | +                              if (playAction.showAnswerLoading) return;
 | 
	
		
			
				|  |  | +                              if (playStatus.action) {
 | 
	
		
			
				|  |  | +                                playAction.userAnswer = note;
 | 
	
		
			
				|  |  | +                                // 判断用户答题
 | 
	
		
			
				|  |  | +                                const userResult = note.realKey === playAction.standardAnswer.realKey ? 1 : 2;
 | 
	
		
			
				|  |  | +                                playAction.userAnswerStatus = userResult;
 | 
	
		
			
				|  |  | +                                playAction.listenLock = true;
 | 
	
		
			
				|  |  | +                                data.realKey = note.realKey;
 | 
	
		
			
				|  |  | +                                await fingeringPlay(note, 1000);
 | 
	
		
			
				|  |  | +                                resetMode(userResult === 1 ? true : false, 0);
 | 
	
		
			
				|  |  | +                                data.realKey = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                                // 如果是指法模式显示完之后要还原
 | 
	
		
			
				|  |  | +                                if (data.fingeringMode === "fingeringMode" && userResult === 2) {
 | 
	
		
			
				|  |  | +                                  // 延迟显示,因为重置的时候有一个异步操作
 | 
	
		
			
				|  |  | +                                  setTimeout(() => {
 | 
	
		
			
				|  |  | +                                    data.realKey = playAction.standardAnswer.realKey;
 | 
	
		
			
				|  |  | +                                  }, 10);
 | 
	
		
			
				|  |  | +                                }
 | 
	
		
			
				|  |  | +                                playAction.listenLock = false;
 | 
	
		
			
				|  |  | +                              } else {
 | 
	
		
			
				|  |  | +                                noteClick(note);
 | 
	
		
			
				|  |  | +                              }
 | 
	
		
			
				|  |  | +                            }}
 | 
	
		
			
				|  |  | +                          >
 | 
	
		
			
				|  |  | +                            <img draggable={false} src={resultImg(note).icon} />
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                            {playStatus.action && ((playAction.showAnswerLoading && playAction.standardAnswer.realKey === note.realKey) || (playAction.userAnswerStatus === 1 && playAction.userAnswer.realKey === note.realKey)) ? <span class={styles.showAnswer}></span> : ""}
 | 
	
		
			
				|  |  | +                            {playStatus.action && playAction.userAnswerStatus === 2 && playAction.userAnswer.realKey === note.realKey ? <span class={[styles.showAnswer, styles.errorAnswer]}></span> : ""}
 | 
	
		
			
				|  |  | +                            <div
 | 
	
		
			
				|  |  | +                              class={[
 | 
	
		
			
				|  |  | +                                styles.noteKey,
 | 
	
		
			
				|  |  | +                                ((data.realKey === note.realKey && !playStatus.action) ||
 | 
	
		
			
				|  |  | +                                  (playStatus.action && ((playAction.showAnswerLoading && playAction.standardAnswer.realKey === note.realKey) || (playAction.userAnswerStatus === 1 && playAction.userAnswer.realKey === note.realKey))) ||
 | 
	
		
			
				|  |  | +                                  (playStatus.action && playAction.userAnswerStatus === 2 && playAction.userAnswer.realKey === note.realKey)) &&
 | 
	
		
			
				|  |  | +                                  styles.keyActive,
 | 
	
		
			
				|  |  | +                              ]}
 | 
	
		
			
				|  |  | +                            >
 | 
	
		
			
				|  |  | +                              {/* 显示对应的点 */}
 | 
	
		
			
				|  |  | +                              {note.step > 0 ? steps.map((n: any) => <span class={styles.dot}></span>) : null}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                              <div class={styles.noteName}>
 | 
	
		
			
				|  |  | +                                <sup>{note.mark && (note.mark === "rise" ? "#" : "b")}</sup>
 | 
	
		
			
				|  |  | +                                {note.key}
 | 
	
		
			
				|  |  | +                              </div>
 | 
	
		
			
				|  |  | +                              {/* 显示对应的点 */}
 | 
	
		
			
				|  |  | +                              {note.step < 0 ? steps.map((n: any) => <span class={styles.dot}></span>) : null}
 | 
	
		
			
				|  |  | +                            </div>
 | 
	
		
			
				|  |  | +                          </div>
 | 
	
		
			
				|  |  | +                        );
 | 
	
		
			
				|  |  | +                      })}
 | 
	
		
			
				|  |  | +                    </div>
 | 
	
		
			
				|  |  | +                  </div>
 | 
	
		
			
				|  |  | +                </div>
 | 
	
		
			
				|  |  | +                <Button class={styles.noteBtn} onClick={() => scrollNoteBox("right")}>
 | 
	
		
			
				|  |  | +                  <Icon name="arrow" />
 | 
	
		
			
				|  |  | +                </Button>
 | 
	
		
			
				|  |  | +              </div>
 | 
	
		
			
				|  |  | +              {data.fingeringMode !== "scaleMode" && (
 | 
	
		
			
				|  |  | +                <div class={styles.optionBtns}>
 | 
	
		
			
				|  |  | +                  <Button class={[styles.oBtn, styles.gamut, playStatus.action && styles.disabled]} round onClick={onGamutPlayOrPause}>
 | 
	
		
			
				|  |  | +                    {playStatus.gamut ? "暂停" : "播放音阶"}
 | 
	
		
			
				|  |  | +                  </Button>
 | 
	
		
			
				|  |  | +                  <Button class={[styles.oBtn, styles.play, playStatus.gamut && styles.disabled]} round onClick={onActionPlay}>
 | 
	
		
			
				|  |  | +                    {listenText.value}
 | 
	
		
			
				|  |  | +                  </Button>
 | 
	
		
			
				|  |  | +                  <Button class={[styles.oBtn, styles.success, !playStatus.answer && styles.disabled]} round onClick={onShowAnswer}>
 | 
	
		
			
				|  |  | +                    显示答案
 | 
	
		
			
				|  |  | +                  </Button>
 | 
	
		
			
				|  |  | +                </div>
 | 
	
		
			
				|  |  | +              )}
 | 
	
		
			
				|  |  | +            </div>
 | 
	
		
			
				|  |  | +            <div class={[styles.tips, data.tipShow ? "" : styles.tipHidden]}>
 | 
	
		
			
				|  |  | +              <div class={styles.tipTitle}>
 | 
	
		
			
				|  |  | +                <div class={styles.tipTitleName}>{fingerData.fingeringInfo.code}使用说明</div>
 | 
	
		
			
				|  |  | +                <Button class={styles.tipClose} onClick={() => (data.tipShow = false)}>
 | 
	
		
			
				|  |  | +                  <Icon name="cross" size={19} color="#fff" />
 | 
	
		
			
				|  |  | +                </Button>
 | 
	
		
			
				|  |  | +              </div>
 | 
	
		
			
				|  |  | +              <div class={styles.iconBook}></div>
 | 
	
		
			
				|  |  | +              <div class={styles.tipContentbox}>
 | 
	
		
			
				|  |  | +                <div class={styles.tipContent}>
 | 
	
		
			
				|  |  | +                  {data.tips.map((tip, tipIndex) => (
 | 
	
		
			
				|  |  | +                    <div class={styles.tipItem}>
 | 
	
		
			
				|  |  | +                      <div class={styles.iconWrap}>
 | 
	
		
			
				|  |  | +                        <div class={styles.tipItemIcon}>{tipIndex + 1}</div>
 | 
	
		
			
				|  |  | +                      </div>
 | 
	
		
			
				|  |  | +                      <div>
 | 
	
		
			
				|  |  | +                        {tip.name}: {tip.realName}
 | 
	
		
			
				|  |  | +                      </div>
 | 
	
		
			
				|  |  | +                    </div>
 | 
	
		
			
				|  |  | +                  ))}
 | 
	
		
			
				|  |  | +                </div>
 | 
	
		
			
				|  |  | +              </div>
 | 
	
		
			
				|  |  | +            </div>
 | 
	
		
			
				|  |  | +            {data.loadingSoundFonts && (
 | 
	
		
			
				|  |  | +              <div class={styles.loading}>
 | 
	
		
			
				|  |  | +                <div class={styles.loadingWrap}>
 | 
	
		
			
				|  |  | +                  <img class={styles.loadingIcon} src={icon_loading_img} />
 | 
	
		
			
				|  |  | +                  <Progress percentage={data.loadingSoundProgress} />
 | 
	
		
			
				|  |  | +                  <div class={styles.loadingTip}>加载中,请稍后…</div>
 | 
	
		
			
				|  |  | +                </div>
 | 
	
		
			
				|  |  | +              </div>
 | 
	
		
			
				|  |  | +            )}
 | 
	
		
			
				|  |  | +          </div>
 | 
	
		
			
				|  |  | +          {!!data.tones.length && data.fingeringMode === "scaleMode" && (
 | 
	
		
			
				|  |  | +            <>
 | 
	
		
			
				|  |  | +              {fingerData.fingeringInfo.name == "hulusi-flute" ? (
 | 
	
		
			
				|  |  | +                <div id="finger-note-1" class={[styles.toggleBtn, styles.toggleBtnhulusi]} onClick={() => (data.tnoteShow = true)}>
 | 
	
		
			
				|  |  | +                  <div>
 | 
	
		
			
				|  |  | +                    全按作
 | 
	
		
			
				|  |  | +                    <div class={[styles.noteKey]}>
 | 
	
		
			
				|  |  | +                      {data.activeTone.step > 0 ? <span class={styles.dot}></span> : null}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                      <div class={styles.noteName}>
 | 
	
		
			
				|  |  | +                        <sup>{data.activeTone.mark && (data.activeTone.mark === "rise" ? "#" : "b")}</sup>
 | 
	
		
			
				|  |  | +                        {data.activeTone.key}
 | 
	
		
			
				|  |  | +                      </div>
 | 
	
		
			
				|  |  | +                      {data.activeTone.step < 0 ? <span class={styles.dot}></span> : null}
 | 
	
		
			
				|  |  | +                    </div>
 | 
	
		
			
				|  |  | +                  </div>
 | 
	
		
			
				|  |  | +                  <img src={icons.icon_arrow} />
 | 
	
		
			
				|  |  | +                </div>
 | 
	
		
			
				|  |  | +              ) : (
 | 
	
		
			
				|  |  | +                <div id="finger-note-1" class={styles.toggleBtn} onClick={() => (data.tnoteShow = true)}>
 | 
	
		
			
				|  |  | +                  <div style={{ marginTop: "-4px" }}>
 | 
	
		
			
				|  |  | +                    <sup>{data.activeTone.mark && (data.activeTone.mark === "rise" ? "#" : "b")}</sup>
 | 
	
		
			
				|  |  | +                    {data.activeTone.name}
 | 
	
		
			
				|  |  | +                  </div>
 | 
	
		
			
				|  |  | +                  调
 | 
	
		
			
				|  |  | +                  <img src={icons.icon_arrow} />
 | 
	
		
			
				|  |  | +                </div>
 | 
	
		
			
				|  |  | +              )}
 | 
	
		
			
				|  |  | +            </>
 | 
	
		
			
				|  |  | +          )}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +          <Popup class="tonePopup" v-model:show={data.tnoteShow} position={state.platform !== IPlatform.PC && !query.modelType && fingerData.fingeringInfo.orientation === 1 ? "bottom" : "right"}>
 | 
	
		
			
				|  |  | +            <div class={styles.tones}>
 | 
	
		
			
				|  |  | +              <div class={styles.toneTitle}>
 | 
	
		
			
				|  |  | +                <div class={styles.tipTitleName}>移调</div>
 | 
	
		
			
				|  |  | +                <Button class={styles.tipClose} onClick={() => (data.tnoteShow = false)}>
 | 
	
		
			
				|  |  | +                  <Icon name="cross" size={19} color="#fff" />
 | 
	
		
			
				|  |  | +                </Button>
 | 
	
		
			
				|  |  | +              </div>
 | 
	
		
			
				|  |  | +              <div class={styles.tipContentbox}>
 | 
	
		
			
				|  |  | +                <div class={styles.tipContent}>
 | 
	
		
			
				|  |  | +                  <div class={styles.tipWrap}>
 | 
	
		
			
				|  |  | +                    <Space size={0} class={styles.toneContent}>
 | 
	
		
			
				|  |  | +                      {data.tones.map((tone: IFIGNER_INSTRUMENT_Note) => {
 | 
	
		
			
				|  |  | +                        const steps = new Array(Math.abs(tone.step)).fill(1);
 | 
	
		
			
				|  |  | +                        return (
 | 
	
		
			
				|  |  | +                          <Button
 | 
	
		
			
				|  |  | +                            class={[fingerData.fingeringInfo.name == "hulusi-flute" && styles.hulusiBtn]}
 | 
	
		
			
				|  |  | +                            round
 | 
	
		
			
				|  |  | +                            plain
 | 
	
		
			
				|  |  | +                            type={data.popupActiveTone.realName === tone.realName ? "primary" : "default"}
 | 
	
		
			
				|  |  | +                            onClick={() => {
 | 
	
		
			
				|  |  | +                              data.popupActiveTone = tone;
 | 
	
		
			
				|  |  | +                              setNotes();
 | 
	
		
			
				|  |  | +                            }}
 | 
	
		
			
				|  |  | +                          >
 | 
	
		
			
				|  |  | +                            {fingerData.fingeringInfo.name == "hulusi-flute" ? (
 | 
	
		
			
				|  |  | +                              <div style={{ display: "flex", alignItems: "center" }}>
 | 
	
		
			
				|  |  | +                                全按作
 | 
	
		
			
				|  |  | +                                <div class={[styles.noteKey, styles.hulusiNoteKey]}>
 | 
	
		
			
				|  |  | +                                  {tone.step > 0 ? <span class={styles.dot}></span> : null}
 | 
	
		
			
				|  |  | +                                  <div class={styles.noteName} style={{ fontSize: "0.25rem" }}>
 | 
	
		
			
				|  |  | +                                    <sup>{tone.mark && (tone.mark === "rise" ? "#" : "b")}</sup>
 | 
	
		
			
				|  |  | +                                    {tone.key}
 | 
	
		
			
				|  |  | +                                  </div>
 | 
	
		
			
				|  |  | +                                  {tone.step < 0 ? <span class={styles.dot}></span> : null}
 | 
	
		
			
				|  |  | +                                </div>
 | 
	
		
			
				|  |  | +                              </div>
 | 
	
		
			
				|  |  | +                            ) : (
 | 
	
		
			
				|  |  | +                              <div class={styles.noteName}>
 | 
	
		
			
				|  |  | +                                <sup>{tone.mark && (tone.mark === "rise" ? "#" : "b")}</sup>
 | 
	
		
			
				|  |  | +                                {tone.name}
 | 
	
		
			
				|  |  | +                              </div>
 | 
	
		
			
				|  |  | +                            )}
 | 
	
		
			
				|  |  | +                          </Button>
 | 
	
		
			
				|  |  | +                        );
 | 
	
		
			
				|  |  | +                      })}
 | 
	
		
			
				|  |  | +                    </Space>
 | 
	
		
			
				|  |  | +                  </div>
 | 
	
		
			
				|  |  | +                  <div class={styles.toneAction}>
 | 
	
		
			
				|  |  | +                    <img onClick={() => (data.tnoteShow = false)} src={icons.icon_action_cancel} />
 | 
	
		
			
				|  |  | +                    <img
 | 
	
		
			
				|  |  | +                      onClick={() => {
 | 
	
		
			
				|  |  | +                        data.activeTone = data.popupActiveTone;
 | 
	
		
			
				|  |  | +                        setNotes();
 | 
	
		
			
				|  |  | +                        data.tnoteShow = false;
 | 
	
		
			
				|  |  | +                      }}
 | 
	
		
			
				|  |  | +                      src={icons.icon_action_confirm}
 | 
	
		
			
				|  |  | +                    />
 | 
	
		
			
				|  |  | +                  </div>
 | 
	
		
			
				|  |  | +                </div>
 | 
	
		
			
				|  |  | +              </div>
 | 
	
		
			
				|  |  | +            </div>
 | 
	
		
			
				|  |  | +          </Popup>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +          {props.show && !data.loading && !data.loadingSoundFonts && <GuideIndex showGuide={false} list={["finger"]} />}
 | 
	
		
			
				|  |  | +        </div>
 | 
	
		
			
				|  |  | +      );
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  |  });
 |