Преглед на файлове

Merge branch 'iteration-1.0.5-节拍器'

liushengqiang преди 1 година
родител
ревизия
7965eee1b7
променени са 9 файла, в които са добавени 135 реда и са изтрити 49 реда
  1. 1 0
      package.json
  2. 7 0
      pnpm-lock.yaml
  3. 1 0
      src/env.d.ts
  4. 8 0
      src/pc/api.ts
  5. 6 1
      src/pc/home/component/file-btn/index.module.less
  6. 22 27
      src/pc/home/component/file-btn/index.tsx
  7. 12 0
      src/pc/home/index.module.less
  8. 43 10
      src/pc/home/index.tsx
  9. 35 11
      src/pc/home/runtime.ts

+ 1 - 0
package.json

@@ -22,6 +22,7 @@
     "consola": "^2.15.3",
     "dayjs": "^1.11.7",
     "eventemitter3": "^5.0.0",
+    "file-saver": "^2.0.5",
     "howler": "^2.2.3",
     "html2canvas": "^1.4.1",
     "lodash": "^4.17.21",

+ 7 - 0
pnpm-lock.yaml

@@ -40,6 +40,9 @@ dependencies:
   eventemitter3:
     specifier: ^5.0.0
     version: 5.0.0
+  file-saver:
+    specifier: ^2.0.5
+    version: 2.0.5
   howler:
     specifier: ^2.2.3
     version: 2.2.3
@@ -2772,6 +2775,10 @@ packages:
       reusify: 1.0.4
     dev: true
 
+  /file-saver@2.0.5:
+    resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
+    dev: false
+
   /fill-range@7.0.1:
     resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
     engines: {node: '>=8'}

+ 1 - 0
src/env.d.ts

@@ -5,3 +5,4 @@ declare module "*.vue" {
 
 	export default vueComponent;
 }
+declare module "file-saver"

+ 8 - 0
src/pc/api.ts

