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

✨ feat(classroom): 实现课堂操作确认对话框

- 替换浏览器默认confirm对话框为UI组件库的AlertDialog
- 结束课堂操作添加确认弹窗,防止误操作
- 学生管理中静音、取消静音和移出操作添加确认弹窗
- 优化状态管理,使用useState控制弹窗显示状态

♻️ refactor(classroom): 清理ClassroomPage中未使用的组件导入

- 移除TeacherView和StudentView的导入
- 临时保留空片段占位,为后续视图实现做准备
yourname 6 месяцев назад
Родитель
Сommit
8d2a84aa75

+ 47 - 14
src/client/mobile/components/Classroom/TeacherClassControlButton.tsx

@@ -1,28 +1,61 @@
-import React from 'react';
+import React, { useState } from 'react';
 import { useClassroomContext } from './ClassroomProvider';
 import { ClassStatus } from './useClassroom';
 import { StopIcon } from '@heroicons/react/24/outline';
 import { Button } from '@/client/components/ui/button';
+import {
+  AlertDialog,
+  AlertDialogAction,
+  AlertDialogCancel,
+  AlertDialogContent,
+  AlertDialogDescription,
+  AlertDialogFooter,
+  AlertDialogHeader,
+  AlertDialogTitle,
+} from '@/client/components/ui/alert-dialog';
 
 export const TeacherClassControlButton: React.FC = () => {
   const { endClass, classStatus } = useClassroomContext();
+  const [isConfirmOpen, setIsConfirmOpen] = useState(false);
 
   const handleEndClass = async () => {
-    if (window.confirm('确定要结束课堂吗?所有学生将被断开连接。')) {
-      await endClass();
-    }
+    await endClass();
+    setIsConfirmOpen(false);
+  };
+
+  const openConfirmDialog = () => {
+    setIsConfirmOpen(true);
   };
 
   return (
-    <Button
-      type="button"
-      onClick={handleEndClass}
-      className={`p-2 rounded-full ${classStatus === ClassStatus.ENDED ? 'bg-gray-500' : 'bg-red-500'} text-white`}
-      title={classStatus === ClassStatus.ENDED ? '课堂已结束' : '结束课堂'}
-      size="icon"
-      variant="ghost"
-    >
-      <StopIcon className="w-4 h-4" />
-    </Button>
+    <>
+      <Button
+        type="button"
+        onClick={openConfirmDialog}
+        className={`p-2 rounded-full ${classStatus === ClassStatus.ENDED ? 'bg-gray-500' : 'bg-red-500'} text-white`}
+        title={classStatus === ClassStatus.ENDED ? '课堂已结束' : '结束课堂'}
+        size="icon"
+        variant="ghost"
+      >
+        <StopIcon className="w-4 h-4" />
+      </Button>
+
+      <AlertDialog open={isConfirmOpen} onOpenChange={setIsConfirmOpen}>
+        <AlertDialogContent>
+          <AlertDialogHeader>
+            <AlertDialogTitle>结束课堂确认</AlertDialogTitle>
+            <AlertDialogDescription>
+              确定要结束课堂吗?所有学生将被断开连接。
+            </AlertDialogDescription>
+          </AlertDialogHeader>
+          <AlertDialogFooter>
+            <AlertDialogCancel>取消</AlertDialogCancel>
+            <AlertDialogAction onClick={handleEndClass}>
+              确定结束
+            </AlertDialogAction>
+          </AlertDialogFooter>
+        </AlertDialogContent>
+      </AlertDialog>
+    </>
   );
 };

+ 97 - 34
src/client/mobile/components/Classroom/TeacherStudentManagementButton.tsx

@@ -3,27 +3,71 @@ import { useClassroomContext } from './ClassroomProvider';
 import { UserGroupIcon, XMarkIcon } from '@heroicons/react/24/outline';
 import { Button } from '@/client/components/ui/button';
 import { Card, CardContent, CardHeader, CardTitle } from '@/client/components/ui/card';
