Skip to content

Commit f5f0dbb

Browse files
committed
feat: 优化播放栏,整合高级控制菜单,将定时、均衡器、速度控制改为更多设置按钮显示, 添加定时关闭顶部显示功能
1 parent 7fca6db commit f5f0dbb

File tree

10 files changed

+752
-557
lines changed

10 files changed

+752
-557
lines changed

src/i18n/lang/en-US/player.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ export default {
5959
volume: 'Volume',
6060
favorite: 'Favorite {name}',
6161
unFavorite: 'Unfavorite {name}',
62-
playbackSpeed: 'Playback Speed'
62+
playbackSpeed: 'Playback Speed',
63+
advancedControls: 'Advanced Controls',
6364
},
6465
eq: {
6566
title: 'Equalizer',

src/i18n/lang/zh-CN/player.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ export default {
6060
favorite: '已收藏{name}',
6161
unFavorite: '已取消收藏{name}',
6262
miniPlayBar: '迷你播放栏',
63-
playbackSpeed: '播放速度'
63+
playbackSpeed: '播放速度',
64+
advancedControls: '更多设置s',
6465
},
6566
eq: {
6667
title: '均衡器',

src/renderer/components/EQControl.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,6 @@ const formatFreq = (freq: number) => {
264264
.eq-control {
265265
@apply p-6 rounded-lg;
266266
@apply bg-light dark:bg-dark;
267-
@apply shadow-lg dark:shadow-none;
268267
width: 100%;
269268
max-width: 700px;
270269
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
<template>
2+
<n-dropdown
3+
:show="showDropdown"
4+
:options="dropdownOptions"
5+
trigger="hover"
6+
:z-index="9999999"
7+
@select="handleSelect"
8+
placement="top"
9+
@update:show="(show) => showDropdown = show"
10+
>
11+
<n-tooltip trigger="hover" :z-index="9999999">
12+
<template #trigger>
13+
<div class="advanced-controls-btn">
14+
<i class="iconfont ri-settings-3-line"></i>
15+
16+
<!-- 激活状态的小标记 -->
17+
<div v-if="hasActiveSettings" class="active-indicator">
18+
<span v-if="hasActiveSleepTimer" class="timer-badge">
19+
<i class="ri-time-line"></i>
20+
</span>
21+
</div>
22+
</div>
23+
</template>
24+
{{ t('player.playBar.advancedControls') }}
25+
</n-tooltip>
26+
</n-dropdown>
27+
28+
<!-- EQ 均衡器弹窗 -->
29+
<n-modal v-model:show="showEQModal" :mask-closable="true" :unstable-show-mask="false">
30+
<div class="eq-modal-content">
31+
<div class="modal-close" @click="showEQModal = false">
32+
<i class="ri-close-line"></i>
33+
</div>
34+
<eq-control />
35+
</div>
36+
</n-modal>
37+
38+
<!-- 定时关闭弹窗 -->
39+
<n-modal v-model:show="playerStore.showSleepTimer" :mask-closable="true" :unstable-show-mask="false">
40+
<div class="timer-modal-content">
41+
<div class="modal-close" @click="playerStore.showSleepTimer = false">
42+
<i class="ri-close-line"></i>
43+
</div>
44+
<sleep-timer />
45+
</div>
46+
</n-modal>
47+
48+
<!-- 播放速度设置弹窗 -->
49+
<n-modal v-model:show="showSpeedModal" :mask-closable="true" :unstable-show-mask="false">
50+
<div class="speed-modal-content">
51+
<div class="modal-close" @click="showSpeedModal = false">
52+
<i class="ri-close-line"></i>
53+
</div>
54+
<h3>{{ t('player.playBar.playbackSpeed') }}</h3>
55+
<div class="speed-options">
56+
<div
57+
v-for="option in playbackRateOptions"
58+
:key="option.key"
59+
class="speed-option"
60+
:class="{ 'active': playbackRate === option.key }"
61+
@click="selectSpeed(option.key)"
62+
>
63+
{{ option.label }}
64+
</div>
65+
</div>
66+
</div>
67+
</n-modal>
68+
</template>
69+
70+
<script lang="ts" setup>
71+
import { ref, computed, h } from 'vue';
72+
import { useI18n } from 'vue-i18n';
73+
import { DropdownOption } from 'naive-ui';
74+
import { usePlayerStore } from '@/store/modules/player';
75+
import EqControl from '@/components/EQControl.vue';
76+
import SleepTimer from '@/components/player/SleepTimer.vue';
77+
78+
const { t } = useI18n();
79+
const playerStore = usePlayerStore();
80+
81+
// 下拉菜单状态
82+
const showDropdown = ref(false);
83+
const showEQModal = ref(false);
84+
const showSpeedModal = ref(false);
85+
const isEQVisible = ref(false);
86+
87+
// 播放速度状态
88+
const playbackRate = computed(() => playerStore.playbackRate);
89+
90+
91+
// 播放速度选项
92+
const playbackRateOptions = [
93+
{ label: '0.5x', key: 0.5 },
94+
{ label: '0.75x', key: 0.75 },
95+
{ label: '1.0x', key: 1.0 },
96+
{ label: '1.25x', key: 1.25 },
97+
{ label: '1.5x', key: 1.5 },
98+
{ label: '2.0x', key: 2.0 }
99+
];
100+
101+
// 是否有激活的睡眠定时器
102+
const hasActiveSleepTimer = computed(() => playerStore.hasSleepTimerActive);
103+
104+
// 检查是否有任何高级设置是激活状态
105+
const hasActiveSettings = computed(() => {
106+
return playbackRate.value !== 1.0 || hasActiveSleepTimer.value || isEQVisible.value;
107+
});
108+
109+
// 下拉菜单选项
110+
const dropdownOptions = computed<DropdownOption[]>(() => [
111+
{
112+
label: t('player.playBar.eq'),
113+
key: 'eq',
114+
icon: () => h('i', { class: 'ri-equalizer-line' })
115+
},
116+
{
117+
label: t('player.sleepTimer.title'),
118+
key: 'timer',
119+
icon: () => h('i', { class: 'ri-timer-line' }),
120+
// 如果有激活的定时器,添加标记
121+
suffix: () => hasActiveSleepTimer.value ? h('span', { class: 'active-option-mark' }) : null
122+
},
123+
{
124+
label: t('player.playBar.playbackSpeed') + `(${playbackRate.value}x)`,
125+
key: 'speed',
126+
icon: () => h('i', { class: 'ri-speed-line' }),
127+
// 如果播放速度不是1.0,添加标记
128+
suffix: () => playbackRate.value !== 1.0 ? h('span', { class: 'active-option-mark' }, `${playbackRate.value}x`) : null
129+
}
130+
]);
131+
132+
// 处理菜单选择
133+
const handleSelect = (key: string) => {
134+
switch (key) {
135+
case 'eq':
136+
showEQModal.value = true;
137+
break;
138+
case 'timer':
139+
playerStore.showSleepTimer = true;
140+
break;
141+
case 'speed':
142+
showSpeedModal.value = true;
143+
break;
144+
}
145+
};
146+
147+
// 选择播放速度
148+
const selectSpeed = (speed: number) => {
149+
playerStore.setPlaybackRate(speed);
150+
showSpeedModal.value = false;
151+
};
152+
153+
</script>
154+
155+
<style lang="scss" scoped>
156+
.sleep-timer-countdown {
157+
@apply fixed top-0 left-1/2 transform -translate-x-1/2 py-1 px-3 rounded-b-lg bg-green-500 text-white text-sm flex items-center;
158+
box-shadow: 0 2px 10px rgba(0,0,0,0.15);
159+
z-index: 9998;
160+
min-width: 80px;
161+
text-align: center;
162+
animation: fadeInDown 0.3s ease-out;
163+
164+
@keyframes fadeInDown {
165+
from {
166+
transform: translate(-50%, -100%);
167+
opacity: 0;
168+
}
169+
to {
170+
transform: translate(-50%, 0);
171+
opacity: 1;
172+
}
173+
}
174+
175+
span {
176+
font-variant-numeric: tabular-nums;
177+
letter-spacing: 0.5px;
178+
font-weight: 500;
179+
}
180+
}
181+
182+
.advanced-controls-btn {
183+
@apply cursor-pointer mx-3 relative;
184+
185+
.iconfont {
186+
@apply text-2xl transition;
187+
@apply hover:text-green-500;
188+
}
189+
190+
.active-indicator {
191+
@apply absolute -top-1 -right-1 flex;
192+
193+
.timer-badge, .speed-badge {
194+
@apply flex items-center justify-center text-xs bg-green-500 text-white rounded-full;
195+
height: 16px;
196+
min-width: 16px;
197+
padding: 0 3px;
198+
font-weight: 600;
199+
font-size: 10px;
200+
201+
i {
202+
font-size: 10px;
203+
}
204+
}
205+
206+
.timer-badge + .speed-badge {
207+
@apply -ml-2 z-10;
208+
}
209+
}
210+
}
211+
212+
.eq-modal-content,
213+
.timer-modal-content,
214+
.speed-modal-content {
215+
@apply p-6 rounded-3xl bg-white dark:bg-dark;
216+
max-width: 600px;
217+
margin: 0 auto;
218+
}
219+
220+
.speed-modal-content {
221+
h3 {
222+
@apply text-lg font-medium mb-4 text-center;
223+
}
224+
225+
.speed-options {
226+
@apply flex flex-wrap justify-center gap-4 my-8 mx-4;
227+
228+
.speed-option {
229+
@apply py-2 px-4 rounded-full cursor-pointer transition-all;
230+
@apply bg-gray-100 dark:bg-gray-800;
231+
@apply hover:bg-green-100 dark:hover:bg-green-900;
232+
233+
&.active {
234+
@apply bg-green-500 text-white;
235+
}
236+
}
237+
}
238+
}
239+
240+
.active-option-mark {
241+
@apply ml-2 text-xs bg-green-500 text-white py-0.5 px-1.5 rounded-full;
242+
font-weight: 500;
243+
}
244+
245+
.modal-close {
246+
@apply absolute top-4 right-4 cursor-pointer hover:text-green-500;
247+
i {
248+
@apply text-2xl;
249+
}
250+
}
251+
</style>

0 commit comments

Comments
 (0)