mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-14 14:50:50 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d449930a02 | ||
|
|
820509dbea | ||
|
|
1493ab7317 | ||
|
|
c6ca63ee11 | ||
|
|
4fa1295b84 |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,19 +1,11 @@
|
|||||||
# 更新日志
|
# 更新日志
|
||||||
|
|
||||||
## v3.9.0
|
## v3.9.2
|
||||||
### ✨ 新功能
|
|
||||||
- 添加歌曲右键菜单功能,支持添加到歌单、创建歌单、取消收藏等操作
|
|
||||||
- 添加下一首播放功能(右键歌曲)
|
|
||||||
- 添加自动播放和自动保存正在播放列表功能(设置->播放设置->自动播放)
|
|
||||||
- 优化歌词滚动体验
|
|
||||||
|
|
||||||
### ⚡ 优化
|
|
||||||
- 升级 Electron 版本和相关依赖包
|
|
||||||
- 优化播放体验和代码结构
|
|
||||||
|
|
||||||
### 🐞 修复
|
### 🐞 修复
|
||||||
- 修复我的收藏查看更多跳转空白页的问题
|
- 修复下载功能导致的登陆失败问题
|
||||||
|
- 优化下载功能
|
||||||
|
- 添加下载按钮显隐配置 默认隐藏(设置页面配置)
|
||||||
|
|
||||||
## 咖啡☕️
|
## 咖啡☕️
|
||||||
| 微信 | | 支付宝 |
|
| 微信 | | 支付宝 |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "AlgerMusicPlayer",
|
"name": "AlgerMusicPlayer",
|
||||||
"version": "3.9.0",
|
"version": "3.9.2",
|
||||||
"description": "Alger Music Player",
|
"description": "Alger Music Player",
|
||||||
"author": "Alger <algerkc@qq.com>",
|
"author": "Alger <algerkc@qq.com>",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
|
|||||||
@@ -60,13 +60,22 @@ export function initializeFileManager() {
|
|||||||
// 通用的打开目录处理
|
// 通用的打开目录处理
|
||||||
ipcMain.on('open-directory', (_, filePath) => {
|
ipcMain.on('open-directory', (_, filePath) => {
|
||||||
try {
|
try {
|
||||||
if (fs.statSync(filePath).isDirectory()) {
|
// 验证文件路径
|
||||||
shell.openPath(filePath);
|
if (!filePath) {
|
||||||
|
console.error('无效的文件路径: 路径为空');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一处理路径分隔符
|
||||||
|
const normalizedPath = path.normalize(filePath);
|
||||||
|
|
||||||
|
if (fs.statSync(normalizedPath).isDirectory()) {
|
||||||
|
shell.openPath(normalizedPath);
|
||||||
} else {
|
} else {
|
||||||
shell.showItemInFolder(filePath);
|
shell.showItemInFolder(normalizedPath);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error opening path:', error);
|
console.error('打开路径失败:', error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,5 +18,6 @@
|
|||||||
"fontFamily": "system-ui",
|
"fontFamily": "system-ui",
|
||||||
"fontScope": "global",
|
"fontScope": "global",
|
||||||
"autoPlay": false,
|
"autoPlay": false,
|
||||||
"downloadPath": ""
|
"downloadPath": "",
|
||||||
|
"alwaysShowDownloadButton": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,16 +13,25 @@ export const getMusicQualityDetail = (id: number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 根据音乐Id获取音乐播放URl
|
// 根据音乐Id获取音乐播放URl
|
||||||
export const getMusicUrl = async (id: number) => {
|
export const getMusicUrl = async (id: number, isDownloaded: boolean = false) => {
|
||||||
const res = await request.get('/song/download/url/v1', {
|
// 判断是否登录
|
||||||
params: {
|
try {
|
||||||
id,
|
if (store.state.user && isDownloaded && store.state.user.vipType !== 0) {
|
||||||
level: store.state.setData.musicQuality || 'higher'
|
const url = '/song/download/url/v1';
|
||||||
}
|
const res = await request.get(url, {
|
||||||
});
|
params: {
|
||||||
|
id,
|
||||||
|
level: store.state.setData.musicQuality || 'higher',
|
||||||
|
cookie: `${localStorage.getItem('token')} os=pc;`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (res.data.data.url) {
|
if (res.data.data.url) {
|
||||||
return { data: { data: [{ ...res.data.data }] } };
|
return { data: { data: [{ ...res.data.data }] } };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('error', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await request.get('/song/url/v1', {
|
return await request.get('/song/url/v1', {
|
||||||
@@ -80,8 +89,10 @@ export const likeSong = (id: number, like: boolean = true) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 获取用户喜欢的音乐列表
|
// 获取用户喜欢的音乐列表
|
||||||
export const getLikedList = () => {
|
export const getLikedList = (uid: number) => {
|
||||||
return request.get('/likelist');
|
return request.get('/likelist', {
|
||||||
|
params: { uid }
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 创建歌单
|
// 创建歌单
|
||||||
|
|||||||
4
src/renderer/components.d.ts
vendored
4
src/renderer/components.d.ts
vendored
@@ -27,14 +27,11 @@ declare module 'vue' {
|
|||||||
NInput: typeof import('naive-ui')['NInput']
|
NInput: typeof import('naive-ui')['NInput']
|
||||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||||
NLayout: typeof import('naive-ui')['NLayout']
|
NLayout: typeof import('naive-ui')['NLayout']
|
||||||
NList: typeof import('naive-ui')['NList']
|
|
||||||
NListItem: typeof import('naive-ui')['NListItem']
|
|
||||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
NModal: typeof import('naive-ui')['NModal']
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
NPopover: typeof import('naive-ui')['NPopover']
|
NPopover: typeof import('naive-ui')['NPopover']
|
||||||
NProgress: typeof import('naive-ui')['NProgress']
|
NProgress: typeof import('naive-ui')['NProgress']
|
||||||
NRadio: typeof import('naive-ui')['NRadio']
|
NRadio: typeof import('naive-ui')['NRadio']
|
||||||
NRadioButton: typeof import('naive-ui')['NRadioButton']
|
|
||||||
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
|
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
|
||||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||||
NSelect: typeof import('naive-ui')['NSelect']
|
NSelect: typeof import('naive-ui')['NSelect']
|
||||||
@@ -46,7 +43,6 @@ declare module 'vue' {
|
|||||||
NTabs: typeof import('naive-ui')['NTabs']
|
NTabs: typeof import('naive-ui')['NTabs']
|
||||||
NTag: typeof import('naive-ui')['NTag']
|
NTag: typeof import('naive-ui')['NTag']
|
||||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||||
NTransfer: typeof import('naive-ui')['NTransfer']
|
|
||||||
NVirtualList: typeof import('naive-ui')['NVirtualList']
|
NVirtualList: typeof import('naive-ui')['NVirtualList']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="download-drawer-trigger">
|
<div class="download-drawer-trigger">
|
||||||
<n-badge :value="downloadingCount" :max="99" :show="downloadingCount > 0">
|
<n-badge :value="downloadingCount" :max="99" :show="downloadingCount > 0">
|
||||||
<n-button circle @click="showDrawer = true">
|
<n-button circle @click="store.commit('setShowDownloadDrawer', true)">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="iconfont ri-download-cloud-2-line"></i>
|
<i class="iconfont ri-download-cloud-2-line"></i>
|
||||||
</template>
|
</template>
|
||||||
@@ -9,7 +9,12 @@
|
|||||||
</n-badge>
|
</n-badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<n-drawer v-model:show="showDrawer" :height="'80%'" placement="bottom">
|
<n-drawer
|
||||||
|
v-model:show="showDrawer"
|
||||||
|
:height="'80%'"
|
||||||
|
placement="bottom"
|
||||||
|
@after-leave="handleDrawerClose"
|
||||||
|
>
|
||||||
<n-drawer-content title="下载管理" closable :native-scrollbar="false">
|
<n-drawer-content title="下载管理" closable :native-scrollbar="false">
|
||||||
<div class="drawer-container">
|
<div class="drawer-container">
|
||||||
<n-tabs type="line" animated class="h-full">
|
<n-tabs type="line" animated class="h-full">
|
||||||
@@ -85,7 +90,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else class="downloaded-content">
|
<div v-else class="downloaded-content">
|
||||||
<div class="downloaded-items">
|
<div class="downloaded-items">
|
||||||
<div v-for="item in downloadedList" :key="item.path" class="downloaded-item">
|
<div v-for="item in downList" :key="item.path" class="downloaded-item">
|
||||||
<div class="downloaded-item-content">
|
<div class="downloaded-item-content">
|
||||||
<div class="downloaded-item-cover">
|
<div class="downloaded-item-cover">
|
||||||
<n-image
|
<n-image
|
||||||
@@ -105,11 +110,11 @@
|
|||||||
<div class="downloaded-item-size">{{ formatSize(item.size) }}</div>
|
<div class="downloaded-item-size">{{ formatSize(item.size) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="downloaded-item-actions">
|
<div class="downloaded-item-actions">
|
||||||
<n-button text type="primary" size="large" @click="handlePlayMusic(item)">
|
<!-- <n-button text type="primary" size="large" @click="handlePlayMusic(item)">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="iconfont ri-play-circle-line text-xl"></i>
|
<i class="iconfont ri-play-circle-line text-xl"></i>
|
||||||
</template>
|
</template>
|
||||||
</n-button>
|
</n-button> -->
|
||||||
<n-button
|
<n-button
|
||||||
text
|
text
|
||||||
type="primary"
|
type="primary"
|
||||||
@@ -162,7 +167,7 @@ import { computed, onMounted, ref } from 'vue';
|
|||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
|
|
||||||
import { getMusicDetail } from '@/api/music';
|
import { getMusicDetail } from '@/api/music';
|
||||||
import { audioService } from '@/services/audioService';
|
// import { audioService } from '@/services/audioService';
|
||||||
import { getImgUrl } from '@/utils';
|
import { getImgUrl } from '@/utils';
|
||||||
|
|
||||||
interface DownloadItem {
|
interface DownloadItem {
|
||||||
@@ -185,15 +190,26 @@ interface DownloadedItem {
|
|||||||
ar: { name: string }[];
|
ar: { name: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = useStore();
|
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
const showDrawer = ref(false);
|
const store = useStore();
|
||||||
|
|
||||||
|
const showDrawer = computed({
|
||||||
|
get: () => store.state.showDownloadDrawer,
|
||||||
|
set: (val) => store.commit('setShowDownloadDrawer', val)
|
||||||
|
});
|
||||||
|
|
||||||
const downloadList = ref<DownloadItem[]>([]);
|
const downloadList = ref<DownloadItem[]>([]);
|
||||||
const downloadedList = ref<DownloadedItem[]>([]);
|
const downloadedList = ref<DownloadedItem[]>(
|
||||||
|
JSON.parse(localStorage.getItem('downloadedList') || '[]')
|
||||||
|
);
|
||||||
|
|
||||||
|
const downList = computed(() => {
|
||||||
|
return (downloadedList.value as DownloadedItem[]).reverse();
|
||||||
|
});
|
||||||
|
|
||||||
// 获取播放状态
|
// 获取播放状态
|
||||||
const play = computed(() => store.state.play as boolean);
|
// const play = computed(() => store.state.play as boolean);
|
||||||
const currentMusic = computed(() => store.state.playMusic);
|
// const currentMusic = computed(() => store.state.playMusic);
|
||||||
|
|
||||||
// 计算下载中的任务数量
|
// 计算下载中的任务数量
|
||||||
const downloadingCount = computed(() => {
|
const downloadingCount = computed(() => {
|
||||||
@@ -264,8 +280,7 @@ const formatSize = (bytes: number) => {
|
|||||||
|
|
||||||
// 打开目录
|
// 打开目录
|
||||||
const openDirectory = (path: string) => {
|
const openDirectory = (path: string) => {
|
||||||
const directory = path.substring(0, path.lastIndexOf('/'));
|
window.electron.ipcRenderer.send('open-directory', path);
|
||||||
window.electron.ipcRenderer.send('open-directory', directory);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除相关
|
// 删除相关
|
||||||
@@ -288,6 +303,14 @@ const confirmDelete = async () => {
|
|||||||
itemToDelete.value.path
|
itemToDelete.value.path
|
||||||
);
|
);
|
||||||
if (success) {
|
if (success) {
|
||||||
|
localStorage.setItem(
|
||||||
|
'downloadedList',
|
||||||
|
JSON.stringify(
|
||||||
|
downloadedList.value.filter(
|
||||||
|
(item) => item.id !== (itemToDelete.value as DownloadedItem).id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
await refreshDownloadedList();
|
await refreshDownloadedList();
|
||||||
message.success('删除成功');
|
message.success('删除成功');
|
||||||
} else {
|
} else {
|
||||||
@@ -303,58 +326,59 @@ const confirmDelete = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 播放音乐
|
// 播放音乐
|
||||||
const handlePlayMusic = async (item: DownloadedItem) => {
|
// const handlePlayMusic = async (item: DownloadedItem) => {
|
||||||
// 确保路径正确编码
|
// // 确保路径正确编码
|
||||||
const encodedPath = encodeURIComponent(item.path);
|
// const encodedPath = encodeURIComponent(item.path);
|
||||||
const localUrl = `local://${encodedPath}`;
|
// const localUrl = `local://${encodedPath}`;
|
||||||
|
|
||||||
const musicInfo = {
|
// const musicInfo = {
|
||||||
name: item.filename,
|
// name: item.filename,
|
||||||
id: item.id,
|
// id: item.id,
|
||||||
url: localUrl,
|
// url: localUrl,
|
||||||
playMusicUrl: localUrl,
|
// playMusicUrl: localUrl,
|
||||||
picUrl: item.picUrl,
|
// picUrl: item.picUrl,
|
||||||
ar: item.ar || [{ name: '本地音乐' }],
|
// ar: item.ar || [{ name: '本地音乐' }],
|
||||||
song: {
|
// song: {
|
||||||
artists: item.ar || [{ name: '本地音乐' }]
|
// artists: item.ar || [{ name: '本地音乐' }]
|
||||||
},
|
// },
|
||||||
al: {
|
// al: {
|
||||||
picUrl: item.picUrl || '/images/default_cover.png'
|
// picUrl: item.picUrl || '/images/default_cover.png'
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
// 如果是当前播放的音乐,则切换播放状态
|
// // 如果是当前播放的音乐,则切换播放状态
|
||||||
if (currentMusic.value?.id === item.id) {
|
// if (currentMusic.value?.id === item.id) {
|
||||||
if (play.value) {
|
// if (play.value) {
|
||||||
audioService.getCurrentSound()?.pause();
|
// audioService.getCurrentSound()?.pause();
|
||||||
store.commit('setPlayMusic', false);
|
// store.commit('setPlayMusic', false);
|
||||||
} else {
|
// } else {
|
||||||
audioService.getCurrentSound()?.play();
|
// audioService.getCurrentSound()?.play();
|
||||||
store.commit('setPlayMusic', true);
|
// store.commit('setPlayMusic', true);
|
||||||
}
|
// }
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 播放新的音乐
|
// // 播放新的音乐
|
||||||
store.commit('setPlay', musicInfo);
|
// store.commit('setPlay', musicInfo);
|
||||||
store.commit('setPlayMusic', true);
|
// store.commit('setPlayMusic', true);
|
||||||
store.commit('setIsPlay', true);
|
// store.commit('setIsPlay', true);
|
||||||
|
|
||||||
store.commit(
|
// store.commit(
|
||||||
'setPlayList',
|
// 'setPlayList',
|
||||||
downloadedList.value.map((item) => ({
|
// downloadedList.value.map((item) => ({
|
||||||
...item,
|
// ...item,
|
||||||
playMusicUrl: `local://${encodeURIComponent(item.path)}`
|
// playMusicUrl: `local://${encodeURIComponent(item.path)}`
|
||||||
}))
|
// }))
|
||||||
);
|
// );
|
||||||
};
|
// };
|
||||||
|
|
||||||
// 获取已下载音乐列表
|
// 获取已下载音乐列表
|
||||||
const refreshDownloadedList = async () => {
|
const refreshDownloadedList = async () => {
|
||||||
try {
|
try {
|
||||||
|
let saveList: any = [];
|
||||||
const list = await window.electron.ipcRenderer.invoke('get-downloaded-music');
|
const list = await window.electron.ipcRenderer.invoke('get-downloaded-music');
|
||||||
if (!Array.isArray(list) || list.length === 0) {
|
if (!Array.isArray(list) || list.length === 0) {
|
||||||
downloadedList.value = [];
|
saveList = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,7 +393,7 @@ const refreshDownloadedList = async () => {
|
|||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
downloadedList.value = list.map((item) => {
|
saveList = list.map((item) => {
|
||||||
const songDetail = songDetails[item.id];
|
const songDetail = songDetails[item.id];
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
@@ -379,17 +403,29 @@ const refreshDownloadedList = async () => {
|
|||||||
});
|
});
|
||||||
} catch (detailError) {
|
} catch (detailError) {
|
||||||
console.error('Failed to get music details:', detailError);
|
console.error('Failed to get music details:', detailError);
|
||||||
downloadedList.value = list;
|
saveList = list;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
downloadedList.value = list;
|
saveList = list;
|
||||||
}
|
}
|
||||||
|
setLocalDownloadedList(saveList);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get downloaded music list:', error);
|
console.error('Failed to get downloaded music list:', error);
|
||||||
downloadedList.value = [];
|
downloadedList.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setLocalDownloadedList = (list: DownloadedItem[]) => {
|
||||||
|
const localList = localStorage.getItem('downloadedList');
|
||||||
|
// 合并 去重
|
||||||
|
const saveList = [...(localList ? JSON.parse(localList) : []), ...list];
|
||||||
|
const uniqueList = saveList.filter(
|
||||||
|
(item, index, self) => index === self.findIndex((t) => t.id === item.id)
|
||||||
|
);
|
||||||
|
localStorage.setItem('downloadedList', JSON.stringify(uniqueList));
|
||||||
|
downloadedList.value = uniqueList;
|
||||||
|
};
|
||||||
|
|
||||||
// 监听抽屉显示状态
|
// 监听抽屉显示状态
|
||||||
watch(
|
watch(
|
||||||
() => showDrawer.value,
|
() => showDrawer.value,
|
||||||
@@ -465,6 +501,10 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleDrawerClose = () => {
|
||||||
|
store.commit('setShowDownloadDrawer', false);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -207,6 +207,11 @@ const dropdownOptions = computed<MenuOption[]>(() => {
|
|||||||
type: 'divider',
|
type: 'divider',
|
||||||
key: 'd1'
|
key: 'd1'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '下载歌曲',
|
||||||
|
key: 'download',
|
||||||
|
icon: () => h('i', { class: 'iconfont ri-download-line' })
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '添加到歌单',
|
label: '添加到歌单',
|
||||||
key: 'addToPlaylist',
|
key: 'addToPlaylist',
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const musicHistory = useMusicHistory();
|
|||||||
|
|
||||||
// 获取歌曲url
|
// 获取歌曲url
|
||||||
export const getSongUrl = async (id: number, songData: any, isDownloaded: boolean = false) => {
|
export const getSongUrl = async (id: number, songData: any, isDownloaded: boolean = false) => {
|
||||||
const { data } = await getMusicUrl(id);
|
const { data } = await getMusicUrl(id, isDownloaded);
|
||||||
let url = '';
|
let url = '';
|
||||||
let songDetail = null;
|
let songDetail = null;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -27,7 +27,14 @@
|
|||||||
<!-- 底部音乐播放 -->
|
<!-- 底部音乐播放 -->
|
||||||
<play-bar v-show="isPlay" :style="isMobile && store.state.musicFull ? 'bottom: 0;' : ''" />
|
<play-bar v-show="isPlay" :style="isMobile && store.state.musicFull ? 'bottom: 0;' : ''" />
|
||||||
<!-- 下载管理抽屉 -->
|
<!-- 下载管理抽屉 -->
|
||||||
<download-drawer v-if="isElectron" />
|
<download-drawer
|
||||||
|
v-if="
|
||||||
|
isElectron &&
|
||||||
|
(store.state.setData?.alwaysShowDownloadButton ||
|
||||||
|
store.state.showDownloadDrawer ||
|
||||||
|
store.state.hasDownloadingTasks)
|
||||||
|
"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<install-app-modal v-if="!isElectron"></install-app-modal>
|
<install-app-modal v-if="!isElectron"></install-app-modal>
|
||||||
<update-modal v-if="isElectron" />
|
<update-modal v-if="isElectron" />
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ import { useRouter } from 'vue-router';
|
|||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
|
|
||||||
import { getSearchKeyword } from '@/api/home';
|
import { getSearchKeyword } from '@/api/home';
|
||||||
import { getUserDetail, logout } from '@/api/login';
|
import { getUserDetail } from '@/api/login';
|
||||||
import alipay from '@/assets/alipay.png';
|
import alipay from '@/assets/alipay.png';
|
||||||
import wechat from '@/assets/wechat.png';
|
import wechat from '@/assets/wechat.png';
|
||||||
import Coffee from '@/components/Coffee.vue';
|
import Coffee from '@/components/Coffee.vue';
|
||||||
@@ -132,8 +132,10 @@ const loadPage = async () => {
|
|||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
const { data } = await getUserDetail();
|
const { data } = await getUserDetail();
|
||||||
store.state.user = data.profile;
|
console.log('data', data);
|
||||||
localStorage.setItem('user', JSON.stringify(data.profile));
|
store.state.user =
|
||||||
|
data.profile || store.state.user || JSON.parse(localStorage.getItem('user') || '{}');
|
||||||
|
localStorage.setItem('user', JSON.stringify(store.state.user));
|
||||||
};
|
};
|
||||||
|
|
||||||
loadPage();
|
loadPage();
|
||||||
@@ -202,10 +204,7 @@ const selectItem = async (key: string) => {
|
|||||||
// switch 判断
|
// switch 判断
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'logout':
|
case 'logout':
|
||||||
logout().then(() => {
|
store.commit('logout');
|
||||||
store.commit('logout');
|
|
||||||
router.push('/login');
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case 'login':
|
case 'login':
|
||||||
router.push('/login');
|
router.push('/login');
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { createStore } from 'vuex';
|
import { createStore } from 'vuex';
|
||||||
|
|
||||||
import setData from '@/../main/set.json';
|
import setData from '@/../main/set.json';
|
||||||
|
import { logout } from '@/api/login';
|
||||||
import { getLikedList, likeSong } from '@/api/music';
|
import { getLikedList, likeSong } from '@/api/music';
|
||||||
import { useMusicListHook } from '@/hooks/MusicListHook';
|
import { useMusicListHook } from '@/hooks/MusicListHook';
|
||||||
import homeRouter from '@/router/home';
|
import homeRouter from '@/router/home';
|
||||||
@@ -79,7 +80,7 @@ export interface State {
|
|||||||
user: any;
|
user: any;
|
||||||
playList: SongResult[];
|
playList: SongResult[];
|
||||||
playListIndex: number;
|
playListIndex: number;
|
||||||
setData: any;
|
setData: typeof defaultSettings;
|
||||||
lyric: any;
|
lyric: any;
|
||||||
isMobile: boolean;
|
isMobile: boolean;
|
||||||
searchValue: string;
|
searchValue: string;
|
||||||
@@ -92,6 +93,7 @@ export interface State {
|
|||||||
showArtistDrawer: boolean;
|
showArtistDrawer: boolean;
|
||||||
currentArtistId: number | null;
|
currentArtistId: number | null;
|
||||||
systemFonts: { label: string; value: string }[];
|
systemFonts: { label: string; value: string }[];
|
||||||
|
showDownloadDrawer: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const state: State = {
|
const state: State = {
|
||||||
@@ -115,7 +117,8 @@ const state: State = {
|
|||||||
showUpdateModal: false,
|
showUpdateModal: false,
|
||||||
showArtistDrawer: false,
|
showArtistDrawer: false,
|
||||||
currentArtistId: null,
|
currentArtistId: null,
|
||||||
systemFonts: [{ label: '系统默认', value: 'system-ui' }]
|
systemFonts: [{ label: '系统默认', value: 'system-ui' }],
|
||||||
|
showDownloadDrawer: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const { handlePlayMusic, nextPlay, prevPlay } = useMusicListHook();
|
const { handlePlayMusic, nextPlay, prevPlay } = useMusicListHook();
|
||||||
@@ -231,9 +234,11 @@ const mutations = {
|
|||||||
state.showUpdateModal = value;
|
state.showUpdateModal = value;
|
||||||
},
|
},
|
||||||
logout(state: State) {
|
logout(state: State) {
|
||||||
state.user = null;
|
logout().then(() => {
|
||||||
localStorage.removeItem('user');
|
state.user = null;
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('user');
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
});
|
||||||
},
|
},
|
||||||
setShowArtistDrawer(state, show: boolean) {
|
setShowArtistDrawer(state, show: boolean) {
|
||||||
state.showArtistDrawer = show;
|
state.showArtistDrawer = show;
|
||||||
@@ -252,6 +257,9 @@ const mutations = {
|
|||||||
value: font
|
value: font
|
||||||
}))
|
}))
|
||||||
];
|
];
|
||||||
|
},
|
||||||
|
setShowDownloadDrawer(state: State, show: boolean) {
|
||||||
|
state.showDownloadDrawer = show;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -284,9 +292,9 @@ const actions = {
|
|||||||
const localList: number[] = localFavoriteList ? JSON.parse(localFavoriteList) : [];
|
const localList: number[] = localFavoriteList ? JSON.parse(localFavoriteList) : [];
|
||||||
|
|
||||||
// 如果用户已登录,尝试获取服务器收藏列表并合并
|
// 如果用户已登录,尝试获取服务器收藏列表并合并
|
||||||
if (state.user && localStorage.getItem('token')) {
|
if (state.user && state.user.userId) {
|
||||||
try {
|
try {
|
||||||
const res = await getLikedList();
|
const res = await getLikedList(state.user.userId);
|
||||||
if (res.data?.ids) {
|
if (res.data?.ids) {
|
||||||
// 合并本地和服务器的收藏列表,去重
|
// 合并本地和服务器的收藏列表,去重
|
||||||
const serverList = res.data.ids.reverse();
|
const serverList = res.data.ids.reverse();
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import axios, { InternalAxiosRequestConfig } from 'axios';
|
import axios, { InternalAxiosRequestConfig } from 'axios';
|
||||||
import { createDiscreteApi } from 'naive-ui';
|
|
||||||
|
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
|
|
||||||
import { isElectron } from '.';
|
import { isElectron } from '.';
|
||||||
|
|
||||||
const { notification } = createDiscreteApi(['notification']);
|
|
||||||
|
|
||||||
let setData: any = null;
|
let setData: any = null;
|
||||||
const getSetData = () => {
|
const getSetData = () => {
|
||||||
if (window.electron) {
|
if (window.electron) {
|
||||||
@@ -50,7 +47,7 @@ request.interceptors.request.use(
|
|||||||
};
|
};
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
if (token) {
|
if (token) {
|
||||||
config.params.cookie = `${token} os=pc;`;
|
config.params.cookie = config.params.cookie !== undefined ? config.params.cookie : token;
|
||||||
}
|
}
|
||||||
if (isElectron) {
|
if (isElectron) {
|
||||||
const proxyConfig = setData?.proxyConfig;
|
const proxyConfig = setData?.proxyConfig;
|
||||||
@@ -70,6 +67,8 @@ request.interceptors.request.use(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const NO_RETRY_URLS = ['暂时没有'];
|
||||||
|
|
||||||
// 响应拦截器
|
// 响应拦截器
|
||||||
request.interceptors.response.use(
|
request.interceptors.response.use(
|
||||||
(response) => {
|
(response) => {
|
||||||
@@ -88,28 +87,16 @@ request.interceptors.response.use(
|
|||||||
if (error.response?.status === 301) {
|
if (error.response?.status === 301) {
|
||||||
// 使用 store mutation 清除用户信息
|
// 使用 store mutation 清除用户信息
|
||||||
store.commit('logout');
|
store.commit('logout');
|
||||||
|
console.log(`301 状态码,清除登录信息后重试第 ${config.retryCount} 次`);
|
||||||
// 如果还可以重试,则重新发起请求
|
config.retryCount = 3;
|
||||||
if (config.retryCount === undefined || config.retryCount < MAX_RETRIES) {
|
|
||||||
config.retryCount = (config.retryCount || 1) + 1;
|
|
||||||
console.log(`301 状态码,清除登录信息后重试第 ${config.retryCount} 次`);
|
|
||||||
notification.error({
|
|
||||||
content: '登录状态失效,请重新登录',
|
|
||||||
meta: '请重新登录',
|
|
||||||
duration: 2500,
|
|
||||||
keepAliveOnHover: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// 延迟重试
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY));
|
|
||||||
|
|
||||||
// 重新发起请求
|
|
||||||
return request(config);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否还可以重试
|
// 检查是否还可以重试
|
||||||
if (config.retryCount !== undefined && config.retryCount < MAX_RETRIES) {
|
if (
|
||||||
|
config.retryCount !== undefined &&
|
||||||
|
config.retryCount < MAX_RETRIES &&
|
||||||
|
!NO_RETRY_URLS.includes(config.url as string)
|
||||||
|
) {
|
||||||
config.retryCount++;
|
config.retryCount++;
|
||||||
console.log(`请求重试第 ${config.retryCount} 次`);
|
console.log(`请求重试第 ${config.retryCount} 次`);
|
||||||
|
|
||||||
|
|||||||
@@ -272,9 +272,9 @@ const handleScroll = (e: any) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
store.dispatch('initializeFavoriteList');
|
await store.dispatch('initializeFavoriteList');
|
||||||
getFavoriteSongs();
|
await getFavoriteSongs();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听收藏列表变化
|
// 监听收藏列表变化
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const timerIsQr = (key: string) => {
|
|||||||
localStorage.setItem('token', data.cookie);
|
localStorage.setItem('token', data.cookie);
|
||||||
const user = await getUserDetail();
|
const user = await getUserDetail();
|
||||||
store.state.user = user.data.profile;
|
store.state.user = user.data.profile;
|
||||||
localStorage.setItem('user', JSON.stringify(store.state.user));
|
localStorage.setItem('user', JSON.stringify(user.data.profile));
|
||||||
message.success('登录成功');
|
message.success('登录成功');
|
||||||
|
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
|
|||||||
@@ -180,6 +180,24 @@
|
|||||||
<n-button size="small" @click="showShortcutModal = true">配置</n-button>
|
<n-button size="small" @click="showShortcutModal = true">配置</n-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="isElectron" class="set-item">
|
||||||
|
<div>
|
||||||
|
<div class="set-item-title">下载管理</div>
|
||||||
|
<div class="set-item-content">
|
||||||
|
<n-switch v-model:value="setData.alwaysShowDownloadButton" class="mr-2">
|
||||||
|
<template #checked>显示</template>
|
||||||
|
<template #unchecked>隐藏</template>
|
||||||
|
</n-switch>
|
||||||
|
是否始终显示下载列表按钮
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<n-button size="small" @click="store.commit('setShowDownloadDrawer', true)">
|
||||||
|
打开下载管理
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="set-item">
|
<div class="set-item">
|
||||||
<div>
|
<div>
|
||||||
<div class="set-item-title">下载目录</div>
|
<div class="set-item-title">下载目录</div>
|
||||||
@@ -728,8 +746,6 @@ const clearCache = async () => {
|
|||||||
localStorage.removeItem('favoriteList');
|
localStorage.removeItem('favoriteList');
|
||||||
break;
|
break;
|
||||||
case 'user':
|
case 'user':
|
||||||
localStorage.removeItem('user');
|
|
||||||
localStorage.removeItem('token');
|
|
||||||
store.commit('logout');
|
store.commit('logout');
|
||||||
break;
|
break;
|
||||||
case 'settings':
|
case 'settings':
|
||||||
|
|||||||
@@ -152,6 +152,10 @@ const loadPage = async () => {
|
|||||||
// 检查登录状态
|
// 检查登录状态
|
||||||
if (!checkLoginStatus()) return;
|
if (!checkLoginStatus()) return;
|
||||||
|
|
||||||
|
await loadData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
try {
|
try {
|
||||||
infoLoading.value = true;
|
infoLoading.value = true;
|
||||||
|
|
||||||
@@ -188,10 +192,10 @@ const loadPage = async () => {
|
|||||||
watch(
|
watch(
|
||||||
() => router.currentRoute.value.path,
|
() => router.currentRoute.value.path,
|
||||||
(newPath) => {
|
(newPath) => {
|
||||||
|
console.log('newPath', newPath);
|
||||||
if (newPath === '/user') {
|
if (newPath === '/user') {
|
||||||
checkLoginStatus();
|
checkLoginStatus();
|
||||||
} else {
|
loadData();
|
||||||
loadPage();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user