feat: 优化主入口代码 添加歌曲下载功能

This commit is contained in:
alger
2025-01-02 00:14:05 +08:00
parent 38a9d6ed31
commit 018218a5bf
12 changed files with 441 additions and 172 deletions
-2
View File
@@ -60,12 +60,10 @@ const copyQQ = () => {
defineProps({
alipayQR: {
type: String,
required: true,
default: alipay
},
wechatQR: {
type: String,
required: true,
default: wechat
}
});
+103 -8
View File
@@ -1,5 +1,5 @@
<template>
<div class="song-item" :class="{ 'song-mini': mini, 'song-list': list }">
<div class="song-item" :class="{ 'song-mini': mini, 'song-list': list }" @contextmenu.prevent="handleContextMenu">
<n-image
v-if="item.picUrl"
ref="songImg"
@@ -57,17 +57,30 @@
<i v-else class="iconfont icon-playfill"></i>
</div>
</div>
<n-dropdown
v-if="isElectron"
:show="showDropdown"
:options="dropdownOptions"
:x="dropdownX"
:y="dropdownY"
placement="bottom-start"
@clickoutside="showDropdown = false"
@select="handleSelect"
/>
</div>
</template>
<script lang="ts" setup>
import { computed, useTemplateRef } from 'vue';
import { computed, h, ref, useTemplateRef } from 'vue';
import { useStore } from 'vuex';
import { useMessage } from 'naive-ui';
import type { MenuOption } from 'naive-ui';
import { audioService } from '@/services/audioService';
import type { SongResult } from '@/type/music';
import { getImgUrl } from '@/utils';
import { getImgUrl, isElectron } from '@/utils';
import { getImageBackground } from '@/utils/linearColor';
import { getSongUrl } from '@/hooks/MusicListHook';
const props = withDefaults(
defineProps<{
@@ -84,22 +97,96 @@ const props = withDefaults(
);
const store = useStore();
const message = useMessage();
const play = computed(() => store.state.play as boolean);
const playMusic = computed(() => store.state.playMusic);
const playLoading = computed(
() => playMusic.value.id === props.item.id && playMusic.value.playLoading
);
// 判断是否为正在播放的音乐
const isPlaying = computed(() => {
return playMusic.value.id === props.item.id;
});
const emits = defineEmits(['play']);
const showDropdown = ref(false);
const dropdownX = ref(0);
const dropdownY = ref(0);
const isDownloading = ref(false);
const dropdownOptions = computed<MenuOption[]>(() => [
{
label: isDownloading.value ? '下载中...' : '下载 ' + props.item.name,
key: 'download',
icon: () => h('i', { class: 'iconfont ri-download-line' }),
disabled: isDownloading.value
}
]);
const handleContextMenu = (e: MouseEvent) => {
e.preventDefault();
showDropdown.value = true;
dropdownX.value = e.clientX;
dropdownY.value = e.clientY;
};
const handleSelect = (key: string | number) => {
showDropdown.value = false;
if (key === 'download') {
downloadMusic();
}
};
// 下载音乐
const downloadMusic = async () => {
if (isDownloading.value) {
message.warning('正在下载中,请稍候...');
return;
}
try {
isDownloading.value = true;
const loadingMessage = message.loading('正在下载中...', { duration: 0 });
const url = await getSongUrl(props.item.id);
if (!url) {
loadingMessage.destroy();
message.error('获取音乐下载地址失败');
isDownloading.value = false;
return;
}
// 先移除可能存在的旧监听器
window.electron.ipcRenderer.removeAllListeners('music-download-complete');
// 发送下载请求
window.electron.ipcRenderer.send('download-music', {
url,
filename: `${props.item.name} - ${(props.item.ar || props.item.song?.artists)?.map(a => a.name).join(',')}`
});
// 添加新的一次性监听器
window.electron.ipcRenderer.once('music-download-complete', (_, result) => {
isDownloading.value = false;
loadingMessage.destroy();
if (result.success) {
message.success('下载成功');
} else if (result.canceled) {
// 用户取消了保存
message.info('已取消下载');
} else {
message.error(`下载失败: ${result.error}`);
}
});
} catch (error) {
isDownloading.value = false;
message.destroyAll();
message.error('下载失败');
}
};
const emits = defineEmits(['play']);
const songImageRef = useTemplateRef('songImg');
const imageLoad = async () => {
@@ -207,6 +294,14 @@ const toggleFavorite = async (e: Event) => {
@apply bg-green-500 border-green-500 text-white;
}
}
&-download {
@apply mr-2 cursor-pointer;
.iconfont {
@apply text-xl transition text-gray-500 dark:text-gray-400 hover:text-green-500;
}
}
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ import { getImageLinearBackground } from '@/utils/linearColor';
const musicHistory = useMusicHistory();
// 获取歌曲url
const getSongUrl = async (id: number) => {
export const getSongUrl = async (id: number) => {
const { data } = await getMusicUrl(id);
let url = '';
try {
+1 -1
View File
@@ -27,7 +27,7 @@
<!-- 底部音乐播放 -->
<play-bar v-if="isPlay" />
</div>
<install-app-modal></install-app-modal>
<install-app-modal v-if="!isElectron"></install-app-modal>
<update-modal />
</div>
</template>
+32
View File
@@ -0,0 +1,32 @@
import type { MessageApi } from 'naive-ui';
/**
* 选择目录
* @param message MessageApi 实例
* @returns Promise<string | undefined> 返回选择的目录路径,如果取消则返回 undefined
*/
export const selectDirectory = async (message: MessageApi): Promise<string | undefined> => {
try {
const result = await window.electron.ipcRenderer.invoke('select-directory');
if (result.filePaths?.[0]) {
return result.filePaths[0];
}
} catch (error) {
message.error('选择目录失败');
}
return undefined;
};
/**
* 打开目录
* @param path 要打开的目录路径
* @param message MessageApi 实例
* @param showTip 是否显示提示信息
*/
export const openDirectory = (path: string | undefined, message: MessageApi, showTip = true) => {
if (path) {
window.electron.ipcRenderer.send('open-directory', path);
} else if (showTip) {
message.info('目录不存在');
}
};
+29 -2
View File
@@ -55,6 +55,18 @@
</div>
</div>
</div>
<div class="set-item" v-if="isElectron">
<div>
<div class="set-item-title">下载目录</div>
<div class="set-item-content">
{{ setData.downloadPath || '默认下载目录' }}
</div>
</div>
<div class="flex items-center gap-2">
<n-button size="small" @click="openDownloadPath">打开目录</n-button>
<n-button size="small" @click="selectDownloadPath">修改目录</n-button>
</div>
</div>
<div class="set-item">
<div>
<div class="set-item-title">版本</div>
@@ -97,7 +109,7 @@
</Coffee>
</div>
<div>
<n-button type="primary" @click="openAuthor">前往github</n-button>
<n-button size="small" type="primary" @click="openAuthor"><i class="ri-github-line"></i>前往github</n-button>
</div>
</div>
<div class="set-item" v-if="isElectron">
@@ -118,6 +130,7 @@ import { useStore } from 'vuex';
import { useMessage } from 'naive-ui';
import { isElectron } from '@/utils';
import { checkUpdate, UpdateResult } from '@/utils/update';
import { selectDirectory, openDirectory } from '@/utils/fileOperation';
import config from '../../../../package.json';
import PlayBottom from '@/components/common/PlayBottom.vue';
import Coffee from '@/components/Coffee.vue';
@@ -176,6 +189,20 @@ const openReleasePage = () => {
window.open(updateInfo.value.releaseInfo?.html_url || 'https://github.com/algerkong/AlgerMusicPlayer/releases/latest', '_blank');
};
const selectDownloadPath = async () => {
const path = await selectDirectory(message);
if (path) {
store.commit('setSetData', {
...setData.value,
downloadPath: path
});
}
};
const openDownloadPath = () => {
openDirectory(setData.value.downloadPath, message);
};
onMounted(() => {
checkForUpdates();
});
@@ -183,7 +210,7 @@ onMounted(() => {
<style lang="scss" scoped>
.set-page {
@apply p-4 bg-light dark:bg-dark;
@apply p-4 bg-light dark:bg-dark pb-20;
}
.set-item {