From 7cd6ad144a25c0986705ff83a2e30f81aa9c7894 Mon Sep 17 00:00:00 2001 From: "mula.liu" Date: Thu, 22 Jan 2026 15:23:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E4=BC=9A=E8=AE=AE?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gemini-clipboard/clipboard-1768974204648.png | Bin 14018 -> 0 bytes .gemini-clipboard/clipboard-1769064595946.png | Bin 0 -> 10196 bytes backend/app/api/endpoints/auth.py | 3 +- backend/app/api/endpoints/meetings.py | 15 ++- backend/app/models/models.py | 2 + frontend/src/pages/AdminDashboard.css | 58 +++++++++ frontend/src/pages/AdminDashboard.jsx | 110 +++++++++++++++++- 7 files changed, 179 insertions(+), 9 deletions(-) delete mode 100644 .gemini-clipboard/clipboard-1768974204648.png create mode 100644 .gemini-clipboard/clipboard-1769064595946.png diff --git a/.gemini-clipboard/clipboard-1768974204648.png b/.gemini-clipboard/clipboard-1768974204648.png deleted file mode 100644 index 683895711f4bcea4dfb5778dc97145b7d581d081..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14018 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 5: meeting_data.audio_file_path = meeting['audio_file_path'] + meeting_data.audio_duration = meeting['audio_duration'] try: transcription_status_data = transcription_service.get_meeting_transcription_status(meeting_id) if transcription_status_data: diff --git a/backend/app/models/models.py b/backend/app/models/models.py index f2f4259..cba8c56 100644 --- a/backend/app/models/models.py +++ b/backend/app/models/models.py @@ -88,6 +88,8 @@ class Meeting(BaseModel): creator_id: int creator_username: str audio_file_path: Optional[str] = None + audio_duration: Optional[float] = None + prompt_name: Optional[str] = None transcription_status: Optional[TranscriptionTaskStatus] = None tags: Optional[List[Tag]] = [] access_password: Optional[str] = None diff --git a/frontend/src/pages/AdminDashboard.css b/frontend/src/pages/AdminDashboard.css index 57ca550..1e3784c 100644 --- a/frontend/src/pages/AdminDashboard.css +++ b/frontend/src/pages/AdminDashboard.css @@ -520,4 +520,62 @@ justify-content: flex-end; gap: 1rem; margin-top: 2rem; +} + +/* 会议详情模态框样式 */ +.meeting-details-info { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.detail-item { + display: flex; + align-items: flex-start; + line-height: 1.5; +} + +.detail-label { + font-weight: 600; + color: #64748b; + width: 80px; + flex-shrink: 0; +} + +.detail-value { + color: #1e293b; + flex: 1; +} + +.audio-item { + display: flex; + align-items: center; + margin-bottom: 0.5rem; +} + +.audio-item:last-child { + margin-bottom: 0; +} + +.download-link { + font-size: 0.875rem; + text-decoration: none; +} + +.download-link:hover { + text-decoration: underline; +} + +/* 弹窗内部加载样式 */ +.modal-body-loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3rem 0; + color: #64748b; +} + +.modal-body-loading .loading-spinner { + margin-bottom: 1rem; } \ No newline at end of file diff --git a/frontend/src/pages/AdminDashboard.jsx b/frontend/src/pages/AdminDashboard.jsx index 2d0d245..a41b3da 100644 --- a/frontend/src/pages/AdminDashboard.jsx +++ b/frontend/src/pages/AdminDashboard.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { LogOut, User, Users, Activity, Server, HardDrive, Cpu, MemoryStick, RefreshCw, UserX, ChevronDown, KeyRound, Shield, BookText, Waves, UserCog } from 'lucide-react'; +import { LogOut, User, Users, Activity, Server, HardDrive, Cpu, MemoryStick, RefreshCw, UserX, ChevronDown, KeyRound, Shield, BookText, Waves, UserCog, Search } from 'lucide-react'; import apiClient from '../utils/apiClient'; import { buildApiUrl, API_ENDPOINTS } from '../config/api'; import Dropdown from '../components/Dropdown'; @@ -7,6 +7,7 @@ import menuService from '../services/menuService'; import ConfirmDialog from '../components/ConfirmDialog'; import Toast from '../components/Toast'; import PageLoading from '../components/PageLoading'; +import FormModal from '../components/FormModal'; import './AdminDashboard.css'; // 常量定义 @@ -66,6 +67,11 @@ const AdminDashboard = ({ user, onLogout }) => { // Toast和确认对话框 const [toasts, setToasts] = useState([]); const [kickConfirmInfo, setKickConfirmInfo] = useState(null); + + // 会议详情模态框 + const [showMeetingModal, setShowMeetingModal] = useState(false); + const [meetingDetails, setMeetingDetails] = useState(null); + const [meetingLoading, setMeetingLoading] = useState(false); // Toast辅助函数 const showToast = (message, type = 'info') => { @@ -122,7 +128,7 @@ const AdminDashboard = ({ user, onLogout }) => { }, []); useEffect(() => { - if (autoRefresh) { + if (autoRefresh && !showMeetingModal) { const timer = setInterval(() => { setCountdown(prev => { if (prev <= 1) { @@ -134,7 +140,7 @@ const AdminDashboard = ({ user, onLogout }) => { }, 1000); return () => clearInterval(timer); } - }, [autoRefresh]); + }, [autoRefresh, showMeetingModal]); useEffect(() => { fetchTasks(); @@ -237,6 +243,27 @@ const AdminDashboard = ({ user, onLogout }) => { } }; + const handleViewMeeting = async (meetingId) => { + if (!meetingId) return; + setMeetingLoading(true); + setShowMeetingModal(true); + setMeetingDetails(null); // Clear previous + + try { + const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.MEETINGS.DETAIL(meetingId))); + if (response.code === '200') { + setMeetingDetails(response.data); + } else { + showToast('获取会议详情失败', 'error'); + } + } catch (err) { + console.error('Fetch meeting details error:', err); + showToast('获取会议详情失败', 'error'); + } finally { + setMeetingLoading(false); + } + }; + if (loading && !stats) { return ; } @@ -561,7 +588,25 @@ const AdminDashboard = ({ user, onLogout }) => { {task.task_id} {getTaskTypeText(task.task_type)} - {task.meeting_title || '-'} + +
+ {task.meeting_id && task.task_type === 'transcription' && ( + + )} + {task.meeting_title || '-'} +
+ {task.creator_name || '-'} @@ -594,6 +639,63 @@ const AdminDashboard = ({ user, onLogout }) => { type="warning" /> + {/* 会议数据模态框 (使用标准 FormModal) */} + setShowMeetingModal(false)} + title="会议数据" + size="medium" + actions={ + + } + > + {meetingLoading ? ( +
+
+

正在获取会议数据...

+
+ ) : meetingDetails ? ( +
+
+ 会议名称: + {meetingDetails.title} +
+
+ 开始时间: + + {meetingDetails.meeting_time ? new Date(meetingDetails.meeting_time).toLocaleString() : '-'} + +
+
+ 使用模版: + {meetingDetails.prompt_name || '默认模版'} +
+
+ 音频信息: +
+ {meetingDetails.audio_file_path && meetingDetails.audio_file_path.length > 5 ? ( + <> + {meetingDetails.audio_duration ? `${Math.floor(meetingDetails.audio_duration / 60)}分${Math.floor(meetingDetails.audio_duration % 60)}秒` : '未知时长'} + + 下载 + + + ) : ( + 无音频 + )} +
+
+
+ ) : ( +
无法加载数据
+ )} +
+ {/* Toast notifications */} {toasts.map(toast => (