Browse Source

导出图片有标题和作者

liushengqiang 1 year ago
parent
commit
0d8405f5e2
4 changed files with 800 additions and 744 deletions
  1. 8 0
      src/pc/home/index.module.less
  2. 782 743
      src/pc/home/index.tsx
  3. 8 1
      src/pc/home/runtime.ts
  4. 2 0
      src/pc/types.ts

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

@@ -324,4 +324,12 @@
     display: flex;
     justify-content: center;
     align-items: center;
+}
+.exportPng{
+    position: fixed;
+    left: 0;
+    top: 0;
+    width: 100%;
+    z-index: -10;
+    pointer-events: none;
 }

+ 782 - 743
src/pc/home/index.tsx

@@ -1328,20 +1328,17 @@ export default defineComponent({
 		};
 
 		const downPng = async () => {
-			await handleResetRender();
-			const paper = document.getElementById("paper");
+			abcData.abc.title = `T:${data.musicName}`;
+			abcData.abc.creator = `R:${data.creator}`;
+			const paper = document.getElementById("exportPng");
 			if (!paper) return;
+			const abc = renderMeasures(abcData.abc, { hiddenIndex: true, showTitle: true, showCreator: true });
+			ABCJS.renderAbc(paper, abc, abcData.abcOptions);
 			const svg: any = paper.children[0]?.cloneNode(true);
-			const annotation = svg.querySelectorAll(".abcjs-annotation");
-			annotation.forEach((n: HTMLElement) => {
-				n.remove();
-			});
 			const svgBox = paper.getBoundingClientRect();
-			console.log("🚀 ~ svgBox:", svgBox);
 			svg.setAttribute("width", `${svgBox.width * 3}`);
 			svg.setAttribute("height", `${svgBox.height * 3}`);
 			const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
-			console.log("🚀 ~ svg:", svg);
 			rect.setAttribute("x", "0");
 			rect.setAttribute("y", "0");
 			rect.setAttribute("width", `${svgBox.width * 10}`);
@@ -1462,866 +1459,908 @@ export default defineComponent({
 		};
 
 		return () => (
-			<div class={styles.container}>
-				<div class={styles.containerTop} onKeyup={(e: Event) => e.stopPropagation()}>
-					<div class={styles.topWrap}>
-						<div class={styles.topBtn} onClick={() => handleChange({ type: "exit", value: "exit" })}>
-							<div class={[styles.btnImg]}>
-								<img class={styles.topBtnIcon} src={getImage("icon_-1.png")} />
+			<>
+				<div class={styles.container}>
+					<div class={styles.containerTop} onKeyup={(e: Event) => e.stopPropagation()}>
+						<div class={styles.topWrap}>
+							<div class={styles.topBtn} onClick={() => handleChange({ type: "exit", value: "exit" })}>
+								<div class={[styles.btnImg]}>
+									<img class={styles.topBtnIcon} src={getImage("icon_-1.png")} />
+								</div>
+								<div>退出</div>
+							</div>
+							<div class={styles.topBtn}>
+								<FileBtn
+									onSelect={(val: IFileBtnType) => {
+										if (val === "newMusic") {
+											handleCreateMusic();
+										} else if (val === "save") {
+											handleSaveMusic();
+										} else if (["xml"].includes(val)) {
+											handleExport();
+										} else if (val === "upload") {
+										} else if (["png", "midi", "wav"].includes(val)) {
+											handleDownFile(val);
+										} else if (val === "print") {
+										}
+										//  else if (val === "exit") {
+
+										// }
+									}}
+								/>
+								<div>文件</div>
 							</div>
-							<div>退出</div>
-						</div>
-						<div class={styles.topBtn}>
-							<FileBtn
-								onSelect={(val: IFileBtnType) => {
-									if (val === "newMusic") {
-										handleCreateMusic();
-									} else if (val === "save") {
-										handleSaveMusic();
-									} else if (["xml"].includes(val)) {
-										handleExport();
-									} else if (val === "upload") {
-									} else if (["png", "midi", "wav"].includes(val)) {
-										handleDownFile(val);
-									} else if (val === "print") {
-									}
-									//  else if (val === "exit") {
-
-									// }
-								}}
-							/>
-							<div>文件</div>
-						</div>
 
-						<div class={styles.topLine}></div>
+							<div class={styles.topLine}></div>
 
-						<div class={styles.topBtn} onClick={() => handleChange({ type: "dot", value: ">" })}>
-							<div class={[styles.btnImg, noteComputed.value.dot === ">" && styles.btnImgActive]}>
-								<img class={styles.topBtnIcon} src={getImage("icon_1.png")} />
+							<div class={styles.topBtn} onClick={() => handleChange({ type: "dot", value: ">" })}>
+								<div class={[styles.btnImg, noteComputed.value.dot === ">" && styles.btnImgActive]}>
+									<img class={styles.topBtnIcon} src={getImage("icon_1.png")} />
+								</div>
+								<div>附点</div>
 							</div>
-							<div>附点</div>
-						</div>
 
-						<div class={styles.topLine}></div>
+							<div class={styles.topLine}></div>
+
+							{ABC_DATA.accidentals.map((item) => (
+								<div
+									class={styles.topBtn}
+									onClick={() => handleChange({ type: "accidentals", value: item.value })}
+								>
+									<div
+										class={[
+											styles.btnImg,
+											noteComputed.value.accidental === item.value && styles.btnImgActive,
+										]}
+									>
+										<img class={styles.topBtnIcon} src={item.icon} />
+									</div>
+									<div class={styles.btnName}>{item.name}</div>
+								</div>
+							))}
+
+							<div class={styles.topLine}></div>
 
-						{ABC_DATA.accidentals.map((item) => (
 							<div
 								class={styles.topBtn}
-								onClick={() => handleChange({ type: "accidentals", value: item.value })}
+								onClick={() => handleChange({ type: "tie", value: ABC_DATA.tie[0].value })}
 							>
 								<div
 									class={[
 										styles.btnImg,
-										noteComputed.value.accidental === item.value && styles.btnImgActive,
+										noteComputed.value.tieline === ABC_DATA.tie[0].value && styles.btnImgActive,
 									]}
 								>
-									<img class={styles.topBtnIcon} src={item.icon} />
+									<img class={styles.topBtnIcon} src={ABC_DATA.tie[0].icon} />
 								</div>
-								<div class={styles.btnName}>{item.name}</div>
-							</div>
-						))}
-
-						<div class={styles.topLine}></div>
-
-						<div
-							class={styles.topBtn}
-							onClick={() => handleChange({ type: "tie", value: ABC_DATA.tie[0].value })}
-						>
-							<div
-								class={[
-									styles.btnImg,
-									noteComputed.value.tieline === ABC_DATA.tie[0].value && styles.btnImgActive,
-								]}
-							>
-								<img class={styles.topBtnIcon} src={ABC_DATA.tie[0].icon} />
+								<div>{ABC_DATA.tie[0].name}</div>
 							</div>
-							<div>{ABC_DATA.tie[0].name}</div>
-						</div>
-
-						<div
-							class={styles.topBtn}
-							onClick={() => handleChange({ type: "tie", value: ABC_DATA.tie[1].value })}
-						>
-							<div
-								class={[
-									styles.btnImg,
-									ABC_DATA.tie[1].value.includes(noteComputed.value.tie) && styles.btnImgActive,
-								]}
-							>
-								<img class={styles.topBtnIcon} src={ABC_DATA.tie[1].icon} />
-							</div>
-							<div>{ABC_DATA.tie[1].name}</div>
-						</div>
-
-						<div class={styles.topLine}></div>
 
-						{ABC_DATA.play.slice(0, 4).map((item) => (
 							<div
-								class={[styles.topBtn]}
-								onClick={() => handleChange({ type: "play", value: item.value })}
+								class={styles.topBtn}
+								onClick={() => handleChange({ type: "tie", value: ABC_DATA.tie[1].value })}
 							>
 								<div
 									class={[
 										styles.btnImg,
-										noteComputed.value.play?.includes(item.value) && styles.btnImgActive,
+										ABC_DATA.tie[1].value.includes(noteComputed.value.tie) && styles.btnImgActive,
 									]}
 								>
-									<img class={styles.topBtnIcon} src={item.icon} />
+									<img class={styles.topBtnIcon} src={ABC_DATA.tie[1].icon} />
 								</div>
-								<div>{item.name}</div>
+								<div>{ABC_DATA.tie[1].name}</div>
 							</div>
-						))}
 
-						<NPopover
-							class={styles.popupWrap}
-							showArrow={false}
-							trigger="click"
-							contentStyle={{ width: "400px" }}
-						>
-							{{
-								trigger: () => (
-									<div class={styles.topDownArrow}>
-										<img src={getImage("icon_arrow.png")} />
+							<div class={styles.topLine}></div>
+
+							{ABC_DATA.play.slice(0, 4).map((item) => (
+								<div
+									class={[styles.topBtn]}
+									onClick={() => handleChange({ type: "play", value: item.value })}
+								>
+									<div
+										class={[
+											styles.btnImg,
+											noteComputed.value.play?.includes(item.value) && styles.btnImgActive,
+										]}
+									>
+										<img class={styles.topBtnIcon} src={item.icon} />
 									</div>
-								),
-								default: () => (
-									<NGrid cols={4} yGap={8}>
-										{ABC_DATA.play.slice(4).map((item) => (
-											<NGi>
-												<div
-													class={[
-														styles.btnItem,
-														noteComputed.value.play?.includes(item.value) && styles.btnItemActive,
-													]}
-													onClick={() => {
-														data.morePlay = false;
-														handleChange({ type: "play", value: item.value });
-													}}
-												>
-													<div class={styles.btnItemIcon}>
-														<TheIcon iconClassName={item.icon} />
+									<div>{item.name}</div>
+								</div>
+							))}
+
+							<NPopover
+								class={styles.popupWrap}
+								showArrow={false}
+								trigger="click"
+								contentStyle={{ width: "400px" }}
+							>
+								{{
+									trigger: () => (
+										<div class={styles.topDownArrow}>
+											<img src={getImage("icon_arrow.png")} />
+										</div>
+									),
+									default: () => (
+										<NGrid cols={4} yGap={8}>
+											{ABC_DATA.play.slice(4).map((item) => (
+												<NGi>
+													<div
+														class={[
+															styles.btnItem,
+															noteComputed.value.play?.includes(item.value) && styles.btnItemActive,
+														]}
+														onClick={() => {
+															data.morePlay = false;
+															handleChange({ type: "play", value: item.value });
+														}}
+													>
+														<div class={styles.btnItemIcon}>
+															<TheIcon iconClassName={item.icon} />
+														</div>
+														<div>{item.name}</div>
 													</div>
-													<div>{item.name}</div>
-												</div>
-											</NGi>
-										))}
-									</NGrid>
-								),
-							}}
-						</NPopover>
-
-						<div class={styles.topLine}></div>
-
-						<NDropdown
-							trigger="click"
-							options={ABC_DATA.slus as any}
-							labelField="name"
-							keyField="value"
-							onSelect={(val) => {
-								console.log(val);
-								handleChange({ type: "slus", value: val });
-							}}
-						>
-							<div class={[styles.topBtn]}>
-								<div class={styles.btnImg}>
-									<img class={styles.topBtnIcon} src={getImage("icon_13.png")} />
+												</NGi>
+											))}
+										</NGrid>
+									),
+								}}
+							</NPopover>
+
+							<div class={styles.topLine}></div>
+
+							<NDropdown
+								trigger="click"
+								options={ABC_DATA.slus as any}
+								labelField="name"
+								keyField="value"
+								onSelect={(val) => {
+									console.log(val);
+									handleChange({ type: "slus", value: val });
+								}}
+							>
+								<div class={[styles.topBtn]}>
+									<div class={styles.btnImg}>
+										<img class={styles.topBtnIcon} src={getImage("icon_13.png")} />
+									</div>
+									<div>连音</div>
 								</div>
-								<div>连音</div>
-							</div>
-						</NDropdown>
+							</NDropdown>
 
-						<div class={[styles.topBtn, styles.btnDisabled]}>
-							<div class={styles.btnImg}>
-								<img class={styles.topBtnIcon} src={getImage("icon_14.png")} />
+							<div class={[styles.topBtn, styles.btnDisabled]}>
+								<div class={styles.btnImg}>
+									<img class={styles.topBtnIcon} src={getImage("icon_14.png")} />
+								</div>
+								<div>翻转</div>
 							</div>
-							<div>翻转</div>
-						</div>
 
-						<div class={styles.topLine}></div>
+							<div class={styles.topLine}></div>
 
-						<NPopover
-							class={styles.popupWrap}
-							showArrow={false}
-							v-model:show={popup.selectSubjectShow}
-							trigger="click"
-							contentStyle={{ width: "320px" }}
-						>
-							{{
-								trigger: () => (
-									<div class={styles.topBtn}>
-										<div class={styles.btnImg} onClick={() => (popup.instrument = true)}>
-											<img class={styles.topBtnIcon} src={getImage("icon_25.png")} />
+							<NPopover
+								class={styles.popupWrap}
+								showArrow={false}
+								v-model:show={popup.selectSubjectShow}
+								trigger="click"
+								contentStyle={{ width: "320px" }}
+							>
+								{{
+									trigger: () => (
+										<div class={styles.topBtn}>
+											<div class={styles.btnImg} onClick={() => (popup.instrument = true)}>
+												<img class={styles.topBtnIcon} src={getImage("icon_25.png")} />
+											</div>
+											<div>选择声部</div>
 										</div>
-										<div>选择声部</div>
-									</div>
-								),
-								default: () => (
-									<>
-										<div class={styles.btnLineTitle}>选择声部</div>
-
-										<NSelect
-											filterable
-											options={instruments.value}
-											v-model:value={abcData.synthOptions.program}
-											onUpdate:value={async () => {
-												abcData.synthControl.disable(true);
-												data.playState = false;
-												await loadMiniMp3();
-												resetMidi(true);
-												popup.selectSubjectShow = false;
-											}}
-										></NSelect>
-									</>
-								),
-							}}
-						</NPopover>
-
-						<NPopover
-							class={styles.popupWrap}
-							showArrow={false}
-							v-model:show={popup.moveKeyShow}
-							trigger="click"
-							contentStyle={{ width: "320px" }}
-						>
-							{{
-								trigger: () => (
-									<div class={styles.topBtn}>
-										<div class={styles.btnImg}>
-											<img class={styles.topBtnIcon} src={getImage("icon_15.png")} />
+									),
+									default: () => (
+										<>
+											<div class={styles.btnLineTitle}>选择声部</div>
+
+											<NSelect
+												filterable
+												options={instruments.value}
+												v-model:value={abcData.synthOptions.program}
+												onUpdate:value={async () => {
+													abcData.synthControl.disable(true);
+													data.playState = false;
+													await loadMiniMp3();
+													resetMidi(true);
+													popup.selectSubjectShow = false;
+												}}
+											></NSelect>
+										</>
+									),
+								}}
+							</NPopover>
+
+							<NPopover
+								class={styles.popupWrap}
+								showArrow={false}
+								v-model:show={popup.moveKeyShow}
+								trigger="click"
+								contentStyle={{ width: "320px" }}
+							>
+								{{
+									trigger: () => (
+										<div class={styles.topBtn}>
+											<div class={styles.btnImg}>
+												<img class={styles.topBtnIcon} src={getImage("icon_15.png")} />
+											</div>
+											<div>移调</div>
 										</div>
-										<div>移调</div>
-									</div>
-								),
-								default: () => (
-									<>
-										<div class={styles.btnLineTitle}>移调方式</div>
-
-										<NSpace>
-											<NButton
-												secondary
-												type={data.moveKeyType === "inset" ? "primary" : "default"}
-												onClick={() => (data.moveKeyType = "inset")}
-											>
-												<NIcon component={GripLinesVertical} />
-												最靠近
-											</NButton>
-											<NButton
-												secondary
-												type={data.moveKeyType === "down" ? "primary" : "default"}
-												onClick={() => (data.moveKeyType = "down")}
-											>
-												<NIcon component={LongArrowAltDown} />
-												向下移调
-											</NButton>
-											<NButton
-												secondary
-												type={data.moveKeyType === "up" ? "primary" : "default"}
-												onClick={() => (data.moveKeyType = "up")}
-											>
-												<NIcon component={LongArrowAltUp} />
-												向上移调
-											</NButton>
-										</NSpace>
-
-										<div class={styles.btnLineTitle}>目标音调</div>
-
-										<NGrid cols={5} yGap={8}>
-											{ABC_DATA.key
-												.sort((a, b) => b.step - a.step)
-												.map((item) => (
-													<NGi>
-														<div
-															class={[
-																styles.btnItem,
-																abcData.abc.key === item.value && styles.btnItemActive,
-															]}
-															onClick={() => handleMoveKey(item)}
-														>
-															<div class={[styles.btnItemIcon]}>
-																<TheIcon iconClassName={item.icon} />
+									),
+									default: () => (
+										<>
+											<div class={styles.btnLineTitle}>移调方式</div>
+
+											<NSpace>
+												<NButton
+													secondary
+													type={data.moveKeyType === "inset" ? "primary" : "default"}
+													onClick={() => (data.moveKeyType = "inset")}
+												>
+													<NIcon component={GripLinesVertical} />
+													最靠近
+												</NButton>
+												<NButton
+													secondary
+													type={data.moveKeyType === "down" ? "primary" : "default"}
+													onClick={() => (data.moveKeyType = "down")}
+												>
+													<NIcon component={LongArrowAltDown} />
+													向下移调
+												</NButton>
+												<NButton
+													secondary
+													type={data.moveKeyType === "up" ? "primary" : "default"}
+													onClick={() => (data.moveKeyType = "up")}
+												>
+													<NIcon component={LongArrowAltUp} />
+													向上移调
+												</NButton>
+											</NSpace>
+
+											<div class={styles.btnLineTitle}>目标音调</div>
+
+											<NGrid cols={5} yGap={8}>
+												{ABC_DATA.key
+													.sort((a, b) => b.step - a.step)
+													.map((item) => (
+														<NGi>
+															<div
+																class={[
+																	styles.btnItem,
+																	abcData.abc.key === item.value && styles.btnItemActive,
+																]}
+																onClick={() => handleMoveKey(item)}
+															>
+																<div class={[styles.btnItemIcon]}>
+																	<TheIcon iconClassName={item.icon} />
+																</div>
+																<div class={styles.btnItemName}>{item.name}</div>
 															</div>
-															<div class={styles.btnItemName}>{item.name}</div>
-														</div>
-													</NGi>
-												))}
-										</NGrid>
-									</>
-								),
-							}}
-						</NPopover>
-
-						<NPopover
-							class={styles.popupWrap}
-							showArrow={false}
-							v-model:value={popup.speedShow}
-							trigger="click"
-							placement="bottom"
-							displayDirective="show"
-						>
-							{{
-								trigger: () => (
-									<div class={[styles.topBtn]}>
-										<div class={styles.btnImg}>
-											<img class={styles.topBtnIcon} src={getImage("icon_16.png")} />
+														</NGi>
+													))}
+											</NGrid>
+										</>
+									),
+								}}
+							</NPopover>
+
+							<NPopover
+								class={styles.popupWrap}
+								showArrow={false}
+								v-model:value={popup.speedShow}
+								trigger="click"
+								placement="bottom"
+								displayDirective="show"
+							>
+								{{
+									trigger: () => (
+										<div class={[styles.topBtn]}>
+											<div class={styles.btnImg}>
+												<img class={styles.topBtnIcon} src={getImage("icon_16.png")} />
+											</div>
+											<div>速度调整</div>
 										</div>
-										<div>速度调整</div>
-									</div>
-								),
-								default: () => <TheSpeed onChange={(val) => handleChange(val)} />,
-							}}
-						</NPopover>
-
-						<NPopover
-							class={styles.popupWrap}
-							showArrow={false}
-							v-model:show={popup.staffShow}
-							trigger="click"
-							placement="bottom"
-							contentStyle={{ width: "320px" }}
-						>
-							{{
-								trigger: () => (
-									<div class={[styles.topBtn]}>
-										<div class={styles.btnImg}>
-											<img class={styles.topBtnIcon} src={getImage("icon_17.png")} />
+									),
+									default: () => <TheSpeed onChange={(val) => handleChange(val)} />,
+								}}
+							</NPopover>
+
+							<NPopover
+								class={styles.popupWrap}
+								showArrow={false}
+								v-model:show={popup.staffShow}
+								trigger="click"
+								placement="bottom"
+								contentStyle={{ width: "320px" }}
+							>
+								{{
+									trigger: () => (
+										<div class={[styles.topBtn]}>
+											<div class={styles.btnImg}>
+												<img class={styles.topBtnIcon} src={getImage("icon_17.png")} />
+											</div>
+											<div>谱面显示</div>
 										</div>
-										<div>谱面显示</div>
-									</div>
-								),
-								default: () => (
-									<>
-										<div class={styles.btnLineTitle}>乐谱大小</div>
-
-										<NSpace>
-											<NButton
-												type={abcData.abcOptions.staffwidth === 1200 ? "primary" : "default"}
-												secondary
-												onClick={() => {
-													abcData.abcOptions.staffwidth = 1200;
-													handleResetRender();
-												}}
-											>
-												小
-											</NButton>
-											<NButton
-												type={abcData.abcOptions.staffwidth === 800 ? "primary" : "default"}
-												secondary
-												onClick={() => {
-													abcData.abcOptions.staffwidth = 800;
-													handleResetRender();
-												}}
-											>
-												中
-											</NButton>
-											<NButton
-												type={abcData.abcOptions.staffwidth === 400 ? "primary" : "default"}
-												secondary
-												onClick={() => {
-													abcData.abcOptions.staffwidth = 400;
-													handleResetRender();
-												}}
-											>
-												大
-											</NButton>
-										</NSpace>
-
-										<div class={styles.btnLineTitle}>小节数</div>
-
-										<NSpace vertical>
-											<NInputNumber
-												min={1}
-												v-model:value={(abcData.abcOptions.wrap as any).preferredMeasuresPerLine}
-												placeholder="请输入小节数"
-												onUpdate:value={() => {
-													handleResetRender();
-												}}
-											/>
+									),
+									default: () => (
+										<>
+											<div class={styles.btnLineTitle}>乐谱大小</div>
 
-											{/* <NSpace style={{ marginTop: "20px" }} align="center" wrap={false} wrapItem={false}>
-												<NButton style={{ width: "48%" }} round onClick={() => (popup.staffShow = false)}>
-													取消
+											<NSpace>
+												<NButton
+													type={abcData.abcOptions.staffwidth === 1200 ? "primary" : "default"}
+													secondary
+													onClick={() => {
+														abcData.abcOptions.staffwidth = 1200;
+														handleResetRender();
+													}}
+												>
+													小
 												</NButton>
 												<NButton
-													style={{ width: "48%" }}
-													round
-													type="primary"
+													type={abcData.abcOptions.staffwidth === 800 ? "primary" : "default"}
+													secondary
+													onClick={() => {
+														abcData.abcOptions.staffwidth = 800;
+														handleResetRender();
+													}}
 												>
-													确定
+													中
 												</NButton>
-											</NSpace> */}
-										</NSpace>
-									</>
-								),
-							}}
-						</NPopover>
-
-						<NPopover
-							class={styles.popupWrap}
-							showArrow={false}
-							v-model:show={popup.barShow}
-							trigger="click"
-							placement="bottom"
-							contentStyle={{ width: "320px" }}
-						>
-							{{
-								trigger: () => (
-									<div class={[styles.topBtn]}>
-										<div class={styles.btnImg}>
-											<img class={styles.topBtnIcon} src={getImage("icon_18.png")} />
-										</div>
-										<div>添加小节</div>
-									</div>
-								),
-								default: () => (
-									<>
-										<div class={styles.btnLineTitle}>添加方式</div>
-
-										<NSpace>
-											<NButton
-												type={data.addMearseType === "pre" ? "primary" : "default"}
-												secondary
-												onClick={() => (data.addMearseType = "pre")}
-											>
-												当前小节前
-											</NButton>
-											<NButton
-												type={data.addMearseType === "next" ? "primary" : "default"}
-												secondary
-												onClick={() => (data.addMearseType = "next")}
-											>
-												当前小节后
-											</NButton>
-											<NButton
-												type={data.addMearseType === "finish" ? "primary" : "default"}
-												secondary
-												onClick={() => (data.addMearseType = "finish")}
-											>
-												曲谱末尾
-											</NButton>
-										</NSpace>
+												<NButton
+													type={abcData.abcOptions.staffwidth === 400 ? "primary" : "default"}
+													secondary
+													onClick={() => {
+														abcData.abcOptions.staffwidth = 400;
+														handleResetRender();
+													}}
+												>
+													大
+												</NButton>
+											</NSpace>
 
-										<div class={styles.btnLineTitle}>小节数</div>
+											<div class={styles.btnLineTitle}>小节数</div>
 
-										<NSpace vertical>
-											<NInputNumber
-												min={1}
-												v-model:value={data.addMearseNumber}
-												placeholder="请输入小节数"
-											/>
+											<NSpace vertical>
+												<NInputNumber
+													min={1}
+													v-model:value={(abcData.abcOptions.wrap as any).preferredMeasuresPerLine}
+													placeholder="请输入小节数"
+													onUpdate:value={() => {
+														handleResetRender();
+													}}
+												/>
 
-											<NSpace style={{ marginTop: "20px" }} align="center" wrap={false} wrapItem={false}>
-												<NButton style={{ width: "48%" }} round onClick={() => (popup.barShow = false)}>
+												{/* <NSpace style={{ marginTop: "20px" }} align="center" wrap={false} wrapItem={false}>
+												<NButton style={{ width: "48%" }} round onClick={() => (popup.staffShow = false)}>
 													取消
 												</NButton>
 												<NButton
 													style={{ width: "48%" }}
 													round
 													type="primary"
-													onClick={() => handleAddMearse()}
 												>
 													确定
 												</NButton>
+											</NSpace> */}
 											</NSpace>
-										</NSpace>
-									</>
-								),
-							}}
-						</NPopover>
-
-						<NPopover
-							class={styles.popupWrap}
-							v-model:show={popup.mearseDeleteShow}
-							trigger="click"
-							placement="bottom"
-						>
-							{{
-								trigger: () => (
-									<div class={[styles.topBtn]}>
-										<div class={styles.btnImg}>
-											<img class={styles.topBtnIcon} src={getImage("icon_19.png")} />
+										</>
+									),
+								}}
+							</NPopover>
+
+							<NPopover
+								class={styles.popupWrap}
+								showArrow={false}
+								v-model:show={popup.barShow}
+								trigger="click"
+								placement="bottom"
+								contentStyle={{ width: "320px" }}
+							>
+								{{
+									trigger: () => (
+										<div class={[styles.topBtn]}>
+											<div class={styles.btnImg}>
+												<img class={styles.topBtnIcon} src={getImage("icon_18.png")} />
+											</div>
+											<div>添加小节</div>
 										</div>
-										<div>删除小节</div>
-									</div>
-								),
-								default: () => (
-									<>
-										<div class={styles.btnLineTitle}>删除方式</div>
+									),
+									default: () => (
+										<>
+											<div class={styles.btnLineTitle}>添加方式</div>
 
-										<NSpace vertical>
 											<NSpace>
 												<NButton
-													type={data.deleteMearseType === "ing" ? "primary" : "default"}
+													type={data.addMearseType === "pre" ? "primary" : "default"}
 													secondary
-													onClick={() => (data.deleteMearseType = "ing")}
+													onClick={() => (data.addMearseType = "pre")}
 												>
-													当前选中小节
+													当前小节
 												</NButton>
 												<NButton
-													type={data.deleteMearseType === "finish" ? "primary" : "default"}
+													type={data.addMearseType === "next" ? "primary" : "default"}
 													secondary
-													onClick={() => (data.deleteMearseType = "finish")}
+													onClick={() => (data.addMearseType = "next")}
 												>
-													末尾空白小节
+													当前小节后
 												</NButton>
-											</NSpace>
-
-											<NSpace style={{ marginTop: "20px" }} align="center" wrap={false} wrapItem={false}>
 												<NButton
-													style={{ width: "48%" }}
-													round
-													onClick={() => (popup.mearseDeleteShow = false)}
+													type={data.addMearseType === "finish" ? "primary" : "default"}
+													secondary
+													onClick={() => (data.addMearseType = "finish")}
 												>
-													取消
+													曲谱末尾
 												</NButton>
-												<NButton
-													style={{ width: "48%" }}
-													round
-													type="primary"
-													onClick={() => handleDeleteMearse()}
+											</NSpace>
+
+											<div class={styles.btnLineTitle}>小节数</div>
+
+											<NSpace vertical>
+												<NInputNumber
+													min={1}
+													v-model:value={data.addMearseNumber}
+													placeholder="请输入小节数"
+												/>
+
+												<NSpace
+													style={{ marginTop: "20px" }}
+													align="center"
+													wrap={false}
+													wrapItem={false}
 												>
-													确定
-												</NButton>
+													<NButton
+														style={{ width: "48%" }}
+														round
+														onClick={() => (popup.barShow = false)}
+													>
+														取消
+													</NButton>
+													<NButton
+														style={{ width: "48%" }}
+														round
+														type="primary"
+														onClick={() => handleAddMearse()}
+													>
+														确定
+													</NButton>
+												</NSpace>
 											</NSpace>
-										</NSpace>
-									</>
-								),
-							}}
-						</NPopover>
-
-						<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")} />
+										</>
+									),
+								}}
+							</NPopover>
+
+							<NPopover
+								class={styles.popupWrap}
+								v-model:show={popup.mearseDeleteShow}
+								trigger="click"
+								placement="bottom"
+							>
+								{{
+									trigger: () => (
+										<div class={[styles.topBtn]}>
+											<div class={styles.btnImg}>
+												<img class={styles.topBtnIcon} src={getImage("icon_19.png")} />
+											</div>
+											<div>删除小节</div>
+										</div>
+									),
+									default: () => (
+										<>
+											<div class={styles.btnLineTitle}>删除方式</div>
+
+											<NSpace vertical>
+												<NSpace>
+													<NButton
+														type={data.deleteMearseType === "ing" ? "primary" : "default"}
+														secondary
+														onClick={() => (data.deleteMearseType = "ing")}
+													>
+														当前选中小节
+													</NButton>
+													<NButton
+														type={data.deleteMearseType === "finish" ? "primary" : "default"}
+														secondary
+														onClick={() => (data.deleteMearseType = "finish")}
+													>
+														末尾空白小节
+													</NButton>
+												</NSpace>
+
+												<NSpace
+													style={{ marginTop: "20px" }}
+													align="center"
+													wrap={false}
+													wrapItem={false}
+												>
+													<NButton
+														style={{ width: "48%" }}
+														round
+														onClick={() => (popup.mearseDeleteShow = false)}
+													>
+														取消
+													</NButton>
+													<NButton
+														style={{ width: "48%" }}
+														round
+														type="primary"
+														onClick={() => handleDeleteMearse()}
+													>
+														确定
+													</NButton>
+												</NSpace>
+											</NSpace>
+										</>
+									),
+								}}
+							</NPopover>
+
+							<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>重播</div>
 							</div>
-							<div>重播</div>
-						</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 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>{data.playState ? "暂停" : "播放"}</div>
+							</div>
+							<div
+								id="selectMearesBtn"
+								class={[styles.topBtn]}
+								onClick={() => (popup.selectMearesShow = !popup.selectMearesShow)}
+							>
+								<div class={[styles.btnImg, popup.selectMearesShow && styles.btnImgActive]}>
+									<img class={styles.topBtnIcon} src={getImage("icon_22.png")} />
 								</div>
-							</NSpin>
-							<div>{data.playState ? "暂停" : "播放"}</div>
-						</div>
-						<div
-							id="selectMearesBtn"
-							class={[styles.topBtn]}
-							onClick={() => (popup.selectMearesShow = !popup.selectMearesShow)}
-						>
-							<div class={[styles.btnImg, popup.selectMearesShow && styles.btnImgActive]}>
-								<img class={styles.topBtnIcon} src={getImage("icon_22.png")} />
+								<div>选段</div>
 							</div>
-							<div>选段</div>
-						</div>
 
-						<div class={[styles.topBtn, styles.btnDisabled]}>
-							<div class={styles.btnImg}>
-								<img class={styles.topBtnIcon} src={getImage("icon_23.png")} />
+							<div class={[styles.topBtn, styles.btnDisabled]}>
+								<div class={styles.btnImg}>
+									<img class={styles.topBtnIcon} src={getImage("icon_23.png")} />
+								</div>
+								<div>节拍器</div>
 							</div>
-							<div>节拍器</div>
-						</div>
-						<div class={[styles.topBtn]} onClick={() => (popup.settingShow = true)}>
-							<div class={styles.btnImg}>
-								<img class={styles.topBtnIcon} src={getImage("icon_24.png")} />
+							<div class={[styles.topBtn]} onClick={() => (popup.settingShow = true)}>
+								<div class={styles.btnImg}>
+									<img class={styles.topBtnIcon} src={getImage("icon_24.png")} />
+								</div>
+								<div>设置</div>
 							</div>
-							<div>设置</div>
 						</div>
 					</div>
-				</div>
-				<div class={styles.content}>
-					<div class={styles.slide}>
-						<Collapse v-model={data.slide} elevation={false} divider={false}>
-							<CollapseItem title="音符时值" name="note">
-								<div class={styles.wrapBox}>
-									{ABC_DATA.types.map((item) => (
-										<div
-											class={styles.topBtn}
-											onClick={() => handleChange({ type: "type", value: item.value })}
-										>
-											<div class={[styles.btnImg, data.noteType === item.value && styles.btnImgActive]}>
-												<TheIcon iconClassName={item.icon} />
-											</div>
-											<div>{item.name}</div>
-										</div>
-									))}
-									<div class={styles.topBtn} onClick={() => handleChange({ type: "note", value: "z" })}>
-										<div
-											class={[styles.btnImg, noteComputed.value.content === "z" && styles.btnImgActive]}
-										>
-											<img style={{ width: "20px", height: "20px" }} src={getImage("icon_rest.png")} />
-										</div>
-										<div>休止符</div>
-									</div>
-
-									<div class={styles.topBtn} onClick={() => handleChange({ type: "segno", value: " " })}>
-										<div
-											class={[styles.btnImg, noteComputed.value.segno === " " && styles.btnImgActive]}
-										>
-											{/* <img style={{ width: "20px", height: "20px" }} src={getImage("icon_rest.png")} /> */}
-										</div>
-										<div>分割</div>
-									</div>
-								</div>
-							</CollapseItem>
-
-							<CollapseItem title="拍号" name="meter">
-								<div class={styles.wrapBox}>
-									{ABC_DATA.meter.map((item) => (
-										<div
-											class={styles.topBtn}
-											onClick={() => handleChange({ type: "meter", value: item.value })}
-										>
-											<div class={[styles.btnImg]}>
-												<TheIcon iconClassName={item.icon} />
+					<div class={styles.content}>
+						<div class={styles.slide}>
+							<Collapse v-model={data.slide} elevation={false} divider={false}>
+								<CollapseItem title="音符时值" name="note">
+									<div class={styles.wrapBox}>
+										{ABC_DATA.types.map((item) => (
+											<div
+												class={styles.topBtn}
+												onClick={() => handleChange({ type: "type", value: item.value })}
+											>
+												<div
+													class={[styles.btnImg, data.noteType === item.value && styles.btnImgActive]}
+												>
+													<TheIcon iconClassName={item.icon} />
+												</div>
+												<div>{item.name}</div>
 											</div>
-											<div>{item.name}</div>
-										</div>
-									))}
-								</div>
-							</CollapseItem>
-
-							<CollapseItem title="力度记号" name="dynamics">
-								<div class={styles.wrapBox}>
-									{ABC_DATA.dynamics.slice(0, 8).map((item) => (
+										))}
 										<div
 											class={styles.topBtn}
-											onClick={() => handleChange({ type: "dynamics", value: item.value })}
+											onClick={() => handleChange({ type: "note", value: "z" })}
 										>
 											<div
 												class={[
 													styles.btnImg,
-													noteComputed.value.dynamics === item.value && styles.btnImgActive,
+													noteComputed.value.content === "z" && styles.btnImgActive,
 												]}
 											>
-												<TheIcon iconClassName={item.icon} size={["2em", "2em"]} />
+												<img style={{ width: "20px", height: "20px" }} src={getImage("icon_rest.png")} />
 											</div>
-											<div>{item.name}</div>
+											<div>休止符</div>
 										</div>
-									))}
-									<div
-										class={styles.topBtn}
-										onClick={() => handleChange({ type: "dynamics", value: ABC_DATA.dynamics[8].value })}
-									>
+
 										<div
-											class={[
-												styles.btnImg,
-												ABC_DATA.dynamics[8].value.includes(noteComputed.value.dynamics) &&
-													styles.btnImgActive,
-											]}
+											class={styles.topBtn}
+											onClick={() => handleChange({ type: "segno", value: " " })}
 										>
-											<TheIcon iconClassName={ABC_DATA.dynamics[8].icon} size={["2em", "2em"]} />
+											<div
+												class={[styles.btnImg, noteComputed.value.segno === " " && styles.btnImgActive]}
+											>
+												{/* <img style={{ width: "20px", height: "20px" }} src={getImage("icon_rest.png")} /> */}
+											</div>
+											<div>分割</div>
 										</div>
-										<div>{ABC_DATA.dynamics[8].name}</div>
 									</div>
-									<div
-										class={styles.topBtn}
-										onClick={() => handleChange({ type: "dynamics", value: ABC_DATA.dynamics[9].value })}
-									>
-										<div
-											class={[
-												styles.btnImg,
-												ABC_DATA.dynamics[9].value.includes(noteComputed.value.dynamics) &&
-													styles.btnImgActive,
-											]}
-										>
-											<TheIcon iconClassName={ABC_DATA.dynamics[9].icon} size={["2em", "2em"]} />
-										</div>
-										<div>{ABC_DATA.dynamics[9].name}</div>
+								</CollapseItem>
+
+								<CollapseItem title="拍号" name="meter">
+									<div class={styles.wrapBox}>
+										{ABC_DATA.meter.map((item) => (
+											<div
+												class={styles.topBtn}
+												onClick={() => handleChange({ type: "meter", value: item.value })}
+											>
+												<div class={[styles.btnImg]}>
+													<TheIcon iconClassName={item.icon} />
+												</div>
+												<div>{item.name}</div>
+											</div>
+										))}
 									</div>
-								</div>
-							</CollapseItem>
-							<CollapseItem title="反复与跳跃" name="repeat">
-								<div class={styles.wrapBox}>
-									{ABC_DATA.repeat.map((item) => (
+								</CollapseItem>
+
+								<CollapseItem title="力度记号" name="dynamics">
+									<div class={styles.wrapBox}>
+										{ABC_DATA.dynamics.slice(0, 8).map((item) => (
+											<div
+												class={styles.topBtn}
+												onClick={() => handleChange({ type: "dynamics", value: item.value })}
+											>
+												<div
+													class={[
+														styles.btnImg,
+														noteComputed.value.dynamics === item.value && styles.btnImgActive,
+													]}
+												>
+													<TheIcon iconClassName={item.icon} size={["2em", "2em"]} />
+												</div>
+												<div>{item.name}</div>
+											</div>
+										))}
 										<div
-											class={[styles.topBtn, styles.longTopBtn]}
-											onClick={() => handleChange({ type: "repeat", value: item.value })}
+											class={styles.topBtn}
+											onClick={() =>
+												handleChange({ type: "dynamics", value: ABC_DATA.dynamics[8].value })
+											}
 										>
 											<div
 												class={[
 													styles.btnImg,
-													measureComputed.value.repeat === item.value && styles.btnImgActive,
+													ABC_DATA.dynamics[8].value.includes(noteComputed.value.dynamics) &&
+														styles.btnImgActive,
 												]}
 											>
-												<TheIcon iconClassName={item.icon} size={["5em", "1em"]} />
+												<TheIcon iconClassName={ABC_DATA.dynamics[8].icon} size={["2em", "2em"]} />
 											</div>
-											<div>{item.name}</div>
+											<div>{ABC_DATA.dynamics[8].name}</div>
 										</div>
-									))}
-								</div>
-							</CollapseItem>
-							<CollapseItem title="小节线" name="line">
-								<div class={styles.wrapBox}>
-									{ABC_DATA.bar.map((item) => (
 										<div
 											class={styles.topBtn}
-											onClick={() => {
-												data.morePlay = false;
-												handleChange({ type: "barline", value: item.value });
-											}}
+											onClick={() =>
+												handleChange({ type: "dynamics", value: ABC_DATA.dynamics[9].value })
+											}
 										>
 											<div
 												class={[
 													styles.btnImg,
-													measureComputed.value.barline === item.value && styles.btnImgActive,
+													ABC_DATA.dynamics[9].value.includes(noteComputed.value.dynamics) &&
+														styles.btnImgActive,
 												]}
 											>
-												<TheIcon iconClassName={item.icon} size={["2em", "2em"]} />
+												<TheIcon iconClassName={ABC_DATA.dynamics[9].icon} size={["2em", "2em"]} />
 											</div>
-											<div>{item.name}</div>
+											<div>{ABC_DATA.dynamics[9].name}</div>
 										</div>
-									))}
-								</div>
-							</CollapseItem>
+									</div>
+								</CollapseItem>
+								<CollapseItem title="反复与跳跃" name="repeat">
+									<div class={styles.wrapBox}>
+										{ABC_DATA.repeat.map((item) => (
+											<div
+												class={[styles.topBtn, styles.longTopBtn]}
+												onClick={() => handleChange({ type: "repeat", value: item.value })}
+											>
+												<div
+													class={[
+														styles.btnImg,
+														measureComputed.value.repeat === item.value && styles.btnImgActive,
+													]}
+												>
+													<TheIcon iconClassName={item.icon} size={["5em", "1em"]} />
+												</div>
+												<div>{item.name}</div>
+											</div>
+										))}
+									</div>
+								</CollapseItem>
+								<CollapseItem title="小节线" name="line">
+									<div class={styles.wrapBox}>
+										{ABC_DATA.bar.map((item) => (
+											<div
+												class={styles.topBtn}
+												onClick={() => {
+													data.morePlay = false;
+													handleChange({ type: "barline", value: item.value });
+												}}
+											>
+												<div
+													class={[
+														styles.btnImg,
+														measureComputed.value.barline === item.value && styles.btnImgActive,
+													]}
+												>
+													<TheIcon iconClassName={item.icon} size={["2em", "2em"]} />
+												</div>
+												<div>{item.name}</div>
+											</div>
+										))}
+									</div>
+								</CollapseItem>
 
-							<CollapseItem title="谱号" name="clef">
-								<div class={styles.wrapBox}>
-									{ABC_DATA.clef.map((item) => (
-										<div
-											class={styles.topBtn}
-											onClick={() => handleChange({ type: "clef", value: item.value })}
-										>
-											<div class={[styles.btnImg]}>
-												<TheIcon iconClassName={item.icon} />
+								<CollapseItem title="谱号" name="clef">
+									<div class={styles.wrapBox}>
+										{ABC_DATA.clef.map((item) => (
+											<div
+												class={styles.topBtn}
+												onClick={() => handleChange({ type: "clef", value: item.value })}
+											>
+												<div class={[styles.btnImg]}>
+													<TheIcon iconClassName={item.icon} />
+												</div>
+												<div>{item.name}</div>
 											</div>
-											<div>{item.name}</div>
-										</div>
-									))}
-								</div>
-							</CollapseItem>
-							<CollapseItem title="调号" name="key">
-								<div class={styles.wrapBox}>
-									{ABC_DATA.key.map((item) => (
-										<div
-											class={styles.topBtn}
-											onClick={() => handleChange({ type: "key", value: item.value })}
-										>
-											<div class={[styles.btnImg]}>
-												<TheIcon iconClassName={item.icon} />
+										))}
+									</div>
+								</CollapseItem>
+								<CollapseItem title="调号" name="key">
+									<div class={styles.wrapBox}>
+										{ABC_DATA.key.map((item) => (
+											<div
+												class={styles.topBtn}
+												onClick={() => handleChange({ type: "key", value: item.value })}
+											>
+												<div class={[styles.btnImg]}>
+													<TheIcon iconClassName={item.icon} />
+												</div>
+												<div>{item.name}</div>
 											</div>
-											<div>{item.name}</div>
-										</div>
-									))}
-								</div>
-							</CollapseItem>
-						</Collapse>
-					</div>
+										))}
+									</div>
+								</CollapseItem>
+							</Collapse>
+						</div>
 
-					<div class={styles.box}>
-						<div class={styles.titleBox}>
-							<div class={styles.titleName} style={{ width: "50%", margin: "0 auto" }}>
-								<NInput
-									onKeyup={(e: Event) => e.stopPropagation()}
-									v-model:value={data.musicName}
-									placeholder="曲谱名称"
-								/>
+						<div class={styles.box}>
+							<div class={styles.titleBox}>
+								<div class={styles.titleName} style={{ width: "50%", margin: "0 auto" }}>
+									<NInput
+										onKeyup={(e: Event) => e.stopPropagation()}
+										v-model:value={data.musicName}
+										placeholder="曲谱名称"
+									/>
+								</div>
+								<div style={{ width: "30%", margin: "10px 0 0 auto" }}>
+									<NInput
+										onKeyup={(e: Event) => e.stopPropagation()}
+										v-model:value={data.creator}
+										placeholder="曲谱作者"
+									/>
+								</div>
 							</div>
-							<div style={{ width: "30%", margin: "10px 0 0 auto" }}>
-								<NInput
-									onKeyup={(e: Event) => e.stopPropagation()}
-									v-model:value={data.creator}
-									placeholder="曲谱作者"
+							<div id="paper"></div>
+							{!data.loading && (
+								<Keys
+									show={data.active ? true : false}
+									instrumentCode={abcData.abc.subjectCode}
+									onClick={(val) => handleChange(val)}
 								/>
-							</div>
+							)}
+
+							{/* <textarea ref={textAreaRef} class={styles.value} id="abc"></textarea> */}
+							<div id="audio" style={{ display: "none" }}></div>
+							<div id="warnings"></div>
+
+							<p class="beat"></p>
+							<pre class="clicked-info"></pre>
+							<pre class="feedback"></pre>
+							<div id="container"></div>
+							{data.loadingAudioSrouce && (
+								<div class={styles.loading}>
+									<NSpin></NSpin>
+								</div>
+							)}
 						</div>
-						<div id="paper"></div>
-						{!data.loading && (
-							<Keys
-								show={data.active ? true : false}
-								instrumentCode={abcData.abc.subjectCode}
-								onClick={(val) => handleChange(val)}
-							/>
-						)}
-
-						{/* <textarea ref={textAreaRef} class={styles.value} id="abc"></textarea> */}
-						<div id="audio" style={{ display: "none" }}></div>
-						<div id="warnings"></div>
-
-						<p class="beat"></p>
-						<pre class="clicked-info"></pre>
-						<pre class="feedback"></pre>
-						<div id="container"></div>
-						{data.loadingAudioSrouce && (
-							<div class={styles.loading}>
-								<NSpin></NSpin>
-							</div>
-						)}
 					</div>
+					<div ref={downRef}></div>
+
+					<TheSetting v-model:show={popup.settingShow} />
+
+					{data.selectMeasures.state && (
+						<UseDraggable
+							initialValue={{ x: data.selectMeasures.x, y: data.selectMeasures.y }}
+							class={[styles.selectMearesBox, !popup.selectMearesShow && styles.selectMearesHidden]}
+						>
+							<div onKeyup={(e: Event) => e.stopPropagation()}>
+								<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>
+							</div>
+						</UseDraggable>
+					)}
 				</div>
-				<div ref={downRef}></div>
-
-				<TheSetting v-model:show={popup.settingShow} />
-
-				{data.selectMeasures.state && (
-					<UseDraggable
-						initialValue={{ x: data.selectMeasures.x, y: data.selectMeasures.y }}
-						class={[styles.selectMearesBox, !popup.selectMearesShow && styles.selectMearesHidden]}
-					>
-						<div onKeyup={(e: Event) => e.stopPropagation()}>
-							<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>
-						</div>
-					</UseDraggable>
-				)}
-			</div>
+				<div class={styles.exportPng}>
+					<div id="exportPng"></div>
+				</div>
+			</>
 		);
 	},
 });

