浏览代码

上传资源到远端

liushengqiang 1 年之前
父节点
当前提交
d344e5c534

+ 13 - 0
src/pc/api.ts

@@ -40,3 +40,16 @@ export const api_xmlToAbc = (data: any) => {
 		data: data
 	});
 }
+/** 创建曲谱 */
+export const api_musicSheetSave = (data: any) => {
+	return request.post(`/musicSheet/save`, {
+		data: data,
+		requestType: 'json'
+	});
+}
+/** wav转mp3 */
+export const api_musicSheetCreationWav2mp3 = (data: any) => {
+	return request.get(`/musicSheetCreation/wav2mp3?url=` + data);
+}
+
+

+ 87 - 0
src/pc/component/upload-to-resources/index.module.less

@@ -0,0 +1,87 @@
+.setbox {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    width: 427px;
+    border-radius: 16px;
+    background: #fff;
+    overflow: hidden;
+}
+
+.head {
+    position: relative;
+    line-height: 53px;
+    height: 60px;
+    text-align: center;
+    background: #F5F6FA;
+    color: #131415;
+    font-weight: 600;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 16px;
+    flex-shrink: 0;
+
+    .close {
+        position: absolute;
+        top: 50%;
+        right: 20px;
+        transform: translateY(-50%);
+    }
+}
+
+.form {
+    padding: 26px;
+    :global {
+        .n-form-item .n-form-item-feedback-wrapper{
+            --n-feedback-height: 26px;
+        }
+        .n-form-item.n-form-item--left-labelled:last-child {
+            .n-form-item-feedback-wrapper {
+                display: none;
+            }
+        }
+        .n-form-item .n-form-item-label{
+            color: #666;
+        }
+        .n-base-selection{
+            --n-height: 46px;
+        }
+    }
+
+    .checkbox {
+        :global {
+            .n-button {
+                border-radius: 6px;
+                width: 73px;
+            }
+
+            .n-button--default-type {
+                background: #F5F6FA;
+                ;
+            }
+
+            .n-button--primary-type {
+                background: #198CFE;
+                color: #fff;
+            }
+        }
+    }
+}
+
+.btns {
+    padding-bottom: 26px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    :global {
+        .n-button {
+            width: 135px;
+            height: 41px;
+            border-radius: 20px;
+            border-color: #AAAAAA;
+            margin: 0 8px;
+        }
+    }
+}

+ 145 - 0
src/pc/component/upload-to-resources/index.tsx

