|  | @@ -167,6 +167,7 @@ export default defineComponent({
 | 
	
		
			
				|  |  |  			/** 移调类型 */
 | 
	
		
			
				|  |  |  			moveKeyType: "inset" as "inset" | "up" | "down", // 移调类型
 | 
	
		
			
				|  |  |  			activePlayNote: null as any, // 当前演奏音符
 | 
	
		
			
				|  |  | +			times: [] as any[], // 节拍器数据
 | 
	
		
			
				|  |  |  		});
 | 
	
		
			
				|  |  |  		const noteTypes = ABC_DATA.types.map((item) => item.value).filter(Boolean);
 | 
	
		
			
				|  |  |  		const accidentals = ABC_DATA.accidentals.map((item) => item.value).filter(Boolean);
 | 
	
	
		
			
				|  | @@ -300,13 +301,16 @@ export default defineComponent({
 | 
	
		
			
				|  |  |  				console.log("开始");
 | 
	
		
			
				|  |  |  				data.playState = true;
 | 
	
		
			
				|  |  |  				var svg = document.querySelector("#paper svg");
 | 
	
		
			
				|  |  | -				var cursor = document.createElementNS("http://www.w3.org/2000/svg", "line");
 | 
	
		
			
				|  |  | -				cursor.setAttribute("class", "ABCJS-cursor");
 | 
	
		
			
				|  |  | +				let cursor = document.querySelector("#paper svg .ABCJS-cursor");
 | 
	
		
			
				|  |  | +				if (!cursor) {
 | 
	
		
			
				|  |  | +					cursor = document.createElementNS("http://www.w3.org/2000/svg", "line");
 | 
	
		
			
				|  |  | +					cursor.setAttribute("class", "ABCJS-cursor");
 | 
	
		
			
				|  |  | +					svg?.appendChild(cursor);
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  |  				cursor.setAttributeNS(null, "x1", "0");
 | 
	
		
			
				|  |  |  				cursor.setAttributeNS(null, "y1", "0");
 | 
	
		
			
				|  |  |  				cursor.setAttributeNS(null, "x2", "0");
 | 
	
		
			
				|  |  |  				cursor.setAttributeNS(null, "y2", "0");
 | 
	
		
			
				|  |  | -				svg?.appendChild(cursor);
 | 
	
		
			
				|  |  |  			},
 | 
	
		
			
				|  |  |  			onBeat: function (beatNumber: any, totalBeats: any, totalTime: any) {
 | 
	
		
			
				|  |  |  				if (!data.playState) return;
 | 
	
	
		
			
				|  | @@ -314,13 +318,17 @@ export default defineComponent({
 | 
	
		
			
				|  |  |  				const time = (beatNumber / totalBeats) * totalTime;
 | 
	
		
			
				|  |  |  				metronomeData.metro.sound(time);
 | 
	
		
			
				|  |  |  			},
 | 
	
		
			
				|  |  | -			onEvent: (ev: any) => {
 | 
	
		
			
				|  |  | +			onEvent: (event: any) => {
 | 
	
		
			
				|  |  | +				let ev = event as any;
 | 
	
		
			
				|  |  |  				if (!data.playState) return;
 | 
	
		
			
				|  |  |  				if (ev.measureStart && ev.left === null) return; // this was the second part of a tie across a measure line. Just ignore it.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  				if (popup.selectMearesShow) {
 | 
	
		
			
				|  |  |  					const startTime = data.selectMeasures.startNote?.currentTrackMilliseconds || 0;
 | 
	
		
			
				|  |  | +					// const timeNote = data.times.find(
 | 
	
		
			
				|  |  | +					// 	(n) => n.startChar === data.selectMeasures.startNote.startChar
 | 
	
		
			
				|  |  | +					// );
 | 
	
		
			
				|  |  |  					const endNote: any = data.selectMeasures.endNote ? data.selectMeasures.endNote : null;
 | 
	
		
			
				|  |  | -					// console.log("🚀 ~ endNote:", ev.milliseconds , endNote.currentTrackMilliseconds)
 | 
	
		
			
				|  |  |  					if (
 | 
	
		
			
				|  |  |  						ev.milliseconds < startTime ||
 | 
	
		
			
				|  |  |  						(endNote && ev.milliseconds > endNote.currentTrackMilliseconds)
 | 
	
	
		
			
				|  | @@ -328,9 +336,8 @@ export default defineComponent({
 | 
	
		
			
				|  |  |  						const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
 | 
	
		
			
				|  |  |  						if (totalTime) {
 | 
	
		
			
				|  |  |  							const progress = startTime / 1000 / totalTime;
 | 
	
		
			
				|  |  | -							nextTick(() => {
 | 
	
		
			
				|  |  | -								(abcData.synthControl as any).seek(progress);
 | 
	
		
			
				|  |  | -							});
 | 
	
		
			
				|  |  | +							(abcData.synthControl as any).seek(progress);
 | 
	
		
			
				|  |  | +							return;
 | 
	
		
			
				|  |  |  						}
 | 
	
		
			
				|  |  |  					}
 | 
	
		
			
				|  |  |  				}
 | 
	
	
		
			
				|  | @@ -513,6 +520,7 @@ export default defineComponent({
 | 
	
		
			
				|  |  |  		/** 生成曲谱节拍器数据 */
 | 
	
		
			
				|  |  |  		const productMetronomeData = () => {
 | 
	
		
			
				|  |  |  			const times = new ABCJS.TimingCallbacks(abcData.visualObj);
 | 
	
		
			
				|  |  | +			data.times = times.noteTimings;
 | 
	
		
			
				|  |  |  			const list: any[] = [];
 | 
	
		
			
				|  |  |  			let meter = abcData.abc.meter || "";
 | 
	
		
			
				|  |  |  			// length - 1是为了去除最后一个空的结束事件
 | 
	
	
		
			
				|  | @@ -625,6 +633,7 @@ export default defineComponent({
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  			if (type === "exit") {
 | 
	
		
			
				|  |  |  				if (!data.isSave) {
 | 
	
		
			
				|  |  | +					clearTimeout(saveTimer);
 | 
	
		
			
				|  |  |  					dialog.warning({
 | 
	
		
			
				|  |  |  						maskClosable: true,
 | 
	
		
			
				|  |  |  						autoFocus: false,
 | 
	
	
		
			
				|  | @@ -1423,7 +1432,7 @@ export default defineComponent({
 | 
	
		
			
				|  |  |  				msg.content = "导出失败";
 | 
	
		
			
				|  |  |  				return;
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  | -			saveAs(res.data.xml, (data.musicName || '曲谱') + ".xml");
 | 
	
		
			
				|  |  | +			saveAs(res.data.xml, (data.musicName || "曲谱") + ".xml");
 | 
	
		
			
				|  |  |  			msg.type = "success";
 | 
	
		
			
				|  |  |  			msg.content = "导出成功";
 | 
	
		
			
				|  |  |  		};
 | 
	
	
		
			
				|  | @@ -2053,14 +2062,13 @@ export default defineComponent({
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  							<div class={styles.topLine}></div>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -							<div
 | 
	
		
			
				|  |  | -								style={{ marginLeft: "auto" }}
 | 
	
		
			
				|  |  | -								class={styles.topBtn}
 | 
	
		
			
				|  |  | -								onClick={() => togglePlay("reset")}
 | 
	
		
			
				|  |  | -							>
 | 
	
		
			
				|  |  | -								<div class={styles.btnImg}>
 | 
	
		
			
				|  |  | -									<img class={styles.topBtnIcon} src={getImage("icon_20.png")} />
 | 
	
		
			
				|  |  | -								</div>
 | 
	
		
			
				|  |  | +							<div style={{ marginLeft: "auto" }} class={styles.topBtn}>
 | 
	
		
			
				|  |  | +								<NSpin show={data.loadingAudioSrouce} size="small">
 | 
	
		
			
				|  |  | +									<div class={styles.btnImg} onClick={() => togglePlay("reset")}>
 | 
	
		
			
				|  |  | +										<img class={styles.topBtnIcon} src={getImage("icon_20.png")} />
 | 
	
		
			
				|  |  | +									</div>
 | 
	
		
			
				|  |  | +								</NSpin>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  								<div>重播</div>
 | 
	
		
			
				|  |  |  							</div>
 | 
	
		
			
				|  |  |  							<div class={styles.topBtn}>
 |