feat: v0.6.0 - 亮色主题、封面主色、发现页重做、漫游页重做、减少推荐、列表风格统一

新功能:
- 亮色主题:新增浅色外观模式,7种主题色各有对应亮色变体
- 封面主色背景:漫游抽屉自动提取封面主色,PlayerBar跟随继承
- 发现页重做:多类型搜索(歌曲/歌手/专辑)+搜索建议+搜索历史
- 漫游页重做:进入即播放,布局改为封面+歌名+播放/下一首/减少推荐
- 减少推荐:FM模式下可标记不推荐歌曲或歌手
- 列表风格统一:播放指示器跳动动画+hover播放图标+图标统一使用Lucide

修复:
- 专辑页艺术家过多时窗口缩小竖排,改为自动换行
- FM播放时退出登录后首页仍可点击下一首
- 本地音乐播放时缓冲进度条未重置
- 亮色主题下多处文字不可见
- 退出FM模式时状态未正确清理
- 暗色模式下关闭抽屉时PlayerBar闪烁亮色(改用opacity过渡)
- player.ts tickInterval双变量状态不同步,统一为clearTick/setTick

变更:
- 移除播放列表按钮数字角标
- 主页卡片标题固定白色不随主题变化
- 全项目空catch块格式统一
- 清理冗余注释和代码
This commit is contained in:
2026-05-28 23:14:25 +08:00
parent 6da544cffb
commit c275461015
36 changed files with 1079 additions and 475 deletions
+47 -10
View File
@@ -1,11 +1,12 @@
import { defineStore } from 'pinia';
import { ref, watch } from 'vue';
import { ref, watch, computed } from 'vue';
export type AudioQuality = 'standard' | 'higher' | 'exhigh' | 'lossless' | 'hires';
export type ThemeName = 'blue' | 'green' | 'rose' | 'violet' | 'orange' | 'cyan' | 'pink';
export type ThemeColor = 'blue' | 'green' | 'rose' | 'violet' | 'orange' | 'cyan' | 'pink';
export type Appearance = 'dark' | 'light';
export type CloseAction = 'ask' | 'minimize' | 'exit';
export const themeLabels: Record<ThemeName, string> = {
export const themeLabels: Record<ThemeColor, string> = {
blue: '天蓝',
green: '翠绿',
rose: '玫红',
@@ -15,7 +16,7 @@ export const themeLabels: Record<ThemeName, string> = {
pink: '粉色',
};
export const themeColors: Record<ThemeName, string> = {
export const themeColors: Record<ThemeColor, string> = {
blue: '#3b82f6',
green: '#22c55e',
rose: '#f43f5e',
@@ -25,6 +26,11 @@ export const themeColors: Record<ThemeName, string> = {
pink: '#ec4899',
};
export const appearanceLabels: Record<Appearance, string> = {
dark: '深色',
light: '浅色',
};
export const qualityLabels: Record<AudioQuality, string> = {
standard: '标准',
higher: '较高',
@@ -60,7 +66,8 @@ export const defaultShortcuts: Record<string, ShortcutBinding> = {
interface SettingsData {
audioQuality: AudioQuality;
downloadPath: string;
theme: ThemeName;
theme: ThemeColor;
appearance: Appearance;
closeAction: CloseAction;
shortcuts: Record<string, ShortcutBinding>;
outputDevice: string | null;
@@ -73,22 +80,38 @@ function loadSettings(): SettingsData {
if (raw) {
const parsed = JSON.parse(raw);
const theme = parsed.theme || parsed.accentColor || 'blue';
const validThemes: ThemeName[] = ['blue', 'green', 'rose', 'violet', 'orange', 'cyan', 'pink'];
const validThemes: ThemeColor[] = ['blue', 'green', 'rose', 'violet', 'orange', 'cyan', 'pink'];
const validAppearances: Appearance[] = ['dark', 'light'];
const appearance = validAppearances.includes(parsed.appearance) ? parsed.appearance : 'dark';
if (parsed.theme && parsed.theme.startsWith('light-')) {
return {
audioQuality: parsed.audioQuality || 'standard',
downloadPath: parsed.downloadPath || '',
theme: validThemes.includes(parsed.theme.slice(6)) ? parsed.theme.slice(6) : 'blue',
appearance: 'light',
closeAction: parsed.closeAction || 'ask',
shortcuts: { ...defaultShortcuts, ...(parsed.shortcuts || {}) },
outputDevice: parsed.outputDevice || null,
volume: typeof parsed.volume === 'number' ? parsed.volume : 100,
};
}
return {
audioQuality: parsed.audioQuality || 'standard',
downloadPath: parsed.downloadPath || '',
theme: validThemes.includes(theme) ? theme : 'blue',
appearance,
closeAction: parsed.closeAction || 'ask',
shortcuts: { ...defaultShortcuts, ...(parsed.shortcuts || {}) },
outputDevice: parsed.outputDevice || null,
volume: typeof parsed.volume === 'number' ? parsed.volume : 100,
};
}
} catch {}
} catch { /* 忽略 */ }
return {
audioQuality: 'standard',
downloadPath: '',
theme: 'blue',
appearance: 'dark',
closeAction: 'ask',
shortcuts: { ...defaultShortcuts },
outputDevice: null,
@@ -101,12 +124,17 @@ export const useSettingsStore = defineStore('settings', () => {
const audioQuality = ref<AudioQuality>(saved.audioQuality);
const downloadPath = ref<string>(saved.downloadPath);
const theme = ref<ThemeName>(saved.theme);
const theme = ref<ThemeColor>(saved.theme);
const appearance = ref<Appearance>(saved.appearance);
const closeAction = ref<CloseAction>(saved.closeAction || 'ask');
const shortcuts = ref<Record<string, ShortcutBinding>>(saved.shortcuts);
const outputDevice = ref<string | null>(saved.outputDevice);
const volume = ref<number>(saved.volume);
const dataTheme = computed(() =>
appearance.value === 'light' ? `light-${theme.value}` : theme.value
);
function setAudioQuality(q: AudioQuality) {
audioQuality.value = q;
}
@@ -115,10 +143,14 @@ export const useSettingsStore = defineStore('settings', () => {
downloadPath.value = p;
}
function setTheme(t: ThemeName) {
function setTheme(t: ThemeColor) {
theme.value = t;
}
function setAppearance(a: Appearance) {
appearance.value = a;
}
function setCloseAction(a: CloseAction) {
closeAction.value = a;
}
@@ -139,17 +171,19 @@ export const useSettingsStore = defineStore('settings', () => {
audioQuality.value = 'standard';
downloadPath.value = '';
theme.value = 'blue';
appearance.value = 'dark';
closeAction.value = 'ask';
shortcuts.value = { ...defaultShortcuts };
outputDevice.value = null;
volume.value = 100;
}
watch([audioQuality, downloadPath, theme, closeAction, shortcuts, outputDevice, volume], () => {
watch([audioQuality, downloadPath, theme, appearance, closeAction, shortcuts, outputDevice, volume], () => {
const data: SettingsData = {
audioQuality: audioQuality.value,
downloadPath: downloadPath.value,
theme: theme.value,
appearance: appearance.value,
closeAction: closeAction.value,
shortcuts: shortcuts.value,
outputDevice: outputDevice.value,
@@ -162,6 +196,8 @@ export const useSettingsStore = defineStore('settings', () => {
audioQuality,
downloadPath,
theme,
appearance,
dataTheme,
closeAction,
shortcuts,
outputDevice,
@@ -169,6 +205,7 @@ export const useSettingsStore = defineStore('settings', () => {
setAudioQuality,
setDownloadPath,
setTheme,
setAppearance,
setCloseAction,
setOutputDevice,
setShortcut,