import React, { useState, useEffect } from 'react'; import { Card, Button, Form, Input, Space, Select, Tag, message, Typography, Divider, Row, Col, DatePicker, Upload, Progress, Tooltip, Avatar, Switch } from 'antd'; import { AudioOutlined, CheckCircleOutlined, UserOutlined, CloudUploadOutlined, LeftOutlined, SettingOutlined, QuestionCircleOutlined, InfoCircleOutlined, CalendarOutlined, TeamOutlined, RobotOutlined, RocketOutlined, FileTextOutlined, CheckOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; import dayjs from 'dayjs'; import { createMeeting, uploadAudio } from '../../api/business/meeting'; import { getAiModelPage, getAiModelDefault, AiModelVO } from '../../api/business/aimodel'; import { getPromptPage, PromptTemplateVO } from '../../api/business/prompt'; import { getHotWordPage, HotWordVO } from '../../api/business/hotword'; import { listUsers } from '../../api'; import { SysUser } from '../../types'; const { Title, Text } = Typography; const { Dragger } = Upload; const { Option } = Select; const MeetingCreate: React.FC = () => { const navigate = useNavigate(); const [form] = Form.useForm(); const [submitLoading, setSubmitLoading] = useState(false); const [asrModels, setAsrModels] = useState([]); const [llmModels, setLlmModels] = useState([]); const [prompts, setPrompts] = useState([]); const [hotwordList, setHotwordList] = useState([]); const [userList, setUserList] = useState([]); const watchedPromptId = Form.useWatch('promptId', form); const [fileList, setFileList] = useState([]); const [uploadProgress, setUploadProgress] = useState(0); const [audioUrl, setAudioUrl] = useState(''); useEffect(() => { loadInitialData(); }, []); const loadInitialData = async () => { try { const [asrRes, llmRes, promptRes, hotwordRes, users] = await Promise.all([ getAiModelPage({ current: 1, size: 100, type: 'ASR' }), getAiModelPage({ current: 1, size: 100, type: 'LLM' }), getPromptPage({ current: 1, size: 100 }), getHotWordPage({ current: 1, size: 1000 }), listUsers() ]); setAsrModels(asrRes.data.data.records.filter(m => m.status === 1)); setLlmModels(llmRes.data.data.records.filter(m => m.status === 1)); const activePrompts = promptRes.data.data.records.filter(p => p.status === 1); setPrompts(activePrompts); setHotwordList(hotwordRes.data.data.records.filter(h => h.status === 1)); setUserList(users || []); const defaultAsr = await getAiModelDefault('ASR'); const defaultLlm = await getAiModelDefault('LLM'); form.setFieldsValue({ asrModelId: defaultAsr.data.data?.id, summaryModelId: defaultLlm.data.data?.id, promptId: activePrompts.length > 0 ? activePrompts[0].id : undefined, meetingTime: dayjs(), useSpkId: 1 }); } catch (err) {} }; const customUpload = async (options: any) => { const { file, onSuccess, onError } = options; setUploadProgress(0); try { const interval = setInterval(() => { setUploadProgress(prev => (prev < 95 ? prev + 5 : prev)); }, 300); const res = await uploadAudio(file); clearInterval(interval); setUploadProgress(100); setAudioUrl(res.data.data); onSuccess(res.data.data); message.success('录音上传成功'); } catch (err) { onError(err); message.error('文件上传失败'); } }; const onFinish = async (values: any) => { if (!audioUrl) { message.error('请先上传录音文件'); return; } setSubmitLoading(true); try { await createMeeting({ ...values, meetingTime: values.meetingTime.format('YYYY-MM-DD HH:mm:ss'), audioUrl, participants: values.participants?.join(','), tags: values.tags?.join(',') }); message.success('会议发起成功'); navigate('/meetings'); } catch (err) { console.error(err); } finally { setSubmitLoading(false); } }; return (
{/* 头部导航 - 紧凑化 */}
发起新会议分析 请配置录音文件及 AI 模型参数
{/* 左侧:文件与基础信息 */} 录音上传} bordered={false} style={{ borderRadius: 12, boxShadow: '0 2px 8px rgba(0,0,0,0.03)' }}> setFileList(info.fileList.slice(-1))} maxCount={1} style={{ borderRadius: 8, padding: '16px 0' }} >

点击或拖拽录音文件

{uploadProgress > 0 && uploadProgress < 100 && } {audioUrl && (
就绪: {audioUrl.split('/').pop()}
)}
基础信息} bordered={false} style={{ borderRadius: 12, boxShadow: '0 2px 8px rgba(0,0,0,0.03)' }}> {userList.map(u => ( ))} {/* 右侧:AI 配置 - 固定且不滚动 */} AI 分析配置} bordered={false} style={{ borderRadius: 12, height: '100%', display: 'flex', flexDirection: 'column', boxShadow: '0 2px 8px rgba(0,0,0,0.03)' }} bodyStyle={{ flex: 1, display: 'flex', flexDirection: 'column', padding: '16px 20px' }} >
{prompts.map(p => { const isSelected = watchedPromptId === p.id; return (
form.setFieldsValue({ promptId: p.id })} style={{ padding: '8px 6px', borderRadius: 8, border: `1.5px solid ${isSelected ? '#1890ff' : '#f0f0f0'}`, backgroundColor: isSelected ? '#f0f7ff' : '#fff', cursor: 'pointer', transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', position: 'relative', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center', boxShadow: isSelected ? '0 2px 6px rgba(24, 144, 255, 0.12)' : 'none' }} >
{p.templateName}
{isSelected && (
)}
); })}
纠错热词 } style={{ marginBottom: 0 }} > 声纹识别 } valuePropName="checked" getValueProps={(value) => ({ checked: value === 1 })} normalize={(value) => (value ? 1 : 0)} style={{ marginBottom: 0 }} >
系统将自动执行:转录固化 + 智能总结。
); }; export default MeetingCreate;