import React, { useState, useEffect } from 'react'; import { Layout, Card, Row, Col, Button, Space, Typography, Tag, Tooltip, Modal, Progress, Spin, App, Descriptions, Avatar, Divider, List, Input, Switch, FloatButton, Tabs, Steps, Checkbox, Pagination, Empty } from 'antd'; import { DatabaseOutlined, PlusOutlined, DeleteOutlined, EditOutlined, FileTextOutlined, PictureOutlined, SearchOutlined, ArrowLeftOutlined, ArrowRightOutlined, DatabaseFilled, ClockCircleOutlined, UserOutlined, CalendarOutlined, CheckCircleOutlined, InfoCircleOutlined, ThunderboltOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; import apiClient from '../utils/apiClient'; import { buildApiUrl, API_ENDPOINTS } from '../config/api'; import ContentViewer from '../components/ContentViewer'; import ActionButton from '../components/ActionButton'; import exportService from '../services/exportService'; import tools from '../utils/tools'; import meetingCacheService from '../services/meetingCacheService'; const { Title, Text, Paragraph } = Typography; const { Sider, Content } = Layout; const { TextArea } = Input; const KnowledgeBasePage = ({ user }) => { const navigate = useNavigate(); const { message, modal } = App.useApp(); const [kbs, setKbs] = useState([]); const [loading, setLoading] = useState(true); const [selectedKb, setSelectedKb] = useState(null); const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const [showCreateForm, setShowCreateForm] = useState(false); // 创建流程状态 const [meetings, setMeetings] = useState([]); const [selectedMeetings, setSelectedMeetings] = useState([]); const [userPrompt, setUserPrompt] = useState(''); const [searchQuery, setSearchQuery] = useState(''); const [selectedTags, setSelectedTags] = useState([]); const [availableTags, setAvailableTags] = useState([]); const [creating, setGenerating] = useState(false); const [createStep, setCreateStep] = useState(0); const [meetingsPagination, setMeetingsPagination] = useState({ page: 1, total: 0 }); const [loadingMeetings, setLoadingMeetings] = useState(false); const [availablePrompts, setAvailablePrompts] = useState([]); const [selectedPromptId, setSelectedPromptId] = useState(null); const [taskProgress, setTaskProgress] = useState(0); useEffect(() => { fetchAllKbs(); fetchAvailableTags(); }, []); useEffect(() => { if (showCreateForm && createStep === 0) { fetchMeetings(meetingsPagination.page); } }, [searchQuery, selectedTags, showCreateForm, createStep, meetingsPagination.page]); const fetchAllKbs = async () => { setLoading(true); try { const res = await apiClient.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.LIST)); const sorted = res.data.kbs.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); setKbs(sorted); if (sorted.length > 0 && !selectedKb) { loadKbDetail(sorted[0].kb_id); } } finally { setLoading(false); } }; const loadKbDetail = async (id) => { try { const res = await apiClient.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.DETAIL(id))); setSelectedKb(res.data); } catch (e) {} }; const fetchMeetings = async (page = 1) => { setLoadingMeetings(true); try { const params = { user_id: user.user_id, page, search: searchQuery || undefined, tags: selectedTags.length > 0 ? selectedTags.join(',') : undefined }; const res = await apiClient.get(buildApiUrl(API_ENDPOINTS.MEETINGS.LIST), { params }); setMeetings(res.data.meetings || []); setMeetingsPagination({ page: res.data.page, total: res.data.total }); } finally { setLoadingMeetings(false); } }; const fetchAvailableTags = async () => { try { const res = await apiClient.get(buildApiUrl(API_ENDPOINTS.TAGS.LIST)); setAvailableTags(res.data?.slice(0, 10) || []); } catch (e) {} }; const fetchPrompts = async () => { try { const res = await apiClient.get(buildApiUrl(API_ENDPOINTS.PROMPTS.ACTIVE('KNOWLEDGE_TASK'))); setAvailablePrompts(res.data.prompts || []); const def = res.data.prompts?.find(p => p.is_default) || res.data.prompts?.[0]; if (def) setSelectedPromptId(def.id); } catch (e) {} }; const handleStartCreate = () => { setShowCreateForm(true); setCreateStep(0); setSelectedMeetings([]); fetchPrompts(); }; const handleGenerate = async () => { setGenerating(true); setTaskProgress(10); try { const res = await apiClient.post(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.CREATE), { user_prompt: userPrompt, source_meeting_ids: selectedMeetings.join(','), prompt_id: selectedPromptId }); const taskId = res.data.task_id; const interval = setInterval(async () => { const statusRes = await apiClient.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.TASK_STATUS(taskId))); const s = statusRes.data; setTaskProgress(s.progress || 20); if (s.status === 'completed') { clearInterval(interval); setGenerating(false); setShowCreateForm(false); message.success('知识库生成成功'); fetchAllKbs(); } else if (s.status === 'failed') { clearInterval(interval); setGenerating(false); message.error('生成失败'); } }, 3000); } catch (e) { setGenerating(false); } }; const handleDelete = (kb) => { modal.confirm({ title: '删除知识库', content: `确定要删除 "${kb.title}" 吗?此操作无法撤销。`, okText: '删除', okType: 'danger', onOk: async () => { await apiClient.delete(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.DELETE(kb.kb_id))); if (selectedKb?.kb_id === kb.kb_id) setSelectedKb(null); fetchAllKbs(); message.success('删除成功'); } }); }; return (