|
|
@@ -37,127 +37,122 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
|
|
|
} = useClassroomContext();
|
|
|
|
|
|
return (
|
|
|
- <div className="flex flex-col h-screen bg-gray-100">
|
|
|
- <div className="flex-1 flex">
|
|
|
- {/* 视频区域 */}
|
|
|
- {showVideo && (
|
|
|
- <div className="flex-1 p-4">
|
|
|
- <div className="bg-white rounded-lg shadow p-4 h-full">
|
|
|
- <div
|
|
|
- id="remoteVideoContainer"
|
|
|
- ref={remoteVideoContainer}
|
|
|
- className="grid grid-cols-1 md:grid-cols-2 gap-4"
|
|
|
- >
|
|
|
- {/* 远程视频将在这里动态添加 */}
|
|
|
- </div>
|
|
|
+ <div className="flex flex-col md:flex-row h-screen bg-gray-100">
|
|
|
+ {/* 视频区域 */}
|
|
|
+ {showVideo && (
|
|
|
+ <div className="flex-1 p-4">
|
|
|
+ <div className="bg-white rounded-lg shadow p-4 h-full">
|
|
|
+ <div
|
|
|
+ id="remoteVideoContainer"
|
|
|
+ ref={remoteVideoContainer}
|
|
|
+ className="grid grid-cols-1 md:grid-cols-2 gap-4"
|
|
|
+ >
|
|
|
+ {/* 远程视频将在这里动态添加 */}
|
|
|
</div>
|
|
|
</div>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 消息和控制面板列 */}
|
|
|
- <div className={`${showVideo ? 'w-96' : 'flex-1'} flex flex-col`}>
|
|
|
-
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
|
|
|
- {/* 消息区域 */}
|
|
|
- <div className="bg-white shadow-lg p-4">
|
|
|
- <div className="h-full flex flex-col">
|
|
|
- {/* 消息列表 */}
|
|
|
- <div className="flex-1 overflow-y-auto mb-1">
|
|
|
- {messageList.map((msg, i) => (
|
|
|
- <div key={i} className="text-sm mb-1">{msg}</div>
|
|
|
- ))}
|
|
|
- </div>
|
|
|
+ {/* 消息和控制面板列 */}
|
|
|
+ <div className={`${showVideo ? 'w-full md:w-96' : 'flex-1'} flex flex-col`}>
|
|
|
+ {/* 消息区域 */}
|
|
|
+ <div className="bg-white shadow-lg p-4">
|
|
|
+ <div className="h-full flex flex-col">
|
|
|
+ {/* 消息列表 */}
|
|
|
+ <div className="flex-1 overflow-y-auto mb-1">
|
|
|
+ {messageList.map((msg, i) => (
|
|
|
+ <div key={i} className="text-sm mb-1">{msg}</div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
|
|
|
- {/* 控制面板 */}
|
|
|
- <div className="bg-white shadow-lg p-2 flex flex-col gap-3 mb-1 border-b border-gray-200">
|
|
|
- <div className="flex flex-wrap gap-2">
|
|
|
- {role === Role.Teacher && (
|
|
|
- <button
|
|
|
- onClick={() => setShowVideo(!showVideo)}
|
|
|
- className={`p-2 rounded-full ${showVideo ? 'bg-gray-500' : 'bg-gray-300'} text-white`}
|
|
|
- title={showVideo ? '隐藏视频' : '显示视频'}
|
|
|
- >
|
|
|
- <VideoCameraIcon className="w-4 h-4" />
|
|
|
- </button>
|
|
|
- )}
|
|
|
+ {/* 控制面板 */}
|
|
|
+ <div className="bg-white shadow-lg p-2 flex flex-col gap-3 mb-1 border-b border-gray-200">
|
|
|
+ <div className="flex flex-wrap gap-2">
|
|
|
+ {role === Role.Teacher && (
|
|
|
<button
|
|
|
- onClick={toggleCamera}
|
|
|
- className={`p-2 rounded-full ${isCameraOn ? 'bg-green-500' : 'bg-red-500'} text-white`}
|
|
|
- title={isCameraOn ? '关闭摄像头' : '开启摄像头'}
|
|
|
+ onClick={() => setShowVideo(!showVideo)}
|
|
|
+ className={`p-2 rounded-full ${showVideo ? 'bg-gray-500' : 'bg-gray-300'} text-white`}
|
|
|
+ title={showVideo ? '隐藏视频' : '显示视频'}
|
|
|
>
|
|
|
- <CameraIcon className="w-4 h-4" />
|
|
|
+ <VideoCameraIcon className="w-4 h-4" />
|
|
|
</button>
|
|
|
+ )}
|
|
|
+ <button
|
|
|
+ onClick={toggleCamera}
|
|
|
+ className={`p-2 rounded-full ${isCameraOn ? 'bg-green-500' : 'bg-red-500'} text-white`}
|
|
|
+ title={isCameraOn ? '关闭摄像头' : '开启摄像头'}
|
|
|
+ >
|
|
|
+ <CameraIcon className="w-4 h-4" />
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ onClick={toggleAudio}
|
|
|
+ className={`p-2 rounded-full ${isAudioOn ? 'bg-green-500' : 'bg-red-500'} text-white`}
|
|
|
+ title={isAudioOn ? '关闭麦克风' : '开启麦克风'}
|
|
|
+ >
|
|
|
+ <MicrophoneIcon className="w-4 h-4" />
|
|
|
+ </button>
|
|
|
+ {role === Role.Teacher && (
|
|
|
<button
|
|
|
- onClick={toggleAudio}
|
|
|
- className={`p-2 rounded-full ${isAudioOn ? 'bg-green-500' : 'bg-red-500'} text-white`}
|
|
|
- title={isAudioOn ? '关闭麦克风' : '开启麦克风'}
|
|
|
+ onClick={toggleScreenShare}
|
|
|
+ className={`p-2 rounded-full ${isScreenSharing ? 'bg-green-500' : 'bg-blue-500'} text-white`}
|
|
|
+ title={isScreenSharing ? '停止共享' : '共享屏幕'}
|
|
|
>
|
|
|
- <MicrophoneIcon className="w-4 h-4" />
|
|
|
+ <ShareIcon className="w-4 h-4" />
|
|
|
</button>
|
|
|
- {role === Role.Teacher && (
|
|
|
- <button
|
|
|
- onClick={toggleScreenShare}
|
|
|
- className={`p-2 rounded-full ${isScreenSharing ? 'bg-green-500' : 'bg-blue-500'} text-white`}
|
|
|
- title={isScreenSharing ? '停止共享' : '共享屏幕'}
|
|
|
- >
|
|
|
- <ShareIcon className="w-4 h-4" />
|
|
|
- </button>
|
|
|
- )}
|
|
|
- {role === Role.Teacher && shareLink && (
|
|
|
+ )}
|
|
|
+ {role === Role.Teacher && shareLink && (
|
|
|
+ <button
|
|
|
+ onClick={() => setShowShareLink(!showShareLink)}
|
|
|
+ className="p-2 rounded-full bg-blue-500 text-white"
|
|
|
+ title="分享链接"
|
|
|
+ >
|
|
|
+ <ClipboardDocumentIcon className="w-4 h-4" />
|
|
|
+ </button>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {showShareLink && shareLink && (
|
|
|
+ <div className="bg-blue-50 p-2 rounded">
|
|
|
+ <div className="flex items-center gap-1">
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ value={shareLink}
|
|
|
+ readOnly
|
|
|
+ className="flex-1 text-xs border rounded px-2 py-1 truncate"
|
|
|
+ />
|
|
|
<button
|
|
|
- onClick={() => setShowShareLink(!showShareLink)}
|
|
|
- className="p-2 rounded-full bg-blue-500 text-white"
|
|
|
- title="分享链接"
|
|
|
+ onClick={() => navigator.clipboard.writeText(shareLink)}
|
|
|
+ className="p-2 bg-blue-500 text-white rounded"
|
|
|
+ title="复制链接"
|
|
|
>
|
|
|
<ClipboardDocumentIcon className="w-4 h-4" />
|
|
|
</button>
|
|
|
- )}
|
|
|
- </div>
|
|
|
-
|
|
|
- {showShareLink && shareLink && (
|
|
|
- <div className="bg-blue-50 p-2 rounded">
|
|
|
- <div className="flex items-center gap-1">
|
|
|
- <input
|
|
|
- type="text"
|
|
|
- value={shareLink}
|
|
|
- readOnly
|
|
|
- className="flex-1 text-xs border rounded px-2 py-1 truncate"
|
|
|
- />
|
|
|
- <button
|
|
|
- onClick={() => navigator.clipboard.writeText(shareLink)}
|
|
|
- className="p-2 bg-blue-500 text-white rounded"
|
|
|
- title="复制链接"
|
|
|
- >
|
|
|
- <ClipboardDocumentIcon className="w-4 h-4" />
|
|
|
- </button>
|
|
|
- </div>
|
|
|
</div>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 角色特定内容 */}
|
|
|
- <div className="flex-1 overflow-y-auto">
|
|
|
- {children}
|
|
|
</div>
|
|
|
-
|
|
|
- </div>
|
|
|
- <div className="relative">
|
|
|
- <textarea
|
|
|
- value={msgText}
|
|
|
- onChange={(e) => setMsgText(e.target.value)}
|
|
|
- className="w-full border rounded px-2 py-1 pr-10"
|
|
|
- placeholder="输入消息..."
|
|
|
- rows={3}
|
|
|
- />
|
|
|
- <button
|
|
|
- onClick={sendMessage}
|
|
|
- className="absolute right-2 bottom-2 p-1 bg-blue-500 text-white rounded-full"
|
|
|
- >
|
|
|
- <PaperAirplaneIcon className="w-4 h-4" />
|
|
|
- </button>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 角色特定内容 */}
|
|
|
+ <div className="flex-1 overflow-y-auto">
|
|
|
+ {children}
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
+ <div className="relative">
|
|
|
+ <textarea
|
|
|
+ value={msgText}
|
|
|
+ onChange={(e) => setMsgText(e.target.value)}
|
|
|
+ className="w-full border rounded px-2 py-1 pr-10"
|
|
|
+ placeholder="输入消息..."
|
|
|
+ rows={3}
|
|
|
+ />
|
|
|
+ <button
|
|
|
+ onClick={sendMessage}
|
|
|
+ className="absolute right-2 bottom-2 p-1 bg-blue-500 text-white rounded-full"
|
|
|
+ >
|
|
|
+ <PaperAirplaneIcon className="w-4 h-4" />
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|