index.tsx 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import { defineComponent, nextTick, onMounted, onUnmounted, reactive, ref, watch } from "vue";
  2. import styles from "./index.module.less";
  3. import { NRadio, NSpace, NSpin, useDialog } from "naive-ui";
  4. import { getImage } from "../home/images";
  5. import TheCreate from "./component/the-create";
  6. import { storeData } from "/src/store";
  7. import { api_musicSheetCreationPage, api_musicSheetCreationRemove } from "../api";
  8. import ABCJS from "abcjs";
  9. import { usePageVisibility } from "@vant/use";
  10. import UploadToResources from "../component/upload-to-resources";
  11. import { getQuery } from "/src/utils/queryString";
  12. import { browser } from "/src/utils";
  13. import UploadToTasks from "../component/upload-to-tasks";
  14. import UploadFile from "../component/upload-file";
  15. import { eventGlobal, saveUploadCatch, uploadState } from "../component/upload-to-tasks/state";
  16. import { formateAbc, renderMeasures } from "../home/runtime";
  17. import requestOrigin from "umi-request";
  18. export default defineComponent({
  19. name: "Create",
  20. setup() {
  21. const query = getQuery();
  22. const dialog = useDialog();
  23. console.log(storeData.user);
  24. const forms = reactive({
  25. teacherId: storeData.user.id,
  26. page: 1,
  27. keyword: "",
  28. rows: 20,
  29. });
  30. const data = reactive({
  31. list: [] as any[],
  32. addShow: query.addShow ? true : false,
  33. loading: false,
  34. finish: false,
  35. isCreated: false,
  36. uploadShow: false,
  37. item: {} as any,
  38. });
  39. const getList = async () => {
  40. data.loading = true;
  41. const res = await api_musicSheetCreationPage({ ...forms });
  42. if (res?.code == 200) {
  43. if (data.isCreated) {
  44. data.isCreated = false;
  45. handleOpenNotaion(res.data.rows[0]);
  46. }
  47. data.list = data.list.concat(res.data.rows);
  48. data.finish = res.data.rows.length < forms.rows;
  49. }
  50. data.loading = false;
  51. };
  52. const handleReset = () => {
  53. forms.page = 1;
  54. data.finish = false;
  55. data.list = [];
  56. getList();
  57. };
  58. const pageVisibility = usePageVisibility();
  59. watch(pageVisibility, (val) => {
  60. if (val === "visible") {
  61. handleReset();
  62. }
  63. });
  64. const handleDelte = (item: any) => {
  65. const checked = ref(true);
  66. dialog.warning({
  67. autoFocus: false,
  68. class: "deleteDialog",
  69. title: "删除曲谱",
  70. content: () => (
  71. <div onClick={() => (checked.value = !checked.value)}>
  72. <NRadio checked={checked.value}>同步删除我的资源中的该曲目</NRadio>
  73. </div>
  74. ),
  75. // content: () => <div>确认删除当前曲谱?</div>,
  76. positiveText: "取消",
  77. positiveButtonProps: {
  78. type: "default",
  79. },
  80. negativeText: "删除",
  81. negativeButtonProps: {
  82. type: "primary",
  83. ghost: false,
  84. },
  85. onPositiveClick: () => {},
  86. onNegativeClick: async () => {
  87. await api_musicSheetCreationRemove(item.id, checked.value ? 1 : 0);
  88. handleReset();
  89. // 删除上传记录里面的数据
  90. const index = uploadState.uploadList.findIndex((upload: any) => upload.musicSheetCreationId === item.id);
  91. if (index !== -1) {
  92. uploadState.uploadList.splice(index, 1);
  93. saveUploadCatch();
  94. }
  95. },
  96. });
  97. };
  98. const loadingRef = ref();
  99. const messageEvent = (params?: any) => {
  100. // 在老师端里面关闭要刷新
  101. if (params.data?.api == "reload") {
  102. handleReset();
  103. }
  104. };
  105. onMounted(() => {
  106. getList();
  107. if (loadingRef.value) {
  108. const obv = new IntersectionObserver((entries) => {
  109. if (entries[0].isIntersecting) {
  110. if (data.finish || data.loading) return;
  111. forms.page++;
  112. getList();
  113. }
  114. });
  115. obv.observe(loadingRef.value?.$el);
  116. }
  117. window.addEventListener("message", (params?: any) => {
  118. messageEvent(params);
  119. });
  120. eventGlobal.on("resetList", handleReset);
  121. });
  122. onUnmounted(() => {
  123. window.removeEventListener("message", messageEvent);
  124. eventGlobal.off("resetList", handleReset);
  125. });
  126. const handleOpenNotaion = (item: any) => {
  127. window.parent.postMessage(
  128. {
  129. api: "notation_open",
  130. url: `${location.origin}/notation/#/?v=${Date.now()}&id=${item.id}`,
  131. },
  132. "*"
  133. );
  134. };
  135. const productSvg = (abc: string, id: string) => {
  136. const a = ABCJS.renderAbc(id, abc, { selectTypes: false, add_classes: true })[0];
  137. return a;
  138. };
  139. const handleSuccess = () => {
  140. data.list.find((item: any) => item.id === data.item.id).uploadStatus = "YES";
  141. };
  142. return () => (
  143. <div class={styles.wrap}>
  144. <UploadFile />
  145. <div class={styles.wrapBox}>
  146. <div class={styles.itemWrap}>
  147. <div class={styles.itemWrapBox}>
  148. <div class={styles.createItem} onClick={() => (data.addShow = true)}>
  149. <img src={getImage("icon_29.png")} />
  150. <div>新建乐谱</div>
  151. </div>
  152. </div>
  153. </div>
  154. {data.list.map((item, index: number) => (
  155. <div class={styles.itemWrap}>
  156. <div class={styles.itemWrapBox}>
  157. <div class={styles.item} onClick={() => handleOpenNotaion(item)}>
  158. <div class={[styles.imgBox]} id={"item_" + index}>
  159. <img
  160. src={getImage("icon_staff.png")}
  161. onLoad={async () => {
  162. // console.log(item.creationConfig, "1212");
  163. if (!item.creationConfig) {
  164. try {
  165. const result = await requestOrigin.get(item.xml, {
  166. mode: "cors",
  167. });
  168. let abc: any = new DOMParser().parseFromString(result, "text/xml");
  169. const title = abc.querySelector("movement-title");
  170. // xmlParse.getElementsByTagName("words");
  171. // const title = abc.getElementsByTagName("movement-title");
  172. if (title && title.textContent.trim() !== item?.name) {
  173. title.textContent = item.name;
  174. }
  175. abc = (window as any).vertaal(abc, { p: "f", t: 1, u: 0, v: 3, mnum: 0 });
  176. const r: any = ABCJS.renderAbc("importRef", abc[0], { responsive: "resize" }); //ABCJS.renderAbc("item_" + index, abc[0], { selectTypes: false, add_classes: true })[0];
  177. const abcd = formateAbc(r[0], { subjectCode: "acoustic_grand_piano" });
  178. const config = renderMeasures(abcd, {
  179. hiddenIndex: true,
  180. showTitle: true,
  181. showCreator: true,
  182. });
  183. item.visualObj = productSvg(config, "item_" + index);
  184. } catch (e) {
  185. console.log(e, "1222222");
  186. }
  187. } else {
  188. item.visualObj = productSvg(item.creationConfig, "item_" + index);
  189. }
  190. }}
  191. />
  192. </div>
  193. <div class={styles.itemBottom}>
  194. <div class={styles.bottombox}>
  195. <div class={styles.bottomLeft}>
  196. <div class={styles.itemtitle}>
  197. <span>{item.name || `未命名乐谱-${index + 1}`}</span>
  198. </div>
  199. <div class={styles.time}>{item.updateTime}</div>
  200. </div>
  201. {item.uploadStatus !== "YES" && (
  202. <img
  203. class={styles.bottomBtn}
  204. src={getImage("icon_29_2.png")}
  205. onClick={(e: Event) => {
  206. e.stopPropagation();
  207. const abc = item.creationData ? JSON.parse(item.creationData) : {};
  208. data.item = { ...item, subjectCode: abc.subjectCode || "acoustic_grand_piano" };
  209. nextTick(() => {
  210. data.uploadShow = true;
  211. });
  212. }}
  213. />
  214. )}
  215. <img
  216. class={styles.bottomBtn}
  217. src={getImage("icon_29_3.png")}
  218. onClick={(e: Event) => {
  219. e.stopPropagation();
  220. handleDelte(item);
  221. }}
  222. />
  223. </div>
  224. </div>
  225. {item.uploadStatus === "YES" && <img class={styles.btn} src={getImage("icon_29_4.png")} />}
  226. {item.uploadStatus === "UPDATE" && <img class={styles.btn} src={getImage("icon_29_5.png")} />}
  227. </div>
  228. </div>
  229. </div>
  230. ))}
  231. <div id="importRef" class={styles.importRef} style={{ display: "none" }}></div>
  232. </div>
  233. {!data.finish && (
  234. <NSpace ref={loadingRef} justify="center" style={{ padding: "30px" }}>
  235. <NSpin size="large" />
  236. </NSpace>
  237. )}
  238. <TheCreate
  239. v-model:show={data.addShow}
  240. onCreate={() => {
  241. data.addShow = false;
  242. }}
  243. />
  244. <UploadToResources v-model:show={data.uploadShow} item={data.item} onSuccess={() => handleSuccess()} />
  245. <UploadToTasks />
  246. </div>
  247. );
  248. },
  249. });