feat: 优化歌词组件和移动端界面设计

This commit is contained in:
alger
2025-06-07 22:30:39 +08:00
parent 6f1909a028
commit 934580552d
12 changed files with 85 additions and 136 deletions

View File

@@ -14,7 +14,7 @@ const { t } = useI18n();
<template>
<div
class="lyric-correction-btns-mac absolute right-0 bottom-4 flex flex-col items-center space-y-1 z-50 select-none transition-opacity duration-200 opacity-0 pointer-events-none"
class="lyric-correction"
>
<n-tooltip placement="right">
<template #trigger>
@@ -46,8 +46,18 @@ const { t } = useI18n();
</div>
</template>
<style scoped>
<style scoped lang="scss">
.lyric-correction {
@apply absolute right-0 bottom-4 flex flex-col items-center space-y-1 z-50 select-none transition-opacity duration-200 opacity-0 pointer-events-none;
}
.lyric-correction-btn {
@apply w-7 h-7 flex items-center justify-center rounded-lg bg-white dark:bg-neutral-800 border border-white/20 dark:border-neutral-700/40 shadow-md backdrop-blur-2xl cursor-pointer transition-all duration-150 text-gray-700 dark:text-gray-200 hover:bg-green-500/80 hover:text-white hover:border-green-400/60 active:scale-95 bg-opacity-40 dark:hover:bg-green-500/80 dark:hover:text-white dark:hover:border-green-400/60 dark:hover:bg-opacity-40;
}
.mobile{
.lyric-correction {
@apply opacity-100;
}
}
</style>

View File

@@ -114,47 +114,6 @@
</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>
@@ -165,7 +124,6 @@ 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 });

View File

