import React, { useState, useEffect } from 'react'; import { Card, Button, Input, Space, Tag, message, Popconfirm, Typography, Row, Col, List, Badge, Empty, Skeleton, Tooltip, Radio, Pagination } from 'antd'; import { PlusOutlined, DeleteOutlined, SearchOutlined, CheckCircleOutlined, LoadingOutlined, UserOutlined, CalendarOutlined, PlayCircleOutlined, TeamOutlined, ClockCircleOutlined, EditOutlined, RightOutlined, SyncOutlined, InfoCircleOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; import { getMeetingPage, deleteMeeting, MeetingVO, getMeetingProgress, MeetingProgress } from '../../api/business/meeting'; import dayjs from 'dayjs'; const { Text, Title } = Typography; // 状态标签组件:集成进度背景 const IntegratedStatusTag: React.FC<{ meeting: MeetingVO }> = ({ meeting }) => { const [progress, setProgress] = useState(null); useEffect(() => { if (meeting.status !== 1 && meeting.status !== 2) return; const fetchProgress = async () => { try { const res = await getMeetingProgress(meeting.id); if (res.data && res.data.data) setProgress(res.data.data); } catch (err) {} }; fetchProgress(); const timer = setInterval(fetchProgress, 3000); return () => clearInterval(timer); }, [meeting.id, meeting.status]); const statusConfig: Record = { 0: { text: '排队中', color: '#8c8c8c', bgColor: '#f5f5f5' }, 1: { text: '识别中', color: '#1890ff', bgColor: '#e6f7ff' }, 2: { text: '总结中', color: '#faad14', bgColor: '#fff7e6' }, 3: { text: '已完成', color: '#52c41a', bgColor: '#f6ffed' }, 4: { text: '失败', color: '#ff4d4f', bgColor: '#fff1f0' } }; const config = statusConfig[meeting.status] || statusConfig[0]; const percent = progress?.percent || 0; const isProcessing = meeting.status === 1 || meeting.status === 2; return (
{/* 进度填充背景 */} {isProcessing && percent > 0 && (
)} {isProcessing ? : null} {config.text} {isProcessing && {percent}%}
); }; // 新增:提取进度信息 Hook 供卡片内部使用 const useMeetingProgress = (meeting: MeetingVO) => { const [progress, setProgress] = useState(null); useEffect(() => { if (meeting.status !== 1 && meeting.status !== 2) return; const fetchProgress = async () => { try { const res = await getMeetingProgress(meeting.id); if (res.data && res.data.data) setProgress(res.data.data); } catch (err) {} }; fetchProgress(); const timer = setInterval(fetchProgress, 3000); return () => clearInterval(timer); }, [meeting.id, meeting.status]); return progress; }; const MeetingCardItem: React.FC<{ item: MeetingVO, config: any, fetchData: () => void }> = ({ item, config, fetchData }) => { const navigate = useNavigate(); const progress = useMeetingProgress(item); const isProcessing = item.status === 1 || item.status === 2; return ( navigate(`/meetings/${item.id}`)} className="meeting-card" style={{ borderRadius: 16, border: 'none', height: '220px', position: 'relative', boxShadow: '0 6px 16px rgba(0,0,0,0.04)', transition: 'all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1)' }} bodyStyle={{ padding: 0, display: 'flex', height: '100%' }} > {/* 左侧状态装饰条 - 增加分析中的呼吸灯效果 */}
{/* 右上角醒目图标 */}
e.stopPropagation()}>
navigate(`/meetings/${item.id}`)}>
deleteMeeting(item.id).then(fetchData)}>
{/* 内容排版 */}
{item.title}
{dayjs(item.meetingTime).format('YYYY-MM-DD HH:mm')}
{isProcessing ? (
{progress?.message || '等待引擎调度...'}
) : (
{item.participants || '无参与人员'}
)}
{/* 底部详情提示 */}
{item.tags?.split(',').slice(0, 2).map(t => ( {t} ))}
); }; const Meetings: React.FC = () => { const navigate = useNavigate(); const [loading, setLoading] = useState(false); const [data, setData] = useState([]); const [total, setTotal] = useState(0); const [current, setCurrent] = useState(1); const [size, setSize] = useState(8); const [searchTitle, setSearchTitle] = useState(''); const [viewType, setViewType] = useState<'all' | 'created' | 'involved'>('all'); useEffect(() => { fetchData(); }, [current, size, searchTitle, viewType]); const fetchData = async () => { setLoading(true); try { const res = await getMeetingPage({ current, size, title: searchTitle, viewType }); if (res.data && res.data.data) { setData(res.data.data.records); setTotal(res.data.data.total); } } catch (err) { console.error(err); } finally { setLoading(false); } }; const statusConfig: Record = { 0: { text: '排队中', color: '#8c8c8c', bgColor: '#f5f5f5' }, 1: { text: '识别中', color: '#1890ff', bgColor: '#e6f7ff' }, 2: { text: '总结中', color: '#faad14', bgColor: '#fff7e6' }, 3: { text: '已完成', color: '#52c41a', bgColor: '#f6ffed' }, 4: { text: '失败', color: '#ff4d4f', bgColor: '#fff1f0' } }; return (
{/* 固定头部 - 极简白卡 */}
会议中心
{ setViewType(e.target.value); setCurrent(1); }} buttonStyle="solid"> 全部 我发起 我参与 } allowClear onPressEnter={(e) => { setSearchTitle((e.target as any).value); setCurrent(1); }} style={{ width: 220, borderRadius: 8 }} />
{/* 列表区 */}
{ const config = statusConfig[item.status] || statusConfig[0]; return ; }} locale={{ emptyText: }} />
{/* 分页 */} {total > 0 && (
{ setCurrent(p); setSize(s); }} showTotal={(total) => 为您找到 {total} 场会议} size="small" />
)}
); }; export default Meetings;