diff --git a/apps/web/app/projects/[id]/review/page.tsx b/apps/web/app/projects/[id]/review/page.tsx
index 5706c80..4903df8 100644
--- a/apps/web/app/projects/[id]/review/page.tsx
+++ b/apps/web/app/projects/[id]/review/page.tsx
@@ -1,8 +1,15 @@
import { notFound } from "next/navigation";
import { ReviewReport } from "@/components/review/ReviewReport";
-import { fetchDraft, fetchProject, fetchReviews } from "@/lib/api/server";
+import {
+ fetchDraft,
+ fetchOutline,
+ fetchProject,
+ fetchReviews,
+} from "@/lib/api/server";
+import type { ProjectResponse } from "@/lib/api/types";
import { latestReview } from "@/lib/review/history";
+import type { ChapterEntry } from "@/lib/workbench/chapter";
interface PageProps {
params: Promise<{ id: string }>;
@@ -17,22 +24,51 @@ export default async function ReviewPage({ params, searchParams }: PageProps) {
const { id } = await params;
const { chapter } = await searchParams;
const chapterNo = parsePositiveInt(chapter) ?? DEFAULT_CHAPTER_NO;
+
+ // 仅当项目确实取不到时才判 404;审稿/草稿/大纲等次级拉取的瞬时错误不应把整页变成「找不到」。
+ let project: ProjectResponse;
try {
- const project = await fetchProject(id);
- const history = await fetchReviews(id, chapterNo);
- // 终稿初值取已存草稿(GET .../draft,404→null→"");否则 final_text 为空 → accept 422。
- const draft = await fetchDraft(id, chapterNo);
- return (
-
- );
+ project = await fetchProject(id);
} catch {
notFound();
}
+
+ // 审稿留痕(GET .../reviews,失败→空历史)。
+ let initialReview;
+ try {
+ const history = await fetchReviews(id, chapterNo);
+ initialReview = latestReview(history.reviews);
+ } catch {
+ initialReview = undefined;
+ }
+
+ // 终稿初值取已存草稿(GET .../draft,404→null→"");否则 final_text 为空 → accept 422。
+ let initialDraft = "";
+ try {
+ const draft = await fetchDraft(id, chapterNo);
+ initialDraft = draft?.content ?? "";
+ } catch {
+ initialDraft = "";
+ }
+
+ // 大纲章节作章节导航目录(fetchOutline 已对空/错误降级为 []);首个节拍当短标题。
+ const chapters: ChapterEntry[] = (await fetchOutline(id)).map((c) => ({
+ no: c.no,
+ title: c.beats?.[0],
+ }));
+
+ return (
+ // key=章号:客户端切章(?chapter=N 变化)时强制重挂载,用新章审稿/草稿刷新状态,
+ // 避免 ReviewReport 内 useState 基线沿用上一章。
+
+ );
}
function parsePositiveInt(raw: string | undefined): number | null {
diff --git a/apps/web/components/review/AcceptPanel.tsx b/apps/web/components/review/AcceptPanel.tsx
index 614c085..a16b332 100644
--- a/apps/web/components/review/AcceptPanel.tsx
+++ b/apps/web/components/review/AcceptPanel.tsx
@@ -1,13 +1,14 @@
"use client";
import Link from "next/link";
-import { ArrowRight, CheckCircle2, ListTree } from "lucide-react";
+import { ArrowRight, CheckCircle2, ClipboardCheck, ListTree } from "lucide-react";
import { Button } from "@/components/ui/Button";
import { StatusNote } from "@/components/ui/StatusNote";
import type { AcceptResponse } from "@/lib/api/types";
import { buildAcceptPreview } from "@/lib/review/accept-preview";
import { ThinkingIndicator } from "@/components/ThinkingIndicator";
+import { reviewChapterHref } from "@/lib/review/chapterNav";
import { buttonClass } from "@/lib/ui/variants";
interface AcceptPanelProps {
@@ -85,6 +86,13 @@ export function AcceptPanel({
写第 {chapterNo + 1} 章
+
+
+ 审第 {chapterNo + 1} 章
+
第 {chapterNo} 章 审稿报告
+
{conflictCount} 冲突