index.tsx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. import {
  2. defineComponent,
  3. toRefs,
  4. reactive,
  5. onMounted,
  6. ref,
  7. watch,
  8. nextTick,
  9. onDeactivated
  10. } from 'vue'
  11. import classes from './index.module.less'
  12. import {
  13. ElButton,
  14. ElOption,
  15. ElSelect,
  16. ElScrollbar,
  17. ElMessageBox,
  18. ElCarousel,
  19. ElCarouselItem
  20. } from 'element-plus'
  21. import printIcon from './images/printIcon.png'
  22. import noMore from './images/noMore.png'
  23. import banner from './images/banner.png'
  24. import colVideo from '@/components/col-video/index'
  25. import iconClose from '@/views/login/images/icon_close.png'
  26. import start from '@/components/albumItem/images/start.png'
  27. import lineStart from '@/components/albumItem/images/lineStart.png'
  28. import oStart from '@/common/images/oStart.png'
  29. import oLineStart from '@/common/images/oLineStart.png'
  30. import teacher from '@/views/videoDetailList/images/teacher.png'
  31. import teacherHeader from '@/common/images/icon_teacher.png'
  32. import musiceIcon from '@/views/videoDetailList/images/musiceIcon.png'
  33. import music from '@/components/musicLIstItem/images/music.png'
  34. import tagItem from '@/components/tagItem'
  35. import code from '@/common/images/student_download.png'
  36. import musicItem from './modals/musicItem'
  37. import ablumIcon from '@/components/col-header/images/ablumIcon.png'
  38. import noAblumIcon from '@/components/col-header/images/noAblumIcon.png'
  39. import onlineIcon from '@/components/col-header/images/onlineIcon.png'
  40. import noOnlineIcon from '@/components/col-header/images/noOnlineIcon.png'
  41. import styleIcon from '@/components/col-header/images/styleIcon.png'
  42. import nostyleIcon from '@/components/col-header/images/noStyleIcon.png'
  43. import videoIcon from '@/components/col-header/images/videoIcon.png'
  44. import noVideoIcon from '@/components/col-header/images/noVideoIcon.png'
  45. import vipIcon from '@/components/col-header/images/vipIcon.png'
  46. import noVipIcon from '@/components/col-header/images/noVipIcon.png'
  47. import umiRequest from 'umi-request'
  48. import request from '@/helpers/request'
  49. import { useRoute, useRouter } from 'vue-router'
  50. import gou from './images/gou.png'
  51. import member from './images/member.png'
  52. import palyer from './images/palyer.png'
  53. import { extend } from 'umi-request'
  54. // import vipIcon from '../images/vipIcon.png'
  55. import {
  56. getUserType,
  57. vaildTeachingUrl,
  58. getAuth,
  59. setAuth
  60. } from '@/helpers/utils'
  61. import { state as states } from '@/state'
  62. import {
  63. imgToCanvas,
  64. addWatermark,
  65. convasToImg,
  66. addMusicTitle
  67. } from '@/helpers/imageFunction'
  68. import arrow from '@/views/home/images/moreArrow.png'
  69. import { Vue3Lottie } from 'vue3-lottie'
  70. import 'vue3-lottie/dist/style.css'
  71. import AstronautJSON from '@/common/animate/bigLoad.json'
  72. import printJS from 'print-js'
  73. const chargeTypes = {
  74. CHARGE: '点播',
  75. FREE: '免费',
  76. VIP: '会员'
  77. }
  78. const chargeImg = {
  79. CHARGE: palyer,
  80. FREE: gou,
  81. VIP: member
  82. }
  83. const chargeClass = {
  84. CHARGE: 'charge',
  85. FREE: 'gou',
  86. VIP: 'member'
  87. }
  88. export default defineComponent({
  89. name: 'muiscDetial',
  90. props: {
  91. title: {
  92. type: String,
  93. default: ''
  94. }
  95. },
  96. components: {
  97. colVideo,
  98. tagItem,
  99. musicItem,
  100. Vue3Lottie
  101. },
  102. setup(props, conent) {
  103. const route = useRoute()
  104. const router = useRouter()
  105. const state = reactive({
  106. title: props.title,
  107. id: route.query.id,
  108. subjectId: '',
  109. subjectList: [],
  110. tagList: [],
  111. teacherDetail: {} as any,
  112. musicDetail: {} as any,
  113. musicList: [],
  114. mp3Type: '',
  115. activeRow: {} as any,
  116. showCode: false,
  117. userType: '',
  118. accompanyUrl: '',
  119. imgData: '',
  120. imgUrl: '',
  121. activeIndex: 0,
  122. showImg: [] as any,
  123. iconList: [] as any,
  124. // ossUploadUrl: 'https://ks3-cn-beijing.ksyuncs.com/cloud-coach',
  125. ossUploadUrl: `https://cloud-coach.ks3-cn-beijing.ksyuncs.com/`,
  126. dataObj: {
  127. policy: '',
  128. signature: '',
  129. key: '',
  130. KSSAccessKeyId: '',
  131. acl: 'public-read',
  132. name: ''
  133. },
  134. fileList: [] as any
  135. })
  136. nextTick(() => {
  137. // 禁用右键
  138. // @ts-ignore:无法被执行的代码的错误
  139. document.oncontextmenu = new Function('event.returnValue=false')
  140. // @ts-ignore:无法被执行的代码的错误
  141. // 禁用选择
  142. document.onselectstart = new Function('event.returnValue=false')
  143. // @ts-ignore:无法被执行的代码的错误
  144. //禁止f12
  145. document.οnkeydοwn = new Function('event.returnValue=false')
  146. })
  147. // 上面的禁止f12那段代码没有生效,但是加了下面这段就能生效。
  148. document.onkeydown = function (e) {
  149. if (e && e.keyCode === 123) {
  150. e.returnValue = false
  151. // e.keyCode = 0 //去掉也可以的,倘若要写,则需要setter 以及 getter配合使用,不配合,会报错
  152. return false
  153. }
  154. }
  155. const print = ref()
  156. const getMusicList = async () => {
  157. try {
  158. const res = await request.get(
  159. `/api-website/open/music/sheet/detail/${state.id}`,
  160. {
  161. params: {
  162. clientType: getUserType()
  163. }
  164. }
  165. )
  166. res.data.paymentType = res.data.paymentType.split(',')
  167. state.musicDetail = res.data
  168. state.musicList = res.data.teacher.musicSheetList
  169. state.subjectList = res.data.background
  170. state.tagList = res.data.musicTagNames
  171. ? res.data.musicTagNames.split(',')
  172. : []
  173. state.mp3Type = res.data.audioType
  174. if (state.subjectList && state.subjectList.length > 0) {
  175. state.activeRow = res.data.background[0]
  176. state.subjectId = res.data.background[0].id
  177. }
  178. state.teacherDetail = res.data.teacher
  179. if (res.data.musicImg) {
  180. // 有图片
  181. state.showImg = res.data.musicImg ? res.data.musicImg.split(',') : []
  182. } else {
  183. // window.addEventListener(
  184. // 'message',
  185. // async e => {
  186. // console.log(e)
  187. // state.imgData = e.data
  188. // // alert(e.data);
  189. // const tempCanvas = await imgToCanvas(state.imgData)
  190. // const showImg = convasToImg(tempCanvas)
  191. // // 开始上传图片
  192. // uploadFunction(showImg)
  193. // },
  194. // false
  195. // )
  196. }
  197. // setAccompanyUrl()
  198. setUser()
  199. } catch (e) {
  200. console.log(e)
  201. }
  202. }
  203. const followVideo = async () => {
  204. try {
  205. const res = await request.get('/api-website/student/starOrUnStar', {
  206. params: {
  207. userId: state.teacherDetail.userId,
  208. starStatus: state.teacherDetail.star ? 0 : 1
  209. }
  210. })
  211. getMusicList()
  212. // state.otherVideoList = res.data
  213. } catch (e) {
  214. console.log(e)
  215. }
  216. }
  217. const getDetail = (val: string | number) => {
  218. state.id = val as string
  219. router.push({
  220. query: { ...route.query, id: state.id }
  221. })
  222. state.showImg = []
  223. getMusicList()
  224. }
  225. watch(
  226. () => state.accompanyUrl,
  227. accompanyUrl => {
  228. state.accompanyUrl = accompanyUrl
  229. }
  230. )
  231. const setAccompanyUrl = () => {
  232. const url = 'https://dev.colexiu.com'
  233. state.accompanyUrl =
  234. url +
  235. `/accompany/colxiu-website.html?id=${state.id}&part-index=${state.subjectId}`
  236. // state.accompanyUrl = `http://192.168.3.8:3000/colxiu-website.html?id=${state.id}&part-index=${state.subjectId}`
  237. }
  238. const setSvg = (val: any) => {
  239. console.log(val)
  240. }
  241. // const goIndex = () => {
  242. // router.push({
  243. // name: 'searchdetail',
  244. // params: { type: 'music' }
  245. // })
  246. // }
  247. onMounted(() => {
  248. // window.setSvg = (val)=>{
  249. // setSvg(val)
  250. // }
  251. getMusicList()
  252. state.userType = getUserType()
  253. state.showCode =
  254. state.userType == 'STUDENT' || !state.userType ? true : false
  255. // if (window.history && window.history.pushState) {
  256. // history.pushState(null, '', document.URL)
  257. // window.addEventListener('popstate', goIndex, false)
  258. // }
  259. // 拼链接
  260. })
  261. onDeactivated(() => {
  262. // window.removeEventListener('popstate', goIndex, false)
  263. })
  264. const base64ToBlob = data => {
  265. var arr = data.split(','),
  266. mime = arr[0].match(/:(.*?);/)[1]
  267. let bstr = atob(arr[1])
  268. let n = bstr.length
  269. let u8arr = new Uint8Array(n)
  270. while (n--) {
  271. u8arr[n] = bstr.charCodeAt(n)
  272. }
  273. return new Blob([u8arr], { type: mime })
  274. }
  275. const uploadFunction = async file => {
  276. try {
  277. const formData = new FormData()
  278. let fileName =
  279. new Date().getTime() + Math.ceil(Math.random() * 1000) + '.png'
  280. let key = new Date().getTime() + fileName
  281. let obj = {
  282. filename: fileName,
  283. bucketName: 'cloud-coach',
  284. postData: {
  285. filename: fileName,
  286. acl: 'public-read',
  287. key: key,
  288. unknowValueField: []
  289. }
  290. }
  291. const res = await request.post('/api-website/open/getUploadSign', {
  292. data: obj
  293. })
  294. state.dataObj = {
  295. policy: res.data.policy,
  296. signature: res.data.signature,
  297. key: key,
  298. KSSAccessKeyId: res.data.kssAccessKeyId,
  299. acl: 'public-read',
  300. name: fileName
  301. }
  302. for (let key in state.dataObj) {
  303. formData.append(key, state.dataObj[key])
  304. }
  305. // const imgRes = await fetch(file)
  306. const files = base64ToBlob(file)
  307. // const files = await imgRes.blob()
  308. formData.append('file', files, fileName)
  309. await umiRequest(state.ossUploadUrl, {
  310. method: 'POST',
  311. data: formData
  312. })
  313. let imgurl = state.ossUploadUrl + key
  314. const ress = await request.post('/api-website/open/music/sheet/img', {
  315. data: { musicSheetId: state.musicDetail.id, musicImg: imgurl }
  316. })
  317. console.log('赋值')
  318. state.showImg = [imgurl]
  319. } catch (e) {
  320. console.log(e)
  321. }
  322. }
  323. const setUser = () => {
  324. state.iconList = [] as any
  325. if (state.teacherDetail.tag.indexOf('STYLE') != -1) {
  326. state.iconList.push(styleIcon)
  327. } else {
  328. state.iconList.push(nostyleIcon)
  329. }
  330. if (state.teacherDetail.tag.indexOf('VIDEO') != -1) {
  331. state.iconList.push(videoIcon)
  332. } else {
  333. state.iconList.push(noVideoIcon)
  334. }
  335. if (state.teacherDetail.tag.indexOf('LIVE') != -1) {
  336. state.iconList.push(onlineIcon)
  337. } else {
  338. state.iconList.push(noOnlineIcon)
  339. }
  340. if (state.teacherDetail.tag.indexOf('MUSIC') != -1) {
  341. state.iconList.push(ablumIcon)
  342. } else {
  343. state.iconList.push(noAblumIcon)
  344. }
  345. }
  346. const printHander = async () => {
  347. // img 转 canvas
  348. const tempCanvas = await imgToCanvas(
  349. state.showImg[state.activeIndex] as any
  350. )
  351. const showImg = convasToImg(tempCanvas)
  352. // state.showImg = showImg
  353. const titleCanvas = addMusicTitle(tempCanvas, {
  354. title: state.musicDetail.musicSheetName,
  355. size: 18
  356. })
  357. const canvas = await addWatermark(titleCanvas, '酷乐秀')
  358. const imgUrl = convasToImg(canvas)
  359. const link = document.createElement('a')
  360. link.setAttribute('download', state.musicDetail.musicSheetName + '.png')
  361. // 添加时间戳,防止浏览器缓存图片
  362. // console.log(imgUrl,'imgUrl')
  363. state.imgUrl = imgUrl
  364. link.href = imgUrl
  365. link.click()
  366. }
  367. const gotoMusic = () => {
  368. router.push({
  369. name: 'musicLibrary',
  370. query: {
  371. search: state.teacherDetail.userName as string,
  372. type: 'music'
  373. }
  374. })
  375. }
  376. const favoriteMusic = async () => {
  377. const tockn = getAuth()
  378. if (!tockn) {
  379. states.loginPopupStatus = true
  380. return
  381. }
  382. ElMessageBox.confirm(
  383. `是否${state.musicDetail.favorite ? '取消收藏' : '收藏'}该曲目?`,
  384. '提示',
  385. {
  386. type: 'warning'
  387. }
  388. ).then(async () => {
  389. try {
  390. const res = await request.post(
  391. `/api-website/music/sheet/favorite/${state.id}`,
  392. {
  393. params: {
  394. clientType: getUserType()
  395. }
  396. }
  397. )
  398. getMusicList()
  399. // state.otherVideoList = res.data
  400. } catch (e) {
  401. console.log(e)
  402. }
  403. })
  404. }
  405. return () => (
  406. <>
  407. <div class={classes.wallWrap}>
  408. <div class={classes.wall}></div>
  409. <div class={classes.musicWraps}>
  410. <div class={[classes.width1200]}>
  411. <div class={[classes.musicWrap]}>
  412. <div class={classes.left}>
  413. <div class={classes.title}>
  414. {/* <div class={classes.titleLeft}>
  415. <p>声部:</p>
  416. <ElSelect
  417. class="w-full subjectChiose"
  418. v-model={state.subjectId}
  419. placeholder="请选择声部"
  420. onChange={() => {
  421. setAccompanyUrl()
  422. }}
  423. >
  424. {state.subjectList.map((item: any) => (
  425. <ElOption
  426. key={item.id}
  427. value={item.id}
  428. label={item.track}
  429. />
  430. ))}
  431. </ElSelect>
  432. </div> */}
  433. <div
  434. class={classes.titleRight}
  435. onClick={() => printHander()}
  436. >
  437. <img src={printIcon} alt="" />
  438. <p>下载乐谱</p>
  439. </div>
  440. </div>
  441. <div class={classes.musicContent}>
  442. {/* id="iframe" ref="iframe"*/}
  443. <img
  444. class={classes.musicStag}
  445. src={chargeImg[state.musicDetail.chargeType]}
  446. alt=""
  447. />
  448. {/* <iframe
  449. id="containerPrint"
  450. ref="print"
  451. style="width: 538px;page-break-after:always; "
  452. src={state.accompanyUrl}
  453. class={classes.iframe}
  454. /> */}
  455. <p class={classes.musicTitle}>
  456. {state.musicDetail.musicSheetName}
  457. </p>
  458. {state.showImg.length > 0 ? (
  459. <ElCarousel
  460. arrow="always"
  461. style="width: 100%; height: 541px"
  462. autoplay={false}
  463. loop={false}
  464. onChange={(index: number) => {
  465. state.activeIndex = index
  466. }}
  467. >
  468. {state.showImg &&
  469. state.showImg.map((img: any) => (
  470. <ElCarouselItem>
  471. <div style="height: 541px; overflow: hidden auto ;">
  472. <img src={img} alt="" />
  473. </div>
  474. </ElCarouselItem>
  475. ))}
  476. </ElCarousel>
  477. ) : (
  478. // <el-carousel :interval="5000" arrow="always">
  479. // <el-carousel-item v-for="item in 4" :key="item">
  480. // <h3>{{ item }}</h3>
  481. // </el-carousel-item>
  482. // </el-carousel>
  483. <div>
  484. <Vue3Lottie
  485. animationData={AstronautJSON}
  486. class={classes.finch}
  487. ></Vue3Lottie>
  488. <p class={classes.finchLoad}>加载中...</p>
  489. </div>
  490. )}
  491. </div>
  492. {state.mp3Type == 'MP3' ? (
  493. <colVideo
  494. src={state.activeRow.audioFileUrl}
  495. styleValue={{
  496. height: '68px',
  497. bacground: '#333'
  498. }}
  499. type={'audto'}
  500. settings={[
  501. 'captions',
  502. 'quality',
  503. 'speed',
  504. 'loop',
  505. 'title'
  506. ]}
  507. class={classes.audios}
  508. poster={state.activeRow.title}
  509. ></colVideo>
  510. ) : (
  511. <>
  512. <div class={classes.noMoreWrap}>
  513. <img src={noMore} alt="" />
  514. <p>温馨提示:该曲目暂不支持播放喔~</p>
  515. </div>
  516. </>
  517. )}
  518. {state.showCode ? (
  519. <div class={classes.showCode}>
  520. <div class={classes.showCodeWrap}>
  521. <img
  522. class={classes.close}
  523. src={iconClose}
  524. alt=""
  525. onClick={() => {
  526. state.showCode = false
  527. }}
  528. />
  529. <img class={classes.code} src={code} alt="" />
  530. {/* <h2 class={classes.title}>酷乐秀</h2>
  531. <p class={classes.conent}>扫码下载酷乐秀APP</p>
  532. <p class={classes.subConent}>
  533. 使用小酷Ai即可智能练习本首曲目哦!
  534. </p> */}
  535. <img src={banner} class={classes.banner} alt="" />
  536. </div>
  537. </div>
  538. ) : null}
  539. </div>
  540. <div class={classes.right}>
  541. <div class={classes.musicInfo}>
  542. <h2>
  543. {Array.isArray(state.musicDetail.paymentType) &&
  544. state.musicDetail.paymentType.map((tag: string) => (
  545. <span
  546. class={[
  547. classes.musicTag,
  548. classes[chargeClass[tag]]
  549. ]}
  550. >
  551. {chargeTypes[tag]}
  552. </span>
  553. ))}
  554. {state.musicDetail.musicSheetName}
  555. </h2>
  556. <div class={classes.collectWrap}>
  557. <div class={classes.masker} onClick={favoriteMusic}>
  558. <img
  559. src={state.musicDetail.favorite ? lineStart : start}
  560. alt=""
  561. />
  562. </div>
  563. <img
  564. src={state.musicDetail.titleImg || music}
  565. class={classes.musiceIcon}
  566. alt=""
  567. />
  568. <div>
  569. <div class={classes.collect} onClick={favoriteMusic}>
  570. <img
  571. src={
  572. state.musicDetail.favorite ? oLineStart : oStart
  573. }
  574. class={classes.start}
  575. alt=""
  576. />
  577. <p>{state.musicDetail.favoriteNum}人收藏</p>
  578. </div>
  579. <div class={classes.tagList}>
  580. {state.tagList.map((item: any) => {
  581. return <div class={classes.tag}>{item}</div>
  582. })}
  583. </div>
  584. <p class={classes.subTitle}>
  585. 作曲人:<span>{state.musicDetail.composer}</span>
  586. </p>
  587. <p class={classes.subTitle}>
  588. 声部:<span>{state.musicDetail.subjectNames}</span>
  589. </p>
  590. </div>
  591. </div>
  592. </div>
  593. <div class={classes.btooom}>
  594. {state.teacherDetail.userId ? (
  595. <div class={classes.teacherInfo}>
  596. <h2>上传者</h2>
  597. <div class={classes.teacherHeadWrap}>
  598. <div class={classes.teacherHeadLeft}>
  599. <img
  600. src={
  601. state.teacherDetail.userAvatar
  602. ? state.teacherDetail.userAvatar
  603. : teacherHeader
  604. }
  605. alt=""
  606. class={classes.teacherHeader}
  607. />
  608. <div class={classes.teacherHeadInfo}>
  609. <div class={classes.teacherHeadName}>
  610. <p>{state.teacherDetail.userName}</p>
  611. {/* {!state.teacherDetail.entryFlag ? (
  612. ''
  613. ) : (
  614. <img
  615. src={teacher}
  616. class={classes.teacherIcon}
  617. alt=""
  618. />
  619. )}
  620. {!state.teacherDetail.musicianFlag ? (
  621. ''
  622. ) : (
  623. <img
  624. src={musiceIcon}
  625. class={classes.teacherIcon}
  626. alt=""
  627. />
  628. )} */}
  629. {state.userType == 'STUDENT' ? (
  630. <div
  631. class={[
  632. classes.teacherHeadRight,
  633. state.teacherDetail.star
  634. ? classes.isStart
  635. : ''
  636. ]}
  637. onClick={() => followVideo()}
  638. >
  639. {state.teacherDetail.star
  640. ? '已关注'
  641. : '+ 关注'}
  642. </div>
  643. ) : (
  644. <div class={classes.iconWrapList}>
  645. <img
  646. src={
  647. state.teacherDetail.isVip
  648. ? vipIcon
  649. : noVipIcon
  650. }
  651. class={classes.vipIcon}
  652. alt=""
  653. />
  654. {state.iconList.map(item => (
  655. <img
  656. class={classes.teacherDetailIcon}
  657. src={item}
  658. alt=""
  659. />
  660. ))}
  661. </div>
  662. )}
  663. </div>
  664. </div>
  665. </div>
  666. <div>
  667. <p class={classes.fensNum}>
  668. {state.teacherDetail.fansNum}
  669. </p>
  670. <p class={classes.fens}>粉丝数</p>
  671. </div>
  672. </div>
  673. </div>
  674. ) : null}
  675. {state.musicList.length > 0 && (
  676. <div class={classes.otherMusic}>
  677. <div class={classes.videoNav}>
  678. <h5>TA的曲谱</h5>
  679. <div
  680. class={classes.wrapRight}
  681. onClick={() => gotoMusic()}
  682. >
  683. <span>更多</span>
  684. <img class={classes.arrow} src={arrow} alt="" />
  685. </div>
  686. </div>
  687. <div class={classes.otherMusicList}>
  688. {state.musicList.map(item => {
  689. return (
  690. <musicItem
  691. item={item}
  692. onMusicDetail={val => getDetail(val)}
  693. ></musicItem>
  694. )
  695. })}
  696. </div>
  697. </div>
  698. )}
  699. </div>
  700. </div>
  701. </div>
  702. </div>
  703. </div>
  704. </div>
  705. </>
  706. )
  707. }
  708. })