This commit is contained in:
amb
2026-09-01 13:57:34 +08:00
parent dfd38c99a0
commit 4db2d1a411
23 changed files with 2496 additions and 140 deletions
+159 -5
View File
@@ -1,10 +1,164 @@
<script setup lang="ts">
// Phase 4 实现知识库详情
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import apiClient from '@/api/client'
const route = useRoute()
const kbId = route.params.id as string
const kb = ref<any>(null)
const docs = ref<any[]>([])
const loading = ref(false)
const uploading = ref(false)
const aiUrl = ref('')
onMounted(async () => {
await loadKb()
await loadDocs()
await loadLink()
})
async function loadKb() {
try {
const { data } = await apiClient.get(`/knowledge-bases/${kbId}`)
kb.value = data
} catch { /* ignore */ }
}
async function loadDocs() {
loading.value = true
try {
const { data } = await apiClient.get(`/documents?kb_id=${kbId}`)
docs.value = data.items
} catch { /* ignore */ }
loading.value = false
}
async function loadLink() {
try {
const { data } = await apiClient.get(`/knowledge-bases/${kbId}/link`)
aiUrl.value = `${window.location.origin}/k/${data.token}`
} catch { /* ignore */ }
}
async function handleUpload(options: any) {
uploading.value = true
try {
const formData = new FormData()
formData.append('kb_id', kbId)
formData.append('file', options.file)
await apiClient.post('/documents/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
})
ElMessage.success('文档上传成功!')
loadDocs()
} catch { /* ignore */ }
uploading.value = false
}
async function handleDeleteDoc(doc: any) {
try {
await ElMessageBox.confirm(`确定删除文档「${doc.original_filename}」?`, '确认删除', { type: 'warning' })
await apiClient.delete(`/documents/${doc.id}`)
ElMessage.success('已删除。')
loadDocs()
} catch { /* ignore */ }
}
async function handleReprocess(doc: any) {
try {
await apiClient.post(`/documents/${doc.id}/reprocess`)
ElMessage.success('重新解析已提交。')
loadDocs()
} catch { /* ignore */ }
}
async function handleCopyLink() {
try {
await navigator.clipboard.writeText(aiUrl.value)
ElMessage.success('AI 链接已复制!')
} catch { /* ignore */ }
}
async function handleRegenerate() {
try {
await ElMessageBox.confirm('重新生成链接后,旧链接将立即失效。确定继续?', '确认', { type: 'warning' })
const { data } = await apiClient.post(`/knowledge-bases/${kbId}/regenerate-token`)
aiUrl.value = `${window.location.origin}/k/${data.token}`
ElMessage.success('链接已重新生成。')
} catch { /* ignore */ }
}
function formatSize(bytes: number) {
if (bytes < 1024) return bytes + ' B'
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
return (bytes / (1024 * 1024)).toFixed(1) + ' MB'
}
function statusType(status: string) {
if (status === 'READY') return 'success'
if (status === 'FAILED') return 'danger'
return 'warning'
}
</script>
<template>
<div>
<h1>知识库详情</h1>
<p>知识库详情Phase 4 实现</p>
<div v-if="kb">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px">
<h1 style="margin: 0">{{ kb.name }}</h1>
<el-button type="primary" @click="handleCopyLink">复制 AI 链接</el-button>
</div>
<el-card style="margin-bottom: 20px">
<el-descriptions :column="2" border>
<el-descriptions-item label="描述">{{ kb.description || '无' }}</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="kb.enabled ? 'success' : 'danger'">{{ kb.enabled ? '启用' : '禁用' }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="AI 链接">
<div style="display: flex; align-items: center; gap: 8px">
<el-input :model-value="aiUrl" readonly size="small" style="flex: 1" />
<el-button size="small" @click="handleRegenerate">重新生成</el-button>
</div>
</el-descriptions-item>
<el-descriptions-item label="文档数">{{ docs.length }}</el-descriptions-item>
</el-descriptions>
</el-card>
<el-card>
<template #header>
<div style="display: flex; justify-content: space-between; align-items: center">
<span>文档列表</span>
<el-upload
:show-file-list="false"
:http-request="handleUpload"
accept=".docx,.pdf"
>
<el-button type="primary" :loading="uploading">上传文档</el-button>
</el-upload>
</div>
</template>
<el-table :data="docs" v-loading="loading" style="width: 100%">
<el-table-column prop="original_filename" label="文件名" min-width="200" show-overflow-tooltip />
<el-table-column label="状态" width="120" align="center">
<template #default="{ row }">
<el-tag :type="statusType(row.status)" size="small">{{ row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column label="大小" width="100" align="center">
<template #default="{ row }">{{ formatSize(row.file_size) }}</template>
</el-table-column>
<el-table-column prop="title" label="标题" min-width="150" show-overflow-tooltip />
<el-table-column prop="keywords" label="关键词" min-width="150" show-overflow-tooltip />
<el-table-column label="操作" width="200" align="center">
<template #default="{ row }">
<el-button size="small" @click="handleReprocess(row)">重新解析</el-button>
<el-button size="small" type="danger" @click="handleDeleteDoc(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
</template>