| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734 | 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 } 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<HTMLIFrameElement>()    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 () => (      <div        style={          {            '--barheight':state.heightV + "px",            "--creationHeight":creationHeight.value ? creationHeight.value+"px" : "100vh"          }        }        class={[          styles.creation,          browser().isTablet ? styles.creationTablet : '',          isScreenScroll.value && styles.isShareScreenScroll        ]}>        <div class={styles.creationBg}></div>        <MSticky position="top"          onBarHeight={(height: any) => {            console.log(height, 'height', height)            state.heightV = height          }}        >            <div class={styles.logoDownload}>              <img src={isScreenScroll.value ? logo1Img : logoImg} class={styles.logoImg}></img>              <div class={styles.logTit} onClick={handlerDownLoad}>下载App</div>            </div>        </MSticky>        {          state.isEmpty ?          <div class={styles.isEmpty}>            <MEmpty image={"empty2"} description="作品已删除~" />          </div> :          <>            <div class={styles.singerBox}>              <div class={styles.musicSheetName}>                {state.musicDetail?.musicSheetName}              </div>              <div class={styles.singerName}>                演奏:{state.musicDetail?.username}              </div>            </div>            <Sticky offsetTop={state.heightV - 1 + "px"}>              <div class={[styles.playSection, plyrState.mediaTimeShow && styles.mediaTimeShow,!plyrState.loaded && styles.notLoaded]} id="playMediaSection" onClick={handlerClickPlay}>                {                  state.playType &&                  <>                    {                      state.playType === 'Audio' &&                      <div class={styles.audioBox}>                        <canvas class={styles.audioVisualizer} id="audioVisualizer"></canvas>                        <Vue3Lottie class={styles.audioBga} animationData={audioBga} autoPlay={true} loop={true}></Vue3Lottie>                        <Vue3Lottie class={styles.audioBga1} animationData={audioBga1} autoPlay={true} loop={true}></Vue3Lottie>                        <Vue3Lottie class={styles.audioBga2} animationData={audioBga2} autoPlay={true} loop={true}></Vue3Lottie>                        <audio                          crossorigin="anonymous"                          id="audioMediaSrc"                          src={state.musicDetail?.videoUrl}                          controls="false"                          preload="metadata"                          playsinline                        />                      </div>                    }                    {                      state.playType === 'Video' &&                      <video                        id="videoMediaSrc"                        class={styles.videoBox}                        src={state.musicDetail?.videoUrl}                        data-poster={ state.musicDetail?.videoImg || videobg}                        preload="metadata"                        playsinline                        webkit-playsinline                      />                    }                    <div class={[styles.playLarge, !plyrState.mediaTimeShow && plyrState.playIngShow && styles.playIngShow]}></div>                    <div class={styles.mediaTimeCon}>                      <div class={styles.mediaTime}>                        <div>                          {getSecondRPM(plyrState.currentTime)}                        </div>                        <div class={styles.note}>/</div>                        <div class={styles.duration}>                          {getSecondRPM(plyrState.duration)}                        </div>                      </div>                    </div>                    <div class={styles.landscapeScreen} onClick={handlerLandscapeScreen}></div>                    {/* 谱面 */}                    {                      staffState.staffSrc &&                      <div                        class={[styles.staffBox, staffState.isShow && styles.staffBoxShow]}                        style={                          {                            '--staffBoxHeight':staffState.height                          }                        }                      >                        <div class={styles.mask}></div>                        <iframe                          ref={staffDom}                          class={styles.staff}                          frameborder="0"                          src={staffState.staffSrc}>                        </iframe>                      </div>                    }                  </>                }              </div>            </Sticky>            <div class={[styles.musicSection, styles.musicShareSection]}>              <div class={styles.avatarInfoBox}>                <div class={styles.avatar}>                  <Image class={styles.userLogo} src={state.musicDetail.avatar} />                  <div class={styles.infoCon}>                    <div class={styles.info}>                      <span class={styles.userName}>{state.musicDetail?.username}</span>                      {state.musicDetail.vipFlag && (                        <img src={iconMember} class={styles.iconMember} />                      )}                    </div>                    <div class={styles.sub}>                      {state.musicDetail.subjectName}{' '}                      {getGradeCh(state.musicDetail.currentGradeNum - 1)}                    </div>                  </div>                </div>                <div class={styles.linkes}  onClick={onStarChange}>                  <img src={state.musicDetail.starFlag ? iconZanActive : iconZan} class={styles.iconZan} />                  <span>{state.musicDetail.likeNum}</span>                </div>              </div>              <TextEllipsis class={styles.textEllipsis} rows={2} content={state.musicDetail?.desc} expand-text="展开" collapse-text="收起" />            </div>            <div class={styles.likeSection}>              <div class={styles.likeTitle}>推荐作品</div>              {state.listState.dataShow ? (                <>                  <List                    finished={true}                    finishedText=" "                    class={[styles.container, styles.containerInformation]}                    //onLoad={getList}                    immediateCheck={false}>                    {state.list.map((item: any, index:number) => (                      <Cell                        class={[styles.likeShareItem, index===state.list.length-1&&styles.likeShareItemLast]}                        border={false}                        onClick={() => onDetail(item)}                      >                        {{                          icon: () => (                            <div class={styles.audioImgBox}>                              <img                                src={audioPan}                                class={styles.audioPan}                                crossorigin="anonymous"                              />                              <img                                src={                                  item.img || musicBg                                }                                class={styles.muploader}                                crossorigin="anonymous"                              />                              <img class={styles.imgLabel} src={item.videoUrl?.lastIndexOf('mp4') !== -1 ? videoLabel : audioLabel} />                            </div>                          ),                          title: () => (                            <div class={styles.userInfo}>                              <div class={[styles.musicSheetName,'van-ellipsis']}>{item.musicSheetName}</div>                              <div class={styles.usernameCon}>                                <div class={styles.likeNum}>                                  <img src={iconZanActive} />                                  <span>{item.likeNum}</span>                                </div>                                <div class={[styles.username, 'van-ellipsis']}>{item.username}</div>                              </div>                            </div>                          ),                          value: () => (                            <img src={playImg} class={styles.playImg} />                          )                        }}                      </Cell>                    ))}                  </List>                  {                    (!state.listState.finished || state.params.page>2) &&                      <div class={styles.btnImg}>                        <img onClick={handleChangeList} onTouchstart={()=>{}} src={btnImg} />                      </div>                  }                </>              ) : (                <MEmpty image={"empty2"} description="暂无作品" />              )}            </div>            {              !isScreenScroll.value &&              <MSticky position="bottom" offsetBottom={state.heightB - 1 + "px"} >                <div class={styles.upward}>                  <img src={iconUpward} />                </div>              </MSticky>            }          </>        }        <Popup          v-model:show={state.loginStatus}          style={{ background: 'transparent', overflow: 'inherit' }}>          <LoginModel            onClose={() => (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;              }            }}          />        </Popup>        <Popup          v-model:show={state.loginChangeState}          style={{ background: 'transparent', overflow: 'inherit' }}>          <LoginChangeModel            credential={state.credential}            onClose={() => {              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;            }}          />        </Popup>        <MWxTip          v-model:show={state.messageStatus}          message={state.message}          showButton={false}        />        {          !staffState.isShow && <Loading></Loading>        }      </div>    );  }});
 |