Skip to content

Commit c21f970

Browse files
committed
✨ feat: 新增 定时关闭 & 播放速度
1 parent 56045cd commit c21f970

6 files changed

Lines changed: 115 additions & 80 deletions

File tree

components.d.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ declare module 'vue' {
1111
AboutSetting: typeof import('./src/components/Setting/AboutSetting.vue')['default']
1212
ArtistList: typeof import('./src/components/List/ArtistList.vue')['default']
1313
AutoClose: typeof import('./src/components/Modal/AutoClose.vue')['default']
14-
BatchList: typeof import('./src/components/Modal/BatchList.vue')['default']
14+
BatchList: typeof import('./src/components/Modal/batchList.vue')['default']
1515
ChangeRate: typeof import('./src/components/Modal/ChangeRate.vue')['default']
1616
CloudMatch: typeof import('./src/components/Modal/CloudMatch.vue')['default']
1717
CommentList: typeof import('./src/components/List/CommentList.vue')['default']
@@ -96,7 +96,6 @@ declare module 'vue' {
9696
NP: typeof import('naive-ui')['NP']
9797
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
9898
NPopover: typeof import('naive-ui')['NPopover']
99-
NProgress: typeof import('naive-ui')['NProgress']
10099
NQrCode: typeof import('naive-ui')['NQrCode']
101100
NRadio: typeof import('naive-ui')['NRadio']
102101
NRadioGroup: typeof import('naive-ui')['NRadioGroup']

src/components/Modal/AutoClose.vue

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
</n-text>
1212
</Transition>
1313
</n-flex>
14-
<n-switch v-model:value="statusStore.autoClose.enable" :round="false" />
14+
<n-switch
15+
v-model:value="statusStore.autoClose.enable"
16+
:round="false"
17+
@update:value="handleUpdate"
18+
/>
1519
</n-flex>
1620
</n-card>
1721
<!-- 时间选择 -->
@@ -23,6 +27,7 @@
2327
type="primary"
2428
size="large"
2529
round
30+
@click="player.startAutoCloseTimer(item, item * 60)"
2631
>
2732
{{ item }}min
2833
</n-tag>
@@ -35,13 +40,19 @@
3540
type: 'primary',
3641
}"
3742
:show-icon="false"
43+
@positive-click="player.startAutoCloseTimer(customTime, customTime * 60)"
3844
>
3945
<template #trigger>
4046
<n-tag :bordered="false" type="primary" size="large" round> 自定义时长 </n-tag>
4147
</template>
4248
<n-flex vertical>
4349
<n-text>自定义时长(分钟)</n-text>
44-
<n-input-number v-model:value="statusStore.autoClose.remainTime" />
50+
<n-input-number
51+
v-model:value="customTime"
52+
:min="1"
53+
:max="120"
54+
placeholder="请输入自定义时长"
55+
/>
4556
</n-flex>
4657
</n-popconfirm>
4758
</n-flex>
@@ -55,8 +66,22 @@
5566
<script setup lang="ts">
5667
import { useStatusStore } from "@/stores";
5768
import { convertSecondsToTime } from "@/utils/time";
69+
import player from "@/utils/player";
5870
5971
const statusStore = useStatusStore();
72+
73+
// 自定义时长
74+
const customTime = ref(1);
75+
76+
// 是否开启
77+
const handleUpdate = (value: boolean) => {
78+
if (value) {
79+
player.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.remainTime);
80+
} else {
81+
statusStore.autoClose.enable = false;
82+
statusStore.autoClose.remainTime = statusStore.autoClose.time * 60;
83+
}
84+
};
6085
</script>
6186

6287
<style scoped lang="scss">

