فهرست منبع

Merge branch 'iteration-1.0.1'

liushengqiang 1 سال پیش
والد
کامیت
e3ca42f7e7
3فایلهای تغییر یافته به همراه289 افزوده شده و 217 حذف شده
  1. 111 115
      src/pc/home/component/file-btn/index.tsx
  2. 22 10
      src/pc/home/index.module.less
  3. 156 92
      src/pc/home/index.tsx

+ 111 - 115
src/pc/home/component/file-btn/index.tsx

@@ -4,121 +4,117 @@ import styles from "./index.module.less";
 import { getImage } from "../../images";
 import { DropdownMixedOption } from "naive-ui/es/dropdown/src/interface";
 
-/** 新建 | 保存 | 导入 | 上传 | 导出 | 打印 */
-export type IFileBtnType = "newMusic" | "save" | "xml" | "upload" | "png" | "wav" | "midi" | "print" | "exit";
+/** 新建 | 保存 | 导入 | 上传 | 导出 | 打印 | 退出*/
+export type IFileBtnType = "newMusic" | "save" | "xml" | "upload" | "png" | "wav" | "midi" | "print" | 'exit';
 
 export default defineComponent({
-  name: "FileBtn",
-  emits: ["select"],
-  setup(props, { emit }) {
-    const options: DropdownMixedOption[] = [
-      {
-        label: () => (
-          <div class={styles.dropItem}>
-            <img class={styles.dropIcon} src={getImage("icon_26_4.png")} />
-            <span>新建曲谱</span>
-          </div>
-        ),
-        key: "newMusic",
-      },
-      {
-        label: () => (
-          <div class={styles.dropItem}>
-            <img class={styles.dropIcon} src={getImage("icon_26_0.png")} />
-            <span>保存</span>
-          </div>
-        ),
-        key: "save",
-      },
-      // {
-      // 	label: () => (
-      // 		<div class={styles.dropItem}>
-      // 			<img class={styles.dropIcon} src={getImage("icon_26_0.png")} />
-      // 			<span>导入</span>
-      // 		</div>
-      // 	),
-      // 	key: "import",
-      // 	disabled: true,
-      // 	children: [
-      // 		{
-      // 			label: "xml",
-      //             key: 'xml',
-      // 			disabled: true
-      // 		},
-      // 	],
-      // },
-      {
-        label: () => (
-          <div class={styles.dropItem}>
-            <img class={styles.dropIcon} src={getImage("icon_26_1.png")} />
-            <span>上传到我的资源</span>
-          </div>
-        ),
-        key: "upload",
-        disabled: true,
-      },
-      {
-        label: () => (
-          <div class={styles.dropItem}>
-            <img class={styles.dropIcon} src={getImage("icon_26_2.png")} />
-            <span>导出</span>
-          </div>
-        ),
-        key: "export",
-        children: [
-          {
-            label: "PNG",
-            key: "png",
-          },
-          {
-            label: "WAV",
-            key: "wav",
-          },
-          {
-            label: "MIDI",
-            key: "midi",
-          },
-        ],
-      },
-      {
-        label: () => (
-          <div class={styles.dropItem}>
-            <img class={styles.dropIcon} src={getImage("icon_26_3.png")} />
-            <span>打印</span>
-          </div>
-        ),
-        key: "print",
-        disabled: true,
-      },
-      {
-        type: "divider",
-        key: "d1",
-      },
-      {
-        label: () => (
-          <div class={styles.dropItem}>
-            <img class={styles.dropIcon} src={getImage("icon_26_5.png")} />
-            <span>退出</span>
-          </div>
-        ),
-        key: "exit",
-        disabled: false,
-      },
-    ];
-    return () => (
-      <NDropdown
-        class={styles.dropWrap}
-        options={options}
-        trigger="click"
-        onSelect={(val) => {
-          console.log("🚀 ~ val:", val);
-          emit("select", val);
-        }}
-      >
-        <div class={styles.btnImg}>
-          <img class={styles.topBtnIcon} src={getImage("icon_0.png")} />
-        </div>
-      </NDropdown>
-    );
-  },
+	name: "FileBtn",
+	emits: ["select"],
+	setup(props, { emit }) {
+		const options: DropdownMixedOption[] = [
+			{
+				label: () => (
+					<div class={styles.dropItem}>
+						<img class={styles.dropIcon} src={getImage("icon_26_4.png")} />
+						<span>新建曲谱</span>
+					</div>
+				),
+				key: "newMusic",
+			},
+			{
+				label: () => (
+					<div class={styles.dropItem}>
+						<img class={styles.dropIcon} src={getImage("icon_26_0.png")} />
+						<span>保存</span>
+					</div>
+				),
+				key: "save",
+			},
+			// {
+			// 	label: () => (
+			// 		<div class={styles.dropItem}>
+			// 			<img class={styles.dropIcon} src={getImage("icon_26_0.png")} />
+			// 			<span>导入</span>
+			// 		</div>
+			// 	),
+			// 	key: "import",
+			// 	disabled: true,
+			// 	children: [
+			// 		{
+			// 			label: "xml",
+			//             key: 'xml',
+			// 			disabled: true
+			// 		},
+			// 	],
+			// },
+			{
+				label: () => (
+					<div class={styles.dropItem}>
+						<img class={styles.dropIcon} src={getImage("icon_26_1.png")} />
+						<span>上传到我的资源</span>
+					</div>
+				),
+				key: "upload",
+				disabled: true,
+			},
+			{
+				label: () => (
+					<div class={styles.dropItem}>
+						<img class={styles.dropIcon} src={getImage("icon_26_2.png")} />
+						<span>导出</span>
+					</div>
+				),
+				key: "export",
+				children: [
+					{
+						label: "PNG",
+						key: "png",
+					},
+					{
+						label: "WAV",
+						key: "wav",
+					},
+					{
+						label: "MIDI",
+						key: "midi",
+					},
+				],
+			},
+			{
+				label: () => (
+					<div class={styles.dropItem}>
+						<img class={styles.dropIcon} src={getImage("icon_26_3.png")} />
+						<span>打印</span>
+					</div>
+				),
+				key: "print",
+				disabled: true,
+			},
+			{
+				label: () => (
+					<div class={styles.dropItem}>
+						<img class={styles.dropIcon} src={getImage("icon_26_5.png")} />
+						<span>退出</span>
+					</div>
+				),
+				key: "exit",
+				disabled: false,
+			},
+		];
+		return () => (
+			<NDropdown
+				class={styles.dropWrap}
+				options={options}
+				trigger="click"
+				onSelect={(val) => {
+					console.log("🚀 ~ val:", val);
+					emit("select", val);
+				}}
+			>
+				<div class={styles.btnImg}>
+					<img class={styles.topBtnIcon} src={getImage("icon_0.png")} />
+				</div>
+			</NDropdown>
+		);
+	},
 });

