import styles from '@/page/Meeting/index.module.scss' import { useEffect, useRef, useState } from "react"; import Operation from '@/components/Operation'; import SpeakerModeModal from '@/components/SpeakerModeModal'; import InvitingPersonnelModal from '@/components/InvitingPersonnelModal'; import { Button, Input, Popover, Modal, Checkbox, message, Popconfirm, notification } from "antd"; import { SearchOutlined, EllipsisOutlined, ExclamationCircleFilled, FullscreenExitOutlined, FullscreenOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import { useLocation, useNavigate } from 'react-router-dom'; import { thumbImageBufferToBase64 } from '@/utils/package/base64' import { storage } from '@/utils'; import { GetRoomUser, PostOpenMicr, PostOpenCamera, GetLeaveAll, PostRoomManager, DeleteRoomManager, GetRoomKickout, GetShowUser, PostShowUser, PostMuteAll, GetRoomUserItem, GetApplySpeak } from '@/api/Meeting'; import ImageUrl from '@/utils/package/imageUrl' import { agora } from '@/utils/package/agora' import { onInvoke, onSignalr, offSignalr } from '@/utils/package/signalr'; import dayjs from 'dayjs'; import durationPlugin from 'dayjs/plugin/duration'; import { AudioVolumeInfo, ConnectionChangedReasonType, ConnectionStateType, LocalVideoStreamReason, LocalVideoStreamState, QualityType, RtcConnection, RtcStats, UserOfflineReasonType, VideoSourceType, VideoStreamType } from 'agora-electron-sdk'; import Avatar from '@/components/Avatar'; import SharedFilesModel from '@/components/SharedFilesModel'; import StupWizard from '@/components/StupWizard'; import EquipmentManagement from '@/components/EquipmentManagement'; import UserVideo from '@/components/UserVideo'; import { role } from '@/config/role'; import { fixWebmDuration } from "webm-duration-fix-buffer"; import { getKeyOpenChildWindow, setKeyOpenChildWindow } from '@/utils/package/public'; const { confirm } = Modal; const { exec } = require('child_process'); const fs = require('fs').promises; dayjs.extend(durationPlugin); const Meeting: React.FC = () => { const navigate = useNavigate(); const { state } = useLocation(); const speakerModeModalRef = useRef(); const sharedFilesModelRef = useRef(); const invitingPersonnelRef = useRef(); const stupWizardRef = useRef(); const equipmentManagementRef = useRef(); const [isClicked, setIsClicked] = useState(false); const [statusList, setStatusList] = useState({ userList: false, userChatList: false, userVideo: false, }) const [isSharedScreenModal, setIsSharedScreenModal] = useState(false); const [quitMeetingModal, setQuitMeetingModal] = useState(false); const [user, setUser] = useState({}); const [sharedScreenList, setSharedScreenList] = useState([]); const [sharedScreenItem, setSharedScreenItem] = useState(''); const [textMsg, setTextMsg] = useState(''); const [footerList, setFooterList] = useState([ [ { title: '解除静音', icon: ImageUrl.icon22, iconActive: ImageUrl.icon22Active, active: true, select: false, }, { title: '开启视频', icon: ImageUrl.icon23, iconActive: ImageUrl.icon23Active, active: true, select: false, }, { title: '申请发言', icon: ImageUrl.icon47, iconActive: ImageUrl.icon47Active, active: false, select: false, }, ], [ { title: '共享屏幕', icon: ImageUrl.icon24, iconSelect: ImageUrl.icon24Select, active: false, select: false, }, { title: '共享文件', icon: ImageUrl.icon25, iconSelect: ImageUrl.icon25Select, active: false, select: false, }, { title: '邀请人员', icon: ImageUrl.icon26, iconSelect: ImageUrl.icon26Select, active: false, select: false, }, { title: '录制', icon: ImageUrl.icon27, iconSelect: ImageUrl.icon27Select, iconActive: ImageUrl.icon27Active, active: false, select: false, }, { title: '设置', icon: ImageUrl.icon28, iconSelect: ImageUrl.icon28Select, active: false, select: false, }, { title: '结束', icon: ImageUrl.icon29, iconSelect: ImageUrl.icon29Select, active: false, select: false, }, ], [{ title: '会议监控', icon: ImageUrl.icon48, iconSelect: ImageUrl.icon48Select, active: false, select: false, }, { title: '成员列表', icon: ImageUrl.icon30, iconSelect: ImageUrl.icon30Select, active: false, select: false, }, { title: '聊天', icon: ImageUrl.icon31, iconSelect: ImageUrl.icon31Select, active: false, select: false, }, ], ]) const [footerListIndex, setFooterListIndex] = useState({ itemIndex: 0, rowIndex: 0, }); const [roomUserList, setRoomUserList] = useState([]) const [chatList, setChatList] = useState([]) const [currentVideoId, setCurrentVideoId] = useState('') let [currentSeconds, setCurrentSeconds] = useState(0) const [currentEffective, setCurrentEffective] = useState(4) const [networkQuality, setNetworkQuality] = useState({ level: '佳', text: '网络质量极好。' }) const [networkOther, setNetworkOther] = useState({}) const [isComputerAudio, setIsComputerAudio] = useState(false) const [_isScreenCapture, setIsScreenCapture] = useState(false) const [isFluencyPriority, setIsFluencyPriority] = useState(false) const [open, setOpen] = useState(false) const [modeOpen, setModeOpen] = useState(false) const [meetingMode, setMeetingMode] = useState('') const [userSearchValue, setUserSearchValue] = useState('') const [noViewChatList, setNoViewChatList] = useState(0) const [currentLookUserAccount, setCurrentLookUserAccount] = useState('') const [recorder, setRecorder] = useState('') const [_currentRequestSpeakType, setCurrentRequestSpeakType] = useState<'video' | 'audio' | ''>('') const [_mediaStream, setMediaStream] = useState('') const [isShare, setIsShare] = useState(null) const [isSharePopConfirm, setIsSharePopConfirm] = useState(false) const [isShareUser, setIsShareUser] = useState(null) const [currentLookUserStatus, setCurrentLookUserStatus] = useState<0 | 1 | 2 | 3 | 4>(1) const [clickCurrentLookUserStatus, setClickCurrentLookUserStatus] = useState(true) const [commonlyChatList] = useState([ '能听到我说话吗?', '听得到', '听不到', '我要发言', ]) const [roomUserItem, setRoomUserItem] = useState(null) const [isAdmin, setIsAdmin] = useState(0) const [api, contextHolder] = notification.useNotification({ stack: { threshold: 3 } }); const [isVideoFullScreen, setIsVideoFullScreen] = useState(false) const [observer, setObserver] = useState() let userInfo = JSON.parse(storage.getItem('user') as string) const msgTips = '您不是管理员或发言人,无法开启此功能!' const channel = new BroadcastChannel('meeting_channel'); useEffect(() => { let time: NodeJS.Timeout; setUser(userInfo) window.electron.getIsMaximized().then((res: boolean) => { if (!res) { window.electron.setViewStatus('maximize') } }) setKeyOpenChildWindow('shareScreenWindow', false) setMeetingMode('StandardMode'); agoraInit() storage.setItem('noViewChatList', 0) window.addEventListener('customStorageChange', handleCustomStorageChange); const container = document.getElementById('videoView') as HTMLElement; container.addEventListener('wheel', handleWheelChange); channel.onmessage = async function (event) { const { type, shareScreenWindowfooterListsTitle, userListWindowPostOpenMicr, userListWindowPostOpenCamera, userListWindowDeleteRoomManager, userListWindowPostRoomManager, userListWindowGetRoomKickout, userListWindowEquipmentManagement, userListWindowSetEquipmentManagement, chatSmallWindowSendChannelMsg, chatBigWindowSetAllUserLook, chatBigWindowDeleteRoomManager, chatBigWindowPostRoomManager, chatBigWindowPostOpenMicr, chatBigWindowPostOpenCamera, chatBigWindowGetRoomKickout, chatBigWindowSendChannelMsg, noticeWindowPostRoomManager } = event.data; switch (type) { case 'shareScreenWindowGetTime': setCurrentSeconds((res => { channel.postMessage({ type: 'time', time: res, }); return res })) break; case 'shareScreenWindowClose': await stopScreenCapture() await allUserLook(userInfo.uid, userInfo.userName) break; case 'shareScreenWindowfooterListsTitle': switch (shareScreenWindowfooterListsTitle) { case '静音': case '解除静音': changeStatusList({ title: shareScreenWindowfooterListsTitle }, 0, 1) break; case '关闭视频': case '开启视频': changeStatusList({ title: shareScreenWindowfooterListsTitle }, 0, 2) break; case '录制': case '录制中': changeStatusList({ title: shareScreenWindowfooterListsTitle }, 1, 3) break; } break; case 'shareScreenWindowGetFooterLists': setFooterList((res: any) => { channel.postMessage({ type: 'footerList', footerList: res, }); return res }) break; case 'userListWindowEquipmentManagement': await onInvoke('getDrivers', { uid: userListWindowEquipmentManagement.uid, }) break; case 'userListWindowSetEquipmentManagement': await onInvoke('setDrivers', { uid: userListWindowSetEquipmentManagement.uid, driversJsonString: userListWindowSetEquipmentManagement.driversJsonString }) break; case 'userListWindowPostOpenMicr': postOpenMicr(userListWindowPostOpenMicr.enableMicr, userListWindowPostOpenMicr.uid) break; case 'userListWindowPostOpenCamera': postOpenCamera(userListWindowPostOpenCamera.enableCamera, userListWindowPostOpenCamera.uid) break; case 'userListWindowDeleteRoomManager': DeleteRoomManager({ roomId: state.roomId, roomNum: state.channelId, userId: userListWindowDeleteRoomManager.uid }) break; case 'userListWindowPostRoomManager': postRoomManager({ roomId: state.roomId, roomNum: state.channelId, userId: userListWindowPostRoomManager.uid }) break; case 'userListWindowGetRoomKickout': GetRoomKickout(state.channelId, userListWindowGetRoomKickout.uid) break; case 'userListWindowAllPostOpenMicr': postOpenMicr(false, userInfo.id, true) break; case 'chatSmallWindowSendChannelMsg': sendMsg(chatSmallWindowSendChannelMsg.msg) break; case 'chatBigWindowSetAllUserLook': setAllUserLook(chatBigWindowSetAllUserLook.roomUserItem) break; case 'chatBigWindowDeleteRoomManager': DeleteRoomManager({ roomId: state.roomId, roomNum: state.channelId, userId: chatBigWindowDeleteRoomManager.uid }) break; case 'chatBigWindowPostRoomManager': postRoomManager({ roomId: state.roomId, roomNum: state.channelId, userId: chatBigWindowPostRoomManager.uid }) break; case 'chatBigWindowPostOpenMicr': postOpenMicr(chatBigWindowPostOpenMicr.enableMicr, chatBigWindowPostOpenMicr.uid) break; case 'chatBigWindowPostOpenCamera': postOpenCamera(chatBigWindowPostOpenCamera.enableCamera, chatBigWindowPostOpenCamera.uid) break; case 'chatBigWindowGetRoomKickout': GetRoomKickout(state.channelId, chatBigWindowGetRoomKickout.uid) break; case 'chatBigWindowSendChannelMsg': if (chatBigWindowSendChannelMsg.msg) { sendMsg(chatBigWindowSendChannelMsg.msg) } else[ setChatList((res: any) => { channel.postMessage({ type: 'chatList', chatList: res, }) return res }) ] break; case 'noticeWindowPostRoomManager': postRoomManager({ roomId: state.roomId, roomNum: state.channelId, userId: noticeWindowPostRoomManager.uid }) break; } } time = setInterval(() => { setCurrentSeconds(currentSeconds++) }, 1000) // 首次加载图标更新 const firstFooterList = [...footerList] firstFooterList[0][0].title = state.enableMicr ? '静音' : '解除静音' firstFooterList[0][0].active = !state.enableMicr firstFooterList[0][1].title = state.enableCamera ? '关闭视频' : '开启视频' firstFooterList[0][1].active = !state.enableCamera agora.muteLocalVideoStream(userInfo, state.enableMicr, state.enableCamera) setFooterList(firstFooterList) setTimeout(async () => { const setting = await JSON.parse(storage.getItem('setting') as string); const stateInfo = await JSON.parse(storage.getItem('stateInfo') as string); if (stateInfo && setting.isRecordingTips && !recorder) { setRecorder((data: any) => { if (!data) { confirm({ title: '提示', icon: , content: `是否录制本次会议?`, centered: true, okText: '确定', cancelText: '取消', async onOk() { changeStatusList({ title: '录制' }, 1, 3) }, onCancel() { } }) } return data }) } }, 10000); return () => { window.removeEventListener('customStorageChange', handleCustomStorageChange); window.removeEventListener('wheel', handleWheelChange); clearInterval(time) }; }, []); useEffect(() => { switch (currentEffective) { case 0: setNetworkQuality({ level: '断开连接', text: '网络连接断开' }) break; case 1: setNetworkQuality({ level: '非常差', text: '完全无法沟通' }) break; case 2: setNetworkQuality({ level: '差', text: '勉强能沟通但不顺畅' }) break; case 3: setNetworkQuality({ level: '良好', text: ' 有瑕疵但不影响沟通' }) break; case 4: setNetworkQuality({ level: '佳', text: '网络质量极好' }) break; } }, [currentEffective]); useEffect(() => { if (chatList.length) { channel.postMessage({ type: 'chatList', chatList, }); } }, [chatList]); useEffect(() => { let currentVideoUserItem = roomUserList.find((item: any) => item.uid === currentVideoId || item.screenShareId === currentVideoId) if (currentVideoUserItem) { setCurrentLookUserAccount(currentVideoUserItem) } }, [currentVideoId]); useEffect(() => { if (isShare) { const item = roomUserList.find((item: any) => item.screenShareId === String(isShare)) setIsShareUser(item || null) } }, [isShare, roomUserList]); useEffect(() => { roomUserList.forEach(async (item: any) => { if (item.uid === currentVideoId) { await agora.setRemoteVideoStreamType(item.uid, VideoStreamType.VideoStreamHigh, true) } else { await agora.setRemoteVideoStreamType(item.uid, VideoStreamType.VideoStreamLow, true) } }); }, [currentVideoId, roomUserList]); useEffect(() => { let item = roomUserList.find((item: any) => currentVideoId == item.uid) if (item) { if (isShare) { if (Number(item.screenShareId) === Number(isShare)) { agora.muteRemoteVideoStream(Number(isShare), false) } else { agora.muteRemoteVideoStream(Number(isShare), true) } } } }, [currentVideoId, isShare]); useEffect(() => { onSignalr(async (item: any) => { const setting = JSON.parse(storage.getItem('setting') as string) switch (item.key) { // 聊天 case 'ReceiveMessage': let meetingUserChatDom = document.getElementById('meetingUserChat') as HTMLElement; if (!meetingUserChatDom) { let storageNoViewChatList = Number(storage.getItem('noViewChatList')) storage.setItem('noViewChatList', storageNoViewChatList += 1) setNoViewChatList(storageNoViewChatList) } setChatList((newChatList: any) => [...newChatList, item]) channel.postMessage({ type: 'chatListIten', chatListIten: item, }); setStatusList((res: any) => { if (!res.userChatList) { api.open({ message: item.userName + '说:', description: item.message, duration: 3, showProgress: true, }); } return res }) chatScrollBotton() break; // 扩展操作 case 'Operation': // 4:屏幕共享 switch (item.type) { case 4: setIsShare((res: any) => { if (userInfo.screenShareId === String(res)) { changeStatusList({ title: '停止共享' }, 1, 0) } return res }) break; } break; // 全员离开房间 case 'AllLeave': message.success('管理员已结束会议!') leaveChannel(false) break; // 移出会议 case 'ForceExitRoom': message.success('管理员已将你移出会议!') leaveChannel() break; // 更新视图模式 case 'RefreshView': setMeetingMode(item.type) break; // 全员看他 case 'ShowUser': if (item.operUid && item.operUserName) { if (item.operUid !== userInfo.uid) { if (item.uid === userInfo.uid) { message.success(`${item.operUserName}设置全员看你`) } else { message.success(`${item.operUserName}设置全员看${item.uname}`) } } } getShowUser() break; // 用户加入频道回调 case 'UserJoined': setAllUserListData('UserJoined', item) break; // 用户退出频道回调 case 'UserLeave': setAllUserListData('UserLeave', item) break; // 所有用户开闭麦 case 'OperAllMicr': setAllUserListData('OperAllMicr', item) break; // 用户关闭开启麦克风 case 'OperMicr': if (item.operUid !== userInfo.uid) { if (item.user.uid === userInfo.uid) { message.success(item.user.enableMicr ? '管理员已取消你的静音' : '你已被管理员静音') } } setAllUserListData('OperMicr', item) break; // 用户开启关闭摄像头 case 'OperCamera': if (item.operUid !== userInfo.uid) { if (item.user.uid === userInfo.uid) { message.success(item.user.enableCamera ? '管理员已开启你的摄像头' : '管理员已关闭你摄像头') } } setAllUserListData('OperCamera', item) break; // 发言人用户信息刷新 case 'ManagerRefresh': setAllUserListData('ManagerRefresh', item, async () => { if (item.user.uid === item.uid) { if (item.user.uid === userInfo.uid) { await agora.allLeaveChannelEx() message.success(`操作成功`) await agora.updateChannelMediaOptions(item.user.isRoomManager) await postOpenMicrApi(item.user.isRoomManager, userInfo.uid, false) await postOpenCameraApi(false, userInfo.uid) // 不管身份如何改变都关闭摄像头 await stopScreenCapture() } else { message.success(`${item.user.userName}已结束发言`) } } else { if (item.user.uid === userInfo.uid) { if (item.user.isRoomManager) { await agora.allLeaveChannelEx() } message.success(`管理员${item.user.isRoomManager ? '设置' : '取消'}您为发言人`) await agora.updateChannelMediaOptions(item.user.isRoomManager) setCurrentRequestSpeakType(res => { if (res === 'video') { postOpenCameraApi(item.user.isRoomManager, userInfo.uid) } else if (res === 'audio') { postOpenMicrApi(item.user.isRoomManager, userInfo.uid, false) } else { postOpenMicrApi(item.user.isRoomManager, userInfo.uid, false) postOpenCameraApi(item.user.isRoomManager, userInfo.uid) } return '' }) await stopScreenCapture() } else { message.success(`管理员${item.user.isRoomManager ? '设置' : '取消'}${item.user.userName}为发言人`) } } }) break; // 申请发言 case 'ApplyToSpeak': const noticeWindow = await getKeyOpenChildWindow('noticeWindow') setIsScreenCapture(bool => { if (bool) { if (noticeWindow) { channel.postMessage({ type: 'noticeItem', noticeItem: item }); } else { window.electron.createChildWindow({ url: location.origin + `/#/noticeWindow`, width: 388, height: 150, key: 'noticeWindow', }) setTimeout(() => { channel.postMessage({ type: 'noticeItem', noticeItem: item }); }, 2000); setKeyOpenChildWindow('noticeWindow', true) } } else { api.open({ message: '', description:
{item.uname}申请发言
, duration: 10, placement: 'bottomRight', showProgress: true, pauseOnHover: false, }); } return bool }) break; // 管理员查看随机用户 case 'Watch': if (!role.ID.includes(userInfo.roleId)) { let userId = item.watchUids.find((uid: any) => uid === userInfo.uid) if (userId) { await agora.allJoinChannelEx() } else { await agora.allLeaveChannelEx() } } break; // 设备列表 case 'DriverList': Promise.all([ agora.getVideoDeviceManager(), agora.getAudioMediaList(), ]).then((res) => { const data = { videoList: res[0].list.map((row: any) => { return { value: row.deviceId, label: row.deviceName } }), videoDeviceId: res[0].list.find((row: any) => row.deviceId === setting.videoDeviceId) ? setting.videoDeviceId : res[0].list.length ? res[0].list[0].deviceId : null, ecordingList: res[1].ecordingList.map((row: any) => { return { value: row.deviceId, label: row.deviceName } }), ecordingDeviceId: res[1].ecordingList.find((row: any) => row.deviceId === setting.ecordingDeviceId) ? setting.ecordingDeviceId : res[1].ecordingList.length ? res[1].ecordingList[0].deviceId : null, playBackList: res[1].playBackList.map((row: any) => { return { value: row.deviceId, label: row.deviceName } }), playBackDeviceId: res[1].ecordingList.find((row: any) => row.deviceId === setting.playBackDeviceId) ? setting.playBackDeviceId : res[1].playBackList.length ? res[1].playBackList[0].deviceId : null, ecordingVolume: res[1].ecordingVolume } onInvoke('sendDrivers', { uid: item.callerUid, driversJsonString: JSON.stringify(data) }) }) break; // 设置设备 case 'SaveDriver': if (item.driver) { const data = JSON.parse(item.driver); if (data.videoDeviceId) await agora.setVideoDeviceManager(data.videoDeviceId); if (data.ecordingDeviceId) await agora.setRecordingDevice(data.ecordingDeviceId) if (data.playBackDeviceId) await agora.setPlaybackDevice(data.playBackDeviceId) await agora.setRecordingDeviceVolume(data.ecordingVolume) setting.videoDeviceId = data.videoDeviceId || null; setting.ecordingDeviceId = data.ecordingDeviceId || null; setting.playBackDeviceId = data.playBackDeviceId || null; setting.ecordingVolume = data.ecordingVolume storage.setItem('setting', JSON.stringify(setting)) } break; // 显示设备列表 case 'ShowDriverList': if (item.driversJsonString) { const data = JSON.parse(item.driversJsonString); const isOpen = await getKeyOpenChildWindow('shareScreenWindow') if (isOpen) { channel.postMessage({ type: 'showDriverList', showDriverList: data }) } else { equipmentManagementRef.current.setData(data) } } break; } }) return () => { offSignalr() } }, []) useEffect(() => { if (recorder) { recorder.start(); recorder.ondataavailable = async (event: any) => { const blob = await fixWebmDuration(event.data); const reader = new FileReader() as any; reader.onload = async () => { const setting = await JSON.parse(storage.getItem('setting') as string) const buffer = Buffer.from(reader.result); const mp4Path = `${setting.recordingFilesPath}会议录制_${state.roomName}_${state.channelId}_${dayjs().format('YYYY年MM月DD日HH时mm分')}.webm`; await fs.writeFile(mp4Path, buffer, {}); setRecorder('') setMediaStream('') confirm({ title: '提示', icon: , content: `录制成功!文件已保存至:${setting.recordingFilesPath}`, centered: true, okText: '打开文件夹', cancelText: '关闭', async onOk() { await fs.access(setting.recordingFilesPath, fs.constants.F_OK); if (process.platform === 'win32') { exec(`explorer "${setting.recordingFilesPath}"`); } else if (process.platform === 'darwin') { exec(`open "${setting.recordingFilesPath}"`); } }, onCancel() { } }) }; reader.readAsArrayBuffer(blob); } }; }, [recorder]) useEffect(() => { let timer: NodeJS.Timeout; if (isClicked) { timer = setTimeout(() => { setIsClicked(false); setCurrentRequestSpeakType('') }, 10000); } return () => clearTimeout(timer); }, [isClicked]); useEffect(() => { const elements = document.querySelectorAll('.intersectionObserver-view'); if (elements.length && currentVideoId) { elements.forEach(element => { observer?.unobserve(element); }); const observerObject = new IntersectionObserver(async (entries: IntersectionObserverEntry[], _observer: IntersectionObserver) => { entries.forEach(async (entry) => { if (entry.target.id !== user.uid) { await agora.muteRemoteVideoStreamEx(Number(entry.target.id), !entry.isIntersecting) } }); await agora.muteRemoteVideoStreamEx(Number(currentVideoId), false) }, { threshold: 0, root: document.getElementById('videoView') }); setObserver(observerObject) elements.forEach(element => { observerObject.observe(element); }); } channel.postMessage({ type: 'roomUserList', roomUserList, }); channel.postMessage({ type: 'footerList', footerList, }); return () => { elements.forEach(element => { observer?.unobserve(element); }); observer?.disconnect(); } }, [roomUserList, currentVideoId, footerList]); // 声网初始化 const agoraInit = async () => { await agora.init(true) await getJoin(state.enableMicr, state.enableCamera) agora.registerEventHandler({ onJoinChannelSuccess: async (connection: RtcConnection, _elapsed: number) => { if (connection.channelId === state.channelId) { if (String(connection.localUid).length !== 9) { setTimeout(async () => { await agora.setupLocalVideo({ uid: Number(connection.localUid), view: document.getElementById(`video-${connection.localUid}`), channelId: connection.channelId, sourceType: VideoSourceType.VideoSourceCameraPrimary, }) getShowUser(); }, 1500); } } }, onUserJoined: async (connection: RtcConnection, remoteUid: number, _elapsed: number) => { if (connection.channelId === state.channelId) { if (String(remoteUid).length === 9) { setIsShare(remoteUid) } else { setTimeout(async () => { await agora.setupRemoteVideoJoin({ uid: Number(remoteUid), view: document.getElementById(`video-${remoteUid}`), channelId: connection.channelId, }) }, 1500); } } }, onUserOffline: async (connection: RtcConnection, remoteUid: number, _reason: UserOfflineReasonType) => { if (connection.channelId === state.channelId) { if (String(remoteUid).length === 9) { setIsShare(null) } await agora.setupRemoteVideo({ uid: Number(remoteUid), view: null, channelId: connection.channelId, }); setCurrentVideoId((res: any) => { if (Number(res) === remoteUid) { getShowUser(); } return res }) } }, onAudioVolumeIndication: async (speakers: AudioVolumeInfo[]) => { async function checkUidsInUsers(uids: number[]): Promise { return new Promise(resolve => { const usernames: string[] = []; uids.forEach(uid => { setRoomUserList((res: any) => { const user = res.find((item: any) => item.uid == uid); if (user) { usernames.push(user.userName); } return res }) }); if (uids.length === usernames.length) { resolve(usernames) } }) } if (speakers.length) { speakers.forEach((item: any) => { let domMe = document.getElementById(`micr-item-${userInfo.uid}`); let dom = document.getElementById(`micr-${item.uid ? item.uid : userInfo.uid}`); if (dom) { const percentage = (item.volume / 255) * 100 dom.style.height = `${percentage}%` } if (domMe && !item.uid) { const percentage = (item.volume / 255) * 100 domMe.style.height = `${percentage}%` channel.postMessage({ type: 'currentSpeakUserMe', currentSpeakUserMe: percentage, }); } }); const uidArr = (speakers.filter((item: any) => item.volume)).map(item => item.uid || userInfo.uid) as number[]; const currentSpeakUser = await checkUidsInUsers(uidArr) channel.postMessage({ type: 'currentSpeakUser', currentSpeakUser, }); } }, onNetworkQuality: async (_connection: RtcConnection, remoteUid: number, _txQuality: QualityType, rxQuality: QualityType) => { if (remoteUid === 0) { switch (rxQuality) { case 1: setCurrentEffective(4) break; case 2: case 3: setCurrentEffective(3) break; case 4: setCurrentEffective(2) break; case 5: setCurrentEffective(1) break; case 6: setCurrentEffective(0) break; default: setCurrentEffective(storage.getItem('reconnect') === 'true' ? 4 : 0) break; } } }, onRtcStats: async (stats: RtcStats) => { setNetworkOther(stats) }, onConnectionStateChanged: async (_connection: RtcConnection, stateNumber: ConnectionStateType, reason: ConnectionChangedReasonType) => { const reconnectingCode = [2, 16, 11, 13, 14, 12] if (stateNumber === 4 && reconnectingCode.indexOf(reason) >= 0) { message.error('网络断开,请检查网络') } }, onLocalVideoStateChanged: async (_source: VideoSourceType, _state: LocalVideoStreamState, reason: LocalVideoStreamReason) => { if (reason === 12) { stopScreenCapture() setSharedScreenItem('') allUserLook(userInfo.uid, userInfo.userName) } } }) if (state.enableCamera) { await agora.startCameraCapture() } await agora.setJoinChannel({ channelId: state.channelId, uid: userInfo.uid, screenShareId: userInfo.screenShareId, token: state.token, tokenA: state.tokenA, }) } // 状态更新 const changeAgoraDevice = () => { setRoomUserList((res: any) => { res.forEach(async (item: any) => { if (item.uid === userInfo.uid) { const footerListTemplate = [...footerList] await agora.getVideoDeviceManager().then(async (res) => { getUserRoomInfo().then(async (r) => { if (res.list.length) { footerListTemplate[0][1].title = item.enableCamera ? '关闭视频' : '开启视频' footerListTemplate[0][1].active = !item.enableCamera await agora.muteLocalVideoStream(r, item.enableMicr, item.enableCamera) } else { footerListTemplate[0][1].title = '开启视频' footerListTemplate[0][1].active = true await agora.muteLocalVideoStream(r, item.enableMicr, false) } }) }) await agora.getAudioMediaList().then(async (res) => { getUserRoomInfo().then(async (r) => { if (res.ecordingList.length) { footerListTemplate[0][0].title = item.enableMicr ? '静音' : '解除静音' footerListTemplate[0][0].active = !item.enableMicr await agora.muteLocalAudioStream(r, item.enableMicr, item.enableCamera) } else { footerListTemplate[0][0].title = '解除静音' footerListTemplate[0][0].active = true await agora.muteLocalAudioStream(r, false, item.enableCamera) } }) }) if (!role.ID.includes(userInfo.roleId)) { if (item.isRoomManager) { footerListTemplate[0][2].title = '结束发言' footerListTemplate[0][2].active = true } else { footerListTemplate[0][2].title = '申请发言' footerListTemplate[0][2].active = false } } setFooterList(footerListTemplate) } if (userSearchValue) { if (item.userName.indexOf(userSearchValue) !== -1) { item.isShow = true; } else { item.isShow = false; } } else { item.isShow = true; } }); setIsAdmin(res.filter((item: any) => (role.ID.includes(item.roleId) || item.isRoomManager) && item.isRoom).length) return res }) } // 刷新 const refreshVideoView = async (userItem: any): Promise => { if (userItem.uid === userInfo.uid) { setTimeout(async () => { await agora.setupLocalVideo({ uid: Number(userItem.uid), view: document.getElementById(`video-${userItem.uid}`), channelId: state.channelId, sourceType: VideoSourceType.VideoSourceCameraPrimary, }) }, 1500); } else { setTimeout(async () => { await agora.setupRemoteVideoJoin({ uid: Number(userItem.uid), view: document.getElementById(`video-${userItem.uid}`), channelId: state.channelId, }) }, 1500); } } // 替换数据 const setAllUserListData = async (key: string, item: any, callBack?: Function): Promise => { switch (key) { case 'OperMicr': case 'OperCamera': case 'ManagerRefresh': setRoomUserList((res: any) => { let userItem = res.find((row: any) => row.uid === item.user.uid) if (userItem) { for (const key in item.user) { userItem[key] = item.user[key]; } userItem.isAdmin = role.ID.includes(item.user.roleId) || item.user.isRoomManager; refreshVideoView(userItem) } if (key === 'ManagerRefresh') { callBack && callBack() } return res }) break; case 'UserJoined': setRoomUserList((res: any) => { let userItem = res.find((row: any) => row.uid === item.user.uid) if (userItem) { for (const key in item.user) { userItem[key] = item.user[key]; } userItem.isRoom = true; userItem.isAdmin = role.ID.includes(item.user.roleId) || item.user.isRoomManager; refreshVideoView(userItem) return [...res] } else { item.user.isRoom = true; item.user.isAdmin = role.ID.includes(item.user.roleId) || item.user.isRoomManager; refreshVideoView(item.user) return [...res, item.user] } }) break; case 'UserLeave': setRoomUserList((res: any) => { let userItem = res.find((row: any) => row.uid === item.uid) if (userItem) { userItem.isRoom = false userItem.isAdmin = false } return res }) break; case 'OperAllMicr': setRoomUserList((res: any) => { res.forEach((row: any) => { if (row.uid !== item.uid) { row.enableMicr = item.enableMicr } }) return res }) break; } changeAgoraDevice() } // 滚动 const handleWheelChange = (e: any): void => { const container = document.getElementById('videoView') as HTMLElement; if (e.wheelDeltaY > 0) { container.scrollLeft -= 100 } else { container.scrollLeft += 100 } } // 渲染视频 const renderVideo = async (uid: string = ''): Promise => { if (uid) { if (currentVideoId === uid || clickCurrentLookUserStatus === false) { return } } else { uid = userInfo.uid } setClickCurrentLookUserStatus(false) setCurrentLookUserStatus(0) setRoomUserList((res: any) => { let item = res.find((item: any) => item.uid === uid || item.screenShareId === uid) if (item) { setCurrentVideoId(item.uid) } return res }) setTimeout(() => { if (uid === userInfo.uid || uid === userInfo.screenShareId) { if (String(uid).length === 9) { // 共享屏幕 setCurrentLookUserStatus(2) setTimeout(async () => { await agora.setupLocalVideo({ uid: Number(uid), view: document.getElementById(`video-source-screen`) as HTMLElement, channelId: state.channelId, sourceType: VideoSourceType.VideoSourceScreen, }) setClickCurrentLookUserStatus(true) }, 1500); } else { // 摄像头 setCurrentLookUserStatus(1) setTimeout(async () => { await agora.setupLocalVideo({ uid: Number(uid), view: document.getElementById(`video-source-camera-primary`) as HTMLElement, channelId: state.channelId, sourceType: VideoSourceType.VideoSourceCameraPrimary, }) setClickCurrentLookUserStatus(true) }, 1500); } } else { if (String(uid).length === 9) { // 共享屏幕 setCurrentLookUserStatus(3) setTimeout(async () => { await agora.setupRemoteVideoJoin({ uid: Number(uid), view: document.getElementById(`video-source-remote-screen`) as HTMLElement, channelId: state.channelId, }) setClickCurrentLookUserStatus(true) }, 1500); } else { // 摄像头 setCurrentLookUserStatus(4) setTimeout(async () => { await agora.setupRemoteVideoJoin({ uid: Number(uid), view: document.getElementById(`video-source-remote-camera`) as HTMLElement, channelId: state.channelId, }) setClickCurrentLookUserStatus(true) }, 1500); } } }, 1000) } // 全员观看 const getShowUser = async (): Promise => { if (location.href.indexOf('/meeting') !== -1) { await GetShowUser(state.channelId).then(async (res) => { if (res.code === 200 && res.data) { renderVideo(res.data) } }) } } // 加入房间时间 const changeCurrentSeconds = (): string => { const duration = dayjs.duration(currentSeconds, 'seconds'); const hours = duration.hours(); // 整数小时 const minutes = duration.minutes(); // 整数分钟 const secondsRemaining = duration.seconds(); // 剩余的秒数 return `${hours > 9 ? hours : '0' + hours}:${minutes > 9 ? minutes : '0' + minutes}:${secondsRemaining > 9 ? secondsRemaining : '0' + secondsRemaining}` } // 底部按钮点击效果 const changeFooterListSelect = (row: any, itemIndex: number, rowIndex: number, bool: boolean): void => { let arr = ['静音', '解除静音', '关闭视频', '开启视频'] if (arr.indexOf(row.title) === -1) { const footerListTemplate = [...footerList] footerListTemplate[itemIndex][rowIndex].select = bool; setFooterList(footerListTemplate) } } // 操作按钮 const changeStatusList = async (row: any, itemIndex: number, rowIndex: number): Promise => { function requestSpeak() { confirm({ title: '提示', icon: , content: `该操作需向管理员申请发言权限`, centered: true, okText: '申请', cancelText: '取消', async onOk() { GetApplySpeak(state.channelId).then(res => { if (res.code === 200) { setIsClicked(true); message.success('申请发言成功') } }) }, onCancel() { } }) } const footerListTemplate = [...footerList] setFooterListIndex({ itemIndex, rowIndex, }) switch (row.title) { case '成员列表': setStatusList({ userList: statusList.userList ? false : true, userChatList: false, userVideo: false, }) break; case '聊天': setStatusList({ userList: false, userChatList: statusList.userChatList ? false : true, userVideo: false, }) storage.setItem('noViewChatList', 0) setNoViewChatList(0) chatScrollBotton() break; case '共享屏幕': await getUserRoomInfo().then(async (res) => { if (res) { getDesktopCapturerVideo() setIsSharedScreenModal(true) } else { message.error(msgTips) } }) break; case '停止共享': await getUserRoomInfo().then(async (res) => { if (res) { await stopScreenCapture() } else { message.error(msgTips) } }) await allUserLook(userInfo.uid, userInfo.userName) break; case '静音': await postOpenMicr(false, userInfo.uid) break; case '解除静音': await getUserRoomInfo().then(async (res) => { if (res) { await postOpenMicr(true, userInfo.uid) } else { if (!isClicked) { setCurrentRequestSpeakType('audio') requestSpeak() } else { message.error('申请太频繁了,请稍后重试!'); } } }) break; case '关闭视频': await postOpenCamera(false, userInfo.uid) break; case '开启视频': await getUserRoomInfo().then(async (res) => { if (res) { await postOpenCamera(true, userInfo.uid) } else { if (!isClicked) { setCurrentRequestSpeakType('video') requestSpeak() } else { message.error('申请太频繁了,请稍后重试!'); } } }) break; case '设置': stupWizardRef.current.changeModal() break; case '邀请人员': await getUserRoomInfo().then(async (res) => { if (res) { invitingPersonnelRef.current.changeInvitingPersonnelModal() } else { message.error(msgTips) } }) break; case '录制': const setting = await JSON.parse(storage.getItem('setting') as string); try { await fs.access(setting.recordingFilesPath, fs.constants.F_OK); footerListTemplate[itemIndex][rowIndex].title = '录制中'; footerListTemplate[itemIndex][rowIndex].active = true; setFooterList(footerListTemplate); window.electron.getSources().then(async (sources: any) => { const screenId = sources[0].id; const stream = await navigator.mediaDevices.getUserMedia({ audio: { mandatory: { chromeMediaSource: 'desktop', chromeMediaSourceId: screenId, } } as any, video: { mandatory: { chromeMediaSource: 'desktop', chromeMediaSourceId: screenId, } } as any }); // 获取所有音频输入设备 const devices = await navigator.mediaDevices.enumerateDevices(); const audioInputDevices = devices.filter(device => device.kind === 'audioinput' && device.deviceId !== 'default' && device.deviceId !== 'communications'); // 使用Web Audio API来捕获系统声音和麦克风声音,将它们合并到同一个MediaStream中。 const audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)(); const systemSoundSource = audioCtx.createMediaStreamSource(stream); const systemSoundDestination = audioCtx.createMediaStreamDestination(); systemSoundSource.connect(systemSoundDestination); // 录制所有音频输入设备 audioInputDevices.forEach(async device => { const micStream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId: { exact: device.deviceId } } }); setMediaStream(micStream); const micSoundSource = audioCtx.createMediaStreamSource(micStream); micSoundSource.connect(systemSoundDestination); }) // 合并音频流与视频流 const combinedSource = new MediaStream([...stream.getVideoTracks(), ...systemSoundDestination.stream.getAudioTracks()]); // 开始录制 const mediaRecorder = new MediaRecorder(combinedSource, { mimeType: 'video/webm;codecs=vp9,opus', videoBitsPerSecond: 1.5e6, }); setRecorder(mediaRecorder); }); } catch (error: any) { if (error.code === 'ENOENT') { message.error({ content:
文件夹不存在 { stupWizardRef.current.changeModal(3); }}>前往设置
}); return; } else { message.error(error); } } break; case '录制中': footerListTemplate[itemIndex][rowIndex].title = '录制' footerListTemplate[itemIndex][rowIndex].active = false setFooterList(footerListTemplate) stopRecorderMedia() break; case '共享文件': sharedFilesModelRef.current.getData() break; case '申请发言': if (!isClicked) { requestSpeak() } else { message.error('申请太频繁了,请稍后重试!'); } break; case '会议监控': if (!statusList.userVideo) { await agora.allJoinChannelEx(true) } else { await agora.allLeaveChannelEx() } setStatusList({ userVideo: statusList.userVideo ? false : true, userChatList: false, userList: false, }) storage.setItem('noViewChatList', 0) setNoViewChatList(0) break; } } // 停止录制 const stopRecorderMedia = async (): Promise => { setRecorder((res: any) => { if (res) { res.stop(); } return res }) setMediaStream((res: any) => { if (res) { res.getTracks().forEach((track: any) => { track.stop() }); } return res }) } // 退出房间 const leaveChannel = async (bool: boolean = true): Promise => { await stopRecorderMedia() try { if (bool) { await getLeave() } } catch (error) { } await agora.leaveChannel() setTimeout(() => { if (userInfo.isAnonymous) { storage.setItem('userLogin', false) } else { navigate('/home/index') } }, 0) } // 分享屏幕 const clickSharedScreen = async (): Promise => { let data = sharedScreenList.find((item: any) => item.sourceId === sharedScreenItem.sourceId) if (data) { const footerListTemplate = [...footerList] footerListTemplate[footerListIndex.itemIndex][footerListIndex.rowIndex].title = '停止共享' setIsSharedScreenModal(false) await agora.setDesktopCapturerVideo(sharedScreenItem, isComputerAudio, isFluencyPriority) await allUserLook(user.screenShareId, user.userName) const isOpen = await getKeyOpenChildWindow('shareScreenWindow') setIsScreenCapture(true) window.electron.setViewStatus('hide') if (!isOpen) { window.electron.createChildWindow({ url: location.origin + `/#/shareScreenWindow`, width: 400, height: 80, key: 'shareScreenWindow', }) window.electron.createChildWindow({ url: location.origin + `/#/chatSmallWindow`, width: 200, height: 150, key: 'chatSmallWindow', }) window.electron.createChildWindow({ url: location.origin + `/#/currentSpeakUserWindow`, width: 200, height: 30, key: 'currentSpeakUserWindow', }) setKeyOpenChildWindow('shareScreenWindow', true) } } else { message.error('请选择应用!') } } // 获取桌面可共享屏幕的引用 const getDesktopCapturerVideo = (): void => { agora.getDesktopCapturerVideo({ width: 300, height: 300 }, { width: 300, height: 300 }, true).then((res: any) => { res.forEach((item: any, index: number) => { if (item.type === 1) { item.sourceTitle = '桌面' + (index + 1) } if (item.thumbImage.buffer) { item.thumbnailUrl = thumbImageBufferToBase64(item.thumbImage) } if (item.iconImage.buffer) { item.iconDataUrl = thumbImageBufferToBase64(item.iconImage) } }) setSharedScreenList(res) }) }; // 设置全员看谁 const allUserLook = async (uid: string, name: string): Promise => { await PostShowUser(state.channelId, uid, name) } // 设置发言人 const postRoomManager = async (data: any): Promise => { if (isAdmin >= 20) { message.error('设置失败,已达最大发言人20个!') } else { await PostRoomManager({ roomId: data.roomId, roomNum: data.roomNum, userId: data.userId }) } } // 停止共享 const stopScreenCapture = async (): Promise => { const footerListTemplate = [...footerList] await agora.leaveChannelEx(userInfo.screenShareId) agora.stopScreenCapture() footerListTemplate[1][0].title = '共享屏幕' setFooterList(footerListTemplate) window.electron.closeChildWindow('shareScreenWindow') setKeyOpenChildWindow('shareScreenWindow', false) setIsScreenCapture(bool => { if (bool) { window.electron.setViewStatus('show') window.electron.getWindowSize().then((res: any) => { window.electron.setMainWindowSize({ width: Math.ceil(res.width / 1.5), height: Math.ceil(res.height / 1.3), }) window.electron.getIsMaximized().then((b: boolean) => { if (!b) { window.electron.setViewStatus('maximize') } }) }) } return false }) } // 获取房间用户 const getRoomUser = async (): Promise => { GetRoomUser(state.channelId).then(res => { if (res.code === 200) { res.data.forEach((item: any) => { item.isShow = true; item.isRoom = true; item.isAdmin = role.ID.includes(item.roleId) || item.isRoomManager }) setRoomUserList(res.data) getUserRoomInfo().then(async (res) => { await agora.updateChannelMediaOptions(res ? true : false) changeAgoraDevice() }) } }) } // 监听缓存变化 const handleCustomStorageChange = async (e: any): Promise => { switch (e.key) { case 'meetingMode': setMeetingMode(e.value) break; case 'quitMeeting': if (e.value) { setQuitMeetingModal(true) } break; case 'reconnect': if (e.value == true) { await onInvoke('joinChannel', { roomNum: state.channelId, enableMicr: !footerList[0][0].active, enableCamera: !footerList[0][1].active }) message.success('网络已连接。') setRoomUserList((res: any) => { let userItem = res.find((item: any) => item.uid === userInfo.uid) if (userItem.isRoomManager) { DeleteRoomManager({ roomId: state.roomId, roomNum: state.channelId, userId: userInfo.uid }) } return res }) } break; } }; // 聊天发送 const sendMsg = (text?: string): void => { let msg = text ? text : textMsg; if (msg) { onInvoke('sendChannelMsg', { roomNum: state.channelId, msg: msg, }) let item = { uid: userInfo.uid, userName: userInfo.userName, message: msg, timestamp: +new Date() } setChatList((newChatList: any) => [...newChatList, item]) channel.postMessage({ type: 'chatListIten', chatListIten: item, }); setTextMsg(''); chatScrollBotton() } else { message.error('请输入内容!') } } // 聊天框滚动到底部 const chatScrollBotton = (): void => { setTimeout(() => { const meetingUserChatContentView = document.getElementById('meetingUserChatContentView') as HTMLElement; if (meetingUserChatContentView) { meetingUserChatContentView.scrollTop = meetingUserChatContentView.scrollHeight; } }, 0); } // 设备管理 const equipmentManagement = async (uid: string, userName: string): Promise => { equipmentManagementRef.current.changeModal(uid, userName) } // 开关麦克风 const postOpenMicr = async (enableMicr: boolean, uid: string, isAll: boolean = false): Promise => { await getUserRoomInfo().then(async (res) => { if (res) { if (!isAll) { let msg = ''; if (uid === user.uid) { await agora.getAudioMediaList().then(res => { if (!res.ecordingList.length) { msg = '未检测到麦克风!' } }) } if (msg) { message.error(msg) return } } if (enableMicr) { const enableMicrLenght = roomUserList.filter((item: any) => item.enableMicr).length if (enableMicrLenght >= 20) { return message.error('房间内最多20个开启麦克风') } } await postOpenMicrApi(enableMicr, uid, isAll, true) } else { message.error(msgTips) } }) } // 开关麦克风 const postOpenMicrApi = async (enableMicr: boolean, uid: string, isAll: boolean, isMessage: boolean = false): Promise => { if (isAll) { await PostMuteAll({ roomNum: state.channelId, enableMicr }) } else { await PostOpenMicr({ roomNum: state.channelId, uid, enableMicr }) } if (isMessage) { message.success('操作成功') } } // 开关视频 const postOpenCamera = async (enableCamera: boolean, uid: string): Promise => { await getUserRoomInfo().then(async (res) => { if (res) { let msg = ''; if (uid === user.uid) { await agora.getVideoDeviceManager().then(res => { if (!res.list.length) { msg = '未检测到摄像头!' } }) } if (msg) { message.error(msg) return } if (enableCamera) { const enableCameraLenght = roomUserList.filter((item: any) => item.enableCamera).length if (enableCameraLenght >= 20) { return message.error('房间内最多20个开启摄像头') } } await postOpenCameraApi(enableCamera, uid, true) } else { message.error(msgTips) } }) } // 开关视频 const postOpenCameraApi = async (enableCamera: boolean, uid: string, isMessage: boolean = false): Promise => { if (enableCamera) { await agora.startCameraCapture() } else { await agora.stopCameraCapture(); } await PostOpenCamera({ roomNum: state.channelId, uid, enableCamera }) if (isMessage) { message.success('操作成功') } } // 演讲者模式 // const changeSpeakerMode = (): void => { // speakerModeModalRef.current.changeSpeakerMode() // } // 获取当前用户在房间的角色信息 const getUserRoomInfo = async (): Promise => { return new Promise((resolve, _reject) => { setRoomUserList((res: any) => { let userItem = res.find((item: any) => item.uid === userInfo.uid) if (userItem && (role.ID.includes(userItem.roleId) || userItem.isRoomManager)) { resolve(userItem) } else { resolve('') } return res }) }) } // 获取当前模式样式 const getMeetingContentBodyLeftModeClass = (): string => { switch (meetingMode) { case 'FreedomMode': return styles.meetingContentBodyLeftFreedomMode case 'StandardMode': return styles.meetingContentBodyLeftStandardMode case 'SpeakerMode': return styles.meetingContentBodyLeftSpeakerMode case 'SingleScreenMode': return styles.meetingContentBodyLeftSingleScreenMode case 'DualScreenMode': return styles.meetingContentBodyLeftDualScreenMode case 'FourScreenMode': return styles.meetingContentBodyLeftFourScreenMode } return '' } // 获取当前模式文字 const getMeetingContentBodyLeftModeText = (): string => { switch (meetingMode) { case 'FreedomMode': return '自由者模式' case 'StandardMode': return '标准模式' case 'SpeakerMode': return '演讲模式' case 'SingleScreenMode': return '单画面模式' case 'DualScreenMode': return '二分屏模式' case 'FourScreenMode': return '四分屏模式' } return '' } // 设置单个视频样式 const setMeetingContentSwiperCardClass = (): string => { switch (meetingMode) { case 'StandardMode': return styles.meetingContentSwiperCardStandardMode case 'SpeakerMode': return styles.meetingContentSwiperCardSpeakerMode } return '' } // 视频是否全屏 const setMeetingContentSwiperCardFullScreenClass = (): string => { switch (meetingMode) { case 'StandardMode': return isVideoFullScreen ? styles.meetingContentSwiperCardStandardModeFullScreen : '' case 'SpeakerMode': return isVideoFullScreen ? styles.meetingContentSwiperCardSpeakerModeFullScreen : '' } return '' } // 获取展开折叠按钮 const getSettingIcon = (): any => { switch (meetingMode) { case 'StandardMode': if (isVideoFullScreen) { return
setIsVideoFullScreen(false)}>
//下 } else { return
setIsVideoFullScreen(true)}>
//上 } case 'SpeakerMode': if (isVideoFullScreen) { return
setIsVideoFullScreen(false)}>
//右 } else { return
setIsVideoFullScreen(true)}>
//左 } } } // 加入房间 const getJoin = async (enableMicr: boolean, enableCamera: boolean): Promise => { // await GetJoin({ // roomNum: state.channelId, // enableMicr, // enableCamera // }) await onInvoke('joinChannel', { roomNum: state.channelId, enableMicr, enableCamera }) await getRoomUser() } // 离开房间 const getLeave = async (): Promise => { // await GetLeave({ // roomNum: state.channelId, // }) await onInvoke('levelChannel', { roomNum: state.channelId }) } // 设置全员观看 const setAllUserLook = async (item: any): Promise => { if (isShare) { await allUserLook(String(isShare) === item.screenShareId ? item.screenShareId : item.uid, item.userName) } else { await allUserLook(item.uid, item.userName) } message.success('操作成功') } // 移出房间 const getRoomKickout = async (channelId: string, uid: string, userName: string): Promise => { confirm({ title: '移出会议', icon: , content: `确定将用户${userName}移出会议?`, centered: true, okText: '确定', cancelText: '取消', async onOk() { await GetRoomKickout(channelId, uid) message.success('操作成功') }, onCancel() { }, }); } return ( <>
{contextHolder}
网络质量: {networkQuality.level} {networkQuality.text} } title="" >
带宽占用: ↑{networkOther.txKBitRate}kbps ↓{networkOther.rxKBitRate}kbps
丢包率: ↑{networkOther.txPacketLossRate}% ↓{networkOther.rxPacketLossRate}%
延迟: {networkOther.lastmileDelay}ms
} title="" trigger="hover" >
{networkIcon(currentEffective)} 详情
{changeCurrentSeconds()}
会议号:{state.channelId} 会议名称:{state.roomName}
{ setModeOpen(false) storage.setItem('meetingMode', 'StandardMode') }}> 标准模式
{ setModeOpen(false) storage.setItem('meetingMode', 'SpeakerMode') }}> 演讲模式
{ setModeOpen(false) }}> 取消
} title="" trigger="click" open={modeOpen} onOpenChange={() => setModeOpen(true)} >
{meetingMode === 'StandardMode' ? : } {getMeetingContentBodyLeftModeText()}
{isAdmin && currentLookUserAccount ? getSettingIcon() : null}
{roomUserList.map((item: any, index: number) => { return (index <= 19 && item.isRoom && item.isAdmin ?
{ if (String(isShare) === item.screenShareId) { renderVideo(item.screenShareId) } else { renderVideo(item.uid) } }} >
{meetingContentUser(item)} {item.enableCamera ? null : meetingContentError(item)} {String(isShare) === item.screenShareId ?
屏幕分享中
: null} {role.ID.includes(user.roleId) ? {item.isRoomManager || role.ID.includes(item.roleId) ? : null} {item.uid !== user.uid && !role.ID.includes(item.roleId) ? : null} {item.isRoomManager ? : null} {item.isRoomManager ? : null} {item.uid !== user.uid ? : null} {item.uid !== user.uid ? : null}
}>
: null}
: null) } )} {currentLookUserStatus === 0 && currentLookUserAccount ?
{
}
{isVideoFullScreen ? setIsVideoFullScreen(false)} /> : setIsVideoFullScreen(true)} />} {meetingContentUser(currentLookUserAccount, true)}
: null} {currentLookUserStatus === 1 && currentLookUserAccount ?
{
}
{isVideoFullScreen ? setIsVideoFullScreen(false)} /> : setIsVideoFullScreen(true)} />} {meetingContentUser(currentLookUserAccount, true)} {currentLookUserAccount.enableCamera ? null : meetingContentError(currentLookUserAccount)}
: null} {currentLookUserStatus === 2 && currentLookUserAccount ?
{isVideoFullScreen ? setIsVideoFullScreen(false)} /> : setIsVideoFullScreen(true)} />} {meetingContentUser(currentLookUserAccount, true)}
: null} {currentLookUserStatus === 3 && currentLookUserAccount ?
{isVideoFullScreen ? setIsVideoFullScreen(false)} /> : setIsVideoFullScreen(true)} />} {meetingContentUser(currentLookUserAccount, true)}
: null} {currentLookUserStatus === 4 && currentLookUserAccount ?
{isVideoFullScreen ? setIsVideoFullScreen(false)} /> : setIsVideoFullScreen(true)} />} {meetingContentUser(currentLookUserAccount, true)} {currentLookUserAccount.enableCamera ? null : meetingContentError(currentLookUserAccount)}
: null} {isAdmin ? null :
}
{ (statusList.userList || statusList.userChatList || statusList.userVideo) ? (
{statusList.userList ?
成员列表 { setStatusList({ userList: false, userChatList: false, userVideo: false, }) }} />
} value={userSearchValue} onChange={(e) => { setUserSearchValue(e.target.value) const newRoomUserList = [...roomUserList] newRoomUserList.forEach(row => { if (e.target.value) { if (row.userName.indexOf(e.target.value) !== -1) { row.isShow = true; } else { row.isShow = false; } } else { row.isShow = true; } }); setRoomUserList(newRoomUserList) }} />
{roomUserList.map((item: any, index: number) => { return ( item.isShow && item.isRoom ?
{item.userName}{item.uid === user.uid ? '(我)' : ''} {role.ID.includes(item.roleId) || item.isRoomManager ? {role.ID.includes(item.roleId) ? '管理员' : '发言人'} : null}
{role.ID.includes(item.roleId) || item.isRoomManager ?
{ postOpenMicr(!item.enableMicr, item.uid) }} title={item.enableMicr ? '静音' : '解除静音'} />
: null} {role.ID.includes(item.roleId) || item.isRoomManager ?
{ postOpenCamera(!item.enableCamera, item.uid) }} title={item.enableCamera ? '关闭视频' : '开启视频'} />
: null} {item.uid !== user.uid && role.ID.includes(user.roleId) ?
}>
: null} {item.uid !== user.uid && !role.ID.includes(item.roleId) ?
{ if (item.isRoomManager) { DeleteRoomManager({ roomId: state.roomId, roomNum: state.channelId, userId: item.uid }) } else { postRoomManager({ roomId: state.roomId, roomNum: state.channelId, userId: item.uid }) } }}> {!item.isRoomManager ? : }
: null}
: null ) } )}
{ await getUserRoomInfo().then(async (res) => { if (res) { invitingPersonnelRef.current.changeInvitingPersonnelModal() } else { message.error(msgTips) } }) }}>邀请
postOpenMicr(false, user.id, true)}>全员静音
: statusList.userChatList ?
聊天 { setStatusList({ userList: false, userChatList: false, userVideo: false, }) }} />
{chatList.map((item: any, index: number) =>
{role.ID.includes(user.roleId) ? { if (e) { GetRoomUserItem(state.channelId, item.uid).then((res: any) => { if (res.code === 200) { setRoomUserItem(res.data) } }) } else { setRoomUserItem(null) } }} content={ roomUserItem ?
{roomUserItem.isRoomManager || role.ID.includes(roomUserItem.roleId) ? : null} {roomUserItem.uid !== user.uid && !role.ID.includes(roomUserItem.roleId) ? : null} {roomUserItem.isRoomManager ? : null} {roomUserItem.isRoomManager ? : null} {roomUserItem.uid !== user.uid ? : null} {roomUserItem.uid !== user.uid ? : null}
:
用户不在房间内
}>
{item.uid !== user.uid ? {item.userName} {dayjs(item.timestamp).format('HH:mm:ss')} : {dayjs(item.timestamp).format('HH:mm:ss')} {item.userName} }
:
{item.uid !== user.uid ? {item.userName}{dayjs(item.timestamp).format('HH:mm:ss')} : {dayjs(item.timestamp).format('HH:mm:ss')} {item.userName} }
}
{item.message}
)}
{ commonlyChatList.map((item: string, index: number) => { return }) }
{ setTextMsg(e.target.value) }}>
:
会议监控 { setStatusList({ userList: false, userChatList: false, userVideo: false, }) }} />
}
) : null }
{footerList.map((item: any, itemIndex: number) => { return (
{item.map((row: any, rowIndex: number) => { switch (row.title) { case '结束': return {role.ID.includes(user.roleId) ? { GetLeaveAll({ roomNum: state.channelId, }) }} onCancel={() => { }} okText="结束" cancelText="取消" >
全员结束会议
: null}
leaveChannel()}>仅自己离开
{ setOpen(false) }}>取消
} title="" trigger="click" open={open} onOpenChange={() => setOpen(true)} >
changeFooterListSelect(row, itemIndex, rowIndex, true)} onMouseUp={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} onMouseLeave={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} > {row.select ? : } {row.title}
case '申请发言': // if (!role.ID.includes(user.roleId)) { // return
changeStatusList(row, itemIndex, rowIndex)} key={rowIndex}> // // {row.title} //
// } return null case '结束发言': if (!role.ID.includes(user.roleId)) { return { DeleteRoomManager({ roomId: state.roomId, roomNum: state.channelId, userId: userInfo.uid }) }} okText="确定" cancelText="取消" >
{row.title}
} return null case '会议监控': if (role.ID.includes(user.roleId)) { return
changeStatusList(row, itemIndex, rowIndex)} onMouseDown={() => changeFooterListSelect(row, itemIndex, rowIndex, true)} onMouseUp={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} onMouseLeave={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} key={rowIndex}> {statusList.userVideo ? : row.select ? : } {row.title}
} return null case '成员列表': return
changeStatusList(row, itemIndex, rowIndex)} onMouseDown={() => changeFooterListSelect(row, itemIndex, rowIndex, true)} onMouseUp={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} onMouseLeave={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} key={rowIndex}> {statusList.userList ? : row.select ? : } {row.title} {row.title === '成员列表' ?
{roomUserList.filter((item: any) => item.isRoom).length}
: null}
case '聊天': return
changeStatusList(row, itemIndex, rowIndex)} onMouseDown={() => changeFooterListSelect(row, itemIndex, rowIndex, true)} onMouseUp={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} onMouseLeave={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} key={rowIndex}> {statusList.userChatList ? : row.select ? : } {row.title} {row.title === '聊天' && noViewChatList > 0 ?
{noViewChatList}
: null}
case '静音': case '解除静音': return
changeStatusList(row, itemIndex, rowIndex)} onMouseDown={() => changeFooterListSelect(row, itemIndex, rowIndex, true)} onMouseUp={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} onMouseLeave={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} key={rowIndex}> {row.title}
default: return
changeStatusList(row, itemIndex, rowIndex)} onMouseDown={() => changeFooterListSelect(row, itemIndex, rowIndex, true)} onMouseUp={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} onMouseLeave={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} key={rowIndex}> {row.select ? : } {row.title}
} })}
) })}
{sharedScreenList.map((item: any, index: number) => { return (
{ setSharedScreenItem(item) }}> {item.iconDataUrl ? : ''}
{item.sourceTitle}
) })}
{ setIsComputerAudio(e.target.checked) }} checked={isComputerAudio}>共享电脑音频 { setIsFluencyPriority(e.target.checked) }} checked={isFluencyPriority}>流畅度优先
{isShare ? { setIsSharePopConfirm(false) await onInvoke('sendOper', { roomNum: state.channelId, type: 4, }) clickSharedScreen() }} onCancel={() => { setIsSharePopConfirm(false) }} okText="是" cancelText="否" > : }
{role.ID.includes(user.roleId) ? { await GetLeaveAll({ roomNum: state.channelId, }) }} onCancel={() => { }} okText="结束" cancelText="取消" >
全员结束会议
: null}
{ await leaveChannel() }}>仅自己离开
{ setQuitMeetingModal(false) }}>取消
) } const meetingContentUser = (item: any, bool?: boolean) => { return ( <>
{role.ID.includes(item.roleId) || item.isRoomManager ?
: null} { bool ? !item.enableMicr ? : '' : } {item.userName} {role.ID.includes(item.roleId) || item.isRoomManager ? role.ID.includes(item.roleId) ? '(管理员)' : '(发言人)' : ''}
) } const meetingContentError = (item: any) => { return ( <>
) } const networkIcon = (network: number) => { switch (network) { case 0: return case 1: return case 2: return case 3: return case 4: return } } export default Meeting