index.tsx 24 KB

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