+ 8 - 1
src/pc/home/runtime.ts

@@ -182,6 +182,8 @@ export const createMeasure = (): IMeasure => {
 interface IRenderMeasuresOption {
 	/** 是否生成index */
 	hiddenIndex?: boolean;
+	showTitle?: boolean;
+	showCreator?: boolean;
 }
 /** 
  * 生成小节
@@ -194,7 +196,12 @@ export const renderMeasures = (abc: IAbc, option?: IRenderMeasuresOption) => {
 	let wrap = 1;
 	let text = `X:1\n`;
 
-	abc.title && (text += abc.title + "\n");
+	if (option?.showTitle){
+		abc.title && (text += abc.title + "\n");
+	}
+	if (option?.showCreator) {
+		abc.creator && (text += abc.creator + "\n");
+	}
 	abc.celf && (text += abc.celf + "\n");
 	abc.meter && (text += abc.meter + "\n");
 	abc.minUnit && (text += abc.minUnit + "\n");

+ 2 - 0
src/pc/types.ts

@@ -60,6 +60,8 @@ export interface IAbc {
 	minUnit?: string;
 	/** 曲谱名 */
 	title?: string;
+	/** 作者 */
+	creator?: string;
 	/** 速度 */
 	speed: string;
 	measures: IMeasure[];