Parcourir la source

✨ feat(classroom): 添加RTC音频静音控制功能

- 新增RTCToggleMuteButton组件,实现老师对学生RTC音频静音控制
- 添加RTC静音状态查询方法(checkRtcMuteStatus, checkRtcMuteStatusBatch, getAllRtcMuteStatus)
- 在教师学生管理面板中集成RTC静音控制按钮,替代原有的静音按钮
- 实现3秒定时检查RTC静音状态的机制,确保状态同步

🔧 chore(classroom): 重构学生管理组件代码

- 移除TeacherStudentManagementButton中的直接静音控制逻辑
- 简化确认对话框状态,只保留踢出操作
- 调整组件导入结构,引入RTCToggleMuteButton组件
- 优化useClassroom hook返回值,添加RTC相关方法
yourname il y a 6 mois
Parent
commit
88ab147235

+ 72 - 0
src/client/mobile/components/Classroom/RTCToggleMuteButton.tsx

@@ -0,0 +1,72 @@
+import React, { useState, useEffect } from 'react';
+import { useClassroomContext } from './ClassroomProvider';
+import { Button } from '@/client/components/ui/button';
+import { Role } from './useClassroom';
+
+interface RTCToggleMuteButtonProps {
+  userId: string;
+  userName?: string;
+}
+
+export const RTCToggleMuteButton: React.FC<RTCToggleMuteButtonProps> = ({ userId, userName }) => {
+  const { role, toggleMuteMember, checkRtcMuteStatus } = useClassroomContext();
+  const [isMuted, setIsMuted] = useState(false);
+  const [loading, setLoading] = useState(false);
+
+  // 只有老师才能看到音频静音按钮
+  if (role !== Role.Teacher) {
+    return null;
+  }
+
+  // 组件挂载时查询RTC静音状态
+  useEffect(() => {
+    const fetchRtcMuteStatus = () => {
+      try {
+        const muted = checkRtcMuteStatus(userId);
+        setIsMuted(muted);
+      } catch (error) {
+        console.error('查询RTC静音状态失败:', error);
+      }
+    };
+    
+    fetchRtcMuteStatus();
+    
+    // 设置定时器定期检查状态
+    const interval = setInterval(fetchRtcMuteStatus, 3000); // 每3秒检查一次
+    
+    return () => clearInterval(interval);
+  }, [userId, checkRtcMuteStatus]);
+
+  const handleToggleMute = async () => {
+    if (loading) return;
+    
+    setLoading(true);
+    try {
+      await toggleMuteMember(userId, !isMuted);
+      setIsMuted(!isMuted);
+    } catch (error) {
+      console.error('切换RTC静音状态失败:', error);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const displayName = userName || userId;
+
+  return (
+    <Button
+      onClick={handleToggleMute}
+      variant="outline"
+      size="sm"
+      className={`text-xs ${
+        isMuted 
+          ? 'bg-green-100 text-green-700 hover:bg-green-200 border-green-300' 
+          : 'bg-yellow-100 text-yellow-700 hover:bg-yellow-200 border-yellow-300'
+      } transition-colors`}
+      disabled={loading}
+      title={isMuted ? `取消 ${displayName} 的音频静音` : `静音 ${displayName} 的音频`}
+    >
+      {loading ? '处理中...' : (isMuted ? '取消音频' : '音频静音')}
+    </Button>
+  );
+};

+ 5 - 27
src/client/mobile/components/Classroom/TeacherStudentManagementButton.tsx

@@ -14,13 +14,14 @@ import {
   AlertDialogTitle,
 } from '@/client/components/ui/alert-dialog';
 import { IMToggleMuteButton } from './IMToggleMuteButton';
+import { RTCToggleMuteButton } from './RTCToggleMuteButton';
 
 export const TeacherStudentManagementButton: React.FC = () => {
-  const { students, toggleMuteMember, kickStudent } = useClassroomContext();
+  const { students, kickStudent } = useClassroomContext();
   const [showPanel, setShowPanel] = useState(false);
   const [confirmDialog, setConfirmDialog] = useState<{
     open: boolean;
-    action: 'mute' | 'unmute' | 'kick' | null;
+    action: 'kick' | null;
     studentId: string;
     studentName: string;
   }>({
@@ -30,7 +31,7 @@ export const TeacherStudentManagementButton: React.FC = () => {
     studentName: '',
   });
 
-  const openConfirmDialog = (action: 'mute' | 'unmute' | 'kick', studentId: string, studentName: string) => {
+  const openConfirmDialog = (action: 'kick', studentId: string, studentName: string) => {
     setConfirmDialog({
       open: true,
       action,
@@ -43,12 +44,6 @@ export const TeacherStudentManagementButton: React.FC = () => {
     const { action, studentId } = confirmDialog;
     
     switch (action) {
-      case 'mute':
-        toggleMuteMember(studentId, true);
-        break;
-      case 'unmute':
-        toggleMuteMember(studentId, false);
-        break;
       case 'kick':
         kickStudent(studentId);
         break;
@@ -116,22 +111,7 @@ export const TeacherStudentManagementButton: React.FC = () => {
                     <div key={student.id} className="flex items-center justify-between p-2 border rounded text-sm">
                       <span>{student.name}</span>
                       <div className="flex gap-1 flex-wrap">
-                        <Button
-                         onClick={() => openConfirmDialog('mute', student.id, student.name)}
-                         variant="outline"
-                         size="sm"
-                         className="text-xs bg-yellow-100 text-yellow-700 hover:bg-yellow-200 border-yellow-300"
-                       >
-                         音频静音
-                       </Button>
-                       <Button
-                         onClick={() => openConfirmDialog('unmute', student.id, student.name)}
-                         variant="outline"
-                         size="sm"
-                         className="text-xs bg-green-100 text-green-700 hover:bg-green-200 border-green-300"
-                       >
-                         取消音频
-                       </Button>
+                       <RTCToggleMuteButton userId={student.id} userName={student.name} />
                        <IMToggleMuteButton userId={student.id} userName={student.name} />
                        <Button
                          onClick={() => openConfirmDialog('kick', student.id, student.name)}
@@ -156,8 +136,6 @@ export const TeacherStudentManagementButton: React.FC = () => {
           <AlertDialogHeader>
             <AlertDialogTitle>操作确认</AlertDialogTitle>
             <AlertDialogDescription>
-              {confirmDialog.action === 'mute' && `确定要静音 ${confirmDialog.studentName} 吗?(RTC音频)`}
-              {confirmDialog.action === 'unmute' && `确定要取消静音 ${confirmDialog.studentName} 吗?(RTC音频)`}
               {confirmDialog.action === 'kick' && `确定要移出 ${confirmDialog.studentName} 吗?`}
             </AlertDialogDescription>
           </AlertDialogHeader>

+ 54 - 1
src/client/mobile/components/Classroom/useClassroom.ts

@@ -1076,6 +1076,56 @@ export const useClassroom = ({ user }:{ user : User }) => {
     }
   };
 
+  // 查询RTC用户静音状态
+  const checkRtcMuteStatus = (userId: string): boolean => {
+    if (!aliRtcEngine.current) return false;
+    
+    try {
+      const userInfo = aliRtcEngine.current.getUserInfo(userId);
+      return userInfo ? userInfo.isMuteAudioPlaying : false;
+    } catch (error) {
+      console.error('查询RTC静音状态失败:', error);
+      return false;
+    }
+  };
+
+  // 批量查询RTC用户静音状态
+  const checkRtcMuteStatusBatch = (userIds: string[]): Record<string, boolean> => {
+    if (!aliRtcEngine.current) return {};
+    
+    const result: Record<string, boolean> = {};
+    try {
+      userIds.forEach(userId => {
+        const userInfo = aliRtcEngine.current!.getUserInfo(userId);
+        result[userId] = userInfo ? userInfo.isMuteAudioPlaying : false;
+      });
+      return result;
+    } catch (error) {
+      console.error('批量查询RTC静音状态失败:', error);
+      return {};
+    }
+  };
+
+  // 获取所有在线用户的RTC静音状态
+  const getAllRtcMuteStatus = (): Record<string, boolean> => {
+    if (!aliRtcEngine.current) return {};
+    
+    try {
+      const onlineUsers = aliRtcEngine.current.getOnlineRemoteUsers();
+      const result: Record<string, boolean> = {};
+      
+      onlineUsers.forEach(userId => {
+        const userInfo = aliRtcEngine.current!.getUserInfo(userId);
+        result[userId] = userInfo ? userInfo.isMuteAudioPlaying : false;
+      });
+      
+      return result;
+    } catch (error) {
+      console.error('获取所有用户RTC静音状态失败:', error);
+      return {};
+    }
+  };
+
   const answerHandUp = async (studentId: string): Promise<void> => {
     if (!imMessageManager.current || !classId || role !== Role.Teacher) return;
     
@@ -1183,7 +1233,10 @@ export const useClassroom = ({ user }:{ user : User }) => {
     muteAllIM,
     unmuteAllIM,
     checkMuteStatus,
-    checkAllMuteStatus
+    checkAllMuteStatus,
+    checkRtcMuteStatus,
+    checkRtcMuteStatusBatch,
+    getAllRtcMuteStatus
   };
 };