feat: 优化移动端界面设计以及歌词界面设计 添加播放模式选择

This commit is contained in:
alger
2025-06-07 10:48:54 +08:00
parent 155bdf246c
commit 21b2fc08be
22 changed files with 1485 additions and 147 deletions

View File

@@ -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',

View File

@@ -16,7 +16,7 @@ export default {
playFailed: '当前歌曲播放失败,播放下一首',
playMode: {
sequence: '顺序播放',
loop: '循环播放',
loop: '单曲循环',
random: '随机播放'
},
fullscreen: {

View File

@@ -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
View 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": "此设置仅在移动端可用"
}
}
}

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}
}
}

View File

@@ -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,

View File

@@ -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;
}
}
}
}

View File

@@ -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,

View File

@@ -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;
};
// 播放音乐

View File

@@ -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>

View File

@@ -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>

View 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>

View File

@@ -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'

View File

@@ -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
};

View File

@@ -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 {

View File

@@ -539,7 +539,7 @@ const handleBatchDownload = async () => {
.mobile {
.favorite-page {
@apply p-4;
@apply p-4 m-0;
.favorite-header {
@apply mb-4;

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>