@@ -0,0 +1,145 @@
+import { defineComponent, onMounted, reactive, watch } from "vue";
+import { api_musicSheetCreationWav2mp3, api_musicSheetSave, api_subjectList } from "../../api";
+import { NButton, NForm, NFormItem, NIcon, NModal, NSelect, NSpace } from "naive-ui";
+import styles from "./index.module.less";
+import { Close } from "@vicons/ionicons5";
+import { SelectMixedOption } from "naive-ui/es/select/src/interface";
+
+export default defineComponent({
+	name: "UploadToResources",
+	props: {
+		show: {
+			type: Boolean,
+			default: false,
+		},
+		item: {
+			type: Object,
+			default: () => ({}),
+		},
+	},
+	emits: ["update:show"],
+	setup(props, { emit }) {
+		const model = reactive({
+			subjects: [] as SelectMixedOption[],
+			saveLoading: false,
+		});
+		const froms = reactive({
+			subjectId: null,
+			isPublic: 0,
+			mp3: "",
+		});
+		const getSubjects = async () => {
+			const { data } = await api_subjectList();
+			model.subjects = data.map((item: any) => {
+				return {
+					label: item.name,
+					value: item.id,
+				};
+			});
+		};
+		onMounted(() => {
+			getSubjects();
+		});
+		watch(
+			() => props.item,
+			() => {
+				console.log(props.item);
+                froms.subjectId = props.item.subjectId ?? null;
+			}
+		);
+
+		const createMusic = async () => {
+			await api_musicSheetSave({
+				musicSheetName: props.item.name || "曲谱名称",
+				musicSheetCategoriesId: 29,
+				audioType: "MP3",
+				mp3Type: "MP3",
+				xmlFileUrl: props.item.xml,
+				musicSubject: froms.subjectId,
+				showFingering: 1,
+				canEvaluate: 1,
+				notation: 1,
+				playSpeed: props.item?.visualObj?.metaText?.tempo?.bpm || "80",
+				background: [
+					{
+						audioFileUrl: froms.mp3,
+						track: "",
+					},
+				],
+				musicImg: props.item.coverImg,
+				musicSvg: "",
+				musicJianSvg: "",
+				extConfigJson: "",
+			});
+		};
+		const wav2mp3 = async () => {
+			const { data } = await api_musicSheetCreationWav2mp3(props.item.filePath);
+			froms.mp3 = data;
+		};
+
+		const handleUpload = async () => {
+			model.saveLoading = true;
+
+			await wav2mp3();
+			await createMusic();
+			setTimeout(() => {
+				model.saveLoading = false;
+				emit("update:show", false);
+			}, 1000);
+		};
+		return () => (
+			<NModal autoFocus={false} show={props.show} onUpdate:show={(val) => emit("update:show", val)}>
+				<div class={styles.setbox}>
+					<div class={styles.head}>
+						<div>上传到我的资源</div>
+						<NButton
+							class={styles.close}
+							quaternary
+							circle
+							size="small"
+							onClick={() => emit("update:show", false)}
+						>
+							<NIcon component={Close} size={18} />
+						</NButton>
+					</div>
+					<NForm class={styles.form} labelPlacement="left">
+						<NFormItem label="可用声部">
+							<NSelect
+								to="body"
+								placeholder="请选择素材可用乐器"
+								options={model.subjects}
+								v-model:value={froms.subjectId}
+							></NSelect>
+						</NFormItem>
+						<NFormItem label="是否公开">
+							<NSpace class={styles.checkbox} wrapItem={false}>
+								<NButton
+									secondary
+									bordered={false}
+									type={froms.isPublic === 1 ? "primary" : "default"}
+									onClick={() => (froms.isPublic = 1)}
+								>
+									公开
+								</NButton>
+								<NButton
+									secondary
+									bordered={false}
+									type={froms.isPublic === 0 ? "primary" : "default"}
+									onClick={() => (froms.isPublic = 0)}
+								>
+									不公开
+								</NButton>
+							</NSpace>
+						</NFormItem>
+					</NForm>
+					<div class={styles.btns}>
+						<NButton onClick={() => emit("update:show", false)}>取消</NButton>
+						<NButton type="primary" loading={model.saveLoading} onClick={() => handleUpload()}>
+							确定
+						</NButton>
+					</div>
+				</div>
+			</NModal>
+		);
+	},
+});

+ 93 - 19
src/pc/create/index.module.less