src/components/Player/MainPlayer.vue

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,34 @@
161161
class="play-menu"
162162
justify="end"
163163
>
164-
<!-- 播放时间 -->
165-
<div class="time">
166-
<n-text depth="2">{{ secondsToTime(statusStore.currentTime) }}</n-text>
167-
<n-text depth="2">{{ secondsToTime(statusStore.duration) }}</n-text>
168-
</div>
164+
<!-- 时间相关 -->
165+
<Transition name="fade" mode="out-in">
166+
<n-flex
167+
:key="statusStore.autoClose.enable ? 'autoClose' : 'time'"
168+
:size="4"
169+
justify="center"
170+
class="time-container"
171+
vertical
172+
>
173+
<div class="time">
174+
<n-text depth="2">{{ secondsToTime(statusStore.currentTime) }}</n-text>
175+
<n-text depth="2">{{ secondsToTime(statusStore.duration) }}</n-text>
176+
</div>
177+
<!-- 定时关闭 -->
178+
<n-tag
179+
v-if="statusStore.autoClose.enable"
180+
size="small"
181+
type="primary"
182+
round
183+
@click="openAutoClose"
184+
>
185+
{{ convertSecondsToTime(statusStore.autoClose.remainTime) }}
186+
<template #icon>
187+
<SvgIcon name="TimeAuto" />
188+
</template>
189+
</n-tag>
190+
</n-flex>
191+
</Transition>
169192
<!-- 功能区 -->
170193
<PlayerRightMenu />
171194
</n-flex>
@@ -176,10 +199,16 @@
176199
<script setup lang="ts">
177200
import type { DropdownOption } from "naive-ui";
178201
import { useMusicStore, useStatusStore, useDataStore, useSettingStore } from "@/stores";
179-
import { secondsToTime, calculateCurrentTime } from "@/utils/time";
202+
import { secondsToTime, calculateCurrentTime, convertSecondsToTime } from "@/utils/time";
180203
import { renderIcon, coverLoaded } from "@/utils/helper";
181204
import { toLikeSong } from "@/utils/auth";
182-
import { openChangeRate, openDownloadSong, openJumpArtist, openPlaylistAdd } from "@/utils/modal";
205+
import {
206+
openAutoClose,
207+
openChangeRate,
208+
openDownloadSong,
209+
openJumpArtist,
210+
openPlaylistAdd,
211+
} from "@/utils/modal";
183212
import player from "@/utils/player";
184213
185214
const router = useRouter();
@@ -476,11 +505,17 @@ const instantLyrics = computed(() => {
476505
}
477506
}
478507
.play-menu {
508+
.time-container {
509+
margin-right: 8px;
510+
.n-tag {
511+
justify-content: center;
512+
font-size: 12px;
513+
}
514+
}
479515
.time {
480516
display: flex;
481517
align-items: center;
482518
font-size: 12px;
483-
margin-right: 8px;
484519
.n-text {
485520
color: var(--primary-hex);
486521
opacity: 0.8;

src/components/Player/PlayerControl.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ const sliderDragend = () => {
140140
height: 100%;
141141
padding: 0 30px;
142142
transition: opacity 0.3s;
143-
.menu-icon {
143+
:deep(.menu-icon) {
144144
display: flex;
145145
align-items: center;
146146
justify-content: center;
@@ -162,6 +162,12 @@ const sliderDragend = () => {
162162
transform: scale(1);
163163
}
164164
}
165+
:deep(.n-badge-sup) {
166+
background-color: rgba(var(--main-color), 0.14);
167+
.n-base-slot-machine {
168+
color: rgb(var(--main-color));
169+
}
170+
}
165171
}
166172
.center {
167173
height: 100%;

src/utils/init.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ const init = async () => {
3232
);
3333
// 同步播放模式
3434
player.playModeSyncIpc();
35+
// 初始化自动关闭定时器
36+
if (statusStore.autoClose.enable) {
37+
player.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.remainTime);
38+
}
3539

3640
if (isElectron) {
3741
// 注册全局快捷键

src/utils/player.ts

Lines changed: 33 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Player {
3535
// 定时器
3636
private playerInterval: ReturnType<typeof setInterval> | undefined;
3737
// 自动关闭定时器
38-
private autoCloseTimer: ReturnType<typeof setTimeout> | undefined;
38+
private autoCloseInterval: ReturnType<typeof setInterval> | undefined;
3939
// 频谱数据
4040
private audioContext: AudioContext | null = null;
4141
private analyser: AnalyserNode | null = null;
@@ -54,8 +54,6 @@ class Player {
5454
this.player = new Howl({ src: [""], format: allowPlayFormat, autoplay: false });
5555
// 初始化媒体会话
5656
this.initMediaSession();
57-
// 初始化自动关闭定时器
58-
this.toggleAutoCloseTimer();
5957
}
6058
/**
6159
* 获取当前播放歌曲
@@ -429,15 +427,19 @@ class Player {
429427
if (currentSessionId !== this.playSessionId) return;
430428
// statusStore.playStatus = false;
431429
console.log("⏹️ song end:", playSongData);
432-
430+
433431
// 检查是否需要在歌曲结束时执行自动关闭
434432
const statusStore = useStatusStore();
435-
if (statusStore.autoClose.enable && statusStore.autoClose.waitSongEnd && statusStore.autoClose.remainTime <= 0) {
433+
if (
434+
statusStore.autoClose.enable &&
435+
statusStore.autoClose.waitSongEnd &&
436+
statusStore.autoClose.remainTime <= 0
437+
) {
436438
// 执行自动关闭
437439
this.executeAutoClose();
438440
return;
439441
}
440-
442+
441443
this.nextOrPrev("next");
442444
});
443445
// 错误
@@ -899,12 +901,6 @@ class Player {
899901
statusStore.lyricIndex = -1;
900902
statusStore.currentTime = 0;
901903
statusStore.progress = 0;
902-
903-
// 重置自动关闭计时器(切换歌曲时重新开始计时)
904-
if (statusStore.autoClose.enable) {
905-
this.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.waitSongEnd);
906-
}
907-
908904
// 暂停
909905
await this.pause(false);
910906
// 初始化播放器(不传入seek参数,确保从头开始播放)
@@ -1197,12 +1193,7 @@ class Player {
11971193
statusStore.lyricIndex = -1;
11981194
statusStore.currentTime = 0;
11991195
statusStore.progress = 0;
1200-
1201-
// 重置自动关闭计时器(切换歌曲时重新开始计时)
1202-
if (statusStore.autoClose.enable) {
1203-
this.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.waitSongEnd);
1204-
}
1205-
1196+
12061197
// 清理并播放(不传入seek参数,确保从头开始播放)
12071198
await this.initPlayer(true, 0);
12081199
}
@@ -1446,72 +1437,47 @@ class Player {
14461437
}
14471438
/**
14481439
* 开始定时关闭
1449-
* @param time 关闭时间(
1450-
* @param waitSongEnd 是否等待歌曲结束
1440+
* @param time 关闭时间(分钟
1441+
* @param remainTime 剩余时间(秒)
14511442
*/
1452-
startAutoCloseTimer(time: number, waitSongEnd: boolean = true) {
1443+
startAutoCloseTimer(time: number, remainTime: number) {
14531444
const statusStore = useStatusStore();
1454-
// 清除之前的定时器
1455-
clearTimeout(this.autoCloseTimer);
1445+
if (!time || !remainTime) return;
1446+
// 如已有定时器在运行,先停止以防叠加
1447+
if (this.autoCloseInterval) {
1448+
clearInterval(this.autoCloseInterval);
1449+
this.autoCloseInterval = undefined;
1450+
}
14561451
// 重置剩余时间
1457-
statusStore.autoClose = {
1452+
Object.assign(statusStore.autoClose, {
14581453
enable: true,
14591454
time,
1460-
remainTime: time,
1461-
waitSongEnd,
1462-
};
1455+
remainTime,
1456+
});
14631457
// 开始减少剩余时间
1464-
const { pause } = useIntervalFn(() => {
1465-
if (statusStore.autoClose.remainTime <= 0) pause();
1458+
this.autoCloseInterval = setInterval(() => {
1459+
if (statusStore.autoClose.remainTime <= 0) {
1460+
clearInterval(this.autoCloseInterval);
1461+
this.autoCloseInterval = undefined;
1462+
if (!statusStore.autoClose.waitSongEnd) {
1463+
this.executeAutoClose();
1464+
}
1465+
return;
1466+
}
14661467
statusStore.autoClose.remainTime--;
14671468
}, 1000);
1468-
// 设置新的定时器
1469-
this.autoCloseTimer = setTimeout(() => {
1470-
pause();
1471-
statusStore.autoClose.remainTime = 0;
1472-
// 根据设置决定如何关闭
1473-
if (statusStore.autoClose.waitSongEnd) {
1474-
// 等待歌曲结束,不在这里执行关闭,而是在歌曲结束事件中处理
1475-
console.log("⏰ 自动关闭计时结束,等待当前歌曲播放完毕");
1476-
} else {
1477-
// 立即执行关闭
1478-
this.executeAutoClose();
1479-
}
1480-
}, time * 1000);
14811469
}
1482-
/**
1483-
* 开启或者停止自动关闭定时器
1484-
*/
1485-
toggleAutoCloseTimer() {
1486-
const statusStore = useStatusStore();
1487-
if (statusStore.autoClose.enable) {
1488-
this.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.waitSongEnd);
1489-
} else {
1490-
clearTimeout(this.autoCloseTimer);
1491-
}
1492-
}
1493-
14941470
/**
14951471
* 执行自动关闭操作
14961472
*/
14971473
private executeAutoClose() {
14981474
console.log("🔄 执行自动关闭");
14991475
// 暂停播放
15001476
this.pause();
1501-
// 清除定时器
1502-
clearTimeout(this.autoCloseTimer);
15031477
// 重置状态
1504-
const statusStore = useStatusStore();
1505-
statusStore.autoClose.enable = false;
1506-
statusStore.autoClose.remainTime = 0;
1507-
1508-
// 如果是 Electron 环境,关闭应用
1509-
if (isElectron) {
1510-
window.electron.ipcRenderer.send("app-close");
1511-
} else {
1512-
// 浏览器环境,显示提示信息
1513-
window.$message.info("自动关闭已触发,播放已暂停");
1514-
}
1478+
const { autoClose } = useStatusStore();
1479+
autoClose.enable = false;
1480+
autoClose.remainTime = autoClose.time * 60;
15151481
}
15161482
}
15171483

0 commit comments

Comments
 (0)