From 919577365d0ce7718b087f04059a5f7433f89c03 Mon Sep 17 00:00:00 2001 From: JiaoTianBo Date: Mon, 30 Mar 2026 19:17:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(knowledge-base):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E9=A2=84=E8=A7=88=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将KbDocumentVO的fileSize类型由数字改为字符串,并新增fileUrl字段 - 引入vue-pdf-embed组件实现PDF预览 - 新增预览相关响应式状态变量及控制方法 - 支持PDF分页显示、全部页显示、旋转和打印功能 - 支持文本和Markdown文件通过iframe预览 - 对不支持直接预览的文件类型显示提示并提供打开下载链接 - 在操作栏新增“预览”按钮,符合文档状态才显示 - 添加预览对话框及配套样式,提升用户体验 --- src/api/ai-chat.ts | 3 +- src/views/knowledge-base/index.vue | 257 ++++++++++++++++++++++++++++- 2 files changed, 258 insertions(+), 2 deletions(-) diff --git a/src/api/ai-chat.ts b/src/api/ai-chat.ts index 03ae1bf..5d6f157 100644 --- a/src/api/ai-chat.ts +++ b/src/api/ai-chat.ts @@ -54,8 +54,9 @@ export interface KbDocumentVO { title: string; docType: string; // report/document/text/data/other fileType: string; // pdf/doc/txt/md等 - fileSize: number; // 字节 + fileSize: string; // 字节 filePath: string; + fileUrl?: string; // 文件访问URL sourceType: string; // upload/project/risk等 chunkCount: number; // 分块数量 status: "pending" | "processing" | "active" | "error"; diff --git a/src/views/knowledge-base/index.vue b/src/views/knowledge-base/index.vue index db92290..24bac6c 100644 --- a/src/views/knowledge-base/index.vue +++ b/src/views/knowledge-base/index.vue @@ -2,6 +2,7 @@ import { ref, onMounted, watch } from "vue"; import { ElMessage, ElMessageBox } from "element-plus"; import { useRenderIcon } from "@/components/ReIcon/src/hooks"; +import VuePdfEmbed from "vue-pdf-embed"; import { getDocuments, uploadDocument, @@ -25,6 +26,7 @@ import DocIcon from "~icons/ri/file-word-line"; import TxtIcon from "~icons/ri/file-text-line"; import ViewIcon from "~icons/ri/eye-line"; import ChunkIcon from "~icons/ri/file-list-3-line"; +import PreviewIcon from "~icons/ri/file-search-line"; defineOptions({ name: "KnowledgeBase" @@ -51,6 +53,17 @@ const currentChunkDoc = ref(null); const selectedChunk = ref(null); const chunkDetailVisible = ref(false); +// 预览相关 +const previewDialogVisible = ref(false); +const previewDoc = ref(null); +const previewLoading = ref(false); +const previewPageCount = ref(1); +const previewCurrentPage = ref(1); +const previewRotation = ref(0); +const previewPdfRef = ref(null); +const previewAllPages = ref(false); +const previewRotations = [0, 90, 180, 270]; + // 加载项目列表 async function loadProjects() { projectLoading.value = true; @@ -234,6 +247,62 @@ function handleViewChunkDetail(chunk: DocumentChunkVO) { chunkDetailVisible.value = true; } +// 预览文档 +function handlePreview(doc: KbDocumentVO) { + previewDoc.value = doc; + previewDialogVisible.value = true; + previewLoading.value = true; + previewCurrentPage.value = 1; + previewRotation.value = 0; + previewAllPages.value = false; +} + +// PDF渲染完成 +function handlePdfRender() { + previewLoading.value = false; + if (previewPdfRef.value?.doc) { + previewPageCount.value = previewPdfRef.value.doc.numPages; + } +} + +// 切换显示所有页面 +function handlePreviewAllPagesChange() { + previewCurrentPage.value = previewAllPages.value ? null : 1; +} + +// 旋转PDF +function handleRotatePdf() { + previewRotation.value = + previewRotation.value === 3 ? 0 : previewRotation.value + 1; +} + +// 打印PDF +function handlePrintPdf() { + previewPdfRef.value?.print(); +} + +// 打开文件链接 +function handleOpenFile(url: string) { + window.open(url, "_blank"); +} + +// 判断是否可预览 +function canPreview(doc: KbDocumentVO): boolean { + return !!doc.fileUrl && doc.status === "active"; +} + +// 获取文件类型显示名 +function getFileTypeName(fileType: string): string { + const typeMap: Record = { + pdf: "PDF", + doc: "Word", + docx: "Word", + txt: "文本", + md: "Markdown" + }; + return typeMap[fileType.toLowerCase()] || fileType.toUpperCase(); +} + // 获取状态标签类型 function getStatusType( status: DocumentStatus @@ -402,8 +471,17 @@ onMounted(() => { - + + + + +