2
0
Просмотр исходного кода

✨ feat(classroom): 实现学生IM禁言功能组件

- 新增IMToggleMuteButton组件,用于老师对学生进行IM禁言/解禁操作
- 在TeacherStudentManagementButton中集成IMToggleMuteButton组件
- 从useClassroom导出imGroupManager供组件使用
- 优化学生管理界面,将原有的IM禁言和解禁按钮合并为一个切换按钮
- 实现禁言状态查询和切换功能,包含加载状态和错误处理
yourname 6 месяцев назад
Родитель
Сommit
399ceb32b0

+ 75 - 0
src/client/mobile/components/Classroom/IMToggleMuteButton.tsx

@@ -0,0 +1,75 @@
+import React, { useState, useEffect } from 'react';
+import { useClassroomContext } from './ClassroomProvider';
+import { Button } from '@/client/components/ui/button';
+import { Role } from './useClassroom';
+
+interface IMToggleMuteButtonProps {
+  userId: string;
+  userName?: string;
+}
+
+export const IMToggleMuteButton: React.FC<IMToggleMuteButtonProps> = ({ userId, userName }) => {
+  const { role, muteStudentIM, unmuteStudentIM, classId, imGroupManager } = useClassroomContext();
+  const [isMuted, setIsMuted] = useState(false);
+  const [loading, setLoading] = useState(false);
+
+  // 只有老师才能看到禁言按钮
+  if (role !== Role.Teacher) {
+    return null;
+  }
+
+  // 查询用户禁言状态
+  const checkMuteStatus = async () => {
+    if (!imGroupManager || !classId) return;
+    
+    try {
+      const muteStatus = await imGroupManager.listMuteUsers({ groupId: classId });
+      setIsMuted(muteStatus.muteUserList.includes(userId));
+    } catch (error) {
+      console.error('查询禁言状态失败:', error);
+    }
+  };
+
+  // 组件挂载时查询禁言状态
+  useEffect(() => {
+    checkMuteStatus();
+  }, [userId, classId, imGroupManager]);
+
+  const handleToggleMute = async () => {
+    if (loading) return;
+    
+    setLoading(true);
+    try {
+      if (isMuted) {
+        await unmuteStudentIM(userId);
+        setIsMuted(false);
+      } else {
+        await muteStudentIM(userId);
+        setIsMuted(true);
+      }
+    } catch (error) {
+      console.error('切换禁言状态失败:', 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-purple-100 text-purple-700 hover:bg-purple-200 border-purple-300'
+      } transition-colors`}
+      disabled={loading}
+      title={isMuted ? `解禁 ${displayName}(IM聊天)` : `禁言 ${displayName}(IM聊天)`}
+    >
+      {loading ? '处理中...' : (isMuted ? '解禁IM' : 'IM禁言')}
+    </Button>
+  );
+};

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

@@ -13,13 +13,14 @@ import {
   AlertDialogHeader,
   AlertDialogHeader,
   AlertDialogTitle,
   AlertDialogTitle,
 } from '@/client/components/ui/alert-dialog';
 } from '@/client/components/ui/alert-dialog';
+import { IMToggleMuteButton } from './IMToggleMuteButton';
 
 
 export const TeacherStudentManagementButton: React.FC = () => {
 export const TeacherStudentManagementButton: React.FC = () => {
-  const { students, toggleMuteMember, kickStudent, muteStudentIM, unmuteStudentIM } = useClassroomContext();
+  const { students, toggleMuteMember, kickStudent } = useClassroomContext();
   const [showPanel, setShowPanel] = useState(false);
   const [showPanel, setShowPanel] = useState(false);
   const [confirmDialog, setConfirmDialog] = useState<{
   const [confirmDialog, setConfirmDialog] = useState<{
     open: boolean;
     open: boolean;
-    action: 'mute' | 'unmute' | 'kick' | 'mute_im' | 'unmute_im' | null;
+    action: 'mute' | 'unmute' | 'kick' | null;
     studentId: string;
     studentId: string;
     studentName: string;
     studentName: string;
   }>({
   }>({
@@ -29,7 +30,7 @@ export const TeacherStudentManagementButton: React.FC = () => {
     studentName: '',
     studentName: '',
   });
   });
 
 
-  const openConfirmDialog = (action: 'mute' | 'unmute' | 'kick' | 'mute_im' | 'unmute_im', studentId: string, studentName: string) => {
+  const openConfirmDialog = (action: 'mute' | 'unmute' | 'kick', studentId: string, studentName: string) => {
     setConfirmDialog({
     setConfirmDialog({
       open: true,
       open: true,
       action,
       action,
@@ -51,12 +52,6 @@ export const TeacherStudentManagementButton: React.FC = () => {
       case 'kick':
       case 'kick':
         kickStudent(studentId);
         kickStudent(studentId);
         break;
         break;
-      case 'mute_im':
-        muteStudentIM(studentId);
-        break;
-      case 'unmute_im':
-        unmuteStudentIM(studentId);
-        break;
     }
     }
     
     
     setConfirmDialog({
     setConfirmDialog({
@@ -137,22 +132,7 @@ export const TeacherStudentManagementButton: React.FC = () => {
                        >
                        >
                          取消音频
                          取消音频
                        </Button>
                        </Button>
-                       <Button
-                         onClick={() => openConfirmDialog('mute_im', student.id, student.name)}
-                         variant="outline"
-                         size="sm"
-                         className="text-xs bg-purple-100 text-purple-700 hover:bg-purple-200 border-purple-300"
-                       >
-                         IM禁言
-                       </Button>
-                       <Button
-                         onClick={() => openConfirmDialog('unmute_im', student.id, student.name)}
-                         variant="outline"
-                         size="sm"
-                         className="text-xs bg-blue-100 text-blue-700 hover:bg-blue-200 border-blue-300"
-                       >
-                         取消IM禁言
-                       </Button>
+                       <IMToggleMuteButton userId={student.id} userName={student.name} />
                        <Button
                        <Button
                          onClick={() => openConfirmDialog('kick', student.id, student.name)}
                          onClick={() => openConfirmDialog('kick', student.id, student.name)}
                          variant="outline"
                          variant="outline"
@@ -178,8 +158,6 @@ export const TeacherStudentManagementButton: React.FC = () => {
             <AlertDialogDescription>
             <AlertDialogDescription>
               {confirmDialog.action === 'mute' && `确定要静音 ${confirmDialog.studentName} 吗?(RTC音频)`}
               {confirmDialog.action === 'mute' && `确定要静音 ${confirmDialog.studentName} 吗?(RTC音频)`}
               {confirmDialog.action === 'unmute' && `确定要取消静音 ${confirmDialog.studentName} 吗?(RTC音频)`}
               {confirmDialog.action === 'unmute' && `确定要取消静音 ${confirmDialog.studentName} 吗?(RTC音频)`}
-              {confirmDialog.action === 'mute_im' && `确定要禁言 ${confirmDialog.studentName} 吗?(IM聊天)`}
-              {confirmDialog.action === 'unmute_im' && `确定要取消禁言 ${confirmDialog.studentName} 吗?(IM聊天)`}
               {confirmDialog.action === 'kick' && `确定要移出 ${confirmDialog.studentName} 吗?`}
               {confirmDialog.action === 'kick' && `确定要移出 ${confirmDialog.studentName} 吗?`}
             </AlertDialogDescription>
             </AlertDialogDescription>
           </AlertDialogHeader>
           </AlertDialogHeader>

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

@@ -1119,6 +1119,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
     remoteCameraContainer, // 导出摄像头容器ref
     remoteCameraContainer, // 导出摄像头容器ref
     showCameraOverlay,
     showCameraOverlay,
     setShowCameraOverlay,
     setShowCameraOverlay,
+    imGroupManager: imGroupManager.current,
 
 
     // 方法
     // 方法
     login,
     login,