Merge pull request #229 from first19326/dev

feat: 替换 vue3-aplayer 插件,并添加播放器设置选项
This commit is contained in:
底层用户 2024-01-02 15:59:16 +08:00 committed by GitHub
commit e2290042f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 324 additions and 499 deletions

View File

@ -14,6 +14,7 @@
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix"
},
"dependencies": {
"@worstone/vue-aplayer": "^1.0.4",
"aplayer": "^1.10.1",
"axios": "^1.1.3",
"element-plus": "^2.2.18",
@ -21,8 +22,7 @@
"pinia": "^2.0.23",
"pinia-plugin-persistedstate": "^3.0.0",
"swiper": "^9.3.2",
"vue": "^3.3.4",
"vue3-aplayer": "^1.7.3"
"vue": "^3.3.4"
},
"devDependencies": {
"@icon-park/vue-next": "^1.4.2",

File diff suppressed because it is too large Load Diff

View File

@ -22,18 +22,18 @@ export const getPlayerList = async (server, type, id) => {
).replace("http://", "https://");
return data.map((v, i) => ({
title: v.name || v.title,
name: v.name || v.title,
artist: v.artist || v.author,
src: domain + jsonpData.req_0.data.midurlinfo[i].purl,
pic: v.pic,
url: domain + jsonpData.req_0.data.midurlinfo[i].purl,
cover: v.cover || v.pic,
lrc: v.lrc,
}));
} else {
return data.map((v) => ({
title: v.name || v.title,
name: v.name || v.title,
artist: v.artist || v.author,
src: v.url,
pic: v.pic,
url: v.url,
cover: v.cover || v.pic,
lrc: v.lrc,
}));
}

View File

