import { defineComponent, onMounted, onUnmounted, reactive, ref, watch, nextTick } from 'vue'; // import WaveSurfer from 'wavesurfer.js'; // import Regions from 'wavesurfer.js/dist/plugins/regions.js'; import styles from './index.module.less'; import { Cell, Image, List, Popup, Sticky, TextEllipsis, NoticeBar } from 'vant'; import iconMember from './images/icon-member.png'; import iconZan from './images/icon-zan.png'; import iconZanActive from './images/icon-zan-active.png'; import logoImg from './images/logo.png'; import logo1Img from './images/logo1.png'; import iconUpward from './images/upward.png'; import iconPlay from './images/icon-play.png'; import iconPause from './images/icon-pause.png'; import audioPan from './images/audio-pan.png'; import audioLabel from './share-model/images/audioLabel.png'; import videoLabel from './share-model/images/videoLabel.png'; import musicBg from './share-model/images/music-bg.png'; import playImg from './images/play.png'; import { browser, getGradeCh, getSecondRPM, vaildMusicScoreUrl } from '@/helpers/utils'; import { onBeforeRouteUpdate, useRoute, useRouter, onBeforeRouteLeave } from 'vue-router'; import { api_openUserMusicDetail, api_openUserMusicPage, api_userMusicStar, api_verification } from './api'; import MEmpty from '@/components/m-empty'; import MVideo from '@/components/m-video'; import LoginModel from './login-model'; import { removeAuth } from '../student-register/layout/utils'; import { setLogout } from '@/state'; import { storage } from '@/helpers/storage'; import { ACCESS_TOKEN } from '@/store/mutation-types'; import MWxTip from '@/components/m-wx-tip'; import { usePageVisibility, useEventListener, useWindowSize } from '@vant/use'; import LoginChangeModel from './login-change-model'; import MSticky from '@/components/m-sticky'; import "plyr/dist/plyr.css"; import Plyr from "plyr"; import { Vue3Lottie } from "vue3-lottie"; import audioBga from "./images/audioBga.json"; import audioBga1 from "./images/leftCloud.json"; import audioBga2 from "./images/rightCloud.json"; import videobg from "./images/videobg.png"; import btnImg from './images/btn.png'; import audioVisualDraw from "./audioVisualDraw" import playProgressData from "./playCreation/playProgress" import Loading from './loading'; export default defineComponent({ name: 'creation-detail', setup() { const route = useRoute(); const router = useRouter(); const isScreenScroll = ref(false) const creationHeight = ref(0) const state = reactive({ id: route.query.id, isEmpty:false, loginTag: false, // 是否登录标识 loginStatus: false, loginChangeState: false, // 切换账号 credential: {} as any, playType: '' as 'Audio' | 'Video' | '', // 播放类型 musicDetail: {} as any, isClick: false, list: [] as any, listState: { dataShow: true, // 判断是否有数据 loading: false, finished: false }, params: { page: 1, rows: 4 }, messageStatus: false, message: '', _plrl: null as any, heightV:0, heightB:0 }); const plyrState = reactive({ duration: 0, currentTime: 0, mediaTimeShow: false, playIngShow: true, loaded:false }) // 谱面 const staffState = reactive({ staffSrc: "", isShow: false, height:"initial", speedRate:1, musicRenderType:"staff", partIndex:0 }) const staffDom= ref() const {playStaff, pauseStaff, updateProgressStaff} = staffMoveInstance() let isInitAudioVisualDraw =false // 点赞 const onStarChange = async () => { await checkLogin(); // 是否登录 if (!state.loginTag) { state.loginStatus = true; return; } try { await api_userMusicStar({ userMusicId: state.id, star: !state.musicDetail.starFlag }); state.musicDetail.starFlag = !state.musicDetail.starFlag; if (state.musicDetail.starFlag) { state.musicDetail.likeNum += 1; } else { state.musicDetail.likeNum -= 1; } } catch { // } }; // 获取列表 const getList = async () => { try { if (state.isClick) return; state.isClick = true; const res = await api_openUserMusicPage({ type: 'FORMAL', exclusionId: state.id, sort: 1, ...state.params }); state.listState.loading = false; const result = res.data || {}; // 处理重复请求数据 // if (state.list.length > 0 && result.current === 1) { // return; // } state.list = result.rows || []; state.listState.finished = result.current >= result.pages; state.params.page = result.current + 1; state.listState.dataShow = state.list.length > 0; state.isClick = false; } catch { state.listState.dataShow = false; state.listState.finished = true; state.isClick = false; } }; function handleChangeList() { if(state.listState.finished){ state.listState.finished = false state.params.page = 1; getList() }else{ getList() } } const onDetail = (item: any) => { playProgressData.playProgress = 0 playProgressData.playState = false router.push({ path: '/shareCreation', query: { id: item.id } }); }; // 初始化 媒体播放 function initMediaPlay(){ const id = state.playType === "Audio" ? "#audioMediaSrc" : "#videoMediaSrc"; state._plrl = new Plyr(id, { controls: ["progress"], fullscreen: { enabled: false, fallback: false } }); const player = state._plrl // 在微信中运行的时候,微信没有开放自动加载资源的权限,所以要等播放之后才显示播放控制器 player.on('loadedmetadata', () => { plyrState.loaded = true player.currentTime = playProgressData.playProgress }); player.on("timeupdate", ()=>{ plyrState.currentTime = player.currentTime }) player.on('play', () => { plyrState.playIngShow = false playStaff() }); player.on('pause', () => { plyrState.playIngShow = true pauseStaff() }); player.on('ended', () => { player.currentTime = 0 if(!player.playing){ setTimeout(() => { updateProgressStaff(player.currentTime) }, 100); } }); // 处理按压事件 const handleStart = () => { plyrState.duration = player.duration plyrState.mediaTimeShow = true }; // 处理松开事件 const handleEnd = () => { plyrState.mediaTimeShow = false // 暂停的时候调用 if(!player.playing){ updateProgressStaff(player.currentTime) } }; const progressDom = document.querySelector("#playMediaSection .plyr__controls .plyr__progress__container") as HTMLElement progressDom.addEventListener('mousedown', handleStart); progressDom.addEventListener('touchstart', handleStart); progressDom.addEventListener('mouseup', handleEnd); progressDom.addEventListener('touchend', handleEnd); } //点击改变播放状态 function handlerClickPlay(){ const player = state._plrl; // 由于ios低版本必须在用户操作之后才能初始化 createMediaElementSource 所以必须在用户操作之后初始化 if(!isInitAudioVisualDraw && state.playType === "Audio"){ isInitAudioVisualDraw = true // 创建音波数据 const audioDom = document.querySelector("#audioMediaSrc") as HTMLAudioElement const canvasDom = document.querySelector("#audioVisualizer") as HTMLCanvasElement const { pauseVisualDraw, playVisualDraw } = audioVisualDraw(audioDom, canvasDom) player.on('play', () => { playVisualDraw() }); player.on('pause', () => { pauseVisualDraw() }); } if (player.playing) { player.pause(); } else { player.play(); } } function handlerLandscapeScreen(event:any){ event.stopPropagation() playProgressData.playState = !!state._plrl?.playing playProgressData.playProgress = state._plrl?.currentTime || 0 router.push({ path:"/playCreation", query:{ resourceUrl:encodeURIComponent(state.musicDetail?.videoUrl), videoBgUrl:encodeURIComponent(state.musicDetail?.videoImg || ""), musicSheetName:encodeURIComponent(state.musicDetail?.musicSheetName), username:encodeURIComponent(state.musicDetail?.username), musicSheetId:encodeURIComponent(state.musicDetail?.musicSheetId), speedRate:encodeURIComponent(staffState.speedRate), musicRenderType:encodeURIComponent(staffState.musicRenderType), partIndex:encodeURIComponent(staffState.partIndex), } }) } const checkLogin = async () => { try { // 判断是否登录 const Authorization = storage.get(ACCESS_TOKEN) || ''; if (Authorization) { const res = await api_verification({ token: Authorization }); console.log(res.data, 'res.data'); state.loginTag = res.data; if (!res.data) { removeAuth(); setLogout(); } } } catch (err) { // storage.remove(ACCESS_TOKEN); removeAuth(); setLogout(); state.loginTag = false; } }; const __init = async () => { await checkLogin(); try { const res = await api_openUserMusicDetail(state.id); if (res.code === 999) { // 没有的时候显示缺省页 state.isEmpty = true staffState.isShow = true return; } else { state.musicDetail = res.data; try{ const jsonConfig = JSON.parse(res.data.jsonConfig) jsonConfig.speedRate && (staffState.speedRate = jsonConfig.speedRate) jsonConfig.musicRenderType && (staffState.musicRenderType = jsonConfig.musicRenderType) jsonConfig.partIndex && (staffState.partIndex = jsonConfig.partIndex) }catch{ } // 五线谱 initStaff() getList(); // 判断是视频还是音频 if (res.data.videoUrl.lastIndexOf('mp4') !== -1) { state.playType = 'Video'; } else { state.playType = 'Audio'; } // 初始化 nextTick(() => { initMediaPlay(); }); } } catch (err:any) { // 没有的时候显示缺省页 state.message = err; state.messageStatus = true; } }; // 滚动事件 const cleanScrollEvent = useEventListener('scroll', () => { // 作品已删除不让滚动变色 if(state.isEmpty) return const height = window.scrollY || document.documentElement.scrollTop // 防止多次调用 if(height > 0 && isScreenScroll.value === false){ isScreenScroll.value = true } if(height <= 0){ isScreenScroll.value = false } }) function handlerDownLoad(){ router.push({ path:"/transfer" }) } const pageVisibility = usePageVisibility(); watch(pageVisibility, value => { if (value === 'hidden') { state._plrl?.pause(); } }); // 初始化五线谱 function initStaff(){ const src = `${vaildMusicScoreUrl()}/instrument/#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=${staffState.musicRenderType}&part-index=${staffState.partIndex}`; //const src = `http://192.168.3.122:3000/instrument.html#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=${staffState.musicRenderType}&part-index=${staffState.partIndex}`; staffState.staffSrc = src window.addEventListener('message', (event) => { const { api, height } = event.data; if (api === 'api_musicPage') { staffState.isShow = true staffState.height = height + "px" // 如果是播放中自动开始播放 不是播放 自动跳转到当前位置 if(playProgressData.playState){ handlerClickPlay() }else{ updateProgressStaff(state._plrl.currentTime) } } }); } function staffMoveInstance(){ let isPause = true const requestAnimationFrameFun = () => { requestAnimationFrame(() => { staffDom.value?.contentWindow?.postMessage( { api: 'api_playProgress', content: { currentTime: state._plrl.currentTime * staffState.speedRate } }, "*" ) if (!isPause) { requestAnimationFrameFun() } }) } const playStaff = () => { // 没渲染不执行 if(!staffState.isShow) return isPause = false staffDom.value?.contentWindow?.postMessage( { api: 'api_play' }, "*" ) requestAnimationFrameFun() } const pauseStaff = () => { // 没渲染不执行 if(!staffState.isShow) return isPause = true staffDom.value?.contentWindow?.postMessage( { api: 'api_paused' }, "*" ) } const updateProgressStaff = (currentTime: number) => { // 没渲染不执行 if(!staffState.isShow) return staffDom.value?.contentWindow?.postMessage( { api: 'api_updateProgress', content: { currentTime: currentTime * staffState.speedRate } }, "*" ) } return { playStaff, pauseStaff, updateProgressStaff } } function setFullHeight(){ creationHeight.value = window.innerHeight } onMounted(async () => { __init(); window.addEventListener('resize', setFullHeight) }); onUnmounted(() => { cleanScrollEvent() window.removeEventListener('resize', setFullHeight) state._plrl?.destroy() }); onBeforeRouteUpdate((to: any) => { state.id = to.query.id; state.playType = ''; state.params.page = 1; state.list = []; isInitAudioVisualDraw = false if(state._plrl){ state._plrl.destroy() } plyrState.playIngShow = true staffState.staffSrc = "" staffState.isShow = false staffState.height = "initial" __init(); }); onBeforeRouteLeave((to, from, next)=>{ if(to.path !== "/playCreation"){ playProgressData.playProgress = 0 playProgressData.playState = false } next() }) return () => (
{ console.log(height, 'height', height) state.heightV = height }} >
下载App
{ state.isEmpty ?
: <>
演奏:{state.musicDetail?.username}
{ state.playType && <> { state.playType === 'Audio' &&
} { state.playType === 'Video' &&
{state.musicDetail?.username} {state.musicDetail.vipFlag && ( )}
{state.musicDetail.subjectName}{' '} {getGradeCh(state.musicDetail.currentGradeNum - 1)}
{state.musicDetail.likeNum}
推荐作品
{state.listState.dataShow ? ( <> {state.list.map((item: any, index:number) => ( onDetail(item)} > {{ icon: () => (
), title: () => (
{item.musicSheetName}
{item.likeNum}
{item.username}
), value: () => ( ) }}
))}
{ (!state.listState.finished || state.params.page>2) &&
{}} src={btnImg} />
} ) : ( )}
{ !isScreenScroll.value &&
} } (state.loginStatus = false)} onConfirm={async (val: any) => { if (val.loginTag) { state.loginTag = val.loginTag; state.loginStatus = false; const { data } = await api_openUserMusicDetail(state.id); state.musicDetail = data; } else { state.credential = val.data; state.loginChangeState = true; state.loginStatus = false; } }} /> { state.credential = {}; state.loginChangeState = false; }} onConfirm={async (val: any) => { state.loginTag = val.loginTag; state.loginChangeState = false; const { data } = await api_openUserMusicDetail(state.id); state.musicDetail = data; }} /> { !staffState.isShow && }
); } });