You've already forked DataMate
feat(knowledge): 添加知识库条目预览功能
- 集成 docx4j 和 LibreOffice 实现 Office 文档转 PDF 预览 - 新增 KnowledgeItemPreviewService 处理预览转换逻辑 - 添加异步任务 KnowledgeItemPreviewAsyncService 进行文档转换 - 实现预览状态管理包括待转换、转换中、就绪和失败状态 - 在前端界面添加 Office 文档预览状态标签显示 - 支持 DOC/DOCX 文件在线预览功能 - 添加预览元数据存储和管理机制
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
Empty,
|
||||
Input,
|
||||
Modal,
|
||||
Tag,
|
||||
Table,
|
||||
Tooltip,
|
||||
} from "antd";
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
deleteKnowledgeItemByIdUsingDelete,
|
||||
deleteKnowledgeSetByIdUsingDelete,
|
||||
downloadKnowledgeItemFileUsingGet,
|
||||
convertKnowledgeItemPreviewUsingPost,
|
||||
exportKnowledgeItemsUsingGet,
|
||||
queryKnowledgeDirectoriesUsingGet,
|
||||
queryKnowledgeItemsUsingGet,
|
||||
@@ -61,6 +63,53 @@ const PREVIEW_MODAL_WIDTH = {
|
||||
const PREVIEW_TEXT_FONT_SIZE = 12;
|
||||
const PREVIEW_TEXT_PADDING = 12;
|
||||
const PREVIEW_AUDIO_PADDING = 40;
|
||||
const OFFICE_FILE_EXTENSIONS = [".doc", ".docx"];
|
||||
|
||||
type OfficePreviewStatus = "UNSET" | "PENDING" | "PROCESSING" | "READY" | "FAILED";
|
||||
|
||||
const OFFICE_PREVIEW_STATUS_META: Record<OfficePreviewStatus, { label: string; color: string }> = {
|
||||
UNSET: { label: "未转换", color: "default" },
|
||||
PENDING: { label: "待转换", color: "default" },
|
||||
PROCESSING: { label: "转换中", color: "processing" },
|
||||
READY: { label: "可预览", color: "success" },
|
||||
FAILED: { label: "转换失败", color: "error" },
|
||||
};
|
||||
|
||||
type OfficePreviewMetadata = {
|
||||
previewStatus?: string;
|
||||
previewError?: string;
|
||||
previewUpdatedAt?: string;
|
||||
previewPdfPath?: string;
|
||||
};
|
||||
|
||||
const isOfficeFileName = (fileName?: string) => {
|
||||
const lowerName = (fileName || "").toLowerCase();
|
||||
return OFFICE_FILE_EXTENSIONS.some((ext) => lowerName.endsWith(ext));
|
||||
};
|
||||
|
||||
const parseOfficePreviewMetadata = (metadata?: string): OfficePreviewMetadata => {
|
||||
if (!metadata) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(metadata) as OfficePreviewMetadata;
|
||||
return parsed || {};
|
||||
} catch (error) {
|
||||
console.warn("解析预览元数据失败", error);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeOfficePreviewStatus = (status?: string): OfficePreviewStatus => {
|
||||
if (!status) {
|
||||
return "UNSET";
|
||||
}
|
||||
const upper = status.toUpperCase();
|
||||
if (upper === "PENDING" || upper === "PROCESSING" || upper === "READY" || upper === "FAILED") {
|
||||
return upper as OfficePreviewStatus;
|
||||
}
|
||||
return "UNSET";
|
||||
};
|
||||
|
||||
type KnowledgeItemRow = KnowledgeItemView & {
|
||||
isDirectory?: boolean;
|
||||
@@ -284,20 +333,60 @@ const KnowledgeSetDetail = () => {
|
||||
return "文件";
|
||||
};
|
||||
|
||||
const resolveOfficePreviewDisplay = (record: KnowledgeItemView) => {
|
||||
const fileName = resolvePreviewFileName(record);
|
||||
if (!isOfficeFileName(fileName)) {
|
||||
return null;
|
||||
}
|
||||
const previewMetadata = parseOfficePreviewMetadata(record.metadata);
|
||||
const status = normalizeOfficePreviewStatus(previewMetadata.previewStatus);
|
||||
const meta = OFFICE_PREVIEW_STATUS_META[status];
|
||||
return {
|
||||
status,
|
||||
label: meta.label,
|
||||
color: meta.color,
|
||||
error: previewMetadata.previewError,
|
||||
};
|
||||
};
|
||||
|
||||
const handlePreviewItemFile = async (record: KnowledgeItemView) => {
|
||||
if (!id) return;
|
||||
const fileName = resolvePreviewFileName(record);
|
||||
const previewUrl = `/api/data-management/knowledge-sets/${id}/items/${record.id}/preview`;
|
||||
setPreviewFileName(fileName);
|
||||
setPreviewContent("");
|
||||
setPreviewMediaUrl("");
|
||||
|
||||
if (isOfficeFileName(fileName)) {
|
||||
setPreviewFileType("pdf");
|
||||
setPreviewLoadingItemId(record.id);
|
||||
try {
|
||||
const { data } = await convertKnowledgeItemPreviewUsingPost(id, record.id);
|
||||
const status = normalizeOfficePreviewStatus(data?.status);
|
||||
if (status === "READY") {
|
||||
setPreviewMediaUrl(previewUrl);
|
||||
setPreviewVisible(true);
|
||||
} else if (status === "FAILED") {
|
||||
message.error(data?.previewError || "转换失败,请稍后重试");
|
||||
} else {
|
||||
message.info("已开始转换,请稍后重试");
|
||||
}
|
||||
fetchItems();
|
||||
} catch (error) {
|
||||
console.error("触发预览转换失败", error);
|
||||
message.error("触发预览转换失败");
|
||||
} finally {
|
||||
setPreviewLoadingItemId(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const fileType = resolvePreviewFileType(fileName);
|
||||
if (!fileType) {
|
||||
message.warning("不支持预览该文件类型");
|
||||
return;
|
||||
}
|
||||
|
||||
const previewUrl = `/api/data-management/knowledge-sets/${id}/items/${record.id}/preview`;
|
||||
setPreviewFileName(fileName);
|
||||
setPreviewFileType(fileType);
|
||||
setPreviewContent("");
|
||||
setPreviewMediaUrl("");
|
||||
|
||||
if (fileType === "text") {
|
||||
setPreviewLoadingItemId(record.id);
|
||||
@@ -669,8 +758,20 @@ const KnowledgeSetDetail = () => {
|
||||
const isFileRecord =
|
||||
record.contentType === KnowledgeContentType.FILE ||
|
||||
record.sourceType === KnowledgeSourceType.FILE_UPLOAD;
|
||||
const officePreview = isFileRecord ? resolveOfficePreviewDisplay(record) : null;
|
||||
return (
|
||||
<>
|
||||
{officePreview && (
|
||||
<Tooltip
|
||||
title={
|
||||
officePreview.status === "FAILED"
|
||||
? officePreview.error || officePreview.label
|
||||
: officePreview.label
|
||||
}
|
||||
>
|
||||
<Tag color={officePreview.color}>{officePreview.label}</Tag>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isFileRecord && (
|
||||
<Tooltip title="预览">
|
||||
<Button
|
||||
|
||||
@@ -101,6 +101,16 @@ export function downloadKnowledgeItemFileUsingGet(setId: string, itemId: string,
|
||||
return download(`/api/data-management/knowledge-sets/${setId}/items/${itemId}/file`, null, fileName || "");
|
||||
}
|
||||
|
||||
// 知识条目预览状态
|
||||
export function queryKnowledgeItemPreviewStatusUsingGet(setId: string, itemId: string) {
|
||||
return get(`/api/data-management/knowledge-sets/${setId}/items/${itemId}/preview/status`);
|
||||
}
|
||||
|
||||
// 触发知识条目预览转换
|
||||
export function convertKnowledgeItemPreviewUsingPost(setId: string, itemId: string) {
|
||||
return post(`/api/data-management/knowledge-sets/${setId}/items/${itemId}/preview/convert`, {});
|
||||
}
|
||||
|
||||
// 导出知识条目
|
||||
export function exportKnowledgeItemsUsingGet(setId: string) {
|
||||
return download(`/api/data-management/knowledge-sets/${setId}/items/export`);
|
||||
|
||||
Reference in New Issue
Block a user