import React, { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Card, Descriptions, Button, Tag, Typography, Space, Modal, Input, message, Spin, Timeline, Tabs, Avatar, Progress, Select, Form, Switch, Divider, Alert, Table, Rate, Statistic, Row, Col, } from 'antd'; import { ArrowLeftOutlined, PlayCircleOutlined, PauseCircleOutlined, DownloadOutlined, StarOutlined, PhoneOutlined, ClockCircleOutlined, DollarOutlined, UserOutlined, SoundOutlined, FileTextOutlined, TranslationOutlined, EditOutlined, DeleteOutlined, CheckCircleOutlined, ExclamationCircleOutlined, AuditOutlined, SettingOutlined, MessageOutlined, } from '@ant-design/icons'; import { TranslationCall } from '../../types'; import { database } from '../../utils/database'; import { api } from '../../utils/api'; const { Title, Text, Paragraph } = Typography; const { TextArea } = Input; const { TabPane } = Tabs; const { Option } = Select; interface CallDetailProps {} const CallDetail: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const [call, setCall] = useState(null); const [loading, setLoading] = useState(true); const [isPlaying, setIsPlaying] = useState(false); const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); const [editModalVisible, setEditModalVisible] = useState(false); const [statusModalVisible, setStatusModalVisible] = useState(false); const [refundModalVisible, setRefundModalVisible] = useState(false); const [adminNoteModalVisible, setAdminNoteModalVisible] = useState(false); const [form] = Form.useForm(); const [statusForm] = Form.useForm(); const [refundForm] = Form.useForm(); const [noteForm] = Form.useForm(); // 模拟音频播放状态 const [audioProgress, setAudioProgress] = useState(0); useEffect(() => { if (id) { loadCallDetails(); } }, [id]); const loadCallDetails = async () => { try { setLoading(true); await database.connect(); // 模拟获取通话详情(管理员视角) const mockCall: TranslationCall = { id: id!, userId: 'user_1', callId: `CA${Date.now()}`, clientName: '张先生', clientPhone: '+86 138 0013 8000', type: 'human', status: 'completed', sourceLanguage: 'zh-CN', targetLanguage: 'en-US', startTime: '2024-01-15T10:30:00Z', endTime: '2024-01-15T10:45:00Z', duration: 900, cost: 45.00, rating: 5, feedback: '翻译非常专业,沟通顺畅,非常满意!', translatorId: 'translator_1', translatorName: '李翻译', translatorPhone: '+86 138 0013 8001', recordingUrl: '/recordings/call_123456.mp3', transcription: '用户: 您好,我想了解一下贵公司的产品服务。\n翻译: Hello, I would like to learn about your company\'s products and services.\n客户: Thank you for your interest. Let me introduce our main products...\n翻译: 感谢您的关注。让我为您介绍我们的主要产品...', translation: '这是一次关于产品咨询的商务通话,客户询问了公司的主要产品和服务,我们提供了详细的介绍和说明。', // 管理员相关字段 adminNotes: '通话质量良好,客户满意度高', paymentStatus: 'paid', refundAmount: 0, qualityScore: 95, issues: [], }; setCall(mockCall); setDuration(mockCall.duration || 0); // 填充表单数据 form.setFieldsValue({ clientName: mockCall.clientName, clientPhone: mockCall.clientPhone, translatorName: mockCall.translatorName, cost: mockCall.cost, }); statusForm.setFieldsValue({ status: mockCall.status, }); } catch (error) { console.error('加载通话详情失败:', error); message.error('加载通话详情失败'); } finally { setLoading(false); } }; const handlePlayPause = () => { setIsPlaying(!isPlaying); if (!isPlaying) { // 模拟音频播放 const interval = setInterval(() => { setCurrentTime(prev => { const newTime = prev + 1; setAudioProgress((newTime / duration) * 100); if (newTime >= duration) { clearInterval(interval); setIsPlaying(false); setCurrentTime(0); setAudioProgress(0); } return newTime; }); }, 1000); } }; const handleEdit = async (values: any) => { if (!call) return; try { const updatedCall = { ...call, ...values, updatedAt: new Date().toISOString(), }; setCall(updatedCall); setEditModalVisible(false); message.success('通话信息更新成功'); } catch (error) { message.error('更新通话信息失败'); } }; const handleStatusChange = async (values: any) => { if (!call) return; try { const updatedCall = { ...call, status: values.status, updatedAt: new Date().toISOString(), }; setCall(updatedCall); setStatusModalVisible(false); message.success('状态更新成功'); } catch (error) { message.error('更新状态失败'); } }; const handleRefund = async (values: any) => { if (!call) return; try { const refundAmount = values.amount || call.cost; // 模拟退款API调用 await api.refundPayment(`payment_${call.id}`, refundAmount); const updatedCall = { ...call, refundAmount: refundAmount, paymentStatus: 'refunded' as const, updatedAt: new Date().toISOString(), }; setCall(updatedCall); setRefundModalVisible(false); message.success('退款处理成功'); } catch (error) { message.error('退款处理失败'); } }; const handleAddAdminNote = async (values: any) => { if (!call) return; try { const updatedCall = { ...call, adminNotes: values.note, updatedAt: new Date().toISOString(), }; setCall(updatedCall); setAdminNoteModalVisible(false); message.success('管理员备注添加成功'); } catch (error) { message.error('添加备注失败'); } }; const formatTime = (seconds: number) => { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; }; const getStatusColor = (status: string) => { const colors = { pending: 'orange', active: 'blue', completed: 'green', cancelled: 'red', refunded: 'purple', }; return colors[status as keyof typeof colors] || 'default'; }; const getStatusText = (status: string) => { const texts = { pending: '等待中', active: '通话中', completed: '已完成', cancelled: '已取消', refunded: '已退款', }; return texts[status as keyof typeof texts] || status; }; const getPaymentStatusColor = (status: string) => { const colors = { pending: 'orange', paid: 'green', refunded: 'purple', failed: 'red', }; return colors[status as keyof typeof colors] || 'default'; }; const getPaymentStatusText = (status: string) => { const texts = { pending: '待支付', paid: '已支付', refunded: '已退款', failed: '支付失败', }; return texts[status as keyof typeof texts] || status; }; if (loading) { return (
加载通话详情...
); } if (!call) { return (
通话记录不存在
); } return (
{/* 头部导航 */}
通话详情 #{call.id}
{/* 管理员操作按钮 */}
{/* 系统状态提醒 */} {call.issues && call.issues.length > 0 && ( )} {/* 基本信息卡片 */} {call.callId} {getStatusText(call.status)} {getPaymentStatusText(call.paymentStatus)} {call.clientName} {call.clientPhone} } /> {call.translatorName} {new Date(call.startTime).toLocaleString()} {call.endTime ? new Date(call.endTime).toLocaleString() : '-'} {formatTime(call.duration || 0)} ¥{call.cost.toFixed(2)} 0 ? 'danger' : 'secondary'}> ¥{call.refundAmount.toFixed(2)} = 90 ? '#52c41a' : call.qualityScore >= 70 ? '#faad14' : '#ff4d4f' }}> {call.qualityScore}/100 {call.adminNotes && (
管理员备注: {call.adminNotes}
)}
{/* 录音播放器 */} {call.recordingUrl && ( 录音播放 } style={{ marginBottom: '24px' }} >
{formatTime(currentTime)} {formatTime(duration)}
)} {/* 详细内容标签页 */} 转录内容 } key="transcription" >
{call.transcription ? (
                    {call.transcription}
                  
) : (
暂无转录内容
)}
翻译摘要 } key="translation" >
{call.translation ? ( {call.translation} ) : (
暂无翻译摘要
)}
用户评价 } key="rating" >
服务评分: {call.rating && ( ({call.rating}/5 分) )}
{call.feedback && (
用户反馈: {call.feedback}
)}
质量分析 } key="quality" >
= 90 ? '#52c41a' : call.qualityScore >= 70 ? '#faad14' : '#ff4d4f'} /> {call.issues && call.issues.length > 0 ? (
{call.issues.map((issue, index) => ( {issue} ))}
) : ( 无异常 )}
{/* 编辑信息弹窗 */} setEditModalVisible(false)} footer={null} width={600} >
{/* 更改状态弹窗 */} setStatusModalVisible(false)} footer={null} >
{/* 退款处理弹窗 */} setRefundModalVisible(false)} footer={null} >