index.tsx 17 KB

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