|
@@ -4,7 +4,8 @@ import {
|
|
onMounted,
|
|
onMounted,
|
|
onUnmounted,
|
|
onUnmounted,
|
|
reactive,
|
|
reactive,
|
|
- watch
|
|
|
|
|
|
+ watch,
|
|
|
|
+ ref
|
|
} from 'vue';
|
|
} from 'vue';
|
|
// import WaveSurfer from 'wavesurfer.js';
|
|
// import WaveSurfer from 'wavesurfer.js';
|
|
import styles from './index.module.less';
|
|
import styles from './index.module.less';
|
|
@@ -18,13 +19,20 @@ import {
|
|
Popup,
|
|
Popup,
|
|
Slider,
|
|
Slider,
|
|
showDialog,
|
|
showDialog,
|
|
- showToast
|
|
|
|
|
|
+ showToast,
|
|
|
|
+ Sticky,
|
|
|
|
+ TextEllipsis
|
|
} from 'vant';
|
|
} from 'vant';
|
|
import iconDownload from './images/icon-download.png';
|
|
import iconDownload from './images/icon-download.png';
|
|
import iconShare from './images/icon-share.png';
|
|
import iconShare from './images/icon-share.png';
|
|
import iconDelete from './images/icon-delete.png';
|
|
import iconDelete from './images/icon-delete.png';
|
|
|
|
+import iconEdit from './images/edit.png';
|
|
|
|
+import iconUpward from './images/upward.png';
|
|
import iconMember from './images/icon-member.png';
|
|
import iconMember from './images/icon-member.png';
|
|
import iconZan from './images/icon-zan.png';
|
|
import iconZan from './images/icon-zan.png';
|
|
|
|
+import promptImg from './images/prompt.png';
|
|
|
|
+import confirmImg from './images/confirm.png';
|
|
|
|
+import canceImg from './images/cance.png';
|
|
import iconZanActive from './images/icon-zan-active.png';
|
|
import iconZanActive from './images/icon-zan-active.png';
|
|
import iconPlay from './images/icon-play.png';
|
|
import iconPlay from './images/icon-play.png';
|
|
import iconPause from './images/icon-pause.png';
|
|
import iconPause from './images/icon-pause.png';
|
|
@@ -40,29 +48,26 @@ import MEmpty from '@/components/m-empty';
|
|
import dayjs from 'dayjs';
|
|
import dayjs from 'dayjs';
|
|
import MVideo from '@/components/m-video';
|
|
import MVideo from '@/components/m-video';
|
|
import ShareModel from './share-model';
|
|
import ShareModel from './share-model';
|
|
-import { usePageVisibility } from '@vant/use';
|
|
|
|
-import videoBg from './images/video-bg.png';
|
|
|
|
|
|
+import { usePageVisibility, useEventListener } from '@vant/use';
|
|
|
|
+import "plyr/dist/plyr.css";
|
|
|
|
+import Plyr from "plyr";
|
|
|
|
+import { Vue3Lottie } from "vue3-lottie";
|
|
|
|
+import audioBga from "./images/audioBga.json";
|
|
|
|
+import videobg from "./images/videobg.png";
|
|
|
|
|
|
export default defineComponent({
|
|
export default defineComponent({
|
|
name: 'creation-detail',
|
|
name: 'creation-detail',
|
|
setup() {
|
|
setup() {
|
|
const route = useRoute();
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const router = useRouter();
|
|
- const audioId = 'a' + +Date.now() + Math.floor(Math.random() * 100);
|
|
|
|
-
|
|
|
|
|
|
+ const isScreenScroll = ref(false)
|
|
|
|
+ const lottieDom = ref()
|
|
const state = reactive({
|
|
const state = reactive({
|
|
id: route.query.id,
|
|
id: route.query.id,
|
|
deleteStatus: false,
|
|
deleteStatus: false,
|
|
shareStatus: false,
|
|
shareStatus: false,
|
|
playType: '' as 'Audio' | 'Video' | '', // 播放类型
|
|
playType: '' as 'Audio' | 'Video' | '', // 播放类型
|
|
musicDetail: {} as any,
|
|
musicDetail: {} as any,
|
|
- timer: null as any,
|
|
|
|
- audioWidth: 0,
|
|
|
|
- paused: true,
|
|
|
|
- currentTime: 0,
|
|
|
|
- duration: 0.1,
|
|
|
|
- loop: false,
|
|
|
|
- dragStatus: false, // 是否开始拖动
|
|
|
|
isClick: false,
|
|
isClick: false,
|
|
list: [] as any,
|
|
list: [] as any,
|
|
listState: {
|
|
listState: {
|
|
@@ -73,36 +78,17 @@ export default defineComponent({
|
|
params: {
|
|
params: {
|
|
page: 1,
|
|
page: 1,
|
|
rows: 20
|
|
rows: 20
|
|
- }
|
|
|
|
|
|
+ },
|
|
|
|
+ _plrl: null as any,
|
|
|
|
+ heightV:0,
|
|
|
|
+ heightB:0
|
|
});
|
|
});
|
|
- const audioDom = new Audio();
|
|
|
|
- audioDom.controls = true;
|
|
|
|
- audioDom.style.width = '100%';
|
|
|
|
- audioDom.className = styles.audio;
|
|
|
|
-
|
|
|
|
- /** 改变播放时间 */
|
|
|
|
- const handleChangeTime = (val: number) => {
|
|
|
|
- state.currentTime = val;
|
|
|
|
- clearTimeout(state.timer);
|
|
|
|
- state.timer = setTimeout(() => {
|
|
|
|
- // audioRef.value.currentTime = val;
|
|
|
|
- audioDom.currentTime = val;
|
|
|
|
- state.timer = null;
|
|
|
|
- }, 60);
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- // 切换音频播放
|
|
|
|
- const onToggleAudio = (e: any) => {
|
|
|
|
- e.stopPropagation();
|
|
|
|
- if (audioDom.paused) {
|
|
|
|
- audioDom.play();
|
|
|
|
- } else {
|
|
|
|
- audioDom.pause();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- state.paused = audioDom.paused;
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
|
|
+ const plyrState = reactive({
|
|
|
|
+ duration: 0,
|
|
|
|
+ currentTime: 0,
|
|
|
|
+ mediaTimeShow: false,
|
|
|
|
+ playIngShow: true
|
|
|
|
+ })
|
|
// 获取列表
|
|
// 获取列表
|
|
const getStarList = async () => {
|
|
const getStarList = async () => {
|
|
try {
|
|
try {
|
|
@@ -129,70 +115,10 @@ export default defineComponent({
|
|
state.isClick = false;
|
|
state.isClick = false;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
-
|
|
|
|
- const initAudio = () => {
|
|
|
|
- audioDom.src = state.musicDetail.videoUrl;
|
|
|
|
- audioDom.load();
|
|
|
|
- audioDom.oncanplaythrough = () => {
|
|
|
|
- state.paused = audioDom.paused;
|
|
|
|
- state.duration = audioDom.duration;
|
|
|
|
- };
|
|
|
|
- // 播放时监听
|
|
|
|
- audioDom.addEventListener('timeupdate', () => {
|
|
|
|
- state.duration = audioDom.duration;
|
|
|
|
- state.currentTime = audioDom.currentTime;
|
|
|
|
- const rate = (state.currentTime / state.duration) * 100;
|
|
|
|
- state.audioWidth = rate > 100 ? 100 : rate;
|
|
|
|
- });
|
|
|
|
- audioDom.addEventListener('ended', () => {
|
|
|
|
- state.paused = audioDom.paused;
|
|
|
|
- });
|
|
|
|
- // const wavesurfer = WaveSurfer.create({
|
|
|
|
- // container: document.querySelector(`#${audioId}`) as HTMLElement,
|
|
|
|
- // waveColor: '#fff',
|
|
|
|
- // progressColor: '#2FA1FD',
|
|
|
|
- // url: state.musicDetail.videoUrl,
|
|
|
|
- // cursorWidth: 0,
|
|
|
|
- // height: 35,
|
|
|
|
- // width: 'auto',
|
|
|
|
- // normalize: true,
|
|
|
|
- // // Set a bar width
|
|
|
|
- // barWidth: 2,
|
|
|
|
- // // Optionally, specify the spacing between bars
|
|
|
|
- // barGap: 2,
|
|
|
|
- // // And the bar radius
|
|
|
|
- // barRadius: 4,
|
|
|
|
- // barHeight: 0.6,
|
|
|
|
- // autoScroll: true,
|
|
|
|
- // /** If autoScroll is enabled, keep the cursor in the center of the waveform during playback */
|
|
|
|
- // autoCenter: true,
|
|
|
|
- // hideScrollbar: false,
|
|
|
|
- // media: audioDom
|
|
|
|
- // });
|
|
|
|
-
|
|
|
|
- // wavesurfer.once('interaction', () => {
|
|
|
|
- // // wavesurfer.play();
|
|
|
|
- // });
|
|
|
|
- // wavesurfer.once('ready', () => {
|
|
|
|
- // state.paused = audioDom.paused;
|
|
|
|
- // state.duration = audioDom.duration;
|
|
|
|
- // });
|
|
|
|
-
|
|
|
|
- // wavesurfer.on('finish', () => {
|
|
|
|
- // state.paused = true;
|
|
|
|
- // });
|
|
|
|
-
|
|
|
|
- // // 播放时监听
|
|
|
|
- // audioDom.addEventListener('timeupdate', () => {
|
|
|
|
- // state.currentTime = audioDom.currentTime;
|
|
|
|
- // });
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
// 删除作品
|
|
// 删除作品
|
|
const onDelete = async () => {
|
|
const onDelete = async () => {
|
|
try {
|
|
try {
|
|
await api_userMusicRemove({ id: state.id });
|
|
await api_userMusicRemove({ id: state.id });
|
|
-
|
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
state.deleteStatus = false;
|
|
state.deleteStatus = false;
|
|
showToast('删除成功');
|
|
showToast('删除成功');
|
|
@@ -221,17 +147,184 @@ export default defineComponent({
|
|
}
|
|
}
|
|
});
|
|
});
|
|
};
|
|
};
|
|
-
|
|
|
|
- const pageVisibility = usePageVisibility();
|
|
|
|
- watch(pageVisibility, value => {
|
|
|
|
- if (value === 'hidden') {
|
|
|
|
- if (audioDom) {
|
|
|
|
- audioDom.pause();
|
|
|
|
- state.paused = audioDom.paused;
|
|
|
|
|
|
+ // 滚动事件
|
|
|
|
+ const cleanScrollEvent = useEventListener('scroll', () => {
|
|
|
|
+ const height =
|
|
|
|
+ window.scrollY ||
|
|
|
|
+ document.documentElement.scrollTop
|
|
|
|
+ // 防止多次调用
|
|
|
|
+ if(height > 0 && isScreenScroll.value === false){
|
|
|
|
+ isScreenScroll.value = true
|
|
|
|
+ setStatusBarTextColor(false)
|
|
|
|
+ }
|
|
|
|
+ if(height <= 0){
|
|
|
|
+ isScreenScroll.value = false
|
|
|
|
+ setStatusBarTextColor(true)
|
|
}
|
|
}
|
|
|
|
+ })
|
|
|
|
+ // 设置导航栏颜色
|
|
|
|
+ function setStatusBarTextColor(isWhite:boolean){
|
|
|
|
+ postMessage({
|
|
|
|
+ api: 'setStatusBarTextColor',
|
|
|
|
+ content: { statusBarTextColor: isWhite }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ // 初始化 媒体播放
|
|
|
|
+ function initMediaPlay(){
|
|
|
|
+ const id = state.playType === "Audio" ? "#audioMediaSrc" : "#videoMediaSrc";
|
|
|
|
+ state._plrl = new Plyr(id, {
|
|
|
|
+ controls: ["progress"],
|
|
|
|
+ fullscreen: { enabled: false },
|
|
|
|
+ });
|
|
|
|
+ const player = state._plrl
|
|
|
|
+ // 创建音波数据
|
|
|
|
+ if(state.playType === "Audio"){
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ const audioDom = document.querySelector("#audioMediaSrc") as HTMLAudioElement
|
|
|
|
+ const canvasDom = document.querySelector("#audioVisualizer") as HTMLCanvasElement
|
|
|
|
+ const { pauseVisualDraw, playVisualDraw } = audioVisualDraw(audioDom, canvasDom)
|
|
|
|
+ player.on('play', () => {
|
|
|
|
+ lottieDom.value.play()
|
|
|
|
+ playVisualDraw()
|
|
|
|
+ });
|
|
|
|
+ player.on('pause', () => {
|
|
|
|
+ lottieDom.value.pause()
|
|
|
|
+ pauseVisualDraw()
|
|
|
|
+ });
|
|
|
|
+ }, 300); // 弹窗动画是0.25秒 这里用定时器 确保canvas 能获取到宽高
|
|
|
|
+ }
|
|
|
|
+ player.on("timeupdate", ()=>{
|
|
|
|
+ plyrState.currentTime = player.currentTime
|
|
|
|
+ })
|
|
|
|
+ player.on('play', () => {
|
|
|
|
+ plyrState.playIngShow = false
|
|
|
|
+ });
|
|
|
|
+ player.on('pause', () => {
|
|
|
|
+ plyrState.playIngShow = true
|
|
|
|
+ });
|
|
|
|
+ // 处理按压事件
|
|
|
|
+ const handleStart = () => {
|
|
|
|
+ plyrState.duration = player.duration
|
|
|
|
+ plyrState.mediaTimeShow = true
|
|
|
|
+ };
|
|
|
|
+ // 处理松开事件
|
|
|
|
+ const handleEnd = () => {
|
|
|
|
+ plyrState.mediaTimeShow = false
|
|
|
|
+ };
|
|
|
|
+ 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(){
|
|
|
|
+ if (state._plrl.playing) {
|
|
|
|
+ state._plrl.pause();
|
|
|
|
+ } else {
|
|
|
|
+ state._plrl.play();
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * 音频可视化
|
|
|
|
+ * @param audioDom
|
|
|
|
+ * @param canvasDom
|
|
|
|
+ * @param fftSize 2的幂数,最小为32
|
|
|
|
+ */
|
|
|
|
+ function audioVisualDraw(audioDom: HTMLAudioElement, canvasDom: HTMLCanvasElement, fftSize = 128) {
|
|
|
|
+ type propsType = { canvWidth: number; canvHeight: number; canvFillColor: string; lineColor: string; lineGap: number }
|
|
|
|
+ // canvas
|
|
|
|
+ const canvasCtx = canvasDom.getContext("2d")!
|
|
|
|
+ const { width, height } = canvasDom.getBoundingClientRect()
|
|
|
|
+ canvasDom.width = width
|
|
|
|
+ canvasDom.height = height
|
|
|
|
+ // audio
|
|
|
|
+ const audioCtx = new AudioContext()
|
|
|
|
+ const analyser = audioCtx.createAnalyser()
|
|
|
|
+ const source = audioCtx.createMediaElementSource(audioDom)
|
|
|
|
+ analyser.fftSize = fftSize
|
|
|
|
+ source.connect(analyser)
|
|
|
|
+ analyser.connect(audioCtx.destination)
|
|
|
|
+ const dataArray = new Uint8Array(analyser.frequencyBinCount)
|
|
|
|
+ const draw = (data: Uint8Array, ctx: CanvasRenderingContext2D, { lineGap, canvWidth, canvHeight, canvFillColor, lineColor }: propsType) => {
|
|
|
|
+ if (!ctx) return
|
|
|
|
+ const w = canvWidth
|
|
|
|
+ const h = canvHeight
|
|
|
|
+ fillCanvasBackground(ctx, w, h, canvFillColor)
|
|
|
|
+ // 可视化
|
|
|
|
+ const dataLen = data.length
|
|
|
|
+ const step = (w / 2 - lineGap * dataLen) / dataLen
|
|
|
|
+ const midX = w / 2
|
|
|
|
+ const midY = h / 2
|
|
|
|
+ let xLeft = midX
|
|
|
|
+ for (let i = 0; i < dataLen; i++) {
|
|
|
|
+ const value = data[i]
|
|
|
|
+ const percent = value / 255 // 最大值为255
|
|
|
|
+ const barHeight = percent * midY
|
|
|
|
+ canvasCtx.fillStyle = lineColor
|
|
|
|
+ // 中间加间隙
|
|
|
|
+ if (i === 0) {
|
|
|
|
+ xLeft -= lineGap / 2
|
|
|
|
+ }
|
|
|
|
+ canvasCtx.fillRect(xLeft - step, midY - barHeight, step, barHeight)
|
|
|
|
+ canvasCtx.fillRect(xLeft - step, midY, step, barHeight)
|
|
|
|
+ xLeft -= step + lineGap
|
|
|
|
+ }
|
|
|
|
+ let xRight = midX
|
|
|
|
+ for (let i = 0; i < dataLen; i++) {
|
|
|
|
+ const value = data[i]
|
|
|
|
+ const percent = value / 255 // 最大值为255
|
|
|
|
+ const barHeight = percent * midY
|
|
|
|
+ canvasCtx.fillStyle = lineColor
|
|
|
|
+ if (i === 0) {
|
|
|
|
+ xRight += lineGap / 2
|
|
|
|
+ }
|
|
|
|
+ canvasCtx.fillRect(xRight, midY - barHeight, step, barHeight)
|
|
|
|
+ canvasCtx.fillRect(xRight, midY, step, barHeight)
|
|
|
|
+ xRight += step + lineGap
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ const fillCanvasBackground = (ctx: CanvasRenderingContext2D, w: number, h: number, colors: string) => {
|
|
|
|
+ ctx.clearRect(0, 0, w, h)
|
|
|
|
+ ctx.fillStyle = colors
|
|
|
|
+ ctx.fillRect(0, 0, w, h)
|
|
|
|
+ }
|
|
|
|
+ const requestAnimationFrameFun = () => {
|
|
|
|
+ requestAnimationFrame(() => {
|
|
|
|
+ analyser.getByteFrequencyData(dataArray)
|
|
|
|
+ draw(dataArray, canvasCtx, {
|
|
|
|
+ lineGap: 2,
|
|
|
|
+ canvWidth: width,
|
|
|
|
+ canvHeight: height,
|
|
|
|
+ canvFillColor: "transparent",
|
|
|
|
+ lineColor: "rgba(255, 255, 255, 0.7)"
|
|
|
|
+ })
|
|
|
|
+ if (!isPause) {
|
|
|
|
+ requestAnimationFrameFun()
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ let isPause = true
|
|
|
|
+ const playVisualDraw = () => {
|
|
|
|
+ isPause = false
|
|
|
|
+ audioCtx.resume()
|
|
|
|
+ requestAnimationFrameFun()
|
|
|
|
+ }
|
|
|
|
+ const pauseVisualDraw = () => {
|
|
|
|
+ isPause = true
|
|
|
|
+ audioCtx.suspend()
|
|
|
|
+ }
|
|
|
|
+ return {
|
|
|
|
+ playVisualDraw,
|
|
|
|
+ pauseVisualDraw
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ const pageVisibility = usePageVisibility();
|
|
|
|
+ watch(pageVisibility, value => {
|
|
|
|
+ if (value === 'hidden') {}
|
|
});
|
|
});
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
|
|
+ setStatusBarTextColor(true)
|
|
try {
|
|
try {
|
|
const res = await api_userMusicDetail(state.id);
|
|
const res = await api_userMusicDetail(state.id);
|
|
// console.log(res);
|
|
// console.log(res);
|
|
@@ -259,161 +352,127 @@ export default defineComponent({
|
|
state.playType = 'Video';
|
|
state.playType = 'Video';
|
|
} else {
|
|
} else {
|
|
state.playType = 'Audio';
|
|
state.playType = 'Audio';
|
|
- // 初始化
|
|
|
|
- nextTick(() => {
|
|
|
|
- initAudio();
|
|
|
|
- });
|
|
|
|
}
|
|
}
|
|
|
|
+ nextTick(()=>{
|
|
|
|
+ initMediaPlay()
|
|
|
|
+ })
|
|
} catch {
|
|
} catch {
|
|
//
|
|
//
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|
|
- if (audioDom) {
|
|
|
|
- audioDom.pause();
|
|
|
|
- state.paused = audioDom.paused;
|
|
|
|
- }
|
|
|
|
|
|
+ setStatusBarTextColor(false)
|
|
|
|
+ cleanScrollEvent()
|
|
});
|
|
});
|
|
return () => (
|
|
return () => (
|
|
<div
|
|
<div
|
|
|
|
+ style={
|
|
|
|
+ {
|
|
|
|
+ '--barheight':state.heightV + "px"
|
|
|
|
+ }
|
|
|
|
+ }
|
|
class={[
|
|
class={[
|
|
styles.creation,
|
|
styles.creation,
|
|
- browser().isTablet ? styles.creationTablet : ''
|
|
|
|
|
|
+ browser().isTablet && styles.creationTablet,
|
|
|
|
+ isScreenScroll.value && styles.isScreenScroll
|
|
]}>
|
|
]}>
|
|
- <MSticky position="top">
|
|
|
|
|
|
+ <div class={styles.creationBg}></div>
|
|
|
|
+ <MSticky position="top"
|
|
|
|
+ onBarHeight={(height: any) => {
|
|
|
|
+ console.log(height, 'height', height)
|
|
|
|
+ state.heightV = height
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
<MHeader
|
|
<MHeader
|
|
|
|
+ color={isScreenScroll.value ? "#333333" : "#ffffff"}
|
|
|
|
+ background={isScreenScroll.value ? `rgb(255,255,255` : "transparent"}
|
|
|
|
+ title={state.musicDetail?.musicSheetName}
|
|
border={false}
|
|
border={false}
|
|
isBack={route.query.platformType != 'ANALYSIS'}
|
|
isBack={route.query.platformType != 'ANALYSIS'}
|
|
/>
|
|
/>
|
|
</MSticky>
|
|
</MSticky>
|
|
- <div class={styles.playSection}>
|
|
|
|
- {state.playType === 'Video' && (
|
|
|
|
- <MVideo
|
|
|
|
- class={styles.videoSection}
|
|
|
|
- src={state.musicDetail?.videoUrl}
|
|
|
|
- poster={state.musicDetail?.videoImg || videoBg}
|
|
|
|
- />
|
|
|
|
- )}
|
|
|
|
- {state.playType === 'Audio' && (
|
|
|
|
- <div class={styles.audioSection}>
|
|
|
|
- <div class={styles.audioContainer}>
|
|
|
|
- {/* <div
|
|
|
|
- id={audioId}
|
|
|
|
- onClick={(e: MouseEvent) => {
|
|
|
|
- e.stopPropagation();
|
|
|
|
- }}></div> */}
|
|
|
|
- <div
|
|
|
|
- class={styles.waveActive}
|
|
|
|
- style={{
|
|
|
|
- width: state.audioWidth + '%'
|
|
|
|
- }}></div>
|
|
|
|
- <div class={styles.waveDefault}></div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
|
|
+ <div class={styles.singer}>
|
|
|
|
+ 演奏:{state.musicDetail?.username}
|
|
|
|
+ </div>
|
|
|
|
+ <Sticky offsetTop={state.heightV - 1 + "px"}>
|
|
|
|
+ <div class={[styles.playSection, plyrState.mediaTimeShow && styles.mediaTimeShow]} id="playMediaSection" onClick={handlerClickPlay}>
|
|
|
|
+ {
|
|
|
|
+ state.playType === 'Audio' &&
|
|
<div class={styles.audioBox}>
|
|
<div class={styles.audioBox}>
|
|
- <div
|
|
|
|
- class={[styles.audioPan, state.paused && styles.imgRotate]}>
|
|
|
|
- <Image class={styles.audioImg} src={state.musicDetail?.img} />
|
|
|
|
- </div>
|
|
|
|
- <i class={styles.audioPoint}></i>
|
|
|
|
- <i
|
|
|
|
- class={[styles.audioZhen, state.paused && styles.active]}></i>
|
|
|
|
|
|
+ <canvas class={styles.audioVisualizer} id="audioVisualizer"></canvas>
|
|
|
|
+ <Vue3Lottie ref={lottieDom} class={styles.audioBga} animationData={audioBga} autoPlay={false} loop={true}></Vue3Lottie>
|
|
|
|
+ <audio
|
|
|
|
+ crossorigin="anonymous"
|
|
|
|
+ id="audioMediaSrc"
|
|
|
|
+ src={state.musicDetail?.videoUrl}
|
|
|
|
+ controls="false"
|
|
|
|
+ preload="metadata"
|
|
|
|
+ playsinline
|
|
|
|
+ />
|
|
</div>
|
|
</div>
|
|
- <div
|
|
|
|
- class={[styles.controls]}
|
|
|
|
- onClick={(e: Event) => {
|
|
|
|
- e.stopPropagation();
|
|
|
|
- }}
|
|
|
|
- onTouchmove={(e: TouchEvent) => {
|
|
|
|
- // emit('close');
|
|
|
|
- }}>
|
|
|
|
- <div class={styles.actions}>
|
|
|
|
- <div class={styles.actionBtn} onClick={onToggleAudio}>
|
|
|
|
- <img src={state.paused ? iconPlay : iconPause} />
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div class={[styles.slider]}>
|
|
|
|
- <Slider
|
|
|
|
- step={0.01}
|
|
|
|
- class={styles.timeProgress}
|
|
|
|
- v-model={state.currentTime}
|
|
|
|
- max={state.duration}
|
|
|
|
- onUpdate:modelValue={val => {
|
|
|
|
- handleChangeTime(val);
|
|
|
|
- }}
|
|
|
|
- onDragStart={() => {
|
|
|
|
- state.dragStatus = true;
|
|
|
|
- console.log('onDragStart');
|
|
|
|
- }}
|
|
|
|
- onDragEnd={() => {
|
|
|
|
- state.dragStatus = false;
|
|
|
|
- console.log('onDragEnd');
|
|
|
|
- }}
|
|
|
|
- />
|
|
|
|
- </div>
|
|
|
|
- <div class={styles.time}>
|
|
|
|
- <div>{getSecondRPM(state.currentTime)}</div>
|
|
|
|
- <span>/</span>
|
|
|
|
- <div>{getSecondRPM(state.duration)}</div>
|
|
|
|
- </div>
|
|
|
|
|
|
+ }
|
|
|
|
+ {
|
|
|
|
+ state.playType === 'Video' &&
|
|
|
|
+ <video
|
|
|
|
+ id="videoMediaSrc"
|
|
|
|
+ class={styles.videoBox}
|
|
|
|
+ src={state.musicDetail?.videoUrl}
|
|
|
|
+ data-poster={videobg}
|
|
|
|
+ preload="metadata"
|
|
|
|
+ playsinline
|
|
|
|
+ />
|
|
|
|
+ }
|
|
|
|
+ <div class={[styles.playLarge, plyrState.playIngShow && styles.playIngShow]}></div>
|
|
|
|
+ <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>
|
|
- )}
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <Cell class={styles.userSection} center border={false}>
|
|
|
|
- {{
|
|
|
|
- icon: () => (
|
|
|
|
|
|
+ </div>
|
|
|
|
+ </Sticky>
|
|
|
|
+ <div class={styles.musicSection}>
|
|
|
|
+ <div class={styles.avatarInfoBox}>
|
|
|
|
+ <div class={styles.avatar}>
|
|
<Image class={styles.userLogo} src={state.musicDetail.avatar} />
|
|
<Image class={styles.userLogo} src={state.musicDetail.avatar} />
|
|
- ),
|
|
|
|
- title: () => (
|
|
|
|
- <div class={styles.userInfo}>
|
|
|
|
- <p class={styles.name}>
|
|
|
|
- <span>{state.musicDetail?.username}</span>
|
|
|
|
|
|
+ <div class={styles.infoCon}>
|
|
|
|
+ <div class={styles.info}>
|
|
|
|
+ <span class={styles.userName}>{state.musicDetail?.username}</span>
|
|
{state.musicDetail.vipFlag && (
|
|
{state.musicDetail.vipFlag && (
|
|
<img src={iconMember} class={styles.iconMember} />
|
|
<img src={iconMember} class={styles.iconMember} />
|
|
)}
|
|
)}
|
|
- </p>
|
|
|
|
- <p class={styles.sub}>
|
|
|
|
|
|
+ </div>
|
|
|
|
+ <div class={styles.sub}>
|
|
{state.musicDetail.subjectName}{' '}
|
|
{state.musicDetail.subjectName}{' '}
|
|
{getGradeCh(state.musicDetail.currentGradeNum - 1)}
|
|
{getGradeCh(state.musicDetail.currentGradeNum - 1)}
|
|
- </p>
|
|
|
|
- </div>
|
|
|
|
- ),
|
|
|
|
- value: () => (
|
|
|
|
- <div class={[styles.zan, styles.zanActive]}>
|
|
|
|
- <img src={iconZanActive} class={styles.iconZan} />
|
|
|
|
- {state.musicDetail.likeNum}
|
|
|
|
|
|
+ </div>
|
|
</div>
|
|
</div>
|
|
- )
|
|
|
|
- }}
|
|
|
|
- </Cell>
|
|
|
|
-
|
|
|
|
- <div class={styles.musicSection}>
|
|
|
|
- <div class={styles.musicName}>
|
|
|
|
- <span class={styles.musicTag}>曲目名称</span>
|
|
|
|
- {state.musicDetail?.musicSheetName}
|
|
|
|
|
|
+ </div>
|
|
|
|
+ <div class={styles.linkes}>
|
|
|
|
+ <img src={iconZan} class={styles.iconZan} />
|
|
|
|
+ <span>{state.musicDetail.likeNum}</span>
|
|
|
|
+ </div>
|
|
</div>
|
|
</div>
|
|
- {state.musicDetail.desc && (
|
|
|
|
- <div class={styles.musicDesc}>{state.musicDetail.desc}</div>
|
|
|
|
- )}
|
|
|
|
|
|
+ <TextEllipsis class={styles.textEllipsis} rows={2} content={state.musicDetail?.desc} expand-text="展开" collapse-text="收起" />
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class={styles.likeSection}>
|
|
<div class={styles.likeSection}>
|
|
<div class={styles.likeTitle}>点赞记录</div>
|
|
<div class={styles.likeTitle}>点赞记录</div>
|
|
-
|
|
|
|
{state.listState.dataShow ? (
|
|
{state.listState.dataShow ? (
|
|
<List
|
|
<List
|
|
finished={state.listState.finished}
|
|
finished={state.listState.finished}
|
|
finishedText=" "
|
|
finishedText=" "
|
|
- class={[styles.container, styles.containerInformation]}
|
|
|
|
onLoad={getStarList}
|
|
onLoad={getStarList}
|
|
immediateCheck={false}>
|
|
immediateCheck={false}>
|
|
{state.list.map((item: any, index: number) => (
|
|
{state.list.map((item: any, index: number) => (
|
|
<Cell
|
|
<Cell
|
|
- class={styles.likeItem}
|
|
|
|
- border={state.list.length - 1 == index ? false : true}>
|
|
|
|
|
|
+ class={[styles.likeItem, index===state.list.length-1&&styles.likeItemLast]}
|
|
|
|
+ border={false}
|
|
|
|
+ >
|
|
{{
|
|
{{
|
|
icon: () => (
|
|
icon: () => (
|
|
<Image src={item.userAvatar} class={styles.userLogo} />
|
|
<Image src={item.userAvatar} class={styles.userLogo} />
|
|
@@ -437,11 +496,21 @@ export default defineComponent({
|
|
))}
|
|
))}
|
|
</List>
|
|
</List>
|
|
) : (
|
|
) : (
|
|
- <MEmpty description="暂无数据" />
|
|
|
|
|
|
+ <MEmpty class={styles.mEmpty} description="暂无数据" />
|
|
)}
|
|
)}
|
|
</div>
|
|
</div>
|
|
-
|
|
|
|
- <MSticky position="bottom">
|
|
|
|
|
|
+ {
|
|
|
|
+ !isScreenScroll.value &&
|
|
|
|
+ <MSticky position="bottom" offsetBottom={state.heightB - 1 + "px"} >
|
|
|
|
+ <div class={styles.upward}>
|
|
|
|
+ <img src={iconUpward} />
|
|
|
|
+ </div>
|
|
|
|
+ </MSticky>
|
|
|
|
+ }
|
|
|
|
+ <MSticky position="bottom" onBarHeight={(height: any) => {
|
|
|
|
+ console.log(height, 'height', height)
|
|
|
|
+ state.heightB = height
|
|
|
|
+ }}>
|
|
<div class={styles.bottomSection}>
|
|
<div class={styles.bottomSection}>
|
|
<div class={styles.bottomShare}>
|
|
<div class={styles.bottomShare}>
|
|
<p onClick={onDownload}>
|
|
<p onClick={onDownload}>
|
|
@@ -457,10 +526,8 @@ export default defineComponent({
|
|
<span>删除</span>
|
|
<span>删除</span>
|
|
</p>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
- <Button
|
|
|
|
- round
|
|
|
|
|
|
+ <img src={iconEdit}
|
|
class={styles.btnEdit}
|
|
class={styles.btnEdit}
|
|
- type="primary"
|
|
|
|
onClick={() => {
|
|
onClick={() => {
|
|
router.push({
|
|
router.push({
|
|
path: '/creation-edit',
|
|
path: '/creation-edit',
|
|
@@ -468,25 +535,28 @@ export default defineComponent({
|
|
id: state.id
|
|
id: state.id
|
|
}
|
|
}
|
|
});
|
|
});
|
|
- }}>
|
|
|
|
- 编辑
|
|
|
|
- </Button>
|
|
|
|
|
|
+ }}
|
|
|
|
+ />
|
|
</div>
|
|
</div>
|
|
</MSticky>
|
|
</MSticky>
|
|
|
|
|
|
<Popup
|
|
<Popup
|
|
v-model:show={state.deleteStatus}
|
|
v-model:show={state.deleteStatus}
|
|
|
|
+ overlay-style={
|
|
|
|
+ {
|
|
|
|
+ backgroundColor:"rgba(0,0,0,.5)"
|
|
|
|
+ }
|
|
|
|
+ }
|
|
round
|
|
round
|
|
class={styles.popupContainer}>
|
|
class={styles.popupContainer}>
|
|
- <p class={styles.popupContent}>确定删除吗?</p>
|
|
|
|
- <div class={styles.popupBtnGroup}>
|
|
|
|
- <Button round onClick={() => (state.deleteStatus = false)}>
|
|
|
|
- 取消
|
|
|
|
- </Button>
|
|
|
|
- <Button round type="primary" onClick={onDelete}>
|
|
|
|
- 确定
|
|
|
|
- </Button>
|
|
|
|
- </div>
|
|
|
|
|
|
+ <img class={styles.prompt} src={promptImg} />
|
|
|
|
+ <div class={styles.deleteBox}>
|
|
|
|
+ <p class={styles.popupContent}>确定删除吗?</p>
|
|
|
|
+ <div class={styles.popupBtnGroup}>
|
|
|
|
+ <img src={canceImg} onClick={() => (state.deleteStatus = false)} />
|
|
|
|
+ <img src={confirmImg} onClick={onDelete} />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
</Popup>
|
|
</Popup>
|
|
|
|
|
|
<Popup
|
|
<Popup
|
|
@@ -494,6 +564,7 @@ export default defineComponent({
|
|
v-model:show={state.shareStatus}
|
|
v-model:show={state.shareStatus}
|
|
style={{ background: 'transparent' }}>
|
|
style={{ background: 'transparent' }}>
|
|
<ShareModel
|
|
<ShareModel
|
|
|
|
+ playType={state.playType}
|
|
musicDetail={state.musicDetail}
|
|
musicDetail={state.musicDetail}
|
|
onClose={() => (state.shareStatus = false)}
|
|
onClose={() => (state.shareStatus = false)}
|
|
/>
|
|
/>
|