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