import { computed, defineComponent, nextTick, onMounted, onUnmounted, reactive, ref, watch } from "vue";
import ABCJS, { AbcElem, AbcVisualParams, ClickListenerAnalysis, ClickListenerDrag, NoteTimingEvent, SynthObjectController } from "abcjs";
import "ABCJS/ABCJS-audio.css";
import styles from "./index.module.less";
import { showConfirmDialog, showToast } from "vant";
import Keys from "../component/keys";
import { Collapse, CollapseItem } from "@varlet/ui";
import { IAbc, IMeasure, INote, INoteActive } from "../types";
import { ABC_DATA, createMeasure, createNote, formateAbc, getKeyStep, moveNoteKey, renderMeasures } from "./runtime";
import TheIcon from "/src/components/The-icon";
import { cloneDeep } from "lodash";
import TheSpeed from "./component/the-speed";
import { getImage } from "./images";
import { NButton, NDropdown, NGi, NGrid, NIcon, NInput, NInputNumber, NModal, NPopover, NPopselect, NSelect, NSpace, NSpin, NTab, NTabPane, NTabs, useDialog, useMessage } from "naive-ui";
import { LongArrowAltDown, LongArrowAltUp, GripLinesVertical } from "@vicons/fa";
import { svg2canvas } from "/src/utils/svg2canvas";
import { decodeUrl, downloadFile } from "/src/utils";
import FileBtn, { IFileBtnType } from "./component/file-btn";
import TheSetting from "./component/the-setting";
import { useRoute } from "vue-router";
import { api_musicSheetCreationDetail, api_musicSheetCreationSave, api_musicSheetCreationUpdate, api_xmlToAbc } from "../api";
import instrumentsNames from "/src/constant/instrmentsNames.json";
import { ALL_NOTES, ALL_Pitches } from "./noteData";
import { Close } from "@vicons/ionicons5";
import requestOrigin from "umi-request";
import { getQuery } from "/src/utils/queryString";
import Metronome, { metronomeData } from "/src/helpers/metronome";
import cleanDeep from "clean-deep";
import { saveAs } from "file-saver";
import qs from "query-string";
import { useDocumentVisibility } from "@vueuse/core";
import request from "/src/utils/request";
import { api_uploadFile } from "/src/utils/uploadFile";
import { bufferToWave } from "/src/helpers/parseABC";
import UploadToResources from "../component/upload-to-resources";
import TheJianpu from "./component/the-jianpu";

export const initMusic = (total: number): IMeasure[] => {
  return new Array(total).fill(0).map((item, index) => {
    return {
      measureNumber: index + 1,
      barline: "|",
      celf: "",
      key: "",
      repeat: "",
      meter: "",
      notes: [
        {
          accidental: "",
          clef: "",
          meter: "",
          content: "z",
          noteType: "4",
          play: [],
          key: "",
          speed: "",
          dynamics: "",
          dCode: "",
          tie: "",
          tCode: "",
          dot: "",
          slus: "",
          tieline: "",
          segno: "",
        },
      ],
    };
  });
};

function moveNote(note: string, step: number) {
  var x = ALL_Pitches.indexOf(note);
  if (x >= 0) {
    const _note = ALL_Pitches[x - step];
    return _note ? _note : note;
  }
  return note;
}

