From a5d755ffc8ffea10e15540aee7be7aef177f7e6f Mon Sep 17 00:00:00 2001 From: yj <1336058017@qq.com> Date: Tue, 24 Sep 2024 15:15:24 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E7=BD=91=E7=BB=9C=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/Meeting/index.module.scss | 8 ++ src/page/Meeting/index.tsx | 200 +++++++++++++++++++++++------ src/utils/package/agora.ts | 33 +++-- vite.config.ts | 14 +- 4 files changed, 202 insertions(+), 53 deletions(-) diff --git a/src/page/Meeting/index.module.scss b/src/page/Meeting/index.module.scss index 1947a60..a05e87b 100644 --- a/src/page/Meeting/index.module.scss +++ b/src/page/Meeting/index.module.scss @@ -111,6 +111,14 @@ >div:nth-child(1) { margin-right: 20px; display: flex; + align-items: center; + cursor: pointer; + + >span { + color: #1677ff; + font-size: 16px; + margin-left: 4px; + } } >div:nth-child(2) { diff --git a/src/page/Meeting/index.tsx b/src/page/Meeting/index.tsx index 96754af..29925d7 100644 --- a/src/page/Meeting/index.tsx +++ b/src/page/Meeting/index.tsx @@ -4,7 +4,7 @@ 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 } from '@ant-design/icons'; +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'; @@ -14,7 +14,7 @@ import { agora } from '@/utils/package/agora' import { onInvoke, onSignalr, offSignalr, onStart } from '@/utils/package/signalr'; import dayjs from 'dayjs'; import durationPlugin from 'dayjs/plugin/duration'; -import { VideoSourceType, VideoStreamType } from 'agora-electron-sdk'; +import { AudioVolumeInfo, 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'; @@ -146,7 +146,14 @@ const Meeting: React.FC = () => { const [chatList, setChatList] = useState([]) const [currentVideoId, setCurrentVideoId] = useState('') let [currentSeconds, setCurrentSeconds] = useState(0) - const [currentEffective, setCurrentEffective] = useState(0) + const [currentEffective, setCurrentEffective] = useState(4) + const [currentNetworkInfo, setCurrentNetworkInfo] = useState({ + networkQuality: { + level: '佳', + text: '网络质量极好。' + }, + networkOther: {} as RtcStats + }) const [isComputerAudio, setIsComputerAudio] = useState(true) const [isFluencyPriority, setIsFluencyPriority] = useState(false) const [open, setOpen] = useState(false) @@ -231,14 +238,54 @@ const Meeting: React.FC = () => { }, []); useEffect(() => { - const connection = (navigator as any).connection - if (connection.downlink === 0) { - setCurrentEffective(0) - } else { - let effectiveTypeLength = ['slow-2g', '2g', '3g', '4g'].indexOf((navigator as any).connection.effectiveType) - setCurrentEffective(effectiveTypeLength + 1) + switch (currentEffective) { + case 0: + setCurrentNetworkInfo({ + ...currentNetworkInfo, + networkQuality: { + level: '断开连接', + text: '网络连接断开。' + } + }) + break; + case 1: + setCurrentNetworkInfo({ + ...currentNetworkInfo, + networkQuality: { + level: '非常差', + text: '完全无法沟通。' + } + }) + break; + case 2: + setCurrentNetworkInfo({ + ...currentNetworkInfo, + networkQuality: { + level: '差', + text: '勉强能沟通但不顺畅,网络质量非常差,基本不能沟通。' + } + }) + break; + case 3: + setCurrentNetworkInfo({ + ...currentNetworkInfo, + networkQuality: { + level: '良好', + text: ' 用户主观感受有瑕疵但不影响沟通。' + } + }) + break; + case 4: + setCurrentNetworkInfo({ + ...currentNetworkInfo, + networkQuality: { + level: '佳', + text: '网络质量极好。' + } + }) + break; } - }, [(navigator as any).connection.effectiveType]); + }, [currentEffective]); useEffect(() => { let currentVideoUserItem = roomUserList.find((item: any) => item.uid === currentVideoId || item.screenShareId === currentVideoId) @@ -644,14 +691,14 @@ const Meeting: React.FC = () => { await getJoin(state.enableMicr, state.enableCamera) await agora.init(true) agora.registerEventHandler({ - onJoinChannelSuccess: async (info: any, _elapsed: any) => { - if (info.channelId === state.channelId) { - if (String(info.localUid).length !== 9) { + onJoinChannelSuccess: async (connection: RtcConnection, _elapsed: number) => { + if (connection.channelId === state.channelId) { + if (String(connection.localUid).length !== 9) { setTimeout(async () => { await agora.setupLocalVideo({ - uid: Number(info.localUid), - view: document.getElementById(`video-${info.localUid}`), - channelId: info.channelId, + uid: Number(connection.localUid), + view: document.getElementById(`video-${connection.localUid}`), + channelId: connection.channelId, sourceType: VideoSourceType.VideoSourceCameraPrimary, }) getShowUser(); @@ -659,8 +706,8 @@ const Meeting: React.FC = () => { } } }, - onUserJoined: async (info: any, remoteUid: any, _elapsed: any) => { - if (info.channelId === state.channelId) { + onUserJoined: async (connection: RtcConnection, remoteUid: number, _elapsed: number) => { + if (connection.channelId === state.channelId) { if (String(remoteUid).length === 9) { setIsShare(remoteUid) } else { @@ -668,21 +715,21 @@ const Meeting: React.FC = () => { await agora.setupRemoteVideoJoin({ uid: Number(remoteUid), view: document.getElementById(`video-${remoteUid}`), - channelId: info.channelId, + channelId: connection.channelId, }) }, 1500); } } }, - onUserOffline: async (info: any, remoteUid: any, _reason: any) => { - if (info.channelId === state.channelId) { + 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: info.channelId, + channelId: connection.channelId, }); setCurrentVideoId((res: any) => { if (Number(res) === remoteUid) { @@ -692,7 +739,7 @@ const Meeting: React.FC = () => { }) } }, - onAudioVolumeIndication: async (speakers: any) => { + onAudioVolumeIndication: async (speakers: AudioVolumeInfo[]) => { speakers.forEach((item: any) => { let domMe = document.getElementById(`micr-item-${userInfo.uid}`); let dom = document.getElementById(`micr-${item.uid ? item.uid : userInfo.uid}`); @@ -705,6 +752,35 @@ const Meeting: React.FC = () => { domMe.style.height = `${percentage}%` } }); + }, + 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: + case 5: + setCurrentEffective(2) + break; + case 6: + setCurrentEffective(1) + break; + default: + setCurrentEffective(navigator.onLine ? 4 : 0) + break; + } + } + }, + onRtcStats: async (stats: RtcStats) => { + setCurrentNetworkInfo({ + ...currentNetworkInfo, + networkOther: stats + }) } }) if (state.enableCamera) { @@ -1550,9 +1626,51 @@ const Meeting: React.FC = () => { {contextHolder}
-
- {networkIcon(currentEffective)} -
+ +
+ 网络质量: + {currentNetworkInfo.networkQuality.level} + + + {currentNetworkInfo.networkQuality.text} + + } + title="" + > + + + +
+
+ 带宽占用: + ↑{currentNetworkInfo.networkOther.txKBitRate}kbps ↓{currentNetworkInfo.networkOther.rxKBitRate}kbps +
+
+ 丢包率: + ↑{currentNetworkInfo.networkOther.txPacketLossRate}% ↓{currentNetworkInfo.networkOther.rxPacketLossRate}% +
+
+ 延迟: + {currentNetworkInfo.networkOther.lastmileDelay}ms +
+
+ } + title="" + trigger="hover" + > +
+ {networkIcon(currentEffective)} + 详情 +
+
{changeCurrentSeconds()}
会议号:{state.channelId} 会议名称:{state.roomName}
@@ -2339,30 +2457,30 @@ const meetingContentError = (item: any) => { const networkIcon = (network: number) => { switch (network) { case 0: - return - - - + return + + + - + case 1: - return - - - - + return + + + + case 2: - return - - - - + return + + + + case 3: return diff --git a/src/utils/package/agora.ts b/src/utils/package/agora.ts index f49bb29..8fdd786 100644 --- a/src/utils/package/agora.ts +++ b/src/utils/package/agora.ts @@ -8,7 +8,12 @@ import { ChannelProfileType, AudioAinsMode, SimulcastStreamMode, - VideoStreamType + VideoStreamType, + QualityType, + RtcConnection, + RtcStats, + AudioVolumeInfo, + UserOfflineReasonType } from "agora-electron-sdk"; import { GetRoomRtcToken, GetAgoraConf } from "@/api/Home/Index"; import { storage } from '@/utils'; @@ -104,19 +109,19 @@ export const agora = { }, 1000); }, // 事件回调 - registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline, onAudioVolumeIndication }: any) => { + registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline, onAudioVolumeIndication, onNetworkQuality, onRtcStats }: any) => { rtcEngine.registerEventHandler({ // 监听本地用户加入频道事件 - onJoinChannelSuccess: async (info: any, elapsed: any) => { - await onJoinChannelSuccess(info, elapsed) + onJoinChannelSuccess: async (connection: RtcConnection, elapsed: number) => { + await onJoinChannelSuccess(connection, elapsed) }, // 监听远端用户加入频道事件 - onUserJoined: async (info: any, remoteUid: any, elapsed: any) => { - await onUserJoined(info, remoteUid, elapsed) + onUserJoined: async (connection: RtcConnection, remoteUid: number, elapsed: number) => { + await onUserJoined(connection, remoteUid, elapsed) }, // 监听用户离开频道事件 - onUserOffline: async (info: any, remoteUid: any, reason: any) => { - await onUserOffline(info, remoteUid, reason) + onUserOffline: async (connection: RtcConnection, remoteUid: number, reason: UserOfflineReasonType) => { + await onUserOffline(connection, remoteUid, reason) }, // // 视频发布状态改变回调 // onVideoPublishStateChanged: (source: any, channel: any, oldState: any, newState: any, elapseSinceLastState: any) => { @@ -131,9 +136,17 @@ export const agora = { // } // }, // // 用户音量提示回调。 - onAudioVolumeIndication: async (_connection: any, speakers: any, _speakerNumber: any, _totalVolume: any,) => { + onAudioVolumeIndication: async (_connection: RtcConnection, speakers: AudioVolumeInfo[], _speakerNumber: number, _totalVolume: number) => { await onAudioVolumeIndication(speakers) - } + }, + //通话中每个用户的网络上下行 last mile 质量报告回调。 + onNetworkQuality: async (connection: RtcConnection, remoteUid: number, txQuality: QualityType, rxQuality: QualityType) => { + await onNetworkQuality(connection, remoteUid, txQuality, rxQuality) + }, + //当前通话相关的统计信息回调。 + onRtcStats: async (_connection: RtcConnection, stats: RtcStats) => { + await onRtcStats(stats) + }, }); }, // 获取视图模式 diff --git a/vite.config.ts b/vite.config.ts index 7487160..325229a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -63,7 +63,12 @@ export default defineConfig({ VideoViewSetupMode, AudioAinsMode, SimulcastStreamMode, - VideoStreamType + VideoStreamType, + QualityType, + RtcConnection, + RtcStats, + AudioVolumeInfo, + UserOfflineReasonType } = require("agora-electron-sdk") export { createAgoraRtcEngine, @@ -75,7 +80,12 @@ export default defineConfig({ VideoViewSetupMode, AudioAinsMode, SimulcastStreamMode, - VideoStreamType + VideoStreamType, + QualityType, + RtcConnection, + RtcStats, + AudioVolumeInfo, + UserOfflineReasonType } `, }) From 5c7d0d7649d93867f436a31d70832b0e660812d2 Mon Sep 17 00:00:00 2001 From: yj <1336058017@qq.com> Date: Tue, 24 Sep 2024 15:22:08 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E5=BC=BA=E5=88=B6=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UpdateModal/index.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/UpdateModal/index.tsx b/src/components/UpdateModal/index.tsx index 343cf40..7166fd0 100644 --- a/src/components/UpdateModal/index.tsx +++ b/src/components/UpdateModal/index.tsx @@ -48,11 +48,12 @@ const UpdateModal = forwardRef((props: any, ref: any) => { title="" open={isUpdateModal} footer={null} - onCancel={() => closeModal()} + // onCancel={() => closeModal()} centered width={'400px'} className='modal-padding' maskClosable={false} + closeIcon={false} >
@@ -62,10 +63,10 @@ const UpdateModal = forwardRef((props: any, ref: any) => {
-
setIsUpdateModal(false)}>暂不更新
+ {/*
setIsUpdateModal(false)}>暂不更新
*/}
: progress < 100 ?
下载进度:{progress}% From a913c6520b45b244b3e2a520c1016ba7a612c257 Mon Sep 17 00:00:00 2001 From: yj <1336058017@qq.com> Date: Tue, 24 Sep 2024 15:24:47 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=BC=80=E5=8F=91=E7=8E=AF=E5=A2=83?= =?UTF-8?q?=E5=8F=AF=E5=85=B3=E9=97=AD=E6=9B=B4=E6=96=B0=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UpdateModal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/UpdateModal/index.tsx b/src/components/UpdateModal/index.tsx index 7166fd0..e97819b 100644 --- a/src/components/UpdateModal/index.tsx +++ b/src/components/UpdateModal/index.tsx @@ -66,7 +66,7 @@ const UpdateModal = forwardRef((props: any, ref: any) => { style={{ width: '100%', height: '40px', marginBottom: '10px' }} className={`m-ant-btn`} >立即更新 - {/*
setIsUpdateModal(false)}>暂不更新
*/} + {import.meta.env.VITE_ENV === "development" ?
setIsUpdateModal(false)}>暂不更新
: null}
: progress < 100 ?
下载进度:{progress}% From ef932fd3a8865b58682084273c1e3d0dccd6a45a Mon Sep 17 00:00:00 2001 From: yj <1336058017@qq.com> Date: Tue, 24 Sep 2024 15:40:34 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E6=88=BF=E9=97=B4=E9=92=B1=E7=9A=84loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/JoinSetting/index.tsx | 15 +++++++++++++++ src/page/Home/Index/index.tsx | 9 ++++++++- src/utils/styles/App.scss | 4 ++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/components/JoinSetting/index.tsx b/src/components/JoinSetting/index.tsx index 2d38cba..6a11b2a 100644 --- a/src/components/JoinSetting/index.tsx +++ b/src/components/JoinSetting/index.tsx @@ -68,7 +68,11 @@ const JoinSetting = forwardRef((_props: any, ref: any) => { await GetCheckoutRoomNum(roomNum).then(res => { if (res.code === 200) { callBack(res.data) + } else { + storage.setItem('loading', false) } + }).catch(() => { + storage.setItem('loading', false) }) } const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise => { @@ -78,7 +82,11 @@ const JoinSetting = forwardRef((_props: any, ref: any) => { token: res[0].data, tokenA: res[1].data, }) + } else { + storage.setItem('loading', false) } + }).catch(() => { + storage.setItem('loading', false) }) } const postRefresh = async (callBack: Function): Promise => { @@ -87,7 +95,11 @@ const JoinSetting = forwardRef((_props: any, ref: any) => { storage.setItem('user', JSON.stringify(res.data)) storage.setItem('userLogin', true) callBack(res.data) + } else { + storage.setItem('loading', false) } + }).catch(() => { + storage.setItem('loading', false) }) } return ( @@ -172,6 +184,7 @@ const JoinSetting = forwardRef((_props: any, ref: any) => { message.error('请输入会议号!') return } + storage.setItem('loading', true) isGetCheckoutRoomNum(roomNumber, (bool: boolean) => { if (bool) { getRoomRtcToken(roomNumber, (options: any) => { @@ -194,6 +207,8 @@ const JoinSetting = forwardRef((_props: any, ref: any) => { } }) } + }).finally(() => { + storage.setItem('loading', false) }) }) } diff --git a/src/page/Home/Index/index.tsx b/src/page/Home/Index/index.tsx index d24253a..765597c 100644 --- a/src/page/Home/Index/index.tsx +++ b/src/page/Home/Index/index.tsx @@ -87,6 +87,8 @@ const Index: React.FC = () => { tokenA: res[1].data, }) } + }).finally(() => { + storage.setItem('loading', false) }) } const postRefresh = async (callBack: Function): Promise => { @@ -95,7 +97,11 @@ const Index: React.FC = () => { storage.setItem('user', JSON.stringify(res.data)) storage.setItem('userLogin', true) callBack(res.data) + } else { + storage.setItem('loading', false) } + }).catch(() => { + storage.setItem('loading', false) }) } @@ -206,7 +212,7 @@ const Index: React.FC = () => {
- {role.ID.includes(userInfo.roleId) ? {userInfo.roleId === '1' ? { if (role.ID.includes(userInfo.roleId)) { joinSettingRef.current.changeModal(item.roomNum) } else { + storage.setItem('loading', true) postRefresh(() => { getRoomRtcToken(item.roomNum, (options: any) => { if (options) { diff --git a/src/utils/styles/App.scss b/src/utils/styles/App.scss index 1ca5229..f14107f 100644 --- a/src/utils/styles/App.scss +++ b/src/utils/styles/App.scss @@ -380,4 +380,8 @@ $pagination-hover-background-color: #5575F2; // ant-message .ant-message { -webkit-app-region: no-drag; +} + +.ant-spin-fullscreen { + z-index: 10000; } \ No newline at end of file