Преглед изворни кода

统一使用share/types.ts中的User类型
修改了ClassroomProvider.tsx、useClassroom.ts和pages_classroom.tsx
消除了类型不一致问题,提高了代码可维护性

yourname пре 7 месеци
родитељ
комит
06e7042b12

+ 8 - 0
client/mobile/components/Classroom/ClassroomLayout.tsx

@@ -66,6 +66,7 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
           
           {/* 摄像头小窗开关按钮 */}
           <button
+            type="button"
             onClick={() => setShowCameraOverlay(!showCameraOverlay)}
             className={`absolute top-4 right-4 z-20 p-2 rounded-full ${
               showCameraOverlay ? 'bg-green-500' : 'bg-gray-500'
@@ -96,6 +97,7 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
               <div className="flex flex-wrap gap-2">
                 {role === Role.Teacher && (
                   <button
+                    type="button"
                     onClick={() => setShowVideo(!showVideo)}
                     className={`p-2 rounded-full ${showVideo ? 'bg-gray-500' : 'bg-gray-300'} text-white`}
                     title={showVideo ? '隐藏视频' : '显示视频'}
@@ -104,6 +106,7 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
                   </button>
                 )}
                 <button
+                  type="button"
                   onClick={toggleCamera}
                   className={`p-2 rounded-full ${isCameraOn ? 'bg-green-500' : 'bg-red-500'} text-white`}
                   title={isCameraOn ? '关闭摄像头' : '开启摄像头'}
@@ -111,6 +114,7 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
                   <CameraIcon className="w-4 h-4" />
                 </button>
                 <button
+                  type="button"
                   onClick={toggleAudio}
                   className={`p-2 rounded-full ${isAudioOn ? 'bg-green-500' : 'bg-red-500'} text-white`}
                   title={isAudioOn ? '关闭麦克风' : '开启麦克风'}
@@ -119,6 +123,7 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
                 </button>
                 {role === Role.Teacher && (
                   <button
+                    type="button"
                     onClick={toggleScreenShare}
                     className={`p-2 rounded-full ${isScreenSharing ? 'bg-green-500' : 'bg-blue-500'} text-white`}
                     title={isScreenSharing ? '停止共享' : '共享屏幕'}
@@ -128,6 +133,7 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
                 )}
                 {role === Role.Teacher && shareLink && (
                   <button
+                    type="button"
                     onClick={() => setShowShareLink(!showShareLink)}
                     className="p-2 rounded-full bg-blue-500 text-white"
                     title="分享链接"
@@ -147,6 +153,7 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
                       className="flex-1 text-xs border rounded px-2 py-1 truncate"
                     />
                     <button
+                      type="button"
                       onClick={() => navigator.clipboard.writeText(shareLink)}
                       className="p-2 bg-blue-500 text-white rounded"
                       title="复制链接"
@@ -179,6 +186,7 @@ export const ClassroomLayout = ({ children, role }: ClassroomLayoutProps) => {
                 rows={3}
               />
               <button
+                type="button"
                 onClick={sendMessage}
                 className="absolute right-2 bottom-2 p-1 bg-blue-500 text-white rounded-full"
               >

+ 5 - 3
client/mobile/components/Classroom/ClassroomProvider.tsx

@@ -1,13 +1,15 @@
-import React, { useState, useEffect, useRef, createContext, useContext } from 'react';
+import React, { useState, useEffect, useRef, createContext, useContext, useMemo } from 'react';
 import { useParams } from 'react-router';
 import { useClassroom , Role } from './useClassroom.ts';
+import { User } from '../../../share/types.ts';
 
 type ClassroomContextType = ReturnType<typeof useClassroom>;
 
 const ClassroomContext = createContext<ClassroomContextType | null>(null);
 
-export const ClassroomProvider: React.FC<{children: React.ReactNode}> = ({ children }) => {
-  const classroom = useClassroom();
+export const ClassroomProvider: React.FC<{children: React.ReactNode, user: User}> = ({ children, user }) => {
+  const classroom = useClassroom({ user });
+
   const { id: classId, role: pathRole } = useParams();
 
   useEffect(() => {

+ 11 - 7
client/mobile/components/Classroom/useClassroom.ts

@@ -1,5 +1,6 @@
 import { useState, useEffect, useRef } from 'react';
 import { useParams } from 'react-router';
+import { User } from '../../../share/types.ts';
 // @ts-types="../../../share/aliyun-rtc-sdk.d.ts"
 import AliRtcEngine, { AliRtcSubscribeState, AliRtcVideoTrack } from 'aliyun-rtc-sdk';
 import { toast } from 'react-toastify';
@@ -73,9 +74,10 @@ const IM_APP_SIGN = 'H4sIAAAAAAAE/wCQAG//zguHB+lYCilkv7diSkk4GmcvLuds+InRu9vFOFe
 const RTC_APP_ID = 'a5842c2a-d94a-43be-81de-1fdb712476e1';
 const RTC_APP_KEY = 'b71d65f4f84c450f6f058f4ad507bd42';
 
-export const useClassroom = () => {
+export const useClassroom = ({ user }:{ user : User }) => {
   // 状态管理
-  const [userId, setUserId] = useState<string>('');
+  // const [userId, setUserId] = useState<string>(''); // 保持string类型
+  const userId = user.id.toString();
   const [isCameraOn, setIsCameraOn] = useState<boolean>(false);
   const [isAudioOn, setIsAudioOn] = useState<boolean>(false);
   const [isScreenSharing, setIsScreenSharing] = useState<boolean>(false);
@@ -165,7 +167,7 @@ export const useClassroom = () => {
         await imEngine.current!.login({
           user: {
             userId,
-            userExtension: '{}'
+            userExtension: JSON.stringify(user)
           },
           userAuth: {
             nonce: 'AK_4',
@@ -265,7 +267,10 @@ export const useClassroom = () => {
           console.error('解析应答消息失败', err);
         }
       } else if (msg.type === 88888) { // 普通文本消息
-        showMessage(`${msg.sender?.userId || '未知用户'}: ${msg.data}`);
+        const sender = msg.sender;
+        const userExtension = JSON.parse(sender?.userExtension || '{}') as User;
+        const senderName = userExtension.nickname || userExtension.username;
+        showMessage(`${ senderName || '未知用户' }: ${msg.data}`);
       }
     });
   };
@@ -429,7 +434,7 @@ export const useClassroom = () => {
   };
 
   // 课堂操作方法
-  const login = async (userId: string, role: Role): Promise<void> => {
+  const login = async (role: Role): Promise<void> => {
     if(!role) {
       showToast('error', '角色不存在');
       return;
@@ -449,7 +454,7 @@ export const useClassroom = () => {
       await imEngine.current.login({
         user: {
           userId,
-          userExtension: '{}'
+          userExtension: JSON.stringify({ nickname: user?.nickname || user?.username || '' })
         },
         userAuth: {
           nonce: 'AK_4',
@@ -905,7 +910,6 @@ export const useClassroom = () => {
   return {
     // 状态
     userId,
-    setUserId,
     isCameraOn,
     isAudioOn,
     isScreenSharing,

+ 12 - 16
client/mobile/pages_classroom.tsx

@@ -2,8 +2,8 @@ import React, { useState, useEffect } from 'react';
 import { useAuth } from './hooks.tsx';
 import { useNavigate } from 'react-router';
 import { Role, ClassStatus } from './components/Classroom/useClassroom.ts';
-import { TeacherView } from './components/Classroom/TeacherView.tsx';
-import { StudentView } from './components/Classroom/StudentView.tsx';
+// import { TeacherView } from './components/Classroom/TeacherView.tsx';
+// import { StudentView } from './components/Classroom/StudentView.tsx';
 import { ClassroomLayout } from './components/Classroom/ClassroomLayout.tsx';
 import { AuthLayout } from './components/Classroom/AuthLayout.tsx';
 import { ClassroomProvider, useClassroomContext } from "./components/Classroom/ClassroomProvider.tsx";
@@ -11,12 +11,10 @@ import { ToastContainer } from 'react-toastify';
 
 const RoleSelection = () => {
   const { setRole, login } = useClassroomContext();
-  const { user } = useAuth();
   const chooseRole = (role: Role) => {
-    if(!user) return;
 
     setRole(role);
-    login(user.id.toString(), role);
+    login(role);
   }
   
   return (
@@ -63,6 +61,7 @@ const JoinClassSection = () => {
           className="flex-1 px-3 py-2 border rounded"
         />
         <button
+          type="button"
           onClick={handleJoinClass}
           className="px-4 py-2 bg-blue-500 text-white rounded"
         >
@@ -99,6 +98,7 @@ const CreateClassSection = () => {
           className="flex-1 px-3 py-2 border rounded"
         />
         <button
+          type="button"
           onClick={handleCreateClass}
           className="px-4 py-2 bg-green-500 text-white rounded"
         >
@@ -113,23 +113,17 @@ const CreateClassSection = () => {
 const Classroom = () => {
   const context = useClassroomContext();
   const { role, classStatus, isLoggedIn, login, classId, joinClass } = context;
-  const { user, isAuthenticated } = useAuth();
-
-  useEffect(() => {
-    if(user)
-      context.setUserId(user?.id.toString())
-  },[ user ])
 
   useEffect(() => {
     
-    if (isAuthenticated && user && !isLoggedIn && role && classId) {
+    if (!isLoggedIn && role && classId) {
       (async () => {
-        await login(user.id.toString(), role);
+        await login(role);
         await joinClass(classId);
       })()
       
     }
-  }, [isAuthenticated, user, isLoggedIn, role , classId]);
+  }, [isLoggedIn, role , classId]);
 
   if (!role) {
     return (
@@ -180,15 +174,17 @@ const Classroom = () => {
 
   return (
     <ClassroomLayout role={role}>
-      {role === Role.Teacher ? <TeacherView /> : <StudentView />}
+      {/* {role === Role.Teacher ? <TeacherView /> : <StudentView />} */}
+      <></>
     </ClassroomLayout>
   );
 };
 
 export const ClassroomPage = () => {
+  const { user } = useAuth();
   return (
     <>
-      <ClassroomProvider>
+      <ClassroomProvider user={user!}>
         <Classroom />
       </ClassroomProvider>
       <ToastContainer

+ 5 - 1
版本迭代需求.md

@@ -1,6 +1,10 @@
 2025.05.13 0.1.0
 在移动端加入课堂入口
+
 排查课堂选择角色后,一直停留在自动登录的原因
+
 优化课堂视频区域显示的布局,主体显示对方屏幕共享,对方摄像头的缩小叠加显示在右上角或右下角,增加开关控制对方摄像头的显示与否
 要分开对方摄像头,和对方屏幕共享的显示容器,现在是都混在了 id="remoteVideoContainer"
-remoteCameraContainer ref已正确引入ClassroomLayout, 统一命名remoteVideoContainer为remoteScreenContainer, 所有相关引用同步更新
+remoteCameraContainer ref已正确引入ClassroomLayout, 统一命名remoteVideoContainer为remoteScreenContainer, 所有相关引用同步更新
+
+增强userExtension,im登录时,将当前登录用户的昵称也传入