diff --git a/src/api/Meeting/index.ts b/src/api/Meeting/index.ts index 01af128..a754d1e 100644 --- a/src/api/Meeting/index.ts +++ b/src/api/Meeting/index.ts @@ -92,4 +92,9 @@ export const PostRoomInvite = (roomId: string, data: any) => url: `/room/invite?roomId=${roomId}`, method: 'post', data - }) \ No newline at end of file + }) +export const GetShowUser = (roomNum: string) => + request({ + url: `/room/show-user?roomNum=${roomNum}`, + method: 'get' + }) diff --git a/src/components/SharedFilesModel/index.module.scss b/src/components/SharedFilesModel/index.module.scss index 4343829..32c389b 100644 --- a/src/components/SharedFilesModel/index.module.scss +++ b/src/components/SharedFilesModel/index.module.scss @@ -1,35 +1,47 @@ .sharedFilesModel { - > div:nth-child(1) { + >div:nth-child(1) { display: flex; align-items: center; justify-content: space-between; - > span { - color: #EEEEEE; - font-size: 16px; - } - - > div { + >div:nth-child(1) { display: flex; align-items: center; - > span { + >span { + color: #EEEEEE; + font-size: 16px; + } + + >div { + display: flex; + align-items: center; + margin-left: 10px; + + >span { + color: #EEEEEE; + font-size: 14px; + white-space: nowrap; + } + } + } + + >div:nth-child(2) { + display: flex; + align-items: center; + + >span { margin-right: 20px; cursor: pointer; font-size: 16px; } } + + + } - > div:nth-child(2) { + >div:nth-child(2) { margin: 20px 0; } - - .updateDiv { - color: #ffffff; - - .ant-progress .ant-progress-text { - color: #ffffff; - } - } -} +} \ No newline at end of file diff --git a/src/components/SharedFilesModel/index.tsx b/src/components/SharedFilesModel/index.tsx index 1b2d7ed..0cdc223 100644 --- a/src/components/SharedFilesModel/index.tsx +++ b/src/components/SharedFilesModel/index.tsx @@ -6,14 +6,14 @@ import { SearchOutlined, VerticalAlignBottomOutlined } from '@ant-design/icons'; -import {Button, Input, message, Modal, Pagination, Progress, Table} from 'antd'; -import {forwardRef, useEffect, useImperativeHandle, useState} from "react"; -import {DeleteRoomFile, GetRoomFile, GetRoomFileDwUrl, GetRoomUpFileurl, PostRoomFile} from '@/api/Meeting'; +import { Button, Input, message, Modal, Pagination, Progress, Table } from 'antd'; +import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; +import { DeleteRoomFile, GetRoomFile, GetRoomFileDwUrl, GetRoomUpFileurl, PostRoomFile } from '@/api/Meeting'; import axios from 'axios'; -import {useLocation} from 'react-router-dom'; -import {storage} from '@/utils'; +import { useLocation } from 'react-router-dom'; +import { storage } from '@/utils'; -const {Column} = Table +const { Column } = Table const SharedFilesModel = forwardRef((props: any, ref: any) => { useImperativeHandle(ref, () => ({ @@ -21,7 +21,7 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => { setIsSharedFilesModel(true) } })) - const {state} = useLocation(); + const { state } = useLocation(); const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [showRowSelection, setShowRowSelection] = useState(false); const [isSharedFilesModel, setIsSharedFilesModel] = useState(false); @@ -74,18 +74,24 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => { >
-
- 共{fileList.total}个文件 +
- + + 共{fileList.total}个文件 + +
+ 上传进度: + {/* {uploadProgress === 100 ? '上传完成' : '上传中'} */} + +
-
+
} + style={{ width: '200px' }} + prefix={} onChange={(e) => { setFileList({ ...fileList, @@ -122,10 +128,10 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => { pageIndex: 1 }) } - }}/> + }} /> { setShowRowSelection(!showRowSelection) - }} style={{color: showRowSelection ? '#5575F2' : 'white'}}/> + }} style={{ color: showRowSelection ? '#5575F2' : 'white' }} /> {showRowSelection ? { if (selectedRowKeys.length) { DeleteRoomFile(selectedRowKeys).then(res => { @@ -137,57 +143,56 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => { } else { message.error('请选择文件!') } - }}/> : null} -
@@ -202,130 +207,97 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => { } : undefined} dataSource={fileList.data} pagination={false} - scroll={{y: '40vh'}} - style={{width: '100%'}} + scroll={{ y: '40vh' }} + style={{ width: '100%' }} > - - + ( + <> +
+ {item.fileName} + + {item.showPercentComplete ? : null} + +
+ + )} + /> + ( <> {item.size / 1024 > 1000 ? (item.size / (1024 * 1024)).toFixed(2) + 'MB' : (item.size / 1024).toFixed(2) + 'KB'} - )}/> - + )} /> + ( - <> - {item.downloadCount}次 - - )} + render={(item) => ( + <> + {item.downloadCount}次 + + )} /> ( <> { - GetRoomFileDwUrl(item.fileUrl, item.id).then(res => { - console.log(res) - if (res.code === 200) { - // const downloadLink = document.createElement("a"); - // downloadLink.href = res.data; - // downloadLink.download = item.fileName; - axios({ - url: res.data, - method: 'GET', - // headers: { - // "Authorization": `Bearer ${user.token}`, - // "responseType": 'stream' - // }, - onDownloadProgress: (progressEvent: any) => { - const totalLength = item.size; - console.log(2222222) - if (totalLength !== null) { - const percentComplete = (progressEvent.loaded / totalLength * 100).toFixed(2); - console.log(`下载进度: ${percentComplete}%`); - item.percentComplete = percentComplete - } - } - }).then(() => { - const downloadLink = document.createElement("a"); - downloadLink.href = res.data; - downloadLink.download = item.fileName; - downloadLink.click(); - getRoomFile() - }) - // downloadLink.click(); - // getRoomFile() - } - }) + style={{ color: '#5575F2', cursor: 'pointer' }} + onClick={async () => { + GetRoomFileDwUrl(item.fileUrl, item.id).then(res => { + if (res.code === 200) { + axios({ + url: res.data, + method: 'GET', + onDownloadProgress: (progressEvent: any) => { + const totalLength = item.size; + if (totalLength !== null) { + const percentComplete = (progressEvent.loaded / totalLength * 100).toFixed(2); + const fileIndex = fileList.data.findIndex((row: any) => row.id === item.id); + let fileItem = [...fileList.data] as any; + fileItem[fileIndex].percentComplete = percentComplete + fileItem[fileIndex].showPercentComplete = true + setFileList({ + ...fileList, + data: fileItem, + }) + } + } + }).then(() => { + const fileIndex = fileList.data.findIndex((row: any) => row.id === item.id); + let fileItem = [...fileList.data] as any; + fileItem[fileIndex].percentComplete = 100 + setTimeout(() => { + fileItem[fileIndex].showPercentComplete = false + }, 3000) + setFileList({ + ...fileList, + data: fileItem, + }) + const downloadLink = document.createElement("a"); + downloadLink.href = res.data; + downloadLink.download = item.fileName; + downloadLink.click(); + getRoomFile() + }) + } + }) - // const url = "https://wgshare.oss-cn-chengdu.aliyuncs.com/share_file/559167236182085/20421555/b24e2c41f47140308c47cdd77e6305c7.jpg?Expires=1722923931&OSSAccessKeyId=LTAI5tQYVQHkkXxXTmjwiSDv&Signature=eJTWD93ifV2v1R6XCHSOa6j1R%2FE%3D" - // axios({ - // url, - // method: 'GET', - // // headers: { - // // "Authorization": `Bearer ${user.token}`, - // // "responseType": 'stream' - // // }, - // onDownloadProgress: (progressEvent: any) => { - // const totalLength = item.size; - // console.log(2222222) - // if (totalLength !== null) { - // const percentComplete = (progressEvent.loaded / totalLength * 100).toFixed(2); - // console.log(`下载进度: ${percentComplete}%`); - // } - // } - // }) - // .then((response: any) => { - // const downloadLink = document.createElement("a"); - // downloadLink.href = response.data; - // downloadLink.download = item.fileName; - // downloadLink.click(); - // getRoomFile() - // }) - - // await axios.get(`${import.meta.env.VITE_BASE_URL_API}/room/file-dw-url?fileUrl=${item.fileUrl}&fileId=${item.id}`, { - // headers: { - // "Authorization": `Bearer ${user.token}`, - // "responseType": 'stream' - // }, - // onDownloadProgress: (progressEvent: any) => { - // const totalLength = item.size; - // const percentComplete = (progressEvent.loaded / totalLength * 100).toFixed(2); - // console.log(`下载进度: ${percentComplete}%`); - // } - // }) - // await axios.get(`${import.meta.env.VITE_BASE_URL_API}/room/file-dw-url?fileUrl=${item.fileUrl}&fileId=${item.id}`, { - // headers: { - // "ResponseType": "blob", - // "Authorization": `Bearer ${user.token}` - // }, - // withCredentials: false, - // onDownloadProgress: (progressEvent: any) => { - // console.log(progressEvent, 33333) - // // 获取上传进度 - // const {loaded, total} = progressEvent; - // const progress = Math.round((loaded * 100) / total); - // console.log(`下载进度: ${progress}%`); - // setUploadProgress(progress) - // } - // }) - }}/> + }} /> {/* */} - )}/> + )} /> -
+
{ setFileList({ ...fileList, pageIndex: e }) - }} pageSize={fileList.pageSize} current={fileList.pageIndex} hideOnSinglePage={true}/> + }} pageSize={fileList.pageSize} current={fileList.pageIndex} hideOnSinglePage={true} />
-
+
diff --git a/src/page/Home/Index/index.tsx b/src/page/Home/Index/index.tsx index 54bf1fe..f395d3b 100644 --- a/src/page/Home/Index/index.tsx +++ b/src/page/Home/Index/index.tsx @@ -187,7 +187,7 @@ const Index: React.FC = () => { maxLength={8} value={createRoomFrom.roomNum} onChange={(e) => { - const regex = /^[0-9 ]*$/; + const regex = /^[0-9]*$/; if (regex.test(e.target.value)) { setCreateRoomFrom({ ...createRoomFrom, diff --git a/src/page/Home/User/index.tsx b/src/page/Home/User/index.tsx index 4c72aaa..a7ce234 100644 --- a/src/page/Home/User/index.tsx +++ b/src/page/Home/User/index.tsx @@ -225,10 +225,13 @@ const User: React.FC = () => { showCount={true} value={addUserFrom.Account} onChange={(e) => { - setAddUserFrom({ - ...addUserFrom, - Account: e.target.value, - }); + const regex = /^[0-9]*$/; + if (regex.test(e.target.value)) { + setAddUserFrom({ + ...addUserFrom, + Account: e.target.value, + }); + } }} />
diff --git a/src/page/Login/index.tsx b/src/page/Login/index.tsx index c3c817b..9dcc27b 100644 --- a/src/page/Login/index.tsx +++ b/src/page/Login/index.tsx @@ -82,7 +82,6 @@ const Login: React.FC = () => { if (!operation.account) { return message.error('请输入账号!') } - operation.password = ""; GetCheckUser(operation.account).then(res => { if (res.code === 200) { res.data ? setAccountPasswordStatus(true) : message.error('账号不存在!') diff --git a/src/page/Meeting/index.module.scss b/src/page/Meeting/index.module.scss index 0e02401..7347819 100644 --- a/src/page/Meeting/index.module.scss +++ b/src/page/Meeting/index.module.scss @@ -694,7 +694,7 @@ .active { >div { - // border: 1px solid #EBEBEB; + border: 1px solid #EBEBEB; } } } diff --git a/src/page/Meeting/index.tsx b/src/page/Meeting/index.tsx index b293bcd..041d853 100644 --- a/src/page/Meeting/index.tsx +++ b/src/page/Meeting/index.tsx @@ -8,7 +8,7 @@ import { SearchOutlined } 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, PostRoomManager, DeleteRoomManager, GetRoomKickout } from '@/api/Meeting'; +import { GetRoomUser, PostOpenMicr, PostOpenCamera, PostRoomManager, DeleteRoomManager, GetRoomKickout, GetShowUser } from '@/api/Meeting'; import ImageUrl from '@/utils/package/ImageUrl' import agora from '@/utils/package/agora' import { onInvoke, onSignalr, offSignalr, onStart } from '@/utils/package/signalr'; @@ -112,6 +112,7 @@ const Meeting: React.FC = () => { const [meetingMode, setMeetingMode] = useState('') const [userSearchValue, setUserSearchValue] = useState('') const [noViewChatList, setNoViewChatList] = useState(0) + const [currentLookUserAccount, setCurrentLookUserAccount] = useState('') useEffect(() => { let time = null as any; let userInfo = JSON.parse(storage.getItem('user') as string) @@ -131,7 +132,9 @@ const Meeting: React.FC = () => { account: Number(info.localUid), view: document.getElementById(`video-${info.localUid}`) as HTMLElement, channelId: info.channelId, + sourceType: VideoSourceType.VideoSourceCameraPrimary, }) + getShowUser(); }, 1000); }, onUserJoined: async (info: any, remoteUid: any, _elapsed: any) => { @@ -155,7 +158,7 @@ const Meeting: React.FC = () => { }, 1000); } }) - agora.setCameraCapture(VideoSourceType.VideoSourceCameraPrimary) + agora.setCameraCapture() agora.setJoinChannel({ channelId: state.channelId, userid: userInfo.account, @@ -249,6 +252,27 @@ const Meeting: React.FC = () => { }, [currentVideoId]) + useEffect(() => { + const userItem = allUserList.find((item: any) => item.account === currentLookUserAccount) + if (userItem) { + if (userItem.account === user.account) { + // agora.setupLocalVideo({ + // account: Number(userItem.account), + // view: document.getElementById(`look-video`) as HTMLElement, + // channelId: state.channelId, + // sourceType: VideoSourceType.VideoSourceCameraPrimary, + // }) + } else { + // agora.setupRemoteVideoJoin({ + // account: Number(userItem.account), + // view: document.getElementById(`look-video`) as HTMLElement, + // channelId: state.channelId, + // sourceType: VideoSourceType.VideoSourceCameraPrimary, + // }) + } + } + }, [allUserList, currentLookUserAccount]) + // 网络 const handleNetworkChange = (): void => { if (navigator.onLine) { @@ -267,6 +291,14 @@ const Meeting: React.FC = () => { message.error('网络已断开!') } } + // 全员观看 + const getShowUser = async (): Promise => { + await GetShowUser(state.channelId).then(res => { + if (res.code === 200 && res.data) { + setCurrentLookUserAccount(res.data) + } + }) + } // 加入房间时间 const changeCurrentSeconds = (): string => { const duration = dayjs.duration(currentSeconds, 'seconds'); @@ -304,7 +336,7 @@ const Meeting: React.FC = () => { setIsSharedScreenModal(true) break; case '停止共享': - agora.setCameraCapture(VideoSourceType.VideoSourceCameraPrimary) + agora.setCameraCapture() footerListTemplate[itemIndex][rowIndex].title = '共享屏幕' break; case '关闭声音': @@ -386,7 +418,7 @@ const Meeting: React.FC = () => { const footerListTemplate = [...footerList] footerListTemplate[footerListIndex.itemIndex][footerListIndex.rowIndex].title = '停止共享' setIsSharedScreenModal(false) - agora.setDesktopCapturerVideo(sharedScreenItem, VideoSourceType.VideoSourceScreen) + agora.setDesktopCapturerVideo(sharedScreenItem) } else { message.error('请选择应用!') } @@ -538,8 +570,8 @@ const Meeting: React.FC = () => { return '' } // 设置单个视频样式 - const setMeetingContentSwiperCardClass = (account: string): string => { - if (currentVideoId === account && (meetingMode === 'StandardMode' || meetingMode === 'SpeakerMode')) { + const setMeetingContentSwiperCardClass = (account: string, bool: boolean = false): string => { + if ((bool || currentVideoId === account) && (meetingMode === 'StandardMode' || meetingMode === 'SpeakerMode')) { switch (meetingMode) { case 'StandardMode': return styles.meetingContentSwiperCardStandardMode @@ -569,11 +601,12 @@ const Meeting: React.FC = () => {
+ {/* ${setMeetingContentSwiperCardClass(item.account)} */} {allUserList.map((item: any, index: number) => { return ( item.isRoom ?
{ if (footerList[1][3].active) { @@ -593,6 +626,19 @@ const Meeting: React.FC = () => { ) } )} + {currentLookUserAccount ? +
+
+ +
+
+ : +
+
+ 暂无视频 +
+
+ }
{ diff --git a/src/utils/package/agora.ts b/src/utils/package/agora.ts index 2a91160..d07920b 100644 --- a/src/utils/package/agora.ts +++ b/src/utils/package/agora.ts @@ -17,7 +17,6 @@ const option: any = { token: '', channelId: '', userid: '', - sourceType: VideoSourceType.VideoSourceCameraPrimary, } let iMediaRecorder: any = ''; let rtcEngine: any = ''; @@ -80,19 +79,11 @@ const agora = { setupLocalVideo: async (item: any) => { await rtcEngine.setupLocalVideo({ renderMode: RenderModeType.RenderModeFit, - sourceType: option.sourceType, + sourceType: item.sourceType, uid: item.account, view: item.view, setupMode: VideoViewSetupMode.VideoViewSetupAdd, }); - switch (option.sourceType) { - case VideoSourceType.VideoSourceCameraPrimary: - agora.updateChannelMediaOptions(true) - break; - case VideoSourceType.VideoSourceScreen: - agora.updateChannelMediaOptions(false) - break; - } }, // 远端加入 setupRemoteVideoJoin: async (item: any) => { @@ -135,33 +126,14 @@ const agora = { }, // 加入频道 joinChannel: () => { - rtcEngine.joinChannel(option.token, option.channelId, option.userid, {}); - }, - // 更新频道信息 - updateChannelMediaOptions: (bool: boolean) => { - if (bool) { - // 摄像头 - rtcEngine.updateChannelMediaOptions({ - channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting, //设置频道场景为直播场景 - clientRoleType: ClientRoleType.ClientRoleBroadcaster, //设置用户角色为主播;如果要将用户角色设置为观众,保持默认值即可 - publishMicrophoneTrack: true, //发布麦克风采集的音频 - publishCameraTrack: true, //发布摄像头采集的视频 - publishScreenTrack: false, //设置是否发布屏幕采集的视频 - autoSubscribeAudio: true, //自动订阅所有音频流 - autoSubscribeVideo: true, //自动订阅所有视频流 - }) - } else { - // 屏幕 - rtcEngine.updateChannelMediaOptions({ - channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting, //设置频道场景为直播场景 - clientRoleType: ClientRoleType.ClientRoleBroadcaster, //用户角色 1主播 2观众 - publishMicrophoneTrack: true, //设置是否发布麦克风采集到的音频 - publishCameraTrack: false, //设置是否发布摄像头采集的视频 - publishScreenTrack: true, //设置是否发布屏幕采集的视频 - autoSubscribeAudio: true, //设置是否自动订阅所有音频流 - autoSubscribeVideo: true, //设置是否自动订阅所有视频流 - }) - } + rtcEngine.joinChannel(option.token, option.channelId, option.userid, { + autoSubscribeAudio: true, + autoSubscribeVideo: true, + publishMicrophoneTrack: true, + publishCameraTrack: true, + clientRoleType: ClientRoleType.ClientRoleBroadcaster, + publishScreenTrack: false, + }); }, // 停止共享屏幕 stopScreenCapture: () => { @@ -176,17 +148,15 @@ const agora = { rtcEngine.muteLocalVideoStream(mute) }, // 摄像头采集 - setCameraCapture: (sourceType: number) => { + setCameraCapture: () => { agora.stopScreenCapture() - if (sourceType !== option.sourceType) { - rtcEngine.destroyRendererByConfig(option.sourceType, option.channelId, option.account) - option.sourceType = sourceType - agora.setupLocalVideo({ - account: Number(option.userid), - view: document.getElementById(`video-${option.userid}`), - channelId: option.channelId, - }) - } + // rtcEngine.destroyRendererByConfig(option.sourceType, option.channelId, option.account) + agora.setupLocalVideo({ + account: Number(option.userid), + view: document.getElementById(`video-${option.userid}`), + channelId: option.channelId, + sourceType: VideoSourceType.VideoSourceCameraPrimary, + }) rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, {}) }, // 加入频道 @@ -201,18 +171,28 @@ const agora = { return rtcEngine.getScreenCaptureSources({ width: 300, height: 300 }, { width: 300, height: 300 }, true); }, // 共享屏幕采集 - setDesktopCapturerVideo: async (targetSource: any, sourceType: number) => { - if (sourceType !== option.sourceType) { - await rtcEngine.stopCameraCapture(option.sourceType) - rtcEngine.destroyRendererByConfig(option.sourceType, option.channelId, option.account) - option.sourceType = sourceType - agora.setupLocalVideo({ - account: Number(option.userid), - view: document.getElementById(`video-${option.userid}`), - channelId: option.channelId, - }) - } + setDesktopCapturerVideo: async (targetSource: any) => { + // await rtcEngine.stopCameraCapture(option.sourceType) + // rtcEngine.destroyRendererByConfig(option.sourceType, option.channelId, option.account) + await agora.setupLocalVideo({ + account: Number(option.userid), + view: document.getElementById(`look-video`), + channelId: option.channelId, + sourceType: VideoSourceType.VideoSourceScreen, + }) agora.stopScreenCapture(); + rtcEngine.joinChannelEx( + option.token, + { channelId: option.channelId, localUid: option.account }, + { + autoSubscribeAudio: false, + autoSubscribeVideo: false, + publishMicrophoneTrack: false, + publishCameraTrack: false, + clientRoleType: ClientRoleType.ClientRoleBroadcaster, + publishScreenTrack: true, + } + ); if ( targetSource.type === ScreenCaptureSourceType.ScreencapturesourcetypeScreen diff --git a/src/utils/request/request.ts b/src/utils/request/request.ts index f622aa9..b7d4a38 100644 --- a/src/utils/request/request.ts +++ b/src/utils/request/request.ts @@ -58,6 +58,12 @@ class Request { message.error(resData.message) } } + if (resData.code === 1403) { + setTimeout(() => { + storage.removeItem('user') + location.href = location.origin + '/#/login' + }, 3000) + } return resData }, (err: any) => {