mirror of
https://github.com/atdunbg/Nekosonic-Music.git
synced 2026-08-06 03:44:18 +08:00
feat: v0.8.0 详情页架构重构、皮肤系统、侧边栏交互升级、底层模块拆分
- 详情页统一 DetailLayout 组件(常驻头部 + 独立滚动 + 紧凑态动画) - 歌曲/评论 tab 切换(懒加载 + 保活),专辑模糊搜索 - 皮肤系统 skins.ts(19 语义色 + 14 预设 + 自定义皮肤 + 壁纸) - 设置页皮肤模块(外观/主题色/皮肤编辑器/壁纸选择) - 可折叠侧边栏 + TopBar + 折叠态歌单浮层(高度不越 PlayerBar) - 首页扩展至 6 区块 + 通用卡片组件 + 虚拟歌曲列表 - audio.rs 拆为 8 模块,api.ts 拆为 api/ 目录,player store 拆为 4 store - 修复播放竞态、seek 暂停、评论提前加载、紧凑态卡死等
This commit is contained in:
+28
-52
@@ -1,6 +1,7 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, watch, computed } from 'vue';
|
||||
import { getPresetSkin, isPresetSkinId, applySkinColors, type SkinColors } from '../skins';
|
||||
import { migrateSettings, CURRENT_SCHEMA_VERSION } from './migrations/settingsMigrations';
|
||||
|
||||
export type AudioQuality = 'standard' | 'higher' | 'exhigh' | 'lossless' | 'hires';
|
||||
export type CloseAction = 'ask' | 'minimize' | 'exit';
|
||||
@@ -58,7 +59,6 @@ export interface MusicFolder {
|
||||
interface SettingsData {
|
||||
audioQuality: AudioQuality;
|
||||
downloadPath: string;
|
||||
localMusicPaths: string[]; // 旧格式,迁移用
|
||||
localMusicFolders: MusicFolder[];
|
||||
skin: string; // 预设皮肤 id 或 custom-xxx
|
||||
customSkins: CustomSkin[];
|
||||
@@ -66,61 +66,13 @@ interface SettingsData {
|
||||
shortcuts: Record<string, ShortcutBinding>;
|
||||
outputDevice: string | null;
|
||||
volume: number;
|
||||
schemaVersion: number;
|
||||
}
|
||||
|
||||
function loadSettings(): SettingsData {
|
||||
try {
|
||||
const raw = localStorage.getItem('app_settings');
|
||||
if (raw) {
|
||||
const parsed = JSON.parse(raw);
|
||||
|
||||
// 迁移旧版 theme + appearance → skin
|
||||
let skin = parsed.skin || 'dark-blue';
|
||||
if (!parsed.skin && (parsed.theme || parsed.appearance)) {
|
||||
const appearance = parsed.appearance || 'dark';
|
||||
const theme = parsed.theme || 'blue';
|
||||
const validThemes = ['blue', 'green', 'rose', 'violet', 'orange', 'cyan', 'pink'];
|
||||
const t = validThemes.includes(theme) ? theme : 'blue';
|
||||
skin = appearance === 'light' ? `light-${t}` : `dark-${t}`;
|
||||
}
|
||||
|
||||
// 迁移旧版全局壁纸 → 移入自定义皮肤(如果有自定义皮肤且没有壁纸)
|
||||
let customSkins = parsed.customSkins || [];
|
||||
if (parsed.wallpaper && customSkins.length > 0) {
|
||||
customSkins = customSkins.map((s: any) => {
|
||||
if (!s.wallpaper) {
|
||||
return { ...s, wallpaper: parsed.wallpaper, wallpaperBlur: parsed.wallpaperBlur ?? 10, wallpaperOpacity: parsed.wallpaperOpacity ?? 0.3 };
|
||||
}
|
||||
return s;
|
||||
});
|
||||
}
|
||||
|
||||
// 迁移旧格式 localMusicPaths → localMusicFolders
|
||||
let folders: MusicFolder[] = (parsed.localMusicFolders || []).map((f: any) =>
|
||||
typeof f === 'string' ? { path: f, enabled: true } : f
|
||||
);
|
||||
if (!parsed.localMusicFolders && parsed.localMusicPaths?.length) {
|
||||
folders = parsed.localMusicPaths.map((p: string) => ({ path: p, enabled: true }));
|
||||
}
|
||||
|
||||
return {
|
||||
audioQuality: parsed.audioQuality || 'standard',
|
||||
downloadPath: parsed.downloadPath || '',
|
||||
localMusicPaths: [],
|
||||
localMusicFolders: folders,
|
||||
skin,
|
||||
customSkins,
|
||||
closeAction: parsed.closeAction || 'ask',
|
||||
shortcuts: { ...defaultShortcuts, ...(parsed.shortcuts || {}) },
|
||||
outputDevice: parsed.outputDevice || null,
|
||||
volume: typeof parsed.volume === 'number' ? parsed.volume : 100,
|
||||
};
|
||||
}
|
||||
} catch { /* 忽略 */ }
|
||||
return {
|
||||
const defaults: SettingsData = {
|
||||
audioQuality: 'standard',
|
||||
downloadPath: '',
|
||||
localMusicPaths: [],
|
||||
localMusicFolders: [],
|
||||
skin: 'dark-blue',
|
||||
customSkins: [],
|
||||
@@ -128,7 +80,31 @@ function loadSettings(): SettingsData {
|
||||
shortcuts: { ...defaultShortcuts },
|
||||
outputDevice: null,
|
||||
volume: 100,
|
||||
schemaVersion: CURRENT_SCHEMA_VERSION,
|
||||
};
|
||||
|
||||
try {
|
||||
const raw = localStorage.getItem('app_settings');
|
||||
if (!raw) return defaults;
|
||||
|
||||
const parsed = JSON.parse(raw);
|
||||
// 执行迁移(theme→skin、wallpaper→customSkin、localMusicPaths→localMusicFolders)
|
||||
const migrated = migrateSettings(parsed);
|
||||
|
||||
return {
|
||||
audioQuality: migrated.audioQuality || defaults.audioQuality,
|
||||
downloadPath: migrated.downloadPath || defaults.downloadPath,
|
||||
localMusicFolders: Array.isArray(migrated.localMusicFolders) ? migrated.localMusicFolders : defaults.localMusicFolders,
|
||||
skin: migrated.skin || defaults.skin,
|
||||
customSkins: Array.isArray(migrated.customSkins) ? migrated.customSkins : defaults.customSkins,
|
||||
closeAction: migrated.closeAction || defaults.closeAction,
|
||||
shortcuts: { ...defaultShortcuts, ...(migrated.shortcuts || {}) },
|
||||
outputDevice: migrated.outputDevice ?? null,
|
||||
volume: typeof migrated.volume === 'number' ? migrated.volume : defaults.volume,
|
||||
schemaVersion: CURRENT_SCHEMA_VERSION,
|
||||
};
|
||||
} catch { /* 忽略 */ }
|
||||
return defaults;
|
||||
}
|
||||
|
||||
export const useSettingsStore = defineStore('settings', () => {
|
||||
@@ -290,7 +266,6 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
const data: SettingsData = {
|
||||
audioQuality: audioQuality.value,
|
||||
downloadPath: downloadPath.value,
|
||||
localMusicPaths: [],
|
||||
localMusicFolders: localMusicFolders.value,
|
||||
skin: skin.value,
|
||||
customSkins: customSkins.value,
|
||||
@@ -298,6 +273,7 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
shortcuts: shortcuts.value,
|
||||
outputDevice: outputDevice.value,
|
||||
volume: volume.value,
|
||||
schemaVersion: CURRENT_SCHEMA_VERSION,
|
||||
};
|
||||
localStorage.setItem('app_settings', JSON.stringify(data));
|
||||
}, { deep: true });
|
||||
|
||||
Reference in New Issue
Block a user