From 186ae5530246470181fa5d6ab2a5c4d43b396b57 Mon Sep 17 00:00:00 2001 From: AI Clinical Dev Team Date: Sat, 11 Oct 2025 12:48:26 +0800 Subject: [PATCH] feat(frontend): Day 21 knowledge base management frontend completed --- frontend/src/api/knowledgeBaseApi.ts | 185 +++++++ .../components/knowledge/CreateKBDialog.tsx | 115 +++++ .../src/components/knowledge/DocumentList.tsx | 211 ++++++++ .../components/knowledge/DocumentUpload.tsx | 144 ++++++ .../src/components/knowledge/EditKBDialog.tsx | 111 ++++ .../knowledge/KnowledgeBaseList.tsx | 203 ++++++++ frontend/src/pages/AgentChatPage.tsx | 10 +- frontend/src/pages/KnowledgePage.tsx | 484 ++++++++++-------- frontend/src/stores/useKnowledgeBaseStore.ts | 217 ++++++++ 9 files changed, 1459 insertions(+), 221 deletions(-) create mode 100644 frontend/src/api/knowledgeBaseApi.ts create mode 100644 frontend/src/components/knowledge/CreateKBDialog.tsx create mode 100644 frontend/src/components/knowledge/DocumentList.tsx create mode 100644 frontend/src/components/knowledge/DocumentUpload.tsx create mode 100644 frontend/src/components/knowledge/EditKBDialog.tsx create mode 100644 frontend/src/components/knowledge/KnowledgeBaseList.tsx create mode 100644 frontend/src/stores/useKnowledgeBaseStore.ts diff --git a/frontend/src/api/knowledgeBaseApi.ts b/frontend/src/api/knowledgeBaseApi.ts new file mode 100644 index 00000000..70808ac9 --- /dev/null +++ b/frontend/src/api/knowledgeBaseApi.ts @@ -0,0 +1,185 @@ +import axios from 'axios'; + +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3001'; + +const api = axios.create({ + baseURL: `${API_BASE_URL}/api/v1`, + timeout: 30000, + headers: { + 'Content-Type': 'application/json', + }, +}); + +/** + * 知识库类型定义 + */ +export interface KnowledgeBase { + id: string; + userId: string; + name: string; + description?: string; + difyDatasetId: string; + fileCount: number; + totalSizeBytes: number; + createdAt: string; + updatedAt: string; + _count?: { + documents: number; + }; +} + +export interface Document { + id: string; + kbId: string; + userId: string; + filename: string; + fileType: string; + fileSizeBytes: number; + fileUrl: string; + difyDocumentId: string; + status: 'uploading' | 'parsing' | 'indexing' | 'completed' | 'error'; + progress: number; + errorMessage?: string; + segmentsCount?: number; + tokensCount?: number; + uploadedAt: string; + processedAt?: string; +} + +export interface CreateKnowledgeBaseRequest { + name: string; + description?: string; +} + +export interface UpdateKnowledgeBaseRequest { + name?: string; + description?: string; +} + +export interface KnowledgeBaseStats { + totalDocuments: number; + completedDocuments: number; + processingDocuments: number; + errorDocuments: number; + totalSizeBytes: number; + totalTokens: number; +} + +/** + * 知识库管理API + */ +export const knowledgeBaseApi = { + /** + * 获取知识库列表 + */ + async getList(): Promise { + const response = await api.get('/knowledge-bases'); + return response.data.data; + }, + + /** + * 获取知识库详情 + */ + async getById(id: string): Promise { + const response = await api.get(`/knowledge-bases/${id}`); + return response.data.data; + }, + + /** + * 创建知识库 + */ + async create(data: CreateKnowledgeBaseRequest): Promise { + const response = await api.post('/knowledge-bases', data); + return response.data.data; + }, + + /** + * 更新知识库 + */ + async update(id: string, data: UpdateKnowledgeBaseRequest): Promise { + const response = await api.put(`/knowledge-bases/${id}`, data); + return response.data.data; + }, + + /** + * 删除知识库 + */ + async delete(id: string): Promise { + await api.delete(`/knowledge-bases/${id}`); + }, + + /** + * 获取知识库统计信息 + */ + async getStats(id: string): Promise { + const response = await api.get(`/knowledge-bases/${id}/stats`); + return response.data.data; + }, + + /** + * 检索知识库 + */ + async search(id: string, query: string, topK: number = 3): Promise { + const response = await api.get(`/knowledge-bases/${id}/search`, { + params: { query, top_k: topK }, + }); + return response.data.data; + }, +}; + +/** + * 文档管理API + */ +export const documentApi = { + /** + * 获取文档列表 + */ + async getList(kbId: string): Promise { + const response = await api.get(`/knowledge-bases/${kbId}/documents`); + return response.data.data; + }, + + /** + * 获取文档详情 + */ + async getById(id: string): Promise { + const response = await api.get(`/documents/${id}`); + return response.data.data; + }, + + /** + * 上传文档 + */ + async upload(kbId: string, file: File, onProgress?: (progress: number) => void): Promise { + const formData = new FormData(); + formData.append('file', file); + + const response = await api.post(`/knowledge-bases/${kbId}/documents`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + onUploadProgress: (progressEvent) => { + if (progressEvent.total && onProgress) { + const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); + onProgress(percentCompleted); + } + }, + }); + return response.data.data; + }, + + /** + * 删除文档 + */ + async delete(id: string): Promise { + await api.delete(`/documents/${id}`); + }, + + /** + * 重新处理文档 + */ + async reprocess(id: string): Promise { + await api.post(`/documents/${id}/reprocess`); + }, +}; + diff --git a/frontend/src/components/knowledge/CreateKBDialog.tsx b/frontend/src/components/knowledge/CreateKBDialog.tsx new file mode 100644 index 00000000..a181aa04 --- /dev/null +++ b/frontend/src/components/knowledge/CreateKBDialog.tsx @@ -0,0 +1,115 @@ +import React, { useState } from 'react'; +import { Modal, Form, Input, message } from 'antd'; + +const { TextArea } = Input; + +interface CreateKBDialogProps { + open: boolean; + onCancel: () => void; + onCreate: (name: string, description?: string) => Promise; +} + +const CreateKBDialog: React.FC = ({ + open, + onCancel, + onCreate, +}) => { + const [form] = Form.useForm(); + const [loading, setLoading] = useState(false); + + const handleOk = async () => { + try { + const values = await form.validateFields(); + setLoading(true); + + await onCreate(values.name, values.description); + + message.success('知识库创建成功!'); + form.resetFields(); + onCancel(); + } catch (error: any) { + if (error.errorFields) { + // 表单验证错误 + return; + } + message.error(error.message || '创建失败'); + } finally { + setLoading(false); + } + }; + + const handleCancel = () => { + form.resetFields(); + onCancel(); + }; + + return ( + +
+ + + + + +