mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-24 16:27:23 +08:00
🔧 feat: 更新依赖版本 修复类型错误 优化首页推荐样式
This commit is contained in:
+24
-27
@@ -21,41 +21,40 @@
|
|||||||
"build:linux": "npm run build && electron-builder --linux"
|
"build:linux": "npm run build && electron-builder --linux"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-toolkit/preload": "^3.0.1",
|
"@electron-toolkit/preload": "^3.0.0",
|
||||||
"@electron-toolkit/utils": "^4.0.0",
|
"@electron-toolkit/utils": "^3.0.0",
|
||||||
"@unblockneteasemusic/server": "^0.27.8-patch.1",
|
"@unblockneteasemusic/server": "^0.27.8-patch.1",
|
||||||
"electron-store": "^8.1.1",
|
"electron-store": "^8.1.0",
|
||||||
"electron-updater": "^6.1.7",
|
"electron-updater": "^6.1.7",
|
||||||
"font-list": "^1.5.1",
|
"font-list": "^1.5.1",
|
||||||
"netease-cloud-music-api-alger": "^4.25.0",
|
"netease-cloud-music-api-alger": "^4.25.0",
|
||||||
"vue-i18n": "11.1.2"
|
"vue-i18n": "9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "^4.0.14",
|
|
||||||
"@electron-toolkit/eslint-config": "^1.0.2",
|
"@electron-toolkit/eslint-config": "^1.0.2",
|
||||||
"@electron-toolkit/eslint-config-ts": "^2.0.0",
|
"@electron-toolkit/eslint-config-ts": "^2.0.0",
|
||||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||||
"@rushstack/eslint-patch": "^1.10.3",
|
"@rushstack/eslint-patch": "^1.10.3",
|
||||||
"@tailwindcss/postcss7-compat": "^2.2.4",
|
"@tailwindcss/postcss7-compat": "^2.2.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
|
||||||
"@vue/eslint-config-typescript": "^13.0.0",
|
|
||||||
"@types/howler": "^2.2.12",
|
"@types/howler": "^2.2.12",
|
||||||
"@types/node": "^20.14.8",
|
"@types/node": "^20.14.8",
|
||||||
"@types/tinycolor2": "^1.4.6",
|
"@types/tinycolor2": "^1.4.6",
|
||||||
"@vitejs/plugin-vue": "^5.2.3",
|
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||||
"@vue/compiler-sfc": "^3.5.13",
|
"@typescript-eslint/parser": "^7.0.0",
|
||||||
|
"@vitejs/plugin-vue": "^5.0.5",
|
||||||
|
"@vue/compiler-sfc": "^3.5.0",
|
||||||
"@vue/eslint-config-prettier": "^9.0.0",
|
"@vue/eslint-config-prettier": "^9.0.0",
|
||||||
"@vue/runtime-core": "^3.5.13",
|
"@vue/eslint-config-typescript": "^13.0.0",
|
||||||
"@vueuse/core": "^13.0.0",
|
"@vue/runtime-core": "^3.5.0",
|
||||||
"@vueuse/electron": "^13.0.0",
|
"@vueuse/core": "^11.0.3",
|
||||||
|
"@vueuse/electron": "^11.0.3",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"axios": "^1.8.3",
|
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
|
"axios": "^1.7.7",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"electron": "^34.0.0",
|
"electron": "^35.0.2",
|
||||||
"electron-builder": "^25.1.8",
|
"electron-builder": "^25.1.8",
|
||||||
"electron-vite": "3.0.0",
|
"electron-vite": "^3.0.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
@@ -66,27 +65,25 @@
|
|||||||
"eslint-plugin-vue-scoped-css": "^2.7.2",
|
"eslint-plugin-vue-scoped-css": "^2.7.2",
|
||||||
"howler": "^2.2.4",
|
"howler": "^2.2.4",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"marked": "^15.0.7",
|
"marked": "^15.0.4",
|
||||||
"naive-ui": "^2.41.0",
|
"naive-ui": "^2.41.0",
|
||||||
"postcss": "^8.4.49",
|
|
||||||
"prettier": "^3.3.2",
|
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
"remixicon": "^4.2.0",
|
"postcss": "^8.5.3",
|
||||||
"sass": "^1.83.4",
|
"prettier": "^3.3.2",
|
||||||
|
"remixicon": "^4.6.0",
|
||||||
|
"sass": "^1.86.0",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"tinycolor2": "^1.6.0",
|
"tinycolor2": "^1.6.0",
|
||||||
"tunajs": "^1.0.15",
|
"tunajs": "^1.0.15",
|
||||||
"typescript": "^5.5.2",
|
"typescript": "^5.5.2",
|
||||||
"unplugin-auto-import": "^0.18.2",
|
"unplugin-auto-import": "^19.1.1",
|
||||||
"unplugin-vue-components": "^0.27.4",
|
"unplugin-vue-components": "^28.4.1",
|
||||||
"vfonts": "^0.1.0",
|
"vite": "^6.2.2",
|
||||||
"vite": "^5.3.1",
|
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-vue-devtools": "7.7.2",
|
"vite-plugin-vue-devtools": "7.7.2",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0",
|
||||||
"vue-tsc": "^2.0.22",
|
"vue-tsc": "^2.0.22"
|
||||||
"vuex": "^4.1.0"
|
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "com.alger.music",
|
"appId": "com.alger.music",
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ export default {
|
|||||||
closeTitle: 'Choose how to close',
|
closeTitle: 'Choose how to close',
|
||||||
minimizeToTray: 'Minimize to Tray',
|
minimizeToTray: 'Minimize to Tray',
|
||||||
exitApp: 'Exit App',
|
exitApp: 'Exit App',
|
||||||
rememberChoice: 'Remember my choice'
|
rememberChoice: 'Remember my choice',
|
||||||
|
closeApp: 'Close App'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ export default {
|
|||||||
closeTitle: '请选择关闭方式',
|
closeTitle: '请选择关闭方式',
|
||||||
minimizeToTray: '最小化到托盘',
|
minimizeToTray: '最小化到托盘',
|
||||||
exitApp: '退出应用',
|
exitApp: '退出应用',
|
||||||
rememberChoice: '记住我的选择'
|
rememberChoice: '记住我的选择',
|
||||||
|
closeApp: '关闭应用'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Vendored
+1
@@ -2,6 +2,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
// Generated by unplugin-vue-components
|
// Generated by unplugin-vue-components
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
// biome-ignore lint: disable
|
||||||
export {}
|
export {}
|
||||||
|
|
||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
|
|||||||
@@ -544,10 +544,6 @@ watch(showControls, (newValue) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const isMobile = computed(() => false); // TODO: 从 settings store 获取
|
const isMobile = computed(() => false); // TODO: 从 settings store 获取
|
||||||
|
|
||||||
const handlePlay = () => {
|
|
||||||
playerStore.setIsPlay(true);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -181,10 +181,11 @@ import { computed, onMounted, ref } from 'vue';
|
|||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import { getMusicDetail } from '@/api/music';
|
import { getMusicDetail } from '@/api/music';
|
||||||
import { usePlayerStore } from '@/store/modules/player';
|
// import { usePlayerStore } from '@/store/modules/player';
|
||||||
import { useSettingsStore } from '@/store/modules/settings';
|
import { useSettingsStore } from '@/store/modules/settings';
|
||||||
// import { audioService } from '@/services/audioService';
|
// import { audioService } from '@/services/audioService';
|
||||||
import { getImgUrl } from '@/utils';
|
import { getImgUrl } from '@/utils';
|
||||||
|
// import { SongResult } from '@/type/music';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -209,7 +210,7 @@ interface DownloadedItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
const playerStore = usePlayerStore();
|
// const playerStore = usePlayerStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
|
||||||
const showDrawer = computed({
|
const showDrawer = computed({
|
||||||
@@ -343,11 +344,11 @@ const confirmDelete = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 播放音乐
|
// 播放音乐
|
||||||
const handlePlay = async (musicInfo: SongResult) => {
|
// const handlePlay = async (musicInfo: SongResult) => {
|
||||||
await playerStore.setPlay(musicInfo);
|
// await playerStore.setPlay(musicInfo);
|
||||||
playerStore.setPlayMusic(true);
|
// playerStore.setPlayMusic(true);
|
||||||
playerStore.setIsPlay(true);
|
// playerStore.setIsPlay(true);
|
||||||
};
|
// };
|
||||||
|
|
||||||
// 获取已下载音乐列表
|
// 获取已下载音乐列表
|
||||||
const refreshDownloadedList = async () => {
|
const refreshDownloadedList = async () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="search-item" :class="[item.type, shape]" @click="handleClick">
|
<div class="search-item" :class="[shape, item.type]" @click="handleClick">
|
||||||
<div class="search-item-img">
|
<div class="search-item-img">
|
||||||
<n-image
|
<n-image
|
||||||
class="w-full h-full"
|
class="w-full h-full"
|
||||||
@@ -107,16 +107,15 @@ const handleClick = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (props.item.type === 'mv') {
|
if (props.item.type === 'mv') {
|
||||||
handleShowMv(getCurrentMv());
|
handleShowMv();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleShowMv = async (item: IMvItem) => {
|
const handleShowMv = async () => {
|
||||||
playerStore.setIsPlay(false);
|
playerStore.setIsPlay(false);
|
||||||
playerStore.setPlayMusic(false);
|
playerStore.setPlayMusic(false);
|
||||||
audioService.getCurrentSound()?.pause();
|
audioService.getCurrentSound()?.pause();
|
||||||
showPop.value = true;
|
showPop.value = true;
|
||||||
currentMv.value = item;
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -172,15 +171,15 @@ const handleShowMv = async (item: IMvItem) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mv {
|
.search-item.mv {
|
||||||
&:hover {
|
&:hover {
|
||||||
.play {
|
.play {
|
||||||
@apply opacity-60;
|
@apply opacity-60;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.search-item-img {
|
.search-item-img {
|
||||||
width: 160px;
|
width: 160px !important;
|
||||||
height: 90px;
|
height: 90px !important;
|
||||||
@apply rounded-lg relative;
|
@apply rounded-lg relative;
|
||||||
}
|
}
|
||||||
.play {
|
.play {
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import { useSettingsStore } from '@/store/modules/settings';
|
import { useSettingsStore } from '@/store/modules/settings';
|
||||||
|
|||||||
@@ -7,13 +7,14 @@
|
|||||||
:space-between="20"
|
:space-between="20"
|
||||||
draggable
|
draggable
|
||||||
show-arrow
|
show-arrow
|
||||||
:autoplay="false"
|
autoplay
|
||||||
>
|
>
|
||||||
<n-carousel-item :class="setAnimationClass('animate__backInRight')" :style="setAnimationDelay(0, 100)" style="width: calc((100% / 5) - 16px)">
|
<n-carousel-item
|
||||||
<div
|
:class="setAnimationClass('animate__backInRight')"
|
||||||
v-if="dayRecommendData"
|
:style="setAnimationDelay(0, 100)"
|
||||||
class="recommend-singer-item relative"
|
style="width: calc((100% / 5) - 16px)"
|
||||||
>
|
>
|
||||||
|
<div v-if="dayRecommendData" class="recommend-singer-item relative">
|
||||||
<div
|
<div
|
||||||
:style="
|
:style="
|
||||||
setBackgroundImg(getImgUrl(dayRecommendData?.dailySongs[0].al.picUrl, '500y500'))
|
setBackgroundImg(getImgUrl(dayRecommendData?.dailySongs[0].al.picUrl, '500y500'))
|
||||||
@@ -42,20 +43,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</n-carousel-item>
|
</n-carousel-item>
|
||||||
|
|
||||||
<n-carousel-item :class="setAnimationClass('animate__backInRight')"
|
<n-carousel-item
|
||||||
:style="setAnimationDelay(1, 100)"
|
v-if="userStore.user && userPlaylist.length"
|
||||||
style="width: calc(((100% / 5) - 16px) * 3)">
|
:class="setAnimationClass('animate__backInRight')"
|
||||||
|
:style="setAnimationDelay(1, 100) + '; width: calc(100% / 2); max-width: 460px;'"
|
||||||
|
>
|
||||||
<div class="user-play">
|
<div class="user-play">
|
||||||
<div class="user-play-title">
|
<div class="user-play-title flex items-center mb-3">
|
||||||
{{ store.state.user?.nickname }}
|
<n-avatar size="small" round :src="userStore.user?.avatarUrl" class="mr-2" />
|
||||||
|
{{ userStore.user?.nickname }}的常听
|
||||||
</div>
|
</div>
|
||||||
<div class="user-play-item" v-for="item in userPlaylist" :key="item.id">
|
<div class="user-play-list">
|
||||||
<div class="user-play-item-img">
|
<div
|
||||||
<img :src="getImgUrl(item.coverImgUrl, '200y200')" alt="">
|
v-for="item in userPlaylist"
|
||||||
</div>
|
:key="item.id"
|
||||||
<div class="user-play-item-info">
|
class="user-play-item"
|
||||||
<div class="user-play-item-info-name text- overflow-hidden">{{ item.name }}</div>
|
@click="toPlaylist(item.id)"
|
||||||
<div class="user-play-item-info-count">{{ t('common.songCount', { count: item.trackCount }) }}</div>
|
>
|
||||||
|
<div class="user-play-item-img">
|
||||||
|
<img :src="getImgUrl(item.coverImgUrl, '200y200')" alt="" />
|
||||||
|
<div class="user-play-item-overlay"></div>
|
||||||
|
</div>
|
||||||
|
<div class="user-play-item-info">
|
||||||
|
<div class="user-play-item-info-name">{{ item.name }}</div>
|
||||||
|
<div class="user-play-item-info-count text-xs opacity-70">
|
||||||
|
{{ t('common.songCount', { count: item.trackCount }) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -99,25 +113,36 @@
|
|||||||
:song-list="dayRecommendData?.dailySongs"
|
:song-list="dayRecommendData?.dailySongs"
|
||||||
:cover="false"
|
:cover="false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 添加用户歌单弹窗 -->
|
||||||
|
<music-list
|
||||||
|
v-model:show="showPlaylist"
|
||||||
|
v-model:loading="playlistLoading"
|
||||||
|
:name="playlistItem?.name || ''"
|
||||||
|
:song-list="playlistDetail?.playlist?.tracks || []"
|
||||||
|
:list-info="playlistDetail?.playlist"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref, watchEffect } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useStore } from 'vuex';
|
|
||||||
|
|
||||||
import { getDayRecommend, getHotSinger } from '@/api/home';
|
import { getDayRecommend, getHotSinger } from '@/api/home';
|
||||||
|
import { getListDetail } from '@/api/list';
|
||||||
|
import { getUserPlaylist } from '@/api/user';
|
||||||
import MusicList from '@/components/MusicList.vue';
|
import MusicList from '@/components/MusicList.vue';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
|
import { useUserStore } from '@/store';
|
||||||
import { IDayRecommend } from '@/type/day_recommend';
|
import { IDayRecommend } from '@/type/day_recommend';
|
||||||
|
import { Playlist } from '@/type/list';
|
||||||
|
import type { IListDetail } from '@/type/listDetail';
|
||||||
import type { IHotSinger } from '@/type/singer';
|
import type { IHotSinger } from '@/type/singer';
|
||||||
import { getImgUrl, setAnimationClass, setAnimationDelay, setBackgroundImg } from '@/utils';
|
import { getImgUrl, setAnimationClass, setAnimationDelay, setBackgroundImg } from '@/utils';
|
||||||
import { getUserPlaylist } from '@/api/user';
|
|
||||||
import { Playlist } from '@/type/list';
|
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const store = useStore();
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
// 歌手信息
|
// 歌手信息
|
||||||
@@ -126,6 +151,12 @@ const dayRecommendData = ref<IDayRecommend>();
|
|||||||
const showMusic = ref(false);
|
const showMusic = ref(false);
|
||||||
const userPlaylist = ref<Playlist[]>([]);
|
const userPlaylist = ref<Playlist[]>([]);
|
||||||
|
|
||||||
|
// 为歌单弹窗添加的状态
|
||||||
|
const showPlaylist = ref(false);
|
||||||
|
const playlistLoading = ref(false);
|
||||||
|
const playlistItem = ref<Playlist | null>(null);
|
||||||
|
const playlistDetail = ref<IListDetail | null>(null);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadData();
|
await loadData();
|
||||||
});
|
});
|
||||||
@@ -146,9 +177,11 @@ const loadData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hotSingerData.value = singerData;
|
hotSingerData.value = singerData;
|
||||||
if(store.state.user){
|
if (userStore.user) {
|
||||||
const { data: playlistData } = await getUserPlaylist(store.state.user?.userId);
|
const { data: playlistData } = await getUserPlaylist(userStore.user?.userId);
|
||||||
userPlaylist.value = (playlistData.playlist as Playlist[]).sort((a, b) => b.playCount - a.playCount).slice(0, 3);
|
userPlaylist.value = (playlistData.playlist as Playlist[])
|
||||||
|
.sort((a, b) => b.playCount - a.playCount)
|
||||||
|
.slice(0, 3);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('error', error);
|
console.error('error', error);
|
||||||
@@ -164,9 +197,32 @@ const toSearchSinger = (keyword: string) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toPlaylist = async (id: number) => {
|
||||||
|
playlistLoading.value = true;
|
||||||
|
playlistItem.value = null;
|
||||||
|
playlistDetail.value = null;
|
||||||
|
showPlaylist.value = true;
|
||||||
|
|
||||||
|
// 设置当前点击的歌单信息
|
||||||
|
const selectedPlaylist = userPlaylist.value.find((item) => item.id === id);
|
||||||
|
if (selectedPlaylist) {
|
||||||
|
playlistItem.value = selectedPlaylist;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取歌单详情
|
||||||
|
const { data } = await getListDetail(id);
|
||||||
|
playlistDetail.value = data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取歌单详情失败:', error);
|
||||||
|
} finally {
|
||||||
|
playlistLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 监听登录状态
|
// 监听登录状态
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (store.state.user) {
|
if (userStore.user) {
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -200,23 +256,48 @@ watchEffect(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-play{
|
.user-play {
|
||||||
@apply flex bg-light-100 dark:bg-dark rounded-3xl p-4 gap-4;
|
@apply bg-light-200 dark:bg-dark-100 rounded-3xl p-5 h-full flex flex-col;
|
||||||
&-item{
|
backdrop-filter: blur(20px);
|
||||||
@apply bg-light dark:bg-dark-100 rounded-3xl overflow-hidden w-28;
|
&-title {
|
||||||
&-img{
|
@apply text-gray-900 dark:text-gray-100 font-bold text-lg;
|
||||||
@apply w-28 h-28 rounded-3xl overflow-hidden;
|
}
|
||||||
img{
|
&-list {
|
||||||
@apply w-full h-full object-cover;
|
@apply grid grid-cols-3 gap-5 h-full;
|
||||||
|
}
|
||||||
|
&-item {
|
||||||
|
@apply bg-light dark:bg-dark-200 rounded-2xl overflow-hidden flex flex-col cursor-pointer transition-all duration-300;
|
||||||
|
height: 190px;
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
||||||
|
.user-play-item-overlay {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&-info{
|
&-img {
|
||||||
@apply flex-1;
|
@apply relative;
|
||||||
&-name{
|
height: 0;
|
||||||
@apply text-gray-900 dark:text-gray-100 line-clamp-1;
|
width: 100%;
|
||||||
|
padding-bottom: 100%; /* 确保宽高比为1:1,即正方形 */
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
@apply absolute inset-0 w-full h-full object-cover;
|
||||||
}
|
}
|
||||||
&-count{
|
}
|
||||||
@apply text-gray-900 dark:text-gray-100;
|
&-overlay {
|
||||||
|
@apply absolute inset-0 bg-black bg-opacity-30 flex items-center justify-center opacity-0 transition-opacity duration-300;
|
||||||
|
}
|
||||||
|
&-info {
|
||||||
|
@apply px-1 py-1;
|
||||||
|
&-name {
|
||||||
|
@apply text-gray-900 dark:text-gray-100 font-medium text-sm line-clamp-1;
|
||||||
|
}
|
||||||
|
&-count {
|
||||||
|
@apply text-gray-700 dark:text-gray-300 mt-1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,6 @@ import {
|
|||||||
textColors,
|
textColors,
|
||||||
useLyricProgress
|
useLyricProgress
|
||||||
} from '@/hooks/MusicHook';
|
} from '@/hooks/MusicHook';
|
||||||
import { usePlayerStore } from '@/store/modules/player';
|
|
||||||
import { useSettingsStore } from '@/store/modules/settings';
|
import { useSettingsStore } from '@/store/modules/settings';
|
||||||
import { getImgUrl, isMobile } from '@/utils';
|
import { getImgUrl, isMobile } from '@/utils';
|
||||||
import { animateGradient, getHoverBackgroundColor, getTextColors } from '@/utils/linearColor';
|
import { animateGradient, getHoverBackgroundColor, getTextColors } from '@/utils/linearColor';
|
||||||
@@ -373,7 +372,6 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const playerStore = usePlayerStore();
|
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
|
||||||
const handleArtistClick = (id: number) => {
|
const handleArtistClick = (id: number) => {
|
||||||
|
|||||||
@@ -112,7 +112,6 @@ 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';
|
||||||
import { SEARCH_TYPES, USER_SET_OPTIONS } from '@/const/bar-const';
|
import { SEARCH_TYPES, USER_SET_OPTIONS } from '@/const/bar-const';
|
||||||
import { usePlayerStore } from '@/store/modules/player';
|
|
||||||
import { useSearchStore } from '@/store/modules/search';
|
import { useSearchStore } from '@/store/modules/search';
|
||||||
import { useSettingsStore } from '@/store/modules/settings';
|
import { useSettingsStore } from '@/store/modules/settings';
|
||||||
import { useUserStore } from '@/store/modules/user';
|
import { useUserStore } from '@/store/modules/user';
|
||||||
@@ -122,7 +121,6 @@ import { checkUpdate, UpdateResult } from '@/utils/update';
|
|||||||
import config from '../../../../package.json';
|
import config from '../../../../package.json';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const playerStore = usePlayerStore();
|
|
||||||
const searchStore = useSearchStore();
|
const searchStore = useSearchStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<div class="button" @click="minimize">
|
<div class="button" @click="minimize">
|
||||||
<i class="iconfont icon-minisize"></i>
|
<i class="iconfont icon-minisize"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="button" @click="close">
|
<div class="button" @click="handleClose">
|
||||||
<i class="iconfont icon-close"></i>
|
<i class="iconfont icon-close"></i>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<n-modal
|
<n-modal
|
||||||
v-model:show="showCloseModal"
|
v-model:show="showCloseModal"
|
||||||
preset="dialog"
|
preset="dialog"
|
||||||
title="关闭应用"
|
:title="t('comp.titleBar.closeApp')"
|
||||||
:style="{ width: '400px' }"
|
:style="{ width: '400px' }"
|
||||||
:mask-closable="true"
|
:mask-closable="true"
|
||||||
>
|
>
|
||||||
@@ -80,7 +80,7 @@ const minimize = () => {
|
|||||||
|
|
||||||
const handleAction = (action: 'minimize' | 'close') => {
|
const handleAction = (action: 'minimize' | 'close') => {
|
||||||
if (rememberChoice.value) {
|
if (rememberChoice.value) {
|
||||||
settingsStore.setSettings({
|
settingsStore.setSetData({
|
||||||
...settingsStore.setData,
|
...settingsStore.setData,
|
||||||
closeAction: action
|
closeAction: action
|
||||||
});
|
});
|
||||||
@@ -112,10 +112,6 @@ const drag = (event: MouseEvent) => {
|
|||||||
}
|
}
|
||||||
window.api.dragStart(event as unknown as string);
|
window.api.dragStart(event as unknown as string);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleThemeChange = () => {
|
|
||||||
settingsStore.toggleTheme();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,415 +1,5 @@
|
|||||||
import { createPinia } from 'pinia';
|
import { createPinia } from 'pinia';
|
||||||
import { markRaw } from 'vue';
|
|
||||||
|
|
||||||
import setData from '@/../main/set.json';
|
|
||||||
import { logout } from '@/api/login';
|
|
||||||
import { getLikedList, likeSong } from '@/api/music';
|
|
||||||
import { useMusicListHook } from '@/hooks/MusicListHook';
|
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import homeRouter from '@/router/home';
|
|
||||||
import type { SongResult } from '@/type/music';
|
|
||||||
import { isElectron } from '@/utils';
|
|
||||||
import { applyTheme, getCurrentTheme, ThemeType } from '@/utils/theme';
|
|
||||||
|
|
||||||
// 默认设置
|
|
||||||
const defaultSettings = setData;
|
|
||||||
|
|
||||||
function isValidUrl(urlString: string): boolean {
|
|
||||||
try {
|
|
||||||
return Boolean(new URL(urlString));
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLocalStorageItem<T>(key: string, defaultValue: T): T {
|
|
||||||
try {
|
|
||||||
const item = localStorage.getItem(key);
|
|
||||||
if (!item) return defaultValue;
|
|
||||||
|
|
||||||
// 尝试解析 JSON
|
|
||||||
const parsedItem = JSON.parse(item);
|
|
||||||
|
|
||||||
// 对于音乐 URL,检查是否是有效的 URL 格式或本地文件路径
|
|
||||||
if (key === 'currentPlayMusicUrl' && typeof parsedItem === 'string') {
|
|
||||||
if (!parsedItem.startsWith('local://') && !isValidUrl(parsedItem)) {
|
|
||||||
console.warn(`Invalid URL in localStorage for key ${key}, using default value`);
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对于播放列表,检查是否是数组且每个项都有必要的字段
|
|
||||||
if (key === 'playList') {
|
|
||||||
if (!Array.isArray(parsedItem)) {
|
|
||||||
console.warn(`Invalid playList format in localStorage, using default value`);
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
// 检查每个歌曲对象是否有必要的字段
|
|
||||||
const isValid = parsedItem.every((item) => item && typeof item === 'object' && 'id' in item);
|
|
||||||
if (!isValid) {
|
|
||||||
console.warn(`Invalid song objects in playList, using default value`);
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对于当前播放音乐,检查是否是对象且包含必要的字段
|
|
||||||
if (key === 'currentPlayMusic') {
|
|
||||||
if (!parsedItem || typeof parsedItem !== 'object' || !('id' in parsedItem)) {
|
|
||||||
console.warn(`Invalid currentPlayMusic format in localStorage, using default value`);
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedItem;
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(`Error parsing localStorage item for key ${key}:`, error);
|
|
||||||
// 如果解析失败,删除可能损坏的数据
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface State {
|
|
||||||
menus: any[];
|
|
||||||
play: boolean;
|
|
||||||
isPlay: boolean;
|
|
||||||
playMusic: SongResult;
|
|
||||||
playMusicUrl: string;
|
|
||||||
user: any;
|
|
||||||
playList: SongResult[];
|
|
||||||
playListIndex: number;
|
|
||||||
setData: typeof defaultSettings;
|
|
||||||
lyric: any;
|
|
||||||
isMobile: boolean;
|
|
||||||
searchValue: string;
|
|
||||||
searchType: number;
|
|
||||||
favoriteList: number[];
|
|
||||||
playMode: number;
|
|
||||||
theme: ThemeType;
|
|
||||||
musicFull: boolean;
|
|
||||||
showUpdateModal: boolean;
|
|
||||||
showArtistDrawer: boolean;
|
|
||||||
currentArtistId: number | null;
|
|
||||||
systemFonts: { label: string; value: string }[];
|
|
||||||
showDownloadDrawer: boolean;
|
|
||||||
savedPlayProgress?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const state: State = {
|
|
||||||
menus: homeRouter,
|
|
||||||
play: false,
|
|
||||||
isPlay: false,
|
|
||||||
playMusic: getLocalStorageItem('currentPlayMusic', {} as SongResult),
|
|
||||||
playMusicUrl: getLocalStorageItem('currentPlayMusicUrl', ''),
|
|
||||||
user: getLocalStorageItem('user', null),
|
|
||||||
playList: getLocalStorageItem('playList', []),
|
|
||||||
playListIndex: getLocalStorageItem('playListIndex', 0),
|
|
||||||
setData: defaultSettings,
|
|
||||||
lyric: {},
|
|
||||||
isMobile: false,
|
|
||||||
searchValue: '',
|
|
||||||
searchType: 1,
|
|
||||||
favoriteList: getLocalStorageItem('favoriteList', []),
|
|
||||||
playMode: getLocalStorageItem('playMode', 0),
|
|
||||||
theme: getCurrentTheme(),
|
|
||||||
musicFull: false,
|
|
||||||
showUpdateModal: false,
|
|
||||||
showArtistDrawer: false,
|
|
||||||
currentArtistId: null,
|
|
||||||
systemFonts: [{ label: '系统默认', value: 'system-ui' }],
|
|
||||||
showDownloadDrawer: false
|
|
||||||
};
|
|
||||||
|
|
||||||
const { handlePlayMusic, nextPlay, prevPlay } = useMusicListHook();
|
|
||||||
|
|
||||||
const mutations = {
|
|
||||||
setMenus(state: State, menus: any[]) {
|
|
||||||
state.menus = menus;
|
|
||||||
},
|
|
||||||
async setPlay(state: State, playMusic: SongResult) {
|
|
||||||
await handlePlayMusic(state, playMusic);
|
|
||||||
localStorage.setItem('currentPlayMusic', JSON.stringify(state.playMusic));
|
|
||||||
localStorage.setItem('currentPlayMusicUrl', state.playMusicUrl);
|
|
||||||
},
|
|
||||||
setIsPlay(state: State, isPlay: boolean) {
|
|
||||||
state.isPlay = isPlay;
|
|
||||||
localStorage.setItem('isPlaying', isPlay.toString());
|
|
||||||
},
|
|
||||||
async setPlayMusic(state: State, play: boolean) {
|
|
||||||
state.play = play;
|
|
||||||
localStorage.setItem('isPlaying', play.toString());
|
|
||||||
|
|
||||||
// 每次更改播放状态时,确保当前播放歌曲信息也被保存
|
|
||||||
if (state.playMusic && Object.keys(state.playMusic).length > 0) {
|
|
||||||
localStorage.setItem('currentPlayMusic', JSON.stringify(state.playMusic));
|
|
||||||
if (state.playMusicUrl) {
|
|
||||||
localStorage.setItem('currentPlayMusicUrl', state.playMusicUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setMusicFull(state: State, musicFull: boolean) {
|
|
||||||
state.musicFull = musicFull;
|
|
||||||
},
|
|
||||||
setPlayList(state: State, playList: SongResult[]) {
|
|
||||||
state.playListIndex = playList.findIndex((item) => item.id === state.playMusic.id);
|
|
||||||
state.playList = playList;
|
|
||||||
localStorage.setItem('playList', JSON.stringify(playList));
|
|
||||||
localStorage.setItem('playListIndex', state.playListIndex.toString());
|
|
||||||
},
|
|
||||||
async nextPlay(state: State) {
|
|
||||||
await nextPlay(state);
|
|
||||||
},
|
|
||||||
async prevPlay(state: State) {
|
|
||||||
await prevPlay(state);
|
|
||||||
},
|
|
||||||
// 添加到下一首播放
|
|
||||||
addToNextPlay(state: State, song: SongResult) {
|
|
||||||
const playList = [...state.playList];
|
|
||||||
const currentIndex = state.playListIndex;
|
|
||||||
|
|
||||||
// 检查歌曲是否已经在播放列表中
|
|
||||||
const existingIndex = playList.findIndex((item) => item.id === song.id);
|
|
||||||
if (existingIndex !== -1) {
|
|
||||||
// 如果歌曲已经在列表中,将其移动到当前播放歌曲的下一个位置
|
|
||||||
playList.splice(existingIndex, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在当前播放歌曲后插入新歌曲
|
|
||||||
playList.splice(currentIndex + 1, 0, song);
|
|
||||||
|
|
||||||
// 更新播放列表
|
|
||||||
state.playList = playList;
|
|
||||||
state.playListIndex = playList.findIndex((item) => item.id === state.playMusic.id);
|
|
||||||
localStorage.setItem('playList', JSON.stringify(playList));
|
|
||||||
localStorage.setItem('playListIndex', state.playListIndex.toString());
|
|
||||||
},
|
|
||||||
setSetData(state: State, setData: any) {
|
|
||||||
state.setData = setData;
|
|
||||||
if (isElectron) {
|
|
||||||
// (window as any).electron.ipcRenderer.setStoreValue(
|
|
||||||
// 'set',
|
|
||||||
// JSON.parse(JSON.stringify(setData))
|
|
||||||
// );
|
|
||||||
window.electron.ipcRenderer.send(
|
|
||||||
'set-store-value',
|
|
||||||
'set',
|
|
||||||
JSON.parse(JSON.stringify(setData))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
localStorage.setItem('appSettings', JSON.stringify(setData));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async addToFavorite(state: State, songId: number) {
|
|
||||||
// 先添加到本地
|
|
||||||
if (!state.favoriteList.includes(songId)) {
|
|
||||||
state.favoriteList = [songId, ...state.favoriteList];
|
|
||||||
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果用户已登录,尝试同步到服务器
|
|
||||||
if (state.user && localStorage.getItem('token')) {
|
|
||||||
try {
|
|
||||||
await likeSong(songId, true);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('同步收藏到服务器失败,但已保存在本地:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async removeFromFavorite(state: State, songId: number) {
|
|
||||||
// 先从本地移除
|
|
||||||
state.favoriteList = state.favoriteList.filter((id) => id !== songId);
|
|
||||||
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
|
|
||||||
|
|
||||||
// 如果用户已登录,尝试同步到服务器
|
|
||||||
if (state.user && localStorage.getItem('token')) {
|
|
||||||
try {
|
|
||||||
await likeSong(songId, false);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('同步取消收藏到服务器失败,但已在本地移除:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
togglePlayMode(state: State) {
|
|
||||||
state.playMode = (state.playMode + 1) % 3;
|
|
||||||
localStorage.setItem('playMode', JSON.stringify(state.playMode));
|
|
||||||
},
|
|
||||||
toggleTheme(state: State) {
|
|
||||||
state.theme = state.theme === 'dark' ? 'light' : 'dark';
|
|
||||||
applyTheme(state.theme);
|
|
||||||
},
|
|
||||||
setShowUpdateModal(state, value) {
|
|
||||||
state.showUpdateModal = value;
|
|
||||||
},
|
|
||||||
logout(state: State) {
|
|
||||||
logout().then(() => {
|
|
||||||
state.user = null;
|
|
||||||
localStorage.removeItem('user');
|
|
||||||
localStorage.removeItem('token');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
setShowArtistDrawer(state, show: boolean) {
|
|
||||||
state.showArtistDrawer = show;
|
|
||||||
if (!show) {
|
|
||||||
state.currentArtistId = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setCurrentArtistId(state, id: number) {
|
|
||||||
state.currentArtistId = id;
|
|
||||||
},
|
|
||||||
setSystemFonts(state, fonts: string[]) {
|
|
||||||
state.systemFonts = [
|
|
||||||
{ label: '系统默认', value: 'system-ui' },
|
|
||||||
...fonts.map((font) => ({
|
|
||||||
label: font,
|
|
||||||
value: font
|
|
||||||
}))
|
|
||||||
];
|
|
||||||
},
|
|
||||||
setShowDownloadDrawer(state: State, show: boolean) {
|
|
||||||
state.showDownloadDrawer = show;
|
|
||||||
},
|
|
||||||
setLanguage(state: State, language: string) {
|
|
||||||
state.setData.language = language;
|
|
||||||
if (isElectron) {
|
|
||||||
window.electron.ipcRenderer.send('set-store-value', 'set.language', language);
|
|
||||||
} else {
|
|
||||||
localStorage.setItem('appSettings', JSON.stringify(state.setData));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getLanguage(state: State) {
|
|
||||||
return state.setData.language;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const actions = {
|
|
||||||
initializeSettings({ commit }: { commit: any }) {
|
|
||||||
if (isElectron) {
|
|
||||||
const setData = window.electron.ipcRenderer.sendSync('get-store-value', 'set');
|
|
||||||
commit('setSetData', {
|
|
||||||
...defaultSettings,
|
|
||||||
...setData
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const savedSettings = localStorage.getItem('appSettings');
|
|
||||||
if (savedSettings) {
|
|
||||||
commit('setSetData', {
|
|
||||||
...defaultSettings,
|
|
||||||
...JSON.parse(savedSettings)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
commit('setSetData', defaultSettings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
initializeTheme({ state }: { state: State }) {
|
|
||||||
applyTheme(state.theme);
|
|
||||||
},
|
|
||||||
async initializeFavoriteList({ state }: { state: State }) {
|
|
||||||
// 先获取本地收藏列表
|
|
||||||
const localFavoriteList = localStorage.getItem('favoriteList');
|
|
||||||
const localList: number[] = localFavoriteList ? JSON.parse(localFavoriteList) : [];
|
|
||||||
|
|
||||||
// 如果用户已登录,尝试获取服务器收藏列表并合并
|
|
||||||
if (state.user && state.user.userId) {
|
|
||||||
try {
|
|
||||||
const res = await getLikedList(state.user.userId);
|
|
||||||
if (res.data?.ids) {
|
|
||||||
// 合并本地和服务器的收藏列表,去重
|
|
||||||
const serverList = res.data.ids.reverse();
|
|
||||||
const mergedList = Array.from(new Set([...localList, ...serverList]));
|
|
||||||
state.favoriteList = mergedList;
|
|
||||||
} else {
|
|
||||||
state.favoriteList = localList;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取服务器收藏列表失败,使用本地数据:', error);
|
|
||||||
state.favoriteList = localList;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.favoriteList = localList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新本地存储
|
|
||||||
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
|
|
||||||
},
|
|
||||||
showArtist({ commit }, id: number) {
|
|
||||||
commit('setCurrentArtistId', id);
|
|
||||||
},
|
|
||||||
async initializeSystemFonts({ commit, state }) {
|
|
||||||
if (!isElectron) return;
|
|
||||||
// 如果已经有字体列表(不只是默认字体),则不重复获取
|
|
||||||
if (state.systemFonts.length > 1) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const fonts = await window.api.invoke('get-system-fonts');
|
|
||||||
commit('setSystemFonts', fonts);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取系统字体失败:', error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async initializePlayState({ state, commit }: { state: State; commit: any }) {
|
|
||||||
const savedPlayList = getLocalStorageItem('playList', []);
|
|
||||||
const savedPlayMusic = getLocalStorageItem('currentPlayMusic', null) as SongResult | null;
|
|
||||||
const savedPlayProgress = localStorage.getItem('playProgress');
|
|
||||||
|
|
||||||
if (savedPlayList.length > 0) {
|
|
||||||
commit('setPlayList', savedPlayList);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedPlayMusic && Object.keys(savedPlayMusic).length > 0) {
|
|
||||||
// 不直接使用保存的 URL,而是重新获取
|
|
||||||
try {
|
|
||||||
// 使用 handlePlayMusic 来重新获取音乐 URL
|
|
||||||
|
|
||||||
// 根据自动播放设置决定是否恢复播放状态
|
|
||||||
const shouldAutoPlay = state.setData.autoPlay;
|
|
||||||
await handlePlayMusic(state, savedPlayMusic, shouldAutoPlay);
|
|
||||||
state.play = shouldAutoPlay;
|
|
||||||
state.isPlay = true;
|
|
||||||
|
|
||||||
// 如果有保存的播放进度,则提供给前端组件使用
|
|
||||||
if (savedPlayProgress) {
|
|
||||||
try {
|
|
||||||
const progress = JSON.parse(savedPlayProgress);
|
|
||||||
if (progress && progress.songId === savedPlayMusic.id) {
|
|
||||||
// 在全局状态中添加播放进度
|
|
||||||
state.savedPlayProgress = progress.progress;
|
|
||||||
} else {
|
|
||||||
// 如果歌曲ID不匹配,清除保存的进度
|
|
||||||
localStorage.removeItem('playProgress');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('解析保存的播放进度失败', e);
|
|
||||||
localStorage.removeItem('playProgress');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('重新获取音乐链接失败:', error);
|
|
||||||
// 清除无效的播放状态
|
|
||||||
state.play = false;
|
|
||||||
state.isPlay = false;
|
|
||||||
state.playMusic = {} as SongResult;
|
|
||||||
state.playMusicUrl = '';
|
|
||||||
localStorage.removeItem('currentPlayMusic');
|
|
||||||
localStorage.removeItem('currentPlayMusicUrl');
|
|
||||||
localStorage.removeItem('isPlaying');
|
|
||||||
localStorage.removeItem('playProgress');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
initializeLanguage({ state }: { state: State }) {
|
|
||||||
state.setData.language = getLocalStorageItem('appSettings', { language: 'zh-CN' }).language;
|
|
||||||
if (isElectron) {
|
|
||||||
window.electron.ipcRenderer.send('set-store-value', 'set.language', state.setData.language);
|
|
||||||
} else {
|
|
||||||
localStorage.setItem('appSettings', JSON.stringify(state.setData));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 创建 pinia 实例
|
// 创建 pinia 实例
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
|
|||||||
@@ -29,12 +29,17 @@ export const useSettingsStore = defineStore('settings', () => {
|
|||||||
const showDownloadDrawer = ref(false);
|
const showDownloadDrawer = ref(false);
|
||||||
|
|
||||||
const setSetData = (data: any) => {
|
const setSetData = (data: any) => {
|
||||||
|
// 合并现有设置和新设置
|
||||||
|
const mergedData = {
|
||||||
|
...setData.value,
|
||||||
|
...data
|
||||||
|
};
|
||||||
|
|
||||||
if (isElectron) {
|
if (isElectron) {
|
||||||
console.log('data', data);
|
window.electron.ipcRenderer.send('set-store-value', 'set', cloneDeep(mergedData));
|
||||||
window.electron.ipcRenderer.send('set-store-value', 'set', cloneDeep(data));
|
setData.value = cloneDeep(mergedData);
|
||||||
setData.value = cloneDeep(data);
|
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem('appSettings', JSON.stringify(cloneDeep(data)));
|
localStorage.setItem('appSettings', JSON.stringify(cloneDeep(mergedData)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -73,10 +78,7 @@ export const useSettingsStore = defineStore('settings', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setLanguage = (language: string) => {
|
const setLanguage = (language: string) => {
|
||||||
setSetData({
|
setSetData({ language });
|
||||||
...setData.value,
|
|
||||||
language
|
|
||||||
});
|
|
||||||
if (isElectron) {
|
if (isElectron) {
|
||||||
window.electron.ipcRenderer.send('change-language', language);
|
window.electron.ipcRenderer.send('change-language', language);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,10 +84,10 @@ onBeforeUnmount(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 是否扫码登陆
|
// 是否扫码登陆
|
||||||
const chooseQr = () => {
|
// const chooseQr = () => {
|
||||||
isQr.value = !isQr.value;
|
// isQr.value = !isQr.value;
|
||||||
loadLogin();
|
// loadLogin();
|
||||||
};
|
// };
|
||||||
|
|
||||||
// 手机号登录
|
// 手机号登录
|
||||||
const phone = ref('');
|
const phone = ref('');
|
||||||
|
|||||||
@@ -461,7 +461,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { cloneDeep } from 'lodash';
|
|
||||||
import type { FormRules } from 'naive-ui';
|
import type { FormRules } from 'naive-ui';
|
||||||
import { useMessage } from 'naive-ui';
|
import { useMessage } from 'naive-ui';
|
||||||
import { computed, h, nextTick, onMounted, ref, watch } from 'vue';
|
import { computed, h, nextTick, onMounted, ref, watch } from 'vue';
|
||||||
@@ -494,23 +493,19 @@ const updateInfo = ref<UpdateResult>({
|
|||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const setData = ref(settingsStore.setData);
|
// 使用计算属性来管理设置数据
|
||||||
|
const setData = computed({
|
||||||
|
get: () => settingsStore.setData,
|
||||||
|
set: (newData) => {
|
||||||
|
settingsStore.setSetData(newData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const isDarkTheme = computed({
|
const isDarkTheme = computed({
|
||||||
get: () => settingsStore.theme === 'dark',
|
get: () => settingsStore.theme === 'dark',
|
||||||
set: () => settingsStore.toggleTheme()
|
set: () => settingsStore.toggleTheme()
|
||||||
});
|
});
|
||||||
|
|
||||||
// watch(
|
|
||||||
// () => setData.value,
|
|
||||||
// (newData) => {
|
|
||||||
// console.log('newData', newData);
|
|
||||||
// settingsStore.setSetData(newData);
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// deep: true
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
|
|
||||||
const openAuthor = () => {
|
const openAuthor = () => {
|
||||||
window.open(setData.value.authorUrl);
|
window.open(setData.value.authorUrl);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="user-page">
|
<div class="user-page">
|
||||||
<div
|
<div
|
||||||
v-if="userDetail"
|
v-if="userDetail && user"
|
||||||
class="left"
|
class="left"
|
||||||
:class="setAnimationClass('animate__fadeInLeft')"
|
:class="setAnimationClass('animate__fadeInLeft')"
|
||||||
:style="{ backgroundImage: `url(${getImgUrl(user.backgroundUrl)})` }"
|
:style="{ backgroundImage: `url(${getImgUrl(user.backgroundUrl)})` }"
|
||||||
@@ -167,6 +167,8 @@ const loadData = async () => {
|
|||||||
try {
|
try {
|
||||||
infoLoading.value = true;
|
infoLoading.value = true;
|
||||||
|
|
||||||
|
if (!user.value) return;
|
||||||
|
|
||||||
const { data: userData } = await getUserDetail(user.value.userId);
|
const { data: userData } = await getUserDetail(user.value.userId);
|
||||||
if (!mounted.value) return;
|
if (!mounted.value) return;
|
||||||
userDetail.value = userData;
|
userDetail.value = userData;
|
||||||
@@ -186,7 +188,7 @@ const loadData = async () => {
|
|||||||
console.error('加载用户页面失败:', error);
|
console.error('加载用户页面失败:', error);
|
||||||
// 如果获取用户数据失败,可能是token过期
|
// 如果获取用户数据失败,可能是token过期
|
||||||
if (error.response?.status === 401) {
|
if (error.response?.status === 401) {
|
||||||
userStore.logout();
|
userStore.handleLogout();
|
||||||
router.push('/login');
|
router.push('/login');
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -214,7 +216,7 @@ watch(
|
|||||||
(newUser) => {
|
(newUser) => {
|
||||||
if (!mounted.value) return;
|
if (!mounted.value) return;
|
||||||
if (newUser) {
|
if (newUser) {
|
||||||
loadUserData();
|
loadPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user