music(player): 加音量条 + 静音按钮(localStorage 持久化)
deploy music / build-and-deploy (push) Successful in 1m48s

This commit is contained in:
Fam Zheng
2026-05-10 15:39:30 +01:00
parent 9ce3b66810
commit f7fac352a5
@@ -230,6 +230,20 @@
<div class="fill" :style="{ width: progressPct + '%' }"></div>
</div>
<span class="time">{{ fmtTime(duration) }}</span>
<button
class="btn-icon vol-icon"
:title="muted ? '取消静音' : '静音'"
@click="toggleMute"
>{{ volIcon }}</button>
<input
class="vol-slider"
type="range"
min="0"
max="100"
:value="muted ? 0 : Math.round(volume * 100)"
@input="onVolumeInput"
title="音量"
/>
</div>
</footer>
@@ -336,6 +350,37 @@ const activeTagName = ref(null)
const search = ref('')
const sortMode = ref(localStorage.getItem('music.sort') || 'name')
const repeatOne = ref(false)
const volume = ref(parseFloat(localStorage.getItem('music.vol') || '1'))
const muted = ref(localStorage.getItem('music.muted') === '1')
const volIcon = computed(() => {
if (muted.value || volume.value === 0) return '🔇'
if (volume.value < 0.34) return '🔈'
if (volume.value < 0.67) return '🔉'
return '🔊'
})
function applyVolume() {
if (audioEl.value) {
audioEl.value.volume = volume.value
audioEl.value.muted = muted.value
}
}
function onVolumeInput(e) {
const v = parseInt(e.target.value, 10) / 100
volume.value = isNaN(v) ? 0 : Math.max(0, Math.min(1, v))
if (volume.value > 0 && muted.value) muted.value = false
localStorage.setItem('music.vol', String(volume.value))
localStorage.setItem('music.muted', muted.value ? '1' : '0')
applyVolume()
}
function toggleMute() {
muted.value = !muted.value
localStorage.setItem('music.muted', muted.value ? '1' : '0')
applyVolume()
}
const audioEl = ref(null)
const lyricsBoxEl = ref(null)
@@ -650,6 +695,7 @@ function onTimeUpdate(e) {
function onLoaded(e) {
duration.value = e.target.duration || 0
applyVolume()
}
function onEnded() {
@@ -1364,6 +1410,38 @@ onBeforeUnmount(() => {
text-align: center;
}
.vol-icon { font-size: 16px; width: 32px; height: 32px; }
.vol-slider {
-webkit-appearance: none;
appearance: none;
width: 90px;
height: 4px;
background: var(--border);
border-radius: 2px;
cursor: pointer;
}
.vol-slider:focus { outline: none; }
.vol-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 12px;
height: 12px;
background: var(--accent-strong);
border-radius: 50%;
cursor: pointer;
}
.vol-slider::-moz-range-thumb {
width: 12px;
height: 12px;
background: var(--accent-strong);
border: none;
border-radius: 50%;
cursor: pointer;
}
@media (max-width: 768px) {
.vol-slider { width: 60px; }
}
/* Chat sidebar */
.chat {
width: 320px;