This commit is contained in:
Rocky Kwok committed 2026-06-24 15:41:25 +08:00
commit 71a865dc0c
2 files changed
+45 -14

No files matched your search

@@ -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 || "导入失败,请重试");
@@ -154,6 +154,7 @@ function SourcesDialog({
isLoadingSources,
selectedSourceIds,
pendingSources,
pendingImportTitles,
onToggle,
onDelete,
}: {
@@ -164,6 +165,7 @@ function SourcesDialog({
isLoadingSources: boolean;
selectedSourceIds: number[];
pendingSources: Map<string, { id?: number; title: string }>;
pendingImportTitles: string[];
onToggle: (id: number) => void;
onDelete: (id: number) => void;
}) {
@@ -216,6 +218,14 @@ function SourcesDialog({
.map(([taskId, s]) => (
<PendingSourceCard key={taskId} title={s.title} />
))}
{pendingImportTitles
.filter((t) => !sources.some((src) => {
const srcTitle = src.title ?? (src.document as { title?: string } | undefined)?.title ?? "";
return srcTitle === t;
}))
.map((t) => (
<PendingSourceCard key={`import-${t}`} title={t} />
))}
{sources.map((src) => (
<SourceCard
key={src.id}
@@ -269,6 +279,8 @@ export default function StudioNotebookDetailPage({
// 轮询任务管理:task_id -> 来源信息
const [pendingSources, setPendingSources] = useState<Map<string, { id?: number; title: string }>>(new Map());
const pollingIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
// 同步导入(搜索导入)的占位标题列表,sources 刷新后按标题匹配移除
const [pendingImportTitles, setPendingImportTitles] = useState<string[]>([]);
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}
/>