Explorar o código

feat: 前端导入路径预检测+重映射+类型更新

Mochocyang hai 3 meses
pai
achega
32f3e8c800
Modificáronse 2 ficheiros con 68 adicións e 0 borrados
  1. 59 0
      src/lib/backup/import.ts
  2. 9 0
      src/lib/backup/types.ts

+ 59 - 0
src/lib/backup/import.ts

@@ -10,6 +10,7 @@ import type {
   ImportResult,
   ImportStrategy,
   ProjectRestoreInfo,
+  ProjectManifestEntry,
   BackupProgressCallback,
 } from "./types"
 
@@ -31,6 +32,50 @@ async function refreshCurrentProjectIfNeeded(restoredProjects: Array<{ path: str
   }
 }
 
+/**
+ * 导入前读取备份 manifest,检查路径可达性。
+ * 如果有项目路径的盘符不存在,弹窗让用户选择新目录并构建路径重映射表。
+ * @returns 重映射表(projectId -> 新路径),如果无需重映射则返回空对象。
+ *          如果用户取消选择,返回 null。
+ */
+async function checkAndRemapPaths(zipPath: string): Promise<Record<string, string> | null> {
+  let manifest: ProjectManifestEntry[]
+  try {
+    manifest = await invoke<ProjectManifestEntry[]>("read_backup_manifest", { zipPath })
+  } catch {
+    // manifest 读取失败,回退到原行为(直接导入)
+    return {}
+  }
+
+  const inaccessibleProjects = manifest.filter((p) => !p.pathAccessible)
+
+  if (inaccessibleProjects.length === 0) {
+    // 所有路径可达,无需重映射
+    return {}
+  }
+
+  // 弹窗让用户选择新的基础目录
+  const newBaseDir = await open({
+    title: "部分项目路径不存在(原盘符不可用),请选择新的存放目录",
+    directory: true,
+    multiple: false,
+  })
+
+  if (!newBaseDir || typeof newBaseDir !== "string") {
+    // 用户取消
+    return null
+  }
+
+  // 构建重映射表:{ projectId: "{新目录}/{原项目文件夹名}" }
+  const overrides: Record<string, string> = {}
+  for (const project of inaccessibleProjects) {
+    const folderName = project.path.split(/[\\/]/).filter(Boolean).pop() || project.id
+    overrides[project.id] = `${newBaseDir}\\${folderName}`
+  }
+
+  return overrides
+}
+
 export async function importBackup(
   strategy: ImportStrategy,
   projects?: ProjectRestoreInfo[],
@@ -52,10 +97,24 @@ export async function importBackup(
     }
   }
 
+  // 导入前检查路径可达性,必要时弹窗让用户选择新目录
+  const pathOverrides = await checkAndRemapPaths(zipPath)
+  if (pathOverrides === null) {
+    return {
+      success: false,
+      appState: null,
+      localStorageData: null,
+      projects: [],
+      warnings: [],
+      error: "用户取消了导入",
+    }
+  }
+
   const params: ImportParams = {
     zipPath,
     strategy,
     projects,
+    projectPathOverrides: Object.keys(pathOverrides).length > 0 ? pathOverrides : undefined,
   }
 
   let unlisten: UnlistenFn | undefined

+ 9 - 0
src/lib/backup/types.ts

@@ -35,6 +35,7 @@ export interface ImportParams {
   zipPath: string
   strategy: ImportStrategy
   projects?: ProjectRestoreInfo[]
+  projectPathOverrides?: Record<string, string>
 }
 
 /** 项目恢复结果 */
@@ -64,6 +65,14 @@ export interface BackupManifest {
   projects: ProjectBackupInfo[]
 }
 
+/** manifest 项目条目(含路径可达性) */
+export interface ProjectManifestEntry {
+  id: string
+  path: string
+  name: string
+  pathAccessible: boolean
+}
+
 /** 进度事件载荷 */
 export interface BackupProgressPayload {
   operation: "export" | "import"