import React, { useCallback, useEffect, useState } from 'react'; import { Card, Form, Input, Button, DatePicker, Select, Typography, App, Divider, Row, Col, Upload, Space, Progress } from 'antd'; import { ArrowLeftOutlined, UploadOutlined, SaveOutlined, DeleteOutlined, VideoCameraAddOutlined, AudioOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; import dayjs from 'dayjs'; import httpService from '../services/httpService'; import { buildApiUrl, API_ENDPOINTS } from '../config/api'; import configService from '../utils/configService'; import { AUDIO_UPLOAD_ACCEPT, uploadMeetingAudio, validateMeetingAudioFile } from '../services/meetingAudioService'; const { Title, Text } = Typography; const CreateMeeting = () => { const navigate = useNavigate(); const { message } = App.useApp(); const [form] = Form.useForm(); const [loading, setLoading] = useState(false); const [users, setUsers] = useState([]); const [prompts, setPrompts] = useState([]); const [selectedAudioFile, setSelectedAudioFile] = useState(null); const [audioUploading, setAudioUploading] = useState(false); const [audioUploadProgress, setAudioUploadProgress] = useState(0); const [audioUploadMessage, setAudioUploadMessage] = useState(''); const [maxAudioSize, setMaxAudioSize] = useState(() => configService.getCachedMaxAudioSize()); const fetchUsers = useCallback(async () => { try { const res = await httpService.get(buildApiUrl(API_ENDPOINTS.USERS.LIST)); setUsers(res.data.users || []); } catch { setUsers([]); } }, []); const fetchPrompts = useCallback(async () => { try { const res = await httpService.get(buildApiUrl(API_ENDPOINTS.PROMPTS.ACTIVE('MEETING_TASK'))); setPrompts(res.data.prompts || []); } catch { setPrompts([]); } }, []); const loadAudioUploadConfig = useCallback(async () => { try { const nextMaxAudioSize = await configService.getMaxAudioSize(); setMaxAudioSize(nextMaxAudioSize); } catch (error) { message.error(error?.message || '加载音频上传限制失败'); } }, [message]); useEffect(() => { fetchUsers(); fetchPrompts(); loadAudioUploadConfig(); }, [fetchPrompts, fetchUsers, loadAudioUploadConfig]); const handleAudioBeforeUpload = (file) => { if (!maxAudioSize) { message.error('系统音频上传限制未加载完成,请稍后重试'); return Upload.LIST_IGNORE; } const validationMessage = validateMeetingAudioFile(file, maxAudioSize, configService.formatFileSize.bind(configService)); if (validationMessage) { message.warning(validationMessage); return Upload.LIST_IGNORE; } setSelectedAudioFile(file); return false; }; const clearSelectedAudio = () => { setSelectedAudioFile(null); setAudioUploadProgress(0); setAudioUploadMessage(''); }; const onFinish = async (values) => { setLoading(true); try { const payload = { ...values, meeting_time: values.meeting_time.format('YYYY-MM-DD HH:mm:ss'), attendee_ids: values.attendee_ids || [], tags: values.tags?.join(',') || '' }; const res = await httpService.post(buildApiUrl(API_ENDPOINTS.MEETINGS.CREATE), payload); if (res.code === '200') { const meetingId = res.data.meeting_id; if (selectedAudioFile) { setAudioUploading(true); setAudioUploadProgress(0); setAudioUploadMessage('正在上传音频文件...'); try { const uploadResponse = await uploadMeetingAudio({ meetingId, file: selectedAudioFile, promptId: values.prompt_id, onUploadProgress: (progressEvent) => { if (progressEvent.total) { setAudioUploadProgress(Math.min(100, Math.round((progressEvent.loaded * 100) / progressEvent.total))); } setAudioUploadMessage('正在上传音频文件...'); }, }); setAudioUploadProgress(100); setAudioUploadMessage('上传完成,后台正在处理音频...'); message.success(uploadResponse?.message || '会议创建成功,音频已进入后台处理'); } catch (uploadError) { message.warning(uploadError.response?.data?.message || uploadError.response?.data?.detail || '会议已创建,但音频上传失败,请在详情页重试'); } finally { setAudioUploading(false); } } else { message.success('会议创建成功'); } navigate(`/meetings/${meetingId}`); } } catch (error) { message.error(error.response?.data?.message || error.response?.data?.detail || '创建失败'); } finally { setLoading(false); } }; return (