532 lines
19 KiB
JavaScript
532 lines
19 KiB
JavaScript
const { createApp, ref, onMounted, nextTick, onUnmounted, computed } = Vue;
|
||
const search = 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 || "";
|
||
|
||
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;
|
||
|
||
console.log("src, area", src, area);
|
||
|
||
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 };
|
||
}
|
||
});
|
||
}, 500);
|
||
};
|
||
|
||
// 重新播放
|
||
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);
|
||
};
|
||
|
||
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");
|