2025-06-28 17:40:57 +08:00
|
|
|
<template>
|
|
|
|
|
<div class="play-bar" :class="{ 'dark-theme': isDarkMode }" ref="playBarRef">
|
|
|
|
|
<div class="container">
|
|
|
|
|
<!-- 顶部进度条和时间 -->
|
|
|
|
|
<div class="top-section">
|
|
|
|
|
<!-- 进度条 -->
|
|
|
|
|
<div class="progress-bar" @click="handleProgressClick">
|
|
|
|
|
<div class="progress-track"></div>
|
|
|
|
|
<div class="progress-fill" :style="{ width: `${(nowTime / allTime) * 100}%` }"></div>
|
|
|
|
|
</div>
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
<!-- 时间显示 -->
|
|
|
|
|
<div class="time-display">
|
|
|
|
|
<span class="current-time">{{ formatTime(nowTime) }}</span>
|
|
|
|
|
<span class="total-time">{{ formatTime(allTime) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
<!-- 主控制区域 -->
|
|
|
|
|
<div class="controls-section">
|
|
|
|
|
<div class="left-controls">
|
|
|
|
|
<button class="control-btn small-btn" @click="togglePlayMode">
|
|
|
|
|
<i class="iconfont" :class="playModeIcon"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
<div class="center-controls">
|
|
|
|
|
<!-- 上一首 -->
|
|
|
|
|
<button class="control-btn" @click="handlePrev">
|
|
|
|
|
<i class="iconfont icon-prev"></i>
|
|
|
|
|
</button>
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
<!-- 播放/暂停 -->
|
|
|
|
|
<button class="control-btn play-btn" @click="playMusicEvent">
|
|
|
|
|
<i class="iconfont" :class="play ? 'icon-stop' : 'icon-play'"></i>
|
|
|
|
|
</button>
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
<!-- 下一首 -->
|
|
|
|
|
<button class="control-btn" @click="handleNext">
|
|
|
|
|
<i class="iconfont icon-next"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
<div class="right-controls">
|
|
|
|
|
<!-- 播放列表按钮 -->
|
|
|
|
|
<button class="control-btn small-btn" @click="openPlayListDrawer">
|
|
|
|
|
<i class="iconfont icon-list"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
<!-- 底部控制区域 -->
|
|
|
|
|
<div class="bottom-section">
|
|
|
|
|
<div class="spacer"></div>
|
|
|
|
|
<!-- 音量控制 -->
|
|
|
|
|
<div class="volume-control">
|
|
|
|
|
<i class="iconfont" :class="getVolumeIcon" @click="mute"></i>
|
|
|
|
|
<div class="volume-slider">
|
2025-07-23 23:54:35 +08:00
|
|
|
<n-slider
|
|
|
|
|
v-model:value="volumeSlider"
|
|
|
|
|
:step="1"
|
2025-06-28 17:40:57 +08:00
|
|
|
:tooltip="false"
|
|
|
|
|
@wheel.prevent="handleVolumeWheel"
|
|
|
|
|
></n-slider>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-07-23 23:54:35 +08:00
|
|
|
import { computed, onMounted, ref, watch } from 'vue';
|
|
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
import { allTime, nowTime, playMusic } from '@/hooks/MusicHook';
|
|
|
|
|
import { audioService } from '@/services/audioService';
|
|
|
|
|
import { usePlayerStore } from '@/store/modules/player';
|
2025-07-23 23:54:35 +08:00
|
|
|
import { secondToMinute } from '@/utils';
|
2025-06-28 17:40:57 +08:00
|
|
|
|
2025-07-23 23:54:35 +08:00
|
|
|
const props = withDefaults(
|
|
|
|
|
defineProps<{
|
|
|
|
|
isDark: boolean;
|
|
|
|
|
}>(),
|
|
|
|
|
{
|
|
|
|
|
isDark: false
|
|
|
|
|
}
|
|
|
|
|
);
|
2025-06-28 17:40:57 +08:00
|
|
|
|
|
|
|
|
const playerStore = usePlayerStore();
|
|
|
|
|
const playBarRef = ref<HTMLElement | null>(null);
|
|
|
|
|
|
|
|
|
|
// 播放状态
|
|
|
|
|
const play = computed(() => playerStore.isPlay);
|
|
|
|
|
|
|
|
|
|
// 播放模式
|
|
|
|
|
const playMode = computed(() => playerStore.playMode);
|
|
|
|
|
const playModeIcon = computed(() => {
|
|
|
|
|
switch (playMode.value) {
|
|
|
|
|
case 0:
|
|
|
|
|
return 'ri-repeat-2-line';
|
|
|
|
|
case 1:
|
|
|
|
|
return 'ri-repeat-one-line';
|
|
|
|
|
case 2:
|
|
|
|
|
return 'ri-shuffle-line';
|
|
|
|
|
default:
|
|
|
|
|
return 'ri-repeat-2-line';
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 切换播放模式
|
|
|
|
|
const togglePlayMode = () => {
|
|
|
|
|
playerStore.togglePlayMode();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 音量控制
|
|
|
|
|
const audioVolume = ref(
|
|
|
|
|
localStorage.getItem('volume') ? parseFloat(localStorage.getItem('volume') as string) : 1
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const volumeSlider = computed({
|
|
|
|
|
get: () => audioVolume.value * 100,
|
|
|
|
|
set: (value) => {
|
|
|
|
|
localStorage.setItem('volume', (value / 100).toString());
|
|
|
|
|
audioService.setVolume(value / 100);
|
|
|
|
|
audioVolume.value = value / 100;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 音量图标
|
|
|
|
|
const getVolumeIcon = computed(() => {
|
|
|
|
|
if (audioVolume.value === 0) return 'ri-volume-mute-line';
|
|
|
|
|
if (audioVolume.value <= 0.5) return 'ri-volume-down-line';
|
|
|
|
|
return 'ri-volume-up-line';
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 静音切换
|
|
|
|
|
const mute = () => {
|
|
|
|
|
if (volumeSlider.value === 0) {
|
|
|
|
|
volumeSlider.value = 30;
|
|
|
|
|
} else {
|
|
|
|
|
volumeSlider.value = 0;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 鼠标滚轮调整音量
|
|
|
|
|
const handleVolumeWheel = (e: WheelEvent) => {
|
|
|
|
|
const delta = e.deltaY < 0 ? 5 : -5;
|
|
|
|
|
const newValue = Math.min(Math.max(volumeSlider.value + delta, 0), 100);
|
|
|
|
|
volumeSlider.value = newValue;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 播放控制
|
|
|
|
|
const handlePrev = () => playerStore.prevPlay();
|
|
|
|
|
const handleNext = () => playerStore.nextPlay();
|
|
|
|
|
|
|
|
|
|
const playMusicEvent = async () => {
|
|
|
|
|
try {
|
|
|
|
|
await playerStore.setPlay({ ...playMusic.value });
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('播放出错:', error);
|
|
|
|
|
playerStore.nextPlay();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 进度条控制
|
|
|
|
|
const handleProgressClick = (e: MouseEvent) => {
|
|
|
|
|
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
|
|
|
|
|
const percent = (e.clientX - rect.left) / rect.width;
|
|
|
|
|
audioService.seek(allTime.value * percent);
|
|
|
|
|
nowTime.value = allTime.value * percent;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 格式化时间
|
|
|
|
|
const formatTime = (seconds: number) => {
|
|
|
|
|
return secondToMinute(seconds);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 打开播放列表抽屉
|
|
|
|
|
const openPlayListDrawer = () => {
|
|
|
|
|
playerStore.setPlayListDrawerVisible(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 深色模式
|
2025-10-22 21:49:53 +08:00
|
|
|
const isDarkMode = computed(() => props.isDark);
|
2025-06-28 17:40:57 +08:00
|
|
|
|
|
|
|
|
// 主题颜色应用函数
|
|
|
|
|
const applyThemeColor = (colorValue: string) => {
|
|
|
|
|
if (!colorValue || !playBarRef.value) return;
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
console.log('应用主题色:', colorValue);
|
|
|
|
|
const playBarElement = playBarRef.value;
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
// 解析RGB值
|
|
|
|
|
const rgbMatch = colorValue.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
if (rgbMatch) {
|
|
|
|
|
const [_, r, g, b] = rgbMatch.map(Number);
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
// 计算颜色亮度 (0-255)
|
|
|
|
|
// 使用加权平均值公式: 0.299*R + 0.587*G + 0.114*B
|
|
|
|
|
const brightness = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
console.log(`主题色亮度: ${brightness}/255`);
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
// 设置主色
|
|
|
|
|
playBarElement.style.setProperty('--fill-color', colorValue);
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
// 亮度自适应处理
|
2025-07-23 23:54:35 +08:00
|
|
|
if (brightness > 200) {
|
|
|
|
|
// 非常亮的颜色
|
2025-06-28 17:40:57 +08:00
|
|
|
// 深化主色以增加对比度
|
|
|
|
|
const darkenedColor = `rgb(${Math.max(0, r - 60)}, ${Math.max(0, g - 60)}, ${Math.max(0, b - 60)})`;
|
|
|
|
|
playBarElement.style.setProperty('--fill-color-alt', darkenedColor);
|
|
|
|
|
playBarElement.style.setProperty('--fill-color-transparent', `rgba(${r}, ${g}, ${b}, 0.5)`); // 提高透明度
|
|
|
|
|
playBarElement.style.setProperty('--text-on-fill', '#000000'); // 亮色背景上用黑色文字
|
|
|
|
|
playBarElement.style.setProperty('--high-contrast-color', '#000000'); // 高对比度颜色
|
|
|
|
|
playBarElement.classList.add('light-theme-color');
|
|
|
|
|
playBarElement.classList.remove('dark-theme-color');
|
2025-07-23 23:54:35 +08:00
|
|
|
} else if (brightness < 50) {
|
|
|
|
|
// 非常暗的颜色
|
2025-06-28 17:40:57 +08:00
|
|
|
// 提亮主色以增加可见性
|
|
|
|
|
const lightenedColor = `rgb(${Math.min(255, r + 60)}, ${Math.min(255, g + 60)}, ${Math.min(255, b + 60)})`;
|
|
|
|
|
playBarElement.style.setProperty('--fill-color-alt', lightenedColor);
|
|
|
|
|
playBarElement.style.setProperty('--fill-color-transparent', `rgba(${r}, ${g}, ${b}, 0.7)`); // 提高透明度
|
|
|
|
|
playBarElement.style.setProperty('--text-on-fill', '#ffffff'); // 暗色背景上用白色文字
|
|
|
|
|
playBarElement.style.setProperty('--high-contrast-color', '#ffffff'); // 高对比度颜色
|
|
|
|
|
playBarElement.classList.add('dark-theme-color');
|
|
|
|
|
playBarElement.classList.remove('light-theme-color');
|
|
|
|
|
} else {
|
|
|
|
|
// 计算辅助色和高亮色
|
|
|
|
|
// 普通亮度颜色,正常处理
|
|
|
|
|
playBarElement.style.setProperty('--fill-color-alt', colorValue); // 保持一致
|
|
|
|
|
playBarElement.style.setProperty('--fill-color-transparent', `rgba(${r}, ${g}, ${b}, 0.25)`);
|
|
|
|
|
// 根据亮度决定文本颜色
|
|
|
|
|
const textColor = brightness > 125 ? '#000000' : '#ffffff';
|
|
|
|
|
playBarElement.style.setProperty('--text-on-fill', textColor);
|
|
|
|
|
playBarElement.style.setProperty('--high-contrast-color', textColor);
|
|
|
|
|
playBarElement.classList.remove('light-theme-color');
|
|
|
|
|
playBarElement.classList.remove('dark-theme-color');
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
// 设置亮色(用于高亮效果)
|
|
|
|
|
const lightenedColor = `rgb(${Math.min(255, r + 40)}, ${Math.min(255, g + 40)}, ${Math.min(255, b + 40)})`;
|
|
|
|
|
playBarElement.style.setProperty('--fill-color-light', lightenedColor);
|
|
|
|
|
} else {
|
|
|
|
|
// 无法解析RGB值时的默认设置
|
|
|
|
|
playBarElement.style.setProperty('--fill-color', colorValue);
|
|
|
|
|
playBarElement.style.setProperty('--fill-color-transparent', `${colorValue}40`);
|
|
|
|
|
playBarElement.style.setProperty('--fill-color-light', `${colorValue}80`);
|
|
|
|
|
playBarElement.style.setProperty('--fill-color-alt', colorValue);
|
|
|
|
|
playBarElement.style.setProperty('--text-on-fill', '#ffffff');
|
|
|
|
|
playBarElement.style.setProperty('--high-contrast-color', '#ffffff');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 监听主题色变化
|
2025-07-23 23:54:35 +08:00
|
|
|
watch(
|
|
|
|
|
() => playerStore.playMusic.primaryColor,
|
|
|
|
|
(newVal) => {
|
|
|
|
|
if (newVal) {
|
|
|
|
|
applyThemeColor(newVal);
|
|
|
|
|
}
|
2025-06-28 17:40:57 +08:00
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
);
|
2025-06-28 17:40:57 +08:00
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
if (playerStore.playMusic?.primaryColor) {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
applyThemeColor(playerStore.playMusic.primaryColor as string);
|
|
|
|
|
}, 50);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.play-bar {
|
|
|
|
|
@apply w-full;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
transition: all 0.3s ease;
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
/* 默认变量 */
|
|
|
|
|
--text-on-fill: #ffffff;
|
|
|
|
|
--high-contrast-color: #ffffff;
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
&.dark-theme {
|
|
|
|
|
--text-color: #333333;
|
|
|
|
|
--muted-color: rgba(0, 0, 0, 0.6);
|
|
|
|
|
--track-color: rgba(0, 0, 0, 0.2);
|
|
|
|
|
--track-color-hover: rgba(0, 0, 0, 0.4);
|
|
|
|
|
--fill-color: #1ed760;
|
|
|
|
|
--fill-color-alt: #1ed760;
|
|
|
|
|
--fill-color-transparent: rgba(30, 215, 96, 0.25);
|
|
|
|
|
--fill-color-light: rgba(30, 215, 96, 0.5);
|
|
|
|
|
--button-bg: rgba(0, 0, 0, 0.1);
|
|
|
|
|
--button-hover: rgba(0, 0, 0, 0.2);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
&:not(.dark-theme) {
|
|
|
|
|
--text-color: #f1f1f1;
|
|
|
|
|
--muted-color: rgba(255, 255, 255, 0.6);
|
|
|
|
|
--track-color: rgba(255, 255, 255, 0.1);
|
|
|
|
|
--track-color-hover: rgba(255, 255, 255, 0.2);
|
|
|
|
|
--fill-color: #73e49a;
|
|
|
|
|
--fill-color-alt: #73e49a;
|
|
|
|
|
--fill-color-transparent: rgba(115, 228, 154, 0.25);
|
|
|
|
|
--fill-color-light: rgba(115, 228, 154, 0.5);
|
|
|
|
|
--button-bg: rgba(255, 255, 255, 0.05);
|
|
|
|
|
--button-hover: rgba(255, 255, 255, 0.1);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
/* 极亮主题色适配 */
|
|
|
|
|
&.light-theme-color {
|
|
|
|
|
.progress-fill {
|
2025-07-23 23:54:35 +08:00
|
|
|
box-shadow:
|
|
|
|
|
0 0 8px var(--fill-color-transparent),
|
|
|
|
|
inset 0 0 0 1px rgba(0, 0, 0, 0.1);
|
2025-06-28 17:40:57 +08:00
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.control-btn.play-btn {
|
2025-07-23 23:54:35 +08:00
|
|
|
box-shadow:
|
|
|
|
|
0 3px 8px var(--fill-color-transparent),
|
|
|
|
|
0 1px 2px rgba(0, 0, 0, 0.3);
|
2025-06-28 17:40:57 +08:00
|
|
|
color: var(--text-on-fill);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.volume-control .iconfont:hover {
|
|
|
|
|
color: var(--fill-color-alt);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
/* 极暗主题色适配 */
|
|
|
|
|
&.dark-theme-color {
|
|
|
|
|
.progress-fill {
|
2025-07-23 23:54:35 +08:00
|
|
|
box-shadow:
|
|
|
|
|
0 0 10px var(--fill-color-transparent),
|
|
|
|
|
inset 0 0 0 1px rgba(255, 255, 255, 0.2);
|
2025-06-28 17:40:57 +08:00
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.control-btn.play-btn {
|
2025-07-23 23:54:35 +08:00
|
|
|
box-shadow:
|
|
|
|
|
0 3px 12px var(--fill-color-transparent),
|
|
|
|
|
0 0 0 1px rgba(255, 255, 255, 0.2);
|
|
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.iconfont {
|
2025-07-23 23:54:35 +08:00
|
|
|
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
2025-06-28 17:40:57 +08:00
|
|
|
}
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.volume-control .iconfont:hover {
|
|
|
|
|
color: var(--fill-color-light);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.container {
|
|
|
|
|
@apply flex flex-col;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.top-section {
|
|
|
|
|
@apply mb-3;
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.progress-bar {
|
|
|
|
|
@apply relative cursor-pointer h-2 mb-2 w-full;
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.progress-track {
|
|
|
|
|
@apply absolute inset-0 rounded-full transition-all duration-150;
|
|
|
|
|
background-color: var(--track-color);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.progress-fill {
|
|
|
|
|
@apply absolute top-0 left-0 h-full rounded-full transition-all duration-150;
|
|
|
|
|
background: linear-gradient(90deg, var(--fill-color), var(--fill-color-light));
|
|
|
|
|
box-shadow: 0 0 8px var(--fill-color-transparent);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
&:hover {
|
2025-07-23 23:54:35 +08:00
|
|
|
.progress-track {
|
2025-06-28 17:40:57 +08:00
|
|
|
background-color: var(--track-color-hover);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
.progress-track,
|
|
|
|
|
.progress-fill {
|
2025-06-28 17:40:57 +08:00
|
|
|
@apply h-full;
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.progress-fill {
|
|
|
|
|
box-shadow: 0 0 12px var(--fill-color-transparent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.time-display {
|
|
|
|
|
@apply flex justify-between text-base;
|
|
|
|
|
color: var(--muted-color);
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.time-separator {
|
|
|
|
|
@apply mx-1;
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.current-time {
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
transition: opacity 0.3s ease;
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
&:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.controls-section {
|
|
|
|
|
@apply flex items-center justify-between mb-4;
|
2025-07-23 23:54:35 +08:00
|
|
|
|
|
|
|
|
.left-controls,
|
|
|
|
|
.right-controls {
|
2025-06-28 17:40:57 +08:00
|
|
|
@apply flex items-center;
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.center-controls {
|
|
|
|
|
@apply flex items-center justify-center space-x-6;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bottom-section {
|
|
|
|
|
@apply flex items-center justify-between mt-2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.control-btn {
|
|
|
|
|
@apply flex items-center justify-center rounded-full outline-none border-0 transition-all duration-200;
|
|
|
|
|
color: var(--text-color);
|
|
|
|
|
background: transparent;
|
|
|
|
|
width: 32px;
|
|
|
|
|
height: 32px;
|
|
|
|
|
cursor: pointer;
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
&:hover {
|
|
|
|
|
background-color: var(--button-bg);
|
|
|
|
|
transform: scale(1.05);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
&:active {
|
|
|
|
|
background-color: var(--button-hover);
|
|
|
|
|
transform: scale(0.95);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
&.play-btn {
|
|
|
|
|
background: linear-gradient(145deg, var(--fill-color), var(--fill-color-alt));
|
|
|
|
|
color: var(--text-on-fill);
|
|
|
|
|
width: 46px;
|
|
|
|
|
height: 46px;
|
|
|
|
|
box-shadow: 0 3px 8px var(--fill-color-transparent);
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
&:hover {
|
|
|
|
|
box-shadow: 0 4px 12px var(--fill-color-transparent);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.iconfont {
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
&.small-btn {
|
|
|
|
|
@apply text-2xl;
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.iconfont {
|
|
|
|
|
@apply text-2xl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.volume-control {
|
|
|
|
|
@apply flex items-center space-x-2;
|
|
|
|
|
color: var(--text-color);
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.iconfont {
|
|
|
|
|
@apply cursor-pointer text-base;
|
2025-07-23 23:54:35 +08:00
|
|
|
transition:
|
|
|
|
|
transform 0.2s ease,
|
|
|
|
|
color 0.2s ease;
|
|
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
&:hover {
|
|
|
|
|
transform: scale(1.1);
|
|
|
|
|
color: var(--fill-color);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.volume-slider {
|
|
|
|
|
@apply w-24;
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
:deep(.n-slider) {
|
|
|
|
|
--n-rail-height: 3px;
|
|
|
|
|
--n-fill-color: var(--fill-color);
|
|
|
|
|
--n-rail-color: var(--track-color);
|
|
|
|
|
--n-handle-size: 12px;
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.n-slider-rail {
|
|
|
|
|
@apply rounded-full;
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.n-slider-rail__fill {
|
|
|
|
|
background: linear-gradient(90deg, var(--fill-color), var(--fill-color-light));
|
|
|
|
|
box-shadow: 0 0 6px var(--fill-color-transparent);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
.n-slider-handle {
|
|
|
|
|
@apply opacity-0 transition-opacity duration-200;
|
|
|
|
|
background: white;
|
2025-07-23 23:54:35 +08:00
|
|
|
box-shadow:
|
|
|
|
|
0 0 6px var(--fill-color-transparent),
|
|
|
|
|
0 0 0 1px var(--high-contrast-color);
|
2025-06-28 17:40:57 +08:00
|
|
|
border: 2px solid var(--fill-color);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
|
2025-06-28 17:40:57 +08:00
|
|
|
&:hover .n-slider-handle {
|
|
|
|
|
@apply opacity-100;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.spacer {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.like-active {
|
|
|
|
|
color: var(--fill-color);
|
|
|
|
|
text-shadow: 0 0 8px var(--fill-color-transparent);
|
|
|
|
|
}
|
2025-07-23 23:54:35 +08:00
|
|
|
</style>
|