import React, { useCallback, useEffect, useState } 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 { Link, useNavigate } from 'react-router-dom'; import httpService from '../services/httpService'; import { buildApiUrl, API_ENDPOINTS } from '../config/api'; import ContentViewer from '../components/ContentViewer'; import ActionButton from '../components/ActionButton'; import tools from '../utils/tools'; 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 [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); const loadKbDetail = useCallback(async (id) => { try { const res = await httpService.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.DETAIL(id))); setSelectedKb(res.data); } catch { message.error('加载知识库详情失败'); } }, [message]); const fetchAllKbs = useCallback(async () => { try { const res = await httpService.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); } } catch { message.error('加载知识库列表失败'); } }, [loadKbDetail, message, selectedKb]); const fetchMeetings = useCallback(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 httpService.get(buildApiUrl(API_ENDPOINTS.MEETINGS.LIST), { params }); setMeetings(res.data.meetings || []); setMeetingsPagination({ page: res.data.page, total: res.data.total }); } catch { message.error('获取会议列表失败'); } finally { setLoadingMeetings(false); } }, [message, searchQuery, selectedTags, user.user_id]); const fetchAvailableTags = useCallback(async () => { try { const res = await httpService.get(buildApiUrl(API_ENDPOINTS.TAGS.LIST)); setAvailableTags(res.data?.slice(0, 10) || []); } catch { setAvailableTags([]); } }, []); const fetchPrompts = useCallback(async () => { try { const res = await httpService.get(buildApiUrl(API_ENDPOINTS.PROMPTS.ACTIVE('KNOWLEDGE_TASK'))); setAvailablePrompts(res.data.prompts || []); const def = res.data.prompts?.find((prompt) => prompt.is_default); if (def) setSelectedPromptId(def.id); } catch { setAvailablePrompts([]); setSelectedPromptId(null); } }, []); useEffect(() => { fetchAllKbs(); fetchAvailableTags(); }, [fetchAllKbs, fetchAvailableTags]); useEffect(() => { if (showCreateForm && createStep === 0) { fetchMeetings(meetingsPagination.page); } }, [createStep, fetchMeetings, meetingsPagination.page, selectedTags, searchQuery, showCreateForm]); const handleStartCreate = () => { setShowCreateForm(true); setCreateStep(0); setSelectedMeetings([]); fetchPrompts(); }; const handleGenerate = useCallback(async () => { setGenerating(true); setTaskProgress(10); try { const res = await httpService.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 httpService.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 { setGenerating(false); message.error('创建知识库任务失败'); } }, [fetchAllKbs, message, selectedMeetings, selectedPromptId, userPrompt]); const handleDelete = (kb) => { modal.confirm({ title: '删除知识库', content: `确定要删除 "${kb.title}" 吗?此操作无法撤销。`, okText: '删除', okType: 'danger', onOk: async () => { await httpService.delete(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.DELETE(kb.kb_id))); if (selectedKb?.kb_id === kb.kb_id) setSelectedKb(null); fetchAllKbs(); message.success('删除成功'); } }); }; return (