@ -43,7 +43,7 @@
</div>
<!-- 音乐列表弹窗 -->
<Transition name="fade" mode="out-in">
<div class="music-list" v-show="musicListShow" @click="musicListShow = false">
<div class="music-list" v-show="musicListShow" @click="closeMusicList()">
<Transition name="zoom">
<div class="list" v-show="musicListShow" @click.stop>
<close-one
@ -51,7 +51,7 @@
theme="filled"
size="28"
fill="#ffffff60"
@click="musicListShow = false"
@click="closeMusicList()"
/>
<Player
ref="playerRef"
@ -59,7 +59,6 @@
:songType="playerData.type"
:songId="playerData.id"
:volume="volumeNum"
:shuffle="false"
/>
</div>
</Transition>
@ -98,6 +97,13 @@ const playerData = reactive({
//
const openMusicList = () => {
musicListShow.value = true;
playerRef.value.toggleList();
};
//
const closeMusicList = () => {
musicListShow.value = false;
playerRef.value.toggleList();
};
//

View File

@ -1,21 +1,21 @@
<template>
<aplayer
showLrc
ref="player"
<APlayer
v-if="playList[0]"
:music="playList[playIndex]"
:list="playList"
:autoplay="autoplay"
ref="player"
:audio="playList"
:autoplay="store.playerAutoplay"
:theme="theme"
:repeat="repeat"
:shuffle="shuffle"
:listMaxHeight="listMaxHeight"
:listFolded="listFolded"
:autoSwitch="false"
:loop="store.playerLoop"
:order="store.playerOrder"
:volume="volume"
:showLrc="true"
:listFolded="listFolded"
:listMaxHeight="listMaxHeight"
:noticeSwitch="false"
@play="onPlay"
@pause="onPause"
@timeupdate="onTimeUp"
@onSelectSong="onSelectSong"
@error="loadMusicError"
/>
</template>
@ -24,7 +24,7 @@
import { MusicOne, PlayWrong } from "@icon-park/vue-next";
import { getPlayerList } from "@/api";
import { mainStore } from "@/store";
import aplayer from "vue3-aplayer";
import APlayer from "@worstone/vue-aplayer";
const store = mainStore();
@ -36,32 +36,14 @@ const playList = ref([]);
//
const playIndex = ref(0);
const playListCount = ref(0);
const skipTimeout = ref(null);
//
const props = defineProps({
//
autoplay: {
type: Boolean,
default: false,
},
//
theme: {
type: String,
default: "#efefef",
},
//
repeat: {
type: String,
default: "list", //'list' | 'music' | 'none'
},
//
shuffle: {
type: Boolean,
default: false,
},
//
volume: {
type: Number,
@ -92,39 +74,28 @@ const props = defineProps({
},
//
listMaxHeight: {
type: String,
default: "420px",
type: Number,
default: 420,
},
});
const listHeight = computed(() => {
return props.listMaxHeight + "px";
});
//
onMounted(() => {
nextTick(() => {
try {
getPlayerList(props.songServer, props.songType, props.songId).then((res) => {
console.log(res);
//
playIndex.value = Math.floor(Math.random() * res.length);
playListCount.value = res.length;
//
store.musicIsOk = true;
//
res.forEach((v) => {
playList.value.push({
title: v.name || v.title,
artist: v.artist || v.author,
src: v.url || v.src,
pic: v.pic,
lrc: v.lrc,
});
});
console.log(
"音乐加载完成",
playList.value,
playIndex.value,
playListCount.value,
props.volume,
);
playList.value = res;
console.log("音乐加载完成");
console.log(playList.value);
console.log(playIndex.value, playList.value.length, props.volume);
});
} catch (err) {
console.error(err);
@ -144,10 +115,11 @@ onMounted(() => {
//
const onPlay = () => {
console.log("播放");
playIndex.value = player.value.aplayer.index;
//
store.setPlayerState(player.value.audio.paused);
store.setPlayerState(player.value.audioRef.paused);
//
store.setPlayerData(player.value.currentMusic.title, player.value.currentMusic.artist);
store.setPlayerData(playList.value[playIndex.value].name, playList.value[playIndex.value].artist);
ElMessage({
message: store.getPlayerData.name + " - " + store.getPlayerData.artist,
grouping: true,
@ -160,19 +132,23 @@ const onPlay = () => {
//
const onPause = () => {
store.setPlayerState(player.value.audio.paused);
store.setPlayerState(player.value.audioRef.paused);
};
//
const onTimeUp = () => {
let playerRef = player.value.$.vnode.el;
if (playerRef) {
const currentLrcElement = playerRef.querySelector(".aplayer-lrc-current");
const previousLrcElement = currentLrcElement?.previousElementSibling;
const lrcContent =
currentLrcElement?.innerHTML || previousLrcElement?.innerHTML || "这句没有歌词";
store.setPlayerLrc(lrcContent);
let lyrics = player.value.aplayer.lyrics[playIndex.value];
let lyricIndex = player.value.aplayer.lyricIndex;
if (!lyrics || !lyrics[lyricIndex]) {
return;
}
let lrc = lyrics[lyricIndex][1];
if (lrc === "Loading") {
lrc = "歌词加载中";
} else if (lrc === "Not available") {
lrc = "歌词加载失败";
}
store.setPlayerLrc(lrc);
};
//
@ -182,41 +158,27 @@ const playToggle = () => {
//
const changeVolume = (value) => {
player.value.audio.volume = value;
};
const onSelectSong = (val) => {
console.log(val);
player.value.setVolume(value, false);
};
//
const changeSong = (type) => {
playIndex.value = player.value.playIndex;
playIndex.value += type ? 1 : -1;
// /
if (playIndex.value < 0) {
playIndex.value = playListCount.value - 1;
} else if (playIndex.value >= playListCount.value) {
playIndex.value = 0;
}
// console.log(playIndex.value, playList.value[playIndex.value]);
type === 0 ? player.value.skipBack() : player.value.skipForward();
nextTick(() => {
player.value.play();
});
};
//
const toggleList = () => {
player.value.toggleList();
};
//
const loadMusicError = () => {
let notice = "";
if (playList.value.length > 1) {
notice = "播放歌曲出现错误,播放器将在 2s 后进行下一首";
//
skipTimeout.value = setTimeout(() => {
changeSong(1);
if (!player.value.audio.paused) {
onPlay();
}
}, 2000);
} else {
notice = "播放歌曲出现错误";
}
@ -229,24 +191,22 @@ const loadMusicError = () => {
duration: 2000,
}),
});
console.error("播放歌曲: " + player.value.currentMusic.title + " 出现错误");
console.error(
"播放歌曲: " + player.value.aplayer.audio[player.value.aplayer.index].name + " 出现错误",
);
};
//
defineExpose({ playToggle, changeVolume, changeSong });
onBeforeUnmount(() => {
clearTimeout(skipTimeout.value);
});
defineExpose({ playToggle, changeVolume, changeSong, toggleList });
</script>
<style lang="scss" scoped>
.aplayer {
width: 80%;
background: transparent;
border-radius: 6px;
font-family: "HarmonyOS_Regular", sans-serif !important;
:deep(.aplayer-body) {
background-color: transparent;
.aplayer-pic {
display: none;
}
@ -268,8 +228,8 @@ onBeforeUnmount(() => {
}
.aplayer-lrc {
text-align: left;
margin: 4px 0 6px 6px;
height: 100%;
margin: 7px 0 6px 6px;
height: 44px;
mask: linear-gradient(
#fff 15%,
#fff 85%,
@ -301,6 +261,8 @@ onBeforeUnmount(() => {
}
:deep(.aplayer-list) {
margin-top: 6px;
height: v-bind(listHeight);
background-color: transparent;
ol {
&::-webkit-scrollbar-track {
background-color: transparent;

View File

@ -50,7 +50,34 @@
</div>
</el-collapse-item>
<el-collapse-item title="播放器配置" name="3">
<div>设置内容待增加</div>
<div class="item">
<span class="text">自动播放</span>
<el-switch
v-model="playerAutoplay"
inline-prompt
:active-icon="CheckSmall"
:inactive-icon="CloseSmall"
/>
</div>
<div class="item">
<span class="text">随机播放</span>
<el-switch
v-model="playerOrder"
inline-prompt
:active-icon="CheckSmall"
:inactive-icon="CloseSmall"
active-value="random"
inactive-value="list"
/>
</div>
<div class="item">
<span class="text">循环模式</span>
<el-radio-group v-model="playerLoop" size="small" text-color="#FFFFFF">
<el-radio label="all" border>列表</el-radio>
<el-radio label="one" border>单曲</el-radio>
<el-radio label="none" border>不循环</el-radio>
</el-radio-group>
</div>
</el-collapse-item>
<el-collapse-item title="其他设置" name="4">
<div>设置内容待增加</div>
@ -65,7 +92,16 @@ import { mainStore } from "@/store";
import { storeToRefs } from "pinia";
const store = mainStore();
const { coverType, siteStartShow, musicClick, playerLrcShow, footerBlur } = storeToRefs(store);
const {
coverType,
siteStartShow,
musicClick,
playerLrcShow,
footerBlur,
playerAutoplay,
playerOrder,
playerLoop,
} = storeToRefs(store);
//
const activeName = ref("1");
@ -107,13 +143,23 @@ const radioChange = () => {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
font-size: 14px;
.el-switch__core {
border-color: transparent;
background-color: #ffffff30;
}
.el-radio-group {
.el-radio {
margin: 2px 10px 2px 0;
border-radius: 5px;
&:last-child {
margin-right: 0;
}
}
}
}
.bg-set {
.el-radio-group {
justify-content: space-between;
@ -152,6 +198,5 @@ const radioChange = () => {
}
}
}
}
}
</style>

View File

@ -22,6 +22,9 @@ export const mainStore = defineStore("main", {
playerLrc: "歌词加载中", // 当前播放歌词
playerLrcShow: true, // 是否显示底栏歌词
footerBlur: true, // 底栏模糊
playerAutoplay: false, // 是否自动播放
playerLoop: "all", // 循环播放 "all", "one", "none"
playerOrder: "list", // 循环顺序 "list", "random"
};
},
getters: {
@ -82,6 +85,9 @@ export const mainStore = defineStore("main", {
"musicClick",
"playerLrcShow",
"footerBlur",
"playerAutoplay",
"playerLoop",
"playerOrder",
],
},
});