import React, { useState, useEffect } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import apiClient from '../utils/apiClient'; import configService from '../utils/configService'; import { ArrowLeft, Upload, Users, Calendar, FileText, X, User, Plus, Tag } from 'lucide-react'; import { buildApiUrl, API_ENDPOINTS } from '../config/api'; import DateTimePicker from '../components/DateTimePicker'; import TagEditor from '../components/TagEditor'; import './CreateMeeting.css'; const CreateMeeting = ({ user }) => { const navigate = useNavigate(); const [formData, setFormData] = useState({ title: '', meeting_time: '', attendees: [], tags: '' }); const [availableUsers, setAvailableUsers] = useState([]); const [userSearch, setUserSearch] = useState(''); const [showUserDropdown, setShowUserDropdown] = useState(false); const [audioFile, setAudioFile] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const [maxFileSize, setMaxFileSize] = useState(100 * 1024 * 1024); // 默认100MB useEffect(() => { fetchUsers(); loadMaxFileSize(); }, []); const loadMaxFileSize = async () => { try { const size = await configService.getMaxFileSize(); setMaxFileSize(size); } catch (error) { console.warn('Failed to load max file size config:', error); } }; const fetchUsers = async () => { try { // 获取所有用户,设置较大的size参数 const response = await apiClient.get(buildApiUrl(`${API_ENDPOINTS.USERS.LIST}?page=1&size=1000`)); 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 handleFileChange = (e) => { const file = e.target.files[0]; if (file) { // Check file type - include both MIME types and extensions const allowedMimeTypes = ['audio/mp3', 'audio/wav', 'audio/m4a', 'audio/mpeg', 'audio/mp4', 'audio/x-m4a']; const fileExtension = file.name.toLowerCase().split('.').pop(); const allowedExtensions = ['mp3', 'wav', 'm4a', 'mpeg']; if (!allowedMimeTypes.includes(file.type) && !allowedExtensions.includes(fileExtension)) { setError('请上传支持的音频格式 (MP3, WAV, M4A)'); return; } // Check file size using dynamic config if (file.size > maxFileSize) { const maxSizeMB = Math.round(maxFileSize / (1024 * 1024)); setError(`音频文件大小不能超过${maxSizeMB}MB`); return; } setAudioFile(file); setError(''); } }; const handleSubmit = async (e) => { e.preventDefault(); if (!formData.title.trim()) { setError('请输入会议标题'); return; } setIsLoading(true); setError(''); try { // Create meeting const meetingData = { user_id: user.user_id, title: formData.title, meeting_time: formData.meeting_time || null, attendee_ids: formData.attendees.map(a => a.user_id), tags: formData.tags }; const response = await apiClient.post(buildApiUrl(API_ENDPOINTS.MEETINGS.CREATE), meetingData); const meetingId = response.data.meeting_id; // Upload audio file if provided if (audioFile) { const formDataUpload = new FormData(); formDataUpload.append('audio_file', audioFile); formDataUpload.append('meeting_id', meetingId); await apiClient.post(buildApiUrl(API_ENDPOINTS.MEETINGS.UPLOAD_AUDIO), formDataUpload, { headers: { 'Content-Type': 'multipart/form-data', }, }); } navigate(`/meetings/${meetingId}`); } catch (err) { setError(err.response?.data?.message || '创建会议失败,请重试'); } finally { setIsLoading(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()); }); return (
返回首页

新建会议纪要

创建新的会议记录并上传音频文件进行AI处理

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}
)) ) : (
未找到匹配的用户
)}
)}
{audioFile && (
已选择: {audioFile.name}
)}
{error && (
{error}
)}
取消
); }; export default CreateMeeting;