index.tsx 22 KB

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