This commit is contained in:
yj 2024-07-12 15:31:51 +08:00
parent 0df7896ee5
commit 1674f227a3
13 changed files with 504 additions and 385 deletions

16
main.js
View File

@ -1,4 +1,4 @@
const { app, BrowserWindow, screen, Tray, nativeImage, Menu, ipcMain, clipboard, dialog } = require('electron'); const { app, BrowserWindow, screen, Tray, nativeImage, Menu, ipcMain, clipboard, dialog, webFrame } = require('electron');
const path = require('node:path') const path = require('node:path')
app.allowRendererProcessReuse = false; app.allowRendererProcessReuse = false;
let mainWindow = null; let mainWindow = null;
@ -136,20 +136,6 @@ app.on('ready', () => {
break; break;
} }
}); });
// 下载文件并放置选择的文件夹
ipcMain.handle('dwFile', (event, url) => {
dialog.showOpenDialog(mainWindow, {
properties: ['openDirectory']
}).then(result => {
if (!result.canceled) {
const selectedPath = result.filePaths[0];
console.log('Selected download folder:', selectedPath);
console.log(url);
}
}).catch(err => {
});
});
// 导出是否全屏 // 导出是否全屏
ipcMain.handle('getIsMaximized', () => { ipcMain.handle('getIsMaximized', () => {
return mainWindow.isMaximized(); return mainWindow.isMaximized();

View File

@ -16,9 +16,5 @@ window.electron = {
// 复制文字 // 复制文字
setWriteText: (text) => { setWriteText: (text) => {
return ipcRenderer.invoke('setWriteText', text) return ipcRenderer.invoke('setWriteText', text)
},
// 下载文件并放置选择的文件夹
dwFile: (url) => {
ipcRenderer.invoke('dwFile', url)
} }
} }

View File

@ -1,7 +1,7 @@
import { request } from '@/utils' import { request } from '@/utils'
export const GetCheckUser = (userName: string) => export const GetCheckUser = (account: string) =>
request({ request({
url: `/auth/check-user?userName=${userName}`, url: `/auth/check-user?account=${account}`,
method: 'get' method: 'get'
}) })

View File

@ -32,3 +32,9 @@ export const GetRoomFileDwUrl = (fileUrl: string, fileId: string) =>
method: 'get' method: 'get'
}) })
export const GetRoomUser = (roomNum: string) =>
request({
url: `/room/user?roomNum=${roomNum}`,
method: 'get'
})

BIN
src/assets/icon36.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 B

View File

@ -0,0 +1,85 @@
// 设置向导
.stupWizard {
display: flex;
flex-direction: column;
>div:nth-child(1) {
padding: 20px 0 0;
overflow-x: hidden;
max-height: 60vh;
overflow-y: auto;
>div:nth-child(1) {
color: #EEEEEE;
font-weight: bold;
font-size: 20px;
}
>div:nth-child(2) {
>div {
display: flex;
align-items: center;
margin-top: 22px;
>span {
flex-shrink: 0;
color: #828282;
font-size: 18px;
width: 130px;
}
}
}
>div:nth-child(3) {
margin-top: 34px;
>span {
color: #828282;
font-size: 18px;
}
>div {
display: flex;
align-items: center;
margin-top: 20px;
>img {
flex-shrink: 0;
width: 36px;
margin-right: 20px;
}
>div {
flex-grow: 1;
height: 40px;
position: relative;
>img {
height: 100%;
}
>div {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 0%;
overflow: hidden;
>img {
height: 100%;
}
}
}
}
}
}
>div:nth-child(2) {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: flex-end;
margin-top: 20px;
}
}

View File

