|
|
@@ -1,6 +1,6 @@
|
|
|
import React, { useState } from 'react';
|
|
|
import { useClassroomContext } from './ClassroomProvider';
|
|
|
-import { UserGroupIcon, XMarkIcon, EyeIcon, UsersIcon } from '@heroicons/react/24/outline';
|
|
|
+import { UserGroupIcon, EyeIcon, UsersIcon } from '@heroicons/react/24/outline';
|
|
|
import { Button } from '@/client/components/ui/button';
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/client/components/ui/card';
|
|
|
import {
|
|
|
@@ -13,6 +13,13 @@ import {
|
|
|
AlertDialogHeader,
|
|
|
AlertDialogTitle,
|
|
|
} from '@/client/components/ui/alert-dialog';
|
|
|
+import {
|
|
|
+ Dialog,
|
|
|
+ DialogContent,
|
|
|
+ DialogHeader,
|
|
|
+ DialogTitle,
|
|
|
+ DialogTrigger,
|
|
|
+} from '@/client/components/ui/dialog';
|
|
|
import { IMToggleMuteButton } from './IMToggleMuteButton';
|
|
|
import { RTCToggleMuteButton } from './RTCToggleMuteButton';
|
|
|
|
|
|
@@ -67,83 +74,74 @@ export const TeacherStudentManagementButton: React.FC = () => {
|
|
|
};
|
|
|
|
|
|
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">
|
|
|
- {/* 统计信息 */}
|
|
|
- <div className="flex justify-between mb-3 p-2 bg-gray-50 rounded text-sm">
|
|
|
- <div className="flex items-center">
|
|
|
- <UsersIcon className="w-4 h-4 mr-1 text-blue-500" />
|
|
|
- <span>在线: {onlineCount}人</span>
|
|
|
- </div>
|
|
|
- <div className="flex items-center">
|
|
|
- <EyeIcon className="w-4 h-4 mr-1 text-green-500" />
|
|
|
- <span>看过: {pvCount}人</span>
|
|
|
- </div>
|
|
|
+ <Dialog open={showPanel} onOpenChange={setShowPanel}>
|
|
|
+ <DialogTrigger asChild>
|
|
|
+ <Button
|
|
|
+ type="button"
|
|
|
+ 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>
|
|
|
+ </DialogTrigger>
|
|
|
+
|
|
|
+ <DialogContent className="sm:max-w-md">
|
|
|
+ <DialogHeader>
|
|
|
+ <DialogTitle className="flex items-center">
|
|
|
+ <UserGroupIcon className="w-4 h-4 mr-2 text-blue-500" />
|
|
|
+ 学生管理 ({students.length})
|
|
|
+ </DialogTitle>
|
|
|
+ </DialogHeader>
|
|
|
+
|
|
|
+ <Card className="border-0 shadow-none">
|
|
|
+ <CardContent className="p-0">
|
|
|
+ {/* 统计信息 */}
|
|
|
+ <div className="flex justify-between mb-3 p-2 bg-gray-50 rounded text-sm">
|
|
|
+ <div className="flex items-center">
|
|
|
+ <UsersIcon className="w-4 h-4 mr-1 text-blue-500" />
|
|
|
+ <span>在线: {onlineCount}人</span>
|
|
|
</div>
|
|
|
-
|
|
|
- <div className="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 flex-wrap">
|
|
|
- <RTCToggleMuteButton userId={student.id} userName={student.name} />
|
|
|
- <IMToggleMuteButton userId={student.id} userName={student.name} />
|
|
|
- <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>
|
|
|
- ))}
|
|
|
- </div>
|
|
|
- )}
|
|
|
+ <div className="flex items-center">
|
|
|
+ <EyeIcon className="w-4 h-4 mr-1 text-green-500" />
|
|
|
+ <span>看过: {pvCount}人</span>
|
|
|
</div>
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
- </div>
|
|
|
- )}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="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 flex-wrap">
|
|
|
+ <RTCToggleMuteButton userId={student.id} userName={student.name} />
|
|
|
+ <IMToggleMuteButton userId={student.id} userName={student.name} />
|
|
|
+ <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>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ </DialogContent>
|
|
|
|
|
|
<AlertDialog open={confirmDialog.open} onOpenChange={(open) => !open && handleCancel()}>
|
|
|
<AlertDialogContent>
|
|
|
@@ -161,6 +159,6 @@ export const TeacherStudentManagementButton: React.FC = () => {
|
|
|
</AlertDialogFooter>
|
|
|
</AlertDialogContent>
|
|
|
</AlertDialog>
|
|
|
- </div>
|
|
|
+ </Dialog>
|
|
|
);
|
|
|
};
|