feat: 优化歌词界面配置

This commit is contained in:
algerkong
2025-04-04 22:19:35 +08:00
parent 30ff7b2930
commit 55b50d764b
7 changed files with 229 additions and 155 deletions
+6 -1
View File
@@ -183,7 +183,12 @@ export default {
dark: 'Dark' dark: 'Dark'
}, },
hideMiniPlayBar: 'Hide Mini Play Bar', hideMiniPlayBar: 'Hide Mini Play Bar',
hideLyrics: 'Hide Lyrics' hideLyrics: 'Hide Lyrics',
tabs: {
interface: 'Interface',
display: 'Display',
typography: 'Typography'
}
}, },
shortcutSettings: { shortcutSettings: {
title: 'Shortcut Settings', title: 'Shortcut Settings',
+6 -1
View File
@@ -183,7 +183,12 @@ export default {
dark: '暗色' dark: '暗色'
}, },
hideMiniPlayBar: '隐藏迷你播放栏', hideMiniPlayBar: '隐藏迷你播放栏',
hideLyrics: '隐藏歌词' hideLyrics: '隐藏歌词',
tabs: {
interface: '界面',
typography: '文字',
display: '显示'
}
}, },
shortcutSettings: { shortcutSettings: {
title: '快捷键设置', title: '快捷键设置',
+2
View File
@@ -17,6 +17,8 @@ declare module 'vue' {
NCarouselItem: typeof import('naive-ui')['NCarouselItem'] NCarouselItem: typeof import('naive-ui')['NCarouselItem']
NCheckbox: typeof import('naive-ui')['NCheckbox'] NCheckbox: typeof import('naive-ui')['NCheckbox']
NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup'] NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
NCollapse: typeof import('naive-ui')['NCollapse']
NCollapseItem: typeof import('naive-ui')['NCollapseItem']
NConfigProvider: typeof import('naive-ui')['NConfigProvider'] NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDialogProvider: typeof import('naive-ui')['NDialogProvider'] NDialogProvider: typeof import('naive-ui')['NDialogProvider']
NDrawer: typeof import('naive-ui')['NDrawer'] NDrawer: typeof import('naive-ui')['NDrawer']
+171 -124
View File
@@ -2,94 +2,119 @@
<div class="settings-panel transparent-popover"> <div class="settings-panel transparent-popover">
<div class="settings-title">{{ t('settings.lyricSettings.title') }}</div> <div class="settings-title">{{ t('settings.lyricSettings.title') }}</div>
<div class="settings-content"> <div class="settings-content">
<div class="settings-item"> <n-tabs type="line" animated size="small">
<span>{{ t('settings.lyricSettings.pureMode') }}</span> <!-- 显示设置 -->
<n-switch v-model:value="config.pureModeEnabled" /> <n-tab-pane :name="'display'" :tab="t('settings.lyricSettings.tabs.display')">
</div> <div class="tab-content">
<div class="settings-grid">
<div class="settings-item">
<span>{{ t('settings.lyricSettings.pureMode') }}</span>
<n-switch v-model:value="config.pureModeEnabled" />
</div>
<div class="settings-item">
<span>{{ t('settings.lyricSettings.hideCover') }}</span>
<n-switch v-model:value="config.hideCover" />
</div>
<div class="settings-item">
<span>{{ t('settings.lyricSettings.centerDisplay') }}</span>
<n-switch v-model:value="config.centerLyrics" />
</div>
<div class="settings-item">
<span>{{ t('settings.lyricSettings.showTranslation') }}</span>
<n-switch v-model:value="config.showTranslation" />
</div>
<div class="settings-item">
<span>{{ t('settings.lyricSettings.hideLyrics') }}</span>
<n-switch v-model:value="config.hideLyrics" />
</div>
</div>
</div>
</n-tab-pane>
<div class="settings-item"> <!-- 界面设置 -->
<span>{{ t('settings.lyricSettings.hideCover') }}</span> <n-tab-pane :name="'interface'" :tab="t('settings.lyricSettings.tabs.interface')">
<n-switch v-model:value="config.hideCover" /> <div class="tab-content">
</div> <div class="settings-grid">
<div class="settings-item">
<span>{{ t('settings.lyricSettings.hidePlayBar') }}</span>
<n-switch v-model:value="config.hidePlayBar" />
</div>
<div class="settings-item">
<span>{{ t('settings.lyricSettings.hideMiniPlayBar') }}</span>
<n-switch v-model:value="config.hideMiniPlayBar" />
</div>
</div>
<div class="theme-section">
<div class="section-title">{{ t('settings.lyricSettings.backgroundTheme') }}</div>
<n-radio-group v-model:value="config.theme" name="theme" class="theme-radio-group">
<n-space>
<n-radio value="default">{{
t('settings.lyricSettings.themeOptions.default')
}}</n-radio>
<n-radio value="light">{{
t('settings.lyricSettings.themeOptions.light')
}}</n-radio>
<n-radio value="dark">{{
t('settings.lyricSettings.themeOptions.dark')
}}</n-radio>
</n-space>
</n-radio-group>
</div>
</div>
</n-tab-pane>
<div class="settings-item"> <!-- 文字设置 -->
<span>{{ t('settings.lyricSettings.centerDisplay') }}</span> <n-tab-pane :name="'typography'" :tab="t('settings.lyricSettings.tabs.typography')">
<n-switch v-model:value="config.centerLyrics" /> <div class="tab-content">
</div> <div class="slider-section">
<div class="slider-item">
<span>{{ t('settings.lyricSettings.fontSize') }}</span>
<n-slider
v-model:value="config.fontSize"
:step="1"
:min="12"
:max="32"
:marks="{
12: t('settings.lyricSettings.fontSizeMarks.small'),
22: t('settings.lyricSettings.fontSizeMarks.medium'),
32: t('settings.lyricSettings.fontSizeMarks.large')
}"
/>
</div>
<div class="settings-item"> <div class="slider-item">
<span>{{ t('settings.lyricSettings.showTranslation') }}</span> <span>{{ t('settings.lyricSettings.letterSpacing') }}</span>
<n-switch v-model:value="config.showTranslation" /> <n-slider
</div> v-model:value="config.letterSpacing"
:step="0.2"
:min="-2"
:max="10"
:marks="{
'-2': t('settings.lyricSettings.letterSpacingMarks.compact'),
0: t('settings.lyricSettings.letterSpacingMarks.default'),
10: t('settings.lyricSettings.letterSpacingMarks.loose')
}"
/>
</div>
<div class="settings-item"> <div class="slider-item">
<span>{{ t('settings.lyricSettings.hidePlayBar') }}</span> <span>{{ t('settings.lyricSettings.lineHeight') }}</span>
<n-switch v-model:value="config.hidePlayBar" /> <n-slider
</div> v-model:value="config.lineHeight"
:step="0.1"
<div class="settings-item"> :min="1"
<span>{{ t('settings.lyricSettings.hideMiniPlayBar') }}</span> :max="3"
<n-switch v-model:value="config.hideMiniPlayBar" /> :marks="{
</div> 1: t('settings.lyricSettings.lineHeightMarks.compact'),
1.5: t('settings.lyricSettings.lineHeightMarks.default'),
<div class="settings-item"> 3: t('settings.lyricSettings.lineHeightMarks.loose')
<span>{{ t('settings.lyricSettings.hideLyrics') }}</span> }"
<n-switch v-model:value="config.hideLyrics" /> />
</div> </div>
</div>
<div class="settings-slider"> </div>
<span>{{ t('settings.lyricSettings.fontSize') }}</span> </n-tab-pane>
<n-slider </n-tabs>
v-model:value="config.fontSize"
:step="1"
:min="12"
:max="32"
:marks="{
12: t('settings.lyricSettings.fontSizeMarks.small'),
22: t('settings.lyricSettings.fontSizeMarks.medium'),
32: t('settings.lyricSettings.fontSizeMarks.large')
}"
/>
</div>
<div class="settings-slider">
<span>{{ t('settings.lyricSettings.letterSpacing') }}</span>
<n-slider
v-model:value="config.letterSpacing"
:step="0.2"
:min="-2"
:max="10"
:marks="{
'-2': t('settings.lyricSettings.letterSpacingMarks.compact'),
0: t('settings.lyricSettings.letterSpacingMarks.default'),
10: t('settings.lyricSettings.letterSpacingMarks.loose')
}"
/>
</div>
<div class="settings-slider">
<span>{{ t('settings.lyricSettings.lineHeight') }}</span>
<n-slider
v-model:value="config.lineHeight"
:step="0.1"
:min="1"
:max="3"
:marks="{
1: t('settings.lyricSettings.lineHeightMarks.compact'),
1.5: t('settings.lyricSettings.lineHeightMarks.default'),
3: t('settings.lyricSettings.lineHeightMarks.loose')
}"
/>
</div>
<div class="settings-item">
<span>{{ t('settings.lyricSettings.backgroundTheme') }}</span>
<n-radio-group v-model:value="config.theme" name="theme">
<n-radio value="default">{{ t('settings.lyricSettings.themeOptions.default') }}</n-radio>
<n-radio value="light">{{ t('settings.lyricSettings.themeOptions.light') }}</n-radio>
<n-radio value="dark">{{ t('settings.lyricSettings.themeOptions.dark') }}</n-radio>
</n-radio-group>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -98,39 +123,12 @@
import { onMounted, ref, watch } from 'vue'; import { onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { DEFAULT_LYRIC_CONFIG, LyricConfig } from '@/types/lyric';
const { t } = useI18n(); const { t } = useI18n();
const config = ref<LyricConfig>({ ...DEFAULT_LYRIC_CONFIG });
interface LyricConfig {
hideCover: boolean;
centerLyrics: boolean;
fontSize: number;
letterSpacing: number;
lineHeight: number;
showTranslation: boolean;
theme: 'default' | 'light' | 'dark';
hidePlayBar: boolean;
hideMiniPlayBar: boolean;
pureModeEnabled: boolean;
hideLyrics: boolean;
}
const config = ref<LyricConfig>({
hideCover: false,
centerLyrics: false,
fontSize: 22,
letterSpacing: 0,
lineHeight: 2,
showTranslation: true,
theme: 'default',
hidePlayBar: false,
hideMiniPlayBar: false,
pureModeEnabled: false,
hideLyrics: false
});
const emit = defineEmits(['themeChange']); const emit = defineEmits(['themeChange']);
// 监听配置变化并保存到本地存储
watch( watch(
() => config.value, () => config.value,
(newConfig) => { (newConfig) => {
@@ -139,7 +137,6 @@ watch(
{ deep: true } { deep: true }
); );
// 监听主题变化
watch( watch(
() => config.value.theme, () => config.value.theme,
(newTheme) => { (newTheme) => {
@@ -147,14 +144,12 @@ watch(
} }
); );
// 更新 CSS 变量
const updateCSSVariables = (config: LyricConfig) => { const updateCSSVariables = (config: LyricConfig) => {
document.documentElement.style.setProperty('--lyric-font-size', `${config.fontSize}px`); document.documentElement.style.setProperty('--lyric-font-size', `${config.fontSize}px`);
document.documentElement.style.setProperty('--lyric-letter-spacing', `${config.letterSpacing}px`); document.documentElement.style.setProperty('--lyric-letter-spacing', `${config.letterSpacing}px`);
document.documentElement.style.setProperty('--lyric-line-height', config.lineHeight.toString()); document.documentElement.style.setProperty('--lyric-line-height', config.lineHeight.toString());
}; };
// 加载保存的配置
onMounted(() => { onMounted(() => {
const savedConfig = localStorage.getItem('music-full-config'); const savedConfig = localStorage.getItem('music-full-config');
if (savedConfig) { if (savedConfig) {
@@ -170,14 +165,50 @@ defineExpose({
<style scoped lang="scss"> <style scoped lang="scss">
.settings-panel { .settings-panel {
@apply p-4 w-72 rounded-lg relative overflow-hidden backdrop-blur-lg bg-black/10; @apply p-4 w-80 rounded-lg relative overflow-hidden backdrop-blur-lg bg-black/10;
.settings-title { .settings-title {
@apply text-base font-bold mb-4; @apply text-base font-bold mb-4;
color: var(--text-color-active); color: var(--text-color-active);
} }
.settings-content { .settings-content {
@apply space-y-4; :deep(.n-tabs-nav) {
@apply mb-3;
}
:deep(.n-tab-pane) {
@apply p-0;
}
:deep(.n-tabs-tab) {
@apply text-xs;
color: var(--text-color-primary);
&.n-tabs-tab--active {
color: var(--text-color-active);
}
}
:deep(.n-tabs-tab-wrapper) {
@apply pb-0;
}
:deep(.n-tabs-pane-wrapper) {
@apply px-2;
}
:deep(.n-tabs-bar) {
background-color: var(--text-color-active);
}
}
.tab-content {
@apply py-2;
}
.settings-grid {
@apply grid grid-cols-1 gap-3;
} }
.settings-item { .settings-item {
@@ -188,22 +219,38 @@ defineExpose({
} }
} }
.settings-slider { .section-title {
@apply space-y-2; @apply text-sm font-medium mb-2;
@apply mb-10 !important; color: var(--text-color-primary);
}
.theme-section {
@apply mt-4;
}
.slider-section {
@apply space-y-6;
}
.slider-item {
@apply space-y-2 mb-10 !important;
span { span {
@apply text-sm; @apply text-sm;
color: var(--text-color-primary); color: var(--text-color-primary);
} }
} }
.theme-radio-group {
@apply flex;
}
} }
// 修改 slider 字体颜色
:deep(.n-slider-mark) { :deep(.n-slider-mark) {
color: var(--text-color-primary) !important; color: var(--text-color-primary) !important;
} }
// 修改 radio 字体颜色
:deep(.n-radio__label) { :deep(.n-radio__label) {
color: var(--text-color-active) !important; color: var(--text-color-active) !important;
@apply text-xs;
} }
</style> </style>
@@ -585,4 +585,16 @@ const setMusicFull = () => {
.playlist-items { .playlist-items {
padding: 4px 0; padding: 4px 0;
} }
.dark {
.song-info {
.song-title {
color: var(--text-color-1, #fff);
}
.song-artist {
color: var(--text-color-2, #fff);
}
}
}
</style> </style>
+5 -29
View File
@@ -29,6 +29,7 @@
</n-popover> </n-popover>
<div <div
v-if="!config.hideCover"
class="music-img" class="music-img"
:class="{ 'only-cover': config.hideLyrics }" :class="{ 'only-cover': config.hideLyrics }"
:style="{ color: textColors.theme === 'dark' ? '#000000' : '#ffffff' }" :style="{ color: textColors.theme === 'dark' ? '#000000' : '#ffffff' }"
@@ -74,7 +75,7 @@
<div <div
class="music-content" class="music-content"
:class="{ :class="{
center: config.centerLyrics && config.hideCover, center: config.centerLyrics,
hide: config.hideLyrics hide: config.hideLyrics
}" }"
> >
@@ -159,6 +160,7 @@ import {
import { useArtist } from '@/hooks/useArtist'; import { useArtist } from '@/hooks/useArtist';
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 { DEFAULT_LYRIC_CONFIG, LyricConfig } from '@/types/lyric';
import { getImgUrl, isMobile } from '@/utils'; import { getImgUrl, isMobile } from '@/utils';
import { animateGradient, getHoverBackgroundColor, getTextColors } from '@/utils/linearColor'; import { animateGradient, getHoverBackgroundColor, getTextColors } from '@/utils/linearColor';
@@ -173,34 +175,8 @@ const isDark = ref(false);
const showStickyHeader = ref(false); const showStickyHeader = ref(false);
const lyricSettingsRef = ref<InstanceType<typeof LyricSettings>>(); const lyricSettingsRef = ref<InstanceType<typeof LyricSettings>>();
interface LyricConfig {
hideCover: boolean;
centerLyrics: boolean;
fontSize: number;
letterSpacing: number;
lineHeight: number;
showTranslation: boolean;
theme: 'default' | 'light' | 'dark';
hidePlayBar: boolean;
pureModeEnabled: boolean;
hideMiniPlayBar: boolean;
hideLyrics: boolean;
}
// 移除 computed 配置 // 移除 computed 配置
const config = ref<LyricConfig>({ const config = ref<LyricConfig>({ ...DEFAULT_LYRIC_CONFIG });
hideCover: false,
centerLyrics: false,
fontSize: 22,
letterSpacing: 0,
lineHeight: 1.5,
showTranslation: true,
theme: 'default',
hidePlayBar: false,
pureModeEnabled: false,
hideMiniPlayBar: false,
hideLyrics: false
});
// 监听设置组件的配置变化 // 监听设置组件的配置变化
watch( watch(
@@ -617,7 +593,7 @@ defineExpose({
transition: all 0.3s ease; transition: all 0.3s ease;
&.center { &.center {
@apply w-full; @apply w-auto;
.music-lrc { .music-lrc {
@apply w-full max-w-3xl mx-auto; @apply w-full max-w-3xl mx-auto;
} }
+27
View File
@@ -0,0 +1,27 @@
export interface LyricConfig {
hideCover: boolean;
centerLyrics: boolean;
fontSize: number;
letterSpacing: number;
lineHeight: number;
showTranslation: boolean;
theme: 'default' | 'light' | 'dark';
hidePlayBar: boolean;
pureModeEnabled: boolean;
hideMiniPlayBar: boolean;
hideLyrics: boolean;
}
export const DEFAULT_LYRIC_CONFIG: LyricConfig = {
hideCover: false,
centerLyrics: false,
fontSize: 22,
letterSpacing: 0,
lineHeight: 2,
showTranslation: true,
theme: 'default',
hidePlayBar: false,
hideMiniPlayBar: true,
pureModeEnabled: false,
hideLyrics: false
};