diff --git a/frontend/src/pages/KnowledgeManagement/Detail/KnowledgeSetDetail.tsx b/frontend/src/pages/KnowledgeManagement/Detail/KnowledgeSetDetail.tsx index 3ca5f2f..60840ac 100644 --- a/frontend/src/pages/KnowledgeManagement/Detail/KnowledgeSetDetail.tsx +++ b/frontend/src/pages/KnowledgeManagement/Detail/KnowledgeSetDetail.tsx @@ -6,10 +6,12 @@ import { Card, Descriptions, Empty, + Modal, Table, Tag, + Tooltip, } from "antd"; -import { DeleteOutlined, EditOutlined, PlusOutlined } from "@ant-design/icons"; +import { DeleteOutlined, EditOutlined, EyeOutlined, PlusOutlined } from "@ant-design/icons"; import { useNavigate, useParams } from "react-router"; import DetailHeader from "@/components/DetailHeader"; import { SearchControls } from "@/components/SearchControls"; @@ -29,6 +31,7 @@ import { import { KnowledgeItem, KnowledgeSet, + KnowledgeContentType, KnowledgeStatusType, } from "../knowledge-management.model"; import CreateKnowledgeSet from "../components/CreateKnowledgeSet"; @@ -36,6 +39,8 @@ import KnowledgeItemEditor from "../components/KnowledgeItemEditor"; import ImportKnowledgeItemsDialog from "../components/ImportKnowledgeItemsDialog"; import { formatDate } from "@/utils/unit"; +const MAX_READ_LENGTH = 50000; + const KnowledgeSetDetail = () => { const navigate = useNavigate(); const { message } = App.useApp(); @@ -44,6 +49,10 @@ const KnowledgeSetDetail = () => { const [showEdit, setShowEdit] = useState(false); const [itemEditorOpen, setItemEditorOpen] = useState(false); const [currentItem, setCurrentItem] = useState(null); + const [readItemId, setReadItemId] = useState(null); + const [readModalOpen, setReadModalOpen] = useState(false); + const [readContent, setReadContent] = useState(""); + const [readTitle, setReadTitle] = useState(""); const fetchKnowledgeSet = useCallback(async () => { if (!id) return; @@ -91,6 +100,55 @@ const KnowledgeSetDetail = () => { fetchData(); }; + const isReadableItem = (record: KnowledgeItemView) => { + return ( + record.contentType === KnowledgeContentType.TEXT || + record.contentType === KnowledgeContentType.MARKDOWN + ); + }; + + const handleReadItem = async (record: KnowledgeItemView) => { + setReadItemId(record.id); + setReadTitle(record.title || "知识条目"); + + if (!record.sourceDatasetId || !record.sourceFileId) { + const content = record.content || ""; + if (content.length > MAX_READ_LENGTH) { + setReadContent( + `${content.slice(0, MAX_READ_LENGTH)}\n\n... (内容过长,仅显示前 ${MAX_READ_LENGTH} 字符)` + ); + } else { + setReadContent(content); + } + setReadModalOpen(true); + setReadItemId(null); + return; + } + + const fileUrl = `/api/data-management/datasets/${record.sourceDatasetId}/files/${record.sourceFileId}/download`; + + try { + const response = await fetch(fileUrl); + if (!response.ok) { + throw new Error("下载失败"); + } + const text = await response.text(); + if (text.length > MAX_READ_LENGTH) { + setReadContent( + `${text.slice(0, MAX_READ_LENGTH)}\n\n... (内容过长,仅显示前 ${MAX_READ_LENGTH} 字符)` + ); + } else { + setReadContent(text); + } + setReadModalOpen(true); + } catch (error) { + console.error("读取知识条目失败", error); + message.error("读取失败,请稍后重试"); + } finally { + setReadItemId(null); + } + }; + const statusMeta = knowledgeSet?.status ? knowledgeStatusMap[knowledgeSet.status] : undefined; @@ -164,9 +222,19 @@ const KnowledgeSetDetail = () => { { title: "操作", key: "actions", - width: 140, + width: 200, render: (_: unknown, record: KnowledgeItemView) => (
+ + , + ]} + > +
+          {readContent}
+        
+
); };