import React, { useState, useEffect, useRef } from 'react'; import { LogOut, User, Calendar, Users, TrendingUp, Clock, MessageSquare, Plus, ChevronDown, KeyRound, Shield, Filter, X, Library, BookText, Waves } from 'lucide-react'; import apiClient from '../utils/apiClient'; import { Link } from 'react-router-dom'; import { buildApiUrl, API_ENDPOINTS } from '../config/api'; import MeetingTimeline from '../components/MeetingTimeline'; import TagCloud from '../components/TagCloud'; import VoiceprintCollectionModal from '../components/VoiceprintCollectionModal'; import ConfirmDialog from '../components/ConfirmDialog'; import PageLoading from '../components/PageLoading'; import './Dashboard.css'; const Dashboard = ({ user, onLogout }) => { const [userInfo, setUserInfo] = useState(null); const [meetings, setMeetings] = useState(null); const [filteredMeetings, setFilteredMeetings] = useState([]); const [selectedTags, setSelectedTags] = useState([]); const [filterType, setFilterType] = useState('all'); // 'all', 'created', 'attended' const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [dropdownOpen, setDropdownOpen] = useState(false); const [showChangePasswordModal, setShowChangePasswordModal] = useState(false); const [oldPassword, setOldPassword] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [passwordChangeError, setPasswordChangeError] = useState(''); const [passwordChangeSuccess, setPasswordChangeSuccess] = useState(''); const dropdownRef = useRef(null); // 声纹相关状态 const [voiceprintStatus, setVoiceprintStatus] = useState(null); const [showVoiceprintModal, setShowVoiceprintModal] = useState(false); const [voiceprintTemplate, setVoiceprintTemplate] = useState(null); const [voiceprintLoading, setVoiceprintLoading] = useState(true); const [showDeleteVoiceprintDialog, setShowDeleteVoiceprintDialog] = useState(false); useEffect(() => { fetchUserData(); fetchVoiceprintData(); }, [user.user_id]); const fetchVoiceprintData = async () => { try { setVoiceprintLoading(true); // 获取声纹状态 const statusResponse = await apiClient.get(buildApiUrl(API_ENDPOINTS.VOICEPRINT.STATUS(user.user_id))); console.log('声纹状态响应:', statusResponse); setVoiceprintStatus(statusResponse.data); // 获取朗读模板 const templateResponse = await apiClient.get(buildApiUrl(API_ENDPOINTS.VOICEPRINT.TEMPLATE)); console.log('朗读模板响应:', templateResponse); setVoiceprintTemplate(templateResponse.data); } catch (err) { console.error('获取声纹数据失败:', err); } finally { setVoiceprintLoading(false); } }; // 过滤会议 useEffect(() => { filterMeetings(); }, [meetings, selectedTags, filterType]); const filterMeetings = () => { if (!meetings) return; let filtered = [...meetings]; // 根据创建/参与类型过滤 if (filterType === 'created') { filtered = filtered.filter(meeting => String(meeting.creator_id) === String(user.user_id)); } else if (filterType === 'attended') { filtered = filtered.filter(meeting => String(meeting.creator_id) !== String(user.user_id)); } // 根据选中的标签过滤 if (selectedTags.length > 0) { filtered = filtered.filter(meeting => { if (!meeting.tags || meeting.tags.length === 0) return false; const meetingTags = meeting.tags.map(tag => typeof tag === 'string' ? tag : tag.name); return selectedTags.some(selectedTag => meetingTags.includes(selectedTag)); }); } setFilteredMeetings(filtered); }; const handleTagClick = (tagName) => { setSelectedTags(prev => { if (prev.includes(tagName)) { return prev.filter(tag => tag !== tagName); } else { return [...prev, tagName]; } }); }; const handleFilterTypeChange = (type) => { setFilterType(type); }; const clearFilters = () => { setSelectedTags([]); setFilterType('all'); }; useEffect(() => { const handleClickOutside = (event) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { setDropdownOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [dropdownRef]); const fetchUserData = async () => { try { setLoading(true); console.log('Fetching user data for user_id:', user.user_id); const userResponse = await apiClient.get(buildApiUrl(API_ENDPOINTS.USERS.DETAIL(user.user_id))); console.log('User response:', userResponse.data); setUserInfo(userResponse.data); const meetingsResponse = await apiClient.get(buildApiUrl(`${API_ENDPOINTS.MEETINGS.LIST}?user_id=${user.user_id}`)); setMeetings(meetingsResponse.data); } catch (err) { console.error('Error fetching data:', err); setError('获取数据失败,请刷新重试'); } finally { setLoading(false); } }; const handleDeleteMeeting = async (meetingId) => { try { await apiClient.delete(buildApiUrl(API_ENDPOINTS.MEETINGS.DELETE(meetingId))); // Refresh meetings list const meetingsResponse = await apiClient.get(buildApiUrl(`${API_ENDPOINTS.MEETINGS.LIST}?user_id=${user.user_id}`)); setMeetings(meetingsResponse.data); } catch (err) { console.error('Error deleting meeting:', err); // You might want to show an error message to the user here } }; const handlePasswordChange = async (e) => { e.preventDefault(); if (newPassword !== confirmPassword) { setPasswordChangeError('新密码不匹配'); return; } if (newPassword.length < 6) { setPasswordChangeError('新密码长度不能少于6位'); return; } setPasswordChangeError(''); setPasswordChangeSuccess(''); try { await apiClient.put(buildApiUrl(API_ENDPOINTS.USERS.UPDATE_PASSWORD(user.user_id)), { old_password: oldPassword, new_password: newPassword, }); setPasswordChangeSuccess('密码修改成功!'); // 清空输入框并准备关闭模态框 setOldPassword(''); setNewPassword(''); setConfirmPassword(''); setTimeout(() => { setShowChangePasswordModal(false); setPasswordChangeSuccess(''); }, 2000); } catch (err) { setPasswordChangeError(err.response?.data?.message || '密码修改失败'); } }; const handleVoiceprintUpload = async (formData) => { try { await apiClient.post( buildApiUrl(API_ENDPOINTS.VOICEPRINT.UPLOAD(user.user_id)), formData, { headers: { 'Content-Type': 'multipart/form-data', }, } ); // 上传成功后刷新声纹状态并关闭模态框 await fetchVoiceprintData(); setShowVoiceprintModal(false); } catch (err) { throw new Error(err.response?.data?.message || '声纹上传失败'); } }; const handleDeleteVoiceprint = async () => { try { await apiClient.delete(buildApiUrl(API_ENDPOINTS.VOICEPRINT.DELETE(user.user_id))); await fetchVoiceprintData(); } catch (err) { console.error('删除声纹失败:', err); } }; const groupMeetingsByDate = (meetingsToGroup) => { return meetingsToGroup.reduce((acc, meeting) => { const date = new Date(meeting.meeting_time || meeting.created_at).toISOString().split('T')[0]; if (!acc[date]) { acc[date] = []; } acc[date].push(meeting); return acc; }, {}); }; const formatDate = (dateString) => { const date = new Date(dateString); return date.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }); }; if (loading || !meetings) { return ; } if (error) { return (

{error}

); } const groupedMeetings = groupMeetingsByDate(filteredMeetings); // 计算统计数据 const createdMeetings = meetings.filter(m => String(m.creator_id) === String(user.user_id)); const attendedMeetings = meetings.filter(m => String(m.creator_id) !== String(user.user_id)); return (
{/* Header */}
iMeeting
setDropdownOpen(!dropdownOpen)}> 欢迎,{userInfo?.caption}
{dropdownOpen && (
setDropdownOpen(false)}> 提示词仓库 {user.role_id === 1 && ( setDropdownOpen(false)}> 平台管理 )}
)}
{/* 用户信息、统计和标签云一行布局 */}
{/* 左侧列:用户卡片和知识库入口 */}

