music-list.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. import OEmpty from '@/components/o-empty'
  2. import { postMessage } from '@/helpers/native-message'
  3. import request from '@/helpers/request'
  4. import { state } from '@/state'
  5. import OFullRefresh from '@/components/o-full-refresh'
  6. import {
  7. Cell,
  8. CellGroup,
  9. DropdownItem,
  10. DropdownMenu,
  11. Icon,
  12. List,
  13. Picker,
  14. Popup,
  15. Toast,
  16. closeToast,
  17. showLoadingToast
  18. } from 'vant'
  19. import { defineComponent, reactive, ref, onMounted, nextTick, computed } from 'vue'
  20. import { useRoute, useRouter } from 'vue-router'
  21. import { getImage } from './images'
  22. import styles from './index.module.less'
  23. import OSticky from '@/components/o-sticky'
  24. import OSearch from '@/components/o-search'
  25. import OHeader from '@/components/o-header'
  26. import { getInstrumentName } from '@/constant/instruments'
  27. import { browser } from '@/helpers/utils'
  28. export default defineComponent({
  29. name: 'accompany-music-list',
  30. props: {
  31. musicTree: {
  32. type: Array,
  33. default: () => []
  34. }
  35. },
  36. setup() {
  37. const route = useRoute()
  38. const router = useRouter()
  39. const imgDefault = getImage('icon-music.svg')
  40. const userInfo = ref<any>({})
  41. const subjectKey = state.user?.data?.phone || 'accompany-music-list-subject'
  42. const subjectId =
  43. localStorage.getItem(subjectKey) || state.user?.data?.subjectId?.split(',')?.[0] || ''
  44. const data = reactive({
  45. loading: false,
  46. firstRender: false,
  47. finished: false,
  48. refreshing: false,
  49. musicTree: [] as any,
  50. pagenation: {
  51. page: 1,
  52. rows: 20
  53. },
  54. value1: null,
  55. value2: null,
  56. PopoverOpen: false,
  57. list: [] as any,
  58. keyword: '',
  59. musicSubject: subjectId,
  60. subjectList: [] as any
  61. })
  62. const getTree = async () => {
  63. try {
  64. // const res: any = await request.get(
  65. // state.platformApi + '/musicSheetCategories/queryTree?enable=true&parentId=' + route.query.categorieid
  66. // )
  67. const res: any = await request.post(
  68. state.platformApi + '/musicSheetCategories/queryTreeByParentId', {
  69. data: {
  70. parentId: route.query.categorieid,
  71. enable: true
  72. }
  73. }
  74. )
  75. if (Array.isArray(res?.data)) {
  76. data.musicTree = res.data
  77. }
  78. nextTick(() => {
  79. getList()
  80. })
  81. } catch (error) {
  82. console.log(error)
  83. }
  84. }
  85. // 获取声部信息
  86. const getSubjects = async () => {
  87. try {
  88. const subjects = await request.get(state.platformApi + '/subject/musicList', {
  89. params: {
  90. enableFlag: true,
  91. page: 1,
  92. rows: 100
  93. }
  94. })
  95. const rows = subjects.data || []
  96. rows.forEach((item: any) => {
  97. data.subjectList.push({
  98. text: item.name,
  99. value: item.id + ''
  100. })
  101. })
  102. } catch {
  103. //
  104. }
  105. }
  106. /**获取会员购买记录 */
  107. const getUserInfo = async () => {
  108. try {
  109. const res: any = await request.get(`/api-student/student/member`)
  110. userInfo.value = res.data || {}
  111. } catch (error) {
  112. console.log(error)
  113. }
  114. }
  115. const option1 = computed(() => {
  116. const v1: any = data.musicTree
  117. //.find((n: any) => n.id == route.query.categorieid)
  118. if (Array.isArray(v1)) {
  119. const list = v1.map((m: any) => {
  120. if (!data.value1) {
  121. data.value1 = m.id
  122. data.value2 = null
  123. }
  124. return {
  125. text: m.name,
  126. value: m.id
  127. }
  128. })
  129. return list
  130. }
  131. return []
  132. })
  133. const option2 = computed(() => {
  134. const v1: any = data.musicTree
  135. // .find((n: any) => n.id == route.query.categorieid)
  136. console.log(v1, '---')
  137. if (Array.isArray(v1)) {
  138. const v2: any = v1?.find((n: any) => n.id == data.value1)
  139. if (Array.isArray(v2?.musicSheetCategoriesList)) {
  140. const list = [{ text: '全部', value: null }].concat(
  141. v2.musicSheetCategoriesList.map((m: any) => {
  142. return {
  143. text: m.name,
  144. value: m.id
  145. }
  146. })
  147. )
  148. return list
  149. }
  150. }
  151. return [{ text: '全部', value: null }]
  152. })
  153. const getList = async () => {
  154. if (data.loading) return
  155. data.loading = true
  156. const bodyData: any = {
  157. ...data.pagenation,
  158. keyword: data.keyword,
  159. musicSheetCategoriesId: data.value2 || data.value1,
  160. status: 1
  161. }
  162. if (state.platformType == 'TEACHER') {
  163. bodyData.musicSubject = data.musicSubject
  164. }
  165. try {
  166. const res: any = await request.post(state.platformApi + '/musicSheet/page', {
  167. data: bodyData,
  168. hideLoading: true
  169. })
  170. if (Array.isArray(res?.data?.rows)) {
  171. data.list = [].concat(data.list, res.data.rows)
  172. data.pagenation.page += 1
  173. data.finished = res.data.rows.length < data.pagenation.rows ? true : false
  174. } else {
  175. data.finished = true
  176. }
  177. } catch (error) {
  178. data.finished = true
  179. }
  180. data.loading = false
  181. data.refreshing = false
  182. data.firstRender = true
  183. }
  184. // 重置搜索
  185. const onSearch = () => {
  186. data.pagenation.page = 1
  187. data.list = []
  188. data.finished = false
  189. data.list = []
  190. getList()
  191. }
  192. //进入云练习
  193. const openView = async (item: any) => {
  194. const src = `${location.origin}/orchestra-music-score/?id=${item.id}&part-index=${staffData.partIndex}`
  195. console.log('🚀 ~ src:', src)
  196. postMessage({
  197. api: 'openAccompanyWebView',
  198. content: {
  199. url: src,
  200. orientation: 0,
  201. isHideTitle: true,
  202. statusBarTextColor: false,
  203. isOpenLight: true
  204. }
  205. })
  206. }
  207. onMounted(() => {
  208. if (state.platformType == 'STUDENT') {
  209. getUserInfo()
  210. }
  211. if (state.platformType == 'TEACHER') {
  212. getSubjects()
  213. }
  214. getTree()
  215. })
  216. const staffData = reactive({
  217. open: false,
  218. musicXml: {} as any,
  219. instrumentName: '',
  220. partIndex: 0,
  221. partList: [] as any[]
  222. })
  223. const getPartNames = async (item: any) => {
  224. let partNames: any[] = []
  225. // showLoadingToast('加载中...')
  226. // try {
  227. // const res = await fetch(xmlUrl).then((response) => response.text())
  228. // const xml = new DOMParser().parseFromString(res, 'text/xml')
  229. // let parts = Array.from(xml.getElementsByTagName('part-name'))
  230. // parts = parts.filter((n) => !n.textContent?.toLocaleUpperCase()?.includes('COMMON'))
  231. // for (let i = 0; i < parts.length; i++) {
  232. // const name = getInstrumentName(parts[i].textContent || '')
  233. // partNames.push({ text: name ? parts[i].textContent + `(${name})` : parts[i].textContent || '', value: i })
  234. // }
  235. // closeToast()
  236. // } catch (error) {
  237. // console.log(error)
  238. // }
  239. try {
  240. const { data } = await request.get(state.platformApi + '/musicSheet/detail/' + item.id)
  241. let partList = data.background || []
  242. partList = partList.filter(
  243. (item: any) => !item.track?.toLocaleUpperCase()?.includes('COMMON')
  244. )
  245. partNames = partList.map((item: any, index: number) => {
  246. const instrumentName = getInstrumentName(item.track)
  247. return {
  248. text: item.track + (instrumentName ? `(${instrumentName})` : ''),
  249. value: index
  250. }
  251. })
  252. } catch (error) {
  253. console.log(error)
  254. }
  255. console.log('🚀 ~ partNames:', partNames)
  256. return partNames
  257. }
  258. const openMutilPart = async (item: any) => {
  259. if (staffData.musicXml[item.id]) {
  260. staffData.instrumentName = item.id
  261. staffData.open = true
  262. return Promise.resolve()
  263. }
  264. staffData.musicXml[item.id] = await getPartNames(item)
  265. staffData.instrumentName = item.id
  266. staffData.open = true
  267. }
  268. return () => (
  269. <div class={styles['accompany-music-list']}>
  270. <OSticky
  271. mode="sticky"
  272. class={styles.heade}
  273. onGetHeight={(height: number) => {
  274. document.documentElement.style.setProperty('--header-height', height + 'px')
  275. }}
  276. >
  277. <OHeader border={false} />
  278. <div>
  279. <DropdownMenu activeColor="var(--van-primary)">
  280. <DropdownItem
  281. v-model:modelValue={data.value1}
  282. options={option1.value}
  283. onChange={() => {
  284. data.value2 = null
  285. onSearch()
  286. }}
  287. ></DropdownItem>
  288. <DropdownItem
  289. v-model:modelValue={data.value2}
  290. options={option2.value as any}
  291. onChange={() => onSearch()}
  292. ></DropdownItem>
  293. </DropdownMenu>
  294. <div class={styles.filter}>
  295. <OSearch
  296. class={styles.filterBox}
  297. onSearch={(keyword: string) => {
  298. data.keyword = keyword
  299. onSearch()
  300. }}
  301. >
  302. {{
  303. left: () => (
  304. <>
  305. {state.platformType == 'TEACHER' ? (
  306. <DropdownMenu activeColor="var(--van-primary)">
  307. <DropdownItem
  308. v-model:modelValue={data.musicSubject}
  309. options={data.subjectList}
  310. onChange={() => {
  311. localStorage.setItem(subjectKey, data.musicSubject)
  312. onSearch()
  313. }}
  314. ></DropdownItem>
  315. </DropdownMenu>
  316. ) : null}
  317. </>
  318. )
  319. }}
  320. </OSearch>
  321. </div>
  322. </div>
  323. </OSticky>
  324. <OFullRefresh
  325. v-model:modelValue={data.refreshing}
  326. onRefresh={onSearch}
  327. style="min-height: calc(100vh - var(--header-height))"
  328. >
  329. <List
  330. loading-text=" "
  331. immediateCheck={false}
  332. loading={data.loading}
  333. v-model:finished={data.finished}
  334. finishedText=" "
  335. onLoad={() => {
  336. getList()
  337. }}
  338. >
  339. <CellGroup inset>
  340. {data.list.map((item: any) => {
  341. return (
  342. <Cell
  343. size="large"
  344. center
  345. title={item.musicSheetName}
  346. isLink
  347. onClick={() => {
  348. if (browser().isApp) {
  349. const url = `${location.origin}${location.pathname}#/musicDetail?id=${item.id}`
  350. postMessage({
  351. api: 'openWebView',
  352. content: {
  353. url,
  354. orientation: 1,
  355. isHideTitle: true,
  356. statusBarTextColor: false,
  357. isOpenLight: false
  358. }
  359. })
  360. } else {
  361. router.push({
  362. path: '/musicDetail',
  363. query: {
  364. id: item.id
  365. }
  366. })
  367. }
  368. }}
  369. >
  370. {{
  371. icon: () => (
  372. <Icon style={{ marginRight: '12px' }} size={40} name={imgDefault} />
  373. )
  374. }}
  375. </Cell>
  376. )
  377. })}
  378. </CellGroup>
  379. <div style={{ height: '40px' }}></div>
  380. </List>
  381. {data.firstRender && !data.loading && !data.list.length && <OEmpty tips="暂无曲谱" />}
  382. </OFullRefresh>
  383. <Popup teleport="body" position="bottom" round v-model:show={staffData.open}>
  384. <Picker
  385. columns={staffData.musicXml[staffData.instrumentName]}
  386. onConfirm={(value) => {
  387. staffData.open = false
  388. staffData.partIndex = value.selectedValues[0]
  389. openView({ id: staffData.instrumentName })
  390. }}
  391. onCancel={() => (staffData.open = false)}
  392. />
  393. </Popup>
  394. </div>
  395. )
  396. }
  397. })