diff --git a/src/App.jsx b/src/App.jsx index 88ffb2c..ced3745 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,6 +8,7 @@ import MeetingDetails from './pages/MeetingDetails'; import CreateMeeting from './pages/CreateMeeting'; import EditMeeting from './pages/EditMeeting'; import AdminManagement from './pages/AdminManagement'; +import KnowledgeBasePage from './pages/KnowledgeBasePage'; import './App.css'; function App() { @@ -81,6 +82,9 @@ function App() { : } /> + : + } /> diff --git a/src/components/knowledgebase/PersonalKnowledgeBase.jsx b/src/components/knowledgebase/PersonalKnowledgeBase.jsx new file mode 100644 index 0000000..60b5568 --- /dev/null +++ b/src/components/knowledgebase/PersonalKnowledgeBase.jsx @@ -0,0 +1,369 @@ +import React, { useState, useEffect } from 'react'; +import apiClient from '../../utils/apiClient'; +import { buildApiUrl, API_ENDPOINTS } from '../../config/api'; +import { Plus, X, Eye, Trash2, Calendar, Database, Search } from 'lucide-react'; +import { Link } from 'react-router-dom'; + +const PersonalKnowledgeBase = ({ user }) => { + const [kbs, setKbs] = useState([]); + const [loading, setLoading] = useState(true); + const [showModal, setShowModal] = useState(false); + const [meetings, setMeetings] = useState([]); + const [selectedMeetings, setSelectedMeetings] = useState([]); + const [userPrompt, setUserPrompt] = useState(''); + const [searchQuery, setSearchQuery] = useState(''); + const [generating, setGenerating] = useState(false); + const [taskId, setTaskId] = useState(null); + const [progress, setProgress] = useState(0); + const [selectedKb, setSelectedKb] = useState(null); + const [showDetailModal, setShowDetailModal] = useState(false); + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); + const [deletingKb, setDeletingKb] = useState(null); + + useEffect(() => { + fetchPersonalKbs(); + fetchMeetings(); + }, []); + + useEffect(() => { + if (taskId) { + const interval = setInterval(() => { + apiClient.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.TASK_STATUS(taskId))) + .then(response => { + const { status, progress } = response.data; + setProgress(progress || 0); + if (status === 'completed') { + clearInterval(interval); + setTaskId(null); + setGenerating(false); + setProgress(0); + // Reset form + setUserPrompt(''); + setSelectedMeetings([]); + setShowModal(false); + // Refresh the list + fetchPersonalKbs(); + } else if (status === 'failed') { + clearInterval(interval); + setTaskId(null); + setGenerating(false); + setProgress(0); + alert('知识库生成失败,请稍后重试'); + } + }) + .catch(error => { + console.error("Error fetching task status:", error); + clearInterval(interval); + setTaskId(null); + setGenerating(false); + setProgress(0); + }); + }, 2000); // 缩短轮询间隔到2秒 + return () => clearInterval(interval); + } + }, [taskId]); + + const fetchPersonalKbs = () => { + setLoading(true); + apiClient.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.LIST, { is_shared: false })) + .then(response => { + setKbs(response.data.kbs); + setLoading(false); + }) + .catch(error => { + console.error("Error fetching personal knowledge bases:", error); + setLoading(false); + }); + }; + + const fetchTags = () => { + // Tags functionality removed - replaced with search + }; + + const fetchMeetings = () => { + apiClient.get(buildApiUrl(API_ENDPOINTS.MEETINGS.LIST, { user_id: user.user_id })) + .then(response => { + setMeetings(response.data); + }) + .catch(error => { + console.error("Error fetching meetings:", error); + }); + }; + + const handleGenerate = async () => { + if (!selectedMeetings || selectedMeetings.length === 0) { + alert('请至少选择一个会议'); + return; + } + + setGenerating(true); + try { + const response = await apiClient.post(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.CREATE), { + user_prompt: userPrompt, + source_meeting_ids: selectedMeetings.join(','), + is_shared: false + }); + setTaskId(response.data.task_id); + } catch (error) { + console.error("Error creating knowledge base:", error); + setGenerating(false); + } + }; + + const toggleMeetingSelection = (meetingId) => { + setSelectedMeetings(prev => + prev.includes(meetingId) + ? prev.filter(id => id !== meetingId) + : [...prev, meetingId] + ); + }; + + const filteredMeetings = (meetings || []).filter(meeting => { + if (!searchQuery) return true; + return meeting.title.toLowerCase().includes(searchQuery.toLowerCase()); + }); + + const handleDelete = async (kbId) => { + setDeletingKb(kbs.find(kb => kb.kb_id === kbId)); + setShowDeleteConfirm(true); + }; + + const confirmDelete = async () => { + try { + await apiClient.delete(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.DELETE(deletingKb.kb_id))); + setShowDeleteConfirm(false); + setDeletingKb(null); + fetchPersonalKbs(); // Refresh the list + } catch (error) { + console.error("Error deleting knowledge base:", error); + alert('删除失败,请稍后重试'); + setShowDeleteConfirm(false); + setDeletingKb(null); + } + }; + + const handleViewDetail = async (kbId) => { + try { + const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.DETAIL(kbId))); + setSelectedKb(response.data); + setShowDetailModal(true); + } catch (error) { + console.error("Error fetching knowledge base detail:", error); + alert('获取详情失败,请稍后重试'); + } + }; + + const formatDate = (dateString) => { + const date = new Date(dateString); + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit' + }); + }; + + if (loading) { + return
Loading...
; + } + + return ( +
+
+ {kbs.length === 0 ? ( +
+

暂无知识库条目

+
+ ) : ( + kbs.map(kb => ( +
+
+

{kb.title}

+
+ + +
+
+
+
+ + + {formatDate(kb.created_at)} + + + + {kb.source_meeting_count || 0} 个数据源 + +
+ {kb.user_prompt && ( +
+ 提示词: {kb.user_prompt} +
+ )} +
+ {kb.content ? kb.content.substring(0, 200) + '...' : '内容生成中...'} +
+
+
+ )) + )} +
+ +
+
+
+
+ +