import React, { useState, useEffect, useRef } from 'react'; import { Link, useNavigate, useParams } from 'react-router-dom'; import apiClient from '../utils/apiClient'; import configService from '../utils/configService'; import { ArrowLeft, Users, Calendar, FileText, X, User, Save, Upload, Plus, Tag } from 'lucide-react'; import { buildApiUrl, API_ENDPOINTS, API_BASE_URL } from '../config/api'; import DateTimePicker from '../components/DateTimePicker'; import TagEditor from '../components/TagEditor'; import MarkdownEditor from '../components/MarkdownEditor'; import './EditMeeting.css'; const EditMeeting = ({ user }) => { const navigate = useNavigate(); const { meeting_id } = useParams(); const [formData, setFormData] = useState({ title: '', meeting_time: '', summary: '', attendees: [], tags: '' }); const [availableUsers, setAvailableUsers] = useState([]); const [userSearch, setUserSearch] = useState(''); const [showUserDropdown, setShowUserDropdown] = useState(false); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); const [isUploadingImage, setIsUploadingImage] = useState(false); const [error, setError] = useState(''); const [meeting, setMeeting] = useState(null); const [maxImageSize, setMaxImageSize] = useState(10 * 1024 * 1024); // 默认10MB useEffect(() => { fetchMeetingData(); fetchUsers(); loadFileSizeConfig(); }, [meeting_id]); const loadFileSizeConfig = async () => { try { const imageSize = await configService.getMaxImageSize(); setMaxImageSize(imageSize); } catch (error) { console.warn('Failed to load file size config:', error); } }; const fetchMeetingData = async () => { try { const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.MEETINGS.EDIT(meeting_id))); const meetingData = response.data; // Check if current user is the creator if (meetingData.creator_id !== user.user_id) { navigate('/dashboard'); return; } setMeeting(meetingData); setFormData({ title: meetingData.title, meeting_time: meetingData.meeting_time || '', summary: meetingData.summary || '', attendees: meetingData.attendees || [], tags: meetingData.tags ? meetingData.tags.map(t => t.name).join(', ') : '' }); } catch (err) { setError('无法加载会议信息'); console.error('Error fetching meeting:', err); } finally { setIsLoading(false); } }; const fetchUsers = async () => { try { // 获取所有普通用户(role_id=2),设置较大的size参数 const response = await apiClient.get(buildApiUrl(`${API_ENDPOINTS.USERS.LIST}?page=1&size=1000&role_id=2`)); setAvailableUsers(response.data.users.filter(u => u.user_id !== user.user_id)); } catch (err) { console.error('Error fetching users:', err); } }; const handleInputChange = (e) => { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value })); }; const handleAddAttendee = (selectedUser) => { if (!formData.attendees.find(a => a.user_id === selectedUser.user_id)) { setFormData(prev => ({ ...prev, attendees: [...prev.attendees, selectedUser] })); } setUserSearch(''); setShowUserDropdown(false); }; const handleRemoveAttendee = (userId) => { setFormData(prev => ({ ...prev, attendees: prev.attendees.filter(a => a.user_id !== userId) })); }; const handleSubmit = async (e) => { e.preventDefault(); if (!formData.title.trim()) { setError('请输入会议标题'); return; } setIsSaving(true); setError(''); try { const updateData = { title: formData.title, meeting_time: formData.meeting_time || null, summary: formData.summary, attendee_ids: formData.attendees.map(a => a.user_id), tags: formData.tags }; await apiClient.put(buildApiUrl(API_ENDPOINTS.MEETINGS.UPDATE(meeting_id)), updateData); navigate(`/meetings/${meeting_id}`); } catch (err) { setError(err.response?.data?.message || '更新会议失败,请重试'); } finally { setIsSaving(false); } }; const handleImageUpload = async (file) => { if (!file) return null; // Validate file type const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp']; if (!allowedTypes.includes(file.type)) { setError('请上传支持的图片格式 (JPG, PNG, GIF, WebP)'); return null; } // Validate file size using dynamic config if (file.size > maxImageSize) { const maxSizeMB = Math.round(maxImageSize / (1024 * 1024)); setError(`图片大小不能超过${maxSizeMB}MB`); return null; } setIsUploadingImage(true); setError(''); try { const formData = new FormData(); formData.append('image_file', file); const response = await apiClient.post( buildApiUrl(API_ENDPOINTS.MEETINGS.UPLOAD_IMAGE(meeting_id)), formData, { headers: { 'Content-Type': 'multipart/form-data', }, } ); return `${API_BASE_URL}${response.data.file_path}`; } catch (err) { setError('上传图片失败,请重试'); return null; } finally { setIsUploadingImage(false); } }; const filteredUsers = availableUsers.filter(user => { // Exclude users already selected as attendees const isAlreadySelected = formData.attendees.some(attendee => attendee.user_id === user.user_id); if (isAlreadySelected) return false; // Filter by search text return user.caption.toLowerCase().includes(userSearch.toLowerCase()) || user.username.toLowerCase().includes(userSearch.toLowerCase()); }); if (isLoading) { return (

加载中...

); } return (
返回首页

编辑会议纪要

修改会议信息、参会人员和摘要内容

setFormData(prev => ({ ...prev, meeting_time: value }))} placeholder="选择会议时间(可选)" />
setFormData(prev => ({ ...prev, tags: value }))} placeholder="输入标签,按回车或逗号分隔" />
{formData.attendees.map(attendee => (
{attendee.caption}
))}
{ setUserSearch(e.target.value); setShowUserDropdown(true); }} onFocus={() => setShowUserDropdown(true)} onBlur={() => { // Delay hiding dropdown to allow click events setTimeout(() => setShowUserDropdown(false), 200); }} placeholder="搜索用户名或姓名添加参会人..." className="user-search-input" /> {showUserDropdown && userSearch && (
{filteredUsers.length > 0 ? ( filteredUsers.map(user => (
handleAddAttendee(user)} >
{user.caption} @{user.username}
)) ) : (
未找到匹配的用户
)}
)}
setFormData(prev => ({ ...prev, summary: value || '' }))} onImageUpload={handleImageUpload} placeholder="在这里编写会议摘要..." height={400} showImageUpload={true} />
使用Markdown格式编写会议摘要,支持**粗体**、*斜体*、# 标题、- 列表、表格等格式。
{isUploadingImage && (
正在上传图片...
)}
{error && !showUploadArea && (
{error}
)}
取消
); }; export default EditMeeting;