cloudPractice.tsx 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128
  1. import { computed, defineComponent, nextTick, onMounted, reactive, ref, shallowRef } from "vue"
  2. import styles from "./index.module.scss"
  3. import NavContainer from "@/businessComponents/navContainer"
  4. import { ElEmpty, 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 PlayLoading from "./component/play-loading"
  9. import PlayItem from "./component/play-item"
  10. import icon_default from "../../img/cloudPractice/icon_default.png"
  11. import iconBtnPause from "../../img/cloudPractice/icon-btn-pause.png"
  12. import iconBtnPlay from "../../img/cloudPractice/icon-btn-play.png"
  13. import btnSubmit from "../../img/cloudPractice/btn-submit.png"
  14. import iconTransfer from "../../img/cloudPractice/icon-transfer.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. } from "@/api/cloudPractice.api"
  28. // import { getToken } from "@/libs/auth"
  29. // import { URL_TEACH_GYM } from "@/config"
  30. import axios from "axios"
  31. import { getInstrumentName } from "@/libs/instruments"
  32. import { formatXML, getCustomInfo, onlyVisible } from "./instrument"
  33. import { useFunction } from "./useData"
  34. import userStore from "@/store/modules/user"
  35. import PlayLoading from "./component/play-loading"
  36. export default defineComponent({
  37. name: "cloudPractice",
  38. setup() {
  39. const userStoreHook = userStore()
  40. const { goToCloud } = useFunction()
  41. const navs = [
  42. {
  43. name: "主页",
  44. url: "/"
  45. },
  46. {
  47. name: "云练习"
  48. }
  49. ]
  50. const spinRef = ref()
  51. const state = reactive({
  52. finshed: false,
  53. reshing: false,
  54. page: 1,
  55. rows: 20,
  56. iframeSrc: "",
  57. listActive: 0, // 当前选中的对象
  58. firstTreeId: null as any, // 左侧
  59. categoryId: null as any, // 类型
  60. categoryName: "" as any, // 类型名称
  61. categoryList: [] as any[],
  62. levelList: [] as any[], // 级别
  63. levelId: null as any,
  64. typeList: [] as any[], // 类型
  65. typeId: -1 as any,
  66. subjectList: [] as any[], // 声部列表
  67. subjectId: -1 as any,
  68. list: [] as any[],
  69. searchStatus: false,
  70. queryStr: "", // 搜索条件
  71. partList: [] as any[],
  72. partNames: [] as any[],
  73. selectedPartName: "" as any,
  74. selectedPartIndex: 0,
  75. partXmlIndex: 0,
  76. categoryShow: false, // 是否展开
  77. playState: "pause" as "play" | "pause", // 播放状态
  78. showPlayer: false // 是否显示播放器
  79. })
  80. const partColumns = ref<any>([])
  81. /** 选中的item */
  82. const activeItem = computed(() => {
  83. const list = state.list[state.listActive] || {}
  84. let tempList: any = {}
  85. if (userStoreHook.roles === "GYM") {
  86. const item = list.background?.[0]
  87. const audioFileUrl = item?.musicSheetType === "CONCERT" ? item?.metronomeUrl : item?.metronomeMp3Url || item?.mp3Url
  88. tempList = {
  89. id: item?.id,
  90. name: item?.examSongName,
  91. background: list?.background,
  92. xmlUrl: item?.xmlUrl,
  93. musicSheetType: item?.musicSheetType,
  94. audioFileUrl,
  95. // titleImg: list?.titleImg,
  96. isComberRender: item?.isScoreRender
  97. }
  98. } else if (userStoreHook.roles === "GYT") {
  99. tempList = {
  100. id: list?.id,
  101. name: list?.musicSheetName,
  102. background: list?.background,
  103. xmlUrl: list?.xmlFileUrl,
  104. musicSheetType: list?.musicSheetType,
  105. audioFileUrl: list?.audioFileUrl,
  106. titleImg: list?.titleImg,
  107. isComberRender: list?.musicSubjectId === "1"
  108. }
  109. } else if (userStoreHook.roles === "KLX") {
  110. const item: any = list.background?.[0]
  111. tempList = {
  112. id: list?.id,
  113. name: list?.musicSheetName,
  114. background: list?.background,
  115. xmlUrl: list?.xmlFileUrl,
  116. musicSheetType: list?.musicSheetType,
  117. audioFileUrl: item?.audioFileUrl,
  118. titleImg: list?.titleImg,
  119. isComberRender: false
  120. }
  121. }
  122. return tempList
  123. })
  124. const songPrevNextStatus = computed(() => {
  125. let prev = true,
  126. next = true
  127. if (state.listActive === 0) {
  128. prev = false
  129. }
  130. if (state.listActive >= state.list.length - 1) {
  131. next = false
  132. }
  133. return {
  134. prev,
  135. next
  136. }
  137. })
  138. const loading = ref(false)
  139. const staffLoading = ref(false)
  140. const storeData = shallowRef<any[]>([])
  141. const handleSearchList_gym = async () => {
  142. loading.value = true
  143. await httpAjaxErrMsg(queryTree_gym).then(res => {
  144. loading.value = false
  145. if (res.code === 200) {
  146. storeData.value = res.data || []
  147. setDefaultData()
  148. }
  149. })
  150. }
  151. const handleGetSubject_gym = async () => {
  152. loading.value = true
  153. await httpAjaxErrMsg(querySubjectIds_gym, { categoriesId: state.categoryId || state.firstTreeId }).then(res => {
  154. loading.value = false
  155. if (res.code === 200) {
  156. const result = res.data || []
  157. state.subjectList = result.map((item: any) => {
  158. return {
  159. label: item.name,
  160. value: item.id
  161. }
  162. })
  163. state.subjectList.unshift({
  164. label: "全部声部",
  165. value: -1
  166. })
  167. const userSubjectId = userStoreHook.userInfo.subjectId
  168. if (userSubjectId) {
  169. const tempSubjectId = userSubjectId.split(",")[0]
  170. state.subjectList.forEach((item: any) => {
  171. // 判断是否存在声部编号
  172. if (item.value === Number(tempSubjectId)) {
  173. state.subjectId = Number(tempSubjectId)
  174. }
  175. })
  176. }
  177. }
  178. })
  179. }
  180. const handleGetList_gym = async () => {
  181. loading.value = true
  182. const params = {
  183. page: state.page,
  184. rows: state.rows,
  185. subjectId: state.subjectId === -1 ? null : state.subjectId,
  186. categoriesId: state.typeId === -1 ? state.levelId : state.typeId,
  187. search: state.queryStr
  188. }
  189. await httpAjaxErrMsg(queryPage2_gym, params).then(res => {
  190. loading.value = false
  191. if (res.code === 200) {
  192. const result = res.data || []
  193. if (state.reshing) {
  194. state.list = []
  195. state.reshing = false
  196. }
  197. if (Array.isArray(result.rows)) {
  198. state.list = [...state.list, ...result.rows]
  199. state.finshed = state.page >= result.totalPage
  200. } else {
  201. state.finshed = true
  202. }
  203. }
  204. })
  205. }
  206. /** 管乐团数据查询 */
  207. const handleSearchList_gyt = async () => {
  208. loading.value = true
  209. await httpAjaxErrMsg(queryTree_gyt, {
  210. enable: true,
  211. page: 1,
  212. parentId: 0,
  213. rows: 10
  214. }).then(res => {
  215. loading.value = false
  216. if (res.code === 200) {
  217. storeData.value = res.data || []
  218. setDefaultData()
  219. }
  220. })
  221. }
  222. const handleGetSubject_gyt = async () => {
  223. loading.value = true
  224. await httpAjaxErrMsg(querySubjectIds_gyt, {
  225. enableFlag: true,
  226. page: 1,
  227. rows: 100
  228. }).then(res => {
  229. loading.value = false
  230. if (res.code === 200) {
  231. const result = res.data || []
  232. state.subjectList = result.map((item: any) => {
  233. return {
  234. label: item.name,
  235. value: item.id
  236. }
  237. })
  238. state.subjectList.unshift({
  239. label: "全部声部",
  240. value: -1
  241. })
  242. const userSubjectId = userStoreHook.userInfo.subjectId
  243. if (userSubjectId) {
  244. const tempSubjectId = userSubjectId.split(",")[0]
  245. state.subjectList.forEach((item: any) => {
  246. // 判断是否存在声部编号
  247. if (item.value === Number(tempSubjectId)) {
  248. state.subjectId = Number(tempSubjectId)
  249. }
  250. })
  251. }
  252. }
  253. })
  254. }
  255. const handleGetList_gyt = async () => {
  256. loading.value = true
  257. const params = {
  258. page: state.page,
  259. rows: state.rows,
  260. musicSubject: state.subjectId === -1 ? null : state.subjectId,
  261. musicSheetCategoriesId: state.typeId === -1 ? state.levelId : state.typeId,
  262. keyword: state.queryStr,
  263. detailFlag: true,
  264. status: 1
  265. }
  266. await httpAjaxErrMsg(queryPage2_gyt, params).then(res => {
  267. loading.value = false
  268. if (res.code === 200) {
  269. const result = res.data || {}
  270. if (state.reshing) {
  271. state.list = []
  272. state.reshing = false
  273. }
  274. if (Array.isArray(result.rows)) {
  275. result.rows.forEach((item: any) => {
  276. item.name = item.musicSheetName
  277. })
  278. state.list = [...state.list, ...result.rows]
  279. state.finshed = state.page >= result.pages
  280. } else {
  281. state.finshed = true
  282. }
  283. }
  284. })
  285. }
  286. /** 酷乐秀机构数据查询 */
  287. const handleSearchList_klx = async () => {
  288. loading.value = true
  289. await httpAjaxErrMsg(queryTree_klx, {
  290. enable: true,
  291. page: 1,
  292. parentId: 0,
  293. rows: 10
  294. })
  295. .then(res => {
  296. loading.value = false
  297. if (res.code === 200) {
  298. const result = res.data || []
  299. const tempList: any = []
  300. result.forEach((item: any) => {
  301. if (item.musicNum > 0) {
  302. const subjectCounts = item.subjectCounts ? true : false
  303. const musicCounts = item.musicCounts ? true : false
  304. const ensembleCounts = item.ensembleCounts ? true : false
  305. const list: any = []
  306. if (subjectCounts) {
  307. list.push({
  308. label: "基础云练",
  309. value: "SUBJECT"
  310. })
  311. }
  312. if (musicCounts) {
  313. list.push({
  314. label: "独奏云练",
  315. value: "MUSIC"
  316. })
  317. }
  318. if (ensembleCounts) {
  319. list.push({
  320. label: "合奏云练",
  321. value: "ENSEMBLE"
  322. })
  323. }
  324. tempList.push({
  325. value: item.id,
  326. label: item.name,
  327. musicSheetCategoriesList: list
  328. })
  329. }
  330. })
  331. state.categoryList = tempList
  332. setDefaultData()
  333. }
  334. })
  335. .catch(() => {
  336. state.finshed = true
  337. })
  338. }
  339. const handleGetSubject_klx = async () => {
  340. loading.value = true
  341. await httpAjaxErrMsg(querySubjectIds_klx, {
  342. queryType: "list",
  343. page: 1,
  344. rows: 100
  345. }).then(res => {
  346. loading.value = false
  347. if (res.code === 200) {
  348. const result = res.data?.rows || []
  349. state.subjectList = result.map((item: any) => {
  350. return {
  351. label: item.name,
  352. value: item.id
  353. }
  354. })
  355. state.subjectList.unshift({
  356. label: "全部声部",
  357. value: -1
  358. })
  359. const userSubjectId = userStoreHook.userInfo.subjectId
  360. if (userSubjectId) {
  361. const tempSubjectId = userSubjectId.split(",")[0]
  362. state.subjectList.forEach((item: any) => {
  363. // 判断是否存在声部编号
  364. if (item.value === Number(tempSubjectId)) {
  365. state.subjectId = Number(tempSubjectId)
  366. }
  367. })
  368. }
  369. }
  370. })
  371. }
  372. const handleGetList_klx = async () => {
  373. if (!state.categoryId) return
  374. loading.value = true
  375. const params = {
  376. page: state.page,
  377. rows: state.rows,
  378. albumId: state.categoryId,
  379. subjectId: state.subjectId === -1 ? null : state.subjectId,
  380. subjectType: state.firstTreeId,
  381. level: state.levelId === -1 ? null : state.levelId,
  382. type: state.typeId === -1 ? null : state.typeId,
  383. keyword: state.queryStr
  384. }
  385. await httpAjaxErrMsg(queryPage2_klx, params).then(res => {
  386. loading.value = false
  387. if (res.code === 200) {
  388. const result = res.data || {}
  389. if (state.reshing) {
  390. state.list = []
  391. state.reshing = false
  392. }
  393. if (Array.isArray(result.rows)) {
  394. result.rows.forEach((item: any) => {
  395. item.name = item.musicSheetName
  396. })
  397. state.list = [...state.list, ...result.rows]
  398. state.finshed = state.page >= result.totalPage
  399. } else {
  400. state.finshed = true
  401. }
  402. }
  403. })
  404. }
  405. const handleSelectCondition_klx = async () => {
  406. if (!state.categoryId || !state.firstTreeId) return
  407. loading.value = true
  408. const params = {
  409. tenantAlbumId: state.categoryId,
  410. subjectType: state.firstTreeId
  411. }
  412. await httpAjaxErrMsg(selectCondition_klx, params).then(res => {
  413. loading.value = false
  414. if (res.code === 200) {
  415. const result = res.data || {}
  416. if (result.levelList && result.levelList.length > 0) {
  417. state.levelList = result.levelList.map((item: any) => {
  418. return {
  419. label: item.value,
  420. value: item.id
  421. }
  422. })
  423. state.levelList.unshift({
  424. label: "全部级别",
  425. value: -1
  426. })
  427. state.levelId = -1
  428. } else {
  429. state.levelList = []
  430. }
  431. if (result.typeList && result.typeList.length > 0) {
  432. state.typeList = result.typeList.map((item: any) => {
  433. return {
  434. label: item.value,
  435. value: item.id
  436. }
  437. })
  438. state.typeList.unshift({
  439. label: "全部类型",
  440. value: -1
  441. })
  442. state.typeId = -1
  443. } else {
  444. state.typeList = []
  445. }
  446. }
  447. })
  448. }
  449. /** 条件查询 */
  450. const handleAllSearchList = async () => {
  451. // GYM,GYT,KLX 区分 查询搜索条件数据
  452. if (userStoreHook.roles === "GYM") {
  453. await handleSearchList_gym()
  454. } else if (userStoreHook.roles === "GYT") {
  455. await handleSearchList_gyt()
  456. } else if (userStoreHook.roles === "KLX") {
  457. await handleSearchList_klx()
  458. }
  459. }
  460. const handleAllGetSubject = async () => {
  461. // GYM,GYT,KLX 区分 查询声部数据
  462. if (userStoreHook.roles === "GYM") {
  463. await handleGetSubject_gym()
  464. } else if (userStoreHook.roles === "GYT") {
  465. await handleGetSubject_gyt()
  466. } else if (userStoreHook.roles === "KLX") {
  467. await handleGetSubject_klx()
  468. }
  469. }
  470. const handleAllGetList = async () => {
  471. // GYM,GYT,KLX 区分 查询声部数据·
  472. if (userStoreHook.roles === "GYM") {
  473. await handleGetList_gym()
  474. } else if (userStoreHook.roles === "GYT") {
  475. await handleGetList_gyt()
  476. } else if (userStoreHook.roles === "KLX") {
  477. await handleGetList_klx()
  478. }
  479. }
  480. /** 初始化数据 */
  481. const setDefaultData = async (type?: "first" | "category" | "level" | "type") => {
  482. if (userStoreHook.roles === "GYM") {
  483. await initCategories_gym(type)
  484. } else if (userStoreHook.roles === "GYT") {
  485. initCategories_gyt(type)
  486. } else if (userStoreHook.roles === "KLX") {
  487. await initCategories_klx(type)
  488. }
  489. }
  490. const initCategories_gym = async (type?: "first" | "category" | "level" | "type") => {
  491. if (storeData.value.length > 0 && !["category", "level", "type"].includes(type as any)) {
  492. let result: any = []
  493. if (type === "first" && state.firstTreeId) {
  494. result = storeData.value.find((item: any) => item.id === state.firstTreeId)?.sysMusicScoreCategoriesList || []
  495. } else {
  496. state.firstTreeId = storeData.value[0]?.id
  497. result = storeData.value[0]?.sysMusicScoreCategoriesList || []
  498. }
  499. state.categoryList = result.map((item: any) => {
  500. return {
  501. label: item.name,
  502. value: item.id,
  503. sysMusicScoreCategoriesList: item.sysMusicScoreCategoriesList || []
  504. }
  505. })
  506. state.categoryId = null
  507. state.categoryName = null
  508. state.levelId = null
  509. state.typeId = -1
  510. }
  511. if (state.categoryList.length > 0 && !["level", "type"].includes(type as any)) {
  512. let result: any = []
  513. if (type === "category" && state.categoryId) {
  514. result = state.categoryList.find((item: any) => item.value === state.categoryId)?.sysMusicScoreCategoriesList || []
  515. } else {
  516. state.categoryId = state.categoryList[0]?.value
  517. state.categoryName = state.categoryList[0]?.label
  518. result = state.categoryList[0]?.sysMusicScoreCategoriesList || []
  519. }
  520. state.levelList = result.map((item: any) => {
  521. return {
  522. label: item.name,
  523. value: item.id,
  524. sysMusicScoreCategoriesList: item.sysMusicScoreCategoriesList || []
  525. }
  526. })
  527. await handleGetSubject_gym()
  528. }
  529. if (state.levelList.length > 0) {
  530. let result: any = []
  531. if (type === "level" && state.levelId) {
  532. result = state.levelList.find((item: any) => item.value === state.levelId)?.sysMusicScoreCategoriesList
  533. } else {
  534. state.levelId = state.levelList[0]?.value
  535. result = state.levelList[0]?.sysMusicScoreCategoriesList || []
  536. }
  537. state.typeList = result.map((item: any) => {
  538. return {
  539. label: item.name,
  540. value: item.id
  541. }
  542. })
  543. state.typeList.unshift({
  544. label: "全部",
  545. value: -1
  546. })
  547. }
  548. }
  549. const initCategories_gyt = (type?: "first" | "category" | "level" | "type") => {
  550. if (storeData.value.length > 0 && !["level", "type"].includes(type as any)) {
  551. let result: any = []
  552. if (type === "first" && state.firstTreeId) {
  553. result = storeData.value.find((item: any) => item.id === state.firstTreeId)?.musicSheetCategoriesList || []
  554. } else {
  555. state.firstTreeId = storeData.value[0]?.id
  556. result = storeData.value[0]?.musicSheetCategoriesList || []
  557. }
  558. state.levelList = result.map((item: any) => {
  559. return {
  560. label: item.name,
  561. value: item.id,
  562. musicSheetCategoriesList: item.musicSheetCategoriesList || []
  563. }
  564. })
  565. state.levelId = null
  566. state.typeId = -1
  567. }
  568. if (state.levelList.length > 0) {
  569. let result: any = []
  570. if (type === "level" && state.levelId) {
  571. result = state.levelList.find((item: any) => item.value === state.levelId)?.musicSheetCategoriesList
  572. } else {
  573. state.levelId = state.levelList[0]?.value
  574. result = state.levelList[0]?.musicSheetCategoriesList || []
  575. }
  576. state.typeList = result.map((item: any) => {
  577. return {
  578. label: item.name,
  579. value: item.id
  580. }
  581. })
  582. state.typeList.unshift({
  583. label: "全部",
  584. value: -1
  585. })
  586. }
  587. }
  588. const initCategories_klx = async (type?: "first" | "category" | "level" | "type") => {
  589. if (state.categoryList.length > 0 && !["level", "type", "first"].includes(type as any)) {
  590. let result: any = []
  591. if (type === "category" && state.categoryId) {
  592. result = state.categoryList.find((item: any) => item.value === state.categoryId)?.musicSheetCategoriesList || []
  593. } else {
  594. state.categoryId = state.categoryList[0]?.value
  595. state.categoryName = state.categoryList[0]?.label
  596. result = state.categoryList[0]?.musicSheetCategoriesList || []
  597. }
  598. storeData.value = result.map((item: any) => {
  599. return {
  600. id: item.value,
  601. name: item.label
  602. }
  603. })
  604. }
  605. if (storeData.value.length > 0 && !["level", "type"].includes(type as any)) {
  606. console.log(storeData.value.length, "storeData.value.length", type)
  607. if (type === "first" && state.firstTreeId) {
  608. await handleSelectCondition_klx()
  609. } else {
  610. //
  611. state.firstTreeId = storeData.value[0]?.id
  612. await handleSelectCondition_klx()
  613. }
  614. }
  615. }
  616. const __init = async () => {
  617. await handleAllSearchList()
  618. await handleAllGetSubject()
  619. await handleAllGetList()
  620. await toDetail()
  621. renderStaff()
  622. }
  623. __init()
  624. const handleResh = () => {
  625. if (loading.value || state.finshed) return
  626. state.page = state.page + 1
  627. handleAllGetList()
  628. }
  629. const handleGetList = async () => {
  630. if (loading.value) return
  631. state.listActive = 0
  632. state.showPlayer = false
  633. state.playState = "pause"
  634. state.partNames = []
  635. state.partList = []
  636. state.selectedPartName = ""
  637. state.selectedPartIndex = 0
  638. state.partXmlIndex = 0
  639. document.querySelector(".musicList-container")?.scroll(0, 0)
  640. state.page = 1
  641. state.finshed = false
  642. state.reshing = true
  643. state.list = []
  644. await handleAllGetList()
  645. }
  646. const toDetail = async () => {
  647. const row: any = activeItem.value
  648. if (row.musicSheetType === "SINGLE") {
  649. loading.value = false
  650. return
  651. }
  652. state.partNames = await getPartNames(row.xmlUrl)
  653. let partList = row.background || []
  654. partList = partList.filter((item: any) => !item.track?.toLocaleUpperCase()?.includes("COMMON"))
  655. partColumns.value = partList.map((item: any, index: number) => {
  656. const instrumentName = getInstrumentName(item.track)
  657. const xmlIndex = state.partNames.findIndex((name: any) => name === item.track)
  658. return {
  659. label: item.track + (instrumentName ? `(${instrumentName})` : ""),
  660. instrumentName: instrumentName,
  661. xmlIndex,
  662. value: index
  663. }
  664. })
  665. // 初始化数据
  666. const defaultShowStaff = partColumns.value[state.selectedPartIndex]
  667. console.log(defaultShowStaff, partList)
  668. state.selectedPartName = defaultShowStaff?.instrumentName
  669. state.partXmlIndex = defaultShowStaff?.xmlIndex
  670. }
  671. const getPartNames = async (xmlUrl: string) => {
  672. const partNames: string[] = []
  673. try {
  674. const res: any = await axios.get(xmlUrl)
  675. const xml: any = new DOMParser().parseFromString(res.data, "text/xml")
  676. for (const item of xml.getElementsByTagName("part-name")) {
  677. if (item.textContent) {
  678. partNames.push(item.textContent)
  679. }
  680. }
  681. } catch (error) {
  682. //
  683. }
  684. return partNames.filter((text: string) => text.toLocaleUpperCase() !== "COMMON") || []
  685. }
  686. const musicIframeLoad = async () => {
  687. const iframeRef: any = document.getElementById("staffIframeRef")
  688. if (iframeRef && iframeRef.contentWindow.renderXml) {
  689. staffLoading.value = true
  690. const res: any = await axios.get(activeItem.value.xmlUrl)
  691. const parseXmlInfo = getCustomInfo(res.data)
  692. const xml = formatXML(parseXmlInfo.parsedXML)
  693. if (activeItem.value.isComberRender) {
  694. iframeRef.contentWindow.renderXml(xml, state.partXmlIndex, activeItem.value.isComberRender)
  695. } else {
  696. const currentXml = onlyVisible(xml, state.partXmlIndex)
  697. iframeRef.contentWindow.renderXml(currentXml, state.partXmlIndex, activeItem.value.isComberRender)
  698. }
  699. }
  700. }
  701. const resetRender = async () => {
  702. const iframeRef: any = document.getElementById("staffIframeRef")
  703. if (iframeRef && iframeRef.contentWindow.renderXml) {
  704. staffLoading.value = true
  705. const res: any = await axios.get(activeItem.value.xmlUrl)
  706. const parseXmlInfo = getCustomInfo(res.data)
  707. const xml = formatXML(parseXmlInfo.parsedXML)
  708. if (activeItem.value.isComberRender) {
  709. iframeRef.contentWindow.renderXml(xml, state.partXmlIndex, activeItem.value.isComberRender)
  710. } else {
  711. console.log(state.partXmlIndex, " state.partXmlIndex")
  712. const currentXml = onlyVisible(xml, state.partXmlIndex)
  713. iframeRef.contentWindow.renderXml(currentXml, 0, activeItem.value.isComberRender)
  714. }
  715. }
  716. }
  717. const renderStaff = async () => {
  718. try {
  719. // ${location.origin}${location.pathname}
  720. state.iframeSrc = `/osmd/index.html`
  721. } catch (error) {
  722. //
  723. }
  724. }
  725. /** 音频控制 */
  726. const handleChangeAudio = (type: "play" | "pause" | "pre" | "next") => {
  727. if (type === "play") {
  728. state.playState = "play"
  729. } else if (type === "pause") {
  730. state.playState = "pause"
  731. } else if (type === "pre") {
  732. if (state.list[state.listActive - 1]) {
  733. handlePlay(state.list[state.listActive - 1])
  734. }
  735. } else if (type === "next") {
  736. if (state.list[state.listActive + 1]) {
  737. handlePlay(state.list[state.listActive + 1])
  738. }
  739. }
  740. }
  741. /** 播放曲目 */
  742. const handlePlay = (item: any) => {
  743. const index = state.list.findIndex((_item: any) => _item.id === item.id)
  744. if (index > -1) {
  745. if (state.listActive === index) {
  746. state.playState = state.playState === "play" ? "pause" : "play"
  747. } else {
  748. state.playState = "play"
  749. }
  750. state.showPlayer = true
  751. state.listActive = index
  752. }
  753. }
  754. const showLoading = async (e: any) => {
  755. if (e.data?.api === "musicStaffRender") {
  756. staffLoading.value = e.data.loading
  757. }
  758. }
  759. onMounted(() => {
  760. const obv = new IntersectionObserver(entries => {
  761. if (entries[0].intersectionRatio > 0) {
  762. handleResh()
  763. }
  764. })
  765. obv.observe(spinRef.value)
  766. window.addEventListener("message", showLoading)
  767. })
  768. return () => (
  769. <NavContainer navs={navs}>
  770. {/* <ElScrollbar class="elScrollbar"> */}
  771. <div class={styles.cloudPractice}>
  772. <div class={styles.leftContainer}>
  773. <div class={styles.details}>
  774. {storeData.value.length > 0 && (
  775. <ElScrollbar class={styles.leftSection}>
  776. {/* 基 础 云 练 */}
  777. {storeData.value.map((item: any) => (
  778. <div
  779. class={[styles.leftSection_item, item.id === state.firstTreeId && styles.leftSection_item__active]}
  780. onClick={async () => {
  781. if (loading.value) return
  782. state.firstTreeId = item.id
  783. await setDefaultData("first")
  784. await handleGetList()
  785. await toDetail()
  786. }}
  787. >
  788. {item.name}
  789. </div>
  790. ))}
  791. </ElScrollbar>
  792. )}
  793. <div class={[styles.musicList, "musicList-container"]}>
  794. <div class={styles.searchHeader}>
  795. {state.categoryList.length > 1 && (
  796. <div class={[styles.categorySection]}>
  797. <NPopselect
  798. placement="bottom-start"
  799. disabled={loading.value}
  800. options={state.categoryList}
  801. v-model:value={state.categoryId}
  802. onUpdate:value={async (val: any) => {
  803. const item = state.categoryList.find((item: any) => item.value === val)
  804. if (item) {
  805. state.categoryName = item.label
  806. state.categoryId = item.value
  807. await setDefaultData("category")
  808. await handleGetList()
  809. await toDetail()
  810. }
  811. }}
  812. onUpdate:show={(value: any) => {
  813. state.categoryShow = value
  814. }}
  815. trigger="click"
  816. class={"PopSelect"}
  817. >
  818. <span class={[styles.iconTagName, state.categoryShow && styles.show]}>
  819. <span>{state.categoryName}</span>
  820. </span>
  821. </NPopselect>
  822. </div>
  823. )}
  824. <div class={styles.searchMore}>
  825. <div class={styles.searchSection}>
  826. <Dictionary
  827. popperClass="classTypePopper"
  828. v-model={state.subjectId}
  829. height={42}
  830. // disabled={loading.value}
  831. options={state.subjectList}
  832. placeholder="全部声部"
  833. onChange={async () => {
  834. await handleGetList()
  835. await toDetail()
  836. }}
  837. />
  838. {state.levelList.length ? (
  839. <Dictionary
  840. popperClass="classTypePopper"
  841. v-model={state.levelId}
  842. height={42}
  843. // disabled={loading.value}
  844. options={state.levelList}
  845. placeholder="级别"
  846. onChange={async () => {
  847. setDefaultData("level")
  848. await handleGetList()
  849. await toDetail()
  850. }}
  851. />
  852. ) : null}
  853. {state.typeList.length > 0 ? (
  854. <Dictionary
  855. popperClass="classTypePopper"
  856. v-model={state.typeId}
  857. height={42}
  858. // disabled={loading.value}
  859. options={state.typeList}
  860. propsOpt={{
  861. labelField: "name",
  862. valueField: "id"
  863. }}
  864. placeholder="分类"
  865. onChange={async () => {
  866. await handleGetList()
  867. await toDetail()
  868. }}
  869. />
  870. ) : null}
  871. </div>
  872. <div
  873. class={[styles.btnSearch, state.searchStatus && styles.btnSearchActive]}
  874. onClick={() => (state.searchStatus = !state.searchStatus)}
  875. ></div>
  876. </div>
  877. {state.searchStatus && (
  878. <MyInput
  879. class="queryCp"
  880. v-model={state.queryStr}
  881. height={42}
  882. placeholder="请输入曲目关键词"
  883. onKeyup={async (e: any) => {
  884. if (e.code === "Enter" || e.key === "Enter") {
  885. await handleGetList()
  886. await toDetail()
  887. }
  888. }}
  889. onHandleQuery={async () => {
  890. await handleGetList()
  891. await toDetail()
  892. }}
  893. clearable
  894. />
  895. )}
  896. </div>
  897. <div class={[styles.wrapList, !state.list.length && !loading.value && styles.wrapListEmpty]}>
  898. {state.list.map((item: any, index: number) => (
  899. <div
  900. class={[styles.item, index === state.listActive && styles.active]}
  901. onClick={async () => {
  902. state.listActive = index
  903. await toDetail()
  904. resetRender()
  905. }}
  906. >
  907. <div class={styles.itemInfo}>
  908. <div class={styles.img}>
  909. <NImage
  910. lazy
  911. objectFit="cover"
  912. previewDisabled={true}
  913. src={item.titleImg || icon_default}
  914. onLoad={(e: any) => {
  915. ;(e.target as any).dataset.loaded = "true"
  916. }}
  917. />
  918. <PlayLoading
  919. class={[state.listActive === index && state.playState === "play" ? "" : styles.showPlayLoading]}
  920. />
  921. </div>
  922. <div class={styles.title}>
  923. <div class={styles.titleName}>
  924. <ellipsisScroll title={item.name} />
  925. </div>
  926. </div>
  927. </div>
  928. <div class={styles.btnSection}>
  929. <div
  930. class={styles.btn}
  931. onClick={(e: any) => {
  932. e.stopPropagation()
  933. handlePlay(item)
  934. if (state.listActive !== index) {
  935. resetRender()
  936. }
  937. }}
  938. >
  939. {state.listActive === index && (
  940. <>
  941. {state.playState === "pause" ? "播放" : "暂停"}
  942. <img src={state.playState === "pause" ? iconBtnPlay : (iconBtnPause as any)} />
  943. </>
  944. )}
  945. {state.listActive !== index && (
  946. <>
  947. 播放
  948. <img src={iconBtnPlay as any} />
  949. </>
  950. )}
  951. </div>
  952. </div>
  953. </div>
  954. ))}
  955. {!state.list.length && !loading.value && (
  956. <ElEmpty class={styles.empty} image={require("@/img/layout/empty.png")} description="暂无内容" />
  957. )}
  958. <div ref={spinRef} class={[styles.loadingWrap, state.finshed && styles.showLoading]}>
  959. <NSpin show={true} stroke="#FF531C"></NSpin>
  960. </div>
  961. </div>
  962. </div>
  963. </div>
  964. </div>
  965. <div class={styles.rightContainer}>
  966. {/* <i class={styles.leftArrow}></i> */}
  967. <NSpin show={staffLoading.value} stroke="#FF531C">
  968. <div class={styles.musicName}>
  969. {activeItem.value.name}
  970. {activeItem.value.musicSheetType === "CONCERT" && state.selectedPartName ? `(${state.selectedPartName})` : ""}
  971. </div>
  972. <div class={[styles.staffImgs, !loading.value && !activeItem.value?.id && styles.staffImgsEmpty]}>
  973. {state.iframeSrc && activeItem.value?.id && (
  974. <iframe
  975. id="staffIframeRef"
  976. style={{
  977. // opacity: loading.value ? 0 : 1,
  978. width: "100%",
  979. height: "100%"
  980. }}
  981. src={state.iframeSrc}
  982. onLoad={musicIframeLoad}
  983. ></iframe>
  984. )}
  985. {!loading.value && !activeItem.value?.id && (
  986. <ElEmpty class={styles.empty} image={require("@/img/layout/empty.png")} description="暂无内容" />
  987. )}
  988. </div>
  989. </NSpin>
  990. <img
  991. style={{
  992. display: activeItem.value?.id ? "" : "none"
  993. }}
  994. class={[styles.goBtn]}
  995. src={btnSubmit as any}
  996. onClick={() => {
  997. handleChangeAudio("pause")
  998. goToCloud(activeItem.value.id, state.partXmlIndex)
  999. }}
  1000. />
  1001. <div
  1002. class={styles.rightBtns}
  1003. style={{ display: activeItem.value.id && activeItem.value.musicSheetType === "CONCERT" ? "" : "none" }}
  1004. >
  1005. <NPopselect
  1006. options={partColumns.value}
  1007. placement="bottom-end"
  1008. trigger="click"
  1009. v-model:value={state.selectedPartIndex}
  1010. scrollable
  1011. onUpdate:value={async (value: any) => {
  1012. console.log(value, "value")
  1013. const item = partColumns.value.find((item: any) => item.value === value)
  1014. state.selectedPartIndex = value
  1015. state.selectedPartName = item.instrumentName
  1016. state.partXmlIndex = item.xmlIndex
  1017. nextTick(() => {
  1018. resetRender()
  1019. })
  1020. }}
  1021. class={["PopSelect", "PopSelectPart"]}
  1022. >
  1023. {{
  1024. empty: () => "暂无数据",
  1025. default: () => (
  1026. <NTooltip showArrow={false}>
  1027. {{ trigger: () => <img class={styles.transBtn} src={iconTransfer as any} />, default: "切换声轨" }}
  1028. </NTooltip>
  1029. )
  1030. }}
  1031. </NPopselect>
  1032. </div>
  1033. </div>
  1034. </div>
  1035. {/* </ElScrollbar> */}
  1036. {state.list.length !== 0 && activeItem.value.audioFileUrl && (
  1037. <PlayItem
  1038. show={state.showPlayer}
  1039. playState={state.playState}
  1040. songPrevNextStatus={songPrevNextStatus.value}
  1041. item={activeItem.value}
  1042. onChange={value => handleChangeAudio(value)}
  1043. onShow={(status: boolean) => {
  1044. state.showPlayer = status
  1045. }}
  1046. />
  1047. )}
  1048. </NavContainer>
  1049. )
  1050. }
  1051. })