feat: enhance playback speed controls with slider and improve null safety for playMusic

This commit is contained in:
Qumo
2025-07-24 08:22:03 +02:00
parent c08c2cbf19
commit 8fb382e21f
2 changed files with 46 additions and 29 deletions

View File

@@ -67,15 +67,26 @@
<i class="ri-close-line"></i>
</div>
<h3>{{ t('player.playBar.playbackSpeed') }}</h3>
<div class="speed-options">
<div
v-for="option in playbackRateOptions"
:key="option.key"
class="speed-option"
:class="{ active: playbackRate === option.key }"
@click="selectSpeed(option.key)"
>
{{ option.label }}
<div class="speed-controls">
<div class="speed-options">
<div
v-for="option in playbackRateOptions"
:key="option.key"
class="speed-option"
:class="{ 'active': playbackRate === option.key }"
@click="selectSpeed(option.key)"
>
{{ option.label }}
</div>
</div>
<div class="speed-slider">
<n-slider
:value="playbackRate"
:min="0.25"
:max="2.0"
:step="0.01"
@update:value="selectSpeed"
/>
</div>
</div>
</div>
@@ -83,7 +94,7 @@
</template>
<script lang="ts" setup>
import { DropdownOption } from 'naive-ui';
import { DropdownOption, NSlider } from 'naive-ui';
import { computed, h, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
@@ -199,7 +210,6 @@ const handleSelect = (key: string) => {
// 选择播放速度
const selectSpeed = (speed: number) => {
playerStore.setPlaybackRate(speed);
showSpeedModal.value = false;
};
</script>
@@ -278,18 +288,22 @@ const selectSpeed = (speed: number) => {
@apply text-lg font-medium mb-4 text-center;
}
.speed-controls {
@apply my-8 mx-4;
}
.speed-options {
@apply flex flex-wrap justify-center gap-4 my-8 mx-4;
.speed-option {
@apply py-2 px-4 rounded-full cursor-pointer transition-all;
@apply bg-gray-100 dark:bg-gray-800;
@apply hover:bg-green-100 dark:hover:bg-green-900;
&.active {
@apply bg-green-500 text-white;
}
}
@apply flex flex-wrap justify-center gap-4;
}
.speed-slider {
@apply mt-4;
}
.speed-option {
@apply py-2 px-4 rounded-full cursor-pointer transition-all;
@apply bg-gray-100 dark:bg-gray-800;
@apply hover:bg-green-100 dark:hover:bg-green-900;
}
.speed-option.active {
@apply bg-green-500 text-white;
}
}

View File

@@ -58,7 +58,7 @@
<div class="music-content">
<div class="music-content-title flex items-center">
<n-ellipsis class="text-ellipsis" line-clamp="1">
{{ playMusic.name }}
{{ playMusic?.name || '' }}
</n-ellipsis>
<span v-if="playbackRate !== 1.0" class="playback-rate-badge"> {{ playbackRate }}x </span>
</div>
@@ -123,15 +123,15 @@
<template #trigger>
<i
class="iconfont ri-netease-cloud-music-line"
:class="{ 'text-green-500': isLyricWindowOpen, 'disabled-icon': !playMusic.id }"
@click="playMusic.id && openLyricWindow()"
:class="{ 'text-green-500': isLyricWindowOpen, 'disabled-icon': !(playMusic?.id) }"
@click="playMusic?.id && openLyricWindow()"
></i>
</template>
{{ playMusic.id ? t('player.playBar.lyric') : t('player.playBar.noSongPlaying') }}
{{ playMusic?.id ? t('player.playBar.lyric') : t('player.playBar.noSongPlaying') }}
</n-tooltip>
<n-tooltip v-if="playMusic.id && isElectron" trigger="hover" :z-index="9999999">
<n-tooltip v-if="playMusic?.id && isElectron" trigger="hover" :z-index="9999999">
<template #trigger>
<reparse-popover v-if="playMusic.id" />
<reparse-popover v-if="playMusic?.id" />
</template>
{{ t('player.playBar.reparse') }}
</n-tooltip>
@@ -191,7 +191,9 @@ const background = ref('#000');
watch(
() => playerStore.playMusic,
async () => {
background.value = playMusic.value.backgroundColor as string;
if (playMusic && playMusic.value && playMusic.value.backgroundColor) {
background.value = playMusic.value.backgroundColor as string;
}
},
{ immediate: true, deep: true }
);
@@ -359,6 +361,7 @@ const setMusicFull = () => {
};
const isFavorite = computed(() => {
if (!playMusic || !playMusic.value) return false;
// 对于B站视频使用ID匹配函数
if (playMusic.value.source === 'bilibili' && playMusic.value.bilibiliData?.bvid) {
return playerStore.favoriteList.some((id) => isBilibiliIdMatch(id, playMusic.value.id));