|
|
@@ -0,0 +1,109 @@
|
|
|
+import React, { useState } from 'react';
|
|
|
+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';
|
|
|
+
|
|
|
+export const TeacherStudentManagementButton: React.FC = () => {
|
|
|
+ const { students, toggleMuteMember, kickStudent } = useClassroomContext();
|
|
|
+ const [showPanel, setShowPanel] = useState(false);
|
|
|
+
|
|
|
+ const handleMuteStudent = (studentId: string, studentName: string) => {
|
|
|
+ if (window.confirm(`确定要静音 ${studentName} 吗?`)) {
|
|
|
+ toggleMuteMember(studentId, true);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleUnmuteStudent = (studentId: string, studentName: string) => {
|
|
|
+ if (window.confirm(`确定要取消静音 ${studentName} 吗?`)) {
|
|
|
+ toggleMuteMember(studentId, false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleKickStudent = (studentId: string, studentName: string) => {
|
|
|
+ if (window.confirm(`确定要移出 ${studentName} 吗?`)) {
|
|
|
+ kickStudent(studentId);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="relative">
|
|
|
+ <Button
|
|
|
+ type="button"
|
|
|
+ onClick={() => setShowPanel(!showPanel)}
|
|
|
+ className="p-2 rounded-full bg-blue-500 text-white relative"
|
|
|
+ title={`学生管理 (${students.length})`}
|
|
|
+ size="icon"
|
|
|
+ variant="ghost"
|
|
|
+ >
|
|
|
+ <UserGroupIcon className="w-4 h-4" />
|
|
|
+ {students.length > 0 && (
|
|
|
+ <span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center">
|
|
|
+ {students.length}
|
|
|
+ </span>
|
|
|
+ )}
|
|
|
+ </Button>
|
|
|
+
|
|
|
+ {showPanel && (
|
|
|
+ <div className="absolute bottom-full right-0 mb-2 w-80 bg-white shadow-lg rounded-lg z-50">
|
|
|
+ <Card className="border-0">
|
|
|
+ <CardHeader className="p-3 flex flex-row items-center justify-between">
|
|
|
+ <CardTitle className="text-sm flex items-center">
|
|
|
+ <UserGroupIcon className="w-4 h-4 mr-2 text-blue-500" />
|
|
|
+ 学生管理 ({students.length})
|
|
|
+ </CardTitle>
|
|
|
+ <Button
|
|
|
+ type="button"
|
|
|
+ onClick={() => setShowPanel(false)}
|
|
|
+ className="p-1 rounded-full"
|
|
|
+ size="icon"
|
|
|
+ variant="ghost"
|
|
|
+ >
|
|
|
+ <XMarkIcon className="w-4 h-4" />
|
|
|
+ </Button>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent className="p-3 max-h-60 overflow-y-auto">
|
|
|
+ {students.length === 0 ? (
|
|
|
+ <p className="text-gray-500 text-sm">暂无学生加入</p>
|
|
|
+ ) : (
|
|
|
+ <div className="space-y-2">
|
|
|
+ {students.map((student) => (
|
|
|
+ <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">
|
|
|
+ <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>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+};
|