Explorar o código

✨ feat(classroom): 添加移动端横屏模式支持

- 引入横屏切换功能,添加ArrowsPointingOut/InIcon图标
- 实现屏幕方向检测和自动适配逻辑
- 添加移动设备检测,非移动设备显示提示信息
- 优化视频区域布局,根据横竖屏状态动态调整尺寸
- 为移动设备添加横屏切换按钮,支持全屏和方向锁定
- 监听屏幕方向变化事件,确保UI正确响应方向切换
- 优化消息控制面板在横屏模式下的显示效果
yourname hai 6 meses
pai
achega
a7250b9582
Modificáronse 1 ficheiros con 93 adicións e 4 borrados
  1. 93 4
      src/client/mobile/components/Classroom/ClassroomLayout.tsx

+ 93 - 4
src/client/mobile/components/Classroom/ClassroomLayout.tsx

@@ -1,4 +1,4 @@
-import React, { ReactNode } from 'react';
+import React, { ReactNode, useEffect } from 'react';
 import { Role, type Message } from './useClassroom';
 import { useClassroomContext } from './ClassroomProvider';
 import {
@@ -8,6 +8,8 @@ import {
   ShareIcon,
   ClipboardDocumentIcon,
   PaperAirplaneIcon,
+  ArrowsPointingOutIcon,
+  ArrowsPointingInIcon,
 } from '@heroicons/react/24/outline';
 import { Button } from '@/client/components/ui/button';
 import { Textarea } from '@/client/components/ui/textarea';
@@ -30,6 +32,7 @@ interface ClassroomLayoutProps {
 export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
   const [showVideo, setShowVideo] = React.useState(role !== Role.Teacher);
   const [showShareLink, setShowShareLink] = React.useState(false);
+  const [isLandscape, setIsLandscape] = React.useState(false);
   const {
     remoteScreenContainer,
     remoteCameraContainer,
@@ -52,11 +55,79 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
     showToast
   } = useClassroomContext();
 
+  // 检测是否为移动设备
+  const isMobileDevice = () => {
+    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
+  };
+
+  // 横屏切换功能
+  const toggleLandscape = () => {
+    if (!isMobileDevice()) {
+      showToast('info', '横屏功能仅在移动设备上可用');
+      return;
+    }
+
+    setIsLandscape(!isLandscape);
+    
+    if (!isLandscape) {
+      // 进入横屏模式
+      if (document.documentElement.requestFullscreen) {
+        document.documentElement.requestFullscreen();
+      }
+      // 尝试横屏旋转(需要设备支持)
+      // @ts-ignore
+      if (screen.orientation && screen.orientation.lock) {
+      // @ts-ignore
+        screen.orientation.lock('landscape').catch(() => {
+          showToast('info', '您的设备不支持自动横屏,请手动旋转设备');
+        });
+      }
+    } else {
+      // 退出横屏模式
+      if (document.exitFullscreen) {
+        document.exitFullscreen();
+      }
+      if (screen.orientation && screen.orientation.unlock) {
+        screen.orientation.unlock();
+      }
+    }
+  };
+
+  // 监听屏幕方向变化
+  useEffect(() => {
+    const handleOrientationChange = () => {
+      if (screen.orientation) {
+        setIsLandscape(screen.orientation.type.includes('landscape'));
+      } else {
+        // 备用方案:通过窗口尺寸判断
+        setIsLandscape(window.innerWidth > window.innerHeight);
+      }
+    };
+
+    if (screen.orientation) {
+      screen.orientation.addEventListener('change', handleOrientationChange);
+    } else {
+      // 备用方案:监听窗口尺寸变化
+      window.addEventListener('resize', handleOrientationChange);
+    }
+
+    // 初始检查
+    handleOrientationChange();
+
+    return () => {
+      if (screen.orientation) {
+        screen.orientation.removeEventListener('change', handleOrientationChange);
+      } else {
+        window.removeEventListener('resize', handleOrientationChange);
+      }
+    };
+  }, []);
+
   return (
-    <div className="flex flex-col md:flex-row h-screen bg-gray-100">
+    <div className={`flex ${isLandscape ? 'flex-row' : 'flex-col'} md:flex-row h-screen bg-gray-100`}>
       {/* 视频区域 */}
       {showVideo && (
-        <div className="relative h-[300px] md:flex-[3] md:h-auto bg-black">
+        <div className={`relative ${isLandscape ? 'flex-[3] h-auto' : 'h-[300px]'} md:flex-[3] md:h-auto bg-black`}>
           {/* 主屏幕共享容器 */}
           <div
             id="remoteScreenContainer"
@@ -91,7 +162,7 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
       )}
 
       {/* 消息和控制面板列 */}
-      <div className={`${showVideo ? 'w-full flex-1 md:w-96 md:flex-none' : 'flex-1'} flex flex-col`}>
+      <div className={`${showVideo ? `${isLandscape ? 'w-96 flex-none' : 'w-full flex-1'}` : 'flex-1'} md:w-96 md:flex-none flex flex-col ${isLandscape ? 'max-h-screen overflow-y-auto' : ''}`}>
         {/* 消息区域 */}
         <div className="flex flex-col h-full">
           {/* 消息列表 - 填充剩余空间 */}
@@ -160,6 +231,24 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
                 </Button>
               )}
 
+              {/* 横屏按钮 - 仅在移动设备上显示 */}
+              {isMobileDevice() && (
+                <Button
+                  type="button"
+                  onClick={toggleLandscape}
+                  className={`p-2 rounded-full ${isLandscape ? 'bg-green-500' : 'bg-blue-500'} text-white touch-manipulation`}
+                  title={isLandscape ? '退出横屏' : '进入横屏'}
+                  size="icon"
+                  variant="ghost"
+                >
+                  {isLandscape ? (
+                    <ArrowsPointingInIcon className="w-4 h-4" />
+                  ) : (
+                    <ArrowsPointingOutIcon className="w-4 h-4" />
+                  )}
+                </Button>
+              )}
+
               {/* 图片选择按钮 */}
               <ImageSelectorButton />