+ 22 - 10
src/pc/home/index.module.less

@@ -23,6 +23,12 @@
     flex-wrap: wrap;
     padding-left: 20px;
     width: 100%;
+    .topBtn{
+        .btnImg{
+            width: 40px;
+            height: 40px;
+        }
+    }
 }
 
 .topBtn {
@@ -49,21 +55,13 @@
         }
     }
 
-
-
-    :global {
-        .arco-dropdown-open {
-            background-color: rgba(193, 219, 251, 1) !important;
-        }
-    }
-
     .btnImg.btnImgActive {
         background-color: rgba(193, 219, 251, 1);
     }
 
     .topBtnIcon {
-        width: 42px;
-        height: 42px;
+        width: 100%;
+        height: 100%;
     }
 }
 
@@ -268,6 +266,7 @@
 }
 
 .selectMearesBox {
+    width: 286px;
     position: fixed;
     padding: 12px !important;
     background: #FFFFFF;
@@ -275,6 +274,19 @@
     border-radius: 16px;
     border: 1px solid #F5F5F7;
     cursor: move;
+    .mearesInput{
+        display: flex;
+        align-items: center;
+        flex:1;
+        border: 1px solid rgb(224, 224, 230);
+        border-radius: 4px;
+        :global{
+            .n-input__placeholder,
+            .n-input__input-el{
+                text-align: center;
+            }
+        }
+    }
 }
 .selectMearesHidden{
     display: none;

+ 156 - 92
src/pc/home/index.tsx

@@ -194,9 +194,16 @@ export default defineComponent({
 			isClickNote: false,
 			/** 音符类型 */
 			noteType: "",
-			selectMeasure: {
-				start: "",
-				end: "",
+			/** 选择小节范围 */
+			selectMeasures: {
+				state: false,
+				x: 0,
+				y: 0,
+				start: 1,
+				startNote: null as any,
+				end: 0,
+				endNote: null as any,
+				max: 30,
 			},
 			slide: ["note", "meter", "dynamics"],
 
@@ -209,11 +216,6 @@ export default defineComponent({
 			loadingAudioSrouce: false, // 加载音频资源
 			/** 移调类型 */
 			moveKeyType: "inset" as "inset" | "up" | "down", // 移调类型
-			selectMearesDrag: {
-				state: false,
-				x: 0,
-				y: 0,
-			}, // 选择小节拖拽状态
 		});
 		const noteTypes = ABC_DATA.types.map((item) => item.value).filter(Boolean);
 		const accidentals = ABC_DATA.accidentals.map((item) => item.value).filter(Boolean);
@@ -268,7 +270,7 @@ export default defineComponent({
 				const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
 				if (totalTime) {
 					const progress = (data.active as any).currentTrackMilliseconds / 1000 / totalTime;
-					// console.log("🚀 ~ data.active:", data.active, progress);
+					// console.log("🚀 ~ data.active:", progress);
 					(abcData.synthControl as any).seek(progress);
 				}
 			}
@@ -330,27 +332,6 @@ export default defineComponent({
 			}
 		};
 
-		/**
-		 * 分词
-		 * @param str 字符串
-		 * @returns
-		 * @description
-		 */
-		const tokenize = (str: string) => {
-			const arr = str.split(/(!.+?!|".+?")/);
-			let output: string[] = [];
-			for (let i = 0; i < arr.length; i++) {
-				const token = arr[i];
-				if (token.length > 0) {
-					if (token[0] !== '"' && token[0] !== "!") {
-						const arr2 = arr[i].split(/([A-Ga-gz][,']*)/);
-						output = output.concat(arr2);
-					} else output.push(token);
-				}
-			}
-			return output;
-		};
-
 		const hideCursor = () => {
 			const cursor = document.querySelector("#paper svg .ABCJS-cursor");
 			if (cursor) {
@@ -362,13 +343,7 @@ export default defineComponent({
 		};
 
 		const cursorControl = {
-			// self.onReady = function () {
-			// 	var downloadLink = document.querySelector(".download");
-			// 	downloadLink.addEventListener("click", download);
-			// 	downloadLink.setAttribute("style", "");
-			// 	var clickEl = document.querySelector(".click-explanation");
-			// 	clickEl.setAttribute("style", "");
-			// };
+			onReady: function () {},
 			onStart: function () {
 				console.log("开始");
 				data.playState = true;
@@ -381,12 +356,28 @@ export default defineComponent({
 				cursor.setAttributeNS(null, "y2", "0");
 				svg?.appendChild(cursor);
 			},
-			// self.beatSubdivisions = 2;
 			onBeat: function (beatNumber: any, totalBeats: any, totalTime: any) {},
 			onEvent: (ev: any) => {
+				// console.log("🚀 ~ ev:", ev);
 				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 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)
+					) {
+						const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
+						if (totalTime) {
+							const progress = startTime / 1000 / totalTime;
+							nextTick(() => {
+								(abcData.synthControl as any).seek(progress);
+							});
+						}
+					}
+				}
 				var cursor = document.querySelector("#paper svg .ABCJS-cursor");
 				if (cursor) {
 					cursor.setAttribute("x1", ev.left + ev.width / 2);
@@ -433,7 +424,7 @@ export default defineComponent({
 						})
 						.then(function (response) {
 							data.loadingAudioSrouce = false;
-							// console.log("Audio successfully loaded.");
+							// console.log("Audio successfully loaded.", {...abcData.synthControl});
 							// console.log("🚀 ~ abcData.synthControl:", abcData.synthControl);
 						})
 						.catch((err) => {
@@ -443,7 +434,6 @@ export default defineComponent({
 		};
 
 		const togglePlay = (type: "play" | "pause" | "reset") => {
-			console.log("🚀 ~ abcData.synthControl:", abcData.synthControl);
 			if (type === "play") {
 				abcData.synthControl.play();
 				data.playState = true;
@@ -454,6 +444,7 @@ export default defineComponent({
 			} else {
 				abcData.synthControl.restart();
 			}
+			// console.log("🚀 ~ abcData.synthControl:", abcData.synthControl.timer.noteTimings);
 		};
 
 		const renderSvg = () => {
@@ -469,14 +460,18 @@ export default defineComponent({
 		const renderBoxRect = () => {
 			const svg = document.querySelector("#paper svg");
 			const padding = 4;
+			let measureNumber = 0;
 			for (let i = 0; i < abcData.visualObj.lines.length; i++) {
 				const line = abcData.visualObj.lines[i];
-				console.log("🚀 ~ line:", line);
+				// console.log("🚀 ~ line:", line);
 				for (let j = 0; j < line.staff.length; j++) {
 					const staff = line.staff[j];
 					const voices = [...staff.voices].flat();
 					for (let l = 0; l < voices.length; l++) {
 						const item = voices[l];
+						if (item.el_type === "bar") {
+							measureNumber++;
+						}
 						// console.log(item.el_type);
 						if (["note", "keySignature", "clef", "timeSignature"].includes(item.el_type)) {
 							const box = item.abselem.elemset?.[0]?.getBBox?.() || null;
@@ -497,21 +492,26 @@ export default defineComponent({
 					}
 				}
 			}
+			console.log(measureNumber);
+      data.selectMeasures.max = measureNumber;
 		};
 
 		/**
 		 * @param isProduct 是否是生成曲谱
 		 */
-		const handleResetRender = (isProduct = true) => {
+		const handleResetRender = () => {
 			return new Promise((resolve) => {
 				nextTick(() => {
-					data.music = isProduct ? renderMeasures(abcData.abc) : data.music;
+					data.music = renderMeasures(abcData.abc);
 					renderSvg();
 					resetMidi(data.drawCount > 0 ? true : false);
 					renderBoxRect();
 					resolve(1);
 					textAreaRef.value && (textAreaRef.value.value = data.music);
 					data.drawCount++;
+
+					// const times = new ABCJS.TimingCallbacks(abcData.visualObj);
+					// console.log("🚀 ~ times:", times)
 				});
 			});
 		};
@@ -1140,6 +1140,13 @@ export default defineComponent({
 			};
 			abcData.synthControl.restart();
 			// formateAbc(abcData.visualObj);
+			const selectMearesBtn = document.querySelector("#selectMearesBtn");
+			if (selectMearesBtn) {
+				const rect = selectMearesBtn.getBoundingClientRect();
+				data.selectMeasures.x = document.body.clientWidth - 320;
+				data.selectMeasures.y = rect.top + 70;
+				data.selectMeasures.state = true;
+			}
 		});
 		onUnmounted(() => {
 			document.removeEventListener("keyup", handleKeyUp);
@@ -1295,14 +1302,49 @@ export default defineComponent({
 					console.log("🚀 ~ abc:", abc);
 					abc = (window as any).vertaal(abc, { p: "f", t: 1, u: 0, v: 3, mnum: 0 });
 					console.log(abc);
-					data.music = abc[0];
-					handleResetRender(false);
+					// data.music = abc[0];
+					// handleResetRender(false);
 				};
 				reader.readAsText(file);
 			};
 			input.click();
 		};
 
+		/** 设置选段小节 */
+		const handleSetSelectMeares = (index: number | null, type: "start" | "end") => {
+			console.log("🚀 ~ index:", index);
+			if (type === "start") {
+				const note = index ? useIndexGetNote(`${index - 1}.0`) : null;
+				// console.log("🚀 ~ note:", note);
+				data.selectMeasures.start = index ? index - 1 : 0;
+				data.selectMeasures.startNote = note;
+				if (
+					data.selectMeasures.start &&
+					data.selectMeasures.end &&
+					data.selectMeasures.end < data.selectMeasures.start
+				) {
+					data.selectMeasures.end = 0;
+					data.selectMeasures.endNote = null;
+				}
+			} else {
+				const note = index
+					? useIndexGetNote(`${index - 1}.${abcData.abc.measures[index - 1]?.notes.length - 1}`)
+					: null;
+				// console.log("🚀 ~ note:", note);
+				data.selectMeasures.end = index ? index - 1 : 0;
+				data.selectMeasures.endNote = note;
+				if (
+					data.selectMeasures.start &&
+					data.selectMeasures.end &&
+					data.selectMeasures.start > data.selectMeasures.end
+				) {
+					// console.log(data.selectMeasures.start, data.selectMeasures.end);
+					data.selectMeasures.start = 0;
+					data.selectMeasures.startNote = null;
+				}
+			}
+		};
+
 		return () => (
 			<div class={styles.container}>
 				<div class={styles.containerTop} onKeyup={(e: Event) => e.stopPropagation()}>
@@ -1321,19 +1363,19 @@ export default defineComponent({
 										handleDownFile(val);
 									} else if (val === "print") {
 									} else if (val === "exit") {
-                    // 判断是否在应用中
-                    if (window.matchMedia("(display-mode: standalone)").matches) {
-                      window.onbeforeunload = null;
-                      window.parent.postMessage(
-                        {
-                          api: "notation_exit",
-                        },
-                        "*"
-                      );
-                    } else {
-                      window.close();
-                    }
-                  }
+										// 判断是否在应用中
+										if (window.matchMedia("(display-mode: standalone)").matches) {
+											window.onbeforeunload = null;
+											window.parent.postMessage(
+												{
+													api: "notation_exit",
+												},
+												"*"
+											);
+										} else {
+											window.close();
+										}
+									}
 								}}
 							/>
 							<div>文件</div>
@@ -1363,7 +1405,7 @@ export default defineComponent({
 								>
 									<img class={styles.topBtnIcon} src={item.icon} />
 								</div>
-								<div>{item.name}</div>
+								<div class={styles.btnName}>{item.name}</div>
 							</div>
 						))}
 
@@ -1813,6 +1855,7 @@ export default defineComponent({
 							<div>{data.playState ? "暂停" : "播放"}</div>
 						</div>
 						<div
+							id="selectMearesBtn"
 							class={[styles.topBtn]}
 							onClick={() => (popup.selectMearesShow = !popup.selectMearesShow)}
 						>
@@ -1988,38 +2031,59 @@ export default defineComponent({
 
 				<TheSetting v-model:show={popup.settingShow} />
 
-				{/* <UseDraggable
-					initialValue={{ x: 320, y: 60 }}
-					class={[styles.selectMearesBox, !popup.selectMearesShow && styles.selectMearesHidden]}
-				>
-					<NSpace justify="space-between">
-						<div class={styles.btnLineTitle}>输入小节范围</div>
-						<NButton circle quaternary size="small" onClick={() => (popup.selectMearesShow = false)}>
-							<NIcon size={16} component={<Close />} />
-						</NButton>
-					</NSpace>
-					<NSpace align="center">
-						<div style={{ width: "200px" }}>
-							<NInput pair placeholder={["开始小节", "结束小节"]}></NInput>
-						</div>
-						<div class={styles.topBtn}>
-							<NSpin show={data.loadingAudioSrouce} size="small">
-								<div class={styles.btnImg} onClick={() => togglePlay(data.playState ? "pause" : "play")}>
-									<img
-										style={{ display: data.playState ? "" : "none" }}
-										class={styles.topBtnIcon}
-										src={getImage("icon_21_1.png")}
-									/>
-									<img
-										style={{ display: data.playState ? "none" : "" }}
-										class={styles.topBtnIcon}
-										src={getImage("icon_21.png")}
-									/>
-								</div>
-							</NSpin>
-						</div>
-					</NSpace>
-				</UseDraggable> */}
+				{data.selectMeasures.state && (
+					<UseDraggable
+						initialValue={{ x: data.selectMeasures.x, y: data.selectMeasures.y }}
+						class={[styles.selectMearesBox, !popup.selectMearesShow && styles.selectMearesHidden]}
+					>
+						<NSpace justify="space-between">
+							<div class={styles.btnLineTitle}>输入小节范围</div>
+							<NButton circle quaternary size="small" onClick={() => (popup.selectMearesShow = false)}>
+								<NIcon size={16} component={<Close />} />
+							</NButton>
+						</NSpace>
+						<NSpace align="center" wrap={false} wrapItem={false}>
+							<div class={styles.mearesInput}>
+								<NInputNumber
+									min={1}
+									max={data.selectMeasures.max}
+									bordered={false}
+									placeholder="开始小节"
+									showButton={false}
+									onUpdate:value={(val) => handleSetSelectMeares(val, "start")}
+								></NInputNumber>
+								-
+								<NInputNumber
+									min={data.selectMeasures.start}
+									max={data.selectMeasures.max}
+									bordered={false}
+									placeholder="结束小节"
+									showButton={false}
+									onUpdate:value={(val) => handleSetSelectMeares(val, "end")}
+								></NInputNumber>
+							</div>
+							<div class={styles.topBtn}>
+								<NSpin show={data.loadingAudioSrouce} size="small">
+									<div
+										class={styles.btnImg}
+										onClick={() => togglePlay(data.playState ? "pause" : "play")}
+									>
+										<img
+											style={{ display: data.playState ? "" : "none" }}
+											class={styles.topBtnIcon}
+											src={getImage("icon_21_1.png")}
+										/>
+										<img
+											style={{ display: data.playState ? "none" : "" }}
+											class={styles.topBtnIcon}
+											src={getImage("icon_21.png")}
+										/>
+									</div>
+								</NSpin>
+							</div>
+						</NSpace>
+					</UseDraggable>
+				)}
 			</div>
 		);
 	},