export default defineComponent({
  name: "Home",
  setup() {
    const dialog = useDialog();
    const route = useRoute();
    const message = useMessage();
    const popup = reactive({
      instrument: false,
      selectSubjectShow: false, // 声部弹窗
      moveKeyShow: false, // 移调弹窗
      speedShow: false, // 节拍弹窗
      accidentalsShow: false, // 临时升降记号弹窗
      clefShow: false, // 谱号弹窗
      keyShow: false, // 调号弹窗
      meterShow: false, // 拍号弹窗
      playShow: false, // 演奏技法弹窗
      dynamicsShow: false, // 力度标记弹窗
      barShow: false, // 小节弹窗
      mearseDeleteShow: false,
      staffShow: false, // 五线谱弹窗
      settingShow: false, // 设置弹窗
      selectMearesShow: false, // 选择小节弹窗
      jianpuShow: false, // 简谱弹窗
    });
    const data = reactive({
      uploadStatus: "",
      saveLoading: false,
      saveLoadingText: false,
      loading: true,
      drawCount: 0,
      isSave: true,
      musicId: Date.now().toString(),
      musicName: "" as any, // 曲谱名称
      creator: "", // 创建者
      subjectId: "", // 声部
      speed: "",
      music: "",
      playState: false, // 播放状态
      active: null as unknown as INoteActive,
      select: {
        state: false,
        list: [] as any[],
        parmas: null as any,
      },
      isClickNote: false,
      /** 音符类型 */
      noteType: "",
      /** 选择小节范围 */
      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"],

      morePlay: false, // 更多演奏技法

      addMearseType: "pre" as "pre" | "next" | "finish", // 添加小节类型
      addMearseNumber: 1, // 添加小节数量

      deleteMearseType: "ing" as "ing" | "finish", // 删除小节类型
      loadingAudioSrouce: false, // 加载音频资源
      /** 移调类型 */
      moveKeyType: "inset" as "inset" | "up" | "down", // 移调类型
      activePlayNote: null as any, // 当前演奏音符
      times: [] as any[], // 节拍器数据

      undoList: [] as any[], // 撤销列表
      redoList: [] as any[], // 重做列表

      uploadShow: false, // 上传弹窗
      item: {} as any, // 上传数据
      uploadClick: false, // 上传点击

      /** 多选 */
      multiSelect: false,
      multiSelectList: [] as any[],
      multilList: [] as any[],
    });
    const noteTypes = ABC_DATA.types.map((item) => item.value).filter(Boolean);
    const accidentals = ABC_DATA.accidentals.map((item) => item.value).filter(Boolean);
    const clefs = ABC_DATA.clef.map((item) => item.value).filter(Boolean);
    const playTypes = ABC_DATA.play.map((item) => item.value).filter(Boolean);
    const dynamics = ABC_DATA.dynamics
      .map((item) => item.value)
      .flat()
      .filter(Boolean);
    const barTypes = ABC_DATA.bar.map((item) => item.value).filter(Boolean);
    console.log("🚀 ~ noteTypes:", noteTypes, accidentals, clefs, playTypes, dynamics);
    const setNoteColor = () => {
      // const gs = document.querySelectorAll(`#paper svg g[fill="#0f81ff"]`);
      // Array.from(gs).forEach((g) => {
      // 	g.classList.remove("note_selected");
      // 	g.setAttribute("fill", "currentColor");
      // });
      const gs1 = document.querySelectorAll(`#paper svg g.note_selected`);
      Array.from(gs1).forEach((g) => {
        g.classList.remove("note_selected");
      });
    };
    /** 点击音符 */
    const clickListener = (abcElem: AbcElem, tuneNumber: number, classes: string, analysis: ClickListenerAnalysis, drag: ClickListenerDrag) => {
      setNoteColor();
      // console.log("🚀 ~ data.select.state:", abcData.visualObj);
      let indexStr: any = abcElem.chord?.find((n) => n.position === "left")?.name || "";
      indexStr = indexStr.split(".").map((n: string) => Number(n));
      const active = {
        ...cloneDeep(abcElem),
        measureIndex: indexStr[0],
        noteIndex: indexStr[1],
        isFirstChecked: true,
      };
      if (abcElem) {
        if (abcElem.startChar && abcElem.endChar) {
          abcData.visualObj.engraver.rangeHighlight(abcElem.startChar, abcElem.endChar);
          abcElem?.abselem?.elemset?.[0]?.classList?.add("note_selected");
        }
      }

      // 选择范围模式
      if (data.select.state) {
        data.select.list.push(active);
        if (data.select.list.length === 1) {
          showToast("请先选择结束音符");
        }
        if (data.select.list.length === 2) {
          console.log(data.select.list);
          data.select.list = data.select.list.sort((a, b) => a.startChar - b.startChar);
          handleSelectNote();
        }
        return;
      }
      data.active = active;

      if (data.multiSelect) {
        if (data.multiSelectList.length === 0) {
          data.multiSelectList.push(active);
        }
        if (data.multiSelectList.length > 0) {
          data.multilList = [];
          if (data.multiSelectList[0].startChar > active.startChar) {
            const start = data.multiSelectList[1] || data.multiSelectList[0];
            data.multiSelectList = [cleanDeep(start), cleanDeep(active)];
          } else {
            data.multiSelectList = [cleanDeep(data.multiSelectList[0]), cleanDeep(active)];
          }
          console.log(data.multiSelectList[0].startChar, data.multiSelectList[1].startChar);
          data.multiSelectList = data.multiSelectList.sort((a, b) => a.startChar - b.startChar);
          data.multilList = getmutilList();
          handleChange({ type: "multiSelect", value: "" });
          return;
        }
      } else {
        if (data.multiSelectList.length || data.multilList.length) {
          handleClearMultiSelect();
        }
      }

      console.log("🚀 ~ abcElem:", abcElem, data.music.substring(data.active.startChar, data.active.endChar));
      if (data.active?.el_type === "note" && !popup.selectMearesShow) {
        const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
        if (totalTime) {
          const progress = (data.active as any).currentTrackMilliseconds / 1000 / totalTime;
          // console.log("🚀 ~ data.active:", progress);
          (abcData.synthControl as any).seek(progress);
        }
      }
      // if (abcElem.el_type === "tempo") {
      // 	abcData.visualObj.engraver.rangeHighlight(abcElem.startChar, abcElem.endChar);
      // }
      if (drag && drag.step) {
        handleChange({ type: "move", value: { action: "drag", step: drag.step } });
        return;
      }
      if (!abcElem?.midiPitches) return;
      ABCJS.synth.playEvent(abcElem.midiPitches, abcElem.midiGraceNotePitches, 1000);
    };

    const getmutilList = () => {
      const start = data.multiSelectList[0];
      const end = data.multiSelectList[1];
      const list = [] as any[];
      // console.log("🚀 ~ start:", start.noteIndex, end.noteIndex);
      for (let i = start.measureIndex; i < end.measureIndex + 1; i++) {
        const measure = abcData.abc.measures[i];
        for (let j = 0; j < measure.notes.length; j++) {
          const abcElem = useIndexGetNote(`${i}.${j}`);
          const active = {
            ...cloneDeep(abcElem),
            measureIndex: i,
            noteIndex: j,
          };
          if (start.measureIndex != end.measureIndex) {
            if (start.measureIndex === i) {
              j >= start.noteIndex && list.push(active);
            } else if (i === end.measureIndex) {
              j <= end.noteIndex && list.push(active);
            } else {
              list.push(active);
            }
          } else {
            j >= start.noteIndex && j <= end.noteIndex && list.push(active);
          }
        }
      }
      // console.log(list);
      return list;
    };

    const textAreaRef = ref();
    const abcData = reactive({
      visualObj: null as any,
      midiBuffer: null as unknown as ABCJS.MidiBuffer,
      abcOptions: {
        selectionColor: "#0f81ff",
        jazzchords: true,
        add_classes: true,
        clickListener: clickListener,
        responsive: "resize",
        dragging: true,
        selectTypes: ["note"],
        visualTranspose: 0,
        wrap: {
          minSpacing: 0.1,
          maxSpacing: 2.7,
          preferredMeasuresPerLine: 4,
        },
        staffwidth: 800,
      } as AbcVisualParams,
      synthControl: null as unknown as SynthObjectController,
      synthOptions: {
        program: 0,
        soundFontUrl: "https://oss.dayaedu.com/musicSheet/",
        // soundFontUrl: "https://paulrosen.github.io/midi-js-soundfonts/FluidR3_GM/", // 默认 FluidR3_GM
        // soundFontUrl: "https://paulrosen.github.io/midi-js-soundfonts/MusyngKite/", // Musyng Kite
      },
      abc: {
        celf: "K:treble",
        minUnit: "L:1/4",
        meter: "M:4/4",
        speed: "Q:1/4=60",
        key: "K:C",
        visualTranspose: 0,
        visualKey: "K:C",
        subjectCode: "acoustic_grand_piano",
        measures: initMusic(30),
        isrhythm: "",
      } as IAbc,
    });

    /** 添加音符 */
    const handleCreateNote = (measureIndex: number, noteIndex: number, options: INote) => {
      // console.log("🚀 ~ noteIndex:", noteIndex);
      const measure = abcData.abc.measures[measureIndex];
      if (measure) {
        measure.notes.splice(noteIndex + 1, 0, options);
      }
    };

    const hideCursor = () => {
      const cursor = document.querySelector("#paper svg .ABCJS-cursor");
      if (cursor) {
        cursor.setAttribute("x1", "0");
        cursor.setAttribute("x2", "0");
        cursor.setAttribute("y1", "0");
        cursor.setAttribute("y2", "0");
      }
    };

    const cursorControl = {
      onReady: function () {},
      onStart: function () {
        console.log("开始");
        data.playState = true;
        var svg = document.querySelector("#paper svg");
        let cursor = document.querySelector("#paper svg .ABCJS-cursor");
        if (!cursor) {
          cursor = document.createElementNS("http://www.w3.org/2000/svg", "line");
          cursor.setAttribute("class", "ABCJS-cursor");
          svg?.appendChild(cursor);
        }
        cursor.setAttributeNS(null, "x1", "0");
        cursor.setAttributeNS(null, "y1", "0");
        cursor.setAttributeNS(null, "x2", "0");
        cursor.setAttributeNS(null, "y2", "0");
      },
      onBeat: function (beatNumber: any, totalBeats: any, totalTime: any) {
        if (!data.playState) return;
        if (popup.selectMearesShow && data.selectMeasures.startNote && data.selectMeasures.endNote) {
          const end = totalBeats * (data.selectMeasures.endNote.milliseconds / (abcData.visualObj.getTotalTime() * 1000)) - 0.1;
          // console.log(beatNumber, start, end, progress);
          if (beatNumber >= end) {
            const progress = data.selectMeasures.startNote.milliseconds / (abcData.visualObj.getTotalTime() * 1000);
            (abcData.synthControl as any).seek(progress);
            return;
          }
        }
        // console.log("🚀 ~ beatNumber:", beatNumber, totalBeats, totalTime);
        const time = (beatNumber / totalBeats) * totalTime;
        metronomeData.metro.sound(time);
      },
      onEvent: (event: any) => {
        let ev = event as any;
        if (!data.playState) return;
        if (ev.measureStart && ev.left === null) return; // this was the second part of a tie across a measure line. Just ignore it.

        data.activePlayNote = { ...ev };
        var cursor = document.querySelector("#paper svg .ABCJS-cursor");
        if (cursor) {
          cursor.setAttribute("x1", ev.left + ev.width / 2);
          cursor.setAttribute("x2", ev.left + ev.width / 2);
          cursor.setAttribute("y1", ev.top);
          cursor.setAttribute("y2", ev.top + ev.height);
        }
      },
      onFinished: function () {
        console.log("finished");
        data.playState = false;
        var els = document.querySelectorAll("svg .highlight");
        for (var i = 0; i < els.length; i++) {
          els[i].classList.remove("highlight");
        }
        hideCursor();
      },
    };

    const staffNotes = ALL_NOTES();
    const loadMiniMp3 = async () => {
      data.loadingAudioSrouce = true;
      const midiBuffer = new ABCJS.synth.CreateSynth();
      const str = `X: 1\nM:4/4\nL:1/4\n${staffNotes}`;
      const visualObj = ABCJS.parseOnly(str);
      await midiBuffer.init({
        visualObj: visualObj[0],
        options: { ...abcData.synthOptions },
      });
    };

    const resetMidi = (useActive = false) => {
      const midiBuffer = new ABCJS.synth.CreateSynth();
      midiBuffer
        .init({
          visualObj: abcData.visualObj,
          options: { ...abcData.synthOptions },
        })
        .then(() => {
          abcData.synthControl
            .setTune(abcData.visualObj, useActive, {
              midiTranspose: abcData.abc.visualTranspose,
              program: abcData.synthOptions.program,
            })
            .then(function (response) {
              data.loadingAudioSrouce = false;
              // console.log("Audio successfully loaded.", {...abcData.synthControl});
              // console.log("🚀 ~ abcData.synthControl:", abcData.synthControl);
            })
            .catch((err) => {
              console.log(err);
            });
        });
    };

    const togglePlay = async (type: "play" | "pause" | "reset") => {
      if (type === "play") {
        if (popup.selectMearesShow) {
          if (data.selectMeasures.start > data.selectMeasures.end || !data.selectMeasures.startNote || !data.selectMeasures.endNote) {
            data.selectMeasures.start = 0;
            data.selectMeasures.end = 0;
            data.selectMeasures.startNote = null;
            data.selectMeasures.endNote = null;
            message.warning("请输入正确的小节范围");
            data.selectMeasures.state = false;
            nextTick(() => {
              data.selectMeasures.state = true;
            });
            return;
          }
          if (!(abcData.synthControl as any).isLoaded) {
            (abcData.synthControl as any).runWhenReady(() => {
              const progress = data.selectMeasures.startNote.milliseconds / (abcData.visualObj.getTotalTime() * 1000);
              (abcData.synthControl as any).seek(progress);
            });
          } else {
            const progress = data.selectMeasures.startNote.milliseconds / (abcData.visualObj.getTotalTime() * 1000);
            (abcData.synthControl as any).seek(progress);
          }
        }
        abcData.synthControl.play();
        data.playState = true;
      } else if (type === "pause") {
        abcData.synthControl.play();
        // abcData.synthControl.pause();
        data.playState = false;
        hideCursor();
        const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
        if (totalTime && data.activePlayNote?.milliseconds !== undefined) {
          const progress = data.activePlayNote.milliseconds / 1000 / totalTime;
          nextTick(() => {
            (abcData.synthControl as any).seek(progress);
          });
        }
      } else {
        abcData.synthControl.restart();
        nextTick(() => {
          if (popup.selectMearesShow) {
            if (data.selectMeasures.start > data.selectMeasures.end || !data.selectMeasures.startNote || !data.selectMeasures.endNote) {
              data.selectMeasures.start = 0;
              data.selectMeasures.end = 0;
              data.selectMeasures.startNote = null;
              data.selectMeasures.endNote = null;
              message.warning("请输入正确的小节范围");
              data.selectMeasures.state = false;
              nextTick(() => {
                data.selectMeasures.state = true;
              });
              return;
            }
            if (!(abcData.synthControl as any).isLoaded) {
              (abcData.synthControl as any).runWhenReady(() => {
                const progress = data.selectMeasures.startNote.milliseconds / (abcData.visualObj.getTotalTime() * 1000);
                (abcData.synthControl as any).seek(progress);
              });
            } else {
              const progress = data.selectMeasures.startNote.milliseconds / (abcData.visualObj.getTotalTime() * 1000);
              (abcData.synthControl as any).seek(progress);
            }
          }
          if (!data.playState) {
            abcData.synthControl.play();
          }
        });
      }
      // console.log("🚀 ~ abcData.synthControl:", abcData.synthControl.timer.noteTimings);
    };

    const renderSvg = () => {
      abcData.visualObj = ABCJS.renderAbc("paper", data.music, {
        ...abcData.abcOptions,
        visualTranspose: abcData.abc.visualTranspose,
      })[0];
      if (data.drawCount < 3) {
        console.log("🚀 ~ visualObj:", abcData.visualObj);
      }
    };

    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);
        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;
              // console.log("🚀 ~ box:", item.abselem, box);
              if (box) {
                const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
                rect.setAttributeNS(null, "x", box.x - padding + "");
                rect.setAttributeNS(null, "y", box.y - padding + "");
                rect.setAttributeNS(null, "width", box.width + padding * 2 + "");
                rect.setAttributeNS(null, "height", box.height + padding * 2 + "");
                rect.setAttributeNS(null, "fill", "rgba(0,0,0,0)");
                rect.setAttributeNS(null, "stroke", "rgba(0,0,0,0)");
                rect.setAttributeNS(null, "rx", "2");
                rect.classList.add("abcjs-note-hover");
                svg?.appendChild(rect);
              }
            }
          }
        }
      }
      console.log(measureNumber);
      data.selectMeasures.max = measureNumber;
    };

    let saveTimer: any = null;
    const autoSave = () => {
      saveTimer && clearTimeout(saveTimer);
      saveTimer = setTimeout(() => {
        handleSaveMusic(false, false);
      }, 15000);
    };

    /**
     * @param isProduct 是否是生成曲谱
     */
    const handleResetRender = () => {
      if (data.drawCount > 0) {
        abcData.synthControl.disable(true);
        if (data.playState) {
          data.playState = false;
        }
        data.isSave = false;
      }
      if (popup.selectMearesShow) {
        data.selectMeasures.startNote = null;
        data.selectMeasures.endNote = null;
        data.selectMeasures.start = 0;
        data.selectMeasures.end = 0;
        popup.selectMearesShow = false;
        data.selectMeasures.state = false;
        nextTick(() => {
          data.selectMeasures.state = true;
        });
      }
      return new Promise((resolve) => {
        nextTick(() => {
          data.music = renderMeasures(abcData.abc);
          renderSvg();
          resetMidi(data.drawCount > 0 ? true : false);
          renderBoxRect();
          try {
            productMetronomeData();
          } catch (error) {
            console.log("🚀 ~ error:", error);
          }

          resolve(1);
          if (data.drawCount > 0) {
            const host = location.host;
            if (host.includes("localhost") || host.includes("192")) return;
            autoSave();
          }

          textAreaRef.value && (textAreaRef.value.value = data.music);
          data.drawCount++;
        });
      });
    };

    /** 生成曲谱节拍器数据 */
    const productMetronomeData = () => {
      const times = new ABCJS.TimingCallbacks(abcData.visualObj);
      data.times = times.noteTimings;
      const list: any[] = [];
      let meter = abcData.abc.meter || "";
      // length - 1是为了去除最后一个空的结束事件
      for (let i = 0; i < times.noteTimings.length - 1; i++) {
        const timeNote = times.noteTimings[i];
        const abcNote = getNextNote(timeNote.startChar as number);
        let indexStr: any = abcNote.chord?.find((n) => n.position === "left")?.name || "";
        indexStr = indexStr.split(".").map((n: string) => Number(n));
        if (indexStr.length === 2) {
          const measure = abcData.abc.measures[indexStr[0]];
          // 如果小节里面有拍号，后面一直延用这个拍号,以此类推, ps: 下个版本
          // if (measure.meter){
          // 	meter = measure.meter;
          // }
          const reg = new RegExp(/M:(\d+)\/\d+/);
          const numerator = Number(meter.match(reg)?.[1]);
          // console.log("🚀 ~ reg:", meter.match(reg)?.[1], abcData.abc.meter)
          // console.log("🚀 ~ measure:", measure)
          const note = measure.notes[indexStr[1]];
          list.push({
            ...note,
            timeNote,
            abcNote,
            measure: {
              numerator,
            },
          });
        }
      }
      // console.log("abcData.abc.measures", list);
      if (!metronomeData.metro) {
        metronomeData.metro = new Metronome();
      }
      try {
        metronomeData.activeIndex = -1;
        metronomeData.metro.init(list);
      } catch (error) {
        console.log("🚀 ~ 生成节拍器数据错误:", error);
      }
    };

    // 高亮选中的音符
    const rangeHighlight = (startChar: number) => {
      // console.log(data.active.endChar, abcData.visualObj.getElementFromChar(data.active.startChar));
      const abcElem: AbcElem = abcData.visualObj.getElementFromChar(startChar);
      if (abcElem) {
        // abcData.visualObj.engraver.rangeHighlight(abcElem.startChar, abcElem.endChar);
        abcElem?.abselem?.elemset?.[0]?.classList?.add("note_selected");
      }
      return abcElem;
    };

    const useIndexGetNote = (indexStr: string) => {
      const index = data.music.indexOf(indexStr);
      const note = abcData.visualObj.getElementFromChar(index);
      return note;
    };

    const getMeasureNotes = (measureIndex: number) => {
      const measure = abcData.abc.measures[measureIndex];
      const notes = [];
      for (let k = 0; k < measure.notes.length; k++) {
        const indexStr = `${measureIndex}.${k}`;
        const note = useIndexGetNote(indexStr);
        notes.push(note);
      }
      return notes;
    };

    const handleClose = () => {
      // 判断是否在应用中
      console.log("点击退出", window.matchMedia("(display-mode: standalone)").matches);
      if (window.matchMedia("(display-mode: standalone)").matches) {
        window.onbeforeunload = null;
        console.log("准备发消息");
        window.parent.postMessage(
          {
            api: "notation_exit",
          },
          "*"
        );
      } else {
        window.close();
        // 全屏模式无法判断是否在应用打开

        // 那就都发个消息吧
        window.parent.postMessage(
          {
            api: "notation_exit",
          },
          "*"
        );
      }
    };

    const handleClickExit = async () => {
      if (data.saveLoading) return;
      const msg = message.loading("保存中...", { duration: 0 });
      await handleSaveMusic(false, false);
      setTimeout(async () => {
        msg.type = "success";
        msg.content = "保存成功";
        setTimeout(() => {
          msg.destroy();
        }, 500);
      }, 300);
      if (data.uploadStatus !== "NO") {
        dialog.warning({
          maskClosable: true,
          autoFocus: false,
          class: "deleteDialog saveDialog",
          title: "温馨提示",
          content: "是否更新到我的资源？",
          positiveText: "更新",
          positiveButtonProps: {
            type: "primary",
          },
          negativeText: "不更新",
          negativeButtonProps: {
            type: "default",
            ghost: false,
          },
          onPositiveClick: async () => {
            data.uploadClick = true;
            await handleUpdate();
          },
          onNegativeClick: () => {
            handleClose();
          },
        });
      } else {
        handleClose();
      }
    };

    /**
     *
     * @param key
     * @param type  note 音符 accidental 临时升降记号
     * @returns
     */
    const handleChange = async (params: { type: string; value: any }) => {
      // 最多记录30步
      if (data.undoList.length > 30) {
        data.undoList.shift();
      }
      data.undoList.push(cleanDeep(abcData.abc));
      abcData.synthControl.disable(true);
      if (data.playState) {
        data.playState = false;
      }
      const type = params.type;
      const value = params.value;
      const activeNote = abcData.abc.measures[data.active?.measureIndex]?.notes[data.active?.noteIndex] || null;
      if (type === "multiSelect") {
        data.multilList.forEach((item: any) => {
          item?.abselem?.elemset?.[0]?.classList?.add("note_selected");
        });
        return;
      }
      if (type === "exit") {
        if (!data.isSave) {
          clearTimeout(saveTimer);
          data.uploadClick = false;
          dialog.warning({
            maskClosable: true,
            autoFocus: false,
            class: "deleteDialog saveDialog",
            title: "温馨提示",
            content: "是否保存当前曲谱？",
            positiveText: "保存",
            positiveButtonProps: {
              type: "primary",
            },
            negativeText: "不保存",
            negativeButtonProps: {
              type: "default",
              ghost: false,
            },
            onPositiveClick: () => {
              handleClickExit();
            },
            onNegativeClick: () => {
              handleClose();
            },
          });
          return;
        }
        handleClose();
      }
      // console.log(params, activeNote);
      if (type === "type") {
        // 设置音符类型
        data.noteType = value;
        if (activeNote) {
          activeNote.noteType = value;
          await handleResetRender();
          const abcElem: AbcElem = rangeHighlight(data.active.startChar);
          const active: any = abcElem
            ? {
                ...cloneDeep(abcElem),
                measureIndex: data.active.measureIndex,
                noteIndex: data.active.noteIndex,
                isFirstChecked: true,
              }
            : null;
          data.active = active;
        }
        return;
      }

      // 分割
      if (type === "segno") {
        if (!data.active) {
          showToast("请先选择音符");
          return;
        }
        if (!activeNote) return;
        activeNote.segno = activeNote.segno ? "" : value;
        await handleResetRender();
        rangeHighlight(data.active.startChar);
      }

      // 修改音符,或者添加音符
      if (type === "note") {
        if (data.active && data.active.el_type == "note") {
          const activeNote = abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex] || null;
          const _values = value.split("-");
          if (data.active.isFirstChecked) {
            activeNote.content = _values[0];
            activeNote.noteType = data.noteType;
            if (_values[1]) {
              activeNote.accidental = _values[1] || "";
            }
            data.active.isFirstChecked = false;
          }
          await handleResetRender();
          const oldNote = useIndexGetNote(`${data.active.measureIndex}.${data.active.noteIndex}`);
          if (oldNote?.abselem?.beam?.elems?.length) {
            // 判断是否需要分割beam
            const elems: AbcElem[] = oldNote.abselem.beam.elems;
            const beatDuration = abcData.visualObj.getBeatLength();
            const beamLength = elems.map((n) => n.duration).reduce((a, b) => a + b);
            if (beamLength >= beatDuration) {
              abcData.abc.measures[data.active.measureIndex].notes[data.active.noteIndex].segno = " ";
              await handleResetRender();
            }
          }
          if (oldNote?.midiPitches) {
            ABCJS.synth.playEvent(oldNote.midiPitches, oldNote.midiGraceNotePitches, 1000);
          }
          const nextNote = abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex + 1];
          if (nextNote) {
            const abcNextElem: AbcElem = useIndexGetNote(`${data.active.measureIndex}.${data.active.noteIndex + 1}`);
            rangeHighlight(abcNextElem.startChar);
            data.active = {
              ...abcNextElem,
              measureIndex: data.active.measureIndex,
              noteIndex: data.active.noteIndex + 1,
              isFirstChecked: true,
            };
          } else {
            const notes = getMeasureNotes(data.active.measureIndex);
            const duration = notes.map((n) => n.duration).reduce((a, b) => a + b);
            if (duration >= 1) {
              // 小节内音符总时值过长,自动跳转到下一小节
              const nextMeasureNote = abcData.abc.measures[data.active.measureIndex + 1]?.notes[0];
              if (nextMeasureNote) {
                const abcNextElem: AbcElem = useIndexGetNote(`${data.active.measureIndex + 1}.${0}`);
                rangeHighlight(abcNextElem.startChar);
                data.active = {
                  ...abcNextElem,
                  measureIndex: data.active.measureIndex + 1,
                  noteIndex: 0,
                  isFirstChecked: true,
                };
              } else {
                // 到最后一个小节的最后一个音符了
                rangeHighlight(data.active.startChar);
                data.active.isFirstChecked = true;
              }
            } else {
              handleCreateNote(
                data.active.measureIndex,
                data.active.noteIndex,
                createNote({
                  content: "z",
                  noteType: data.noteType,
                })
              );
              await handleResetRender();
              const newNote = useIndexGetNote(`${data.active.measureIndex}.${data.active.noteIndex + 1}`);
              rangeHighlight(newNote.startChar);
              data.active = {
                ...newNote,
                measureIndex: data.active.measureIndex,
                noteIndex: data.active.noteIndex + 1,
                isFirstChecked: true,
              };
            }
          }
        }
      }

      // 临时升降记号
      if (type === "accidentals") {
        if (!data.active) {
          message.warning("请先选择音符");
          return;
        }
        if (activeNote.content === "z") {
          message.warning("休止符无法添加临时升降记号");
          return;
        }
        activeNote.accidental = activeNote.accidental == value ? "" : value;
        await handleResetRender();
        const abcElem: AbcElem = rangeHighlight(data.active.startChar);
        const active: any = abcElem
          ? {
              ...cloneDeep(abcElem),
              measureIndex: data.active.measureIndex,
              noteIndex: data.active.noteIndex,
              isFirstChecked: true,
            }
          : null;
        data.active = active;
      }

      // 谱号
      if (type === "clef") {
        if (data.active) {
          if (data.active.measureIndex === 0 && data.active.noteIndex === 0) {
            abcData.abc.celf = value;
            handleResetRender();
          } else {
            if (!activeNote) return;
            activeNote.clef = `[${value}]`;
            await handleResetRender();
          }
          rangeHighlight(data.active.startChar);
        } else {
          abcData.abc.celf = value;
          handleResetRender();
        }
      }

      // 调号
      if (type === "key") {
        // if (data.active) {
        // 	if (data.active.measureIndex === 0 && data.active.noteIndex === 0) {
        // 		abcData.abc.key = value;
        // 		abcData.abc.visualTranspose = 0;
        // 		abcData.abc.visualKey = "K:C";
        // 		abcData.abc.visualKey = value;
        // 		await handleResetRender();
        // 	} else {
        // 		if (!activeNote) return;
        // 		activeNote.key = `[${value}]`;
        // 		await handleResetRender();
        // 	}
        // 	rangeHighlight(data.active.startChar);
        // } else {
        // }
        abcData.abc.key = value;
        abcData.abc.visualTranspose = 0;
        abcData.abc.visualKey = "K:C";
        abcData.abc.visualKey = value;
        await handleResetRender();
      }

      // 拍号
      if (type === "meter") {
        if (data.active && data.active.measureIndex !== 0) {
          if (!activeNote) return;
          const measure = abcData.abc.measures[data.active.measureIndex];
          measure.meter = `[${value}]`;
          await handleResetRender();
          const oldNote = useIndexGetNote(`${data.active.measureIndex}.${data.active.noteIndex}`);
          rangeHighlight(oldNote.startChar);
        } else {
          abcData.abc.meter = value;
          await handleResetRender();
        }
      }

      //  演奏技法
      if (type === "play") {
        if (!data.active) {
          message.warning("请先选择音符");
          return;
        }
        if (!activeNote) return;
        if (activeNote.play.includes(value)) {
          activeNote.play = activeNote.play.filter((item) => item !== value);
        } else {
          activeNote.play.push(value);
        }
        await handleResetRender();
        rangeHighlight(data.active.startChar);
      }
      // 力度标记,渐强渐弱
      if (type === "dynamics") {
        if (!data.active) {
          message.info("请先选择音符");
          return;
        }
        if (!activeNote) return;
        if (Array.isArray(value)) {
          if (activeNote?.dynamics) {
            activeNote.dynamics = "";
            for (let i = 0; i < abcData.abc.measures.length; i++) {
              const measure = abcData.abc.measures[i];
              for (let j = 0; j < measure.notes.length; j++) {
                const note = measure.notes[j];
                if (note.dCode === activeNote.dCode) {
                  note.dynamics = "";
                }
              }
            }

            await handleResetRender();
          } else {
            data.select.list = [cloneDeep(data.active)];
            data.select.state = true;
            data.select.parmas = params;
            message.info("请选择结束音符");
          }
          return;
        }
        if (activeNote.dynamics === value) {
          activeNote.dynamics = "";
        } else {
          activeNote.dynamics = value;
        }
        await handleResetRender();
        rangeHighlight(data.active.startChar);
      }

      // 延音
      if (type === "tie") {
        if (!data.active) {
          message.info("请先选择音符");
          return;
        }
        if (!activeNote) return;
        if (Array.isArray(value)) {
          if (activeNote?.tie) {
            activeNote.tie = "";
            for (let i = 0; i < abcData.abc.measures.length; i++) {
              const measure = abcData.abc.measures[i];
              for (let j = 0; j < measure.notes.length; j++) {
                const note = measure.notes[j];
                if (note.tCode === activeNote.tCode) {
                  note.tie = "";
                }
              }
            }

            await handleResetRender();
            return;
          } else {
            data.select.list = [cloneDeep(data.active)];
            data.select.state = true;
            data.select.parmas = params;
            message.info("请选择结束音符");
            return;
          }
        }

        const abcElem: any = getNextNote(data.active.endChar);
        if (activeNote.tieline) {
          activeNote.tieline = "";
        } else {
          if (data.active.averagepitch != abcElem.averagepitch) {
            message.warning("必须同音高才能添加延音线");
            return;
          }
          activeNote.tieline = value;
        }

        await handleResetRender();
        rangeHighlight(data.active.startChar);
      }

      // 反复
      if (type === "repeat") {
        if (!data.active) {
          return;
        }
        const activeMeasure = abcData.abc.measures[data.active.measureIndex] || null;
        if (!activeMeasure) return;
        if (activeMeasure.repeat === value) {
          activeMeasure.repeat = "";
        } else {
          activeMeasure.repeat = value;
        }
        await handleResetRender();
        rangeHighlight(data.active.startChar + value.length);
      }

      // 小节线
      if (type === "barline") {
        if (!data.active) {
          return;
        }
        const activeMeasure = abcData.abc.measures[data.active.measureIndex] || null;
        if (!activeMeasure) return;
        // 如果是开始重复,就修改前一个小节的barline
        if (value === "|:") {
          const prevMeasure = abcData.abc.measures[data.active.measureIndex - 1] || null;
          if (!prevMeasure) return;
          prevMeasure.barline = prevMeasure.barline === value ? "|" : value;
        } else {
          activeMeasure.barline = activeMeasure.barline === value ? "|" : value;
        }
        await handleResetRender();
      }

      // 速度
      if (type === "speeds") {
        if (data.active) {
          if (data.active.measureIndex === 0 && data.active.noteIndex === 0) {
            abcData.abc.speed = value;
            await handleResetRender();
          } else {
            const activeNote = abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex] || null;
            if (!activeNote) return;
            activeNote.speed = `[${value}]`;
            await handleResetRender();
          }
          rangeHighlight(data.active.startChar);
        } else {
          abcData.abc.speed = value;
          await handleResetRender();
        }
      }

      //  附点
      if (type === "dot") {
        if (!data.active) {
          showToast("请先选择音符");
          return;
        }
        if (!activeNote) return;
        activeNote.dot = activeNote.dot ? "" : value;
        // activeNote.dot = activeNote.dot ? "" : NOTE_DOT[activeNote.noteType];
        await handleResetRender();
        rangeHighlight(data.active.startChar);
      }

      // 3连音
      if (type === "slus") {
        const activeNote = abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex] || null;
        if (!activeNote) return;
        activeNote.slus = activeNote.slus === value ? "" : value;
        await handleResetRender();
        rangeHighlight(data.active.startChar);
      }

      // 移动音符
      if (type === "move") {
        const step = value.action === "drag" ? value.step : value.action === "up" ? -1 : 1;
        if (data.multilList.length) {
          for (let i = 0; i < data.multilList.length; i++) {
            const item = data.multilList[i];
            const note = abcData.abc.measures[item.measureIndex].notes[item.noteIndex];
            note.content = moveNote(note.content, step);
          }
          await handleResetRender();
          for (let i = 0; i < data.multilList.length; i++) {
            const item = data.multilList[i];
            const abcElem = useIndexGetNote(`${item.measureIndex}.${item.noteIndex}`);
            abcElem?.abselem?.elemset?.[0]?.classList?.add("note_selected");
            if (!abcElem?.midiPitches) return;
            ABCJS.synth.playEvent(abcElem.midiPitches, abcElem.midiGraceNotePitches, 1000);
          }
          return;
        }
        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("").

        await handleResetRender();
        const _abcElem = rangeHighlight(data.active.startChar);
        if (!_abcElem?.midiPitches) return;
        ABCJS.synth.playEvent(_abcElem.midiPitches, _abcElem.midiGraceNotePitches, 1000);
      }

      // 删除音符
      if (type === "delete") {
        if (data.multilList.length) {
          for (let i = 0; i < data.multilList.length; i++) {
            const item = data.multilList[i];
            abcData.abc.measures[item.measureIndex].notes[item.noteIndex].is_delete = true;
          }
          for (let i = 0; i < abcData.abc.measures.length; i++) {
            abcData.abc.measures[i].notes = abcData.abc.measures[i].notes.filter((n) => n.is_delete !== true);
          }
          for (let i = 0; i < abcData.abc.measures.length; i++) {
            if (!abcData.abc.measures[i].notes.length) {
              abcData.abc.measures[i].notes.push(
                createNote({
                  content: "z",
                  noteType: "4",
                })
              );
            }
          }
        } else {
          if (!data.active) return;
          if (data.active.startChar === 0) return;
          abcData.abc.measures[data.active.measureIndex].notes.splice(data.active.noteIndex, 1);
          if (abcData.abc.measures[data.active.measureIndex].notes.length === 0) {
            abcData.abc.measures.splice(data.active.measureIndex, 1);
          }
        }
        await handleResetRender();
        data.active = null as unknown as INoteActive;
      }
      if (data.multilList.length) {
        handleClearMultiSelect();
      }
      if (type === "setrhythm") {
        abcData.abc.isrhythm = value;
        console.log("🚀 ~ abcData.abc.isrhythm:", abcData.abc.isrhythm);
        popup.moveKeyShow = false;
        await handleResetRender();
      }
    };
    const handleClearMultiSelect = async () => {
      data.multilList = [];
      data.multiSelectList = [];
      data.multiSelect = false;
    };

    const getNextNote = (index: number): AbcElem => {
      const abcElem = abcData.visualObj.getElementFromChar(index);
      // console.log("🚀 ~ abcElem:", index);
      if (abcElem?.el_type === "note") {
        return abcElem;
      } else {
        return getNextNote(abcElem.endChar);
      }
    };

    const handleDeleteNote = () => {
      if (!data.active) return;
      if (data.active.startChar === 0) return;
      abcData.abc.measures[data.active.measureIndex].notes.splice(data.active.noteIndex, 1);
      if (abcData.abc.measures[data.active.measureIndex].notes.length === 0) {
        abcData.abc.measures.splice(data.active.measureIndex, 1);
      }
      handleResetRender();
      data.active = null as unknown as INoteActive;
    };

    const clearSelectNote = () => {
      data.active = null as unknown as INoteActive;
      const notes = document.querySelectorAll(".abcjs-note_selected");
      notes.forEach((item) => {
        item.classList.remove("abcjs-note_selected");
        item.setAttribute("fill", "currentColor");
      });
    };

    const handleSelectNote = async () => {
      const type = data.select.parmas?.type;
      const value = data.select.parmas?.value;
      const startItem = data.select.list[0];
      const endItem = data.select.list[1];
      // 力度标记,渐强渐弱
      if (type === "dynamics") {
        if (abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].dynamics || abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].dynamics) {
          message.warning("已经添加了力度标记");
        } else {
          const crescendo = Date.now() + "";
          abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].dynamics = value[0];
          abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].dCode = crescendo;
          abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].dynamics = value[1];
          abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].dCode = crescendo;
          await handleResetRender();
        }
      }

      // 连音
      if (type === "tie") {
        const crescendo = Date.now() + "";
        if (abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].tie) {
          const tie = abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].tie;
          abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].tie = value[0] + tie;
        } else {
          abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].tie = value[0];
          abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].tCode = crescendo;
        }
        if (abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].tie) {
          const tie = abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].tie;
          abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].tie = tie + value[1];
        } else {
          abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].tie = value[1];
          abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].tCode = crescendo;
        }
        await handleResetRender();
      }
      data.select.state = false;
      data.select.list = [];
      data.select.parmas = null;
      clearSelectNote();
      message.destroyAll();
    };

    /** 移调 */
    const handleMoveKey = async (item: (typeof ABC_DATA.key)[0]) => {
      console.log(item);
      console.log(abcData.abc.visualKey || abcData.abc.key, "=>", item.value);
      const moveData = getKeyStep(item.value, abcData.abc.visualKey || abcData.abc.key, data.moveKeyType);
      console.log("🚀 ~ item:", moveData);
      (window as any).visual_Key = item.value.split(":")[1];
      abcData.abc.visualTranspose = moveData.step;
      abcData.abc.visualKey = item.value;
      popup.moveKeyShow = false;
      if (data.playState) {
        abcData.synthControl.disable(true);
        data.playState = false;
      }
      await handleResetRender();
    };

    const keyDownData = reactive({
      wait: false,
      control: false,
    });
    const handleKeyDonw = async (e: KeyboardEvent) => {
      if ((e.target as HTMLElement).nodeName === "INPUT") return;
      if (e.key === "Control" || e.key === "Meta") {
        keyDownData.control = true;
      }
      if (e.key === "z" && keyDownData.control && !keyDownData.wait) {
        e.preventDefault();
        if (data.undoList.length) {
          abcData.abc = cloneDeep(data.undoList[data.undoList.length - 1]);
          data.undoList.pop();
          keyDownData.wait = true;
          await handleResetRender();
          nextTick(() => {
            keyDownData.wait = false;
          });
        }
      }

      if (e.key.toLocaleLowerCase() === "shift") {
        if (!data.multiSelect && data.active?.el_type === "note") {
          console.log("shift 按下");
          data.multiSelectList = [cleanDeep(data.active)];
        }
        data.multiSelect = true;
      }
    };

    const handleKeyUp = (e: KeyboardEvent) => {
      if ((e.target as HTMLElement).nodeName === "INPUT") return;
      console.log(e.key.toLocaleLowerCase());
      if (e.key.toLocaleLowerCase() === "shift") {
        console.log("shift 抬起");
        data.multiSelect = false;
      }

      if (e.key === "Control" || e.key === "Meta") {
        keyDownData.control = false;
      }
      if (e.key.toLocaleLowerCase() == "1") {
        handleChange({ type: "type", value: "///" });
      }
      if (e.key.toLocaleLowerCase() == "2") {
        handleChange({ type: "type", value: "//" });
      }
      if (e.key.toLocaleLowerCase() == "3") {
        handleChange({ type: "type", value: "/" });
      }
      if (e.key.toLocaleLowerCase() == "4") {
        handleChange({ type: "type", value: "" });
      }
      if (e.key.toLocaleLowerCase() == "5") {
        handleChange({ type: "type", value: "2" });
      }
      if (e.key.toLocaleLowerCase() == "6") {
        handleChange({ type: "type", value: "4" });
      }
      if (!data.active) return false;
      if (e.key === "Backspace") {
        handleChange({ type: "delete", value: "" });
      }
      if (/^[A-Ga-g]$/.test(e.key)) {
        handleChange({ type: "note", value: e.key.toLocaleUpperCase() });
      }
      if (["ArrowUp", "ArrowDown"].includes(e.key)) {
        e.preventDefault();
        e.stopPropagation();
        handleChange({ type: "move", value: { action: e.key === "ArrowUp" ? "up" : "donw" } });
        return false;
      }

      if (e.key.toLocaleLowerCase() === "0") {
        handleChange({ type: "note", value: "z" });
      }

      if (e.key.toLocaleLowerCase() == "7") {
        handleChange({ type: "accidentals", value: "=" });
      }
      if (e.key.toLocaleLowerCase() == "8") {
        handleChange({ type: "accidentals", value: "^" });
      }
      if (e.key.toLocaleLowerCase() == "9") {
        handleChange({ type: "accidentals", value: "_" });
      }
    };

    /** 重置曲谱 */
    const handleCreateSvg = () => {
      abcData.abc.measures = initMusic(30);
      handleResetRender();
    };

    const instruments = computed(() => {
      return ABCJS.synth.instrumentIndexToName.map((name, index) => ({
        label: instrumentsNames[name as keyof typeof instrumentsNames],
        value: index,
      }));
    });
    const instrumentName = computed(() => {
      const code = ABCJS.synth.instrumentIndexToName[abcData.synthOptions.program];
      return instrumentsNames[code as keyof typeof instrumentsNames];
    });
    const getDetailData = async () => {
      const query = getQuery();
      data.loading = true;
      const res = await api_musicSheetCreationDetail(query.id);
      console.log(res, "12");
      if (res?.code == 200) {
        data.uploadStatus = res.data.uploadStatus || "";
        data.musicId = res.data.id || "";
        data.musicName = res.data.name || "";
        data.creator = res.data.creator || "";
        data.subjectId = res.data.subjectId || "";
        let abc = "" as any;
        try {
          abc = JSON.parse(res.data.creationData);
        } catch (error) {
          console.log(error);
        }
        if (abc) {
          console.log("🚀 ~ abc:", abc);
          data.musicName = abc.title ?? data.musicName;
          abcData.abc.title = data.musicName;
          abcData.abc.creator = data.creator;
          abcData.abc.celf = abc.celf || "K:treble";
          abcData.abc.key = abc.key.value || abc.key || "K:C";
          abcData.abc.meter = abc.meter.value || abc.meter || "M:4/4";
          abcData.abc.speed = abc.speed || "Q:1/4=60";
          abcData.abc.visualTranspose = abc.visualTranspose || 0;
          abcData.abc.visualKey = abc.visualKey || "K:C";
          abcData.abc.subjectCode = abc.subjectCode || "acoustic_grand_piano";
          const _instruments = ABCJS.synth.instrumentIndexToName.indexOf(abcData.abc.subjectCode as any);
          abcData.synthOptions.program = _instruments > -1 ? _instruments : 0;
          abcData.abc.measures = abc.measures || initMusic(30);
          abcData.abc.isrhythm = abc.isrhythm ? abc.isrhythm : "";
          // console.log("🚀 ~ abcData.abc:", abcData.abc);
        } else if (res?.data.xml) {
          const result = await requestOrigin.get(res.data.xml, {
            mode: "cors",
          });

          let abc: any = new DOMParser().parseFromString(result, "text/xml");
          // // console.log("🚀 ~ abc:", abc);
          abc = (window as any).vertaal(abc, { p: "f", t: 1, u: 0, v: 3, mnum: 0 });
          // console.log('abc', abc);
          const parseData = ABCJS.renderAbc("importRef", abc[0], { responsive: "resize" });
          console.log("🚀 ~ parseData:", parseData);
          abcData.abc = formateAbc(parseData[0], { subjectCode: abcData.abc.subjectCode });
          console.log(abcData.abc, "12");
          data.musicName = data.musicName || abcData.abc.title;
          data.creator = abcData.abc.creator || data.creator;
          await handleResetRender();
        }
      }
      data.loading = false;
      return res;
    };
    const setSaveLoading = (tips: boolean) => {
      data.saveLoading = true;
      if (tips) {
        data.saveLoadingText = true;
        message.loading("保存中...", { duration: 0 });
      }
    };

    /**
     * 保存谱面
     * @param tips 是否显示加载 默认 true
     * @param saveWav 是否保存wav文件 默认true
     */
    const handleSaveMusic = async (tips = true, saveWav = true) => {
      const query = getQuery();
      abcData.abc.title = data.musicName;
      abcData.abc.creator = data.creator;
      setSaveLoading(tips);
      const wavUrl = saveWav ? await productWav(false) : null;
      const pngUrl = await productPng(false);
      console.log("🚀 ~ pngUrl:", pngUrl);
      try {
        if (query.id) {
          const res = await api_musicSheetCreationUpdate({
            name: data.musicName || "未命名乐谱",
            creator: data.creator || "未命名乐谱",
            creationConfig: renderMeasures(abcData.abc, {
              hiddenIndex: true,
              showTitle: true,
              showCreator: true,
            }),
            creationData: JSON.stringify(cleanDeep(abcData.abc)),
            id: query.id,
            subjectId: data.subjectId,
            filePath: saveWav ? wavUrl : null,
            coverImg: pngUrl,
          });
        } else {
          const res = await api_musicSheetCreationSave({
            name: data.musicName || "未命名乐谱",
            creator: data.creator || "未命名乐谱",
            creationConfig: renderMeasures(abcData.abc, {
              hiddenIndex: true,
              showTitle: true,
              showCreator: true,
            }),
            creationData: JSON.stringify(cleanDeep(abcData.abc)),
            subjectId: data.subjectId,
            filePath: saveWav ? wavUrl : null,
            coverImg: pngUrl,
          });
          if (res?.data) {
            const hash = location.hash.split("?");
            const qs_data = qs.parse(hash[1]);
            qs_data.id = res.data;
            try {
              delete qs_data.config;
            } catch (error) {
              console.log("🚀 ~ error:", error);
            }
            location.hash = hash[0] + "?" + qs.stringify(qs_data);
          }
        }
      } catch (error) {
        console.log(error);
      }

      if (tips) {
        message.destroyAll();
        message.success("保存成功");
      }
      data.isSave = true;
      data.saveLoading = false;
      data.uploadClick = false;
      data.saveLoadingText = false;
    };
    const hanldeInitCreate = () => {
      const query = getQuery();
      const abc = decodeUrl(query.config);
      console.log("🚀 ~ abc:", abc);
      data.subjectId = abc.subjectId || "";
      abcData.abc.celf = abc.celf ?? "K:treble";
      abcData.abc.key = abc.key ?? "K:C";
      abcData.abc.meter = abc.meter ?? "M:4/4";
      abcData.abc.speed = abc.speed ?? "Q:1/4=80";
      abcData.abc.visualTranspose = abc.visualTranspose ?? 0;
      abcData.abc.subjectCode = abc.subjectCode ?? "acoustic_grand_piano";
      const _instruments = ABCJS.synth.instrumentIndexToName.indexOf(abcData.abc.subjectCode as any);
      abcData.synthOptions.program = _instruments > -1 ? _instruments : 0;
      abcData.abc.measures = initMusic(abc.measure ?? 30);
      data.loading = false;
    };
    const visibility = useDocumentVisibility();
    watch(
      () => visibility.value,
      (val: any) => {
        if (val === "hidden") {
          if (data.playState) {
            togglePlay("pause");
          }
        }
      }
    );
    onMounted(async () => {
      const query = getQuery();
      if (query.id) {
        await getDetailData();
      } else {
        hanldeInitCreate();
      }
      if (ABCJS.synth.supportsAudio()) {
        abcData.synthControl = new ABCJS.synth.SynthController();
        // console.log("🚀 ~ abcData.synthControl:", abcData.synthControl)
        abcData.synthControl.load("#audio", cursorControl, {
          displayLoop: true,
          displayRestart: true,
          displayPlay: true,
          displayProgress: true,
        });
      }

      await handleResetRender();
      loadMiniMp3();
      document.addEventListener("keyup", handleKeyUp);
      document.addEventListener("keydown", handleKeyDonw);
      window.onbeforeunload = (e) => {
        if (!data.isSave) {
          e.preventDefault();
          e.returnValue = "还有没保存的";
        }
      };
      abcData.synthControl.restart();
      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);
      document.removeEventListener("keydown", handleKeyDonw);
    });

    const measureComputed = computed(() => {
      if (!data.active) return {} as unknown as IMeasure;
      const activeMeasure = abcData.abc.measures[data.active.measureIndex] || {};
      return activeMeasure;
    });

    const noteComputed = computed(() => {
      if (!data.active) return {} as unknown as INote;
      const activeNote = abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex] || {};
      return activeNote;
    });

    /** 新建曲谱 */
    const handleCreateMusic = () => {
      showConfirmDialog({
        title: "温馨提示",
        message: "是否覆盖当前乐谱?",
      }).then(() => {
        handleCreateSvg();
        data.active = null as unknown as INoteActive;
      });
    };

    /** 添加小节 */
    const handleAddMearse = () => {
      for (let i = 0; i < data.addMearseNumber; i++) {
        if (["pre", "next"].includes(data.addMearseType)) {
          if (!data.active) {
            message.warning("请选择小节");
            return;
          }
          if (data.addMearseType === "pre") {
            abcData.abc.measures.splice(data.active.measureIndex, 0, createMeasure());
          } else if (data.addMearseType === "next") {
            abcData.abc.measures.splice(data.active.measureIndex + 1, 0, createMeasure());
          }
        } else {
          abcData.abc.measures.push(createMeasure());
        }
      }
      popup.barShow = false;
      handleResetRender();
    };

    const handleDeleteMearse = () => {
      if (data.deleteMearseType === "ing") {
        if (!data.active) {
          message.warning("请选择小节");
          return;
        }
        abcData.abc.measures.splice(data.active.measureIndex, 1);
      } else if (data.deleteMearseType === "finish") {
        let len = abcData.abc.measures.length;
        for (let i = len; i > 0; i--) {
          if (abcData.abc.measures[i - 1].notes.length === 1 && abcData.abc.measures[i - 1].notes[0].content === "z") {
            if (abcData.abc.measures.length === 1) {
              break;
            }
            abcData.abc.measures.splice(i - 1, 1);
          } else {
            break;
          }
        }
      }
      popup.mearseDeleteShow = false;
      handleResetRender();
    };

    const productPng = (isUrl = true) => {
      return new Promise((resolve) => {
        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 svgBox = paper.getBoundingClientRect();
        svg.setAttribute("width", `${svgBox.width * 3}`);
        svg.setAttribute("height", `${svgBox.height * 3}`);
        const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
        rect.setAttribute("x", "0");
        rect.setAttribute("y", "0");
        rect.setAttribute("width", `${svgBox.width * 10}`);
        rect.setAttribute("height", `${svgBox.height * 10}`);
        rect.setAttribute("fill", "#fff");
        svg.prepend(rect);
        if (svg) {
          const _canvas = svg2canvas(svg.outerHTML);
          if (isUrl) {
            // document.body.appendChild(_canvas);
            let el: any = document.createElement("a");
            // 设置 href 为图片经过 base64 编码后的字符串，默认为 png 格式
            el.href = _canvas.toDataURL();
            el.download = data.musicName + ".png";

            // 创建一个点击事件并对 a 标签进行触发
            const event = new MouseEvent("click");
            el.dispatchEvent(event);
          } else {
            _canvas.toBlob(async (blob) => {
              const pngUrl = await api_uploadFile(blob, data.musicId + ".png");
              resolve(pngUrl);
            }, "image/png");
          }
        }
      });
    };

    const downPng = async () => {
      abcData.abc.title = `T:${data.musicName}`;
      abcData.abc.creator = `R:${data.creator}`;
      productPng();
    };

    const downRef = ref();
    const downMidi = () => {
      const midi = ABCJS.synth.getMidiFile(abcData.visualObj, {
        chordsOff: true,
        midiOutputType: "link",
        fileName: "曲谱",
      });
      // console.log("🚀 ~ midi:", midi)
      downRef.value.innerHTML = midi;
      downRef.value.querySelector("a").click();
    };
    const productWav = async (isUrl = true) => {
      return new Promise((resolve) => {
        const midiBuffer = new ABCJS.synth.CreateSynth();
        midiBuffer
          .init({
            visualObj: abcData.visualObj,
            options: abcData.synthOptions,
          })
          .then(() => {
            midiBuffer.prime().then(async () => {
              if (isUrl) {
                downloadFile(midiBuffer.download(), (data.musicName || "曲谱") + ".wav");
              } else {
                const blob = bufferToWave((midiBuffer as any).getAudioBuffer());
                const wavurl = await api_uploadFile(blob, data.musicId + ".wav");
                resolve(wavurl);
              }
            });
          });
      });
    };
    const downWav = () => {
      try {
        if (abcData.synthControl) {
          abcData.synthControl.download((data.musicName || "曲谱") + ".wav");
        }
      } catch (error) {
        productWav();
      }
    };

    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();
      } else if (type === "midi") {
        downMidi();
      } else if (type === "wav") {
        downWav();
      } else if (type === "down-xml") {
        downXML();
      }
    };

    const handleExport = () => {
      data.active = null as unknown as INoteActive;
      const input = document.createElement("input");
      input.type = "file";
      input.accept = ".xml,.musicxml";
      input.onchange = async (e: any) => {
        data.loadingAudioSrouce = true;
        const file = e.target.files[0];
        // const formData = new FormData();
        // formData.append("xmlFile", file);
        // const res = await api_xmlToAbc(formData);
        // console.log("🚀 ~ res:", res.data)

        const reader = new FileReader();
        reader.onload = async (e: any) => {
          let abc = e.target.result;
          abc = new DOMParser().parseFromString(abc, "text/xml");
          // // console.log("🚀 ~ abc:", abc);
          abc = (window as any).vertaal(abc, { p: "f", t: 1, u: 0, v: 3, mnum: 0 });
          // console.log('abc', abc);
          const parseData = ABCJS.renderAbc("importRef", abc[0], { responsive: "resize" });
          console.log("🚀 ~ parseData:", parseData);
          abcData.abc = formateAbc(parseData[0], { subjectCode: abcData.abc.subjectCode });
          data.musicName = abcData.abc.title || data.musicName;
          data.creator = abcData.abc.creator || data.creator;
          await handleResetRender();
          data.loadingAudioSrouce = false;
        };
        reader.readAsText(file);
      };
      input.click();
    };

    /** 设置选段小节 */
    const handleSetSelectMeares = (index: number | null, type: "start" | "end") => {
      console.log("🚀 ~ index:", index);
      if (data.playState) {
        togglePlay("pause");
      }
      if (type === "start") {
        let note = index ? useIndexGetNote(`${index - 1}.0`) : null;
        note = data.times.find((_note: any) => _note.startChar === note.startChar);
        // console.log("🚀 ~ note:", note, data.times);
        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 {
        let note = index ? useIndexGetNote(`${index}.0`) : null;
        note = data.times.find((_note: any) => _note.startChar === note.startChar);
        // 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;
        }
      }
    };

    const handleUpdate = async () => {
      if (data.saveLoading) return;
      // if (!data.isSave) {
      // }
      // 由于没有保存wav文件这里需要在保存一次
      await handleSaveMusic();
      const query = getQuery();
      const res = await api_musicSheetCreationDetail(query.id);
      if (res.data) {
        if (res.data.uploadStatus !== "YES") {
          data.item = { ...res.data, visualObj: abcData.visualObj };
          data.uploadShow = true;
        } else {
          message.info("已是最新版本");
        }
      }
    };

    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>
                <div>退出</div>
              </div>
              <div class={styles.topBtn}>
                <FileBtn
                  saveLoading={data.saveLoading}
                  onSelect={(val: IFileBtnType) => {
                    if (val === "newMusic") {
                      handleCreateMusic();
                    } else if (val === "save") {
                      handleSaveMusic(true, false);
                    } else if (["xml"].includes(val)) {
                      handleExport();
                    } else if (val === "upload") {
                      handleUpdate();
                    } else if (["png", "midi", "wav", "down-xml"].includes(val)) {
                      handleDownFile(val);
                    } else if (val === "print") {
                    }
                  }}
                />
                <div>文件</div>
              </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>
                <div>附点</div>
              </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>

              <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>
                <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 })}>
                  <div class={[styles.btnImg, noteComputed.value.play?.includes(item.value) && styles.btnImgActive]}>
                    <img class={styles.topBtnIcon} src={item.icon} />
                  </div>
                  <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>
                        </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>
              </NDropdown>

              <div class={[styles.topBtn, styles.btnDisabled]}>
                <div class={styles.btnImg}>
                  <img class={styles.topBtnIcon} src={getImage("icon_14.png")} />
                </div>
                <div>翻转</div>
              </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")} />
                      </div>
                      <div
                        style={{
                          overflow: "hidden",
                          textOverflow: "ellipsis",
                          whiteSpace: "nowrap",
                          maxWidth: "60px",
                        }}
                      >
                        {instrumentName.value}
                      </div>
                    </div>
                  ),
                  default: () => (
                    <>
                      <div class={styles.btnLineTitle}>选择声部</div>

                      <NSelect
                        filterable
                        options={instruments.value}
                        v-model:value={abcData.synthOptions.program}
                        onUpdate:value={() => {
                          abcData.synthControl.pause();
                          data.playState = false;
                          nextTick(async () => {
                            await loadMiniMp3();
                            resetMidi(true);
                            popup.selectSubjectShow = false;
                            abcData.abc.subjectCode = ABCJS.synth.instrumentIndexToName[abcData.synthOptions.program];
                            autoSave();
                          });
                        }}
                      ></NSelect>
                    </>
                  ),
                }}
              </NPopover>

              <NPopover class={styles.popupWrap} showArrow={false} v-model:show={popup.moveKeyShow} trigger="click" contentStyle={{ width: "320px" }}>
                {{
                  trigger: () => (
                    <div class={[styles.topBtn, abcData.abc.isrhythm === "rhythm" && styles.btnDisabled]}>
                      <div class={styles.btnImg}>
                        <img class={styles.topBtnIcon} src={getImage("icon_15.png")} />
                      </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.visualKey === item.value && styles.btnItemActive]} onClick={() => handleMoveKey(item)}>
                                <div class={[styles.btnItemIcon]}>
                                  <TheIcon iconClassName={item.icon} />
                                </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")} />
                      </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")} />
                      </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();
                          }}
                        />

                        {/* <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"
												>
													确定
												</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>

                      <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 style={{ width: "48%" }} round onClick={() => (popup.barShow = false)}>
                            取消
                          </NButton>
                          <NButton style={{ width: "48%" }} round type="primary" onClick={() => handleAddMearse()}>
                            确定
                          </NButton>
                        </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")} />
                      </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.topBtn, abcData.abc.isrhythm === "rhythm" && styles.btnDisabled]}
                onClick={() => {
                  popup.jianpuShow = true;
                }}
              >
                <div class={[styles.btnImg]}>
                  <img class={styles.topBtnIcon} src={getImage("icon_30.png")} />
                </div>
                <div>简谱</div>
              </div>

              <div class={styles.topLine}></div>

              <div style={{ marginLeft: "auto" }} class={styles.topBtn}>
                <NSpin show={data.loadingAudioSrouce} size="small">
                  <div class={styles.btnImg} onClick={() => togglePlay("reset")}>
                    <img class={styles.topBtnIcon} src={getImage("icon_20.png")} />
                  </div>
                </NSpin>

                <div>重播</div>
              </div>
              <div class={styles.topBtn}>
                <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>
              {/*  styles.btnDisabled */}
              <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
                class={[styles.topBtn]}
                onClick={() => {
                  metronomeData.disable = !metronomeData.disable;
                  metronomeData.metro?.initPlayer();
                }}
              >
                <div class={[styles.btnImg, !metronomeData.disable && styles.btnImgActive]}>
                  <img class={styles.topBtnIcon} src={getImage("icon_23.png")} />
                </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>
                <div>设置</div>
              </div>
            </div>
          </div>
          <div class={styles.content}>
            <div class={styles.slide}>
              <NTabs
                class={styles.tabsmodel}
                v-model:value={abcData.abc.isrhythm}
                justify-content="space-evenly"
                onUpdate:value={(val) => {
                  handleChange({ type: "setrhythm", value: val || "" });
                }}
              >
                <NTabPane name="" tab="制谱模式"></NTabPane>
                <NTabPane name="rhythm" tab="节奏模式"></NTabPane>
              </NTabs>
              <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>
                        <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 })}>
                        <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} 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]}>
                        <TheIcon iconClassName={ABC_DATA.dynamics[8].icon} size={["2em", "2em"]} />
                      </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>
                    </div>
                  </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} />
                        </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>
                        <div>{item.name}</div>
                      </div>
                    ))}
                  </div>
                </CollapseItem>
              </Collapse>
            </div>

            <div class={[styles.box, abcData.abc.isrhythm === "rhythm" && styles.rhythm]}>
              {/* <NButton type={data.multiSelect ? "primary" : "default"}>多选</NButton> */}
              <div class={styles.titleBox}>
                <div class={styles.titleName} style={{ width: "50%", margin: "0 auto" }}>
                  <NInput
                    onBlur={() => {
                      data.isSave = false;
                      autoSave();
                    }}
                    onKeyup={(e: Event) => e.stopPropagation()}
                    v-model:value={data.musicName}
                    placeholder="曲谱名称"
                  />
                </div>
                <div style={{ width: "30%", margin: "10px 0 0 auto" }}>
                  <NInput
                    onBlur={() => {
                      data.isSave = false;
                      autoSave();
                    }}
                    onKeyup={(e: Event) => e.stopPropagation()}
                    v-model:value={data.creator}
                    placeholder="曲谱作者"
                  />
                </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="importRef" style={{ display: "none" }}></div>
              <div id="audio" style={{ display: "none" }}></div>
            </div>

            {data.loadingAudioSrouce && (
              <div class={styles.loading}>
                <NSpin description="资源加载中..."></NSpin>
              </div>
            )}
            {data.saveLoadingText && <div class={styles.loading}></div>}
          </div>
          <div ref={downRef}></div>

          <TheSetting v-model:show={popup.settingShow} />

          {data.selectMeasures.state && (
            <div style={{ left: data.selectMeasures.x + "px", top: data.selectMeasures.y + "px" }} 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.end ? data.selectMeasures.end + 1 : 0) || 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>
            </div>
          )}
        </div>
        <div class={styles.exportPng}>
          <div id="exportPng"></div>
        </div>

        <UploadToResources
          v-model:show={data.uploadShow}
          item={data.item}
          onSuccess={() => {
            data.uploadStatus = "YES";
            message.success("上传成功");
            if (data.uploadClick) {
              setTimeout(() => {
                handleClose();
              }, 300);
            }
          }}
        />

        <TheJianpu
          v-model:show={popup.jianpuShow}
          abc={renderMeasures(abcData.abc, {
            hiddenIndex: true,
            showTitle: true,
            showCreator: true,
            jianpu: true,
          })}
        />
      </>
    );
  },
});