{userInfo?.caption}

{/* 声纹采集按钮 - 放在姓名后 */} {!voiceprintLoading && ( <> {voiceprintStatus?.has_voiceprint ? ( setShowDeleteVoiceprintDialog(true)} title="点击删除声纹" > 声纹 ) : ( )} )}

{userInfo?.email}

加入时间:{formatDate(userInfo?.created_at)}

{/* 知识库入口卡片 */}

知识库

贯穿内容,生成知识库

{/* 统一的统计卡片 */}

会议统计

handleFilterTypeChange('created')} >
我创建的会议 {createdMeetings.length}
handleFilterTypeChange('attended')} >
我参加的会议 {attendedMeetings.length}
handleFilterTypeChange('all')} >
全部会议 {meetings.length}
{/* 标签云卡片 */}
{/* Meetings Timeline Section */}

会议时间轴

按时间顺序展示您参与的所有会议

新建会议纪要
{showChangePasswordModal && (

修改密码

{passwordChangeError &&

{passwordChangeError}

} {passwordChangeSuccess &&

{passwordChangeSuccess}

}
setOldPassword(e.target.value)} required />
setNewPassword(e.target.value)} required />
setConfirmPassword(e.target.value)} required />
)} {/* 声纹采集模态框 */} setShowVoiceprintModal(false)} onSuccess={handleVoiceprintUpload} templateConfig={voiceprintTemplate} /> {/* 删除声纹确认对话框 */} setShowDeleteVoiceprintDialog(false)} onConfirm={handleDeleteVoiceprint} title="删除声纹" message="确定要删除声纹数据吗?删除后可以重新采集。" confirmText="删除" cancelText="取消" type="danger" />
); }; export default Dashboard;