index-share.tsx 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872
  1. import {
  2. defineComponent,
  3. onMounted,
  4. onUnmounted,
  5. onBeforeMount,
  6. reactive,
  7. ref,
  8. watch,
  9. nextTick,
  10. } from 'vue'
  11. // import WaveSurfer from 'wavesurfer.js';
  12. // import Regions from 'wavesurfer.js/dist/plugins/regions.js';
  13. import styles from './index.module.less'
  14. import { Cell, Image, List, Popup, Slider, Sticky, NoticeBar, Toast } from 'vant'
  15. import TextEllipsis from './text-ellipsis/index';
  16. import MSticky from '@/components/col-sticky'
  17. import MHeader from '@/components/col-header'
  18. import iconMember from './images/icon-member.png'
  19. import iconZan from './images/icon-zan.png'
  20. import iconZanActive from './images/icon-zan-active.png'
  21. import logoImg from './images/logo.png';
  22. import logo1Img from './images/logo1.png';
  23. import backImg from "./images/back.png";
  24. import back1Img from "./images/back1.png";
  25. import videobg from "./images/videobg.png";
  26. import audioPan from './images/audio-pan.png';
  27. import audioLabel from './share-model/images/audioLabel.png';
  28. import videoLabel from './share-model/images/videoLabel.png';
  29. import musicBg from './share-model/images/music-bg.png';
  30. import playImg from './images/play.png';
  31. import btnImg from './images/btn.png';
  32. import iconUpward from './images/upward.png';
  33. import vipIcon from './images/vip_icon.png';
  34. import svipIcon from './images/svip_icon.png';
  35. import wxBg from './images/wx_bg.png';
  36. import tyBg from './images/ty.png';
  37. import {
  38. browser,
  39. getAuth,
  40. getGradeCh,
  41. getSecondRPM,
  42. removeAuth,
  43. setAuth
  44. } from '@/helpers/utils'
  45. import { postMessage, promisefiyPostMessage } from '@/helpers/native-message'
  46. import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
  47. import {
  48. api_openUserMusicDetail,
  49. api_openUserMusicPage,
  50. api_userMusicStar,
  51. api_verification
  52. } from './api'
  53. import MEmpty from '@/components/col-result'
  54. import LoginModel from './login-model'
  55. import { setLogout } from '@/state'
  56. import MWxTip from '@/components/the-wx-tip'
  57. import { usePageVisibility } from '@vant/use'
  58. import "plyr/dist/plyr.css";
  59. import Plyr from "plyr";
  60. import audioVisualDraw from "./audioVisualDraw"
  61. import Loading from './loading';
  62. import { state as originState } from '@/state'
  63. export default defineComponent({
  64. name: 'creation-detail',
  65. setup() {
  66. const {isApp, isTablet, weixin, isTeacher} = browser()
  67. const route = useRoute()
  68. const router = useRouter()
  69. const isScreenScroll = ref(false)
  70. const creationHeight = ref(0)
  71. const state = reactive({
  72. id: route.query.id,
  73. isEmpty: false,
  74. loginTag: false, // 是否登录标识
  75. loginStatus: false,
  76. playType: '' as 'Audio' | 'Video' | '', // 播放类型
  77. musicDetail: {} as any,
  78. timer: null as any,
  79. paused: true,
  80. audioWidth: 0,
  81. currentTime: 0,
  82. duration: 0.1,
  83. loop: false,
  84. dragStatus: false, // 是否开始拖动
  85. isClick: false,
  86. list: [] as any,
  87. listState: {
  88. dataShow: true, // 判断是否有数据
  89. loading: false,
  90. finished: false
  91. },
  92. params: {
  93. page: 1,
  94. rows: 4
  95. },
  96. messageStatus: false,
  97. message: '' as any,
  98. _plrl: null as any,
  99. heightV: 0,
  100. heightB: 0,
  101. })
  102. const plyrState = reactive({
  103. duration: 0,
  104. currentTime: 0,
  105. mediaTimeShow: false,
  106. playIngShow: true,
  107. loaded:false
  108. })
  109. // 谱面
  110. const staffState = reactive({
  111. staffSrc: "",
  112. isShow: false,
  113. height:"initial",
  114. speedRate:1,
  115. musicRenderType:"staff",
  116. partIndex:0
  117. })
  118. const isLandscapeScreen = ref(false)
  119. const wxStatus = ref(false)
  120. const staffDom= ref<HTMLIFrameElement>()
  121. const {playStaff, pauseStaff, updateProgressStaff} = staffMoveInstance()
  122. // 点赞
  123. const onStarChange = async () => {
  124. const Authorization = getAuth()
  125. if (!Authorization && route.query.Authorization) {
  126. setAuth(route.query.Authorization)
  127. }
  128. await checkLogin();
  129. // 是否登录
  130. if (!state.loginTag) {
  131. state.loginStatus = true
  132. return
  133. }
  134. try {
  135. await api_userMusicStar({
  136. userMusicId: state.id,
  137. star: !state.musicDetail.starFlag
  138. })
  139. state.musicDetail.starFlag = !state.musicDetail.starFlag
  140. if (state.musicDetail.starFlag) {
  141. state.musicDetail.likeNum += 1
  142. } else {
  143. state.musicDetail.likeNum -= 1
  144. }
  145. } catch {
  146. //
  147. }
  148. }
  149. // 获取列表
  150. const getList = async () => {
  151. try {
  152. if (state.isClick) return
  153. state.isClick = true
  154. const res = await api_openUserMusicPage({
  155. type: 'FORMAL',
  156. exclusionId: state.id,
  157. sort: 1,
  158. ...state.params
  159. })
  160. state.listState.loading = false
  161. const result = res.data || {}
  162. // 处理重复请求数据
  163. // if (state.list.length > 0 && result.current === 1) {
  164. // return
  165. // }
  166. state.list = result.rows || []
  167. state.listState.finished = result.current >= result.pages
  168. state.params.page = result.current + 1
  169. state.listState.dataShow = state.list.length > 0
  170. state.isClick = false
  171. } catch {
  172. state.listState.dataShow = false
  173. state.listState.finished = true
  174. state.isClick = false
  175. }
  176. }
  177. function handleChangeList() {
  178. if(state.listState.finished){
  179. state.listState.finished = false
  180. state.params.page = 1;
  181. getList()
  182. }else{
  183. getList()
  184. }
  185. }
  186. const onDetail = (item: any) => {
  187. router.push({
  188. path: '/shareCreation',
  189. query: {
  190. id: item.id
  191. }
  192. })
  193. }
  194. // 初始化 媒体播放
  195. function initMediaPlay(){
  196. const id = state.playType === "Audio" ? "#audioMediaSrc" : "#videoMediaSrc";
  197. state._plrl = new Plyr(id, {
  198. controls: ["play", "progress", "current-time", "duration"],
  199. fullscreen: {
  200. enabled: false,
  201. fallback: false
  202. }
  203. });
  204. const player = state._plrl
  205. // 创建音波数据
  206. if(state.playType === "Audio"){
  207. const audioDom = document.querySelector("#audioMediaSrc") as HTMLAudioElement
  208. const canvasDom = document.querySelector("#audioVisualizer") as HTMLCanvasElement
  209. const { pauseVisualDraw, playVisualDraw } = audioVisualDraw(audioDom, canvasDom)
  210. player.on('play', () => {
  211. playVisualDraw()
  212. });
  213. player.on('pause', () => {
  214. pauseVisualDraw()
  215. });
  216. }
  217. // 在微信中运行的时候,微信没有开放自动加载资源的权限,所以要等播放之后才显示播放控制器
  218. player.on('loadedmetadata', () => {
  219. plyrState.loaded = true
  220. //player.currentTime = playProgressData.playProgress
  221. });
  222. player.on("timeupdate", ()=>{
  223. plyrState.currentTime = player.currentTime
  224. })
  225. player.on('play', () => {
  226. plyrState.playIngShow = false
  227. playStaff()
  228. });
  229. player.on('pause', () => {
  230. plyrState.playIngShow = true
  231. pauseStaff()
  232. });
  233. player.on('ended', () => {
  234. player.currentTime = 0
  235. if(!player.playing){
  236. setTimeout(() => {
  237. updateProgressStaff(player.currentTime)
  238. }, 100);
  239. }
  240. });
  241. // 处理按压事件
  242. const handleStart = () => {
  243. if(isLandscapeScreen.value){
  244. return
  245. }
  246. plyrState.duration = player.duration
  247. plyrState.mediaTimeShow = true
  248. };
  249. // 处理松开事件
  250. const handleEnd = () => {
  251. plyrState.mediaTimeShow = false
  252. // 暂停的时候调用
  253. if(!player.playing){
  254. updateProgressStaff(player.currentTime)
  255. }
  256. };
  257. const progressDom = document.querySelector("#playMediaSection .plyr__controls .plyr__progress__container") as HTMLElement
  258. progressDom.addEventListener('mousedown', handleStart);
  259. progressDom.addEventListener('touchstart', handleStart);
  260. progressDom.addEventListener('mouseup', handleEnd);
  261. progressDom.addEventListener('touchend', handleEnd);
  262. }
  263. //点击改变播放状态
  264. function handlerClickPlay(event?:MouseEvent){
  265. // 原生 播放暂停按钮 点击的时候 不触发
  266. // @ts-ignore
  267. if(event?.target?.matches('button.plyr__control')){
  268. return
  269. }
  270. const player = state._plrl;
  271. if (player.playing) {
  272. player.pause();
  273. } else {
  274. player.play();
  275. }
  276. }
  277. function handlerBack(event:any){
  278. event.stopPropagation()
  279. verticalScreen()
  280. }
  281. function landscapeScreen(){
  282. postMessage({
  283. api: "setRequestedOrientation",
  284. content: {
  285. orientation: 0,
  286. },
  287. });
  288. isLandscapeScreen.value = true
  289. }
  290. function verticalScreen(){
  291. postMessage({
  292. api: "setRequestedOrientation",
  293. content: {
  294. orientation: 1,
  295. },
  296. });
  297. isLandscapeScreen.value = false
  298. }
  299. function handlerLandscapeScreen(event:any){
  300. event.stopPropagation()
  301. if(isApp){
  302. landscapeScreen()
  303. return
  304. }
  305. if(weixin){
  306. wxStatus.value = true
  307. }else{
  308. const t = Date.now()
  309. const str = location.href
  310. shareCall(str)
  311. setTimeout(() => {
  312. if(Date.now() - t < 3500){
  313. if (window.location.pathname.includes('teacher')) {
  314. window.location.href = location.origin + '/student' + '/#/transfer'
  315. } else {
  316. window.location.href = location.origin + '/student' + '/#/download'
  317. }
  318. }
  319. }, 3000)
  320. }
  321. }
  322. const shareCall = (str: string, params?: any) => {
  323. const query = {
  324. url: str,
  325. action: params?.action || 'h5', // app, h5
  326. pageTag: params?.pageTag || 1 // 页面标识
  327. }
  328. const iosStr = encodeURIComponent(JSON.stringify(query))
  329. const userAgent = navigator.userAgent || navigator.vendor;
  330. const platform = navigator.platform || 'unknown';
  331. if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent) || (platform === 'MacIntel')) {
  332. window.location.href = `ColexiuStudent://linkUrl=${iosStr}`
  333. } else if (/(Android)/i.test(userAgent)) {
  334. window.location.href = `colexiustudent://html:8888/SplashActivity?url=${iosStr}`
  335. } else {
  336. Toast('请用手机或移动设备打开')
  337. }
  338. }
  339. const checkLogin = async () => {
  340. try {
  341. // 判断是否登录
  342. const Authorization = getAuth() // storage.get(ACCESS_TOKEN) || ''
  343. if (Authorization) {
  344. await api_verification({
  345. token: Authorization
  346. })
  347. state.loginTag = true
  348. // if (!res.data) {
  349. // removeAuth()
  350. // setLogout()
  351. // }
  352. }
  353. } catch (e: any) {
  354. // 登录是否有效
  355. state.loginTag = false
  356. removeAuth()
  357. setLogout()
  358. }
  359. };
  360. const __init = async () => {
  361. await checkLogin();
  362. try {
  363. const res = await api_openUserMusicDetail(state.id)
  364. if (res.code === 999) {
  365. // 没有的时候显示缺省页
  366. state.isEmpty = true
  367. staffState.isShow = true
  368. return
  369. } else {
  370. state.musicDetail = res.data
  371. try{
  372. const jsonConfig = JSON.parse(res.data.jsonConfig)
  373. jsonConfig.speedRate && (staffState.speedRate = jsonConfig.speedRate)
  374. jsonConfig.musicRenderType && (staffState.musicRenderType = jsonConfig.musicRenderType)
  375. jsonConfig["part-index"] && (staffState.partIndex = jsonConfig["part-index"])
  376. }catch{
  377. }
  378. // 五线谱
  379. initStaff()
  380. getList()
  381. // 判断是视频还是音频
  382. if (res.data.videoUrl.lastIndexOf('mp4') !== -1) {
  383. state.playType = 'Video'
  384. } else {
  385. state.playType = 'Audio'
  386. }
  387. // 初始化
  388. nextTick(() => {
  389. initMediaPlay();
  390. });
  391. }
  392. } catch (err) {
  393. state.listState.dataShow = false
  394. // 没有的时候显示缺省页
  395. state.message = err;
  396. state.messageStatus = true;
  397. }
  398. }
  399. // 滚动事件
  400. const handleScroll = () => {
  401. // 作品已删除不让滚动变色
  402. if(state.isEmpty) return
  403. const height =
  404. window.scrollY ||
  405. document.documentElement.scrollTop
  406. // 防止多次调用
  407. if(height > 0 && isScreenScroll.value === false){
  408. isScreenScroll.value = true
  409. if(isApp){
  410. setStatusBarTextColor(false)
  411. }
  412. }
  413. if(height <= 0){
  414. isScreenScroll.value = false
  415. if(isApp){
  416. setStatusBarTextColor(true)
  417. }
  418. }
  419. }
  420. // 跳转下载页
  421. function handlerDownLoad(){
  422. if(weixin){
  423. wxStatus.value = true
  424. }else{
  425. // 如果是老师端
  426. if (window.location.pathname.includes('teacher')) {
  427. window.location.href = location.origin + '/student' + '/#/transfer'
  428. } else {
  429. router.push({
  430. path:"/download"
  431. })
  432. }
  433. }
  434. }
  435. const pageVisibility = usePageVisibility()
  436. watch(pageVisibility, value => {
  437. if (value === 'hidden') {
  438. state._plrl?.pause();
  439. }
  440. });
  441. // 初始化五线谱
  442. function initStaff(){
  443. const systemType = originState.platformType === 'TEACHER' || isTeacher ? 'teacher' : 'student'
  444. const src = `/klx-music-score/#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=${staffState.musicRenderType}&part-index=${staffState.partIndex}&userMusicId=${state.id}&systemType=${systemType}`;
  445. // const src = `http://192.168.3.68:3000/instrument.html#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=${staffState.musicRenderType}&part-index=${staffState.partIndex}&userMusicId=${state.id}`;
  446. staffState.staffSrc = src
  447. window.addEventListener('message', (event) => {
  448. const { api, height } = event.data;
  449. if (api === 'api_musicPage') {
  450. staffState.isShow = true
  451. staffState.height = height + "px"
  452. // 如果是播放中自动开始播放 不是播放 自动跳转到当前位置
  453. // if(playProgressData.playState){
  454. // handlerClickPlay()
  455. // }else{
  456. // updateProgressStaff(state._plrl.currentTime)
  457. // }
  458. }
  459. });
  460. }
  461. function staffMoveInstance(){
  462. let isPause = true
  463. const requestAnimationFrameFun = () => {
  464. requestAnimationFrame(() => {
  465. staffDom.value?.contentWindow?.postMessage(
  466. {
  467. api: 'api_playProgress',
  468. content: {
  469. currentTime: state._plrl.currentTime * staffState.speedRate
  470. }
  471. },
  472. "*"
  473. )
  474. if (!isPause) {
  475. requestAnimationFrameFun()
  476. }
  477. })
  478. }
  479. const playStaff = () => {
  480. // 没渲染不执行
  481. if(!staffState.isShow) return
  482. isPause = false
  483. staffDom.value?.contentWindow?.postMessage(
  484. {
  485. api: 'api_play'
  486. },
  487. "*"
  488. )
  489. requestAnimationFrameFun()
  490. }
  491. const pauseStaff = () => {
  492. // 没渲染不执行
  493. if(!staffState.isShow) return
  494. isPause = true
  495. staffDom.value?.contentWindow?.postMessage(
  496. {
  497. api: 'api_paused'
  498. },
  499. "*"
  500. )
  501. }
  502. const updateProgressStaff = (currentTime: number) => {
  503. // 没渲染不执行
  504. if(!staffState.isShow) return
  505. staffDom.value?.contentWindow?.postMessage(
  506. {
  507. api: 'api_updateProgress',
  508. content: {
  509. currentTime: currentTime * staffState.speedRate
  510. }
  511. },
  512. "*"
  513. )
  514. }
  515. return {
  516. playStaff,
  517. pauseStaff,
  518. updateProgressStaff
  519. }
  520. }
  521. // 设置导航栏颜色
  522. function setStatusBarTextColor(isWhite:boolean){
  523. postMessage({
  524. api: 'setStatusBarTextColor',
  525. content: { statusBarTextColor: isWhite }
  526. })
  527. }
  528. function setFullHeight(){
  529. creationHeight.value = window.innerHeight
  530. }
  531. onBeforeMount(() => {
  532. if(isApp) {
  533. postMessage({
  534. api: "setRequestedOrientation",
  535. content: {
  536. orientation: 1,
  537. },
  538. });
  539. setStatusBarTextColor(true)
  540. }
  541. })
  542. onMounted(async () => {
  543. window.addEventListener("scroll", handleScroll)
  544. __init()
  545. setFullHeight()
  546. window.addEventListener('resize', setFullHeight)
  547. })
  548. onUnmounted(() => {
  549. window.removeEventListener("scroll", handleScroll)
  550. window.removeEventListener('resize', setFullHeight)
  551. state._plrl?.destroy()
  552. })
  553. onBeforeRouteUpdate((to: any) => {
  554. state.id = to.query.id;
  555. state.playType = '';
  556. state.params.page = 1;
  557. state.list = [];
  558. if(state._plrl){
  559. state._plrl.destroy()
  560. }
  561. plyrState.playIngShow = true
  562. staffState.staffSrc = ""
  563. staffState.isShow = false
  564. staffState.height = "initial"
  565. __init();
  566. })
  567. return () => (
  568. <div
  569. style={
  570. {
  571. '--barheight':state.heightV + "px",
  572. "--creationHeight":creationHeight.value ? creationHeight.value+"px" : "100vh"
  573. }
  574. }
  575. class={[
  576. styles.creation,
  577. isTablet ? styles.creationTablet : '',
  578. isScreenScroll.value && styles.isShareScreenScroll
  579. ]}>
  580. <div class={styles.creationBg}></div>
  581. <MSticky position="top"
  582. onGetHeight={(height: any) => {
  583. console.log(height, 'height', height)
  584. state.heightV = height
  585. }}
  586. >
  587. {
  588. isApp ? <MHeader
  589. leftClickDefault={false}
  590. color={isScreenScroll.value ? "#333333" : "#ffffff"}
  591. background={isScreenScroll.value ? `rgb(255,255,255` : "transparent"}
  592. border={false}
  593. isBack={route.query.platformType != 'ANALYSIS'}
  594. title={"作品详情"}
  595. onLeftClick={()=>{
  596. setStatusBarTextColor(false)
  597. postMessage({
  598. api: 'back'
  599. });
  600. }}
  601. />
  602. : <div class={styles.logoDownload}>
  603. <img src={isScreenScroll.value ? logo1Img : logoImg} class={styles.logoImg}></img>
  604. <div class={styles.logTit} onClick={handlerDownLoad}>下载App</div>
  605. </div>
  606. }
  607. </MSticky>
  608. {
  609. state.isEmpty ?
  610. <div class={styles.isEmpty}>
  611. <MEmpty tips="作品已删除~" btnStatus={false} />
  612. </div> :
  613. <>
  614. <div class={styles.singerBox}>
  615. <div class={styles.musicSheetName}>
  616. <NoticeBar
  617. text={state.musicDetail?.musicSheetName}
  618. background="none"
  619. />
  620. </div>
  621. <div class={styles.singerName}>
  622. 演奏:{state.musicDetail?.username}
  623. </div>
  624. </div>
  625. <Sticky zIndex={1000} offsetTop={state.heightV - 1 + "px"}>
  626. <div class={[styles.playSection, plyrState.mediaTimeShow && styles.mediaTimeShow,!plyrState.loaded && styles.notLoaded,isLandscapeScreen.value&&styles.isLandscapeScreen, state.playType === 'Audio' && styles.isLandscapeScreen2]} id="playMediaSection" onClick={handlerClickPlay}>
  627. {
  628. isLandscapeScreen.value &&
  629. <div class={styles.backBox}>
  630. <img class={styles.backImg} src={state.playType === 'Video' ? back1Img : backImg} onClick={handlerBack}/>
  631. <div class={[styles.musicDetail, state.playType === 'Audio' && styles.adMusicDetail]}>
  632. <div class={styles.musicSheetName}>
  633. <NoticeBar
  634. text={state.musicDetail?.musicSheetName}
  635. background="none"
  636. />
  637. </div>
  638. <div class={styles.username}>演奏:{state.musicDetail?.username}</div>
  639. </div>
  640. </div>
  641. }
  642. {
  643. state.playType &&
  644. <>
  645. {
  646. state.playType === 'Audio' &&
  647. <div class={styles.audioBox}>
  648. <canvas class={styles.audioVisualizer} id="audioVisualizer"></canvas>
  649. <audio
  650. crossorigin="anonymous"
  651. id="audioMediaSrc"
  652. src={state.musicDetail?.videoUrl}
  653. controls="false"
  654. preload="metadata"
  655. playsinline
  656. webkit-playsinline
  657. />
  658. <img src={tyBg} class={styles.tyBg} />
  659. <div class={styles.audioBoxBg}>
  660. <div class={[styles.audioPan, plyrState.playIngShow && styles.imgRotate]}>
  661. <img class={styles.audioImg} src={state.musicDetail.img || musicBg} />
  662. </div>
  663. <i class={styles.audioPoint}></i>
  664. <i class={[styles.audioZhen, plyrState.playIngShow && styles.active]}></i>
  665. </div>
  666. </div>
  667. }
  668. {
  669. state.playType === 'Video' &&
  670. <video
  671. id="videoMediaSrc"
  672. class={styles.videoBox}
  673. src={state.musicDetail?.videoUrl}
  674. data-poster={ state.musicDetail?.videoImg || videobg}
  675. poster={ state.musicDetail?.videoImg || videobg}
  676. preload="metadata"
  677. playsinline
  678. webkit-playsinline
  679. x5-playsinline
  680. />
  681. }
  682. <div class={[styles.playLarge, !plyrState.mediaTimeShow && plyrState.playIngShow && styles.playIngShow]}></div>
  683. <div class={styles.mediaTimeCon}>
  684. <div class={styles.mediaTime}>
  685. <div>
  686. {getSecondRPM(plyrState.currentTime)}
  687. </div>
  688. <div class={styles.note}>/</div>
  689. <div class={styles.duration}>
  690. {getSecondRPM(plyrState.duration)}
  691. </div>
  692. </div>
  693. </div>
  694. <div class={styles.landscapeScreen} onClick={handlerLandscapeScreen}></div>
  695. {/* 谱面 */}
  696. {
  697. staffState.staffSrc &&
  698. <div class={[styles.staffBoxCon, staffState.isShow && styles.staffBoxShow]}>
  699. <div
  700. class={[styles.staffBox, state.playType === 'Video' && styles.staffBoxBg]}
  701. style={
  702. {
  703. '--staffBoxHeight':staffState.height
  704. }
  705. }
  706. >
  707. <div class={styles.mask}></div>
  708. <iframe
  709. ref={staffDom}
  710. class={styles.staff}
  711. frameborder="0"
  712. src={staffState.staffSrc}>
  713. </iframe>
  714. </div>
  715. </div>
  716. }
  717. </>
  718. }
  719. </div>
  720. </Sticky>
  721. <div class={[styles.musicSection, styles.musicShareSection]}>
  722. <div class={styles.avatarInfoBox}>
  723. <div class={styles.avatar}>
  724. <div class={styles.avatarImg}>
  725. <img class={[styles.userLogo, state.musicDetail.vipType === 'VIP' ? styles.vipLogo : state.musicDetail.vipType === 'PERMANENT_SVIP' || state.musicDetail.vipType === 'SVIP' ? styles.svipLogo : '']} src={state.musicDetail.avatar} />
  726. {
  727. (state.musicDetail.vipType === 'VIP' || state.musicDetail.vipType === 'PERMANENT_SVIP' || state.musicDetail.vipType === 'SVIP') &&
  728. <img class={styles.vipIcon} src={state.musicDetail.vipType === 'VIP' ? vipIcon : svipIcon} />
  729. }
  730. </div>
  731. <div class={styles.infoCon}>
  732. <div class={styles.info}>
  733. <span class={styles.userName}>{state.musicDetail?.username}</span>
  734. {state.musicDetail.vipFlag && (
  735. <img src={iconMember} class={styles.iconMember} />
  736. )}
  737. </div>
  738. <div class={styles.sub}>
  739. {state.musicDetail.subjectName}{' '}
  740. {getGradeCh(state.musicDetail.currentGradeNum - 1)}
  741. </div>
  742. </div>
  743. </div>
  744. <div class={styles.linkes} onClick={onStarChange}>
  745. <img src={state.musicDetail.starFlag ? iconZanActive : iconZan} class={styles.iconZan} />
  746. <span>{state.musicDetail.likeNum}</span>
  747. </div>
  748. </div>
  749. <TextEllipsis class={styles.textEllipsis} text={state.musicDetail?.desc || ''} />
  750. </div>
  751. <div class={styles.likeSection}>
  752. <div class={styles.likeTitle}>推荐作品</div>
  753. {state.listState.dataShow ? (
  754. <>
  755. <List
  756. finished={true}
  757. finishedText=" "
  758. class={[styles.container, styles.containerInformation]}
  759. //onLoad={getList}
  760. immediateCheck={false}>
  761. {state.list.map((item: any, index:number) => (
  762. <Cell
  763. class={[styles.likeShareItem, index===state.list.length-1&&styles.likeShareItemLast]}
  764. border={false}
  765. onClick={() => onDetail(item)}
  766. >
  767. {{
  768. icon: () => (
  769. <div class={styles.audioImgBox}>
  770. <img
  771. src={audioPan}
  772. class={styles.audioPan}
  773. crossorigin="anonymous"
  774. />
  775. <img
  776. src={
  777. item.img || musicBg
  778. }
  779. class={styles.muploader}
  780. crossorigin="anonymous"
  781. />
  782. <img class={styles.imgLabel} src={item.videoUrl?.lastIndexOf('mp4') !== -1 ? videoLabel : audioLabel} />
  783. </div>
  784. ),
  785. title: () => (
  786. <div class={styles.userInfo}>
  787. <div class={[styles.musicSheetName,'van-ellipsis']}>{item.musicSheetName}</div>
  788. <div class={styles.usernameCon}>
  789. <div class={styles.likeNum}>
  790. <img src={iconZanActive} />
  791. <span>{item.likeNum}</span>
  792. </div>
  793. <div class={[styles.username, 'van-ellipsis']}>{item.username}</div>
  794. </div>
  795. </div>
  796. ),
  797. value: () => (
  798. <img src={playImg} class={styles.playImg} />
  799. )
  800. }}
  801. </Cell>
  802. ))}
  803. </List>
  804. {
  805. (!state.listState.finished || state.params.page>2) &&
  806. <div class={styles.btnImg}>
  807. <img onClick={handleChangeList} onTouchstart={()=>{}} src={btnImg} />
  808. </div>
  809. }
  810. </>
  811. ) : (
  812. <MEmpty tips="暂无作品~" btnStatus={false} />
  813. )}
  814. </div>
  815. {
  816. !isScreenScroll.value &&
  817. <MSticky position="bottom" offsetBottom={state.heightB - 1 + "px"} >
  818. <div class={styles.upward}>
  819. <img src={iconUpward} />
  820. </div>
  821. </MSticky>
  822. }
  823. </>
  824. }
  825. <Popup
  826. v-model:show={state.loginStatus}
  827. style={{ background: 'transparent', overflow: 'inherit' }}
  828. >
  829. <LoginModel
  830. onClose={() => (state.loginStatus = false)}
  831. onConfirm={async (val: boolean) => {
  832. state.loginTag = val
  833. state.loginStatus = false
  834. const { data } = await api_openUserMusicDetail(state.id)
  835. state.musicDetail = data
  836. }}
  837. />
  838. </Popup>
  839. <MWxTip
  840. v-model:show={state.messageStatus}
  841. message={state.message}
  842. showButton={false}
  843. />
  844. {
  845. !staffState.isShow && <Loading></Loading>
  846. }
  847. {wxStatus.value && (
  848. <div
  849. class={styles.wxpopup}
  850. onClick={() => {
  851. wxStatus.value = false;
  852. }}>
  853. <img src={wxBg} alt="" />
  854. </div>
  855. )}
  856. </div>
  857. )
  858. }
  859. })