From e47235dfff8374bccbb874fe86b86856eac91c56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E7=BB=AB?= <486494914@qq.com> Date: Tue, 1 Sep 2026 21:14:55 +0800 Subject: [PATCH] 6.1 --- backend/app/api/documents.py | 3 +- backend/app/services/doc_service.py | 4 +- frontend/src/views/KbDetail.vue | 222 +++-- frontend/src/views/KbDetail.vue.js | 1161 +++++++++++++++------------ 4 files changed, 791 insertions(+), 599 deletions(-) diff --git a/backend/app/api/documents.py b/backend/app/api/documents.py index dc7e4fc..38b2daf 100644 --- a/backend/app/api/documents.py +++ b/backend/app/api/documents.py @@ -74,13 +74,14 @@ def create_text_document( @router.get("", response_model=DocumentListResponse) def list_documents( kb_id: str = Query(...), + category_id: str = Query(None), page: int = Query(1, ge=1), page_size: int = Query(50, ge=1, le=200), user: User = Depends(get_current_user), db: Session = Depends(get_db), ) -> DocumentListResponse: svc = DocumentService(db) - items, total = svc.list_by_knowledge_base(kb_id, user, page=page, page_size=page_size) + items, total = svc.list_by_knowledge_base(kb_id, user, category_id=category_id, page=page, page_size=page_size) return DocumentListResponse( items=[_to_response(doc) for doc in items], total=total, diff --git a/backend/app/services/doc_service.py b/backend/app/services/doc_service.py index 73b192c..444fb46 100644 --- a/backend/app/services/doc_service.py +++ b/backend/app/services/doc_service.py @@ -144,13 +144,13 @@ class DocumentService: return doc def list_by_knowledge_base( - self, kb_id: str, user: User, *, page: int = 1, page_size: int = 50 + self, kb_id: str, user: User, *, category_id: str | None = None, page: int = 1, page_size: int = 50 ): # 校验 KB 归属 kb = self._kb_repo.get_by_id(kb_id) if kb is None or kb.user_id != user.id or kb.status == "DELETED": raise NotFoundError("知识库不存在。") - return self._doc_repo.list_by_knowledge_base(kb_id, page=page, page_size=page_size) + return self._doc_repo.list_by_knowledge_base(kb_id, category_id=category_id, page=page, page_size=page_size) def delete(self, doc_id: str, user: User) -> None: doc = self.get_or_404(doc_id, user) diff --git a/frontend/src/views/KbDetail.vue b/frontend/src/views/KbDetail.vue index 3841ec4..04b327d 100644 --- a/frontend/src/views/KbDetail.vue +++ b/frontend/src/views/KbDetail.vue @@ -10,6 +10,7 @@ const kbId = route.params.id as string const kb = ref(null) const docs = ref([]) const categories = ref([]) +const allCategoriesFlat = ref([]) const loading = ref(false) const uploading = ref(false) const aiUrl = ref('') @@ -32,6 +33,9 @@ const catForm = ref({ name: '', parent_id: null as string | null, is_folder: tru const catLoading = ref(false) const editingCatId = ref(null) +// 编辑文档分类 +const editingDocId = ref(null) + onMounted(async () => { await loadKb() await loadCategories() @@ -50,6 +54,9 @@ async function loadCategories() { try { const { data } = await apiClient.get(`/knowledge-bases/${kbId}/categories/tree`) categories.value = data + // 同时加载平铺列表 + const { data: flat } = await apiClient.get(`/knowledge-bases/${kbId}/categories`) + allCategoriesFlat.value = flat } catch { /* ignore */ } } @@ -129,6 +136,16 @@ async function handleCreateText() { textLoading.value = false } +// 修改文档分类 +async function handleChangeDocCategory(doc: any, newCategoryId: string) { + try { + await apiClient.put(`/documents/${doc.id}`, { category_id: newCategoryId }) + ElMessage.success('目录已更新。') + loadDocs() + loadCategories() + } catch { /* ignore */ } +} + async function handleDeleteDoc(doc: any) { try { await ElMessageBox.confirm(`确定删除「${doc.original_filename}」?`, '确认删除', { type: 'warning' }) @@ -154,6 +171,16 @@ async function handleCopyLink() { } catch { /* ignore */ } } +// 重新生成 AI 链接 +async function handleRegenerateLink() { + 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 openAddCategory(parentId: string | null = null) { editingCatId.value = null @@ -196,15 +223,7 @@ async function handleDeleteCategory(cat: any) { clearCategoryFilter() } loadCategories() - } 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('链接已重新生成。') + loadDocs() } catch { /* ignore */ } } @@ -220,117 +239,147 @@ function statusType(status: string) { return 'warning' } -// 递归获取所有叶子节点(用于文本对话框的分类选择器) -function flattenCategories(nodes: any[], level = 0): any[] { - const result: any[] = [] - for (const node of nodes) { - result.push({ ...node, level }) - if (node.children?.length) { - result.push(...flattenCategories(node.children, level + 1)) - } - } - return result +// 获取分类名称 +function getCategoryName(catId: string) { + const cat = allCategoriesFlat.value.find((c: any) => c.id === catId) + return cat ? cat.name : '未分类' } - -const flatCategories = computed(() => flattenCategories(categories.value))