mirror of
https://github.com/atdunbg/Nekosonic-Music.git
synced 2026-08-06 03:44:18 +08:00
- 详情页统一 DetailLayout 组件(常驻头部 + 独立滚动 + 紧凑态动画) - 歌曲/评论 tab 切换(懒加载 + 保活),专辑模糊搜索 - 皮肤系统 skins.ts(19 语义色 + 14 预设 + 自定义皮肤 + 壁纸) - 设置页皮肤模块(外观/主题色/皮肤编辑器/壁纸选择) - 可折叠侧边栏 + TopBar + 折叠态歌单浮层(高度不越 PlayerBar) - 首页扩展至 6 区块 + 通用卡片组件 + 虚拟歌曲列表 - audio.rs 拆为 8 模块,api.ts 拆为 api/ 目录,player store 拆为 4 store - 修复播放竞态、seek 暂停、评论提前加载、紧凑态卡死等
107 lines
3.5 KiB
TypeScript
107 lines
3.5 KiB
TypeScript
/**
|
||
* 设置数据 Schema 迁移
|
||
*
|
||
* 每次新增字段或修改结构时,新增一个迁移函数并递增 CURRENT_SCHEMA_VERSION。
|
||
* 迁移按版本号顺序执行:v0 → v1 → v2 → ... → CURRENT。
|
||
*
|
||
* 迁移函数签名:(data: any) => any
|
||
* - 输入:旧版数据(可能是任意历史版本)
|
||
* - 输出:升级后的数据
|
||
* - 必须幂等:对已经是目标版本的数据执行应无副作用
|
||
*/
|
||
|
||
/** 当前 Schema 版本号 */
|
||
export const CURRENT_SCHEMA_VERSION = 3;
|
||
|
||
/** 迁移函数按目标版本号索引(data 升级到此版本后即为 vN) */
|
||
const migrations: Record<number, (data: any) => any> = {
|
||
/**
|
||
* v1: theme + appearance → skin
|
||
*
|
||
* 旧版用独立的 theme(blue/green/...)和 appearance(dark/light)字段,
|
||
* 合并为统一的 skin 字段(如 "dark-blue")。
|
||
*/
|
||
1: (data) => {
|
||
if (!data.skin && (data.theme || data.appearance)) {
|
||
const appearance = data.appearance || 'dark';
|
||
const theme = data.theme || 'blue';
|
||
const validThemes = ['blue', 'green', 'rose', 'violet', 'orange', 'cyan', 'pink'];
|
||
const t = validThemes.includes(theme) ? theme : 'blue';
|
||
data.skin = appearance === 'light' ? `light-${t}` : `dark-${t}`;
|
||
}
|
||
if (!data.skin) data.skin = 'dark-blue';
|
||
delete data.theme;
|
||
delete data.appearance;
|
||
return data;
|
||
},
|
||
|
||
/**
|
||
* v2: 全局 wallpaper → 移入自定义皮肤
|
||
*
|
||
* 旧版有全局 wallpaper/wallpaperBlur/wallpaperOpacity 字段,
|
||
* 迁移到每个自定义皮肤的对应字段(仅填充缺失项)。
|
||
*/
|
||
2: (data) => {
|
||
if (data.wallpaper && Array.isArray(data.customSkins) && data.customSkins.length > 0) {
|
||
data.customSkins = data.customSkins.map((s: any) => {
|
||
if (!s.wallpaper) {
|
||
return {
|
||
...s,
|
||
wallpaper: data.wallpaper,
|
||
wallpaperBlur: s.wallpaperBlur ?? data.wallpaperBlur ?? 10,
|
||
wallpaperOpacity: s.wallpaperOpacity ?? data.wallpaperOpacity ?? 0.3,
|
||
};
|
||
}
|
||
return s;
|
||
});
|
||
}
|
||
delete data.wallpaper;
|
||
delete data.wallpaperBlur;
|
||
delete data.wallpaperOpacity;
|
||
return data;
|
||
},
|
||
|
||
/**
|
||
* v3: localMusicPaths (string[]) → localMusicFolders ({path, enabled}[])
|
||
*
|
||
* 旧版本地音乐路径是字符串数组,新版改为带 enabled 开关的对象数组。
|
||
*/
|
||
3: (data) => {
|
||
if (!data.localMusicFolders && Array.isArray(data.localMusicPaths) && data.localMusicPaths.length) {
|
||
data.localMusicFolders = data.localMusicPaths.map((p: string) => ({ path: p, enabled: true }));
|
||
}
|
||
if (!Array.isArray(data.localMusicFolders)) {
|
||
data.localMusicFolders = [];
|
||
}
|
||
// 兼容 localMusicFolders 内仍是字符串的情况
|
||
data.localMusicFolders = data.localMusicFolders.map((f: any) =>
|
||
typeof f === 'string' ? { path: f, enabled: true } : f
|
||
);
|
||
delete data.localMusicPaths;
|
||
return data;
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 执行迁移:将旧版数据升级到 CURRENT_SCHEMA_VERSION
|
||
*
|
||
* @param raw 从 localStorage 读取的原始数据(可能无 schemaVersion)
|
||
* @returns 迁移后的数据,含 schemaVersion = CURRENT_SCHEMA_VERSION
|
||
*/
|
||
export function migrateSettings(raw: any): any {
|
||
let data = { ...raw };
|
||
let version = typeof data.schemaVersion === 'number' ? data.schemaVersion : 0;
|
||
|
||
while (version < CURRENT_SCHEMA_VERSION) {
|
||
const next = version + 1;
|
||
const migrator = migrations[next];
|
||
if (migrator) {
|
||
data = migrator(data);
|
||
}
|
||
version = next;
|
||
}
|
||
|
||
data.schemaVersion = CURRENT_SCHEMA_VERSION;
|
||
return data;
|
||
}
|