From 85cd2c888e8a1715b2c58746a3dda8180bacb6be Mon Sep 17 00:00:00 2001 From: xuhd Date: Wed, 24 Jun 2026 13:14:00 +0800 Subject: [PATCH] =?UTF-8?q?fix(notebook):=20=E6=9D=A5=E6=BA=90=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E5=AF=BC=E5=85=A5=E5=90=8E=E7=AB=8B=E5=88=BB=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=8D=A0=E4=BD=8D=E5=8D=A1=E5=B9=B6=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E6=9D=A5=E6=BA=90=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - handleImport 成功后调用 onUploadComplete 通知父组件(原先缺失)并关闭弹窗 - 新增 pendingImportTitles state,同步导入后立刻渲染 PendingSourceCard - sources 刷新后按标题匹配移除占位卡并自动选中新来源 Co-Authored-By: Claude Sonnet 4.6 --- .../assistant-ui/document-upload-popup.tsx | 11 ++++- .../src/pages/StudioNotebookDetailPage.tsx | 48 ++++++++++++++----- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/frontend-web/src/components/assistant-ui/document-upload-popup.tsx b/frontend-web/src/components/assistant-ui/document-upload-popup.tsx index a0afd6d5..78e314fe 100644 --- a/frontend-web/src/components/assistant-ui/document-upload-popup.tsx +++ b/frontend-web/src/components/assistant-ui/document-upload-popup.tsx @@ -405,8 +405,15 @@ const DocumentUploadPopupContent: FC<{ if (response.ok) { const data = await response.json() as { imported_count?: number }; toast.success(`成功导入 ${data.imported_count || dataList.length} 个来源`); - setListData([]); - handleBatchDelete(); + onUploadComplete?.( + selectedSearchSources.map((s) => ({ + id: undefined, + title: s.title, + task_id: undefined, + status: "success" as const, + })) + ); + onOpenChange(false); } else { const errorData = await response.json() as { detail?: string }; toast.error(errorData.detail || "导入失败,请重试"); diff --git a/frontend-web/src/pages/StudioNotebookDetailPage.tsx b/frontend-web/src/pages/StudioNotebookDetailPage.tsx index bfb7ea8b..03134ff6 100644 --- a/frontend-web/src/pages/StudioNotebookDetailPage.tsx +++ b/frontend-web/src/pages/StudioNotebookDetailPage.tsx @@ -154,6 +154,7 @@ function SourcesDialog({ isLoadingSources, selectedSourceIds, pendingSources, + pendingImportTitles, onToggle, onDelete, }: { @@ -164,6 +165,7 @@ function SourcesDialog({ isLoadingSources: boolean; selectedSourceIds: number[]; pendingSources: Map; + pendingImportTitles: string[]; onToggle: (id: number) => void; onDelete: (id: number) => void; }) { @@ -216,6 +218,14 @@ function SourcesDialog({ .map(([taskId, s]) => ( ))} + {pendingImportTitles + .filter((t) => !sources.some((src) => { + const srcTitle = src.title ?? (src.document as { title?: string } | undefined)?.title ?? ""; + return srcTitle === t; + })) + .map((t) => ( + + ))} {sources.map((src) => ( 来源信息 const [pendingSources, setPendingSources] = useState>(new Map()); const pollingIntervalRef = useRef | null>(null); + // 同步导入(搜索导入)的占位标题列表,sources 刷新后按标题匹配移除 + const [pendingImportTitles, setPendingImportTitles] = useState([]); useEffect(() => { if (!selectedModel && models.length > 0) { @@ -287,20 +299,25 @@ export default function StudioNotebookDetailPage({ } }, [sources, notebook?.id, initializedForNotebook]); - // sources 刷新后,将轮询成功的来源(按标题匹配)自动选中 + // sources 刷新后,将轮询成功/同步导入的来源(按标题匹配)自动选中,并移除占位卡 useEffect(() => { - if (pendingSelectTitlesRef.current.size === 0) return; - const matched = sources.filter((s) => { - const title = s.title ?? (s.document as { title?: string } | undefined)?.title ?? ""; - return pendingSelectTitlesRef.current.has(title); - }); - if (matched.length > 0) { - setSelectedSourceIds((prev) => [...new Set([...prev, ...matched.map((s) => s.id)])]); - matched.forEach((s) => { - const title = s.title ?? (s.document as { title?: string } | undefined)?.title ?? ""; - pendingSelectTitlesRef.current.delete(title); - }); + const getTitle = (s: SourceItem) => + s.title ?? (s.document as { title?: string } | undefined)?.title ?? ""; + + if (pendingSelectTitlesRef.current.size > 0) { + const matched = sources.filter((s) => pendingSelectTitlesRef.current.has(getTitle(s))); + if (matched.length > 0) { + setSelectedSourceIds((prev) => [...new Set([...prev, ...matched.map((s) => s.id)])]); + matched.forEach((s) => pendingSelectTitlesRef.current.delete(getTitle(s))); + } } + + setPendingImportTitles((prev) => { + if (prev.length === 0) return prev; + const sourceTitles = new Set(sources.map(getTitle)); + const remaining = prev.filter((t) => !sourceTitles.has(t)); + return remaining.length === prev.length ? prev : remaining; + }); }, [sources]); const citationRehypePlugins = useMemo(() => { const mapped = sources.map((s) => ({ @@ -392,6 +409,12 @@ export default function StudioNotebookDetailPage({ return newMap; }); } + // 同步导入(status=success,无 task_id):立刻显示占位卡 + 等 sources 刷新后自动选中 + const syncTitles = uploadedSources.filter(s => s.status === 'success' && s.title).map(s => s.title); + if (syncTitles.length > 0) { + syncTitles.forEach(t => pendingSelectTitlesRef.current.add(t)); + setPendingImportTitles(prev => [...new Set([...prev, ...syncTitles])]); + } // 刷新来源列表 void queryClient.invalidateQueries({ queryKey: ["studio", "sources", notebook.id] }); // 刷新 notebooks 数据以更新 source_count @@ -854,6 +877,7 @@ export default function StudioNotebookDetailPage({ isLoadingSources={isLoadingSources} selectedSourceIds={selectedSourceIds} pendingSources={pendingSources} + pendingImportTitles={pendingImportTitles} onToggle={handleToggleSource} onDelete={handleDeleteSource} />