Skip to content

Commit 62c9dc3

Browse files
committed
✨ feat: 托盘菜单完善
1 parent 2b6d68e commit 62c9dc3

File tree

13 files changed

+133
-50
lines changed

13 files changed

+133
-50
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
- ✨ 支持扫码登录
3434
- 📱 支持手机号登录
3535
- 📅 自动进行每日签到及云贝签到
36+
- 💻 支持桌面歌词
3637
- 💻 支持切换为本地播放器,此模式将不会连接网络
3738
- 🎨 封面主题色自适应,支持全站着色
3839
- 🌚 Light / Dark / Auto 模式自动切换

electron/main/ipcMain.ts

+5
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,11 @@ const initTrayIpcMain = (
645645
tray?.setPlayMode(mode);
646646
});
647647

648+
// 喜欢状态切换
649+
ipcMain.on("like-status-change", (_, likeStatus: boolean) => {
650+
tray?.setLikeState(likeStatus);
651+
});
652+
648653
// 桌面歌词开关
649654
ipcMain.on("change-desktop-lyric", (_, val: boolean) => {
650655
tray?.setDesktopLyricShow(val);

electron/main/tray.ts

+15-21
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ type PlayState = "play" | "pause" | "loading";
1919
let playMode: PlayMode = "repeat";
2020
let playState: PlayState = "pause";
2121
let playName: string = "未播放歌曲";
22+
let likeSong: boolean = false;
2223
let desktopLyricShow: boolean = false;
2324
let desktopLyricLock: boolean = false;
2425

2526
export interface MainTray {
2627
setTitle(title: string): void;
2728
setPlayMode(mode: PlayMode): void;
29+
setLikeState(like: boolean): void;
2830
setPlayState(state: PlayState): void;
2931
setPlayName(name: string): void;
3032
setDesktopLyricShow(show: boolean): void;
@@ -34,11 +36,11 @@ export interface MainTray {
3436

3537
// 托盘图标
3638
const trayIcon = (filename: string) => {
37-
// const rootPath = isDev
38-
// ? join(__dirname, "../../public/icons/tray")
39-
// : join(app.getAppPath(), "../../public/icons/tray");
40-
// return nativeImage.createFromPath(join(rootPath, filename));
41-
return nativeImage.createFromPath(join(__dirname, `../../public/icons/tray/${filename}`));
39+
const rootPath = isDev
40+
? join(__dirname, "../../public/icons/tray")
41+
: join(app.getAppPath(), "../../public/icons/tray");
42+
return nativeImage.createFromPath(join(rootPath, filename));
43+
// return nativeImage.createFromPath(join(__dirname, `../../public/icons/tray/${filename}`));
4244
};
4345

4446
// 托盘菜单
@@ -60,7 +62,6 @@ const createTrayMenu = (
6062
id: "name",
6163
label: playName,
6264
icon: showIcon("music"),
63-
accelerator: "CmdOrCtrl+Alt+S",
6465
click: () => {
6566
win.show();
6667
win.focus();
@@ -71,19 +72,10 @@ const createTrayMenu = (
7172
},
7273
{
7374
id: "toogleLikeSong",
74-
label: "添加到我喜欢",
75-
icon: showIcon("unlike"),
76-
accelerator: "CmdOrCtrl+Alt+L",
75+
label: likeSong ? "从我喜欢中移除" : "添加到我喜欢",
76+
icon: showIcon(likeSong ? "like" : "unlike"),
7777
click: () => win.webContents.send("toogleLikeSong"),
7878
},
79-
{
80-
id: "unLike",
81-
label: "从我喜欢中移除",
82-
icon: showIcon("like"),
83-
visible: false,
84-
accelerator: "CmdOrCtrl+Alt+L",
85-
click: () => win.webContents.send("unlike-song"),
86-
},
8779
{
8880
id: "changeMode",
8981
label:
@@ -123,21 +115,18 @@ const createTrayMenu = (
123115
id: "playNext",
124116
label: "上一曲",
125117
icon: showIcon("prev"),
126-
accelerator: "CmdOrCtrl+Left",
127118
click: () => win.webContents.send("playPrev"),
128119
},
129120
{
130121
id: "playOrPause",
131122
label: playState === "pause" ? "播放" : "暂停",
132123
icon: showIcon(playState === "pause" ? "play" : "pause"),
133-
accelerator: "CmdOrCtrl+Space",
134124
click: () => win.webContents.send(playState === "pause" ? "play" : "pause"),
135125
},
136126
{
137127
id: "playNext",
138128
label: "下一曲",
139129
icon: showIcon("next"),
140-
accelerator: "CmdOrCtrl+Right",
141130
click: () => win.webContents.send("playNext"),
142131
},
143132
{
@@ -176,7 +165,6 @@ const createTrayMenu = (
176165
id: "exit",
177166
label: "退出",
178167
icon: showIcon("power"),
179-
accelerator: "CmdOrCtrl+Alt+Q",
180168
click: () => {
181169
win.close();
182170
// app.exit(0);
@@ -255,6 +243,12 @@ class CreateTray implements MainTray {
255243
// 更新菜单
256244
this.initTrayMenu();
257245
}
246+
// 设置喜欢状态
247+
setLikeState(like: boolean) {
248+
likeSong = like;
249+
// 更新菜单
250+
this.initTrayMenu();
251+
}
258252
// 桌面歌词开关
259253
setDesktopLyricShow(show: boolean) {
260254
desktopLyricShow = show;

src/App.vue

+6-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,12 @@
7474
<!-- 全屏播放器 -->
7575
<Teleport to="body">
7676
<Transition name="up" mode="out-in">
77-
<FullPlayer v-if="statusStore.showFullPlayer || statusStore.fullPlayerActive" />
77+
<FullPlayer
78+
v-if="
79+
statusStore.showFullPlayer ||
80+
(statusStore.fullPlayerActive && settingStore.fullPlayerCache)
81+
"
82+
/>
7883
</Transition>
7984
</Teleport>
8085
</Provider>

src/components/List/SongList.vue

+2-7
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,9 @@
192192
<div v-if="type !== 'radio'" class="actions" @click.stop @dblclick.stop>
193193
<!-- 喜欢歌曲 -->
194194
<SvgIcon
195-
:name="isLikeSong(item.id) ? 'Favorite' : 'FavoriteBorder'"
195+
:name="dataStore.isLikeSong(item.id) ? 'Favorite' : 'FavoriteBorder'"
196196
:size="20"
197-
@click.stop="toLikeSong(item, !isLikeSong(item.id))"
197+
@click.stop="toLikeSong(item, !dataStore.isLikeSong(item.id))"
198198
@delclick.stop
199199
/>
200200
</div>
@@ -381,11 +381,6 @@ const sortSelect = (key: SortType) => {
381381
}
382382
};
383383
384-
// 是否为喜欢歌曲
385-
const isLikeSong = (id: number) => {
386-
return dataStore.userLikeData.songs.includes(id);
387-
};
388-
389384
// 滚动至播放歌曲
390385
const scrollTo = (index: number) => {
391386
if (index === 0) songListScrollTop.value = 0;

src/components/Modal/LoginQRCode.vue

+75-17
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,36 @@
11
<template>
22
<div class="login-qrcode">
33
<div class="qr-img">
4-
<n-qr-code
4+
<div
55
v-if="qrImg"
6-
:value="qrImg"
76
:class="['qr', { success: qrStatusCode === 802, error: qrStatusCode === 800 }]"
8-
:size="160"
9-
:icon-size="30"
10-
icon-src="/icons/favicon.png?assest"
11-
error-correction-level="H"
12-
/>
7+
>
8+
<n-qr-code
9+
:value="qrImg"
10+
:size="160"
11+
:icon-size="30"
12+
icon-src="/icons/favicon.png?assest"
13+
error-correction-level="H"
14+
/>
15+
<!-- 待确认 -->
16+
<Transition name="fade" mode="out-in">
17+
<div v-if="loginName" class="login-data">
18+
<n-image
19+
:src="loginAvatar.replace(/^http:/, 'https:') + '?param=100y100'"
20+
class="cover"
21+
preview-disabled
22+
@load="coverLoaded"
23+
>
24+
<template #placeholder>
25+
<div class="cover-loading">
26+
<img src="/images/avatar.jpg?assest" class="loading-img" alt="loading-img" />
27+
</div>
28+
</template>
29+
</n-image>
30+
<n-text>{{ loginName }}</n-text>
31+
</div>
32+
</Transition>
33+
</div>
1334
<n-skeleton v-else class="qr" />
1435
</div>
1536
<n-text class="tip" depth="3">{{ qrTipText }}</n-text>
@@ -18,6 +39,7 @@
1839

1940
<script setup lang="ts">
2041
import { qrKey, checkQr } from "@/api/login";
42+
import { coverLoaded } from "@/utils/helper";
2143
2244
const emit = defineEmits<{
2345
saveLogin: [any];
@@ -41,11 +63,17 @@ const qrTipText = computed(() => {
4163
return qrCodeTip[qrStatusCode.value] || "遇到未知状态,请重试";
4264
});
4365
66+
// 待确认数据
67+
const loginName = ref<string>("");
68+
const loginAvatar = ref<string>("");
69+
4470
// 获取二维码
4571
const getQrData = async () => {
4672
try {
4773
pauseCheck();
4874
qrStatusCode.value = 801;
75+
loginName.value = "";
76+
loginAvatar.value = "";
4977
// 获取 key
5078
const res = await qrKey();
5179
qrImg.value = `https://music.163.com/login?codekey=${res.data.unikey}`;
@@ -63,7 +91,7 @@ const getQrData = async () => {
6391
const checkQrStatus = async () => {
6492
if (!qrUnikey.value) return;
6593
// 检查状态
66-
const { code, cookie } = await checkQr(qrUnikey.value);
94+
const { code, cookie, nickname, avatarUrl } = await checkQr(qrUnikey.value);
6795
switch (code) {
6896
// 二维码过期
6997
case 800:
@@ -77,6 +105,8 @@ const checkQrStatus = async () => {
77105
// 待确认
78106
case 802:
79107
qrStatusCode.value = 802;
108+
loginName.value = nickname;
109+
loginAvatar.value = avatarUrl;
80110
break;
81111
// 登录成功
82112
case 803:
@@ -115,15 +145,43 @@ onBeforeUnmount(pauseCheck);
115145
border-radius: 12px;
116146
overflow: hidden;
117147
.qr {
118-
padding: 0;
119-
height: 180px;
120-
width: 180px;
121-
min-height: 180px;
122-
min-width: 180px;
123-
transition: opacity 0.3s;
124-
:deep(canvas) {
125-
width: 100% !important;
126-
height: 100% !important;
148+
position: relative;
149+
display: flex;
150+
align-items: center;
151+
justify-content: center;
152+
.n-qr-code {
153+
padding: 0;
154+
height: 180px;
155+
width: 180px;
156+
min-height: 180px;
157+
min-width: 180px;
158+
transition:
159+
opacity 0.3s,
160+
filter 0.3s;
161+
:deep(canvas) {
162+
width: 100% !important;
163+
height: 100% !important;
164+
}
165+
}
166+
.login-data {
167+
position: absolute;
168+
display: flex;
169+
flex-direction: column;
170+
align-items: center;
171+
z-index: 1;
172+
.cover {
173+
width: 40px;
174+
height: 40px;
175+
border-radius: 50%;
176+
overflow: hidden;
177+
margin-bottom: 8px;
178+
}
179+
}
180+
&.success {
181+
.n-qr-code {
182+
opacity: 0.5;
183+
filter: blur(4px);
184+
}
127185
}
128186
}
129187
}

src/components/Setting/PlaySetting.vue

+7
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@
140140
class="set"
141141
/>
142142
</n-card>
143+
<n-card class="set-item">
144+
<div class="label">
145+
<n-text class="name">全屏播放器留存</n-text>
146+
<n-text class="tip" :depth="3">在播放器收起时是否销毁,开启将会增大内存占用</n-text>
147+
</div>
148+
<n-switch v-model:value="settingStore.fullPlayerCache" class="set" :round="false" />
149+
</n-card>
143150
<n-card class="set-item">
144151
<div class="label">
145152
<n-text class="name">显示前奏倒计时</n-text>

src/stores/setting.ts

+2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ interface SettingState {
8080
routeAnimation: "none" | "fade" | "zoom" | "slide" | "up";
8181
useRealIP: boolean;
8282
realIP: string;
83+
fullPlayerCache: boolean;
8384
}
8485

8586
export const useSettingStore = defineStore({
@@ -104,6 +105,7 @@ export const useSettingStore = defineStore({
104105
showTaskbarProgress: false, // 显示任务栏进度
105106
checkUpdateOnStart: true, // 启动时检查更新
106107
preventSleep: false, // 是否禁止休眠
108+
fullPlayerCache: false, // 全屏播放器缓存
107109
// 播放
108110
songLevel: "exhigh", // 音质
109111
playDevice: "default", // 播放设备

src/utils/auth.ts

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { openUserLogin } from "./modal";
1919
import { debounce } from "lodash-es";
2020
import { isBeforeSixAM } from "./time";
2121
import { dailyRecommend } from "@/api/rec";
22+
import { isElectron } from "./helper";
2223

2324
// 是否登录
2425
export const isLogin = () => !!getCookie("MUSIC_U");
@@ -182,6 +183,8 @@ export const toLikeSong = debounce(
182183
}
183184
// 更新
184185
dataStore.setUserLikeData("songs", likeList);
186+
// ipc
187+
if (isElectron) window.electron.ipcRenderer.send("like-status-change", like);
185188
} else {
186189
window.$message.error(`${like ? "喜欢" : "取消"}音乐时发生错误`);
187190
return;

src/utils/format.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ export const formatSongsList = (data: any[]): SongType[] => {
4545
name: (item.album || item.al)?.name,
4646
cover: (item.album || item.al)?.picUrl,
4747
},
48-
alia: isArray(item.alia || item.alias || item.transNames)
49-
? item.alia?.[0] || item.alias?.[0] || item.transNames?.[0]
48+
alia: isArray(item.alia || item.alias || item.transNames || item.tns)
49+
? item.alia?.[0] || item.alias?.[0] || item.transNames?.[0] || item.tns?.[0]
5050
: item.alia,
5151
dj: item.dj
5252
? {

src/utils/initIpc.ts

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { isElectron } from "./helper";
22
import { openUpdateApp } from "./modal";
3+
import { useMusicStore, useDataStore } from "@/stores";
34
import player from "./player";
5+
import { toLikeSong } from "./auth";
46

57
// 全局 IPC 事件
68
const initIpc = () => {
@@ -22,6 +24,12 @@ const initIpc = () => {
2224
window.electron.ipcRenderer.on("volumeDown", () => player.setVolume("down"));
2325
// 播放模式切换
2426
window.electron.ipcRenderer.on("changeMode", (_, mode) => player.togglePlayMode(mode));
27+
// 喜欢歌曲
28+
window.electron.ipcRenderer.on("toogleLikeSong", async () => {
29+
const dataStore = useDataStore();
30+
const musicStore = useMusicStore();
31+
await toLikeSong(musicStore.playSong, !dataStore.isLikeSong(musicStore.playSong.id));
32+
});
2533
// 桌面歌词开关
2634
window.electron.ipcRenderer.on("toogleDesktopLyric", () => player.toggleDesktopLyric());
2735
window.electron.ipcRenderer.on("closeDesktopLyric", () => player.toggleDesktopLyric());

0 commit comments

Comments
 (0)