Merge branch 'ji' of http://192.168.0.200/knowledge-base-bj/deerflow-server into ji
This commit is contained in:
commit
fb692a0555
30 files changed
+3618
-6
No files matched your search
@@ -0,0 +1,158 @@
|
||||
"use client";
|
||||
|
||||
import { BookMarked } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { useKnowledgeActions } from "@/core/knowledge/hooks";
|
||||
|
||||
import { useThread } from "./messages/context";
|
||||
import { Tooltip } from "./tooltip";
|
||||
|
||||
export function KnowledgeSaveTrigger({ threadId }: { threadId: string }) {
|
||||
const { thread } = useThread();
|
||||
const navigate = useNavigate();
|
||||
const { ingestThread } = useKnowledgeActions();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [title, setTitle] = useState("");
|
||||
const [tags, setTags] = useState("");
|
||||
const [status, setStatus] = useState<"approved" | "draft">("approved");
|
||||
const [mode, setMode] = useState<"summary" | "full">("summary");
|
||||
const [includeSources, setIncludeSources] = useState(true);
|
||||
|
||||
if (thread.messages.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
const result = await ingestThread.mutateAsync({
|
||||
threadId,
|
||||
payload: {
|
||||
title: title.trim() || null,
|
||||
tags: tags
|
||||
.split(/[,,\s]+/)
|
||||
.map((t) => t.trim())
|
||||
.filter(Boolean),
|
||||
status,
|
||||
mode,
|
||||
include_sources: includeSources,
|
||||
},
|
||||
});
|
||||
toast.success("已保存到知识库", {
|
||||
action: {
|
||||
label: "查看",
|
||||
onClick: () =>
|
||||
navigate(`/page/workspace/knowledge/${result.note.id}`),
|
||||
},
|
||||
});
|
||||
setOpen(false);
|
||||
setTitle("");
|
||||
setTags("");
|
||||
} catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : "保存失败");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip content="保存到知识库">
|
||||
<Button
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
variant="ghost"
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
<BookMarked />
|
||||
保存
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent className="max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>保存到知识库</DialogTitle>
|
||||
<DialogDescription>
|
||||
将本次对话沉淀为 obsidian-wiki 兼容的知识笔记。
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-3">
|
||||
<Input
|
||||
placeholder="标题(留空自动生成)"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
/>
|
||||
<Input
|
||||
placeholder="标签,逗号分隔"
|
||||
value={tags}
|
||||
onChange={(e) => setTags(e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<Select
|
||||
value={status}
|
||||
onValueChange={(v) => setStatus(v as "approved" | "draft")}
|
||||
>
|
||||
<SelectTrigger className="flex-1">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="approved">正式</SelectItem>
|
||||
<SelectItem value="draft">草稿</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select
|
||||
value={mode}
|
||||
onValueChange={(v) => setMode(v as "summary" | "full")}
|
||||
>
|
||||
<SelectTrigger className="flex-1">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="summary">摘要</SelectItem>
|
||||
<SelectItem value="full">完整</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={includeSources}
|
||||
onChange={(e) => setIncludeSources(e.target.checked)}
|
||||
className="size-4"
|
||||
/>
|
||||
包含搜索 / 工具来源
|
||||
</label>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setOpen(false)}>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => void handleSave()}
|
||||
disabled={ingestThread.isPending}
|
||||
>
|
||||
{ingestThread.isPending ? "保存中..." : "保存"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user