@@ -1,7 +1,7 @@
 .createItem {
     position: relative;
-    width: 304px;
-    height: 233px;
+    width: 100%;
+    height: 100%;
     background: #F9FAFD;
     border: 2px solid rgba(209, 216, 235, 1);
     border-radius: 12px;
@@ -12,7 +12,8 @@
     font-size: 15px;
     font-weight: 600;
     cursor: pointer;
-    img{
+
+    img {
         width: 45px;
         height: 45px;
         margin-bottom: 23px;
@@ -20,14 +21,79 @@
 }
 
 .wrap {
-    padding: 24px;
+    padding: 24px 14px;
     border-radius: 15px;
     background-color: #fff;
 }
+
+.wrapBox {
+    display: flex;
+    flex-wrap: wrap;
+}
+
+.itemWrap {
+    position: relative;
+    width: calc(100% / 6);
+    padding-bottom: calc(100% / 6 * 0.76644);
+}
+
+@media screen and (max-width: 2400px) {
+    .wrapBox {
+        .itemWrap {
+            width: calc(100% / 5);
+            padding-bottom: calc(100% / 5 * 0.76644);
+        }
+    }
+}
+
+@media screen and (max-width: 1600px) {
+    .wrapBox {
+        .itemWrap {
+            width: calc(100% / 4);
+            padding-bottom: calc(100% / 4 * 0.76644);
+        }
+    }
+}
+
+@media screen and (max-width: 1200px) {
+    .wrapBox {
+        .itemWrap {
+            width: calc(100% / 3);
+            padding-bottom: calc(100% / 3 * 0.76644);
+        }
+    }
+}
+
+@media screen and (max-width: 820px) {
+    .wrapBox {
+        .itemWrap {
+            width: calc(100% / 2);
+            padding-bottom: calc(100% / 2 * 0.76644);
+        }
+    }
+}
+@media screen and (max-width: 600px) {
+    .wrapBox {
+        .itemWrap {
+            width: calc(100%);
+            padding-bottom: calc(100% * 0.76644);
+        }
+    }
+}
+
+.itemWrapBox {
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    padding: 10px;
+}
+
 .item {
     position: relative;
-    width: 304px;
-    height: 233px;
+    width: 100%;
+    height: 100%;
     background: #F9FAFD;
     border: 2px solid rgba(209, 216, 235, 1);
     border-radius: 12px;
@@ -35,58 +101,66 @@
     display: flex;
     flex-direction: column;
     overflow: hidden;
-    .icon_29{
+
+    .icon_29 {
         height: 14px;
         vertical-align: middle;
         margin-right: 6px;
         transform: translateY(-1px);
     }
-    .bottomBtn{
+
+    .bottomBtn {
         width: 30px;
         height: 30px;
         margin: 0 6px;
-        &:hover{
+
+        &:hover {
             opacity: .8;
         }
     }
-    .btn{
+
+    .btn {
         position: absolute;
         right: 10px;
         top: 16px;
         display: block;
         height: 30px;
-        &:hover{
-            opacity: .8;
-        }
     }
 
 }
-.imgBox{
+
+.imgBox {
     flex: 1;
     overflow: hidden;
     background-color: #fff;
+
     img {
         width: 100%;
     }
 }
-.itemBottom{
+
+.itemBottom {
     flex-shrink: 0;
     padding: 8px 12px;
     background: linear-gradient(to left, rgb(219, 241, 255) 0%, rgb(231, 249, 255) 100%);
     overflow: hidden;
-    .bottombox{
+
+    .bottombox {
         width: 100%;
         display: flex;
         align-items: center;
     }
-    .bottomLeft{
+
+    .bottomLeft {
         flex: 1;
         margin-right: 6px;
     }
-    .itemtitle{
+
+    .itemtitle {
         font-weight: 600;
     }
-    .time{
+
+    .time {
         font-size: 12px;
         color: #777;
     }

+ 189 - 170
src/pc/create/index.tsx

@@ -8,182 +8,201 @@ import { api_musicSheetCreationPage, api_musicSheetCreationRemove } from "../api
 import { useRouter } from "vue-router";
 import ABCJS from "abcjs";
 import { usePageVisibility } from "@vant/use";
+import UploadToResources from "../component/upload-to-resources";
 
 export default defineComponent({
-  name: "Create",
-  setup() {
-    const router = useRouter();
-    const dialog = useDialog();
-    console.log(storeData.user);
-    const forms = reactive({
-      teacherId: storeData.user.id,
-      page: 1,
-      keyword: "",
-      rows: 20,
-    });
-    const data = reactive({
-      list: [] as any[],
-      addShow: false,
-      loading: false,
-      finish: false,
-      isCreated: false,
-    });
-    const getList = async () => {
-      data.loading = true;
-      const res = await api_musicSheetCreationPage({ ...forms });
-      if (res?.code == 200) {
-        if (data.isCreated) {
-          data.isCreated = false;
-          handleOpenNotaion(res.data.rows[0]);
-        }
-        data.list = data.list.concat(res.data.rows);
-        data.finish = res.data.rows.length < forms.rows;
-      }
-      data.loading = false;
-    };
-    const handleReset = () => {
-      forms.page = 1;
-      data.finish = false;
-      data.list = [];
-      getList();
-    };
-    const pageVisibility = usePageVisibility();
-    watch(pageVisibility, (val) => {
-      if (val === "visible") {
-        handleReset();
-      }
-    });
+	name: "Create",
+	setup() {
+		const router = useRouter();
+		const dialog = useDialog();
+		console.log(storeData.user);
+		const forms = reactive({
+			teacherId: storeData.user.id,
+			page: 1,
+			keyword: "",
+			rows: 20,
+		});
+		const data = reactive({
+			list: [] as any[],
+			addShow: false,
+			loading: false,
+			finish: false,
+			isCreated: false,
+			uploadShow: false,
+			item: {} as any,
+		});
+		const getList = async () => {
+			data.loading = true;
+			const res = await api_musicSheetCreationPage({ ...forms });
+			if (res?.code == 200) {
+				if (data.isCreated) {
+					data.isCreated = false;
+					handleOpenNotaion(res.data.rows[0]);
+				}
+				data.list = data.list.concat(res.data.rows);
+				data.finish = res.data.rows.length < forms.rows;
+			}
+			data.loading = false;
+		};
+		const handleReset = () => {
+			forms.page = 1;
+			data.finish = false;
+			data.list = [];
+			getList();
+		};
+		const pageVisibility = usePageVisibility();
+		watch(pageVisibility, (val) => {
+			if (val === "visible") {
+				handleReset();
+			}
+		});
 
-    const handleDelte = (item: any) => {
-      const checked = ref(true);
-      dialog.warning({
-        autoFocus: false,
-        class: "deleteDialog",
-        title: "删除曲谱",
-        // content: () => (
-        // 	<div onClick={() => checked.value = !checked.value}>
-        // 		<NRadio checked={checked.value}>同步删除我的资源中的该曲目</NRadio>
-        // 	</div>
-        // ),
-        content: () => <div style={{ paddingRight: "calc(var(--n-close-size) + 6px)" }}>确认删除当前曲谱?</div>,
-        positiveText: "取消",
-        positiveButtonProps: {
-          type: "default",
-        },
-        negativeText: "删除",
-        negativeButtonProps: {
-          type: "primary",
-          ghost: false,
-        },
-        onPositiveClick: () => {},
-        onNegativeClick: async () => {
-          await api_musicSheetCreationRemove(item.id);
-          handleReset();
-        },
-      });
-    };
-    const loadingRef = ref();
+		const handleDelte = (item: any) => {
+			const checked = ref(true);
+			dialog.warning({
+				autoFocus: false,
+				class: "deleteDialog",
+				title: "删除曲谱",
+				// content: () => (
+				// 	<div onClick={() => checked.value = !checked.value}>
+				// 		<NRadio checked={checked.value}>同步删除我的资源中的该曲目</NRadio>
+				// 	</div>
+				// ),
+				content: () => (
+					<div>确认删除当前曲谱?</div>
+				),
+				positiveText: "取消",
+				positiveButtonProps: {
+					type: "default",
+				},
+				negativeText: "删除",
+				negativeButtonProps: {
+					type: "primary",
+					ghost: false,
+				},
+				onPositiveClick: () => {},
+				onNegativeClick: async () => {
+					await api_musicSheetCreationRemove(item.id);
+					handleReset();
+				},
+			});
+		};
+		const loadingRef = ref();
 
-    const messageEvent = (params?: any) => {
-      // 在老师端里面关闭要刷新
-      if (params.data?.api == "reload") {
-        handleReset();
-      }
-    };
-    onMounted(() => {
-      getList();
-      if (loadingRef.value) {
-        const obv = new IntersectionObserver((entries) => {
-          if (entries[0].isIntersecting) {
-            if (data.finish || data.loading) return;
-            forms.page++;
-            getList();
-          }
-        });
-        obv.observe(loadingRef.value?.$el);
-      }
+		const messageEvent = (params?: any) => {
+			// 在老师端里面关闭要刷新
+			if (params.data?.api == "reload") {
+				handleReset();
+			}
+		};
+		onMounted(() => {
+			getList();
+			if (loadingRef.value) {
+				const obv = new IntersectionObserver((entries) => {
+					if (entries[0].isIntersecting) {
+						if (data.finish || data.loading) return;
+						forms.page++;
+						getList();
+					}
+				});
+				obv.observe(loadingRef.value?.$el);
+			}
 
-      window.addEventListener("message", (params?: any) => {
-        messageEvent(params);
-      });
-    });
+			window.addEventListener("message", (params?: any) => {
+				messageEvent(params);
+			});
+		});
 
-    onUnmounted(() => {
-      window.removeEventListener("message", messageEvent);
-    });
-    const handleOpenNotaion = (item: any) => {
-      window.parent.postMessage(
-        {
-          api: "notation_open",
-          url: `${location.origin}/notation/#/?v=1.0.3&id=${item.id}`,
-        },
-        "*"
-      );
-    };
-    const productSvg = (abc: string, id: string) => {
-      if (abc) {
-        const a = ABCJS.renderAbc(id, abc, { selectTypes: false, add_classes: true });
-      }
-    };
-    return () => (
-      <div class={styles.wrap}>
-        <NSpace size={18}>
-          <div class={styles.createItem} onClick={() => (data.addShow = true)}>
-            <img src={getImage("icon_29.png")} />
-            <div>新建乐谱</div>
-          </div>
+		onUnmounted(() => {
+			window.removeEventListener("message", messageEvent);
+		});
+		const handleOpenNotaion = (item: any) => {
+			window.parent.postMessage(
+				{
+					api: "notation_open",
+					url: `${location.origin}/notation/#/?v=1.0.3&id=${item.id}`,
+				},
+				"*"
+			);
+		};
+		const productSvg = (abc: string, id: string) => {
+			const a = ABCJS.renderAbc(id, abc, { selectTypes: false, add_classes: true })[0];
+			return a;
+		};
+		return () => (
+			<div class={styles.wrap}>
+				<div class={styles.wrapBox}>
+					<div class={styles.itemWrap}>
+						<div class={styles.itemWrapBox}>
+							<div class={styles.createItem} onClick={() => (data.addShow = true)}>
+								<img src={getImage("icon_29.png")} />
+								<div>新建乐谱</div>
+							</div>
+						</div>
+					</div>
 
-          {data.list.map((item, index: number) => (
-            <div class={styles.item} onClick={() => handleOpenNotaion(item)}>
-              <div class={styles.imgBox} id={"item_" + index}>
-                <img
-                  src={getImage("icon_staff.png")}
-                  onLoad={() => {
-                    productSvg(item.creationConfig, "item_" + index);
-                  }}
-                />
-              </div>
-              <div class={styles.itemBottom}>
-                <div class={styles.bottombox}>
-                  <div class={styles.bottomLeft}>
-                    <div class={styles.itemtitle}>
-                      <img class={styles.icon_29} src={getImage("icon_29_1.png")} />
-                      <span>{item.name}</span>
-                    </div>
-                    <div class={styles.time}>{item.updateTime}</div>
-                  </div>
-                  {/* <img
-										style={{ pointerEvents: "none", opacity: 0.3 }}
-										class={styles.bottomBtn}
-										src={getImage("icon_29_2.png")}
-									/> */}
-                  <img
-                    class={styles.bottomBtn}
-                    src={getImage("icon_29_3.png")}
-                    onClick={(e: Event) => {
-                      e.stopPropagation();
-                      handleDelte(item);
-                    }}
-                  />
-                </div>
-              </div>
-              {/* <img class={styles.btn} src={getImage("icon_29_4.png")} /> */}
-            </div>
-          ))}
-        </NSpace>
-        {!data.finish && (
-          <NSpace ref={loadingRef} justify="center" style={{ padding: "30px" }}>
-            <NSpin size="large" />
-          </NSpace>
-        )}
+					{data.list.map((item, index: number) => (
+						<div class={styles.itemWrap}>
+							<div class={styles.itemWrapBox}>
+								<div class={styles.item} onClick={() => handleOpenNotaion(item)}>
+									<div class={styles.imgBox} id={"item_" + index}>
+										<img
+											src={getImage("icon_staff.png")}
+											onLoad={() => {
+												item.visualObj = productSvg(item.creationConfig, "item_" + index);
+											}}
+										/>
+									</div>
+									<div class={styles.itemBottom}>
+										<div class={styles.bottombox}>
+											<div class={styles.bottomLeft}>
+												<div class={styles.itemtitle}>
+													<img class={styles.icon_29} src={getImage("icon_29_1.png")} />
+													<span>{item.name}</span>
+												</div>
+												<div class={styles.time}>{item.updateTime}</div>
+											</div>
+											{/* <img
+												class={styles.bottomBtn}
+												src={getImage("icon_29_2.png")}
+												onClick={() => {
+													data.item = { ...item };
+													nextTick(() => {
+														data.uploadShow = true;
+													});
+												}}
+											/> */}
+											<img
+												class={styles.bottomBtn}
+												src={getImage("icon_29_3.png")}
+												onClick={(e: Event) => {
+													e.stopPropagation();
+													handleDelte(item);
+												}}
+											/>
+										</div>
+									</div>
+									{/* <img class={styles.btn} src={getImage("icon_29_4.png")} /> */}
+								</div>
+							</div>
+						</div>
+					))}
+				</div>
+				{!data.finish && (
+					<NSpace ref={loadingRef} justify="center" style={{ padding: "30px" }}>
+						<NSpin size="large" />
+					</NSpace>
+				)}
 
-        <TheCreate
-          v-model:show={data.addShow}
-          onCreate={() => {
-            data.addShow = false;
-          }}
-        />
-      </div>
-    );
-  },
+				<TheCreate
+					v-model:show={data.addShow}
+					onCreate={() => {
+						data.addShow = false;
+					}}
+				/>
+
+				<UploadToResources v-model:show={data.uploadShow} item={data.item} />
+			</div>
+		);
+	},
 });

+ 3 - 1
src/pc/theme.css

@@ -31,16 +31,18 @@ path {
 }
 
 .deleteDialog {
+    width: 400px !important;
     display: flex;
     flex-direction: column;
     border-radius: 12px;
-    min-height   : 178px;
+    min-height   : 190px;
 }
 
 .deleteDialog.n-dialog .n-dialog__title {
     justify-content: center;
     font-weight: 600;
     font-size: 16px;
+    padding-right: 0 !important;
 }
 
 .deleteDialog.n-dialog .n-dialog__icon {