Преглед изворни кода

修复去AI味审核确认后的章节自动推进

darknessomi пре 1 месец
родитељ
комит
e1dafce79e

+ 1 - 2
src/components/layout/preview-panel.tsx

@@ -1761,8 +1761,7 @@ export function PreviewPanel() {
               try {
                 await applyDeAiBatchChapter(task.chapterPath, body)
                 useDeAiTaskStore.getState().updateTask(chapterId, { candidateContent: body })
-                useDeAiTaskStore.getState().confirmTask(chapterId)
-                useDeAiTaskStore.getState().closeReview(project.path)
+                useDeAiTaskStore.getState().confirmTaskAndAdvanceReview(chapterId)
                 toast.success(`${task.chapterTitle} 去AI味结果已保存`)
               } catch (err) {
                 console.error("保存去AI味结果失败:", err)

+ 1 - 0
src/components/novel/de-ai-batch-entry.spec.ts

@@ -15,6 +15,7 @@ describe("de-ai batch entry and settings", () => {
     expect(previewPanel).toContain("createDeAiBatchChapterApplier")
     expect(previewPanel).toContain("await applyDeAiBatchChapter(task.chapterPath, body)")
     expect(previewPanel).toContain("updateTask(chapterId, { candidateContent: body })")
+    expect(previewPanel).toContain("confirmTaskAndAdvanceReview(chapterId)")
     expect(previewPanel).toContain("useDeAiTaskStore.getState().closeReview(project.path)")
     expect(previewPanel).toContain("saveDeAiDraftWithoutOverwrite")
     expect(previewPanel).toContain("writeFileIfAbsent")

+ 38 - 0
src/stores/de-ai-task-store.spec.ts

@@ -128,4 +128,42 @@ describe("de-ai task store project isolation", () => {
     expect(reviews?.["c:/novel-a"]?.open).toBe(false)
     expect(reviews?.["c:/novel-b"]?.open).toBe(true)
   })
+
+  it("确认当前章后保留含其他待确认章节的审核弹窗,并切换到下一章", () => {
+    const firstId = useDeAiTaskStore.getState().startTask({
+      projectPath: "C:/Novel-A",
+      chapterPath: "C:/Novel-A/wiki/chapters/1.md",
+      chapterTitle: "第一章",
+      skillId: "skill-a",
+      skillName: "自然叙事",
+      skillContent: "保留角色语气",
+      modelName: "model-a",
+      sourceContent: "原文 A",
+    })
+    const secondId = useDeAiTaskStore.getState().startTask({
+      projectPath: "C:/Novel-A",
+      chapterPath: "C:/Novel-A/wiki/chapters/2.md",
+      chapterTitle: "第二章",
+      skillId: "skill-a",
+      skillName: "自然叙事",
+      skillContent: "保留角色语气",
+      modelName: "model-a",
+      sourceContent: "原文 B",
+    })
+    useDeAiTaskStore.getState().finishTask(firstId, "候选 A")
+    useDeAiTaskStore.getState().finishTask(secondId, "候选 B")
+
+    useDeAiTaskStore.getState().confirmTaskAndAdvanceReview(firstId)
+
+    let state = useDeAiTaskStore.getState()
+    expect(state.reviewByProject["c:/novel-a"]).toEqual({ open: true, chapterId: secondId })
+    expect(state.tasks.find((task) => task.id === firstId)?.status).toBe("confirmed")
+    expect(state.tasks.find((task) => task.id === secondId)?.status).toBe("ready")
+
+    useDeAiTaskStore.getState().confirmTaskAndAdvanceReview(secondId)
+
+    state = useDeAiTaskStore.getState()
+    expect(state.reviewByProject["c:/novel-a"]).toEqual({ open: false, chapterId: null })
+    expect(selectProjectDeAiTasks(state.tasks, "C:/Novel-A")).toHaveLength(0)
+  })
 })

+ 40 - 0
src/stores/de-ai-task-store.ts

@@ -45,6 +45,7 @@ interface DeAiTaskState {
   failTask: (taskId: string, error: string) => void
   cancelTask: (taskId: string) => void
   confirmTask: (taskId: string) => void
+  confirmTaskAndAdvanceReview: (taskId: string) => void
   removeTask: (taskId: string) => void
   clearConfirmed: (projectPath: string) => void
   openReview: (projectPath: string, taskId?: string) => void
@@ -168,6 +169,45 @@ export const useDeAiTaskStore = create<DeAiTaskState>((set, get) => ({
     }))
   },
 
+  confirmTaskAndAdvanceReview: (taskId) => {
+    const now = Date.now()
+    set((state) => {
+      const confirmedTask = state.tasks.find((task) => task.id === taskId)
+      if (!confirmedTask) return state
+
+      const projectKey = normalizedProjectPath(confirmedTask.projectPath)
+      const tasks = state.tasks.map((task) =>
+        task.id === taskId
+          ? { ...task, status: "confirmed" as const, updatedAt: now }
+          : task
+      )
+      const nextReadyTask = tasks.find(
+        (task) => task.status === "ready"
+          && normalizedProjectPath(task.projectPath) === projectKey
+      )
+
+      if (nextReadyTask) {
+        return {
+          tasks,
+          reviewByProject: {
+            ...state.reviewByProject,
+            [projectKey]: { open: true, chapterId: nextReadyTask.id },
+          },
+        }
+      }
+
+      return {
+        tasks: tasks.filter((task) =>
+          task.status !== "confirmed" || normalizedProjectPath(task.projectPath) !== projectKey
+        ),
+        reviewByProject: {
+          ...state.reviewByProject,
+          [projectKey]: CLOSED_PROJECT_REVIEW,
+        },
+      }
+    })
+  },
+
   removeTask: (taskId) => {
     set((state) => {
       const task = state.tasks.find((item) => item.id === taskId)