|
@@ -1,5 +1,5 @@
|
|
|
import { useState, useEffect, useRef } from 'react';
|
|
import { useState, useEffect, useRef } from 'react';
|
|
|
-import { useParams } from 'react-router';
|
|
|
|
|
|
|
+import { useParams, useSearchParams } from 'react-router';
|
|
|
// import { ClassroomAPI } from '../../api/index.ts';
|
|
// import { ClassroomAPI } from '../../api/index.ts';
|
|
|
// @ts-types="../../../share/aliyun-rtc-sdk.d.ts"
|
|
// @ts-types="../../../share/aliyun-rtc-sdk.d.ts"
|
|
|
// @ts-types="./alivc-im.iife.d.ts"
|
|
// @ts-types="./alivc-im.iife.d.ts"
|
|
@@ -84,12 +84,14 @@ export interface Message {
|
|
|
export const useClassroom = ({ user }:{ user : User }) => {
|
|
export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
// 状态管理
|
|
// 状态管理
|
|
|
// const [userId, setUserId] = useState<string>(''); // 保持string类型
|
|
// const [userId, setUserId] = useState<string>(''); // 保持string类型
|
|
|
|
|
+
|
|
|
|
|
+ const [searchParams] = useSearchParams();
|
|
|
const userId = user.id.toString();
|
|
const userId = user.id.toString();
|
|
|
const [isCameraOn, setIsCameraOn] = useState<boolean>(false);
|
|
const [isCameraOn, setIsCameraOn] = useState<boolean>(false);
|
|
|
const [isAudioOn, setIsAudioOn] = useState<boolean>(false);
|
|
const [isAudioOn, setIsAudioOn] = useState<boolean>(false);
|
|
|
const [isScreenSharing, setIsScreenSharing] = useState<boolean>(false);
|
|
const [isScreenSharing, setIsScreenSharing] = useState<boolean>(false);
|
|
|
const [className, setClassName] = useState<string>('');
|
|
const [className, setClassName] = useState<string>('');
|
|
|
- const [role, setRole] = useState<Role | undefined>();
|
|
|
|
|
|
|
+ const [role, setRole] = useState<Role>(searchParams.get('role') === Role.Teacher ? Role.Teacher : Role.Student);
|
|
|
const [classId, setClassId] = useState<string>('');
|
|
const [classId, setClassId] = useState<string>('');
|
|
|
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
|
|
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
|
|
|
const [isJoinedClass, setIsJoinedClass] = useState<boolean>(false);
|
|
const [isJoinedClass, setIsJoinedClass] = useState<boolean>(false);
|
|
@@ -98,6 +100,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
const [errorMessage, setErrorMessage] = useState<string>('');
|
|
const [errorMessage, setErrorMessage] = useState<string>('');
|
|
|
const [classStatus, setClassStatus] = useState<ClassStatus>(ClassStatus.NOT_STARTED);
|
|
const [classStatus, setClassStatus] = useState<ClassStatus>(ClassStatus.NOT_STARTED);
|
|
|
const [handUpList, setHandUpList] = useState<HandUpRequest[]>([]);
|
|
const [handUpList, setHandUpList] = useState<HandUpRequest[]>([]);
|
|
|
|
|
+ const [isPrivateClass, setIsPrivateClass] = useState<boolean>(false);
|
|
|
const [questions, setQuestions] = useState<Question[]>([]);
|
|
const [questions, setQuestions] = useState<Question[]>([]);
|
|
|
const [students, setStudents] = useState<Array<{id: string, name: string}>>([]);
|
|
const [students, setStudents] = useState<Array<{id: string, name: string}>>([]);
|
|
|
const [shareLink, setShareLink] = useState<string>('');
|
|
const [shareLink, setShareLink] = useState<string>('');
|
|
@@ -601,6 +604,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
} catch (err: any) {
|
|
} catch (err: any) {
|
|
|
setErrorMessage(`登录失败: ${err.message}`);
|
|
setErrorMessage(`登录失败: ${err.message}`);
|
|
|
showToast('error', '登录失败');
|
|
showToast('error', '登录失败');
|
|
|
|
|
+ console.error('登录失败', err)
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -612,7 +616,49 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
const mm = imEngine.current.getMessageManager();
|
|
const mm = imEngine.current.getMessageManager();
|
|
|
imGroupManager.current = gm || null;
|
|
imGroupManager.current = gm || null;
|
|
|
imMessageManager.current = mm || null;
|
|
imMessageManager.current = mm || null;
|
|
|
|
|
+
|
|
|
|
|
+ // 得先加入群组才能获取到群组信息
|
|
|
await gm!.joinGroup(classId);
|
|
await gm!.joinGroup(classId);
|
|
|
|
|
+
|
|
|
|
|
+ // 先获取群组信息并检查闭门标识
|
|
|
|
|
+ let groupInfo;
|
|
|
|
|
+ let hasPermission = true; // 权限检查标志
|
|
|
|
|
+ try {
|
|
|
|
|
+ groupInfo = await gm!.queryGroup(classId);
|
|
|
|
|
+
|
|
|
|
|
+ // 检查闭门课堂权限
|
|
|
|
|
+ if (groupInfo && groupInfo.groupMeta) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const meta = JSON.parse(groupInfo.groupMeta);
|
|
|
|
|
+ if (meta.isPrivate) {
|
|
|
|
|
+ // 设置闭门标识状态
|
|
|
|
|
+ setIsPrivateClass(true);
|
|
|
|
|
+ // 闭门课堂,允许老师和学员身份进入
|
|
|
|
|
+ console.log('user.userType', user.userType)
|
|
|
|
|
+ if (user.userType !== UserType.TRAINEE && user.userType !== UserType.TEACHER) {
|
|
|
|
|
+ // 不抛出错误,而是设置错误状态并阻止后续操作
|
|
|
|
|
+ setErrorMessage('该课堂为闭门会议,只有老师和学员身份可以进入');
|
|
|
|
|
+ setIsPrivateClass(true);
|
|
|
|
|
+ hasPermission = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setIsPrivateClass(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (parseError) {
|
|
|
|
|
+ console.error('解析群组元数据失败:', parseError);
|
|
|
|
|
+ setIsPrivateClass(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error('获取群组信息失败:', err);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有权限,抛出错误统一处理
|
|
|
|
|
+ if (!hasPermission) {
|
|
|
|
|
+ console.log('该课堂为闭门会议,只有老师和学员身份可以进入')
|
|
|
|
|
+ throw new Error('该课堂为闭门会议,只有老师和学员身份可以进入');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
listenGroupEvents();
|
|
listenGroupEvents();
|
|
|
listenMessageEvents();
|
|
listenMessageEvents();
|
|
|
|
|
|
|
@@ -648,17 +694,11 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 获取群组统计信息
|
|
// 获取群组统计信息
|
|
|
- try {
|
|
|
|
|
- const groupInfo = await gm!.queryGroup(classId);
|
|
|
|
|
- if (groupInfo && groupInfo.statistics) {
|
|
|
|
|
- setOnlineCount(groupInfo.statistics.onlineCount || 0);
|
|
|
|
|
- setPvCount(groupInfo.statistics.pv || 0);
|
|
|
|
|
- console.log('群组统计信息:', groupInfo.statistics);
|
|
|
|
|
- }
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- console.error('获取群组统计信息失败:', err);
|
|
|
|
|
|
|
+ if (groupInfo && groupInfo.statistics) {
|
|
|
|
|
+ setOnlineCount(groupInfo.statistics.onlineCount || 0);
|
|
|
|
|
+ setPvCount(groupInfo.statistics.pv || 0);
|
|
|
|
|
+ console.log('群组统计信息:', groupInfo.statistics);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
await joinRtcChannel(classId);
|
|
await joinRtcChannel(classId);
|
|
|
|
|
|
|
|
buildShareLink(classId)
|
|
buildShareLink(classId)
|
|
@@ -674,6 +714,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
} else {
|
|
} else {
|
|
|
setErrorMessage(`加入课堂失败: ${err.message}`);
|
|
setErrorMessage(`加入课堂失败: ${err.message}`);
|
|
|
showToast('error', '加入课堂失败');
|
|
showToast('error', '加入课堂失败');
|
|
|
|
|
+ console.error('加入课堂失败', err)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (imGroupManager.current) {
|
|
if (imGroupManager.current) {
|
|
@@ -855,7 +896,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
setShareLink(`${baseUrl}/mobile/classroom/${classId}/student`);
|
|
setShareLink(`${baseUrl}/mobile/classroom/${classId}/student`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const createClass = async (className: string, maxMembers = 200): Promise<string | null> => {
|
|
|
|
|
|
|
+ const createClass = async (className: string, maxMembers = 200, isPrivate = false): Promise<string | null> => {
|
|
|
if (!imEngine.current || !isLoggedIn || role !== Role.Teacher) {
|
|
if (!imEngine.current || !isLoggedIn || role !== Role.Teacher) {
|
|
|
showToast('error', '只有老师可以创建课堂');
|
|
showToast('error', '只有老师可以创建课堂');
|
|
|
return null;
|
|
return null;
|
|
@@ -867,7 +908,10 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
throw new Error('群组管理器未初始化');
|
|
throw new Error('群组管理器未初始化');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- showToast('info', '正在创建课堂...');
|
|
|
|
|
|
|
+ if(isPrivate)
|
|
|
|
|
+ showToast('info', '正在创建闭门课堂...');
|
|
|
|
|
+ else
|
|
|
|
|
+ showToast('info', '正在创建公开课堂...');
|
|
|
|
|
|
|
|
const response = await gm.createGroup({
|
|
const response = await gm.createGroup({
|
|
|
groupName: className,
|
|
groupName: className,
|
|
@@ -875,7 +919,8 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
classType: 'interactive',
|
|
classType: 'interactive',
|
|
|
creator: userId,
|
|
creator: userId,
|
|
|
createdAt: Date.now(),
|
|
createdAt: Date.now(),
|
|
|
- maxMembers
|
|
|
|
|
|
|
+ maxMembers,
|
|
|
|
|
+ isPrivate: isPrivate
|
|
|
})
|
|
})
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -1315,6 +1360,7 @@ export const useClassroom = ({ user }:{ user : User }) => {
|
|
|
questions,
|
|
questions,
|
|
|
students,
|
|
students,
|
|
|
shareLink,
|
|
shareLink,
|
|
|
|
|
+ isPrivateClass,
|
|
|
remoteScreenContainer, // 重命名为remoteScreenContainer
|
|
remoteScreenContainer, // 重命名为remoteScreenContainer
|
|
|
remoteCameraContainer, // 导出摄像头容器ref
|
|
remoteCameraContainer, // 导出摄像头容器ref
|
|
|
showCameraOverlay,
|
|
showCameraOverlay,
|