import React, { useState, useEffect } from 'react';
import { Database, ChevronLeft, ChevronRight, Plus, Calendar, Trash2, Edit, FileText, Image, X } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import apiClient from '../utils/apiClient';
import { buildApiUrl, API_ENDPOINTS } from '../config/api';
import ContentViewer from '../components/ContentViewer';
import TagDisplay from '../components/TagDisplay';
import Toast from '../components/Toast';
import ConfirmDialog from '../components/ConfirmDialog';
import FormModal from '../components/FormModal';
import StepIndicator from '../components/StepIndicator';
import SimpleSearchInput from '../components/SimpleSearchInput';
import Breadcrumb from '../components/Breadcrumb';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import rehypeSanitize from 'rehype-sanitize';
import exportService from '../services/exportService';
import tools from '../utils/tools';
import PageLoading from '../components/PageLoading';
import meetingCacheService from '../services/meetingCacheService';
import './KnowledgeBasePage.css';
const KnowledgeBasePage = ({ user }) => {
const navigate = useNavigate();
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 [generating, setGenerating] = useState(false);
const [taskId, setTaskId] = useState(null);
const [progress, setProgress] = useState(0);
const [deleteConfirmInfo, setDeleteConfirmInfo] = useState(null);
const [toasts, setToasts] = useState([]);
const [createStep, setCreateStep] = useState(1); // 1: 选择会议, 2: 输入提示词
const [meetingsPagination, setMeetingsPagination] = useState({ page: 1, total: 0, has_more: false });
const [loadingMeetings, setLoadingMeetings] = useState(false);
const [availablePrompts, setAvailablePrompts] = useState([]); // 可用的提示词模版列表
const [selectedPromptId, setSelectedPromptId] = useState(null); // 选中的提示词模版ID
// Toast helper functions
const showToast = (message, type = 'info') => {
const id = Date.now();
setToasts(prev => [...prev, { id, message, type }]);
};
const removeToast = (id) => {
setToasts(prev => prev.filter(toast => toast.id !== id));
};
useEffect(() => {
fetchAllKbs();
fetchAllTagsForFilter(); // 获取标签云数据
}, []);
// 当搜索或标签过滤变化时,重新加载第一页
useEffect(() => {
if (showCreateForm) {
fetchMeetings(1);
}
}, [searchQuery, selectedTags, showCreateForm]);
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);
setUserPrompt('');
setSelectedMeetings([]);
setShowCreateForm(false);
setCreateStep(1); // 重置步骤
setSearchQuery('');
setSelectedTags([]);
fetchAllKbs();
} else if (status === 'failed') {
clearInterval(interval);
setTaskId(null);
setGenerating(false);
setProgress(0);
showToast('知识库生成失败,请稍后重试', 'error');
}
})
.catch(error => {
console.error("Error fetching task status:", error);
clearInterval(interval);
setTaskId(null);
setGenerating(false);
setProgress(0);
});
}, 2000);
return () => clearInterval(interval);
}
}, [taskId]);
const fetchAllKbs = () => {
setLoading(true);
// 获取所有知识库(个人和共享)
apiClient.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.LIST))
.then(response => {
// 按创建时间倒序排序
const sortedKbs = response.data.kbs.sort((a, b) =>
new Date(b.created_at) - new Date(a.created_at)
);
setKbs(sortedKbs);
// 如果有知识库且没有选中,默认选中第一个
if (sortedKbs.length > 0 && !selectedKb) {
loadKbDetail(sortedKbs[0].kb_id);
}
setLoading(false);
})
.catch(error => {
console.error("Error fetching knowledge bases:", error);
setLoading(false);
});
};
const fetchMeetings = async (page = 1) => {
try {
// 生成当前过滤器的键(不包含filterType,因为知识库这里不需要)
const filterKey = meetingCacheService.generateFilterKey('all', searchQuery, selectedTags);
// 先检查缓存
const cachedPage = meetingCacheService.getPage(filterKey, page);
if (cachedPage) {
console.log('Using cached page:', page, 'for filter:', filterKey);
setMeetings(cachedPage.meetings);
setMeetingsPagination(cachedPage.pagination);
return;
}
// 没有缓存,从服务器获取
setLoadingMeetings(true);
const params = {
user_id: user.user_id,
page: page,
search: searchQuery || undefined,
tags: selectedTags.length > 0 ? selectedTags.join(',') : undefined
};
const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.MEETINGS.LIST), { params });
const newMeetings = response.data.meetings || [];
const newPagination = {
page: response.data.page,
total: response.data.total,
has_more: response.data.has_more
};
// 缓存当前页数据
meetingCacheService.setPage(filterKey, page, newMeetings, newPagination);
setMeetings(newMeetings);
setMeetingsPagination(newPagination);
} catch (error) {
console.error("Error fetching meetings:", error);
setMeetings([]);
} finally {
setLoadingMeetings(false);
}
};
// 获取所有标签用于过滤器
const fetchAllTagsForFilter = async () => {
try {
const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.TAGS.LIST));
const allTags = response.data || [];
// 取前6个热门标签
const topSixTags = allTags.slice(0, 6).map(tag => tag.name);
setAvailableTags(topSixTags);
} catch (error) {
console.error("Error fetching tags:", error);
}
};
// 获取知识库任务的启用提示词模版列表
const fetchAvailablePrompts = async () => {
try {
const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.PROMPTS.ACTIVE('KNOWLEDGE_TASK')));
const promptsList = response.data.prompts || [];
setAvailablePrompts(promptsList);
// 自动选中默认模版
const defaultPrompt = promptsList.find(p => p.is_default);
if (defaultPrompt) {
setSelectedPromptId(defaultPrompt.id);
}
} catch (error) {
console.error("Error fetching available prompts:", error);
setAvailablePrompts([]);
}
};
const loadKbDetail = async (kbId) => {
try {
const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.DETAIL(kbId)));
setSelectedKb(response.data);
} catch (error) {
console.error("Error loading knowledge base detail:", error);
}
};
const handleKbSelect = (kb) => {
loadKbDetail(kb.kb_id);
};
const handleGenerate = async () => {
if (!selectedMeetings || selectedMeetings.length === 0) {
showToast('请至少选择一个会议', 'warning');
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,
prompt_id: selectedPromptId // 传递选中的模版ID
});
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 handlePageChange = (newPage) => {
fetchMeetings(newPage);
};
const handleTagToggle = (tag) => {
setSelectedTags(prev =>
prev.includes(tag)
? prev.filter(t => t !== tag)
: [...prev, tag]
);
};
const clearFilters = () => {
setSearchQuery('');
setSelectedTags([]);
};
const handleOpenCreateModal = () => {
setCreateStep(1);
setSelectedMeetings([]);
setUserPrompt('');
setSearchQuery('');
setSelectedTags([]);
setSelectedPromptId(null);
setShowCreateForm(true);
// 获取可用的提示词模版
fetchAvailablePrompts();
};
const handleCloseCreateModal = () => {
setShowCreateForm(false);
setCreateStep(1);
setSelectedMeetings([]);
setUserPrompt('');
setSearchQuery('');
setSelectedTags([]);
};
const handleNextStep = () => {
if (selectedMeetings.length === 0) {
showToast('请至少选择一个会议', 'warning');
return;
}
setCreateStep(2);
};
const handlePrevStep = () => {
setCreateStep(1);
};
const handleDelete = async (kb) => {
setDeleteConfirmInfo({ kb_id: kb.kb_id, title: kb.title });
};
const confirmDelete = async () => {
try {
await apiClient.delete(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.DELETE(deleteConfirmInfo.kb_id)));
// 如果删除的是当前选中的,清除选中
if (selectedKb && selectedKb.kb_id === deleteConfirmInfo.kb_id) {
setSelectedKb(null);
}
setDeleteConfirmInfo(null);
fetchAllKbs();
} catch (error) {
console.error("Error deleting knowledge base:", error);
showToast('删除失败,请稍后重试', 'error');
setDeleteConfirmInfo(null);
}
};
const groupKbsByDate = (kbList) => {
const todayKbs = [];
const pastKbs = [];
kbList.forEach(kb => {
if (tools.isToday(kb.created_at)) {
todayKbs.push(kb);
} else {
pastKbs.push(kb);
}
});
return { todayKbs, pastKbs };
};
// 导出知识库内容为图片
const exportSummaryToImage = async () => {
try {
if (!selectedKb?.content) {
showToast('暂无知识库内容,请稍后再试。', 'warning');
return;
}
const createdAt = tools.formatDate(selectedKb.created_at);
const tags = selectedKb.tags?.join('、') || '';
await exportService.exportKnowledgeBaseToImage({
title: selectedKb.title || '知识库',
content: selectedKb.content,
metadata: {
creator: selectedKb.created_by_name || '未知',
createdTime: createdAt,
tags: tags,
sourceMeetings: selectedKb.source_meetings?.length || 0
}
});
showToast('内容已成功导出为图片', 'success');
} catch (error) {
console.error('图片导出失败:', error);
showToast('图片导出失败,请重试。', 'error');
}
};
// 导出思维导图为图片
const exportMindMapToImage = async () => {
try {
if (!selectedKb?.content) {
showToast('暂无内容,无法导出思维导图。', 'warning');
return;
}
await exportService.exportMindMapToImage({
title: selectedKb.title || '知识库'
});
showToast('思维导图已成功导出为图片', 'success');
} catch (error) {
console.error('思维导图导出失败:', error);
showToast(error.message || '思维导图导出失败,请重试。', 'error');
}
};
const isCreator = selectedKb && user && String(selectedKb.creator_id) === String(user.user_id);
if (loading) {
return
暂无知识库条目
请从左侧选择一个知识库查看详情
加载中...
未找到匹配的会议
您可以添加额外的要求来定制知识库生成内容,例如重点关注某个主题、提取特定信息等。如不填写,系统将使用默认提示词。