@@ -21,18 +21,6 @@
<i class="ri-arrow-down-s-line"></i>
</div>
<n-popover trigger="click" placement="bottom">
<template #trigger>
<div
class="control-btn absolute top-5 right-5"
:class="{ 'pure-mode': config.pureModeEnabled }"
>
<i class="ri-settings-3-line"></i>
</div>
</template>
<lyric-settings ref="lyricSettingsRef" />
</n-popover>
<!-- 全屏歌词页面 -->
<transition name="fade">
<div
@@ -102,7 +90,7 @@
</div>
</div>
<div class="px-2">
<div class="px-2 flex-1 flex flex-col justify-around">
<!-- 歌曲信息 -->
<div class="song-info">
<h1 class="song-title">{{ playMusic.name }}</h1>
@@ -128,9 +116,6 @@
<div v-if="lrcArray.length > 0" class="lyrics-wrapper">
<div v-for="(line, idx) in visibleLyrics" :key="idx" class="lyric-line">
{{ line.text }}
<div v-if="config.showTranslation && line.trText" class="translation">
{{ line.trText }}
</div>
</div>
</div>
<div v-else class="no-lyrics">
@@ -198,7 +183,6 @@
import { computed, onBeforeUnmount, onMounted, ref, watch, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import LyricSettings from '@/components/lyric/LyricSettings.vue';
import {
allTime,
artistList,
@@ -459,13 +443,12 @@ const handleThumbTouchEnd = () => {
const currentBackground = ref('');
const animationFrame = ref<number | null>(null);
const isDark = ref(false);
const lyricSettingsRef = ref<InstanceType<typeof LyricSettings>>();
const config = ref<LyricConfig>({ ...DEFAULT_LYRIC_CONFIG });
// 可见歌词计算
const visibleLyrics = computed(() => {
const centerIndex = nowIndex.value + 1;
const numLines = config.value.mobileShowLyricLines;
const centerIndex = nowIndex.value;
const numLines = 3;
const halfLines = Math.floor(numLines / 2);
let startIdx = centerIndex - halfLines;
@@ -490,29 +473,6 @@ const visibleLyrics = computed(() => {
return lrcArray.value.slice(startIdx, endIdx + 1);
});
// 监听设置组件的配置变化
watch(
() => lyricSettingsRef.value?.config,
(newConfig) => {
if (newConfig) {
config.value = newConfig;
}
},
{ deep: true, immediate: true }
);
// 监听本地配置变化,保存到 localStorage
watch(
() => config.value,
(newConfig) => {
localStorage.setItem('music-full-config', JSON.stringify(newConfig));
if (lyricSettingsRef.value) {
lyricSettingsRef.value.config = newConfig;
}
},
{ deep: true }
);
const props = defineProps({
modelValue: {
type: Boolean,
@@ -802,6 +762,7 @@ defineExpose({
padding-top: 100px;
padding-bottom: 200px;
margin-bottom: 180px; /* 确保底部留出足够空间 */
margin-top: 90px;
.lyrics-padding-top {
height: 70px;
@@ -911,11 +872,11 @@ defineExpose({
@apply w-10 h-10 flex items-center justify-center cursor-pointer transition-all duration-200;
i {
@apply text-xl;
@apply text-2xl;
color: var(--text-color-primary);
&.favorite {
@apply text-red-500;
@apply text-red-500 !important;
}
}
@@ -930,7 +891,7 @@ defineExpose({
@apply w-14 h-14 flex items-center justify-center cursor-pointer transition-all duration-200;
i {
@apply text-2xl;
@apply text-3xl;
color: var(--text-color-primary);
}
@@ -963,7 +924,7 @@ defineExpose({
// 封面样式
.cover-container {
@apply relative mb-6 transition-all duration-500;
@apply relative mb-6 transition-all duration-500 border-gray-900;
&.style-changing {
animation: styleChange 0.5s ease;
@@ -974,10 +935,58 @@ defineExpose({
}
&.record-style {
@apply w-72 h-72 rounded-full overflow-hidden;
@apply w-72 h-72 rounded-full overflow-hidden relative;
// 唱片外圈装饰
&::before {
content: '';
@apply absolute top-0 left-0 w-full h-full rounded-full z-10;
background: radial-gradient(circle at center,
transparent 38%,
rgba(0, 0, 0, 0.15) 38%,
rgba(0, 0, 0, 0.15) 39%,
rgba(255, 255, 255, 0.1) 39%,
rgba(255, 255, 255, 0.1) 39.5%,
rgba(0, 0, 0, 0.08) 39.5%,
rgba(0, 0, 0, 0.08) 40.5%,
rgba(0, 0, 0, 0.2) 40.5%,
rgba(0, 0, 0, 0.2) 41.5%,
rgba(0, 0, 0, 0.6) 41.5%,
rgba(0, 0, 0, 0.6) 100%);
pointer-events: none;
animation: spin 20s linear infinite;
animation-play-state: running;
}
&.paused {
&::before, &::after {
animation-play-state: paused;
}
}
.img-wrapper {
@apply rounded-full overflow-hidden border-[40px] border-solid border-black z-0;
width: 90%;
height: 90%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
// 光泽效果
&::after {
content: '';
@apply absolute top-0 left-0 w-full h-full rounded-full z-[2];
background: linear-gradient(135deg,
rgba(255, 255, 255, 0.05) 0%,
rgba(255, 255, 255, 0) 50%,
rgba(0, 0, 0, 0.05) 100%);
pointer-events: none;
}
}
.cover-image {
@apply w-full h-full rounded-full;
@apply w-full h-full rounded-full border-[5px] border-gray-900;
animation: spin 20s linear infinite;
animation-play-state: running;
}
@@ -988,10 +997,10 @@ defineExpose({
}
&.square-style {
@apply w-72 h-72;
@apply w-72 h-72 shadow-lg rounded-xl overflow-hidden mt-8;
.cover-image {
@apply w-full h-full rounded-xl shadow-lg;
@apply w-full h-full;
transition: transform 0.3s ease-out;
&:active {

View File

@@ -5,7 +5,7 @@
<script setup lang="ts">
import { computed } from 'vue';
import { isMobile } from '@/utils';
import MusicFull from '@/layout/components/MusicFull.vue';
import MusicFull from '@/components/lyric/MusicFull.vue';
import MusicFullMobile from '@/components/lyric/MusicFullMobile.vue';
//

View File

@@ -113,7 +113,7 @@ import { useThrottleFn } from '@vueuse/core';
import { computed, ref, watch } from 'vue';
import { allTime, artistList, nowTime, playMusic, sound, textColors } from '@/hooks/MusicHook';
import MusicFullWrapper from '@/layout/components/MusicFullWrapper.vue';
import MusicFullWrapper from '@/components/lyric/MusicFullWrapper.vue';
import { usePlayerStore } from '@/store/modules/player';
import { useSettingsStore } from '@/store/modules/settings';
import { getImgUrl, secondToMinute, setAnimationClass } from '@/utils';

View File

@@ -169,7 +169,7 @@ import {
textColors
} from '@/hooks/MusicHook';
import { useArtist } from '@/hooks/useArtist';
import MusicFullWrapper from '@/layout/components/MusicFullWrapper.vue';
import MusicFullWrapper from '@/components/lyric/MusicFullWrapper.vue';
import { audioService } from '@/services/audioService';
import {
isBilibiliIdMatch,

View File

@@ -203,7 +203,7 @@ const restartApp = () => {
};
const toLogin = () => {
router.push('/login');
router.push('/user');
};
// 页面初始化

View File

@@ -1,24 +0,0 @@
<template>
<div class="lrc-full">
{{ lrcIndex }}
</div>
</template>
<script setup lang="ts">
defineProps({
lrcList: {
type: Array,
default: () => []
},
lrcIndex: {
type: Number,
default: 0
},
lrcTime: {
type: Number,
default: 0
}
});
</script>
<style scoped lang="scss"></style>

View File

@@ -84,7 +84,8 @@ const layoutRouter = [
title: '设置',
icon: 'ri-settings-3-fill',
keepAlive: true,
noScroll: true
noScroll: true,
back: true
},
component: () => import('@/views/set/index.vue')
}

View File

@@ -29,22 +29,11 @@ const loginRouter = {
component: () => import('@/views/login/index.vue')
};
const setRouter = {
path: '/set',
name: 'set',
meta: {
keepAlive: true,
title: '设置',
icon: 'icon-Home'
},
component: () => import('@/views/set/index.vue')
};
const routes = [
{
path: '/',
component: AppLayout,
children: [...homeRouter, loginRouter, setRouter, ...otherRouter]
children: [...homeRouter, loginRouter, ...otherRouter]
},
{
path: '/lyric',

View File

@@ -783,4 +783,10 @@ const handleVirtualScroll = (e: any) => {
@apply mb-2 bg-light-100 bg-opacity-30 dark:bg-dark-100 dark:bg-opacity-20 rounded-3xl;
}
}
.mobile {
.songs-toolbar{
@apply mb-0;
}
}
</style>