feat(local-music): 支持从本地列表移除单曲并防止扫描失败误删(#713)

- 右键菜单新增'从本地列表移除'(本地歌曲自动切换文案,仅移除条目不删文件,5 语言文案)
- localMusic store 新增 removeEntry action
- 扫描失败的文件夹不再参与'已删除清理',避免移动盘/网络盘暂时不可用时整夹歌曲被误删
- 注:'刷新不清理已删除歌曲'主症状已由 c28368f 修复,本次补齐评论区诉求与防护

Closes #713
This commit is contained in:
alger
2026-07-05 14:35:00 +08:00
parent 2d693cee17
commit 3fc25ba9a4
8 changed files with 70 additions and 7 deletions
+30 -1
View File
@@ -127,6 +127,9 @@ export const useLocalMusicStore = defineStore(
// 磁盘上实际存在的文件路径集合(扫描时收集)
const diskFilePaths = new Set<string>();
// 扫描失败的文件夹:其下的缓存条目不参与"已删除清理",
// 避免移动盘/网络盘暂时不可用时整个文件夹的歌曲被误删(#713)
const failedFolders: string[] = [];
// 遍历每个文件夹进行扫描
for (const folderPath of folderPaths.value) {
@@ -138,6 +141,7 @@ export const useLocalMusicStore = defineStore(
if ((result as any).error) {
console.error(`扫描文件夹失败: ${folderPath}`, (result as any).error);
message.error(`扫描失败: ${(result as any).error}`);
failedFolders.push(folderPath);
continue;
}
@@ -178,12 +182,23 @@ export const useLocalMusicStore = defineStore(
} catch (error) {
console.error(`扫描文件夹出错: ${folderPath}`, error);
message.error(`扫描文件夹出错: ${folderPath}`);
failedFolders.push(folderPath);
}
}
/** 判断文件路径是否位于某个扫描失败的文件夹下 */
const isUnderFailedFolder = (filePath: string): boolean =>
failedFolders.some((folder) => {
if (!filePath.startsWith(folder)) return false;
if (folder.endsWith('/') || folder.endsWith('\\')) return true;
const next = filePath.charAt(folder.length);
return next === '/' || next === '\\';
});
// 4. 清理已删除文件:从 IndexedDB 移除磁盘上不存在的条目
// (扫描失败的文件夹跳过清理,其文件未被枚举并不代表已删除)
for (const [filePath, entry] of cachedMap) {
if (!diskFilePaths.has(filePath)) {
if (!diskFilePaths.has(filePath) && !isUnderFailedFolder(filePath)) {
await localDB.deleteData(LOCAL_MUSIC_STORE, entry.id);
}
}
@@ -213,6 +228,19 @@ export const useLocalMusicStore = defineStore(
}
}
/**
* 从本地列表移除单个条目(仅软件层面移除,不删除磁盘文件)(#713)
* @param id 条目 IDgenerateId 生成的 hex 字符串)
*/
async function removeEntry(id: string): Promise<void> {
const localDB = await getDB();
await localDB.deleteData(LOCAL_MUSIC_STORE, id);
const index = musicList.value.findIndex((entry) => entry.id === id);
if (index !== -1) {
musicList.value.splice(index, 1);
}
}
/**
* 清理缓存:检查文件存在性,移除已不存在的文件条目
*/
@@ -271,6 +299,7 @@ export const useLocalMusicStore = defineStore(
removeFolder,
scanFolders,
loadFromCache,
removeEntry,
clearCache
};
},