+import {
+  AlertDialog,
+  AlertDialogAction,
+  AlertDialogCancel,
+  AlertDialogContent,
+  AlertDialogDescription,
+  AlertDialogFooter,
+  AlertDialogHeader,
+  AlertDialogTitle,
+} from '@/client/components/ui/alert-dialog';
 
 export const TeacherStudentManagementButton: React.FC = () => {
   const { students, toggleMuteMember, kickStudent } = useClassroomContext();
   const [showPanel, setShowPanel] = useState(false);
+  const [confirmDialog, setConfirmDialog] = useState<{
+    open: boolean;
+    action: 'mute' | 'unmute' | 'kick' | null;
+    studentId: string;
+    studentName: string;
+  }>({
+    open: false,
+    action: null,
+    studentId: '',
+    studentName: '',
+  });
 
-  const handleMuteStudent = (studentId: string, studentName: string) => {
-    if (window.confirm(`确定要静音 ${studentName} 吗?`)) {
-      toggleMuteMember(studentId, true);
-    }
+  const openConfirmDialog = (action: 'mute' | 'unmute' | 'kick', studentId: string, studentName: string) => {
+    setConfirmDialog({
+      open: true,
+      action,
+      studentId,
+      studentName,
+    });
   };
 
-  const handleUnmuteStudent = (studentId: string, studentName: string) => {
-    if (window.confirm(`确定要取消静音 ${studentName} 吗?`)) {
-      toggleMuteMember(studentId, false);
+  const handleConfirm = () => {
+    const { action, studentId } = confirmDialog;
+    
+    switch (action) {
+      case 'mute':
+        toggleMuteMember(studentId, true);
+        break;
+      case 'unmute':
+        toggleMuteMember(studentId, false);
+        break;
+      case 'kick':
+        kickStudent(studentId);
+        break;
     }
+    
+    setConfirmDialog({
+      open: false,
+      action: null,
+      studentId: '',
+      studentName: '',
+    });
   };
 
-  const handleKickStudent = (studentId: string, studentName: string) => {
-    if (window.confirm(`确定要移出 ${studentName} 吗?`)) {
-      kickStudent(studentId);
-    }
+  const handleCancel = () => {
+    setConfirmDialog({
+      open: false,
+      action: null,
+      studentId: '',
+      studentName: '',
+    });
   };
 
   return (
@@ -72,29 +116,29 @@ export const TeacherStudentManagementButton: React.FC = () => {
                       <span>{student.name}</span>
                       <div className="flex gap-1">
                         <Button
-                          onClick={() => handleMuteStudent(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={() => handleUnmuteStudent(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>
-                        <Button
-                          onClick={() => handleKickStudent(student.id, student.name)}
-                          variant="outline"
-                          size="sm"
-                          className="text-xs bg-red-100 text-red-700 hover:bg-red-200 border-red-300"
-                        >
-                          移出
-                        </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>
+                       <Button
+                         onClick={() => openConfirmDialog('kick', student.id, student.name)}
+                         variant="outline"
+                         size="sm"
+                         className="text-xs bg-red-100 text-red-700 hover:bg-red-200 border-red-300"
+                       >
+                         移出
+                       </Button>
                       </div>
                     </div>
                   ))}
@@ -104,6 +148,25 @@ export const TeacherStudentManagementButton: React.FC = () => {
           </Card>
         </div>
       )}
+
+      <AlertDialog open={confirmDialog.open} onOpenChange={(open) => !open && handleCancel()}>
+        <AlertDialogContent>
+          <AlertDialogHeader>
+            <AlertDialogTitle>操作确认</AlertDialogTitle>
+            <AlertDialogDescription>
+              {confirmDialog.action === 'mute' && `确定要静音 ${confirmDialog.studentName} 吗?`}
+              {confirmDialog.action === 'unmute' && `确定要取消静音 ${confirmDialog.studentName} 吗?`}
+              {confirmDialog.action === 'kick' && `确定要移出 ${confirmDialog.studentName} 吗?`}
+            </AlertDialogDescription>
+          </AlertDialogHeader>
+          <AlertDialogFooter>
+            <AlertDialogCancel>取消</AlertDialogCancel>
+            <AlertDialogAction onClick={handleConfirm}>
+              确定
+            </AlertDialogAction>
+          </AlertDialogFooter>
+        </AlertDialogContent>
+      </AlertDialog>
     </div>
   );
 };

+ 1 - 3
src/client/mobile/pages/ClassroomPage.tsx

@@ -5,8 +5,6 @@ import { Role, ClassStatus } from '@/client/mobile/components/Classroom/useClass
 import { ClassroomLayout } from '@/client/mobile/components/Classroom/ClassroomLayout';
 import { AuthLayout } from '@/client/mobile/components/Classroom/AuthLayout';
 import { ClassroomProvider, useClassroomContext } from "@/client/mobile/components/Classroom/ClassroomProvider";
-import { TeacherView } from '@/client/mobile/components/Classroom/TeacherView';
-import { StudentView } from '@/client/mobile/components/Classroom/StudentView';
 import { Button } from '@/client/components/ui/button';
 import { Input } from '@/client/components/ui/input';
 import { Card, CardContent, CardHeader, CardTitle } from '@/client/components/ui/card';
@@ -195,7 +193,7 @@ const Classroom = () => {
 
   return (
     <ClassroomLayout role={role}>
-      {role === Role.Teacher ? <TeacherView /> : <StudentView />}
+      <></>
     </ClassroomLayout>
   );
 };