cloudPractice.tsx 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563
  1. import { computed, defineComponent, nextTick, onMounted, reactive, ref, shallowRef, watch } from "vue"
  2. import styles from "./index.module.scss"
  3. import NavContainer from "@/businessComponents/navContainer"
  4. import { ElEmpty, ElMessage, ElScrollbar } from "element-plus"
  5. import Dictionary from "@/components/dictionary"
  6. import MyInput from "@/components/myInput"
  7. import { NImage, NPopselect, NSpin, NTooltip } from "naive-ui"
  8. import PlayItem from "./component/play-item"
  9. import icon_default from "../../img/cloudPractice/icon_default.png"
  10. import iconBtnPause from "../../img/cloudPractice/icon-btn-pause.png"
  11. import iconBtnPlay from "../../img/cloudPractice/icon-btn-play.png"
  12. import btnSubmit from "../../img/cloudPractice/btn-submit.png"
  13. import iconTransfer from "../../img/cloudPractice/icon-transfer.png"
  14. import iconDownload from "../../img/cloudPractice/icon-download.png"
  15. import { httpAjaxErrMsg } from "@/plugin/httpAjax"
  16. import {
  17. queryPage2_gym,
  18. queryPage2_gyt,
  19. queryPage2_klx,
  20. querySubjectIds_gym,
  21. querySubjectIds_gyt,
  22. querySubjectIds_klx,
  23. queryTree_gym,
  24. queryTree_gyt,
  25. queryTree_klx,
  26. selectCondition_klx,
  27. cbsDetail_gym
  28. } from "@/api/cloudPractice.api"
  29. import axios from "axios"
  30. import { getInstrumentName } from "@/libs/instruments"
  31. import { formatXML, getCustomInfo, onlyVisible } from "./instrument"
  32. import { useFunction } from "./useData"
  33. import userStore from "@/store/modules/user"
  34. import PlayLoading from "./component/play-loading"
  35. import PracticeForm from "@/businessComponents/practiceForm"
  36. import { saveAs } from "file-saver"
  37. import JSZip from "jszip"
  38. import { svgtoblob } from "./formatSvgToImg"
  39. import { penShow, whitePenShow } from "@/businessComponents/globalTools/globalTools"
  40. import { handleFullscreen } from "@/libs/fullscreen"
  41. export default defineComponent({
  42. name: "cloudPractice",
  43. setup() {
  44. const userStoreHook = userStore()
  45. const { goToCloud, getPreViewCloud, isPracticeShow, practiceUrl, handlePracticeClose } = useFunction()
  46. const navs = [
  47. {
  48. name: "主页",
  49. url: "/"
  50. },
  51. {
  52. name: "云练习"
  53. }
  54. ]
  55. const spinRef = ref()
  56. const state = reactive({
  57. finshed: false,
  58. reshing: false,
  59. page: 1,
  60. rows: 20,
  61. details: {} as any,
  62. iframeSrc: "",
  63. listActive: 0, // 当前选中的对象
  64. firstTreeId: null as any, // 左侧
  65. categoryId: null as any, // 类型
  66. categoryName: "" as any, // 类型名称
  67. categoryList: [] as any[],
  68. levelList: [] as any[], // 级别
  69. levelId: null as any,
  70. typeList: [] as any[], // 类型
  71. typeId: -1 as any,
  72. subjectList: [] as any[], // 声部列表
  73. subjectId: -1 as any,
  74. list: [] as any[],
  75. searchStatus: false,
  76. queryStr: "", // 搜索条件
  77. partList: [] as any[],
  78. partNames: [] as any[],
  79. selectedPartName: "" as any,
  80. selectedTrack: "" as any,
  81. selectedPartIndex: 0,
  82. partXmlIndex: 0,
  83. musicPdfUrl: "", // 五线谱文件
  84. imgs: [] as any[], // 图片列表
  85. categoryShow: false, // 是否展开
  86. playState: "pause" as "play" | "pause", // 播放状态
  87. showPlayer: false // 是否显示播放器
  88. })
  89. const partColumns = ref<any>([])
  90. /** 选中的item */
  91. const activeItem = computed(() => {
  92. const list = state.list[state.listActive] || {}
  93. let tempList: any = {}
  94. if (userStoreHook.roles === "GYM") {
  95. // const item = list.background?.[0]
  96. // const audioFileUrl = item?.musicSheetType === "CONCERT" ? item?.metronomeUrl : item?.metronomeMp3Url || item?.mp3Url
  97. tempList = {
  98. id: list?.id,
  99. name: list?.name,
  100. // background: list?.background,
  101. // xmlUrl: item?.xmlUrl,
  102. // musicSheetType: item?.musicSheetType,
  103. audioFileUrl: list.audioFileUrl,
  104. titleImg: list?.titleImg
  105. // musicImg: list.titleImg
  106. // musicJianImg: list.musicJianSvg,
  107. // musicFirstImg: list.musicFirstSvg,
  108. // isScoreRender: item?.isScoreRender,
  109. // defaultScoreRender: item?.defaultScoreRender,
  110. // musicPdfUrl: item?.musicPdfUrl // 独奏使用PDF
  111. }
  112. } else if (userStoreHook.roles === "GYT") {
  113. tempList = {
  114. id: list?.id,
  115. name: list?.musicSheetName,
  116. background: list?.background,
  117. xmlUrl: list?.xmlFileUrl,
  118. musicSheetType: list?.musicSheetType,
  119. audioFileUrl: list?.audioFileUrl,
  120. titleImg: list?.titleImg,
  121. musicImg: list.musicImg,
  122. musicJianImg: list.musicJianSvg,
  123. musicFirstImg: list.musicFirstSvg,
  124. isScoreRender: list?.isScoreRender,
  125. defaultScoreRender: list?.defaultScoreRender,
  126. musicPdfUrl: list?.musicPdfUrl
  127. }
  128. } else if (userStoreHook.roles === "KLX") {
  129. const item: any = list.background?.[0]
  130. tempList = {
  131. id: list?.id,
  132. name: list?.musicSheetName,
  133. background: list?.background,
  134. xmlUrl: list?.xmlFileUrl,
  135. musicSheetType: list?.musicSheetType,
  136. audioFileUrl: item?.audioFileUrl,
  137. titleImg: list?.titleImg,
  138. musicImg: list.musicImg,
  139. musicJianImg: list.musicJianSvg,
  140. musicFirstImg: list.musicFirstSvg,
  141. isScoreRender: false,
  142. defaultScoreRender: false,
  143. musicPdfUrl: list?.musicPdfUrl
  144. }
  145. }
  146. return tempList
  147. })
  148. const songPrevNextStatus = computed(() => {
  149. let prev = true,
  150. next = true
  151. if (state.listActive === 0) {
  152. prev = false
  153. }
  154. if (state.listActive >= state.list.length - 1) {
  155. next = false
  156. }
  157. return {
  158. prev,
  159. next
  160. }
  161. })
  162. const downloadStatus = ref(false)
  163. const loading = ref(false)
  164. const staffLoading = ref(false)
  165. const storeData = shallowRef<any[]>([])
  166. const handleSearchList_gym = async () => {
  167. loading.value = true
  168. await httpAjaxErrMsg(queryTree_gym).then(async res => {
  169. loading.value = false
  170. if (res.code === 200) {
  171. storeData.value = res.data || []
  172. await setDefaultData()
  173. }
  174. })
  175. }
  176. const handleGetSubject_gym = async () => {
  177. loading.value = true
  178. await httpAjaxErrMsg(querySubjectIds_gym, { categoriesId: state.categoryId || state.firstTreeId }).then(res => {
  179. loading.value = false
  180. if (res.code === 200) {
  181. const result = res.data || []
  182. state.subjectList = result.map((item: any) => {
  183. return {
  184. label: item.name,
  185. value: item.id,
  186. instrumentIds: item.instrumentIds
  187. }
  188. })
  189. state.subjectList.unshift({
  190. label: "全部声部",
  191. value: -1
  192. })
  193. const userSubjectId = userStoreHook.userInfo.subjectId
  194. if (userSubjectId) {
  195. const tempSubjectId = userSubjectId.split(",")[0]
  196. state.subjectList.forEach((item: any) => {
  197. // 判断是否存在声部编号
  198. if (item.value === Number(tempSubjectId)) {
  199. state.subjectId = Number(tempSubjectId)
  200. }
  201. })
  202. }
  203. }
  204. })
  205. }
  206. const handleGetList_gym = async () => {
  207. loading.value = true
  208. const params = {
  209. page: state.page,
  210. rows: state.rows,
  211. subjectId: state.subjectId === -1 ? null : state.subjectId,
  212. categoriesId: state.typeId === -1 ? state.levelId : state.typeId,
  213. search: state.queryStr
  214. }
  215. await httpAjaxErrMsg(queryPage2_gym, params).then(res => {
  216. loading.value = false
  217. if (res.code === 200) {
  218. const result = res.data || []
  219. if (state.reshing) {
  220. state.list = []
  221. state.reshing = false
  222. }
  223. if (Array.isArray(result.rows)) {
  224. state.list = [...state.list, ...result.rows]
  225. state.finshed = state.page >= result.totalPage
  226. } else {
  227. state.finshed = true
  228. }
  229. } else {
  230. state.finshed = true
  231. }
  232. })
  233. }
  234. const handleGetDetail_gym = async () => {
  235. loading.value = true
  236. const { id } = state.list[state.listActive] || {}
  237. if (!id) return
  238. await httpAjaxErrMsg(cbsDetail_gym, id, { simpleFlag: "1" }).then(res => {
  239. loading.value = false
  240. if (res.code === 200) {
  241. const result = res.data || {}
  242. state.details = result
  243. }
  244. })
  245. }
  246. /** 管乐团数据查询 */
  247. const handleSearchList_gyt = async () => {
  248. loading.value = true
  249. await httpAjaxErrMsg(queryTree_gyt, {
  250. enable: true,
  251. page: 1,
  252. parentId: 0,
  253. rows: 10
  254. }).then(res => {
  255. loading.value = false
  256. if (res.code === 200) {
  257. storeData.value = res.data || []
  258. setDefaultData()
  259. }
  260. })
  261. }
  262. const handleGetSubject_gyt = async () => {
  263. loading.value = true
  264. await httpAjaxErrMsg(querySubjectIds_gyt, {
  265. enableFlag: true,
  266. page: 1,
  267. rows: 100
  268. }).then(res => {
  269. loading.value = false
  270. if (res.code === 200) {
  271. const result = res.data || []
  272. state.subjectList = result.map((item: any) => {
  273. return {
  274. label: item.name,
  275. value: item.id,
  276. instrumentIds: item.instrumentIds
  277. }
  278. })
  279. state.subjectList.unshift({
  280. label: "全部声部",
  281. value: -1
  282. })
  283. const userSubjectId = userStoreHook.userInfo.subjectId
  284. if (userSubjectId) {
  285. const tempSubjectId = userSubjectId.split(",")[0]
  286. state.subjectList.forEach((item: any) => {
  287. // 判断是否存在声部编号
  288. if (item.value === Number(tempSubjectId)) {
  289. state.subjectId = Number(tempSubjectId)
  290. }
  291. })
  292. }
  293. }
  294. })
  295. }
  296. const handleGetList_gyt = async () => {
  297. loading.value = true
  298. const params = {
  299. page: state.page,
  300. rows: state.rows,
  301. musicSubject: state.subjectId === -1 ? null : state.subjectId,
  302. musicSheetCategoriesId: state.typeId === -1 ? state.levelId : state.typeId,
  303. keyword: state.queryStr,
  304. detailFlag: true,
  305. status: 1
  306. }
  307. await httpAjaxErrMsg(queryPage2_gyt, params).then(res => {
  308. loading.value = false
  309. if (res.code === 200) {
  310. const result = res.data || {}
  311. if (state.reshing) {
  312. state.list = []
  313. state.reshing = false
  314. }
  315. if (Array.isArray(result.rows)) {
  316. result.rows.forEach((item: any) => {
  317. item.name = item.musicSheetName
  318. })
  319. state.list = [...state.list, ...result.rows]
  320. state.finshed = state.page >= result.pages
  321. } else {
  322. state.finshed = true
  323. }
  324. } else {
  325. state.finshed = true
  326. }
  327. })
  328. }
  329. /** 酷乐秀机构数据查询 */
  330. const handleSearchList_klx = async () => {
  331. loading.value = true
  332. await httpAjaxErrMsg(queryTree_klx, {
  333. enable: true,
  334. page: 1,
  335. parentId: 0,
  336. rows: 10
  337. }).then(async res => {
  338. loading.value = false
  339. if (res.code === 200) {
  340. const result = res.data || []
  341. const tempList: any = []
  342. result.forEach((item: any) => {
  343. if (item.musicNum > 0) {
  344. const subjectCounts = item.subjectCounts ? true : false
  345. const musicCounts = item.musicCounts ? true : false
  346. const ensembleCounts = item.ensembleCounts ? true : false
  347. const list: any = []
  348. if (subjectCounts) {
  349. list.push({
  350. label: "基础云练",
  351. value: "SUBJECT"
  352. })
  353. }
  354. if (musicCounts) {
  355. list.push({
  356. label: "独奏云练",
  357. value: "MUSIC"
  358. })
  359. }
  360. if (ensembleCounts) {
  361. list.push({
  362. label: "合奏云练",
  363. value: "ENSEMBLE"
  364. })
  365. }
  366. tempList.push({
  367. value: item.id,
  368. label: item.name,
  369. musicSheetCategoriesList: list
  370. })
  371. }
  372. })
  373. state.categoryList = tempList
  374. await setDefaultData()
  375. } else {
  376. state.finshed = true
  377. }
  378. })
  379. }
  380. const handleGetSubject_klx = async () => {
  381. loading.value = true
  382. await httpAjaxErrMsg(querySubjectIds_klx, {
  383. queryType: "list",
  384. page: 1,
  385. rows: 100
  386. }).then(res => {
  387. loading.value = false
  388. if (res.code === 200) {
  389. const result = res.data?.rows || []
  390. state.subjectList = result.map((item: any) => {
  391. return {
  392. label: item.name,
  393. value: item.id,
  394. instrumentIds: item.instrumentIds
  395. }
  396. })
  397. state.subjectList.unshift({
  398. label: "全部声部",
  399. value: -1
  400. })
  401. const userSubjectId = userStoreHook.userInfo.subjectId
  402. if (userSubjectId) {
  403. const tempSubjectId = userSubjectId.split(",")[0]
  404. state.subjectList.forEach((item: any) => {
  405. // 判断是否存在声部编号
  406. if (item.value === Number(tempSubjectId)) {
  407. state.subjectId = Number(tempSubjectId)
  408. }
  409. })
  410. }
  411. }
  412. })
  413. }
  414. const handleGetList_klx = async () => {
  415. if (!state.categoryId) return
  416. loading.value = true
  417. const params = {
  418. page: state.page,
  419. rows: state.rows,
  420. albumId: state.categoryId,
  421. subjectId: state.subjectId === -1 ? null : state.subjectId,
  422. subjectType: state.firstTreeId,
  423. level: state.levelId === -1 ? null : state.levelId,
  424. type: state.typeId === -1 ? null : state.typeId,
  425. keyword: state.queryStr
  426. }
  427. await httpAjaxErrMsg(queryPage2_klx, params).then(res => {
  428. loading.value = false
  429. if (res.code === 200) {
  430. const result = res.data || {}
  431. if (state.reshing) {
  432. state.list = []
  433. state.reshing = false
  434. }
  435. if (Array.isArray(result.rows)) {
  436. result.rows.forEach((item: any) => {
  437. item.name = item.musicSheetName
  438. })
  439. state.list = [...state.list, ...result.rows]
  440. state.finshed = state.page >= result.totalPage
  441. } else {
  442. state.finshed = true
  443. }
  444. } else {
  445. state.finshed = true
  446. }
  447. })
  448. }
  449. const handleSelectCondition_klx = async () => {
  450. if (!state.categoryId || !state.firstTreeId) return
  451. loading.value = true
  452. const params = {
  453. tenantAlbumId: state.categoryId,
  454. subjectType: state.firstTreeId
  455. }
  456. await httpAjaxErrMsg(selectCondition_klx, params).then(res => {
  457. loading.value = false
  458. if (res.code === 200) {
  459. const result = res.data || {}
  460. if (result.levelList && result.levelList.length > 0) {
  461. state.levelList = result.levelList.map((item: any) => {
  462. return {
  463. label: item.value,
  464. value: item.id
  465. }
  466. })
  467. state.levelList.unshift({
  468. label: "全部级别",
  469. value: -1
  470. })
  471. state.levelId = -1
  472. } else {
  473. state.levelList = []
  474. }
  475. if (result.typeList && result.typeList.length > 0) {
  476. state.typeList = result.typeList.map((item: any) => {
  477. return {
  478. label: item.value,
  479. value: item.id
  480. }
  481. })
  482. state.typeList.unshift({
  483. label: "全部类型",
  484. value: -1
  485. })
  486. state.typeId = -1
  487. } else {
  488. state.typeList = []
  489. }
  490. }
  491. })
  492. }
  493. /** 条件查询 */
  494. const handleAllSearchList = async () => {
  495. // GYM,GYT,KLX 区分 查询搜索条件数据
  496. if (userStoreHook.roles === "GYM") {
  497. await handleSearchList_gym()
  498. } else if (userStoreHook.roles === "GYT") {
  499. await handleSearchList_gyt()
  500. } else if (userStoreHook.roles === "KLX") {
  501. await handleSearchList_klx()
  502. }
  503. }
  504. const handleAllGetSubject = async () => {
  505. // GYM,GYT,KLX 区分 查询声部数据
  506. if (userStoreHook.roles === "GYM") {
  507. await handleGetSubject_gym()
  508. } else if (userStoreHook.roles === "GYT") {
  509. await handleGetSubject_gyt()
  510. } else if (userStoreHook.roles === "KLX") {
  511. await handleGetSubject_klx()
  512. }
  513. }
  514. const handleAllGetList = async () => {
  515. // GYM,GYT,KLX 区分 查询声部数据·
  516. if (userStoreHook.roles === "GYM") {
  517. await handleGetList_gym()
  518. } else if (userStoreHook.roles === "GYT") {
  519. await handleGetList_gyt()
  520. } else if (userStoreHook.roles === "KLX") {
  521. await handleGetList_klx()
  522. }
  523. }
  524. const handleGetDetail = async () => {
  525. // GYM,GYT,KLX 区分 查询声部数据·
  526. if (userStoreHook.roles === "GYM") {
  527. await handleGetDetail_gym()
  528. } else if (userStoreHook.roles === "GYT") {
  529. // await handleGetList_gyt()
  530. } else if (userStoreHook.roles === "KLX") {
  531. // await handleGetList_klx()
  532. }
  533. }
  534. /** 初始化数据 */
  535. const setDefaultData = async (type?: "first" | "category" | "level" | "type") => {
  536. if (userStoreHook.roles === "GYM") {
  537. await initCategories_gym(type)
  538. } else if (userStoreHook.roles === "GYT") {
  539. initCategories_gyt(type)
  540. } else if (userStoreHook.roles === "KLX") {
  541. await initCategories_klx(type)
  542. }
  543. }
  544. const initCategories_gym = async (type?: "first" | "category" | "level" | "type") => {
  545. if (storeData.value.length > 0 && !["category", "level", "type"].includes(type as any)) {
  546. let result: any = []
  547. if (type === "first" && state.firstTreeId) {
  548. result = storeData.value.find((item: any) => item.id === state.firstTreeId)?.sysMusicScoreCategoriesList || []
  549. } else {
  550. state.firstTreeId = storeData.value[0]?.id
  551. result = storeData.value[0]?.sysMusicScoreCategoriesList || []
  552. }
  553. state.categoryList = result.map((item: any) => {
  554. return {
  555. label: item.name,
  556. value: item.id,
  557. sysMusicScoreCategoriesList: item.sysMusicScoreCategoriesList || []
  558. }
  559. })
  560. state.categoryId = null
  561. state.categoryName = null
  562. state.levelId = null
  563. state.typeId = -1
  564. }
  565. if (state.categoryList.length > 0 && !["level", "type"].includes(type as any)) {
  566. let result: any = []
  567. if (type === "category" && state.categoryId) {
  568. result = state.categoryList.find((item: any) => item.value === state.categoryId)?.sysMusicScoreCategoriesList || []
  569. } else {
  570. state.categoryId = state.categoryList[0]?.value
  571. state.categoryName = state.categoryList[0]?.label
  572. result = state.categoryList[0]?.sysMusicScoreCategoriesList || []
  573. }
  574. state.levelList = result.map((item: any) => {
  575. return {
  576. label: item.name,
  577. value: item.id,
  578. sysMusicScoreCategoriesList: item.sysMusicScoreCategoriesList || []
  579. }
  580. })
  581. type && (await handleGetSubject_gym())
  582. }
  583. if (state.levelList.length > 0) {
  584. let result: any = []
  585. if (type === "level" && state.levelId) {
  586. result = state.levelList.find((item: any) => item.value === state.levelId)?.sysMusicScoreCategoriesList
  587. state.typeId = -1
  588. } else {
  589. state.levelId = state.levelList[0]?.value
  590. result = state.levelList[0]?.sysMusicScoreCategoriesList || []
  591. }
  592. state.typeList = result.map((item: any) => {
  593. return {
  594. label: item.name,
  595. value: item.id
  596. }
  597. })
  598. state.typeList.unshift({
  599. label: "全部",
  600. value: -1
  601. })
  602. }
  603. }
  604. const initCategories_gyt = (type?: "first" | "category" | "level" | "type") => {
  605. if (storeData.value.length > 0 && !["level", "type"].includes(type as any)) {
  606. let result: any = []
  607. if (type === "first" && state.firstTreeId) {
  608. result = storeData.value.find((item: any) => item.id === state.firstTreeId)?.musicSheetCategoriesList || []
  609. } else {
  610. state.firstTreeId = storeData.value[0]?.id
  611. result = storeData.value[0]?.musicSheetCategoriesList || []
  612. }
  613. state.levelList = result.map((item: any) => {
  614. return {
  615. label: item.name,
  616. value: item.id,
  617. musicSheetCategoriesList: item.musicSheetCategoriesList || []
  618. }
  619. })
  620. state.levelId = null
  621. state.typeId = -1
  622. }
  623. if (state.levelList.length > 0) {
  624. let result: any = []
  625. if (type === "level" && state.levelId) {
  626. result = state.levelList.find((item: any) => item.value === state.levelId)?.musicSheetCategoriesList
  627. } else {
  628. state.levelId = state.levelList[0]?.value
  629. result = state.levelList[0]?.musicSheetCategoriesList || []
  630. }
  631. state.typeList = result.map((item: any) => {
  632. return {
  633. label: item.name,
  634. value: item.id
  635. }
  636. })
  637. state.typeList.unshift({
  638. label: "全部",
  639. value: -1
  640. })
  641. state.typeId = -1
  642. }
  643. }
  644. const initCategories_klx = async (type?: "first" | "category" | "level" | "type") => {
  645. if (state.categoryList.length > 0 && !["level", "type", "first"].includes(type as any)) {
  646. let result: any = []
  647. if (type === "category" && state.categoryId) {
  648. result = state.categoryList.find((item: any) => item.value === state.categoryId)?.musicSheetCategoriesList || []
  649. } else {
  650. state.categoryId = state.categoryList[0]?.value
  651. state.categoryName = state.categoryList[0]?.label
  652. result = state.categoryList[0]?.musicSheetCategoriesList || []
  653. }
  654. storeData.value = result.map((item: any) => {
  655. return {
  656. id: item.value,
  657. name: item.label
  658. }
  659. })
  660. }
  661. if (storeData.value.length > 0 && !["level", "type"].includes(type as any)) {
  662. if (type === "first" && state.firstTreeId) {
  663. await handleSelectCondition_klx()
  664. } else {
  665. //
  666. state.firstTreeId = storeData.value[0]?.id
  667. await handleSelectCondition_klx()
  668. }
  669. state.levelId = -1
  670. state.typeId = -1
  671. }
  672. }
  673. const __init = async () => {
  674. await handleAllSearchList()
  675. await handleAllGetSubject()
  676. await handleAllGetList()
  677. await handleGetDetail() // 默认获取数据
  678. await toDetail()
  679. renderStaff()
  680. }
  681. // 全屏显示
  682. handleFullscreen(true, false)
  683. __init()
  684. const handleResh = () => {
  685. if (loading.value || state.finshed) return
  686. state.page = state.page + 1
  687. handleAllGetList()
  688. }
  689. const handleGetList = async () => {
  690. if (loading.value) return
  691. state.listActive = 0
  692. state.showPlayer = false
  693. state.playState = "pause"
  694. state.partNames = []
  695. state.partList = []
  696. state.details = {}
  697. state.selectedPartName = ""
  698. state.selectedTrack = ""
  699. state.selectedPartIndex = 0
  700. // state.musicPdfUrl = ""
  701. state.partXmlIndex = 0
  702. document.querySelector(".musicList-container")?.scroll(0, 0)
  703. state.page = 1
  704. state.finshed = false
  705. state.reshing = true
  706. state.list = []
  707. await handleAllGetList()
  708. }
  709. const toDetailGYM = async () => {
  710. const row: any = activeItem.value
  711. const details: any = state.details
  712. state.imgs = []
  713. if (details.musicSheetType === "SINGLE") {
  714. loading.value = false
  715. state.musicPdfUrl = details.musicPdfUrl
  716. return
  717. }
  718. // state.partNames = await getPartNames(row.xmlUrl)
  719. let partList = details.musicSheetSoundList || []
  720. partList = partList.filter((item: any) => item.audioPlayType === "PLAY")
  721. partList = partList.filter((item: any) => !item.track?.toLocaleUpperCase()?.includes("COMMON"))
  722. partColumns.value = partList.map((item: any, index: number) => {
  723. const instrumentName = getInstrumentName(item.track)
  724. // const xmlIndex = state.partNames.findIndex((name: any) => name.trim() === item.track)
  725. return {
  726. label: item.track + (instrumentName ? `(${instrumentName})` : ""),
  727. instrumentName: instrumentName,
  728. track: item.track,
  729. musicPdfUrl: item.musicPdfUrl,
  730. // xmlIndex: -1,
  731. value: index
  732. }
  733. })
  734. // 初始化数据
  735. // 是否显示总谱
  736. if (row.isScoreRender) {
  737. partColumns.value.unshift({
  738. label: "总谱",
  739. instrumentName: null,
  740. track: null,
  741. musicPdfUrl: "",
  742. xmlIndex: 999,
  743. value: 999
  744. })
  745. if (row.defaultScoreRender) {
  746. state.selectedPartIndex = 999
  747. }
  748. }
  749. console.log(partColumns.value, "partColumns.value")
  750. // gym 现在单独处理 管乐迷根据查询条件来筛选谱面
  751. let defaultShowStaff
  752. // 这里筛选当前的声轨
  753. const soundCodes = filterSoundCodes()
  754. if (soundCodes) {
  755. const soundCodesArr = soundCodes.split(",").map((code: string) => {
  756. return code
  757. .toLowerCase()
  758. .replace(/^\d+|\d+$/g, "")
  759. .trim()
  760. })
  761. defaultShowStaff = partColumns.value.find((item: any) =>
  762. soundCodesArr.includes(
  763. item.track &&
  764. item.track
  765. .toLowerCase()
  766. .replace(/^\d+|\d+$/g, "")
  767. .trim()
  768. )
  769. )
  770. }
  771. defaultShowStaff || (defaultShowStaff = partColumns.value.find((item: any) => item.value === state.selectedPartIndex))
  772. state.selectedPartName = defaultShowStaff?.instrumentName
  773. state.selectedTrack = defaultShowStaff?.track
  774. state.partXmlIndex = defaultShowStaff?.xmlIndex
  775. state.selectedPartIndex = defaultShowStaff?.value
  776. if (details.isScoreRender && details.defaultScoreRender) {
  777. state.musicPdfUrl = details?.musicPdfUrl || ""
  778. } else {
  779. state.musicPdfUrl = defaultShowStaff?.musicPdfUrl || ""
  780. }
  781. }
  782. const toDetail = async () => {
  783. if (userStoreHook.roles === "GYM") {
  784. await toDetailGYM()
  785. return
  786. }
  787. const row: any = activeItem.value
  788. state.imgs = []
  789. if (row.musicSheetType === "SINGLE") {
  790. loading.value = false
  791. state.musicPdfUrl = row.musicPdfUrl
  792. // if (userStoreHook.roles === "GYM") {
  793. // const imgs = row.musicImg ? row.musicImg.split(",") : []
  794. // imgs.forEach(async (item: string, index: number) => {
  795. // const imgCanvas = await imgToCanvas(item)
  796. // const img = await canvasAddTitle(imgCanvas, row.name)
  797. // state.imgs.push({
  798. // name: index + 1 + ".png",
  799. // url: img
  800. // })
  801. // })
  802. // }
  803. }
  804. state.partNames = await getPartNames(row.xmlUrl)
  805. let partList = row.background || []
  806. partList = partList.filter((item: any) => !item.track?.toLocaleUpperCase()?.includes("COMMON"))
  807. partColumns.value = partList.map((item: any, index: number) => {
  808. const instrumentName = getInstrumentName(item.track)
  809. const xmlIndex = state.partNames.findIndex((name: any) => name.trim() === item.track)
  810. let musicPdfUrl = ""
  811. if (userStoreHook.roles === "GYM") {
  812. musicPdfUrl = item.soundMusicPdfUrl
  813. } else if (userStoreHook.roles === "GYT") {
  814. musicPdfUrl = item.musicPdfUrl
  815. } else if (userStoreHook.roles === "KLX") {
  816. musicPdfUrl = item.musicPdfUrl
  817. }
  818. return {
  819. label: item.track + (instrumentName ? `(${instrumentName})` : ""),
  820. instrumentName: instrumentName,
  821. track: item.track,
  822. musicPdfUrl,
  823. xmlIndex,
  824. value: index
  825. }
  826. })
  827. // 初始化数据
  828. // 是否显示总谱
  829. if (row.isScoreRender) {
  830. partColumns.value.unshift({
  831. label: "总谱",
  832. instrumentName: null,
  833. track: null,
  834. musicPdfUrl: "",
  835. xmlIndex: 999,
  836. value: 999
  837. })
  838. if (row.defaultScoreRender) {
  839. state.selectedPartIndex = 999
  840. }
  841. }
  842. // gym 现在单独处理 管乐迷根据查询条件来筛选谱面
  843. // let defaultShowStaff
  844. // if (userStoreHook.roles === "GYM") {
  845. // // 这里筛选当前的声轨
  846. // const soundCodes = await filterSoundCodes()
  847. // if (soundCodes) {
  848. // const soundCodesArr = soundCodes.split(",").map((code: string) => {
  849. // return code
  850. // .toLowerCase()
  851. // .replace(/^\d+|\d+$/g, "")
  852. // .trim()
  853. // })
  854. // defaultShowStaff = partColumns.value.find((item: any) =>
  855. // soundCodesArr.includes(
  856. // item.track &&
  857. // item.track
  858. // .toLowerCase()
  859. // .replace(/^\d+|\d+$/g, "")
  860. // .trim()
  861. // )
  862. // )
  863. // }
  864. // defaultShowStaff || (defaultShowStaff = partColumns.value.find((item: any) => item.value === state.selectedPartIndex))
  865. // } else {
  866. const defaultShowStaff = partColumns.value.find((item: any) => item.value === state.selectedPartIndex)
  867. // }
  868. state.selectedPartName = defaultShowStaff?.instrumentName
  869. state.selectedTrack = defaultShowStaff?.track
  870. state.partXmlIndex = defaultShowStaff?.xmlIndex
  871. state.selectedPartIndex = defaultShowStaff?.value
  872. if (row.isScoreRender && row.defaultScoreRender) {
  873. state.musicPdfUrl = row?.musicPdfUrl || ""
  874. } else {
  875. state.musicPdfUrl = defaultShowStaff?.musicPdfUrl || ""
  876. }
  877. }
  878. const getPartNames = async (xmlUrl: string) => {
  879. const partNames: string[] = []
  880. try {
  881. const res: any = await axios.get(xmlUrl)
  882. const xml: any = new DOMParser().parseFromString(res.data, "text/xml")
  883. for (const item of xml.getElementsByTagName("part-name")) {
  884. if (item.textContent) {
  885. partNames.push(item.textContent)
  886. }
  887. }
  888. } catch (error) {
  889. //
  890. }
  891. return partNames.filter((text: string) => text.toLocaleUpperCase() !== "COMMON") || []
  892. }
  893. const musicIframeLoad = async () => {
  894. if (userStoreHook.roles === "GYM") {
  895. // 判断是用哪个渲染的
  896. loading.value = false
  897. staffLoading.value = false
  898. return
  899. }
  900. const iframeRef: any = document.getElementById("staffIframeRef")
  901. if (iframeRef && iframeRef.contentWindow?.renderXml) {
  902. staffLoading.value = true
  903. const res: any = await axios.get(activeItem.value.xmlUrl)
  904. const parseXmlInfo = getCustomInfo(res.data)
  905. const xml = formatXML(parseXmlInfo.parsedXML)
  906. if (activeItem.value.isScoreRender) {
  907. const canSelectTracks: any = []
  908. const background = activeItem.value.background || []
  909. background.forEach((item: any) => {
  910. canSelectTracks.push(item.track)
  911. })
  912. iframeRef.contentWindow.renderXml(xml, activeItem.value.isScoreRender)
  913. } else {
  914. const currentXml = onlyVisible(xml, state.partXmlIndex)
  915. iframeRef.contentWindow.renderXml(currentXml)
  916. }
  917. }
  918. }
  919. const resetRender = async () => {
  920. const iframeRef: any = document.getElementById("staffIframeRef")
  921. if (userStoreHook.roles === "GYM") {
  922. iframeRef.contentWindow.location.replace(getPreViewCloud(activeItem.value.id, state.partXmlIndex, state.selectedTrack))
  923. // state.iframeSrc = getPreViewCloud(activeItem.value.id, state.partXmlIndex)
  924. return
  925. }
  926. if (iframeRef && iframeRef.contentWindow?.renderXml) {
  927. staffLoading.value = true
  928. const res: any = await axios.get(activeItem.value.xmlUrl)
  929. const parseXmlInfo = getCustomInfo(res.data)
  930. const xml = formatXML(parseXmlInfo.parsedXML)
  931. if (activeItem.value.isScoreRender) {
  932. iframeRef.contentWindow.renderXml(xml, activeItem.value.isScoreRender)
  933. } else {
  934. const currentXml = onlyVisible(xml, state.partXmlIndex)
  935. iframeRef.contentWindow.renderXml(currentXml)
  936. }
  937. }
  938. }
  939. const renderStaff = async () => {
  940. try {
  941. if (state.musicPdfUrl) {
  942. state.iframeSrc = "/pdf/web/viewer.html?file=" + encodeURIComponent(state.musicPdfUrl) + "&t=" + Date.now()
  943. // https://cdn.oss.dayaedu.com/daya202409/UOFW4q5.pdf
  944. // https://cdn.oss.dayaedu.com/daya202409/UOFVK2A.pdf
  945. // https://cdn.oss.dayaedu.com/daya202409/UODQffO.pdf
  946. } else {
  947. if (userStoreHook.roles === "GYM") {
  948. state.iframeSrc = getPreViewCloud(activeItem.value.id, state.partXmlIndex, state.selectedTrack)
  949. } else {
  950. state.iframeSrc = `/osmd/index.html`
  951. }
  952. }
  953. } catch (error) {
  954. //
  955. }
  956. }
  957. /** 音频控制 */
  958. const handleChangeAudio = (type: "play" | "pause" | "pre" | "next") => {
  959. if (type === "play") {
  960. state.playState = "play"
  961. } else if (type === "pause") {
  962. state.playState = "pause"
  963. } else if (type === "pre") {
  964. if (state.list[state.listActive - 1]) {
  965. handlePlay(state.list[state.listActive - 1])
  966. }
  967. } else if (type === "next") {
  968. if (state.list[state.listActive + 1]) {
  969. handlePlay(state.list[state.listActive + 1])
  970. }
  971. }
  972. }
  973. /** 播放曲目 */
  974. const handlePlay = (item: any) => {
  975. const index = state.list.findIndex((_item: any) => _item.id === item.id)
  976. if (index > -1) {
  977. if (state.listActive === index) {
  978. state.playState = state.playState === "play" ? "pause" : "play"
  979. } else {
  980. state.playState = "play"
  981. }
  982. state.showPlayer = true
  983. state.listActive = index
  984. }
  985. }
  986. // // 多个文件下载
  987. const downLoadMultiFile = (files: any, filesName: string) => {
  988. const zip = new JSZip()
  989. for (const i in files) {
  990. zip.file(files[i].name, files[i].url, { binary: true })
  991. }
  992. zip.generateAsync({ type: "blob" }).then(res => {
  993. saveAs(res, filesName ? filesName + ".zip" : `文件夹${Date.now()}.zip`)
  994. })
  995. downloadStatus.value = false
  996. }
  997. const showLoading = async (e: any) => {
  998. if (e.data?.api === "musicStaffRender") {
  999. const musicName =
  1000. activeItem.value.name +
  1001. (((activeItem.value.musicSheetType === "CONCERT" || state.details.musicSheetType === "CONCERT") && state.selectedPartName) ||
  1002. state.selectedTrack
  1003. ? `(${state.selectedPartName || state.selectedTrack})`
  1004. : "")
  1005. try {
  1006. const osmdImg = e.data.osmdImg
  1007. const imgs = []
  1008. for (let i = 0; i < osmdImg.length; i++) {
  1009. const img = await svgtoblob(osmdImg[i].img, osmdImg[i].width, osmdImg[i].height, musicName)
  1010. imgs.push({
  1011. url: img,
  1012. name: i + 1 + ".png"
  1013. })
  1014. }
  1015. state.imgs = imgs
  1016. } catch (e) {
  1017. // console.log(e);
  1018. }
  1019. staffLoading.value = e.data.loading
  1020. loading.value = e.data.loading
  1021. }
  1022. }
  1023. const searchContent = async () => {
  1024. await toDetail()
  1025. if (activeItem.value?.id) {
  1026. if (state.musicPdfUrl) {
  1027. staffLoading.value = true
  1028. renderStaff()
  1029. } else {
  1030. // 为了处理,之前是使用pdf渲染,现在又用osmd,iframe没有重新加载
  1031. if (state.iframeSrc.indexOf("pdf/web") !== -1) {
  1032. renderStaff()
  1033. } else {
  1034. resetRender()
  1035. }
  1036. }
  1037. }
  1038. }
  1039. /** 下载图片 */
  1040. const onDownload = () => {
  1041. if (downloadStatus.value || staffLoading.value) return
  1042. const musicName =
  1043. activeItem.value.name +
  1044. ((activeItem.value.musicSheetType === "CONCERT" && state.selectedPartName) || state.selectedTrack
  1045. ? `(${state.selectedPartName || state.selectedTrack})`
  1046. : "")
  1047. downloadStatus.value = true
  1048. if (state.musicPdfUrl) {
  1049. // 发起Fetch请求
  1050. fetch(state.musicPdfUrl)
  1051. .then(response => response.blob())
  1052. .then(blob => {
  1053. saveAs(blob, musicName)
  1054. downloadStatus.value = false
  1055. })
  1056. .catch(() => {
  1057. ElMessage.error("下载失败")
  1058. downloadStatus.value = false
  1059. })
  1060. } else {
  1061. downLoadMultiFile(state.imgs, musicName)
  1062. }
  1063. }
  1064. // 根据当前选中的声部和曲目筛选出对应的声轨
  1065. function filterSoundCodes() {
  1066. // const { id } = state.list[state.listActive] || {}
  1067. // if (!id) {
  1068. // return undefined
  1069. // }
  1070. const { instrumentIds } =
  1071. state.subjectList.find((item: any) => {
  1072. return item.value === state.subjectId
  1073. }) || {}
  1074. if (instrumentIds) {
  1075. // GYM,GYT,KLX 区分 查询声部数据
  1076. let cbsDetails: any = []
  1077. if (userStoreHook.roles === "GYM") {
  1078. cbsDetails = state.details.musicalInstruments || []
  1079. } else if (userStoreHook.roles === "GYT") {
  1080. //
  1081. } else if (userStoreHook.roles === "KLX") {
  1082. //
  1083. }
  1084. const { code } =
  1085. cbsDetails.find((item: any) => {
  1086. return instrumentIds == item.id
  1087. }) || {}
  1088. return code
  1089. }
  1090. return undefined
  1091. }
  1092. // 白板的批注打开时暂停播放
  1093. watch(
  1094. () => [whitePenShow.value, penShow.value],
  1095. () => {
  1096. if (whitePenShow.value || penShow.value) {
  1097. handleChangeAudio("pause")
  1098. }
  1099. }
  1100. )
  1101. onMounted(() => {
  1102. const obv = new IntersectionObserver(entries => {
  1103. if (entries[0].intersectionRatio > 0) {
  1104. handleResh()
  1105. }
  1106. })
  1107. obv.observe(spinRef.value)
  1108. window.addEventListener("message", showLoading)
  1109. })
  1110. return () => (
  1111. <NavContainer navs={navs}>
  1112. {/* <ElScrollbar class="elScrollbar"> */}
  1113. <div class={styles.cloudPractice}>
  1114. <div class={styles.leftContainer}>
  1115. <div class={styles.details}>
  1116. {storeData.value.length > 0 && (
  1117. <ElScrollbar class={styles.leftSection}>
  1118. {/* 基 础 云 练 */}
  1119. {storeData.value.map((item: any) => (
  1120. <div
  1121. class={[styles.leftSection_item, item.id === state.firstTreeId && styles.leftSection_item__active]}
  1122. onClick={async () => {
  1123. if (loading.value) return
  1124. state.firstTreeId = item.id
  1125. await setDefaultData("first")
  1126. await handleGetList()
  1127. await handleGetDetail()
  1128. searchContent()
  1129. }}
  1130. >
  1131. {item.name}
  1132. </div>
  1133. ))}
  1134. </ElScrollbar>
  1135. )}
  1136. <div class={[styles.musicList, "musicList-container"]}>
  1137. <div class={styles.searchHeader}>
  1138. {state.categoryList.length > 1 && (
  1139. <div class={[styles.categorySection]}>
  1140. <NPopselect
  1141. placement="bottom-start"
  1142. disabled={loading.value}
  1143. options={state.categoryList}
  1144. v-model:value={state.categoryId}
  1145. onUpdate:value={async (val: any) => {
  1146. const item = state.categoryList.find((item: any) => item.value === val)
  1147. if (item) {
  1148. state.categoryName = item.label
  1149. state.categoryId = item.value
  1150. await setDefaultData("category")
  1151. await handleGetList()
  1152. await handleGetDetail()
  1153. searchContent()
  1154. }
  1155. }}
  1156. onUpdate:show={(value: any) => {
  1157. state.categoryShow = value
  1158. }}
  1159. trigger="click"
  1160. class={"PopSelect"}
  1161. >
  1162. <span class={[styles.iconTagName, state.categoryShow && styles.show]}>
  1163. <span>{state.categoryName}</span>
  1164. </span>
  1165. </NPopselect>
  1166. </div>
  1167. )}
  1168. <div class={styles.searchMore}>
  1169. <div class={styles.searchSection}>
  1170. <Dictionary
  1171. clearable={false}
  1172. popperClass="classTypePopper"
  1173. v-model={state.subjectId}
  1174. height={42}
  1175. // disabled={loading.value}
  1176. options={state.subjectList}
  1177. placeholder="全部声部"
  1178. onChange={async () => {
  1179. await handleGetList()
  1180. await handleGetDetail()
  1181. searchContent()
  1182. }}
  1183. />
  1184. {state.levelList.length ? (
  1185. <Dictionary
  1186. clearable={false}
  1187. popperClass="classTypePopper"
  1188. v-model={state.levelId}
  1189. height={42}
  1190. // disabled={loading.value}
  1191. options={state.levelList}
  1192. placeholder="级别"
  1193. onChange={async () => {
  1194. setDefaultData("level")
  1195. await handleGetList()
  1196. await handleGetDetail()
  1197. searchContent()
  1198. }}
  1199. />
  1200. ) : null}
  1201. {state.typeList.length > 0 ? (
  1202. <Dictionary
  1203. clearable={false}
  1204. popperClass="classTypePopper"
  1205. v-model={state.typeId}
  1206. height={42}
  1207. // disabled={loading.value}
  1208. options={state.typeList}
  1209. propsOpt={{
  1210. labelField: "name",
  1211. valueField: "id"
  1212. }}
  1213. placeholder="分类"
  1214. onChange={async () => {
  1215. await handleGetList()
  1216. await handleGetDetail()
  1217. searchContent()
  1218. }}
  1219. />
  1220. ) : null}
  1221. </div>
  1222. <div
  1223. class={[styles.btnSearch, state.searchStatus && styles.btnSearchActive]}
  1224. onClick={() => (state.searchStatus = !state.searchStatus)}
  1225. ></div>
  1226. </div>
  1227. {state.searchStatus && (
  1228. <MyInput
  1229. class="queryCp"
  1230. v-model={state.queryStr}
  1231. height={42}
  1232. placeholder="请输入曲目关键词"
  1233. onKeyup={async (e: any) => {
  1234. if (e.code === "Enter" || e.key === "Enter") {
  1235. await handleGetList()
  1236. await handleGetDetail()
  1237. searchContent()
  1238. }
  1239. }}
  1240. onHandleQuery={async () => {
  1241. await handleGetList()
  1242. await handleGetDetail()
  1243. searchContent()
  1244. }}
  1245. clearable
  1246. />
  1247. )}
  1248. </div>
  1249. <div class={[styles.wrapList, !state.list.length && !loading.value && styles.wrapListEmpty]}>
  1250. {state.list.map((item: any, index: number) => (
  1251. <div
  1252. class={[styles.item, index === state.listActive && styles.active]}
  1253. onClick={async () => {
  1254. if (index === state.listActive) {
  1255. return
  1256. }
  1257. state.listActive = index
  1258. state.selectedPartIndex = 0
  1259. state.partXmlIndex = 0
  1260. await handleGetDetail()
  1261. searchContent()
  1262. }}
  1263. >
  1264. <div class={styles.itemInfo}>
  1265. <div class={styles.img}>
  1266. <NImage
  1267. lazy
  1268. objectFit="cover"
  1269. previewDisabled={true}
  1270. src={item.titleImg || icon_default}
  1271. onLoad={(e: any) => {
  1272. ;(e.target as any).dataset.loaded = "true"
  1273. }}
  1274. />
  1275. <PlayLoading
  1276. class={[state.listActive === index && state.playState === "play" ? "" : styles.showPlayLoading]}
  1277. />
  1278. </div>
  1279. <div class={styles.title}>
  1280. <div class={styles.titleName}>
  1281. <ellipsisScroll title={item.name} />
  1282. </div>
  1283. </div>
  1284. </div>
  1285. <div class={styles.btnSection}>
  1286. <div
  1287. class={styles.btn}
  1288. onClick={(e: any) => {
  1289. e.stopPropagation()
  1290. handlePlay(item)
  1291. if (state.listActive !== index) {
  1292. resetRender()
  1293. }
  1294. }}
  1295. >
  1296. {state.listActive === index && (
  1297. <>
  1298. {state.playState === "pause" ? "播放" : "暂停"}
  1299. <img src={state.playState === "pause" ? iconBtnPlay : (iconBtnPause as any)} />
  1300. </>
  1301. )}
  1302. {state.listActive !== index && (
  1303. <>
  1304. 播放
  1305. <img src={iconBtnPlay as any} />
  1306. </>
  1307. )}
  1308. </div>
  1309. </div>
  1310. </div>
  1311. ))}
  1312. {!state.list.length && !loading.value && (
  1313. <ElEmpty class={styles.empty} image={require("@/img/layout/empty.png")} description="暂无内容" />
  1314. )}
  1315. <div ref={spinRef} class={[styles.loadingWrap, state.finshed && styles.showLoading]}>
  1316. <NSpin show={true} stroke="#FF531C"></NSpin>
  1317. </div>
  1318. </div>
  1319. </div>
  1320. </div>
  1321. </div>
  1322. <div class={styles.rightContainer}>
  1323. {/* <i class={styles.leftArrow}></i> */}
  1324. <NSpin show={staffLoading.value} stroke="#FF531C">
  1325. {activeItem.value?.id ? (
  1326. state.musicPdfUrl ? (
  1327. <div class={[styles.staffImgs]}>
  1328. <iframe
  1329. style={{
  1330. // opacity: loading.value ? 0 : 1,
  1331. width: "100%",
  1332. height: "100%"
  1333. }}
  1334. src={state.iframeSrc}
  1335. onLoad={() => {
  1336. // 判断是用哪个渲染的
  1337. loading.value = false
  1338. staffLoading.value = false
  1339. }}
  1340. ></iframe>
  1341. </div>
  1342. ) : (
  1343. <>
  1344. <div class={styles.musicName}>
  1345. {activeItem.value.name}
  1346. {activeItem.value.musicSheetType === "CONCERT" &&
  1347. (state.selectedPartName || state.selectedTrack ? `(${state.selectedPartName || state.selectedTrack})` : "")}
  1348. </div>
  1349. <div class={[styles.staffImgs]}>
  1350. <iframe
  1351. id="staffIframeRef"
  1352. style={{
  1353. // opacity: loading.value ? 0 : 1,
  1354. width: "100%",
  1355. height: "100%"
  1356. }}
  1357. src={state.iframeSrc}
  1358. onLoad={musicIframeLoad}
  1359. ></iframe>
  1360. </div>
  1361. </>
  1362. )
  1363. ) : (
  1364. <div class={[styles.staffImgs, !loading.value && !activeItem.value?.id && styles.staffImgsEmpty]}>
  1365. {!loading.value && !activeItem.value?.id && (
  1366. <ElEmpty class={styles.empty} image={require("@/img/layout/empty.png")} description="暂无内容" />
  1367. )}
  1368. </div>
  1369. )}
  1370. </NSpin>
  1371. <img
  1372. style={{
  1373. display: activeItem.value?.id ? "" : "none"
  1374. }}
  1375. class={[styles.goBtn]}
  1376. src={btnSubmit as any}
  1377. onClick={() => {
  1378. handleChangeAudio("pause")
  1379. goToCloud(activeItem.value.id, state.partXmlIndex)
  1380. }}
  1381. />
  1382. <div class={styles.rightBtns} style={{ display: activeItem.value.id ? "" : "none" }}>
  1383. <div style={{ display: state.musicPdfUrl || state.imgs.length > 0 ? "" : "none" }}>
  1384. <NTooltip showArrow={false}>
  1385. {{
  1386. trigger: () => (
  1387. <img
  1388. onClick={onDownload}
  1389. class={[styles.transBtn, (downloadStatus.value || staffLoading.value) && styles.disableBtn]}
  1390. src={iconDownload as any}
  1391. />
  1392. ),
  1393. default: "下载曲谱"
  1394. }}
  1395. </NTooltip>
  1396. </div>
  1397. <div
  1398. style={{ display: activeItem.value.musicSheetType === "CONCERT" || state.details.musicSheetType === "CONCERT" ? "" : "none" }}
  1399. >
  1400. <NPopselect
  1401. options={partColumns.value}
  1402. placement="bottom-end"
  1403. trigger="click"
  1404. v-model:value={state.selectedPartIndex}
  1405. scrollable
  1406. onUpdate:value={async (value: any) => {
  1407. const item = partColumns.value.find((item: any) => item.value === value)
  1408. state.selectedPartIndex = value
  1409. state.selectedPartName = item.instrumentName
  1410. state.selectedTrack = item.track
  1411. state.partXmlIndex = item.xmlIndex
  1412. nextTick(() => {
  1413. let tempPdf = ""
  1414. if (activeItem.value?.isScoreRender && value === 999) {
  1415. if (activeItem.value?.musicPdfUrl) {
  1416. tempPdf = activeItem.value?.musicPdfUrl
  1417. }
  1418. } else {
  1419. tempPdf = item.musicPdfUrl
  1420. }
  1421. if (tempPdf) {
  1422. state.musicPdfUrl = tempPdf
  1423. staffLoading.value = true
  1424. renderStaff()
  1425. } else {
  1426. state.musicPdfUrl = ""
  1427. loading.value = true
  1428. // 为了处理,之前是使用pdf渲染,现在又用osmd,iframe没有重新加载
  1429. if (state.iframeSrc.indexOf("pdf/web") !== -1) {
  1430. renderStaff()
  1431. } else {
  1432. resetRender()
  1433. }
  1434. }
  1435. })
  1436. }}
  1437. class={["PopSelect", "PopSelectPart"]}
  1438. >
  1439. {{
  1440. empty: () => "暂无数据",
  1441. default: () => (
  1442. <NTooltip showArrow={false}>
  1443. {{
  1444. trigger: () => <img class={styles.transBtn} src={iconTransfer as any} />,
  1445. default: "切换声轨"
  1446. }}
  1447. </NTooltip>
  1448. )
  1449. }}
  1450. </NPopselect>
  1451. </div>
  1452. </div>
  1453. </div>
  1454. </div>
  1455. {/* </ElScrollbar> */}
  1456. {state.list.length !== 0 && activeItem.value.audioFileUrl && (
  1457. <PlayItem
  1458. show={state.showPlayer}
  1459. playState={state.playState}
  1460. songPrevNextStatus={songPrevNextStatus.value}
  1461. item={activeItem.value}
  1462. onChange={value => handleChangeAudio(value)}
  1463. onShow={(status: boolean) => {
  1464. state.showPlayer = status
  1465. }}
  1466. />
  1467. )}
  1468. <PracticeForm v-model={isPracticeShow.value} practiceUrl={practiceUrl.value} onClose={handlePracticeClose} />
  1469. </NavContainer>
  1470. )
  1471. }
  1472. })