feat: 添加歌词字体粗细控制并修复 i18n 缺失

This commit is contained in:
algerkong
2025-12-20 14:09:57 +08:00
parent 85302c611a
commit 77bb06c0d6
8 changed files with 81 additions and 51 deletions

View File

@@ -1,5 +1,5 @@
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue';
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'; import { defineConfig } from 'electron-vite';
import { resolve } from 'path'; import { resolve } from 'path';
import AutoImport from 'unplugin-auto-import/vite'; import AutoImport from 'unplugin-auto-import/vite';
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'; import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
@@ -8,12 +8,8 @@ import viteCompression from 'vite-plugin-compression';
import VueDevTools from 'vite-plugin-vue-devtools'; import VueDevTools from 'vite-plugin-vue-devtools';
export default defineConfig({ export default defineConfig({
main: { main: {},
plugins: [externalizeDepsPlugin()] preload: {},
},
preload: {
plugins: [externalizeDepsPlugin()]
},
renderer: { renderer: {
resolve: { resolve: {
alias: { alias: {

View File

@@ -80,7 +80,7 @@
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"electron": "^39.2.7", "electron": "^39.2.7",
"electron-builder": "^26.0.12", "electron-builder": "^26.0.12",
"electron-vite": "^4.0.1", "electron-vite": "^5.0.0",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0", "eslint-plugin-import": "^2.32.0",

View File

@@ -273,6 +273,12 @@ export default {
medium: 'Medium', medium: 'Medium',
large: 'Large' large: 'Large'
}, },
fontWeight: 'Font Weight',
fontWeightMarks: {
thin: 'Thin',
normal: 'Normal',
bold: 'Bold'
},
letterSpacing: 'Letter Spacing', letterSpacing: 'Letter Spacing',
letterSpacingMarks: { letterSpacingMarks: {
compact: 'Compact', compact: 'Compact',

View File

@@ -270,6 +270,12 @@ export default {
medium: '中', medium: '中',
large: '大' large: '大'
}, },
fontWeight: '字体粗细',
fontWeightMarks: {
thin: '细',
normal: '常规',
bold: '粗'
},
letterSpacing: '字间距', letterSpacing: '字间距',
letterSpacingMarks: { letterSpacingMarks: {
compact: '紧凑', compact: '紧凑',

View File

@@ -1,10 +1,10 @@
<template> <template>
<div <div
class="w-96 rounded-2xl bg-white/5 backdrop-blur-3xl border border-white/10 shadow-2xl overflow-hidden" class="w-80 rounded-2xl bg-black/30 backdrop-blur-3xl border border-white/10 shadow-2xl overflow-hidden"
> >
<!-- 标题栏 --> <!-- 标题栏 -->
<div class="px-6 py-4 border-b border-white/5"> <div class="px-6 py-4 border-b border-white/5">
<h2 class="text-lg font-semibold tracking-tight" style="color: var(--text-color-active)"> <h2 class="text-lg font-semibold tracking-tight text-white/90">
{{ t('settings.lyricSettings.title') }} {{ t('settings.lyricSettings.title') }}
</h2> </h2>
</div> </div>
@@ -22,7 +22,7 @@
? 'bg-emerald-500 text-white shadow-lg shadow-emerald-500/30' ? 'bg-emerald-500 text-white shadow-lg shadow-emerald-500/30'
: 'hover:bg-white/5' : 'hover:bg-white/5'
]" ]"
:style="activeTab !== tab.key ? 'color: var(--text-color-primary); opacity: 0.7' : ''" :style="activeTab !== tab.key ? 'color: rgba(255, 255, 255, 0.7);' : ''"
> >
{{ tab.label }} {{ tab.label }}
</button> </button>
@@ -31,10 +31,10 @@
<!-- 内容区域 --> <!-- 内容区域 -->
<div <div
class="px-4 pb-4 max-h-[500px] overflow-y-auto scrollbar-thin scrollbar-thumb-white/20 scrollbar-track-transparent" class="px-3 pb-3 max-h-[450px] overflow-y-auto scrollbar-thin scrollbar-thumb-white/20 scrollbar-track-transparent"
> >
<!-- 显示设置 --> <!-- 显示设置 -->
<div v-show="activeTab === 'display'" class="space-y-3 pt-3"> <div v-show="activeTab === 'display'" class="space-y-2 pt-2">
<div class="setting-item"> <div class="setting-item">
<span>{{ t('settings.lyricSettings.pureMode') }}</span> <span>{{ t('settings.lyricSettings.pureMode') }}</span>
<input type="checkbox" v-model="config.pureModeEnabled" class="toggle-switch" /> <input type="checkbox" v-model="config.pureModeEnabled" class="toggle-switch" />
@@ -118,6 +118,23 @@
</div> </div>
</div> </div>
<div class="slider-group">
<label class="slider-label">{{ t('settings.lyricSettings.fontWeight') }}</label>
<input
type="range"
v-model.number="config.fontWeight"
min="100"
max="900"
step="100"
class="slider-emerald"
/>
<div class="slider-marks">
<span>{{ t('settings.lyricSettings.fontWeightMarks.thin') }}</span>
<span>{{ t('settings.lyricSettings.fontWeightMarks.normal') }}</span>
<span>{{ t('settings.lyricSettings.fontWeightMarks.bold') }}</span>
</div>
</div>
<div class="slider-group"> <div class="slider-group">
<label class="slider-label">{{ t('settings.lyricSettings.lineHeight') }}</label> <label class="slider-label">{{ t('settings.lyricSettings.lineHeight') }}</label>
<input <input
@@ -241,8 +258,7 @@
<button <button
v-if="config.gradientColors.colors.length < 5" v-if="config.gradientColors.colors.length < 5"
@click="addGradientColor" @click="addGradientColor"
class="w-full py-2 px-4 rounded-lg bg-emerald-500/20 hover:bg-emerald-500/30 transition-colors text-sm font-medium flex items-center justify-center gap-2" class="w-full py-2 px-4 rounded-lg bg-emerald-500/20 hover:bg-emerald-500/30 transition-colors text-sm font-medium flex items-center justify-center gap-2 text-white/90"
style="color: var(--text-color-active)"
> >
<i class="ri-add-line"></i> <i class="ri-add-line"></i>
{{ t('settings.lyricSettings.background.addColor') }} {{ t('settings.lyricSettings.background.addColor') }}
@@ -277,8 +293,7 @@
/> />
<button <button
@click="fileInput?.click()" @click="fileInput?.click()"
class="w-full py-2 px-4 rounded-lg bg-emerald-500/20 hover:bg-emerald-500/30 transition-colors text-sm font-medium flex items-center justify-center gap-2" class="w-full py-2 px-4 rounded-lg bg-emerald-500/20 hover:bg-emerald-500/30 transition-colors text-sm font-medium flex items-center justify-center gap-2 text-white/90"
style="color: var(--text-color-active)"
> >
<i class="ri-image-add-line"></i> <i class="ri-image-add-line"></i>
{{ t('settings.lyricSettings.background.imageUpload') }} {{ t('settings.lyricSettings.background.imageUpload') }}
@@ -338,7 +353,7 @@
</div> </div>
</div> </div>
<p class="text-xs" style="color: var(--text-color-primary); opacity: 0.5"> <p class="text-xs text-white/50">
{{ t('settings.lyricSettings.background.fileSizeLimit') }} {{ t('settings.lyricSettings.background.fileSizeLimit') }}
</p> </p>
</div> </div>
@@ -352,10 +367,9 @@
v-model="config.customCss" v-model="config.customCss"
:placeholder="t('settings.lyricSettings.background.customCssPlaceholder')" :placeholder="t('settings.lyricSettings.background.customCssPlaceholder')"
rows="4" rows="4"
class="w-full px-3 py-2 bg-black/20 border border-white/10 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500/50 font-mono" class="w-full px-3 py-2 bg-black/20 border border-white/10 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500/50 font-mono text-white/90"
style="color: var(--text-color-primary)"
></textarea> ></textarea>
<p class="text-xs" style="color: var(--text-color-primary); opacity: 0.5"> <p class="text-xs text-white/50">
{{ t('settings.lyricSettings.background.customCssHelp') }} {{ t('settings.lyricSettings.background.customCssHelp') }}
</p> </p>
</div> </div>
@@ -464,6 +478,10 @@ watch(
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-font-weight',
config.fontWeight?.toString() || '400'
);
document.documentElement.style.setProperty('--lyric-line-height', config.lineHeight.toString()); document.documentElement.style.setProperty('--lyric-line-height', config.lineHeight.toString());
}; };
@@ -486,13 +504,13 @@ defineExpose({
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 12px 16px; padding: 8px 12px;
background: rgba(255, 255, 255, 0.03); background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px; border-radius: 12px;
transition: all 0.2s; transition: all 0.2s;
font-size: 14px; font-size: 13px;
color: var(--text-color-primary); color: rgba(255, 255, 255, 0.9);
} }
.setting-item:hover { .setting-item:hover {
@@ -533,21 +551,21 @@ defineExpose({
/* 滑块组 */ /* 滑块组 */
.slider-group { .slider-group {
padding: 16px; padding: 10px 12px;
background: rgba(255, 255, 255, 0.03); background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px; border-radius: 12px;
} }
.slider-label { .slider-label {
display: block; display: block;
font-size: 12px; font-size: 11px;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
color: var(--text-color-primary); color: rgba(255, 255, 255, 0.8);
opacity: 0.7; opacity: 0.8;
margin-bottom: 12px; margin-bottom: 8px;
} }
.slider-emerald { .slider-emerald {
@@ -584,7 +602,7 @@ defineExpose({
justify-content: space-between; justify-content: space-between;
margin-top: 8px; margin-top: 8px;
font-size: 11px; font-size: 11px;
color: var(--text-color-primary); color: rgba(255, 255, 255, 0.8);
opacity: 0.5; opacity: 0.5;
} }
@@ -602,7 +620,7 @@ defineExpose({
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
color: var(--text-color-primary); color: rgba(255, 255, 255, 0.8);
opacity: 0.7; opacity: 0.7;
margin-bottom: 12px; margin-bottom: 12px;
} }
@@ -615,7 +633,7 @@ defineExpose({
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
font-size: 14px; font-size: 14px;
color: var(--text-color-primary); color: rgba(255, 255, 255, 0.8);
} }
.radio-item:hover { .radio-item:hover {
@@ -632,7 +650,7 @@ defineExpose({
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
font-size: 13px; font-size: 13px;
color: var(--text-color-primary); color: rgba(255, 255, 255, 0.8);
background: rgba(255, 255, 255, 0.03); background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.05);
} }
@@ -685,7 +703,7 @@ defineExpose({
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
color: var(--text-color-primary); color: rgba(255, 255, 255, 0.8);
opacity: 0.7; opacity: 0.7;
margin-bottom: 12px; margin-bottom: 12px;
} }
@@ -741,7 +759,7 @@ defineExpose({
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
color: var(--text-color-primary); color: rgba(255, 255, 255, 0.8);
opacity: 0.7; opacity: 0.7;
margin-bottom: 12px; margin-bottom: 12px;
} }
@@ -752,7 +770,7 @@ defineExpose({
background: rgba(0, 0, 0, 0.2); background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px; border-radius: 8px;
color: var(--text-color-primary); color: rgba(255, 255, 255, 0.8);
font-size: 14px; font-size: 14px;
cursor: pointer; cursor: pointer;
outline: none; outline: none;

View File

@@ -31,7 +31,7 @@
class="control-right absolute top-8 right-8 z-[9999]" class="control-right absolute top-8 right-8 z-[9999]"
:class="{ 'pure-mode': config.pureModeEnabled }" :class="{ 'pure-mode': config.pureModeEnabled }"
> >
<n-popover trigger="click" placement="bottom"> <n-popover trigger="click" placement="bottom" raw>
<template #trigger> <template #trigger>
<div class="control-btn"> <div class="control-btn">
<i class="ri-settings-3-line"></i> <i class="ri-settings-3-line"></i>
@@ -110,7 +110,7 @@
@mouseleave="mouseLeaveLayout" @mouseleave="mouseLeaveLayout"
> >
<!-- 歌曲信息 --> <!-- 歌曲信息 -->
<div ref="lrcContainer" class="music-lrc-container"> <div class="music-lrc-container">
<div <div
v-if="config.hideCover" v-if="config.hideCover"
class="music-info-header" class="music-info-header"
@@ -212,7 +212,6 @@ const { t } = useI18n();
// 定义 refs // 定义 refs
const lrcSider = ref<any>(null); const lrcSider = ref<any>(null);
const isMouse = ref(false); const isMouse = ref(false);
const lrcContainer = ref<HTMLElement | null>(null);
const currentBackground = ref(''); const currentBackground = ref('');
const animationFrame = ref<number | null>(null); const animationFrame = ref<number | null>(null);
const isDark = ref(false); const isDark = ref(false);
@@ -670,6 +669,14 @@ watch(
} }
); );
// 监听字体粗细变化
watch(
() => config.value.fontWeight,
(newWeight) => {
document.documentElement.style.setProperty('--lyric-font-weight', newWeight.toString());
}
);
// 监听主题变化 // 监听主题变化
watch( watch(
() => config.value.theme, () => config.value.theme,
@@ -889,6 +896,7 @@ defineExpose({
.music-lrc-text { .music-lrc-text {
@apply text-2xl cursor-pointer font-bold px-4 py-3; @apply text-2xl cursor-pointer font-bold px-4 py-3;
font-family: var(--current-font-family); font-family: var(--current-font-family);
font-weight: var(--lyric-font-weight, bold) !important;
transition: all 0.3s ease; transition: all 0.3s ease;
background-color: transparent; background-color: transparent;
font-size: var(--lyric-font-size, 22px) !important; font-size: var(--lyric-font-size, 22px) !important;
@@ -1071,10 +1079,4 @@ defineExpose({
pointer-events: auto !important; pointer-events: auto !important;
} }
} }
/* 移除 Popover padding */
:deep(.n-popover) {
padding: 0 !important;
background-color: transparent !important;
}
</style> </style>

View File

@@ -730,8 +730,8 @@ watch(
// 监听落雪音源列表变化 // 监听落雪音源列表变化
watch( watch(
() => [lxMusicApis.value.length, activeLxApiId.value], [() => lxMusicApis.value.length, () => activeLxApiId.value],
([apiCount, activeId]: [number, string | null]) => { ([apiCount, activeId]) => {
// 如果没有音源或没有激活的音源,自动从已选音源中移除 lxMusic // 如果没有音源或没有激活的音源,自动从已选音源中移除 lxMusic
if (apiCount === 0 || !activeId) { if (apiCount === 0 || !activeId) {
const index = selectedSources.value.indexOf('lxMusic'); const index = selectedSources.value.indexOf('lxMusic');

View File

@@ -3,6 +3,7 @@ export interface LyricConfig {
centerLyrics: boolean; centerLyrics: boolean;
fontSize: number; fontSize: number;
letterSpacing: number; letterSpacing: number;
fontWeight: number;
lineHeight: number; lineHeight: number;
showTranslation: boolean; showTranslation: boolean;
theme: 'default' | 'light' | 'dark'; theme: 'default' | 'light' | 'dark';
@@ -35,6 +36,7 @@ export const DEFAULT_LYRIC_CONFIG: LyricConfig = {
centerLyrics: false, centerLyrics: false,
fontSize: 22, fontSize: 22,
letterSpacing: 0, letterSpacing: 0,
fontWeight: 500,
lineHeight: 2, lineHeight: 2,
showTranslation: true, showTranslation: true,
theme: 'default', theme: 'default',