mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-14 14:50:50 +08:00
feat: 优化移动端界面设计以及歌词界面设计 添加播放模式选择
This commit is contained in:
@@ -168,42 +168,57 @@ export default {
|
||||
},
|
||||
lyricSettings: {
|
||||
title: 'Lyric Settings',
|
||||
tabs: {
|
||||
display: 'Display',
|
||||
interface: 'Interface',
|
||||
typography: 'Typography',
|
||||
mobile: 'Mobile'
|
||||
},
|
||||
pureMode: 'Pure Mode',
|
||||
hideCover: 'Hide Cover',
|
||||
centerDisplay: 'Center Display',
|
||||
showTranslation: 'Show Translation',
|
||||
hideLyrics: 'Hide Lyrics',
|
||||
hidePlayBar: 'Hide Play Bar',
|
||||
fontSize: 'Font Size',
|
||||
letterSpacing: 'Letter Spacing',
|
||||
lineHeight: 'Line Height',
|
||||
hideMiniPlayBar: 'Hide Mini Play Bar',
|
||||
backgroundTheme: 'Background Theme',
|
||||
fontSizeMarks: {
|
||||
small: 'Small',
|
||||
medium: 'Medium',
|
||||
large: 'Large'
|
||||
},
|
||||
letterSpacingMarks: {
|
||||
compact: 'Compact',
|
||||
default: 'Default',
|
||||
loose: 'Loose'
|
||||
},
|
||||
lineHeightMarks: {
|
||||
compact: 'Compact',
|
||||
default: 'Default',
|
||||
loose: 'Loose'
|
||||
},
|
||||
themeOptions: {
|
||||
default: 'Default',
|
||||
light: 'Light',
|
||||
dark: 'Dark'
|
||||
},
|
||||
hideMiniPlayBar: 'Hide Mini Play Bar',
|
||||
hideLyrics: 'Hide Lyrics',
|
||||
tabs: {
|
||||
interface: 'Interface',
|
||||
display: 'Display',
|
||||
typography: 'Typography'
|
||||
}
|
||||
fontSize: 'Font Size',
|
||||
fontSizeMarks: {
|
||||
small: 'Small',
|
||||
medium: 'Medium',
|
||||
large: 'Large'
|
||||
},
|
||||
letterSpacing: 'Letter Spacing',
|
||||
letterSpacingMarks: {
|
||||
compact: 'Compact',
|
||||
default: 'Default',
|
||||
loose: 'Loose'
|
||||
},
|
||||
lineHeight: 'Line Height',
|
||||
lineHeightMarks: {
|
||||
compact: 'Compact',
|
||||
default: 'Default',
|
||||
loose: 'Loose'
|
||||
},
|
||||
mobileLayout: 'Mobile Layout',
|
||||
layoutOptions: {
|
||||
default: 'Default',
|
||||
ios: 'iOS Style',
|
||||
android: 'Android Style'
|
||||
},
|
||||
mobileCoverStyle: 'Cover Style',
|
||||
coverOptions: {
|
||||
record: 'Record',
|
||||
square: 'Square',
|
||||
full: 'Full Screen'
|
||||
},
|
||||
lyricLines: 'Lyric Lines',
|
||||
mobileUnavailable: 'This setting is only available on mobile devices'
|
||||
},
|
||||
shortcutSettings: {
|
||||
title: 'Shortcut Settings',
|
||||
|
||||
@@ -16,7 +16,7 @@ export default {
|
||||
playFailed: '当前歌曲播放失败,播放下一首',
|
||||
playMode: {
|
||||
sequence: '顺序播放',
|
||||
loop: '循环播放',
|
||||
loop: '单曲循环',
|
||||
random: '随机播放'
|
||||
},
|
||||
fullscreen: {
|
||||
|
||||
@@ -167,19 +167,33 @@ export default {
|
||||
portNumber: '请输入有效的端口号(1-65535)'
|
||||
},
|
||||
lyricSettings: {
|
||||
title: '页面设置',
|
||||
title: '歌词设置',
|
||||
tabs: {
|
||||
display: '显示',
|
||||
interface: '界面',
|
||||
typography: '文字',
|
||||
mobile: '移动端'
|
||||
},
|
||||
pureMode: '纯净模式',
|
||||
hideCover: '隐藏封面',
|
||||
centerDisplay: '居中显示',
|
||||
showTranslation: '显示翻译',
|
||||
hideLyrics: '隐藏歌词',
|
||||
hidePlayBar: '隐藏播放栏',
|
||||
hideMiniPlayBar: '隐藏迷你播放栏',
|
||||
backgroundTheme: '背景主题',
|
||||
themeOptions: {
|
||||
default: '默认',
|
||||
light: '亮色',
|
||||
dark: '暗色'
|
||||
},
|
||||
fontSize: '字体大小',
|
||||
fontSizeMarks: {
|
||||
small: '小',
|
||||
medium: '中',
|
||||
large: '大'
|
||||
},
|
||||
letterSpacing: '文字间距',
|
||||
letterSpacing: '字间距',
|
||||
letterSpacingMarks: {
|
||||
compact: '紧凑',
|
||||
default: '默认',
|
||||
@@ -191,19 +205,20 @@ export default {
|
||||
default: '默认',
|
||||
loose: '宽松'
|
||||
},
|
||||
backgroundTheme: '背景主题',
|
||||
themeOptions: {
|
||||
mobileLayout: '移动端布局',
|
||||
layoutOptions: {
|
||||
default: '默认',
|
||||
light: '亮色',
|
||||
dark: '暗色'
|
||||
ios: 'iOS风格',
|
||||
android: '安卓风格'
|
||||
},
|
||||
hideMiniPlayBar: '隐藏迷你播放栏',
|
||||
hideLyrics: '隐藏歌词',
|
||||
tabs: {
|
||||
interface: '界面',
|
||||
typography: '文字',
|
||||
display: '显示'
|
||||
}
|
||||
mobileCoverStyle: '封面样式',
|
||||
coverOptions: {
|
||||
record: '唱片',
|
||||
square: '方形',
|
||||
full: '全屏'
|
||||
},
|
||||
lyricLines: '歌词行数',
|
||||
mobileUnavailable: '此设置仅在移动端可用'
|
||||
},
|
||||
shortcutSettings: {
|
||||
title: '快捷键设置',
|
||||
|
||||
58
src/locale/zh-CN.json
Normal file
58
src/locale/zh-CN.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"settings": {
|
||||
"lyricSettings": {
|
||||
"title": "歌词设置",
|
||||
"tabs": {
|
||||
"display": "显示",
|
||||
"interface": "界面",
|
||||
"typography": "文字",
|
||||
"mobile": "移动端"
|
||||
},
|
||||
"pureMode": "纯净模式",
|
||||
"hideCover": "隐藏封面",
|
||||
"centerDisplay": "居中显示",
|
||||
"showTranslation": "显示翻译",
|
||||
"hideLyrics": "隐藏歌词",
|
||||
"hidePlayBar": "隐藏播放栏",
|
||||
"hideMiniPlayBar": "隐藏迷你播放栏",
|
||||
"backgroundTheme": "背景主题",
|
||||
"themeOptions": {
|
||||
"default": "默认",
|
||||
"light": "亮色",
|
||||
"dark": "暗色"
|
||||
},
|
||||
"fontSize": "字体大小",
|
||||
"fontSizeMarks": {
|
||||
"small": "小",
|
||||
"medium": "中",
|
||||
"large": "大"
|
||||
},
|
||||
"letterSpacing": "字间距",
|
||||
"letterSpacingMarks": {
|
||||
"compact": "紧凑",
|
||||
"default": "默认",
|
||||
"loose": "宽松"
|
||||
},
|
||||
"lineHeight": "行高",
|
||||
"lineHeightMarks": {
|
||||
"compact": "紧凑",
|
||||
"default": "默认",
|
||||
"loose": "宽松"
|
||||
},
|
||||
"mobileLayout": "移动端布局",
|
||||
"layoutOptions": {
|
||||
"default": "默认",
|
||||
"ios": "iOS风格",
|
||||
"android": "安卓风格"
|
||||
},
|
||||
"mobileCoverStyle": "封面样式",
|
||||
"coverOptions": {
|
||||
"record": "唱片",
|
||||
"square": "方形",
|
||||
"full": "全屏"
|
||||
},
|
||||
"lyricLines": "歌词行数",
|
||||
"mobileUnavailable": "此设置仅在移动端可用"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,6 +114,47 @@
|
||||
</div>
|
||||
</div>
|
||||
</n-tab-pane>
|
||||
|
||||
<!-- 移动端设置 -->
|
||||
<n-tab-pane :name="'mobile'" :tab="t('settings.lyricSettings.tabs.mobile')">
|
||||
<div class="tab-content" v-if="isMobile">
|
||||
<div class="section-title">{{ t('settings.lyricSettings.mobileLayout') }}</div>
|
||||
<n-radio-group v-model:value="config.mobileLayout" name="mobileLayout" class="mb-4">
|
||||
<n-space>
|
||||
<n-radio value="default">{{ t('settings.lyricSettings.layoutOptions.default') }}</n-radio>
|
||||
<n-radio value="ios">{{ t('settings.lyricSettings.layoutOptions.ios') }}</n-radio>
|
||||
<n-radio value="android">{{ t('settings.lyricSettings.layoutOptions.android') }}</n-radio>
|
||||
</n-space>
|
||||
</n-radio-group>
|
||||
|
||||
<div class="section-title">{{ t('settings.lyricSettings.mobileCoverStyle') }}</div>
|
||||
<n-radio-group v-model:value="config.mobileCoverStyle" name="mobileCoverStyle" class="mb-4">
|
||||
<n-space>
|
||||
<n-radio value="record">{{ t('settings.lyricSettings.coverOptions.record') }}</n-radio>
|
||||
<n-radio value="square">{{ t('settings.lyricSettings.coverOptions.square') }}</n-radio>
|
||||
<n-radio value="full">{{ t('settings.lyricSettings.coverOptions.full') }}</n-radio>
|
||||
</n-space>
|
||||
</n-radio-group>
|
||||
|
||||
<div class="slider-item">
|
||||
<span>{{ t('settings.lyricSettings.lyricLines') }}</span>
|
||||
<n-slider
|
||||
v-model:value="config.mobileShowLyricLines"
|
||||
:step="1"
|
||||
:min="1"
|
||||
:max="5"
|
||||
:marks="{
|
||||
1: '1',
|
||||
3: '3',
|
||||
5: '5'
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="mobile-unavailable">
|
||||
{{ t('settings.lyricSettings.mobileUnavailable') }}
|
||||
</div>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,6 +165,7 @@ import { onMounted, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { DEFAULT_LYRIC_CONFIG, LyricConfig } from '@/types/lyric';
|
||||
import { isMobile } from '@/utils';
|
||||
|
||||
const { t } = useI18n();
|
||||
const config = ref<LyricConfig>({ ...DEFAULT_LYRIC_CONFIG });
|
||||
@@ -253,4 +295,8 @@ defineExpose({
|
||||
color: var(--text-color-active) !important;
|
||||
@apply text-xs;
|
||||
}
|
||||
|
||||
.mobile-unavailable {
|
||||
@apply text-center py-4 text-gray-500 text-sm;
|
||||
}
|
||||
</style>
|
||||
|
||||
1166
src/renderer/components/lyric/MusicFullMobile.vue
Normal file
1166
src/renderer/components/lyric/MusicFullMobile.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,8 @@
|
||||
class="mobile-play-bar"
|
||||
:class="[
|
||||
setAnimationClass('animate__fadeInUp'),
|
||||
musicFullVisible ? 'play-bar-expanded' : 'play-bar-mini'
|
||||
musicFullVisible ? 'play-bar-expanded' : 'play-bar-mini',
|
||||
!shouldShowMobileMenu ? 'mobile-play-bar-no-menu' : ''
|
||||
]"
|
||||
:style="{
|
||||
color: musicFullVisible
|
||||
@@ -16,7 +17,7 @@
|
||||
}"
|
||||
>
|
||||
<!-- 完整模式 - 在musicFullVisible为true时显示 -->
|
||||
<template v-if="musicFullVisible">
|
||||
<template v-if="false">
|
||||
<!-- 顶部信息区域 -->
|
||||
<div class="music-info-header">
|
||||
<div class="music-info-main">
|
||||
@@ -61,31 +62,9 @@
|
||||
<div class="control-btn next" @click="handleNext">
|
||||
<i class="iconfont ri-skip-forward-fill"></i>
|
||||
</div>
|
||||
<n-popover
|
||||
trigger="click"
|
||||
:z-index="99999999"
|
||||
content-class="mobile-play-list"
|
||||
raw
|
||||
:show-arrow="false"
|
||||
placement="top"
|
||||
@update-show="scrollToPlayList"
|
||||
>
|
||||
<template #trigger>
|
||||
<div class="control-btn list">
|
||||
<i class="iconfont ri-menu-line"></i>
|
||||
</div>
|
||||
</template>
|
||||
<div class="mobile-play-list-container">
|
||||
<div class="mobile-play-list-back"></div>
|
||||
<n-virtual-list ref="playListRef" :item-size="56" item-resizable :items="playList">
|
||||
<template #default="{ item }">
|
||||
<div class="mobile-play-list-item">
|
||||
<song-item :key="item.id" :item="item" mini></song-item>
|
||||
</div>
|
||||
</template>
|
||||
</n-virtual-list>
|
||||
</div>
|
||||
</n-popover>
|
||||
<div class="control-btn list" @click="openPlayListDrawer">
|
||||
<i class="iconfont ri-menu-line"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 定时关闭按钮 -->
|
||||
@@ -93,7 +72,7 @@
|
||||
</template>
|
||||
|
||||
<!-- Mini模式 - 在musicFullVisible为false时显示 -->
|
||||
<div v-else class="mobile-mini-controls">
|
||||
<div v-if="!musicFullVisible" class="mobile-mini-controls">
|
||||
<!-- 歌曲信息 -->
|
||||
<div class="mini-song-info" @click="setMusicFull">
|
||||
<n-image
|
||||
@@ -103,12 +82,13 @@
|
||||
preview-disabled
|
||||
/>
|
||||
<div class="mini-song-text">
|
||||
<n-ellipsis class="mini-song-title" line-clamp="1">
|
||||
{{ playMusic.name }}
|
||||
</n-ellipsis>
|
||||
<n-ellipsis class="mini-song-artist" line-clamp="1">
|
||||
<span v-for="(artists, artistsindex) in artistList" :key="artistsindex">
|
||||
{{ artists.name }}{{ artistsindex < artistList.length - 1 ? ' / ' : '' }}
|
||||
<n-ellipsis line-clamp="1">
|
||||
<span class="mini-song-title">{{ playMusic.name }}</span>
|
||||
<span class="mx-2 text-gray-500 dark:text-gray-400">-</span>
|
||||
<span class="mini-song-artist">
|
||||
<span v-for="(artists, artistsindex) in artistList" :key="artistsindex">
|
||||
{{ artists.name }}{{ artistsindex < artistList.length - 1 ? ' / ' : '' }}
|
||||
</span>
|
||||
</span>
|
||||
</n-ellipsis>
|
||||
</div>
|
||||
@@ -119,34 +99,12 @@
|
||||
<div class="mini-control-btn play" @click="playMusicEvent">
|
||||
<i class="iconfont icon" :class="play ? 'icon-stop' : 'icon-play'"></i>
|
||||
</div>
|
||||
<n-popover
|
||||
trigger="click"
|
||||
:z-index="99999999"
|
||||
content-class="mobile-play-list"
|
||||
raw
|
||||
:show-arrow="false"
|
||||
placement="top"
|
||||
@update-show="scrollToPlayList"
|
||||
>
|
||||
<template #trigger>
|
||||
<i class="iconfont icon-list mini-list-icon"></i>
|
||||
</template>
|
||||
<div class="mobile-play-list-container">
|
||||
<div class="mobile-play-list-back"></div>
|
||||
<n-virtual-list ref="playListRef" :item-size="56" item-resizable :items="playList">
|
||||
<template #default="{ item }">
|
||||
<div class="mobile-play-list-item">
|
||||
<song-item :key="item.id" :item="item" mini></song-item>
|
||||
</div>
|
||||
</template>
|
||||
</n-virtual-list>
|
||||
</div>
|
||||
</n-popover>
|
||||
<i class="iconfont icon-list mini-list-icon" @click="openPlayListDrawer"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 全屏播放器 -->
|
||||
<music-full ref="MusicFullRef" v-model="musicFullVisible" :background="background" />
|
||||
<music-full-wrapper ref="MusicFullRef" v-model="musicFullVisible" :background="background" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -154,21 +112,19 @@
|
||||
import { useThrottleFn } from '@vueuse/core';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import SongItem from '@/components/common/SongItem.vue';
|
||||
import { allTime, artistList, nowTime, playMusic, sound, textColors } from '@/hooks/MusicHook';
|
||||
import MusicFull from '@/layout/components/MusicFull.vue';
|
||||
import MusicFullWrapper from '@/layout/components/MusicFullWrapper.vue';
|
||||
import { usePlayerStore } from '@/store/modules/player';
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
import type { SongResult } from '@/type/music';
|
||||
import { getImgUrl, secondToMinute, setAnimationClass } from '@/utils';
|
||||
|
||||
const shouldShowMobileMenu = inject('shouldShowMobileMenu');
|
||||
|
||||
const playerStore = usePlayerStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
// 是否播放
|
||||
const play = computed(() => playerStore.isPlay);
|
||||
// 播放列表
|
||||
const playList = computed(() => playerStore.playList as SongResult[]);
|
||||
// 背景颜色
|
||||
const background = ref('#000');
|
||||
|
||||
@@ -206,14 +162,9 @@ const setMusicFull = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 播放列表引用
|
||||
const playListRef = ref<any>(null);
|
||||
|
||||
const scrollToPlayList = (val: boolean) => {
|
||||
if (!val) return;
|
||||
setTimeout(() => {
|
||||
playListRef.value?.scrollTo({ top: playerStore.playListIndex * 56 });
|
||||
}, 50);
|
||||
// 打开播放列表抽屉
|
||||
const openPlayListDrawer = () => {
|
||||
playerStore.setPlayListDrawerVisible(true);
|
||||
};
|
||||
|
||||
// 收藏功能
|
||||
@@ -251,11 +202,15 @@ watch(
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mobile-play-bar {
|
||||
@apply fixed bottom-[56px] left-0 w-full flex flex-col shadow-lg;
|
||||
@apply fixed bottom-[56px] left-0 w-full flex flex-col;
|
||||
z-index: 10000;
|
||||
animation-duration: 0.3s !important;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.mobile-play-bar-no-menu {
|
||||
@apply bottom-[10px];
|
||||
}
|
||||
|
||||
&.play-bar-expanded {
|
||||
@apply bg-transparent;
|
||||
height: auto; /* 自动适应内容高度 */
|
||||
@@ -285,7 +240,7 @@ watch(
|
||||
}
|
||||
|
||||
&.play-bar-mini {
|
||||
@apply h-14 py-0 bg-light dark:bg-dark;
|
||||
@apply h-14 py-0;
|
||||
}
|
||||
|
||||
// 顶部信息区域
|
||||
@@ -423,13 +378,13 @@ watch(
|
||||
|
||||
// Mini模式样式
|
||||
.mobile-mini-controls {
|
||||
@apply flex items-center justify-between px-4 h-14;
|
||||
@apply flex items-center justify-between pr-4 mx-3 h-12 rounded-full bg-light-100 dark:bg-dark-100 shadow-lg;
|
||||
|
||||
.mini-song-info {
|
||||
@apply flex items-center flex-1 min-w-0 cursor-pointer;
|
||||
|
||||
.mini-song-cover {
|
||||
@apply w-8 h-8 rounded-md;
|
||||
@apply w-12 h-12 rounded-full border-8 border-dark-300 dark:border-light-300;
|
||||
}
|
||||
|
||||
.mini-song-text {
|
||||
@@ -440,7 +395,7 @@ watch(
|
||||
}
|
||||
|
||||
.mini-song-artist {
|
||||
@apply text-xs text-gray-500 dark:text-gray-400 mt-0.5;
|
||||
@apply text-xs text-gray-500 dark:text-gray-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,8 +148,8 @@
|
||||
{{ t('player.playBar.playList') }}
|
||||
</n-tooltip>
|
||||
</div>
|
||||
<!-- 播放音乐 -->
|
||||
<music-full ref="MusicFullRef" v-model="musicFullVisible" :background="background" />
|
||||
<!-- 全屏播放器 -->
|
||||
<music-full-wrapper ref="MusicFullRef" v-model="musicFullVisible" :background="background" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -169,7 +169,7 @@ import {
|
||||
textColors
|
||||
} from '@/hooks/MusicHook';
|
||||
import { useArtist } from '@/hooks/useArtist';
|
||||
import MusicFull from '@/layout/components/MusicFull.vue';
|
||||
import MusicFullWrapper from '@/layout/components/MusicFullWrapper.vue';
|
||||
import { audioService } from '@/services/audioService';
|
||||
import {
|
||||
isBilibiliIdMatch,
|
||||
|
||||
@@ -117,6 +117,10 @@ const handleClearPlaylist = () => {
|
||||
message.info(t('player.playList.alreadyEmpty'));
|
||||
return;
|
||||
}
|
||||
|
||||
if(isMobile.value){
|
||||
closePanel();
|
||||
}
|
||||
|
||||
dialog.warning({
|
||||
title: t('player.playList.clearConfirmTitle'),
|
||||
@@ -254,17 +258,18 @@ const handleDeleteSong = (song: SongResult) => {
|
||||
// 移动端适配
|
||||
@media (max-width: 768px) {
|
||||
.playlist-panel {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 60vh;
|
||||
height: 80vh;
|
||||
top: auto;
|
||||
bottom: 56px; // 移动端底部留出导航栏高度
|
||||
border-radius: 16px 16px 0 0;
|
||||
bottom: 0; // 移动端底部留出导航栏高度
|
||||
border-radius: 30px 30px 0 0;
|
||||
border-left: none;
|
||||
border-top: 1px solid theme('colors.gray.200');
|
||||
box-shadow: 0 -5px 20px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&-header {
|
||||
@apply text-center relative;
|
||||
@apply text-center relative px-4;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
@@ -280,7 +285,11 @@ const handleDeleteSong = (song: SongResult) => {
|
||||
}
|
||||
|
||||
&-content {
|
||||
height: calc(60vh - 60px);
|
||||
height: calc(80vh - 60px);
|
||||
@apply px-4;
|
||||
.delete-btn{
|
||||
@apply visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import { useMessage } from 'naive-ui';
|
||||
|
||||
import { getSongUrl } from '@/store/modules/player';
|
||||
import type { SongResult } from '@/type/music';
|
||||
import { isElectron } from '@/utils';
|
||||
|
||||
const ipcRenderer = isElectron ? window.electron.ipcRenderer : null;
|
||||
|
||||
// 全局下载管理(闭包模式)
|
||||
const createDownloadManager = () => {
|
||||
@@ -58,11 +61,11 @@ const createDownloadManager = () => {
|
||||
|
||||
// 移除可能存在的旧监听器
|
||||
if (completeListener) {
|
||||
window.electron.ipcRenderer.removeListener('music-download-complete', completeListener);
|
||||
ipcRenderer?.removeListener('music-download-complete', completeListener);
|
||||
}
|
||||
|
||||
if (errorListener) {
|
||||
window.electron.ipcRenderer.removeListener('music-download-error', errorListener);
|
||||
ipcRenderer?.removeListener('music-download-error', errorListener);
|
||||
}
|
||||
|
||||
// 创建新的监听器
|
||||
@@ -99,8 +102,8 @@ const createDownloadManager = () => {
|
||||
};
|
||||
|
||||
// 添加监听器
|
||||
window.electron.ipcRenderer.on('music-download-complete', completeListener);
|
||||
window.electron.ipcRenderer.on('music-download-error', errorListener);
|
||||
ipcRenderer?.on('music-download-complete', completeListener);
|
||||
ipcRenderer?.on('music-download-error', errorListener);
|
||||
|
||||
isInitialized = true;
|
||||
},
|
||||
@@ -110,12 +113,12 @@ const createDownloadManager = () => {
|
||||
if (!isInitialized) return;
|
||||
|
||||
if (completeListener) {
|
||||
window.electron.ipcRenderer.removeListener('music-download-complete', completeListener);
|
||||
ipcRenderer?.removeListener('music-download-complete', completeListener);
|
||||
completeListener = null;
|
||||
}
|
||||
|
||||
if (errorListener) {
|
||||
window.electron.ipcRenderer.removeListener('music-download-error', errorListener);
|
||||
ipcRenderer?.removeListener('music-download-error', errorListener);
|
||||
errorListener = null;
|
||||
}
|
||||
|
||||
@@ -181,7 +184,7 @@ export const useDownload = () => {
|
||||
songData.ar = songData.ar || songData.song?.artists;
|
||||
|
||||
// 发送下载请求
|
||||
window.electron.ipcRenderer.send('download-music', {
|
||||
ipcRenderer?.send('download-music', {
|
||||
url: typeof musicUrl === 'string' ? musicUrl : musicUrl.url,
|
||||
filename,
|
||||
songInfo: {
|
||||
@@ -277,7 +280,7 @@ export const useDownload = () => {
|
||||
downloadTime: Date.now()
|
||||
};
|
||||
|
||||
window.electron.ipcRenderer.send('download-music', {
|
||||
ipcRenderer?.send('download-music', {
|
||||
url,
|
||||
filename,
|
||||
songInfo,
|
||||
|
||||
@@ -53,9 +53,9 @@ export function useSongItem(props: {
|
||||
const handleImageLoad = async (imageElement: HTMLImageElement) => {
|
||||
if (!imageElement) return;
|
||||
|
||||
const { backgroundColor } = await getImageBackground(imageElement);
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
const { backgroundColor, primaryColor } = await getImageBackground(imageElement);
|
||||
props.item.backgroundColor = backgroundColor;
|
||||
props.item.primaryColor = primaryColor;
|
||||
};
|
||||
|
||||
// 播放音乐
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<!-- 搜索栏 -->
|
||||
<search-bar />
|
||||
<!-- 主页面路由 -->
|
||||
<div class="main-content" :native-scrollbar="false">
|
||||
<div class="main-content" :native-scrollbar="false" :class="{'mobile-content': !shouldShowMobileMenu}">
|
||||
<router-view
|
||||
v-slot="{ Component }"
|
||||
class="main-page"
|
||||
@@ -21,7 +21,7 @@
|
||||
</router-view>
|
||||
</div>
|
||||
<play-bottom />
|
||||
<app-menu v-if="isMobile && !playerStore.musicFull" class="menu" :menus="menus" />
|
||||
<app-menu v-if="shouldShowMobileMenu" class="menu" :menus="menus" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部音乐播放 -->
|
||||
@@ -103,6 +103,16 @@ const isPlay = computed(() => playerStore.playMusic && playerStore.playMusic.id)
|
||||
const { menus } = menuStore;
|
||||
const route = useRoute();
|
||||
|
||||
// 判断当前路由是否应该在移动端显示AppMenu
|
||||
const shouldShowMobileMenu = computed(() => {
|
||||
// 过滤出在menus中定义的路径
|
||||
const menuPaths = menus.map((item: any) => item.path);
|
||||
// 检查当前路由路径是否在menus中
|
||||
return menuPaths.includes(route.path) && isMobile.value && !playerStore.musicFull;
|
||||
});
|
||||
|
||||
provide('shouldShowMobileMenu', shouldShowMobileMenu);
|
||||
|
||||
onMounted(() => {
|
||||
settingsStore.initializeSettings();
|
||||
settingsStore.initializeTheme();
|
||||
@@ -156,7 +166,10 @@ provide('openPlaylistDrawer', openPlaylistDrawer);
|
||||
overflow: auto;
|
||||
display: block;
|
||||
flex: none;
|
||||
padding-bottom: 70px;
|
||||
}
|
||||
|
||||
.mobile-content {
|
||||
height: calc(100vh - 75px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
import icon from '@/assets/icon.png';
|
||||
|
||||
@@ -115,7 +116,7 @@ const isText = ref(false);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 99999;
|
||||
@apply bg-light dark:bg-black border-t border-gray-200 dark:border-gray-700;
|
||||
@apply bg-light dark:bg-black border-none border-gray-200 dark:border-gray-700;
|
||||
|
||||
&-header {
|
||||
display: none;
|
||||
@@ -127,9 +128,16 @@ const isText = ref(false);
|
||||
|
||||
&-item {
|
||||
&-link {
|
||||
@apply my-2 w-auto;
|
||||
@apply my-2 w-auto px-2;
|
||||
width: auto !important;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&-expanded {
|
||||
@apply w-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
15
src/renderer/layout/components/MusicFullWrapper.vue
Normal file
15
src/renderer/layout/components/MusicFullWrapper.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<component :is="componentToUse" v-bind="$attrs" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { isMobile } from '@/utils';
|
||||
import MusicFull from '@/layout/components/MusicFull.vue';
|
||||
import MusicFullMobile from '@/components/lyric/MusicFullMobile.vue';
|
||||
|
||||
// 根据当前设备类型选择需要显示的组件
|
||||
const componentToUse = computed(() => {
|
||||
return isMobile.value ? MusicFullMobile : MusicFull;
|
||||
});
|
||||
</script>
|
||||
@@ -20,10 +20,11 @@ const getSettingsStore = () => {
|
||||
const loginRouter = {
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
mate: {
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
title: '登录',
|
||||
icon: 'icon-Home'
|
||||
icon: 'icon-Home',
|
||||
back: true
|
||||
},
|
||||
component: () => import('@/views/login/index.vue')
|
||||
};
|
||||
@@ -31,7 +32,7 @@ const loginRouter = {
|
||||
const setRouter = {
|
||||
path: '/set',
|
||||
name: 'set',
|
||||
mate: {
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
title: '设置',
|
||||
icon: 'icon-Home'
|
||||
|
||||
@@ -10,6 +10,10 @@ export interface LyricConfig {
|
||||
pureModeEnabled: boolean;
|
||||
hideMiniPlayBar: boolean;
|
||||
hideLyrics: boolean;
|
||||
// 移动端配置
|
||||
mobileLayout: 'default' | 'ios' | 'android';
|
||||
mobileCoverStyle: 'record' | 'square' | 'full';
|
||||
mobileShowLyricLines: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_LYRIC_CONFIG: LyricConfig = {
|
||||
@@ -23,5 +27,9 @@ export const DEFAULT_LYRIC_CONFIG: LyricConfig = {
|
||||
hidePlayBar: false,
|
||||
hideMiniPlayBar: true,
|
||||
pureModeEnabled: false,
|
||||
hideLyrics: false
|
||||
hideLyrics: false,
|
||||
// 移动端默认配置
|
||||
mobileLayout: 'ios',
|
||||
mobileCoverStyle: 'full',
|
||||
mobileShowLyricLines: 3
|
||||
};
|
||||
|
||||
@@ -115,6 +115,7 @@
|
||||
:compact="isCompactLayout"
|
||||
:item="formatSong(item)"
|
||||
@play="handlePlay"
|
||||
:style="{paddingBottom: index === filteredSongs.length - 1 ? '100px' : '0'}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -697,7 +698,7 @@ const handleVirtualScroll = (e: any) => {
|
||||
}
|
||||
|
||||
.albums-grid {
|
||||
@apply grid gap-6 grid-cols-2 sm:grid-cols-3 md:grid-cols-5 lg:grid-cols-6;
|
||||
@apply grid gap-6 grid-cols-2 sm:grid-cols-3 md:grid-cols-5 lg:grid-cols-6 pb-40;
|
||||
}
|
||||
|
||||
.loading-more {
|
||||
|
||||
@@ -539,7 +539,7 @@ const handleBatchDownload = async () => {
|
||||
|
||||
.mobile {
|
||||
.favorite-page {
|
||||
@apply p-4;
|
||||
@apply p-4 m-0;
|
||||
|
||||
.favorite-header {
|
||||
@apply mb-4;
|
||||
|
||||
@@ -146,7 +146,7 @@ const loginPhone = async () => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-page {
|
||||
@apply flex flex-col items-center justify-center p-20 pt-20;
|
||||
@apply flex flex-col items-center justify-center pt-20;
|
||||
@apply bg-light dark:bg-black;
|
||||
}
|
||||
|
||||
@@ -216,4 +216,10 @@ const loginPhone = async () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile {
|
||||
.login-page {
|
||||
@apply pt-0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
<n-virtual-list
|
||||
ref="songListRef"
|
||||
class="song-virtual-list"
|
||||
style="height: calc(80vh - 60px)"
|
||||
style="max-height: calc(100vh - 130px);"
|
||||
:items="filteredSongs"
|
||||
:item-size="isCompactLayout ? 50 : 70"
|
||||
item-resizable
|
||||
@@ -179,6 +179,7 @@
|
||||
@play="handlePlay"
|
||||
@remove-song="handleRemoveSong"
|
||||
@select="(id, selected) => handleSelect(id, selected)"
|
||||
:style="{paddingBottom: index === filteredSongs.length - 1 ? '100px' : '0'}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -90,6 +90,10 @@
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 未登录时显示登录组件 -->
|
||||
<div v-if="!isLoggedIn && isMobile" class="login-container" :class="setAnimationClass('animate__fadeIn')">
|
||||
<login-component @login-success="handleLoginSuccess" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -109,6 +113,7 @@ import { useUserStore } from '@/store/modules/user';
|
||||
import type { Playlist } from '@/type/listDetail';
|
||||
import type { IUserDetail } from '@/type/user';
|
||||
import { getImgUrl, isElectron, isMobile, setAnimationClass, setAnimationDelay } from '@/utils';
|
||||
import LoginComponent from '@/views/login/index.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'User'
|
||||
@@ -143,7 +148,7 @@ const checkLoginStatus = () => {
|
||||
const userData = localStorage.getItem('user');
|
||||
|
||||
if (!token || !userData) {
|
||||
router.push('/login');
|
||||
!isMobile.value && router.push('/login');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -270,6 +275,14 @@ const showFollowList = () => {
|
||||
// if (!user.value) return;
|
||||
// router.push('/user/followers');
|
||||
// };
|
||||
|
||||
const handleLoginSuccess = () => {
|
||||
// 处理登录成功后的逻辑
|
||||
checkLoginStatus();
|
||||
loadData();
|
||||
};
|
||||
|
||||
const isLoggedIn = computed(() => userStore.user);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -398,5 +411,9 @@ const showFollowList = () => {
|
||||
.user-page {
|
||||
@apply px-4;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
@apply flex justify-center items-center h-full w-full;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user