feat: 优化 Knowledge 前端和需求文档
- 增强知识库前端交互 - 更新知识库 API 需求文档 - 添加 TODO 待办事项 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -48,7 +48,18 @@ const step3Valid = computed(() => {
|
||||
}
|
||||
return true
|
||||
})
|
||||
const step4Valid = computed(() => true) // Storage - 暂时默认通过
|
||||
const step4Valid = computed(() => {
|
||||
// Local 存储不需要额外配置
|
||||
if (storageConfig.value.type === 'local') {
|
||||
return true
|
||||
}
|
||||
// MinIO 存储需要填写所有字段
|
||||
if (storageConfig.value.type === 'minio') {
|
||||
return !!(storageConfig.value.endpoint && storageConfig.value.accessKeyId && storageConfig.value.secretAccessKey && storageConfig.value.bucket)
|
||||
}
|
||||
// S3 暂时默认通过
|
||||
return true
|
||||
})
|
||||
|
||||
// 获取当前步骤是否有效
|
||||
const isCurrentStepValid = computed(() => {
|
||||
@@ -123,11 +134,29 @@ const knowledgeDocuments = ref<any[]>([]) // 知识库文档列表
|
||||
const loadingDocuments = ref(false)
|
||||
const fileInput = ref<HTMLInputElement | null>(null)
|
||||
const uploading = ref(false)
|
||||
const previewUrl = ref('') // 文档预览URL
|
||||
const previewUrl = ref('') // 文档预览URL (blob URL)
|
||||
const previewDownloadUrl = ref('') // 原始下载链接
|
||||
const loadingPreview = ref(false)
|
||||
const previewPage = ref(1) // 当前页码
|
||||
const previewTotalPages = ref(1) // 总页数
|
||||
|
||||
// 使用代理接口加载PDF
|
||||
const loadPdfWithProxy = async (doc: any): Promise<string> => {
|
||||
if (!selectedKnowledge.value || !doc.file_key) {
|
||||
return ''
|
||||
}
|
||||
|
||||
try {
|
||||
const { getFileProxyUrl } = await import('./knowledge/useKnowledge')
|
||||
const proxyUrl = getFileProxyUrl(selectedKnowledge.value.id, doc.file_key)
|
||||
console.log('Using proxy URL for PDF:', proxyUrl)
|
||||
return proxyUrl
|
||||
} catch (error) {
|
||||
console.error('Failed to get proxy URL:', error)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
const newKbForm = ref({
|
||||
name: '',
|
||||
description: '',
|
||||
@@ -151,6 +180,10 @@ const parsingConfig = ref({
|
||||
// Storage 配置
|
||||
const storageConfig = ref({
|
||||
type: 'local',
|
||||
endpoint: '',
|
||||
accessKeyId: '',
|
||||
secretAccessKey: '',
|
||||
bucket: '',
|
||||
})
|
||||
|
||||
const openCreateDialog = () => {
|
||||
@@ -197,6 +230,13 @@ const createKnowledgeBase = async () => {
|
||||
docling_url: parsingConfig.value.engine === 'docling' ? parsingConfig.value.doclingUrl : undefined,
|
||||
enable_pdf: parsingConfig.value.enablePdf,
|
||||
pandoc: parsingConfig.value.pandoc,
|
||||
},
|
||||
storage_config: {
|
||||
type: storageConfig.value.type,
|
||||
endpoint: storageConfig.value.type === 'minio' ? storageConfig.value.endpoint : undefined,
|
||||
access_key_id: storageConfig.value.type === 'minio' ? storageConfig.value.accessKeyId : undefined,
|
||||
secret_access_key: storageConfig.value.type === 'minio' ? storageConfig.value.secretAccessKey : undefined,
|
||||
bucket: storageConfig.value.type === 'minio' ? storageConfig.value.bucket : undefined,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -297,17 +337,16 @@ const enterKnowledge = async (kb: any) => {
|
||||
selectedKnowledge.value = kb
|
||||
selectedFile.value = null
|
||||
previewUrl.value = ''
|
||||
previewDownloadUrl.value = ''
|
||||
|
||||
// 获取文档列表
|
||||
loadingDocuments.value = true
|
||||
try {
|
||||
const docs = await fetchKnowledgeDocuments(kb.id, fileFilter.value)
|
||||
console.log('Fetched documents:', docs)
|
||||
knowledgeDocuments.value = docs
|
||||
|
||||
// 自动选中第一个文档
|
||||
if (docs && docs.length > 0) {
|
||||
console.log('First doc:', docs[0])
|
||||
await selectDocument(docs[0])
|
||||
}
|
||||
} finally {
|
||||
@@ -336,15 +375,27 @@ const selectDocument = async (doc: any) => {
|
||||
selectedFile.value = doc.id
|
||||
selectedDocument.value = doc
|
||||
previewUrl.value = ''
|
||||
previewDownloadUrl.value = ''
|
||||
previewPage.value = 1
|
||||
previewTotalPages.value = 1
|
||||
|
||||
// 尝试从多个字段获取文件URL
|
||||
// 优先使用代理接口加载PDF
|
||||
if (doc.file_key && selectedKnowledge.value) {
|
||||
previewUrl.value = await loadPdfWithProxy(doc)
|
||||
if (previewUrl.value) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 如果代理失败,尝试使用file_url
|
||||
const fileUrl = doc.file_url || doc.fileUrl || doc.url || doc.FileURL
|
||||
if (fileUrl) {
|
||||
previewUrl.value = fileUrl
|
||||
} else if (selectedKnowledge.value && doc.status === 'parsed') {
|
||||
// 获取文档预览
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有file_url,调用预览API获取
|
||||
if (selectedKnowledge.value) {
|
||||
loadingPreview.value = true
|
||||
try {
|
||||
const { getDocumentPreview } = await import('./knowledge/useKnowledge')
|
||||
@@ -402,29 +453,22 @@ const handleFileSelect = async (event: Event) => {
|
||||
if (result.success) {
|
||||
ElMessage.success('File uploaded successfully')
|
||||
|
||||
// 后端返回 result.url 在顶层,result.document 里有 file_url
|
||||
const fileUrl = result.url || result.document?.file_url
|
||||
// 添加到文档列表
|
||||
const newDoc = result.document || {
|
||||
id: result.id,
|
||||
name: file.name,
|
||||
file_size: file.size,
|
||||
status: 'parsing',
|
||||
chunk_count: 0,
|
||||
uploaded_at: new Date().toISOString(),
|
||||
file_url: fileUrl
|
||||
}
|
||||
// 如果返回了file_url,添加到列表开头
|
||||
if (fileUrl) {
|
||||
previewUrl.value = fileUrl
|
||||
// 设置选中的文档信息
|
||||
// 刷新文档列表以获取最新数据(包括 file_key)
|
||||
await changeFileFilter(fileFilter.value)
|
||||
|
||||
// 获取刚上传的文档
|
||||
const uploadedDoc = knowledgeDocuments.value.find(d => d.id === result.id)
|
||||
if (uploadedDoc) {
|
||||
// 选中新上传的文档
|
||||
selectedFile.value = result.id
|
||||
selectedDocument.value = newDoc
|
||||
// 添加到文档列表
|
||||
knowledgeDocuments.value = [newDoc, ...knowledgeDocuments.value]
|
||||
} else {
|
||||
// 刷新文档列表
|
||||
await changeFileFilter(fileFilter.value)
|
||||
selectedDocument.value = uploadedDoc
|
||||
|
||||
// 使用代理接口加载PDF
|
||||
if (uploadedDoc.file_key) {
|
||||
previewUrl.value = await loadPdfWithProxy(uploadedDoc)
|
||||
} else if (uploadedDoc.file_url) {
|
||||
previewUrl.value = uploadedDoc.file_url
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(result.message || 'Failed to upload file')
|
||||
@@ -453,6 +497,7 @@ const deleteDocument = async (docId: string) => {
|
||||
selectedFile.value = null
|
||||
selectedDocument.value = null
|
||||
previewUrl.value = ''
|
||||
previewDownloadUrl.value = ''
|
||||
}
|
||||
// 刷新文档列表
|
||||
await changeFileFilter(fileFilter.value)
|
||||
@@ -739,6 +784,22 @@ const deleteDocument = async (docId: string) => {
|
||||
<el-option label="S3" value="s3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- MinIO 配置 -->
|
||||
<template v-if="storageConfig.type === 'minio'">
|
||||
<el-form-item label="Endpoint">
|
||||
<el-input v-model="storageConfig.endpoint" placeholder="http://localhost:9000" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Access Key ID">
|
||||
<el-input v-model="storageConfig.accessKeyId" placeholder="Enter Access Key ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Secret Access Key">
|
||||
<el-input v-model="storageConfig.secretAccessKey" type="password" placeholder="Enter Secret Access Key" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="Bucket">
|
||||
<el-input v-model="storageConfig.bucket" placeholder="Enter Bucket name" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -925,14 +986,21 @@ const deleteDocument = async (docId: string) => {
|
||||
<i class="fa-solid fa-spinner fa-spin"></i>
|
||||
<span>Loading preview...</span>
|
||||
</div>
|
||||
<!-- 有预览URL时显示PDF -->
|
||||
<embed
|
||||
<!-- 有blob预览URL时显示PDF (使用iframe) -->
|
||||
<iframe
|
||||
v-else-if="previewUrl"
|
||||
type="application/pdf"
|
||||
:src="previewUrl"
|
||||
class="pdf-embed"
|
||||
/>
|
||||
<!-- 无预览时显示提示 -->
|
||||
<!-- 无预览但有下载链接时显示下载按钮 -->
|
||||
<div v-else-if="previewDownloadUrl" class="preview-no-file">
|
||||
<i class="fa-solid fa-file-pdf"></i>
|
||||
<span>Cannot preview PDF directly</span>
|
||||
<a :href="previewDownloadUrl" target="_blank" class="download-link">
|
||||
<i class="fa-solid fa-download"></i> Download PDF
|
||||
</a>
|
||||
</div>
|
||||
<!-- 无预览也无下载链接时显示提示 -->
|
||||
<div v-else class="preview-no-file">
|
||||
<i class="fa-solid fa-file-pdf"></i>
|
||||
<span>Document preview not available</span>
|
||||
|
||||
@@ -77,6 +77,13 @@ export const createKnowledgeBase = async (params: {
|
||||
enable_pdf?: boolean
|
||||
pandoc?: boolean
|
||||
}
|
||||
storage_config?: {
|
||||
type: string
|
||||
endpoint?: string
|
||||
access_key_id?: string
|
||||
secret_access_key?: string
|
||||
bucket?: string
|
||||
}
|
||||
}): Promise<{ success: boolean; id?: string; message?: string }> => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/knowledge/create`, {
|
||||
@@ -198,3 +205,9 @@ export const getDocumentPreview = async (kbId: string, docId: string, page: numb
|
||||
return { success: false, message: 'Failed to get document preview' }
|
||||
}
|
||||
}
|
||||
|
||||
// 获取文件代理URL (用于预览PDF)
|
||||
export const getFileProxyUrl = (kbId: string, key: string): string => {
|
||||
const encodedKey = encodeURIComponent(key)
|
||||
return `${API_BASE}/api/file_proxy?kb_id=${kbId}&key=${encodedKey}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user