@@ -32,3 +32,11 @@ export const api_musicSheetCreationUpdate = (data: any) => {
 export const api_subjectList = () => {
 	return request.post(`/subject/list`);
 };
+
+/** 导入xml */
+export const api_xmlToAbc = (data: any) => {
+	return request.post(`/musicSheetCreation/xmlToAbc`, {
+		requestType: 'form',
+		data: data
+	});
+}

+ 6 - 1
src/pc/home/component/file-btn/index.module.less

@@ -39,10 +39,15 @@
         .n-dropdown-menu {
             min-width: 120px;
         }
+        .n-dropdown-option .n-dropdown-option{
+            min-width: 125px;
+        }
+        
 
         .n-dropdown-option-body {
+            margin: 4px 0;
             padding: 0 12px;
-            --n-option-height: 45px;
+            --n-option-height: 38px;
 
             .n-dropdown-option-body__prefix {
                 display: none;

+ 22 - 27
src/pc/home/component/file-btn/index.tsx

@@ -14,6 +14,7 @@ export type IFileBtnType =
 	| "wav"
 	| "midi"
 	| "print"
+	| "down-xml"
 	| "exit";
 
 export default defineComponent({
@@ -39,23 +40,23 @@ export default defineComponent({
 				),
 				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_0.png")} />
+						<span>导入</span>
+					</div>
+				),
+				key: "import",
+				// disabled: true,
+				children: [
+					{
+						label: "XML",
+						key: "xml",
+						// disabled: true,
+					},
+				],
+			},
 			{
 				label: () => (
 					<div class={styles.dropItem}>
@@ -76,6 +77,10 @@ export default defineComponent({
 				key: "export",
 				children: [
 					{
+						label: "XML",
+						key: "down-xml",
+					},
+					{
 						label: "PNG",
 						key: "png",
 					},
@@ -99,16 +104,6 @@ export default defineComponent({
 				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

+ 12 - 0
src/pc/home/index.module.less

@@ -168,6 +168,18 @@
 }
 
 :global {
+    .n-modal-scroll-content .n-modal-mask{
+        background-color: rgba(0, 0, 0, .6);
+    }
+    .deleteDialog.saveDialog{
+        width: 338px;
+        .n-dialog__title{
+            padding-right: 0 !important;
+        }
+        .n-dialog__content{
+            color: #777;
+        }
+    }
     .abcjs-note-hover {
         &:hover {
             fill: #ffe65948;

+ 43 - 10
src/pc/home/index.tsx

@@ -40,6 +40,7 @@ import {
 	NSelect,
 	NSpace,
 	NSpin,
+	useDialog,
 	useMessage,
 } from "naive-ui";
 import { LongArrowAltDown, LongArrowAltUp, GripLinesVertical } from "@vicons/fa";
@@ -56,6 +57,7 @@ import { UseDraggable } from "@vueuse/components";
 import { getQuery } from "/src/utils/queryString";
 import Metronome, { metronomeData } from "/src/helpers/metronome";
 import cleanDeep from "clean-deep";
+import { saveAs } from "file-saver";
 
 export const initMusic = (total: number): IMeasure[] => {
 	return new Array(total).fill(0).map((item, index) => {
@@ -102,6 +104,7 @@ function moveNote(note: string, step: number) {
 export default defineComponent({
 	name: "Home",
 	setup() {
+		const dialog = useDialog();
 		const route = useRoute();
 		const message = useMessage();
 		const popup = reactive({
@@ -621,11 +624,22 @@ export default defineComponent({
 
 			if (type === "exit") {
 				if (!data.isSave) {
-					showConfirmDialog({
+					dialog.warning({
+						maskClosable: true,
+						autoFocus: false,
+						class: "deleteDialog saveDialog",
 						title: "温馨提示",
-						message: "还没保存,是否保存?",
-					})
-						.then(async () => {
+						content: "是否保存当前曲谱?",
+						positiveText: "保存",
+						positiveButtonProps: {
+							type: "primary",
+						},
+						negativeText: "不保存",
+						negativeButtonProps: {
+							type: "default",
+							ghost: false,
+						},
+						onPositiveClick: async () => {
 							const msg = message.loading("保存中...");
 							await handleSaveMusic(false);
 							setTimeout(() => {
@@ -636,10 +650,11 @@ export default defineComponent({
 									handleClose();
 								}, 500);
 							}, 300);
-						})
-						.catch(() => {
+						},
+						onNegativeClick: () => {
 							handleClose();
-						});
+						},
+					});
 					return;
 				}
 				handleClose();
@@ -1005,7 +1020,7 @@ export default defineComponent({
 
 			// 移动音符
 			if (type === "move") {
-				const step = value._step ? value._step : value.action === "up" ? -1 : 1;
+				const step = value.action === "drag" ? value.step : value.action === "up" ? -1 : 1;
 				if (!activeNote) return;
 				activeNote.content = moveNote(activeNote.content, step);
 				// arr now contains elements that are either a chord, a decoration, a note name, or anything else. It can be put back to its original string with .join("").
@@ -1210,6 +1225,7 @@ export default defineComponent({
 				}
 			}
 			data.loading = false;
+			return res;
 		};
 		const handleSaveMusic = async (tips = true) => {
 			await api_musicSheetCreationUpdate({
@@ -1397,6 +1413,20 @@ export default defineComponent({
 			}
 		};
 
+		const downXML = async () => {
+			const msg = message.loading("导出中...");
+			await handleSaveMusic(false);
+			const res = await getDetailData();
+			if (!res?.data?.xml) {
+				msg.type = "error";
+				msg.content = "导出失败";
+				return;
+			}
+			saveAs(res.data.xml, (data.musicName || '曲谱') + ".xml");
+			msg.type = "success";
+			msg.content = "导出成功";
+		};
+
 		const handleDownFile = (type: IFileBtnType) => {
 			if (type === "png") {
 				downPng();
@@ -1404,6 +1434,8 @@ export default defineComponent({
 				downMidi();
 			} else if (type === "wav") {
 				downWav();
+			} else if (type === "down-xml") {
+				downXML();
 			}
 		};
 
@@ -1413,6 +1445,7 @@ export default defineComponent({
 			input.accept = ".xml,.musicxml";
 			input.onchange = (e: any) => {
 				const file = e.target.files[0];
+
 				const reader = new FileReader();
 				reader.onload = (e: any) => {
 					let abc = e.target.result;
@@ -1487,7 +1520,7 @@ export default defineComponent({
 										} else if (["xml"].includes(val)) {
 											handleExport();
 										} else if (val === "upload") {
-										} else if (["png", "midi", "wav"].includes(val)) {
+										} else if (["png", "midi", "wav", "down-xml"].includes(val)) {
 											handleDownFile(val);
 										} else if (val === "print") {
 										}
@@ -2301,7 +2334,7 @@ export default defineComponent({
 							)}
 
 							{/* <textarea ref={textAreaRef} class={styles.value} id="abc"></textarea> */}
-							<div id="importRef"></div>
+							<div id="importRef" style={{ display: "none" }}></div>
 							<div id="audio" style={{ display: "none" }}></div>
 							{data.loadingAudioSrouce && (
 								<div class={styles.loading}>

+ 35 - 11
src/pc/home/runtime.ts

@@ -323,6 +323,27 @@ export const formateAbc = (visualObj: TuneObject, option: any) => {
 	const list = [];
 	let notes = [];
 	let measureIndex = 0;
+
+	const get_accidental = (noteItem: INote) => {
+		let accidental = "";
+
+		if (noteItem.content.includes("_")) {
+			accidental = "_";
+		}
+		if (noteItem.content.includes("__")) {
+			accidental = "__";
+		}
+		if (noteItem.content.includes("=")) {
+			accidental = "=";
+		}
+		if (noteItem.content.includes("^")) {
+			accidental = "^";
+		}
+		if (noteItem.content.includes("^^")) {
+			accidental = "^^";
+		}
+		return accidental;
+	};
 	for (let i = 0; i < visualObj.lines.length; i++) {
 		const line = visualObj.lines[i];
 		if (line.staff) {
@@ -369,21 +390,14 @@ export const formateAbc = (visualObj: TuneObject, option: any) => {
 							}
 							if (element.el_type === "note") {
 								// const abcEle = visualObj.getElementFromChar(element.startChar);
-								console.log("🚀 ~ abcEle:", element);
-								let content = ''
-								if (element.rest) {
-									content = 'z';
-								} else {
-									content = element.pitches?.[0]?.name ?? "";
-								}
-								console.log(content)
-								const note = createNote({
+								// console.log("🚀 ~ abcEle:", element);
+								let noteItem = {
 									clef: "", //// 谱号
 									key: "", // 调号
 									speed: "", // 速度
 									slus: "", // 3连音
 									tie: "", // 连音线 前,连音线 后
-									content: content, // 音符
+									content: "", // 音符
 									noteType: formateGetData.getNoteType(element.duration), // 音符时值
 									play: [],
 									dynamics: "", // 力度符号
@@ -391,7 +405,17 @@ export const formateAbc = (visualObj: TuneObject, option: any) => {
 									dot: "", //附点
 									tieline: "", // 延音线
 									segno: "", // 分割
-								});
+								};
+								if (element.rest) {
+									noteItem.content = "z";
+								} else {
+									noteItem.content = element.pitches?.[0]?.name ?? "";
+								}
+								noteItem.accidental = get_accidental(noteItem as any);
+								if (noteItem.accidental) {
+									noteItem.content = noteItem.content.replace(noteItem.accidental, "");
+								}
+								const note = createNote(noteItem);
 								measure.notes.push(note);
 							}
 						}