const { createApp, ref, onMounted, nextTick, onUnmounted, computed, watch } = Vue; createApp({ setup() { const trait = [ { icon: "./static/img/headset-icon.png", title: "音乐创作", text: "朴见潮音,2024 年成立于广州,是专注 AI 音乐领域的创新工作室。短短一年便跻身于AI音乐浪潮先锋品牌!", }, { icon: "./static/img/mv-icon.png", title: "MV创作", text: "以 AI 技术为核心,整合音乐创作、MV 制作、教学培训、发行级重制等业务,为创作者和爱好者打造一站式平台。", }, { icon: "./static/img/train-icon.png", title: "教学培训", text: "工作室运用多种 AI 工具,突破传统创作局限,支持流行、摇滚等多元风格创作,满足个性化需求。", }, { icon: "./static/img/remake-icon.png", title: "发行级重制", text: "构建全流程服务体系,从 AI 音乐创作、MV 视觉呈现,到零基础教学,再到发行级重制提升品质,实现作品价值最大化。", }, ]; const audioPlayer = ref(null); const progress = ref(0); // 播放进度百分比 // 响应式变量存储当前播放时间和总时长 const currentTimeFormatted = ref("00:00"); const durationFormatted = ref("00:00"); // 格式化时间函数:将秒数转换为 MM:SS 格式 const formatTime = (seconds) => { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`; }; // 更新进度的函数 const getProgress = () => { if (!audioPlayer.value) return; const currentTime = audioPlayer.value.currentTime || 0; const duration = audioPlayer.value.duration || 0; const p = duration > 0 ? (currentTime / duration) * 100 : 0; setProgress(p, audioPlayer.value.src); progress.value = p; // 更新时间显示 currentTimeFormatted.value = formatTime(currentTime); durationFormatted.value = formatTime(duration); }; const setProgress = (val, src) => { awardAudioList.value.forEach((item) => { if (item.playurl == src) item.progress = val; }); customList.value.forEach((item) => { if (item.playurl == src) item.progress = val; }); studentList.value.forEach((item) => { if (item.playurl == src) item.progress = val; }); if (zeroOrderStudents.value?.playurl == src) { zeroOrderStudents.value["progress"] = val; } }; const introduceRef = ref(null); const worksRef = ref(null); const customRef = ref(null); const studentRef = ref(null); let pointerIndex = ref(0); const albumBoxRef = ref(null); // 点击侧边栏 const changePointer = (index) => { albumBoxRef.value.scrollTo({ top: 448 * index, behavior: "smooth" }); pointerIndex.value = index; }; // 向上滚动到上一页 const scrollToPrevious = () => { let index = pointerIndex.value - 1; if (index < 0) return; changePointer(index); }; // 向下滚动到下一页 const scrollToNext = () => { let index = pointerIndex.value + 1; if (index > bannerList.value.length) return; changePointer(index); }; // 获取当前在可视窗口中的元素ref const visibleRef = computed(() => { const visibleElement = elements.value.find((el) => el.isVisible); return visibleElement ? visibleElement.ref : null; }); let bannerList = ref([]); let awardMVList = ref([]); let awardAudioList = ref([]); const init = () => { ajax("https://pujianchaoyin.com/api/getHomeData").then((res) => { // ajax("https://pujianchaoyin.com/index/api").then((res) => { if (res.code != 200) return; const data = res.data; console.log("data", data); bannerList.value = data.banner; awardMVList.value = data.awardMVList; awardAudioList.value = data.awardAudioList; customList.value = data.customAudioList; studentList.value = data.studentList || []; studentList.value.forEach((item, index) => { item["order"] = index; }); zeroOrderStudents.value = studentList.value[0]; nextTick(() => bannerSwiper()); }); }; let bannerSwiperTimer = null; // 头部轮播图 定时器 const bannerSwiper = () => { clearTimeout(bannerSwiperTimer); bannerSwiperTimer = setTimeout(() => { let index = pointerIndex.value + 1; if (index > bannerList.value.length - 1) index = 0; changePointer(index); bannerSwiper(); }, 3000); }; // 头部轮播图 - 切换 定时器 const changeInterval = (type) => { if (type) clearTimeout(bannerSwiperTimer); else bannerSwiper(); }; onMounted(() => { init(); // 添加进度更新事件监听器 if (audioPlayer.value) { volume.value = audioPlayer.value.volume * 100; audioPlayer.value.addEventListener("timeupdate", getProgress); audioPlayer.value.addEventListener("loadedmetadata", getProgress); } }); // 组件卸载时清理事件监听器 onUnmounted(() => { audioPlayer?.value?.removeEventListener("timeupdate", getProgress); audioPlayer?.value?.removeEventListener("loadedmetadata", getProgress); }); // 播放 组件状态 let previewState = ref(false); let art = null; // 开启播放 MV const openPreview = (id) => { closeAll(); ajax("https://pujianchaoyin.com/api/getMvDetail", { id, }).then((res) => { if (res.code != 200) return; const data = res.data; previewState.value = true; nextTick(() => { art = new Artplayer({ container: ".artplayer-app", url: data.playurl, autoplay: true, poster: data.img || "", fullscreen: true, }); art.play(); }); }); }; // 关闭播放 MV const closePreview = () => { previewState.value = false; art?.destroy(); art = null; }; // 快进 和 后退 10秒 const fastForward = (type = "fast") => { if (!audioPlayer.value) return; const src = playData.value?.playurl || ""; const area = playData.value?.area || ""; console.log(audioPlayer.value.src, src); if (audioPlayer.value.src != src) { manageAudio(src, area); return; } let currentTime = audioPlayer.value.currentTime || 0; const duration = audioPlayer.value.duration || 0; let newTime = 0; if (type == "fast") newTime = Math.min(currentTime + 10, duration); else newTime = Math.max(currentTime - 10, 0); audioPlayer.value.currentTime = newTime; getProgress(); }; // 播放 组件数据 let playData = ref(null); // 管理音频播放 const manageAudio = (src, area) => { const audio = audioPlayer.value; closeAll(); setTimeout(() => { if (audio?.src != src) audio.src = src; audio.play().then(() => { if (area == "works") { awardAudioList.value.forEach((item) => { if (item.playurl == src) { item["state"] = true; playData.value = { ...item, area }; } }); } if (area == "custom") { customList.value.forEach((item) => { if (item.playurl == src) { item["state"] = true; playData.value = { ...item, area }; } }); } if (area == "student") { zeroOrderStudents.value["state"] = true; playData.value = { ...zeroOrderStudents.value, area }; } }); }, 800); }; // 重新播放 const rePlay = () => { if (!playData.value) return; const { playurl, area } = playData.value; manageAudio(playurl, area); }; // 关闭所有播放 const closeAll = () => { audioPlayer.value.pause(); awardAudioList.value.forEach((item) => { item["state"] = false; }); customList.value.forEach((item) => { item["state"] = false; }); zeroOrderStudents.value["state"] = false; playData.value && (playData.value.state = false); }; // 定制音乐 数据 let customList = ref([]); let studentList = ref([]); // 学生数据 let studentIndex = ref(0); // 学生下标 let zeroOrderStudents = ref({}); // 学生 选中播放 数据 // 切换学生 播放 const cutStudent = (index) => { // 找到目标元素和第一个元素 const [target, first] = [studentList.value[index], studentList.value.find((item) => item.order == 0)]; // 交换order值 if (target && first && target !== first) [target.order, first.order] = [first.order, target.order]; zeroOrderStudents.value = target; studentIndex.value = index; target.playurl ? manageAudio(target.playurl, "student") : getPlayUrl(0, "student"); }; // 切换学生 歌曲 上\下一首 const cutSong = (type) => { const listLength = studentList.value.length; const index = studentIndex.value; let newIndex = 0; if (type === "up") newIndex = index - 1 < 0 ? listLength - 1 : index - 1; else if (type === "down") newIndex = index + 1 >= listLength ? 0 : index + 1; cutStudent(newIndex); }; // 响应式数据:音量值、是否静音 let volume = ref(100); // 计算并设置音量百分比 const setVolumePercentage = (percentage) => { const volumePercent = Math.max(0, Math.min(100, percentage)); volume.value = Math.abs(~~volumePercent); // 设置音频元素的音量(范围是0-1) if (audioPlayer.value) audioPlayer.value.volume = volume.value / 100; }; // 处理音量进度条点击 const handleVolumeClick = (event) => { // 获取进度条元素 const progressBar = event.currentTarget; const rect = progressBar.getBoundingClientRect(); const clickPosition = rect.bottom - event.clientY; const percentage = (clickPosition / rect.height) * 100; setVolumePercentage(percentage); }; let volumeShow = ref(false); // 处理音量进度条拖拽 let isDragging = false; const startDrag = (event) => { isDragging = true; handleVolumeDrag(event); // 添加事件监听器 document.addEventListener("mousemove", handleVolumeDrag); document.addEventListener("mouseup", stopDrag); }; const handleVolumeDrag = (event) => { if (!isDragging) return; // 获取音量进度条元素 const progressBar = document.querySelector(".sound-control .progress"); if (!progressBar) return; const rect = progressBar.getBoundingClientRect(); // 计算拖拽位置相对于进度条的比例 let dragPosition = rect.bottom - event.clientY; // 限制在进度条范围内 dragPosition = Math.max(0, Math.min(dragPosition, rect.height)); const percentage = (dragPosition / rect.height) * 100; setVolumePercentage(percentage); }; const stopDrag = () => { isDragging = false; document.removeEventListener("mousemove", handleVolumeDrag); document.removeEventListener("mouseup", stopDrag); }; const handleVolumeShow = () => (volumeShow.value = true); const handleVolumeHide = () => (volumeShow.value = false); onUnmounted(() => { // 确保移除所有拖拽相关事件 document.removeEventListener("mousemove", handleVolumeDrag); document.removeEventListener("mouseup", stopDrag); document.removeEventListener("mousemove", handleBarDragBottomDrag); document.removeEventListener("mouseup", stopBarDragBottom); }); const ajax = (url, data = {}) => { if (["localhost", "127.0.0.1"].includes(location.hostname)) data["authorization"] = "3338bf6a2e53dda872da3664a2560b25"; return new Promise(function (resolve, reject) { axios .post(url, data, { emulateJSON: true, withCredentials: true, }) .then(function (res) { var data = typeof res.data == "string" ? JSON.parse(res.data) : res.data; resolve(data); }); }); }; const ajaxget = (url, data) => { if (!data) data = {}; if (["localhost", "127.0.0.1"].includes(location.hostname)) data["authorization"] = "3338bf6a2e53dda872da3664a2560b25"; return new Promise((resolve, reject) => { axios .get(url, { emulateJSON: true, withCredentials: true, }) .then((res) => { var data = typeof res.data == "string" ? JSON.parse(res.data) : res.data; resolve(data); }); }); }; // 处理音量进度条拖拽 let isBarBottomDragging = false; const startBarDragBottom = (event) => { isBarBottomDragging = true; handleBarDragBottomDrag(event); // 添加事件监听器 document.addEventListener("mousemove", handleBarDragBottomDrag); document.addEventListener("mouseup", stopBarDragBottom); }; const stopBarDragBottom = () => { isBarBottomDragging = false; document.removeEventListener("mousemove", handleBarDragBottomDrag); document.removeEventListener("mouseup", stopBarDragBottom); }; // 直接点击进度条 跳转 const handleBarDragBottomClick = (event) => { // 获取进度条元素 const progressBar = event.currentTarget; const rect = progressBar.getBoundingClientRect(); const clickPosition = event.clientX - rect.left; const percentage = clickPosition / rect.width; // 限制百分比在0-100之间 const clampedPercentage = Math.max(0, Math.min(100, percentage)); updatePlay(clampedPercentage); }; const handleBarDragBottomDrag = (event) => { if (!isBarBottomDragging) return; // 获取音量进度条元素 const progressBar = document.querySelector(".bottom-play .bottom-middle .progress-bar"); if (!progressBar) return; const rect = progressBar.getBoundingClientRect(); // 计算拖拽位置相对于进度条的比例 let dragPosition = event.clientX - rect.left; // 限制在进度条范围内 dragPosition = Math.max(0, Math.min(dragPosition, rect.width)); const percentage = dragPosition / rect.width; updatePlay(percentage); }; const updatePlay = (percentage) => { if (!audioPlayer.value) return; const duration = audioPlayer.value.duration || 0; let newTime = duration * percentage; newTime = Math.max(0, Math.min(duration, newTime)); audioPlayer.value.currentTime = newTime; getProgress(); }; const getPlayUrl = (index, area) => { let id = null; if (area == "student") { const item = zeroOrderStudents.value; id = item.id; } if (area == "custom") { const item = customList.value[index]; id = item.id; } if (area == "works") { const item = awardAudioList.value[index]; id = item.id; } ajax("https://pujianchaoyin.com/api/getMusicDetail", { id, }).then((res) => { if (res.code != 200) return; const data = res.data; if (area == "student") { zeroOrderStudents.value = { ...data, ...zeroOrderStudents.value }; manageAudio(data.playurl, area); } if (area == "custom") { customList.value[index] = { ...data, ...customList.value[index] }; manageAudio(data.playurl, area); } if (area == "works") { awardAudioList.value[index] = { ...data, ...awardAudioList.value[index] }; manageAudio(data.playurl, area); } }); }; const judgmentPlayUrl = (url, area, index) => { if (url) manageAudio(url, area); else getPlayUrl(index, area); }; // 监听 previewState 如果为 true body.style.overflow = 'hidden' watch(previewState, (newVal) => { if (newVal) document.body.style.overflow = "hidden"; else document.body.style.overflow = "auto"; }); return { judgmentPlayUrl, cutSong, getPlayUrl, handleBarDragBottomClick, startBarDragBottom, volumeShow, handleVolumeHide, handleVolumeShow, zeroOrderStudents, rePlay, playData, awardAudioList, changeInterval, awardMVList, bannerList, albumBoxRef, volume, handleVolumeClick, handleVolumeDrag, startDrag, stopDrag, volume, cutStudent, studentList, studentIndex, scrollToPrevious, scrollToNext, changePointer, pointerIndex, visibleRef, studentRef, customRef, formatTime, currentTimeFormatted, durationFormatted, worksRef, introduceRef, customList, closeAll, manageAudio, progress, closePreview, openPreview, previewState, audioPlayer, trait, fastForward }; }, }).mount("#appIndex");