|  | @@ -0,0 +1,190 @@
 | 
	
		
			
				|  |  | +import { defineComponent, onMounted, reactive } from "vue";
 | 
	
		
			
				|  |  | +import state, { EnumMusicRenderType } from "/src/state";
 | 
	
		
			
				|  |  | +import { setGlobalData } from "../../utils";
 | 
	
		
			
				|  |  | +import MusicScore from "../../view/music-score";
 | 
	
		
			
				|  |  | +import { sysMusicScoreAccompanimentQueryPage } from "../api";
 | 
	
		
			
				|  |  | +import styles from "./index.module.less";
 | 
	
		
			
				|  |  | +import { getQuery } from "/src/utils/queryString";
 | 
	
		
			
				|  |  | +import { Toast, closeToast, showLoadingToast, showToast } from "vant";
 | 
	
		
			
				|  |  | +import { svg2canvas } from "/src/utils/svg2canvas";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +export default defineComponent({
 | 
	
		
			
				|  |  | +	name: "music-list",
 | 
	
		
			
				|  |  | +	setup() {
 | 
	
		
			
				|  |  | +		const query: any = getQuery();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		const detailData = reactive({
 | 
	
		
			
				|  |  | +			isLoading: true,
 | 
	
		
			
				|  |  | +			isProductLoading: false,
 | 
	
		
			
				|  |  | +			product: [
 | 
	
		
			
				|  |  | +				{
 | 
	
		
			
				|  |  | +					state: false,
 | 
	
		
			
				|  |  | +					name: "五线谱",
 | 
	
		
			
				|  |  | +					type: EnumMusicRenderType.staff,
 | 
	
		
			
				|  |  | +					base64: "" as any,
 | 
	
		
			
				|  |  | +				},
 | 
	
		
			
				|  |  | +				{
 | 
	
		
			
				|  |  | +					state: false,
 | 
	
		
			
				|  |  | +					name: "首调",
 | 
	
		
			
				|  |  | +					type: EnumMusicRenderType.firstTone,
 | 
	
		
			
				|  |  | +					base64: "" as any,
 | 
	
		
			
				|  |  | +				},
 | 
	
		
			
				|  |  | +				{
 | 
	
		
			
				|  |  | +					state: false,
 | 
	
		
			
				|  |  | +					name: "固定调",
 | 
	
		
			
				|  |  | +					type: EnumMusicRenderType.fixedTone,
 | 
	
		
			
				|  |  | +					base64: "" as any,
 | 
	
		
			
				|  |  | +				},
 | 
	
		
			
				|  |  | +			],
 | 
	
		
			
				|  |  | +		});
 | 
	
		
			
				|  |  | +		// console.log(route.params, query)
 | 
	
		
			
				|  |  | +		/** 获取曲谱数据 */
 | 
	
		
			
				|  |  | +		const getMusicInfo = (res: any) => {
 | 
	
		
			
				|  |  | +			const index = query["part-index"] ? parseInt(query["part-index"] as string) : 0;
 | 
	
		
			
				|  |  | +			const musicData = res.data.background[index] || {};
 | 
	
		
			
				|  |  | +			const musicInfo = {
 | 
	
		
			
				|  |  | +				...res.data,
 | 
	
		
			
				|  |  | +				music: musicData.audioFileUrl || res.data.audioFileUrl,
 | 
	
		
			
				|  |  | +				accompany: musicData.metronomeUrl || res.data.metronomeUrl,
 | 
	
		
			
				|  |  | +				musicSheetId: musicData.musicSheetId || res.data.id,
 | 
	
		
			
				|  |  | +				track: musicData.track || res.data.track,
 | 
	
		
			
				|  |  | +			};
 | 
	
		
			
				|  |  | +			console.log("🚀 ~ musicInfo:", musicInfo);
 | 
	
		
			
				|  |  | +			setState(musicInfo, index);
 | 
	
		
			
				|  |  | +			setCustom();
 | 
	
		
			
				|  |  | +			detailData.isLoading = false;
 | 
	
		
			
				|  |  | +		};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		const setState = (data: any, index: number) => {
 | 
	
		
			
				|  |  | +			state.appName = "COLEXIU";
 | 
	
		
			
				|  |  | +			state.detailId = data.id;
 | 
	
		
			
				|  |  | +			state.xmlUrl = data.xmlFileUrl;
 | 
	
		
			
				|  |  | +			state.partIndex = index;
 | 
	
		
			
				|  |  | +			state.subjectId = data.musicSubject;
 | 
	
		
			
				|  |  | +			state.categoriesId = data.categoriesId;
 | 
	
		
			
				|  |  | +			state.categoriesName = data.musicTagNames;
 | 
	
		
			
				|  |  | +			state.enableEvaluation = data.canEvaluate ? true : false;
 | 
	
		
			
				|  |  | +			state.examSongId = data.id + "";
 | 
	
		
			
				|  |  | +			state.examSongName = data.musicSheetName;
 | 
	
		
			
				|  |  | +			// 解析扩展字段
 | 
	
		
			
				|  |  | +			if (data.extConfigJson) {
 | 
	
		
			
				|  |  | +				try {
 | 
	
		
			
				|  |  | +					state.extConfigJson = JSON.parse(data.extConfigJson as string);
 | 
	
		
			
				|  |  | +				} catch (error) {
 | 
	
		
			
				|  |  | +					console.error("解析扩展字段错误:", error);
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			state.isOpenMetronome = data.mp3Type === "MP3_METRONOME" ? true : false;
 | 
	
		
			
				|  |  | +			// 曲子包含节拍器,就不开启节拍器
 | 
	
		
			
				|  |  | +			state.needTick = data.mp3Type === "MP3_METRONOME" ? false : true;
 | 
	
		
			
				|  |  | +			state.isShowFingering = data.showFingering ? true : false;
 | 
	
		
			
				|  |  | +			state.music = data.music;
 | 
	
		
			
				|  |  | +			state.accompany = data.accompany;
 | 
	
		
			
				|  |  | +			state.midiUrl = data.midiUrl;
 | 
	
		
			
				|  |  | +			state.parentCategoriesId = data.musicTag;
 | 
	
		
			
				|  |  | +			state.musicSheetCategoriesId = data.musicSheetCategoriesId;
 | 
	
		
			
				|  |  | +			state.playMode = data.audioType === "MP3" ? "MP3" : "MIDI";
 | 
	
		
			
				|  |  | +			state.originSpeed = state.speed = data.playSpeed;
 | 
	
		
			
				|  |  | +			state.track = data.code || data.track;
 | 
	
		
			
				|  |  | +			state.enableNotation = data.notation ? true : false;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			//课堂乐器,默认简谱
 | 
	
		
			
				|  |  | +			state.musicRenderType = EnumMusicRenderType.staff;
 | 
	
		
			
				|  |  | +		};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		const setCustom = () => {
 | 
	
		
			
				|  |  | +			if (state.extConfigJson.multitrack) {
 | 
	
		
			
				|  |  | +				setGlobalData("multitrack", state.extConfigJson.multitrack);
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		onMounted(() => {
 | 
	
		
			
				|  |  | +			(window as any).appName = "colexiu";
 | 
	
		
			
				|  |  | +			const id = query.id;
 | 
	
		
			
				|  |  | +			Promise.all([sysMusicScoreAccompanimentQueryPage(id)]).then((values) => {
 | 
	
		
			
				|  |  | +				getMusicInfo(values[0]);
 | 
	
		
			
				|  |  | +			});
 | 
	
		
			
				|  |  | +			showLoadingToast({ message: "生成中", duration: 0 });
 | 
	
		
			
				|  |  | +		});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		/** 渲染完成 */
 | 
	
		
			
				|  |  | +		const handleRendered = async () => {
 | 
	
		
			
				|  |  | +			const item = detailData.product.find((item) => item.type === state.musicRenderType);
 | 
	
		
			
				|  |  | +			if (!item) return;
 | 
	
		
			
				|  |  | +			item.state = true;
 | 
	
		
			
				|  |  | +			item.base64 = await downPng();
 | 
	
		
			
				|  |  | +			const nextItem = detailData.product.find((item: any) => !item.state);
 | 
	
		
			
				|  |  | +			if (nextItem) {
 | 
	
		
			
				|  |  | +				state.musicRenderType = nextItem.type;
 | 
	
		
			
				|  |  | +				detailData.isLoading = true;
 | 
	
		
			
				|  |  | +				setTimeout(() => (detailData.isLoading = false), 500);
 | 
	
		
			
				|  |  | +				return;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			closeToast();
 | 
	
		
			
				|  |  | +			console.log(detailData.product);
 | 
	
		
			
				|  |  | +			window.parent?.postMessage(
 | 
	
		
			
				|  |  | +				{
 | 
	
		
			
				|  |  | +					api: "webApi_renderSvg",
 | 
	
		
			
				|  |  | +					product: JSON.stringify(detailData.product),
 | 
	
		
			
				|  |  | +				},
 | 
	
		
			
				|  |  | +				"*"
 | 
	
		
			
				|  |  | +			);
 | 
	
		
			
				|  |  | +		};
 | 
	
		
			
				|  |  | +		const downPng = () => {
 | 
	
		
			
				|  |  | +			return new Promise((resolve) => {
 | 
	
		
			
				|  |  | +				setTimeout(async () => {
 | 
	
		
			
				|  |  | +					const svg: any = document.getElementById("osmdSvgPage1")?.cloneNode(true);
 | 
	
		
			
				|  |  | +					if (!svg) {
 | 
	
		
			
				|  |  | +						resolve("");
 | 
	
		
			
				|  |  | +						return;
 | 
	
		
			
				|  |  | +					}
 | 
	
		
			
				|  |  | +					const cw = svg.width.animVal.value;
 | 
	
		
			
				|  |  | +					const ch = svg.height.animVal.value;
 | 
	
		
			
				|  |  | +					const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
 | 
	
		
			
				|  |  | +					rect.setAttribute("x", "0");
 | 
	
		
			
				|  |  | +					rect.setAttribute("y", "0");
 | 
	
		
			
				|  |  | +					rect.setAttribute("width", `${cw * 2}`);
 | 
	
		
			
				|  |  | +					rect.setAttribute("height", `${ch * 2}`);
 | 
	
		
			
				|  |  | +					rect.setAttribute("fill", "#fff");
 | 
	
		
			
				|  |  | +					svg.prepend(rect);
 | 
	
		
			
				|  |  | +					const _canvas = svg2canvas(svg.outerHTML);
 | 
	
		
			
				|  |  | +					const base64 = _canvas.toDataURL("image/png", 1);
 | 
	
		
			
				|  |  | +					resolve(base64);
 | 
	
		
			
				|  |  | +					// if (state.platform === IPlatform.PC) {
 | 
	
		
			
				|  |  | +					// 	let el: any = document.createElement("a");
 | 
	
		
			
				|  |  | +					// 	// 设置 href 为图片经过 base64 编码后的字符串,默认为 png 格式
 | 
	
		
			
				|  |  | +					// 	el.href = _canvas.toDataURL();
 | 
	
		
			
				|  |  | +					// 	el.download = state.examSongName;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +					// 	// 创建一个点击事件并对 a 标签进行触发
 | 
	
		
			
				|  |  | +					// 	const event = new MouseEvent("click");
 | 
	
		
			
				|  |  | +					// 	el.dispatchEvent(event);
 | 
	
		
			
				|  |  | +					// 	setTimeout(() => {
 | 
	
		
			
				|  |  | +					// 		showToast({ message: "保存成功", type: "success" });
 | 
	
		
			
				|  |  | +					// 		el = null;
 | 
	
		
			
				|  |  | +					// 	}, 300);
 | 
	
		
			
				|  |  | +					// } else {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +					// 	const res = await api_savePicture({
 | 
	
		
			
				|  |  | +					// 		base64,
 | 
	
		
			
				|  |  | +					// 	});
 | 
	
		
			
				|  |  | +					// 	if (res?.content?.status === "success") {
 | 
	
		
			
				|  |  | +					// 		showToast({ message: "保存成功", type: "success" });
 | 
	
		
			
				|  |  | +					// 	} else {
 | 
	
		
			
				|  |  | +					// 		showToast({ message: "保存失败", type: "fail" });
 | 
	
		
			
				|  |  | +					// 	}
 | 
	
		
			
				|  |  | +					// }
 | 
	
		
			
				|  |  | +				}, 500);
 | 
	
		
			
				|  |  | +			});
 | 
	
		
			
				|  |  | +		};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		return () => (
 | 
	
		
			
				|  |  | +			<div class={styles.detail}>
 | 
	
		
			
				|  |  | +				<div id="scrollContainer" class={[styles.container, "hideCursor"]}>
 | 
	
		
			
				|  |  | +					{/* 曲谱渲染 */}
 | 
	
		
			
				|  |  | +					{!detailData.isLoading && <MusicScore showSelection={false} onRendered={handleRendered} />}
 | 
	
		
			
				|  |  | +				</div>
 | 
	
		
			
				|  |  | +			</div>
 | 
	
		
			
				|  |  | +		);
 | 
	
		
			
				|  |  | +	},
 | 
	
		
			
				|  |  | +});
 |