@ -0,0 +1,197 @@
import styles from '@/components/StupWizard/index.module.scss'
import ImageUrl from '@/utils/package/imageUrl';
import { Button, message, Modal, Select, Slider } from 'antd';
import { useState, useImperativeHandle, forwardRef } from "react";
import agora from '@/utils/package/agora'
const StupWizard = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({
changeIsStupWizard: () => {
setIsStupWizard(true)
getAudioMediaList(true)
agora.startPlaybackDeviceTest()
agora.setPlaybackDeviceVolume(100)
}
}))
const [isStupWizard, setIsStupWizard] = useState(false);
const [stepsStatus, setStepsStatus] = useState<number>(1);
const [isVideoLoad, setIsVideoLoad] = useState<boolean>(false);
const [audioDeviceManager, setAudioDeviceManager] = useState<any>({
currentDevices: [],
currentDevice: {},
currentVolume: 0,
});
// agora.startRecordingDeviceTest(200)
const getAudioMediaList = (bool: boolean): void => {
const { currentDevices, currentVolume, currentDevice } = agora.getAudioMediaList(bool);
setAudioDeviceManager({
currentDevices: currentDevices.map((row: any) => {
return {
value: row.deviceId,
label: row.deviceName
}
}),
currentDevice: currentDevice.deviceId,
currentVolume,
})
}
// 音频设置向导
// 视频测试
return (
<>
<Modal title="设置向导" open={isStupWizard} footer={null} closable={false} centered width={'40vw'}>
<div className={styles.stupWizard}>
<div>
<div></div>
<div>
<div>
<span></span>
<Select
options={audioDeviceManager.currentDevices} style={{ flexGrow: 1 }}
value={audioDeviceManager.currentDevice} onChange={(e) => {
setAudioDeviceManager({
...audioDeviceManager,
currentDevice: e
})
agora.setPlaybackDevice(e)
}} />;
<audio src="" id='startAudio'></audio>
</div>
<div>
<span></span>
<Slider min={0} max={255} value={audioDeviceManager.currentVolume} onChange={(e) => {
setAudioDeviceManager({
...audioDeviceManager,
currentVolume: e
})
agora.setPlaybackDeviceVolume(e)
}} style={{ flexGrow: 1 }} />
</div>
</div>
<div>
<span></span>
<div>
<img src={ImageUrl.icon36} alt="" />
<div>
<img src={ImageUrl.icon34} alt="" />
<div id='recordingDeviceTest'>
<img src={ImageUrl.icon35} alt="" />
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<Button type="primary" className='m-ant-btn' onClick={() => {
}}></Button>
</div>
</div>
</div>
{/* <div className={styles.stupWizard}>
<div>
<div></div>
<div>
<div>
<span></span>
<Select
options={audioDeviceManager.currentDevices} style={{ flexGrow: 1 }}
value={audioDeviceManager.currentDevice} onChange={(e) => {
setAudioDeviceManager({
...audioDeviceManager,
currentDevice: e
})
agora.setRecordingDevice(e);
getAudioMediaList()
}} />;
<audio src="" id='startAudio'></audio>
</div>
{stepsStatus ? <div>
<span></span>
<Slider min={0} max={255} value={audioDeviceManager.currentVolume} onChange={(e) => {
setAudioDeviceManager({
...audioDeviceManager,
currentVolume: e
})
agora.setRecordingDeviceVolume(e)
}} style={{ flexGrow: 1 }} />
</div> :
<div>
<span></span>
<video id='startPreview'
poster={ImageUrl.error}
style={{ width: '226px', height: '136px', backgroundColor: 'black' }}>
</video>
</div>
}
</div>
<div>
<span></span>
<div>
<img src={ImageUrl.icon33} alt="" />
<div>
<img src={ImageUrl.icon34} alt="" />
<div id='recordingDeviceTest'>
<img src={ImageUrl.icon35} alt="" />
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<Button type="primary" className='m-ant-btn' onClick={() => {
let audio = document.getElementById('startAudio') as any;
if (audio.srcObject) {
const tracks = audio.srcObject.getTracks();
tracks.forEach((track: any) => {
track.stop();
});
audio.srcObject = null;
}
// setStepsStatus(false)
agora.startPreview().then((res: boolean) => {
setIsVideoLoad(res)
})
}}></Button>
</div>
<div>
<Button
type="primary"
style={{ backgroundColor: '#31353A', marginRight: '14px' }}
onClick={() => {
if (isVideoLoad) {
setIsVideoLoad(false)
agora.stopAudioDeviceLoopbackTest()
setStepsStatus(true)
agora.startRecordingDeviceTest(200)
} else {
message.error('视频加载中!')
}
}}
></Button>
<Button
type="primary"
className='m-ant-btn'
onClick={() => {
if (isVideoLoad) {
agora.stopAudioDeviceLoopbackTest()
agora.setRecordingDeviceVolume(audioDeviceManager.currentVolume)
setIsStupWizard(false)
setStepsStatus(true)
setIsVideoLoad(false)
} else {
message.error('视频加载中!')
}
}}
>
</Button>
</div>
</div>
</div> */}
</Modal>
</>
)
})
export default StupWizard

View File

