index.tsx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. import { Row, showToast, showConfirmDialog } from "vant";
  2. import { defineComponent, onMounted, onUnmounted, reactive, nextTick, ref } from "vue";
  3. import state, { IPlatform } from "/src/state";
  4. import request from "/src/utils/request";
  5. import { getQuery } from "/src/utils/queryString";
  6. import styles from "./index.module.less";
  7. import { Button, ButtonGroup, Icon, Switch, Tooltip } from "@varlet/ui";
  8. import "@varlet/ui/es/tooltip/style";
  9. import "@varlet/ui/es/button-group/style";
  10. import "@varlet/ui/es/switch/style";
  11. import { storeData } from "/src/store";
  12. import rightHideIcon from './image/right_hide_icon.png';
  13. import editIcon from './image/edit.png';
  14. import editCloseIcon from './image/edit_close.png';
  15. import editSaveIcon from './image/edit_save.png';
  16. import editPreIcon from './image/edit_pre.png';
  17. import editDeleteIcon from './image/edit_delete.png';
  18. import editResetIcon from './image/edit_reset.png';
  19. import editReduceIcon from './image/edit_reduce.png';
  20. import editAddIcon from './image/edit_add.png';
  21. let extStyleConfigJson: any = {};
  22. const clientWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
  23. const showToolBox = ref(true);
  24. export const moveData = reactive({
  25. /** 开启移动 */
  26. open: false,
  27. zoom: 1,
  28. partIndex: "0",
  29. hasExtJson: false,
  30. isWeb: false,
  31. modelList: [] as any[],
  32. activeIndex: -1,
  33. sw: 0,
  34. tool: {
  35. isAddAndSub: false,
  36. },
  37. noteCoords: [] as any[],
  38. });
  39. // 所以可点击音符的dom坐标集合
  40. const initNoteCoord = () => {
  41. const allNoteDot: any = Array.from(document.querySelectorAll('.node-dot'));
  42. moveData.noteCoords = allNoteDot.map((note: any) => {
  43. const note_bbox = note?.getBoundingClientRect?.() || { x: 0, y: 0 };
  44. return {
  45. x: note_bbox.x,
  46. y: note_bbox.y
  47. }
  48. })
  49. }
  50. // 找出离目标元素最近的音符
  51. const computedDistance = (x: number, y: number) => {
  52. let minDistance = -1, minidx = 0;
  53. let a, b, c;
  54. moveData.noteCoords.forEach((note: any, idx: any) => {
  55. //a,b为直角三角形的两个直角边
  56. a = Math.abs(note.x - x)
  57. b = Math.abs(note.y - y)
  58. //c为直角三角形的斜边
  59. c = Math.sqrt(a * a + b * b) as 0
  60. c = Number(c.toFixed(0)) as 0
  61. if (c !== 0 && (minDistance === - 1 || c < minDistance)) {
  62. //min为元素中离目标元素最近元素的距离
  63. minDistance = c
  64. minidx = idx
  65. }
  66. })
  67. return minidx
  68. }
  69. function initSvgId() {
  70. const svg = document.querySelector("#osmdSvgPage1");
  71. if (!svg) return;
  72. const vfstavetempo: HTMLElement[] = Array.from(svg.querySelectorAll(".vf-stavetempo"));
  73. const vftext: HTMLElement[] = Array.from(svg.querySelectorAll(".vf-text"));
  74. const vfstaveSection: HTMLElement[] = Array.from(svg.querySelectorAll(".vf-StaveSection"));
  75. const vfRepetition: HTMLElement[] = Array.from(svg.querySelectorAll(".vf-Repetition"));
  76. const vflineGroup: HTMLElement[] = Array.from(svg.querySelectorAll(".vf-lineGroup"));
  77. // console.log('速度标记',vfstavetempo)
  78. let tempIndex = 1;
  79. [...vfstavetempo].forEach((ele) => {
  80. setEleId(ele, "temp" + tempIndex);
  81. tempIndex++;
  82. });
  83. let textIndex = 1;
  84. [...vftext].forEach((ele) => {
  85. // console.log(ele.textContent,textIndex)
  86. setEleId(ele, "text" + textIndex);
  87. textIndex++;
  88. });
  89. let sectionIndex = 1;
  90. [...vfstaveSection].forEach((ele) => {
  91. setEleId(ele, "section" + sectionIndex);
  92. sectionIndex++;
  93. });
  94. let repIndex = 1;
  95. [...vfRepetition].forEach((ele) => {
  96. setEleId(ele, "repet" + repIndex);
  97. repIndex++;
  98. });
  99. let lineIndex = 1;
  100. [...vflineGroup].forEach((ele) => {
  101. setEleId(ele, "line" + lineIndex);
  102. lineIndex++;
  103. });
  104. // if (moveData.isWeb) {
  105. // readerModelBox();
  106. // }
  107. // if (moveData.hasExtJson) {
  108. // setTimeout(reloadReader, 2);
  109. // }
  110. }
  111. /**赋值id */
  112. function setEleId(ele: HTMLElement, eleId: string) {
  113. if (!ele || !eleId) return;
  114. const id = ele.getAttribute("id");
  115. if (!id) {
  116. ele.setAttribute("id", eleId);
  117. }
  118. createModelBox(ele as any, eleId as any);
  119. }
  120. function createModelBox(ele: SVGAElement, eleId?: any) {
  121. const musicContainer = document.getElementById("musicAndSelection")?.getBoundingClientRect() || { x: 0, y: 0 };
  122. const parentLeft = musicContainer.x || 0;
  123. const parentTop = musicContainer.y || 0;
  124. const noteBbox = ele.getBoundingClientRect();
  125. const bbox = {
  126. left: noteBbox.x - parentLeft + "px",
  127. top: noteBbox.y - parentTop + "px",
  128. width: noteBbox.width + "px",
  129. height: noteBbox.height + "px",
  130. };
  131. const type = ele.getAttribute("class");
  132. moveData.modelList.push({
  133. id: eleId || ele.getAttribute("id"),
  134. bbox,
  135. type,
  136. isMove: false,
  137. left: noteBbox.left,
  138. top: noteBbox.top,
  139. width: noteBbox.width,
  140. height: noteBbox.height,
  141. x: 0,
  142. y: 0,
  143. zoom: state.zoom,
  144. isDelete: false,
  145. d2: getLineGroupPathDx(ele as any),
  146. dx: 0,
  147. });
  148. }
  149. function getBox(ele: SVGAElement) {
  150. if (!ele) return {};
  151. const musicContainer = document.getElementById("musicAndSelection")?.getBoundingClientRect() || { x: 0, y: 0 };
  152. const parentLeft = musicContainer.x || 0;
  153. const parentTop = musicContainer.y || 0;
  154. const box = ele.getBoundingClientRect();
  155. return {
  156. left: box.x - parentLeft,
  157. top: box.y - parentTop,
  158. width: box.width,
  159. height: box.height,
  160. };
  161. }
  162. // 切换开关
  163. const switchMoveState = () => {
  164. // 如果编辑过,没有保存,点击取消状态,需要提醒用户是否取消
  165. if (moveData.open && undoData.undoList.length) {
  166. showConfirmDialog({
  167. className: "noSaveModal",
  168. title: "温馨提示",
  169. message: "您有新的修改还未保存,取消后本次编辑的内容将不会保存",
  170. }).then(() => {
  171. moveData.open = false
  172. });
  173. } else {
  174. moveData.open = !moveData.open
  175. }
  176. }
  177. // 过滤数据
  178. export const filterMoveData = async () => {
  179. const examSongId = state.examSongId;
  180. if (examSongId) {
  181. const fontSize = (window as any).fontSize
  182. const list = moveData.modelList
  183. .filter((n) => n.isMove)
  184. .map((n) => {
  185. /**
  186. * 找到移动后,此时与此元素距离最近的音符,并记录音符的索引和此元素与音符的x轴,y轴的间距
  187. */
  188. // 元素的位置
  189. const elementX = n.left + n.x, elementY = n.top + n.y;
  190. // 找出距离元素最近的音符
  191. const noteIdx = computedDistance(elementX, elementY);
  192. // 此元素距离最近音符的x轴,y轴距离
  193. const noteRelativeX = elementX - moveData.noteCoords[noteIdx]?.x, noteRelativeY = elementY - moveData.noteCoords[noteIdx]?.y;
  194. let item: any = {
  195. id: n.id,
  196. isMove: n.isMove,
  197. isDelete: n.isDelete,
  198. x: n.x,
  199. y: n.y,
  200. xRem: Math.abs(n.x / fontSize),
  201. yRem: Math.abs(n.y / fontSize),
  202. zoom: n.zoom,
  203. w: moveData.sw,
  204. type: n.type,
  205. noteIdx,
  206. noteRelativeX,
  207. noteRelativeY
  208. };
  209. if (n.type === "vf-lineGroup") {
  210. item.dx = n.dx;
  211. // 需要获取当前渐强渐弱线所对应的小节的宽度,算出相对当前宽度的比例,用于不同设备回显用
  212. const measureNum = document.getElementById(n.id)?.getAttribute('data-mnum');
  213. const measureWidth = measureNum ? document.querySelector(`g[data-num='${measureNum}']`)?.getBoundingClientRect()?.width : 0;
  214. if (measureWidth) {
  215. item.dxRate = n.dx / measureWidth;
  216. }
  217. }
  218. if (n.id.includes('text')) {
  219. // let copyDom = document.querySelector("#" + n.id)!.cloneNode(true) as SVGSVGElement
  220. const textContent = document.querySelector("#" + n.id)?.querySelector("text")?.innerHTML || ''
  221. item.textContent = textContent
  222. }
  223. return item;
  224. });
  225. // if (!list.length) {
  226. // showToast("请移动元素后再保存");
  227. // return
  228. // }
  229. extStyleConfigJson[moveData.partIndex] = list;
  230. console.log("🚀 ~ extStyleConfigJson", extStyleConfigJson)
  231. const dataParams = state.musicRenderType === 'staff' ? {
  232. id: examSongId,
  233. extStyleConfigJson: JSON.stringify(extStyleConfigJson),
  234. } : {
  235. id: examSongId,
  236. extJianStyleConfigJson: JSON.stringify(extStyleConfigJson),
  237. }
  238. const res = await request.post("/musicSheet/img", {
  239. requestType: "json",
  240. data: dataParams
  241. });
  242. if (res && res.code == 200) {
  243. showToast("保存成功");
  244. undoData.undoList = [];
  245. undoData.activeItem = null;
  246. if (state.musicRenderType === 'staff') {
  247. state.extStyleConfigJson = JSON.stringify(extStyleConfigJson)
  248. } else {
  249. state.extJianStyleConfigJson = JSON.stringify(extStyleConfigJson)
  250. }
  251. }
  252. clearActiveModel();
  253. }
  254. };
  255. // 移动
  256. const dragData = {
  257. open: false,
  258. startX: 0,
  259. startY: 0,
  260. x: 0,
  261. y: 0,
  262. repeatEdit: false,
  263. };
  264. // 记录
  265. export const undoData = reactive({
  266. undoList: [] as Array<any>, // 撤销列表
  267. redoList: [] as Array<any>, // 回退列表
  268. activeItem: null,
  269. });
  270. function onDown(e: MouseEvent) {
  271. const el: any = e.target;
  272. const index = moveData.modelList.findIndex((n) => n.id === el.dataset.id);
  273. if (index > -1) {
  274. const item = moveData.modelList[index];
  275. moveData.activeIndex = index;
  276. dragData.startX = e.clientX;
  277. dragData.startY = e.clientY;
  278. dragData.x = item.x;
  279. dragData.y = item.y;
  280. dragData.repeatEdit = item.noteIdx >= 0 ? true : false;
  281. // console.log("🚀 ~ 按下", index, el, item.x, item.y);
  282. document.onmousemove = onMove;
  283. document.onmouseup = onUp;
  284. dragData.open = true;
  285. if (item.type === "vf-lineGroup") {
  286. moveData.tool.isAddAndSub = true;
  287. } else {
  288. moveData.tool.isAddAndSub = false;
  289. }
  290. undoData.activeItem = { ...item };
  291. return;
  292. }
  293. moveData.activeIndex = -1;
  294. }
  295. function onMove(e: MouseEvent) {
  296. if (dragData.open) {
  297. const _x = e.clientX - dragData.startX + dragData.x;
  298. const _y = e.clientY - dragData.startY + dragData.y;
  299. setModelPostion(moveData.modelList[moveData.activeIndex], _x, _y, dragData.repeatEdit);
  300. }
  301. }
  302. function onUp(e: MouseEvent) {
  303. // console.log("🚀 ~ 抬起");
  304. document.onmousemove = null;
  305. document.onmouseup = null;
  306. dragData.open = false;
  307. const _x = e.clientX - dragData.startX + dragData.x;
  308. const _y = e.clientY - dragData.startY + dragData.y;
  309. if (_x || _y) {
  310. moveData.modelList[moveData.activeIndex].isMove = true;
  311. moveData.modelList[moveData.activeIndex].x = _x;
  312. moveData.modelList[moveData.activeIndex].y = _y;
  313. if (undoData.activeItem) {
  314. undoData.undoList.push({ ...(undoData.activeItem as any) });
  315. }
  316. }
  317. undoData.activeItem = null;
  318. }
  319. /** 渲染svg元素的属性 */
  320. const renderSvgItem = (item: any) => {
  321. setModelPostion(item, item.x, item.y);
  322. if (item.isDelete) {
  323. const g = document.querySelector("#" + item.id)!;
  324. g && ((g as any).style.display = "none");
  325. } else {
  326. const g = document.querySelector("#" + item.id)!;
  327. g && ((g as any).style.display = "");
  328. }
  329. };
  330. /** 设置元素位置 */
  331. async function setModelPostion(item: any, x: number, y: number, repeatEdit?: boolean) {
  332. // console.log(item)
  333. // console.log('位置',x,y)
  334. if (item) {
  335. const g = document.querySelector("#" + item.id)!; // svg元素
  336. const el: HTMLElement = document.querySelector(`[data-id=${item.id}]`)!; // svg元素的背景div
  337. let scaleZoom: number = item.zoom ? item.zoom : moveData.zoom;
  338. // 预览页时0.65倍的谱面,需要特殊处理下
  339. if (state.isPreView && state.zoom == 0.65) {
  340. scaleZoom = 0.65
  341. }
  342. if (x === 0 && y === 0) {
  343. g && g.removeAttribute("transform");
  344. el && (el.style.transform = "");
  345. } else {
  346. /** 如果是app内嵌打开,需要通过rem转换 */
  347. let tsX = x, tsY = y;
  348. // if (storeData.isApp && (item.xRem || item.yRem)) {
  349. // tsX = item.xRem * clientWidth/10
  350. // tsY = item.yRem * clientWidth/10
  351. // }
  352. if (item.noteIdx >= 0 && !repeatEdit) {
  353. if (!moveData.noteCoords.length) {
  354. await initNoteCoord()
  355. }
  356. const targetX = moveData.noteCoords[item.noteIdx].x + item.noteRelativeX*(state.zoom/0.8), targetY = moveData.noteCoords[item.noteIdx].y + item.noteRelativeY*(state.zoom/0.8);
  357. const original = document.getElementById(item.id)?.getBoundingClientRect() || { x: 0, y: 0 };
  358. tsX = targetX - original.x;
  359. tsY = targetY - original.y;
  360. // console.log('距离',tsX,tsY,x,y,moveData.zoom)
  361. if (state.platform === IPlatform.PC) {
  362. // tsX = tsX / 1.5
  363. // tsY = tsY / 1.8
  364. }
  365. g && g.setAttribute("transform", `translate(${tsX / scaleZoom}, ${tsY / scaleZoom})`);
  366. el && (el.style.transform = `translate(${tsX}px, ${tsY}px)`);
  367. } else {
  368. g && g.setAttribute("transform", `translate(${tsX / scaleZoom}, ${tsY / scaleZoom})`);
  369. el && (el.style.transform = `translate(${tsX}px, ${tsY}px)`);
  370. }
  371. }
  372. }
  373. }
  374. /** 删除元素 */
  375. const handleDeleteMoveNote = () => {
  376. const item = moveData.modelList[moveData.activeIndex];
  377. if (item) {
  378. moveData.modelList[moveData.activeIndex].isMove = true;
  379. undoData.undoList.push({ ...moveData.modelList[moveData.activeIndex] });
  380. moveData.modelList[moveData.activeIndex].isDelete = !item.isDelete;
  381. const g = document.querySelector("#" + item.id)!;
  382. g && ((g as any).style.display = moveData.modelList[moveData.activeIndex].isDelete ? "none" : '');
  383. } else {
  384. showToast("选中需要删除的元素");
  385. }
  386. };
  387. /** 重置数据 */
  388. const resetMoveNote = () => {
  389. for (let i = 0; i < moveData.modelList.length; i++) {
  390. moveData.modelList[i].x = 0;
  391. moveData.modelList[i].y = 0;
  392. moveData.modelList[i].isMove = false;
  393. moveData.modelList[i].isDelete = false;
  394. moveData.modelList[i].dx = 0;
  395. moveData.modelList[i].dxRate = 0;
  396. renderSvgItem(moveData.modelList[i]);
  397. if (moveData.modelList[i].type === "vf-lineGroup") {
  398. renderLineGroup(moveData.modelList[i]);
  399. }
  400. }
  401. clearActiveModel();
  402. };
  403. function clearActiveModel() {
  404. for (let i = 0; i < moveData.modelList.length; i++) {
  405. const item: HTMLElement = document.querySelector(`[data-id=${moveData.modelList[i].id}]`)!;
  406. if (item?.classList?.contains("activeModel")) {
  407. item.classList.remove("activeModel");
  408. }
  409. }
  410. moveData.activeIndex = -1;
  411. moveData.tool.isAddAndSub = false;
  412. }
  413. // 增加或减少, 渐强和渐弱的线长
  414. const handleAddAndSub = (type: 'add' | 'sub') => {
  415. if (!["add", "sub"].includes(type)) return;
  416. const lineGroup = moveData.modelList[moveData.activeIndex];
  417. if (!lineGroup || lineGroup.type !== "vf-lineGroup") return;
  418. lineGroup.isMove = true;
  419. const step = type === "add" ? 10 : -10;
  420. undoData.undoList.push({ ...moveData.modelList[moveData.activeIndex] });
  421. moveData.modelList[moveData.activeIndex].dx = lineGroup.dx + step;
  422. renderLineGroup(moveData.modelList[moveData.activeIndex]);
  423. };
  424. // 获取line的dx
  425. function getLineGroupPathDx(lineGroup: HTMLElement) {
  426. if (!lineGroup) return 0;
  427. const lines = lineGroup.querySelectorAll("path");
  428. if (lines?.length) {
  429. for (let i = 0; i < lines.length; i++) {
  430. const path = lines[i];
  431. let d = path.getAttribute("d");
  432. if (d) {
  433. let dx1: any = d.split("M")?.[1]?.split(" ") || [];
  434. let dx2: any = d.split("L")?.[1]?.split(" ") || [];
  435. dx1 = dx1[0] && !isNaN(Number(dx1[0])) ? Number(dx1[0]) : 0;
  436. dx2 = dx2[0] && !isNaN(Number(dx2[0])) ? Number(dx2[0]) : 0;
  437. if (dx1 && dx2) {
  438. if (dx1 < dx2) {
  439. return dx2;
  440. } else {
  441. return dx1;
  442. }
  443. }
  444. }
  445. }
  446. }
  447. return 0;
  448. }
  449. function renderLineGroup(lineGroup: any) {
  450. const group = document.querySelector("#" + lineGroup.id);
  451. if (!group) return;
  452. const lines = group.querySelectorAll("path");
  453. if (lines?.length) {
  454. for (let i = 0; i < lines.length; i++) {
  455. const path = lines[i];
  456. let d = path.getAttribute("d");
  457. if (d) {
  458. let dx1: any = d.split("M")?.[1]?.split(" ") || [];
  459. let dx2: any = d.split("L")?.[1]?.split(" ") || [];
  460. dx1 = dx1[0] && !isNaN(Number(dx1[0])) ? Number(dx1[0]) : 0;
  461. dx2 = dx2[0] && !isNaN(Number(dx2[0])) ? Number(dx2[0]) : 0;
  462. if (dx1 && dx2) {
  463. // 根据dxRate比例转换dx
  464. let targetDx = lineGroup.dx;
  465. if (lineGroup.dxRate) {
  466. // 需要获取当前渐强渐弱线所对应的小节的宽度,算出相对当前宽度的比例,用于不同设备回显用
  467. const measureNum = document.getElementById(lineGroup.id)?.getAttribute('data-mnum');
  468. const measureWidth = measureNum ? document.querySelector(`g[data-num='${measureNum}']`)?.getBoundingClientRect()?.width : 0;
  469. targetDx = measureWidth ? measureWidth * lineGroup.dxRate : lineGroup.dx;
  470. }
  471. // targetDx = targetDx * state.zoom;
  472. if (storeData.isApp) {
  473. targetDx = targetDx * state.zoom;
  474. }
  475. if (dx1 < dx2) {
  476. d = d.replace(dx2, lineGroup.d2 + targetDx + "");
  477. } else {
  478. d = d.replace(dx1, lineGroup.d2 + targetDx + "");
  479. }
  480. path.setAttribute("d", d);
  481. }
  482. }
  483. }
  484. const { width } = getBox(group as any);
  485. const div: HTMLElement = document.querySelector(`[data-id=${lineGroup.id}]`)!;
  486. div && (div.style.width = width + "px");
  487. }
  488. }
  489. /** 撤销 */
  490. const handleUndo = () => {
  491. const preItem = undoData.undoList.pop();
  492. // console.log("🚀 ~ preItem", preItem)
  493. if (preItem) {
  494. const itemIndex = moveData.modelList.findIndex((n: any) => n.id === preItem.id);
  495. if (itemIndex > -1) {
  496. moveData.modelList[itemIndex] = preItem;
  497. renderSvgItem(moveData.modelList[itemIndex])
  498. if (preItem.type === "vf-lineGroup") {
  499. renderLineGroup(preItem);
  500. }
  501. }
  502. }
  503. };
  504. /** 根据移动数据渲染 */
  505. export const renderForMoveData = () => {
  506. // 一行谱模式暂时不支持谱面编辑和回显
  507. if (state.isSingleLine) return;
  508. if (state.extStyleConfigJson || state.extJianStyleConfigJson) {
  509. try {
  510. extStyleConfigJson = state.musicRenderType === 'staff' ? JSON.parse(state.extStyleConfigJson) : JSON.parse(state.extJianStyleConfigJson);
  511. } catch (error) {
  512. extStyleConfigJson = {};
  513. }
  514. }
  515. if (!extStyleConfigJson || !extStyleConfigJson?.[moveData.partIndex]){
  516. initSvgId();
  517. return
  518. } else {
  519. initSvgId();
  520. }
  521. const list = extStyleConfigJson?.[moveData.partIndex];
  522. if (list && Array.isArray(list)) {
  523. nextTick(() => {
  524. console.log("🚀 ~ list", list);
  525. list.forEach((item: any) => {
  526. let index = moveData.modelList.findIndex((n: any) => n.id === item.id);
  527. if (item.type === 'vf-text' && item.textContent) {
  528. let textValue = document.querySelector("#" + moveData.modelList[index]?.id)?.querySelector("text")?.innerHTML || ''
  529. let targetIndex = index, preEnd = false, done = false, preIndex = index, nextIndex = index;
  530. // while (textValue !== item.textContent) {
  531. // if (preEnd) {
  532. // targetIndex = targetIndex + 1
  533. // } else {
  534. // targetIndex = targetIndex > 0 ? targetIndex - 1 : targetIndex
  535. // }
  536. // if (targetIndex == 0) preEnd = true
  537. // textValue = document.querySelector("#" + moveData.modelList[targetIndex].id)?.querySelector("text")?.innerHTML || ''
  538. // }
  539. if (textValue !== item.textContent) {
  540. while (!done) {
  541. let text1 = moveData.modelList[preIndex] ? document.querySelector("#" + moveData.modelList[preIndex].id)?.querySelector("text")?.innerHTML || '' : ''
  542. let text2 = moveData.modelList[nextIndex] ? document.querySelector("#" + moveData.modelList[nextIndex].id)?.querySelector("text")?.innerHTML || '' : ''
  543. if (text1 === item.textContent || text2 === item.textContent) {
  544. done = true
  545. targetIndex = text1 === item.textContent ? preIndex : nextIndex
  546. } else {
  547. // 有可能后台编辑的元素在部分屏幕尺寸下没有该元素,比如小节索引数,可能后台显示的是1,3,5,部分屏幕尺寸显示的1,3,6
  548. if (!text1 && !text2) {
  549. done = true
  550. targetIndex = -1
  551. }
  552. preIndex = preIndex - 1
  553. nextIndex = nextIndex + 1
  554. }
  555. }
  556. }
  557. index = targetIndex
  558. item.id = moveData.modelList[targetIndex]?.id
  559. }
  560. // console.log(66666666,index)
  561. if (index > -1) {
  562. moveData.modelList[index] = {
  563. ...moveData.modelList[index],
  564. ...item
  565. };
  566. renderSvgItem(moveData.modelList[index]);
  567. if (item.type === "vf-lineGroup") {
  568. renderLineGroup(moveData.modelList[index]);
  569. }
  570. }
  571. });
  572. });
  573. }
  574. };
  575. export default defineComponent({
  576. name: "move-music-score",
  577. setup() {
  578. moveData.zoom = state.zoom;
  579. const query = getQuery();
  580. const isOpen = query.isMove === "1" ? true : false;
  581. console.log("🚀 ~ isOpen:", isOpen);
  582. onMounted(() => {
  583. // if (isOpen) {
  584. // initSvgId();
  585. // }
  586. // renderForMoveData();
  587. moveData.modelList = []
  588. nextTick(() => initNoteCoord())
  589. // const hasToolDom = Array.from(document.body.children)?.some((item: any) => item?.id === 'toolBox')
  590. // if (!hasToolDom) {
  591. // const toolBox = document.getElementById("toolBox");
  592. // toolBox && document.body.appendChild(toolBox);
  593. // }
  594. const toolBox = document.getElementById("toolBox");
  595. toolBox && document.body.appendChild(toolBox);
  596. });
  597. onUnmounted(() => {
  598. moveData.modelList = []
  599. const toolBox = document.getElementById("toolBox");
  600. toolBox && document.body.removeChild(toolBox);
  601. })
  602. return () => (
  603. <div class={[moveData.open ? "" : styles.moveDisabled]}>
  604. <div id="toolBox">
  605. {/* <div class={[styles.toolBox, !showToolBox.value && styles.hideTool]} >
  606. <Switch v-model={moveData.open} />
  607. {moveData.open && (
  608. <>
  609. {moveData.tool.isAddAndSub && (
  610. <ButtonGroup size="small" elevation={false}>
  611. <Button onClick={() => handleAddAndSub('add')}>加</Button>
  612. <Button onClick={() => handleAddAndSub('sub')}>减</Button>
  613. </ButtonGroup>
  614. )}
  615. <Button size="small" onClick={handleUndo} disabled={undoData.undoList.length ? false : true}>
  616. <Icon name="arrow-down" style={{ transform: "rotate(90deg)" }} />
  617. </Button>
  618. <Button size="small" onClick={handleDeleteMoveNote} disabled={moveData.activeIndex > -1 ? false : true}>
  619. {moveData.modelList[moveData.activeIndex]?.isDelete ? '显示元素' : '删除元素'}
  620. </Button>
  621. <Button size="small" onClick={resetMoveNote}>
  622. 重置数据
  623. </Button>
  624. <Button size="small" type="primary" onClick={filterMoveData}>
  625. 保存数据
  626. </Button>
  627. <Button size="small" type="primary" onClick={() => showToolBox.value = false}>
  628. 收起
  629. </Button>
  630. </>
  631. )}
  632. </div>
  633. {
  634. !showToolBox.value &&
  635. <img
  636. class={[styles.rightHideIcon, !showToolBox.value ? styles.rightIconShow : '']}
  637. src={rightHideIcon}
  638. onClick={() => showToolBox.value = true } />
  639. } */}
  640. <div class={[styles.editToolBox, !moveData.open && styles.itemDisabled]}>
  641. {
  642. !state.isSingleLine &&
  643. <>
  644. <div class={[styles.editItem, styles.canEdit]} onClick={switchMoveState}>
  645. <img src={moveData.open ? editCloseIcon : editIcon} />
  646. <span>{moveData.open ? '取消' : '编辑'}</span>
  647. </div>
  648. <div class={styles.editItem} onClick={filterMoveData}>
  649. <img src={editSaveIcon} />
  650. <span>保存</span>
  651. </div>
  652. <div class={[styles.editItem, !undoData.undoList.length && styles.disabled]} onClick={handleUndo}>
  653. <img src={editPreIcon} />
  654. <span>撤回</span>
  655. </div>
  656. <div class={[styles.editItem, moveData.activeIndex <= -1 && styles.disabled]} onClick={handleDeleteMoveNote}>
  657. <img src={editDeleteIcon} />
  658. <span>{moveData.modelList[moveData.activeIndex]?.isDelete ? '回显' : '删除'}</span>
  659. </div>
  660. <div class={styles.editItem} onClick={resetMoveNote}>
  661. <img src={editResetIcon} />
  662. <span>重置</span>
  663. </div>
  664. {
  665. moveData.tool.isAddAndSub &&
  666. <div class={styles.extraItem}>
  667. <img src={editReduceIcon} onClick={() => handleAddAndSub('sub')} />
  668. <img src={editAddIcon} onClick={() => handleAddAndSub('add')} />
  669. </div>
  670. }
  671. </>
  672. }
  673. </div>
  674. </div>
  675. {moveData.modelList.map((item: any, index: number) => {
  676. return (
  677. <div class={[styles.noteMove, moveData.activeIndex === index && styles.activeModel]} style={item.bbox} data-id={item.id} onMousedown={onDown}></div>
  678. );
  679. })}
  680. </div>
  681. );
  682. },
  683. });