diff --git a/frontend/src/pages/business/MeetingDetail.css b/frontend/src/pages/business/MeetingDetail.css index 44ae5a5..b1ac2ce 100644 --- a/frontend/src/pages/business/MeetingDetail.css +++ b/frontend/src/pages/business/MeetingDetail.css @@ -741,6 +741,48 @@ background: #ffffff !important; } +.meeting-detail-page-v2 .ant-list-item.transcript-row { + display: block !important; + min-width: 0; +} + +.meeting-detail-page-v2 .transcript-entry { + width: 100%; + min-width: 0; + display: grid !important; + grid-template-columns: 32px minmax(0, 1fr); + gap: 12px; + align-items: flex-start; +} + +.meeting-detail-page-v2 .transcript-content-wrap, +.meeting-detail-page-v2 .transcript-meta, +.meeting-detail-page-v2 .transcript-bubble, +.meeting-detail-page-v2 .transcript-bubble-editing { + min-width: 0; + max-width: 100%; +} + +.meeting-detail-page-v2 .transcript-avatar { + align-self: start; + margin-top: 0 !important; +} + +.meeting-detail-page-v2 .transcript-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + min-height: 32px; + gap: 6px 8px; +} + +.meeting-detail-page-v2 .transcript-speaker { + max-width: min(100%, 180px); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .meeting-detail-page-v2 .transcript-player.transcript-player--embedded { display: flex; align-items: center; @@ -845,4 +887,13 @@ .meeting-detail-page-v2 .transcript-panel-header .transcript-stage-tabs button { flex: 1; } + + .meeting-detail-page-v2 .transcript-entry { + grid-template-columns: 28px minmax(0, 1fr); + gap: 10px; + } + + .meeting-detail-page-v2 .transcript-meta { + min-height: 28px; + } } diff --git a/frontend/src/pages/business/MeetingDetail.tsx b/frontend/src/pages/business/MeetingDetail.tsx index a5e6523..5785f2a 100644 --- a/frontend/src/pages/business/MeetingDetail.tsx +++ b/frontend/src/pages/business/MeetingDetail.tsx @@ -36,7 +36,6 @@ import { MeetingTranscriptVO, MeetingVO, reSummary, - resolveAudioMimeType, resolveMeetingPlaybackAudioUrl, retryMeetingChapter, retryMeetingSummary, @@ -1738,11 +1737,27 @@ const MeetingDetail: React.FC = () => { setSummaryVisible(true); }; + const handleAudioPlaybackError = useCallback(() => { + const currentAudioUrl = playbackAudioUrl || ''; + if (!currentAudioUrl || audioPlaybackErrorShownRef.current === currentAudioUrl) { + return; + } + const normalizedUrl = currentAudioUrl.split('#')[0]?.split('?')[0]?.toLowerCase() || ''; + const isM4a = normalizedUrl.endsWith('.m4a'); + message.warning( + isM4a + ? '当前 m4a 文件在本机浏览器中无法直接播放。已确认文件与服务端响应基本正常,更可能是浏览器对该录音参数或容器实现的兼容性问题。建议优先使用 mp3、wav,或下载到本地播放。' + : '当前音频文件无法播放,请检查文件是否损坏或格式是否兼容。', + ); + audioPlaybackErrorShownRef.current = currentAudioUrl; + setAudioPlaying(false); + }, [message, playbackAudioUrl]); + const seekTo = useCallback((timeMs: number) => { if (!audioRef.current) return; audioRef.current.currentTime = timeMs / 1000; - audioRef.current.play(); - }, []); + audioRef.current.play().catch(() => handleAudioPlaybackError()); + }, [handleAudioPlaybackError]); const handleTranscriptRowPlay = useCallback((timeMs: number) => { setLinkedTranscriptIds([]); @@ -1948,22 +1963,6 @@ const MeetingDetail: React.FC = () => { transcriptItemRefs.current[transcriptId] = node; }, []); - const handleAudioPlaybackError = useCallback(() => { - const currentAudioUrl = playbackAudioUrl || ''; - if (!currentAudioUrl || audioPlaybackErrorShownRef.current === currentAudioUrl) { - return; - } - const normalizedUrl = currentAudioUrl.split('#')[0]?.split('?')[0]?.toLowerCase() || ''; - const isM4a = normalizedUrl.endsWith('.m4a'); - message.warning( - isM4a - ? '当前 m4a 文件在本机浏览器中无法直接播放。已确认文件与服务端响应基本正常,更可能是浏览器对该录音参数或容器实现的兼容性问题。建议优先使用 mp3、wav,或下载到本地播放。' - : '当前音频文件无法播放,请检查文件是否损坏或格式是否兼容。', - ); - audioPlaybackErrorShownRef.current = currentAudioUrl; - setAudioPlaying(false); - }, [message, playbackAudioUrl]); - useEffect(() => { const audio = audioRef.current; if (!audio) return undefined; @@ -2004,10 +2003,23 @@ const MeetingDetail: React.FC = () => { }; }, [audioPlaybackRate, meeting?.status, playbackAudioUrl, handleAudioPlaybackError]); + useEffect(() => { + const audio = audioRef.current; + if (!audio) return; + + audio.pause(); + audio.load(); + audio.playbackRate = 1; + setAudioPlaybackRate(1); + setAudioCurrentTime(0); + setAudioDuration(0); + setAudioPlaying(false); + }, [playbackAudioUrl]); + const toggleAudioPlayback = () => { if (!audioRef.current) return; if (audioRef.current.paused) { - audioRef.current.play(); + audioRef.current.play().catch(() => handleAudioPlaybackError()); } else { audioRef.current.pause(); } @@ -2658,11 +2670,6 @@ const MeetingDetail: React.FC = () => {
原文}> - {playbackAudioUrl && ( - - )} {emptyTranscriptFailureNotice && (
当前没有可展示的转录内容
@@ -2715,9 +2722,7 @@ const MeetingDetail: React.FC = () => { styles={{ body: { padding: 0, height: '100%', display: 'flex', flexDirection: 'column', minHeight: 0 } }} > {playbackAudioUrl && ( - +