@ -577,93 +577,6 @@
margin-top: 20px; margin-top: 20px;
} }
} }
// 设置向导
.stupWizard {
display: flex;
flex-direction: column;
>div:nth-child(1) {
padding: 20px 0 0;
overflow-x: hidden;
max-height: 60vh;
overflow-y: auto;
>div:nth-child(1) {
color: #EEEEEE;
font-weight: bold;
font-size: 20px;
}
>div:nth-child(2) {
>div {
display: flex;
align-items: center;
margin-top: 22px;
>span {
flex-shrink: 0;
color: #828282;
font-size: 18px;
width: 130px;
}
}
}
>div:nth-child(3) {
margin-top: 34px;
>span {
color: #828282;
font-size: 18px;
}
>div {
display: flex;
align-items: center;
margin-top: 20px;
>img {
flex-shrink: 0;
width: 36px;
margin-right: 20px;
}
>div {
flex-grow: 1;
height: 40px;
position: relative;
>img {
height: 100%;
}
>div {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 0%;
overflow: hidden;
>img {
height: 100%;
}
}
}
}
}
}
>div:nth-child(2) {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: flex-end;
margin-top: 20px;
}
}
// 共享文件 // 共享文件
.sharedFilesModel { .sharedFilesModel {
>div:nth-child(1) { >div:nth-child(1) {

View File

@ -1,24 +1,27 @@
import styles from '@/page/Meeting/index.module.scss' import styles from '@/page/Meeting/index.module.scss'
import { useEffect, useState } from "react"; import { useEffect, useRef, useState } from "react";
import Operation from '@/components/Operation'; import Operation from '@/components/Operation';
import { Navigation, Pagination } from 'swiper/modules'; import { Navigation, Pagination } from 'swiper/modules';
import { Swiper, SwiperSlide } from 'swiper/react'; import { Swiper, SwiperSlide } from 'swiper/react';
import 'swiper/css'; import 'swiper/css';
import 'swiper/css/navigation'; import 'swiper/css/navigation';
import 'swiper/css/pagination'; import 'swiper/css/pagination';
import { Button, Input, Popover, Modal, Checkbox, message, Select, Slider, Table, Pagination as AntdPagination } from "antd"; import { Button, Input, Popover, Modal, Checkbox, message, Table, Pagination as AntdPagination } from "antd";
import { DeleteOutlined, FolderOutlined, ProfileOutlined, ReloadOutlined, SearchOutlined, VerticalAlignBottomOutlined } from '@ant-design/icons'; import { DeleteOutlined, ProfileOutlined, ReloadOutlined, SearchOutlined, VerticalAlignBottomOutlined } from '@ant-design/icons';
import { useLocation, useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import { thumbImageBufferToBase64 } from '@/utils/package/base64' import { thumbImageBufferToBase64 } from '@/utils/package/base64'
import { storage } from '@/utils'; import { storage } from '@/utils';
import { GetRoomFile, PostRoomFile, DeleteRoomFile, GetRoomUpFileurl, GetRoomFileDwUrl } from '@/api/Meeting'; import { GetRoomFile, PostRoomFile, DeleteRoomFile, GetRoomUpFileurl, GetRoomFileDwUrl, GetRoomUser } from '@/api/Meeting';
import axios from 'axios'; import axios from 'axios';
import ImageUrl from '@/utils/package/imageUrl' import ImageUrl from '@/utils/package/imageUrl'
import agora from '@/utils/package/agora' import agora from '@/utils/package/agora'
import StupWizard from '@/components/StupWizard';
import { onInvoke } from '@/utils/package/signalr';
const { Column } = Table const { Column } = Table
const Meeting: React.FC = () => { const Meeting: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { state } = useLocation(); const { state } = useLocation();
const stupWizardRef = useRef<any>();
const [statusList, setStatusList] = useState({ const [statusList, setStatusList] = useState({
userList: false, userList: false,
userChatList: false, userChatList: false,
@ -27,7 +30,6 @@ const Meeting: React.FC = () => {
const [isSharedScreenModal, setIsSharedScreenModal] = useState(false); const [isSharedScreenModal, setIsSharedScreenModal] = useState(false);
const [isInit, setIsInit] = useState(true); const [isInit, setIsInit] = useState(true);
const [user, setUser] = useState<any>({}); const [user, setUser] = useState<any>({});
const [isStupWizard, setIsStupWizard] = useState(false);
const [showRowSelection, setShowRowSelection] = useState(false); const [showRowSelection, setShowRowSelection] = useState(false);
const [isSharedFilesModel, setIsSharedFilesModel] = useState(false); const [isSharedFilesModel, setIsSharedFilesModel] = useState(false);
const [sharedScreenList, setSharedScreenList] = useState<any>([]); const [sharedScreenList, setSharedScreenList] = useState<any>([]);
@ -97,11 +99,6 @@ const Meeting: React.FC = () => {
itemIndex: 0, itemIndex: 0,
rowIndex: 0, rowIndex: 0,
}); });
const [audioDeviceManager, setAudioDeviceManager] = useState<any>({
currentDevices: [],
currentDevice: {},
currentVolume: 0,
});
const [fileList, setFileList] = useState({ const [fileList, setFileList] = useState({
data: [], data: [],
keyword: '', keyword: '',
@ -109,27 +106,31 @@ const Meeting: React.FC = () => {
pageIndex: 1, pageIndex: 1,
pageSize: 10, pageSize: 10,
}) })
const [roomUserList, setRoomUserList] = useState<any>([])
const [stepsStatus, setStepsStatus] = useState<boolean>(true);
const [isVideoLoad, setIsVideoLoad] = useState<boolean>(false);
const [list] = useState<number[]>([1, 2, 3, 4, 5, 6, 7]) const [list] = useState<number[]>([1, 2, 3, 4, 5, 6, 7])
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [videoID, setVideoID] = useState('')
useEffect(() => { useEffect(() => {
if (isInit) { if (isInit) {
setUser(JSON.parse(storage.getItem('user') as string)) let userInfo = JSON.parse(storage.getItem('user') as string)
// agora.setJoinChannel({ agora.init()
// channelId: state.channelId, agora.setJoinChannel({
// userid: user.userName, channelId: state.channelId,
// token: state.token, userid: userInfo.account,
// }) token: state.token,
})
setUser(userInfo)
setIsInit(false) setIsInit(false)
window.addEventListener('customStorageChange', handleCustomStorageChange);
} else { } else {
getRoomFile() getRoomFile()
} }
return () => {
window.removeEventListener('customStorageChange', handleCustomStorageChange);
};
}, [fileList.pageIndex]); }, [fileList.pageIndex]);
// 操作按钮
const changeStatusList = async (row: any, itemIndex: number, rowIndex: number): Promise<void> => { const changeStatusList = async (row: any, itemIndex: number, rowIndex: number): Promise<void> => {
const footerListTemplate = [...footerList] const footerListTemplate = [...footerList]
setFooterListIndex({ setFooterListIndex({
@ -182,9 +183,7 @@ const Meeting: React.FC = () => {
agora.muteLocalVideoStream(false) agora.muteLocalVideoStream(false)
break; break;
case '设置向导': case '设置向导':
getAudioMediaList() stupWizardRef.current.changeIsStupWizard()
agora.startRecordingDeviceTest(200)
setIsStupWizard(true)
break; break;
case '录制': case '录制':
footerListTemplate[itemIndex][rowIndex].title = '录制中' footerListTemplate[itemIndex][rowIndex].title = '录制中'
@ -205,23 +204,19 @@ const Meeting: React.FC = () => {
} }
} }
const changeVdeio = async (bool: boolean): Promise<void> => { // 分享屏幕
if (bool) { const clickSharedScreen = async (): Promise<void> => {
let data = sharedScreenList.find((item: any) => item.sourceId === sharedScreenItem.sourceId)
if (data) {
const footerListTemplate = [...footerList]
footerListTemplate[footerListIndex.itemIndex][footerListIndex.rowIndex].title = '停止共享'
setIsSharedScreenModal(false)
agora.setDesktopCapturerVideo(sharedScreenItem)
} else { } else {
let data = sharedScreenList.find((item: any) => item.sourceId === sharedScreenItem.sourceId) message.error('请选择应用!')
if (data) {
const footerListTemplate = [...footerList]
footerListTemplate[footerListIndex.itemIndex][footerListIndex.rowIndex].title = '停止共享'
setIsSharedScreenModal(false)
agora.setDesktopCapturerVideo(sharedScreenItem)
setVideoID(agora.getVideoId())
} else {
message.error('请选择应用!')
}
} }
} }
// 获取桌面可共享屏幕的引用
const getDesktopCapturerVideo = (): void => { const getDesktopCapturerVideo = (): void => {
agora.getDesktopCapturerVideo().then((res: any) => { agora.getDesktopCapturerVideo().then((res: any) => {
if (sharedScreenList.length !== res.length) { if (sharedScreenList.length !== res.length) {
@ -238,20 +233,7 @@ const Meeting: React.FC = () => {
}) })
}; };
const getAudioMediaList = (): void => { // 获取共享文件列表
const { currentDevices, currentDevice, currentVolume } = agora.getAudioMediaList();
setAudioDeviceManager({
currentDevices: currentDevices.map((row: any) => {
return {
value: row.deviceId,
label: row.deviceName
}
}),
currentDevice: currentDevice.deviceId,
currentVolume,
})
}
const getRoomFile = async (): Promise<void> => { const getRoomFile = async (): Promise<void> => {
await GetRoomFile({ await GetRoomFile({
pageIndex: fileList.pageIndex, pageIndex: fileList.pageIndex,
@ -274,6 +256,33 @@ const Meeting: React.FC = () => {
}) })
} }
// 获取房间用户
const getRoomUser = async (): Promise<void> => {
await GetRoomUser(state.channelId).then(res => {
if (res.code === 200) {
setRoomUserList(res.data)
setTimeout(() => {
res.data.forEach((item: any) => {
agora.setVideo(Number(item.account), document.getElementById(`video-${item.account}`), state.channelId)
})
}, 1000)
}
})
}
const handleCustomStorageChange = (e: any): void => {
if (e.key === 'isJoin') {
if (e.value) {
onInvoke('joinChannel', {
roomNum: state.channelId
})
getRoomUser()
} else {
onInvoke('levelChannel', {
roomNum: state.channelId
})
}
}
};
return ( return (
<> <>
<div className={styles.meeting}> <div className={styles.meeting}>
@ -287,7 +296,7 @@ const Meeting: React.FC = () => {
</div> </div>
<div>00:13:45</div> <div>00:13:45</div>
</div> </div>
<div>2323235</div> <div>{state.channelId}</div>
<div className='drag'> <div className='drag'>
<div className={styles.meetingGrayButton}></div> <div className={styles.meetingGrayButton}></div>
<Operation></Operation> <Operation></Operation>
@ -313,11 +322,11 @@ const Meeting: React.FC = () => {
}} }}
onSlideChange={() => { }} onSlideChange={() => { }}
> >
{list.map((item, index) => {roomUserList.map((item: any, index: number) =>
<SwiperSlide key={item}> <SwiperSlide key={index}>
<div className={styles.meetingContentSwiperCard}> <div className={styles.meetingContentSwiperCard}>
<div className={styles.meetingContentSwiperCardVdeio} id={`video-${index}`}></div> <div className={styles.meetingContentSwiperCardVdeio} id={`video-${item.account}`}></div>
{meetingContentUser()} {meetingContentUser(item)}
</div> </div>
</SwiperSlide> </SwiperSlide>
)} )}
@ -325,7 +334,7 @@ const Meeting: React.FC = () => {
</div> </div>
<div className={`${styles.meetingContentVideo} drag`}> <div className={`${styles.meetingContentVideo} drag`}>
<div className={styles.meetingContentVideoDom}></div> <div className={styles.meetingContentVideoDom}></div>
{meetingContentUser()} {/* {meetingContentUser()} */}
</div> </div>
</div> </div>
{ {
@ -463,114 +472,12 @@ const Meeting: React.FC = () => {
}}></Checkbox> }}></Checkbox>
<div> <div>
<Button type="primary" onClick={() => { setIsSharedScreenModal(false) }} style={{ backgroundColor: '#31353A', marginRight: '14px' }}></Button> <Button type="primary" onClick={() => { setIsSharedScreenModal(false) }} style={{ backgroundColor: '#31353A', marginRight: '14px' }}></Button>
<Button type="primary" className='m-ant-btn' onClick={() => changeVdeio(false)}></Button> <Button type="primary" className='m-ant-btn' onClick={() => clickSharedScreen()}></Button>
</div> </div>
</div> </div>
</div> </div>
</Modal> </Modal>
<Modal title="设置向导" open={isStupWizard} footer={null} closable={false} centered width={'40vw'}> <StupWizard ref={stupWizardRef} />
<div className={styles.stupWizard}>
<div>
<div>{stepsStatus ? '音频设置向导' : '视频测试'}</div>
<div>
<div>
<span></span>
<Select
options={audioDeviceManager.currentDevices} style={{ flexGrow: 1 }}
value={audioDeviceManager.currentDevice} onChange={(e) => {
setAudioDeviceManager({
...audioDeviceManager,
currentDevice: e
})
agora.setRecordingDevice(e);
getAudioMediaList()
}} />;
<audio src="" id='startAudio'></audio>
</div>
{stepsStatus ? <div>
<span></span>
<Slider min={0} max={255} value={audioDeviceManager.currentVolume} onChange={(e) => {
setAudioDeviceManager({
...audioDeviceManager,
currentVolume: e
})
agora.setRecordingDeviceVolume(e)
}} style={{ flexGrow: 1 }} />
</div> :
<div>
<span></span>
<video id='startPreview'
poster={ImageUrl.error}
style={{ width: '226px', height: '136px', backgroundColor: 'black' }}>
</video>
</div>
}
</div>
{stepsStatus ? <div>
<span></span>
<div>
<img src={ImageUrl.icon33} alt="" />
<div>
<img src={ImageUrl.icon34} alt="" />
<div id='recordingDeviceTest'>
<img src={ImageUrl.icon35} alt="" />
</div>
</div>
</div>
</div> : null}
</div>
<div>
{stepsStatus ? <div>
<Button type="primary" className='m-ant-btn' onClick={() => {
let audio = document.getElementById('startAudio') as any;
if (audio.srcObject) {
const tracks = audio.srcObject.getTracks();
tracks.forEach((track: any) => {
track.stop();
});
audio.srcObject = null;
}
setStepsStatus(false)
agora.startPreview().then((res: boolean) => {
setIsVideoLoad(res)
})
}}></Button>
</div> :
<div>
<Button
type="primary"
style={{ backgroundColor: '#31353A', marginRight: '14px' }}
onClick={() => {
if (isVideoLoad) {
setIsVideoLoad(false)
agora.stopAudioDeviceLoopbackTest()
setStepsStatus(true)
agora.startRecordingDeviceTest(200)
} else {
message.error('视频加载中!')
}
}}
></Button>
<Button
type="primary"
className='m-ant-btn'
onClick={() => {
if (isVideoLoad) {
agora.stopAudioDeviceLoopbackTest()
agora.setRecordingDeviceVolume(audioDeviceManager.currentVolume)
setIsStupWizard(false)
setStepsStatus(true)
setIsVideoLoad(false)
} else {
message.error('视频加载中!')
}
}}
>
</Button>
</div>}
</div>
</div>
</Modal>
<Modal <Modal
title="共享文件" title="共享文件"
open={isSharedFilesModel} open={isSharedFilesModel}
@ -703,7 +610,11 @@ const Meeting: React.FC = () => {
<VerticalAlignBottomOutlined title='下载' style={{ color: '#5575F2', cursor: 'pointer' }} onClick={() => { <VerticalAlignBottomOutlined title='下载' style={{ color: '#5575F2', cursor: 'pointer' }} onClick={() => {
GetRoomFileDwUrl(item.fileUrl, item.id).then(res => { GetRoomFileDwUrl(item.fileUrl, item.id).then(res => {
if (res.code === 200) { if (res.code === 200) {
window.electron.dwFile(res.data) const downloadLink = document.createElement("a");
downloadLink.href = res.data;
downloadLink.download = item.fileName;
downloadLink.click();
getRoomFile()
} }
}) })
}} /> }} />
@ -730,16 +641,16 @@ const Meeting: React.FC = () => {
) )
} }
const meetingContentUser = () => { const meetingContentUser = (item: any) => {
return ( return (
<> <>
<div className={styles.meetingContentUser}> <div className={styles.meetingContentUser}>
<div className={styles.meetingContentUserRole}> {item.roleId === '1' ? <div className={styles.meetingContentUserRole}>
<img src={ImageUrl.icon32} alt="" /> <img src={ImageUrl.icon32} alt="" />
</div> </div> : null}
<div className={styles.meetingContentUserName}> <div className={styles.meetingContentUserName}>
<img src={ImageUrl.icon22} alt="" /> <img src={ImageUrl.icon22} alt="" />
<span></span> <span>{item.userName}</span>
</div> </div>
</div> </div>
</> </>

21
src/render.d.ts vendored
View File

@ -4,30 +4,9 @@ export interface IElectronAPI {
setViewStatus: (status: 'quit' | 'maximize' | 'minimize' | 'unmaximize') => void; setViewStatus: (status: 'quit' | 'maximize' | 'minimize' | 'unmaximize') => void;
getIsMaximized: () => Promise<boolean>; getIsMaximized: () => Promise<boolean>;
setWriteText: (text: string) => void; setWriteText: (text: string) => void;
dwFile: (url: string) => void;
}
export interface Agora {
getDesktopCapturerVideo: () => Promise<void>;
setDesktopCapturerVideo: (data: any) => void;
setCameraCapture: (data: any) => void;
getAudioMediaList: () => { currentDevice: any, currentDevices: any, currentVolume: number };
setRecordingDeviceVolume: (volume: number) => void;
setRecordingDevice: (deviceId: string) => void;
setJoinChannel: (data: { channelId: string, userid: string, token: string }) => Promise<void>;
getVideoId: () => string;
startRecordingDeviceTest: (indicationInterval: number) => void;
stopAudioDeviceLoopbackTest: () => void;
startPreview: () => Promise<boolean>;
startRecording: () => void;
stopRecording: () => void;
leaveChannel: () => void;
stopScreenCapture: () => void;
muteLocalAudioStream: (mute: boolean) => void;
muteLocalVideoStream: (mute: boolean) => void;
} }
declare global { declare global {
interface Window { interface Window {
electron: IElectronAPI; electron: IElectronAPI;
agora: Agora
} }
} }

View File

@ -1,3 +1,5 @@
import storage from "./storage";
const { const {
createAgoraRtcEngine, createAgoraRtcEngine,
ClientRoleType, ClientRoleType,
@ -10,89 +12,105 @@ const {
MediaRecorderStreamType MediaRecorderStreamType
} = require("agora-electron-sdk"); } = require("agora-electron-sdk");
const { message } = require('antd'); const { message } = require('antd');
const rtcEngine = createAgoraRtcEngine();
const option: any = { const option: any = {
appId: 'dcfc466a6ecb4a1f972630065dfb1e75', appId: 'dcfc466a6ecb4a1f972630065dfb1e75',
token: '', token: '',
channelId: '', channelId: '',
userid: '', userid: '',
} }
rtcEngine.initialize({
appId: option.appId,
});
rtcEngine.registerEventHandler({
// 监听本地用户加入频道事件
onJoinChannelSuccess: ({ channelId, localUid }: any, elapsed: any) => {
console.log({ channelId, localUid }, elapsed, '加入房间');
// 本地用户加入频道后,设置本地视频窗口
rtcEngine.setupLocalVideo({
renderMode: RenderModeType.RenderModeFit,
// sourceType: VideoSourceType.VideoSourceScreen,
sourceType: VideoSourceType.VideoSourceCameraPrimary,
uid: localUid,
view: agora.getDom(),
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
});
},
// 监听远端用户加入频道事件
onUserJoined: ({ channelId, localUid }: any, remoteUid: any, elapsed: any) => {
console.log({ channelId, localUid }, remoteUid, '远端加入频道');
// 远端用户加入频道后,设置远端视频窗口
rtcEngine.setupRemoteVideo(
{
renderMode: RenderModeType.RenderModeFit,
sourceType: VideoSourceType.VideoSourceRemote,
uid: remoteUid,
view: agora.getDom(),
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
},
{ channelId },
);
},
// 监听用户离开频道事件
onUserOffline: ({ channelId, localUid }: any, remoteUid: any, reason: any) => {
// 远端用户离开频道后,关闭远端视频窗口
console.log({ channelId, localUid }, remoteUid, '离开频道');
rtcEngine.setupRemoteVideo(
{
renderMode: RenderModeType.RenderModeFit,
sourceType: VideoSourceType.VideoSourceRemote,
uid: remoteUid,
view: agora.getDom(),
setupMode: VideoViewSetupMode.VideoViewSetupRemove,
},
);
},
// 视频发布状态改变回调
onVideoPublishStateChanged: (source: any, channel: any, oldState: any, newState: any, elapseSinceLastState: any) => {
if (newState === 1) {
}
},
// 音频发布状态改变回调
onAudioPublishStateChanged: (channel: any, oldState: any, newState: any, elapseSinceLastState: any) => {
if (newState === 1) {
}
},
// 用户音量提示回调。
onAudioVolumeIndication: (connection: any, speakers: any, speakerNumber: any, totalVolume: any,) => {
const percentage = (totalVolume / 255) * 100
const dom = document.getElementById('recordingDeviceTest') as any;
if (dom) {
dom.style.width = `${percentage}%`
}
}
});
let videoID: string = '';
let iMediaRecorder: any = ''; let iMediaRecorder: any = '';
let rtcEngine: any = '';
const agora = { const agora = {
getDom: () => { // 初始化
return document.getElementById('video-1'); init: () => {
rtcEngine = createAgoraRtcEngine();
rtcEngine.initialize({
appId: option.appId,
});
rtcEngine.registerEventHandler({
// 监听本地用户加入频道事件
onJoinChannelSuccess: (info: any, elapsed: any) => {
storage.setItem('isJoin', true)
},
// 监听远端用户加入频道事件
onUserJoined: (info: any, remoteUid: any, elapsed: any) => {
console.log(info, remoteUid, '远端加入频道');
// 远端用户加入频道后,设置远端视频窗口
rtcEngine.setupRemoteVideo(
{
renderMode: RenderModeType.RenderModeFit,
sourceType: VideoSourceType.VideoSourceRemote,
uid: remoteUid,
view: '',
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
},
{ channelId: info.channelId },
);
},
// 监听用户离开频道事件
onUserOffline: ({ channelId, localUid }: any, remoteUid: any, reason: any) => {
// 远端用户离开频道后,关闭远端视频窗口
console.log({ channelId, localUid }, remoteUid, '离开频道');
rtcEngine.setupRemoteVideo(
{
renderMode: RenderModeType.RenderModeFit,
sourceType: VideoSourceType.VideoSourceRemote,
uid: remoteUid,
view: '',
setupMode: VideoViewSetupMode.VideoViewSetupRemove,
},
);
},
// 视频发布状态改变回调
onVideoPublishStateChanged: (source: any, channel: any, oldState: any, newState: any, elapseSinceLastState: any) => {
if (newState === 1) {
}
},
// 音频发布状态改变回调
onAudioPublishStateChanged: (channel: any, oldState: any, newState: any, elapseSinceLastState: any) => {
if (newState === 1) {
}
},
// 用户音量提示回调。
onAudioVolumeIndication: (connection: any, speakers: any, speakerNumber: any, totalVolume: any,) => {
const percentage = (totalVolume / 255) * 100
const dom = document.getElementById('recordingDeviceTest') as any;
if (dom) {
dom.style.width = `${percentage}%`
}
}
});
},
// 渲染视频
setVideo: (localUid: number, view: any, channelId: string) => {
console.log(view, localUid, channelId);
if (option.userid === localUid) {
rtcEngine.setupLocalVideo({
renderMode: RenderModeType.RenderModeFit,
// sourceType: VideoSourceType.VideoSourceScreen,
sourceType: VideoSourceType.VideoSourceCameraPrimary,
uid: localUid,
view,
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
});
} else {
rtcEngine.setupRemoteVideo(
{
renderMode: RenderModeType.RenderModeFit,
sourceType: VideoSourceType.VideoSourceRemote,
uid: localUid,
view,
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
},
{ channelId },
);
}
}, },
// 离开频道 // 离开频道
leaveChannel: () => { leaveChannel: () => {
@ -101,6 +119,8 @@ const agora = {
stopAllEffect: true, stopAllEffect: true,
stopMicrophoneRecording: true, stopMicrophoneRecording: true,
}) })
rtcEngine.release()
storage.setItem('isJoin', false)
}, },
// 加入频道 // 加入频道
joinChannel: (bool: boolean) => { joinChannel: (bool: boolean) => {
@ -211,11 +231,19 @@ const agora = {
}); });
}, },
// 获取音频设备列表 // 获取音频设备列表
getAudioMediaList: () => { getAudioMediaList: (bool: boolean) => {
return { if (bool) {
currentDevice: rtcEngine.getAudioDeviceManager().getRecordingDefaultDevice(), return {
currentDevices: rtcEngine.getAudioDeviceManager().enumerateRecordingDevices(), currentDevices: rtcEngine.getAudioDeviceManager().enumeratePlaybackDevices(),
currentVolume: rtcEngine.getAudioDeviceManager().getRecordingDeviceVolume() currentDevice: rtcEngine.getAudioDeviceManager().getPlaybackDefaultDevice(),
currentVolume: 100,
}
} else {
return {
currentDevices: rtcEngine.getAudioDeviceManager().enumerateRecordingDevices(),
currentDevice: rtcEngine.getAudioDeviceManager().getRecordingDefaultDevice(),
currentVolume: rtcEngine.getAudioDeviceManager().getRecordingDeviceVolume()
}
} }
}, },
// 设置音频设备音量 // 设置音频设备音量
@ -226,10 +254,26 @@ const agora = {
setRecordingDevice: (deviceId: string) => { setRecordingDevice: (deviceId: string) => {
rtcEngine.getAudioDeviceManager().setRecordingDevice(deviceId) rtcEngine.getAudioDeviceManager().setRecordingDevice(deviceId)
}, },
// 启动音频播放设备测试。
startPlaybackDeviceTest: () => {
rtcEngine.getAudioDeviceManager().startPlaybackDeviceTest('https://wgshare.oss-cn-chengdu.aliyuncs.com/TestAudio.mp3')
},
// 停止音频播放设备测试。
stopPlaybackDeviceTest: () => {
rtcEngine.getAudioDeviceManager().stopPlaybackDeviceTest()
},
// 设置播放设备音量
setPlaybackDeviceVolume: (volume: number) => {
rtcEngine.getAudioDeviceManager().setPlaybackDeviceVolume(volume)
},
// 指定播放设备
setPlaybackDevice: (deviceId: string) => {
rtcEngine.getAudioDeviceManager().setPlaybackDevice(deviceId)
},
// 摄像头采集 // 摄像头采集
setCameraCapture: () => { setCameraCapture: () => {
rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, {}) rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, {})
// joinChannel(false) agora.joinChannel(false)
}, },
// 加入频道 // 加入频道
setJoinChannel: (data: any) => { setJoinChannel: (data: any) => {
@ -237,7 +281,7 @@ const agora = {
option.channelId = data.channelId; option.channelId = data.channelId;
option.userid = Number(data.userid); option.userid = Number(data.userid);
rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, {}) rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, {})
// joinChannel(false) agora.joinChannel(false)
}, },
// 取消或恢复发布本地音频流 // 取消或恢复发布本地音频流
muteLocalAudioStream: (mute: any) => { muteLocalAudioStream: (mute: any) => {
@ -247,18 +291,13 @@ const agora = {
muteLocalVideoStream: (mute: any) => { muteLocalVideoStream: (mute: any) => {
rtcEngine.muteLocalVideoStream(mute) rtcEngine.muteLocalVideoStream(mute)
}, },
// 获取当前生成的视频id
getVideoId: () => {
return videoID;
},
// 桌面捕获音频和视频的媒体源的信息 // 桌面捕获音频和视频的媒体源的信息
getDesktopCapturerVideo: async () => { getDesktopCapturerVideo: async () => {
return rtcEngine.getScreenCaptureSources({ width: 300, height: 300 }, { width: 300, height: 300 }, true); return rtcEngine.getScreenCaptureSources({ width: 300, height: 300 }, { width: 300, height: 300 }, true);
}, },
// 共享屏幕采集 // 共享屏幕采集
setDesktopCapturerVideo: (targetSource: any) => { setDesktopCapturerVideo: (targetSource: any) => {
// stopScreenCapture() agora.stopScreenCapture()
if ( if (
targetSource.type === targetSource.type ===
ScreenCaptureSourceType.ScreencapturesourcetypeScreen ScreenCaptureSourceType.ScreencapturesourcetypeScreen
@ -283,7 +322,7 @@ const agora = {
} }
); );
} }
// joinChannel(true) agora.joinChannel(true)
}, },
} }

View File

@ -43,6 +43,7 @@ import icon32 from '@/assets/icon32.png'
import icon33 from '@/assets/icon33.png' import icon33 from '@/assets/icon33.png'
import icon34 from '@/assets/icon34.png' import icon34 from '@/assets/icon34.png'
import icon35 from '@/assets/icon35.png' import icon35 from '@/assets/icon35.png'
import icon36 from '@/assets/icon36.png'
export default { export default {
avatar, avatar,
error, error,
@ -89,4 +90,5 @@ export default {
icon33, icon33,
icon34, icon34,
icon35, icon35,
icon36,
} }

View File

@ -21,7 +21,12 @@ export const onSignalr = (callBack: Function) => {
callBack(message) callBack(message)
}); });
} }
export const onInvoke = (message: string) => { export const onInvoke = (str: string, data: any) => {
connection.invoke("GetUserId", message) switch (str) {
case 'joinChannel':
case 'levelChannel':
connection.invoke(str, data.roomNum)
break;
}
} }