会议监控

This commit is contained in:
yj 2024-08-29 13:38:41 +08:00
parent 3998bed6ff
commit 47c4626171
8 changed files with 363 additions and 377 deletions

41
main.js
View File

@ -16,7 +16,6 @@ const { autoUpdater, CancellationToken } = require('electron-updater');
const cancellationToken = new CancellationToken()
app.allowRendererProcessReuse = false;
let mainWindow = null;
let newWindow = null;
let isMaximized = false;
let env;
@ -263,46 +262,6 @@ app.on('ready', () => {
const y = Math.round((display.workArea.height - mainWindow.getSize()[1]) / 2);
mainWindow.setPosition(x, y);
});
// 打开新窗口
ipcMain.handle('oepnWindow', (event, data) => {
if (newWindow) {
newWindow.focus();
} else {
newWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 1200,
minHeight: 800,
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
enableRemoteModule: true,
nodeIntegrationInWorker: true,
allowMediaDevices: true,
preload: path.join(__dirname, 'preload.js')
},
frame: false,
backgroundColor: '#00000000',
transparent: true,
});
newWindow.loadURL(data.url);
newWindow.focus();
newWindow.webContents.on('before-input-event', (event, input) => {
if (env === 'development') {
if (input.key === 'F12') {
newWindow.webContents.openDevTools()
}
}
});
}
});
// 关闭会议监控窗口
ipcMain.handle('closeMonitorWindow', () => {
if (newWindow) {
newWindow.close();
newWindow = null
}
});
}
});
// 检测更新在你想要检查更新的时候执行renderer事件触发后的操作自行编写

View File

@ -60,13 +60,5 @@ window.electron = {
// 下载文件
downFile: (callback) => {
ipcRenderer.on('downFile', callback)
},
// 打开新窗口
oepnWindow: (data) => {
ipcRenderer.invoke('oepnWindow', data)
},
// 关闭会议监控窗口
closeMonitorWindow: () => {
ipcRenderer.invoke('closeMonitorWindow')
}
}

View File

@ -17,7 +17,6 @@ import { PostLogin } from "@/api/Login";
import { agora } from "@/utils/package/agora";
import QuitTips from "@/components/QuitTips";
import { GetLeave } from "@/api/Meeting";
import UserVideo from "./page/UserVideo";
import path from "path";
const fs = require('fs').promises;
const { exec } = require('child_process');
@ -33,7 +32,6 @@ const App: React.FC = () => {
});
const [spinning, setSpinning] = useState(false);
const [isState, setIsState] = useState(true);
if (location.href.indexOf('/userVideo') === -1) {
useEffect(() => {
let userInfo = JSON.parse(storage.getItem('user') as string)
let loginInfo = JSON.parse(storage.getItem('login') as string)
@ -150,11 +148,7 @@ const App: React.FC = () => {
if (location.href.indexOf('/login') !== -1) {
onStop()
}
if (location.href.indexOf('/meeting') === -1) {
window.electron.closeMonitorWindow()
}
}, [navigate])
}
useEffect(() => {
document.addEventListener('keydown', (event) => {
if (event.key === 'F11') {
@ -259,7 +253,6 @@ const App: React.FC = () => {
</Route>
<Route path='/login' element={<Login />} />
<Route path='/meeting' element={<Meeting />} />
<Route path='/userVideo' element={<UserVideo />} />
<Route path='*' element={<NotFound />} />
</Routes>
<Spin spinning={spinning} fullscreen />

View File

@ -2,40 +2,24 @@
width: 100%;
height: 100%;
background-color: rgb(7, 9, 11);
padding: 20px;
box-sizing: border-box;
display: flex;
flex-direction: column;
.userVideoTitle {
text-align: center;
color: white;
font-size: 20px;
flex-shrink: 0;
position: relative;
>span {
cursor: pointer;
position: absolute;
right: 20px;
top: 50%;
transform: translate(0, -50%);
}
}
.userVideoContent {
flex-grow: 1;
display: flex;
flex-direction: column;
padding: 20px 40px 0;
.userVideoContentHeader {
flex-shrink: 0;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
margin-bottom: 10px;
padding: 0 10px;
padding: 10px;
box-sizing: border-box;
>div:nth-child(1) {
display: flex;
@ -45,12 +29,16 @@
background-color: #181A1D;
border-radius: 4px;
padding: 4px 10px;
box-sizing: border-box;
margin: 0 10px 0 0;
display: flex;
align-items: center;
>span {
color: #EEEEEE;
font-size: 14px;
margin-right: 10px;
font-size: 12px;
white-space: nowrap;
margin-right: 4px;
}
}
}
@ -61,8 +49,8 @@
>span {
color: #EEEEEE;
font-size: 14px;
margin-right: 10px;
font-size: 12px;
margin-right: 4px;
}
}
}
@ -78,7 +66,7 @@
.userVideoContentListItem {
height: 20%;
width: calc(100% / 4 - 20px);
width: calc(100% / 3 - 20px);
padding: 10px;
.userVideoContentListItemVideo {

View File

@ -1,13 +1,14 @@
import styles from '@/components/UserVideo/index.module.scss'
import { GetPolling } from '@/api/Meeting';
import styles from '@/page/UserVideo/index.module.scss'
import { storage } from '@/utils';
import { agora } from '@/utils/package/agora';
import { CloseOutlined } from '@ant-design/icons';
import { Button, Empty, Select, message } from 'antd';
import { useEffect, useState } from "react";
import { useLocation } from 'react-router';
const UserVideo: React.FC = () => {
let userInfo = JSON.parse(storage.getItem('user') as string)
const { state } = useLocation();
const [user, setUser] = useState<any>({});
const [from, setFrom] = useState<any>({
cycleIntervalList: [
@ -77,15 +78,9 @@ const UserVideo: React.FC = () => {
}
};
// 获取地址栏参数
const getQueryParameterRegex = (name: string): string | null => {
const reg = new RegExp(`[?&]${name}=([^&#]*)`);
const results = window.location.href.match(reg);
return results === null ? null : results[1];
}
// 获取轮训用户
const getPolling = async (): Promise<void> => {
GetPolling(getQueryParameterRegex('channelId')?.split('1')[0] as string, from.viewPeopleValue).then((res: any) => {
GetPolling(state.channelId?.split('1')[0] as string, from.viewPeopleValue).then((res: any) => {
if (res.code === 200) {
setUserList(res.data)
}
@ -94,13 +89,7 @@ const UserVideo: React.FC = () => {
return (
<>
<div className={styles.userVideo}>
<div className={styles.userVideoTitle}>
<CloseOutlined className='drag' onClick={() => {
window.electron.closeMonitorWindow()
}} />
</div>
<div className={`${styles.userVideoContent} drag`}>
<div className={`${styles.userVideoContent}`}>
<div className={styles.userVideoContentHeader}>
<div>
<div>
@ -108,6 +97,7 @@ const UserVideo: React.FC = () => {
<Select
placeholder='请选择循环间隔'
options={from.cycleIntervalList}
size={'small'}
value={from.cycleIntervalValue} onChange={(e) => {
setFrom({ ...from, cycleIntervalValue: e })
setTimeNumber(e)
@ -118,6 +108,7 @@ const UserVideo: React.FC = () => {
<Select
placeholder='请选择查看人数'
options={from.viewPeople}
size={'small'}
value={from.viewPeopleValue} onChange={(e) => {
setFrom({ ...from, viewPeopleValue: e })
}} />
@ -127,6 +118,7 @@ const UserVideo: React.FC = () => {
<span>{timeNumber}</span>
{timeStatus ? <Button
type="primary"
size={'small'}
onClick={() => {
setTimeStatus(!timeStatus)
}}
@ -135,6 +127,7 @@ const UserVideo: React.FC = () => {
<Button
type="primary"
className='m-ant-btn'
size={'small'}
onClick={() => {
setTimeStatus(!timeStatus)
}}

View File

@ -343,6 +343,7 @@
}
}
.meetingContentSwiperCardFullScreenIcon {
position: absolute;
z-index: 3;
@ -416,11 +417,11 @@
}
.meetingContentBodyRight {
width: 300px;
flex-shrink: 0;
height: 100%;
.meetingUserList {
width: 300px;
height: 100%;
padding: 10px 0 20px;
box-sizing: border-box;
@ -535,6 +536,7 @@
}
.meetingUserChat {
width: 300px;
height: 100%;
background-color: #16191E;
display: flex;
@ -653,6 +655,40 @@
border-top: 1px solid #23272E;
}
}
.meetingUserVideoList {
width: 500px;
padding: 10px 10px 10px;
box-sizing: border-box;
height: 100%;
background-color: #16191E;
display: flex;
flex-direction: column;
.meetingUserVideoListTitle {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
>span {
color: #EEEEEE;
font-size: 18px;
}
>img {
cursor: pointer;
}
}
.meetingUserVideoListContent {
flex-grow: 1;
height: 0px;
overflow-y: auto;
box-sizing: border-box;
}
}
}
}

View File

@ -18,6 +18,7 @@ import { VideoSourceType } from 'agora-electron-sdk';
import Avatar from '@/components/Avatar';
import SharedFilesModel from '@/components/SharedFilesModel';
import StupWizard from '@/components/StupWizard';
import UserVideo from '@/components/UserVideo';
const { confirm } = Modal;
const { exec } = require('child_process');
const fs = require('fs').promises;
@ -33,6 +34,7 @@ const Meeting: React.FC = () => {
const [statusList, setStatusList] = useState({
userList: false,
userChatList: false,
userVideo: false,
})
const [isSharedScreenModal, setIsSharedScreenModal] = useState(false);
const [user, setUser] = useState<any>({});
@ -85,13 +87,6 @@ const Meeting: React.FC = () => {
active: false,
select: false,
},
// {
// title: '会议监控',
// icon: ImageUrl.icon48,
// iconSelect: ImageUrl.icon48Select,
// active: false,
// select: false,
// },
{
title: '录制',
icon: ImageUrl.icon27,
@ -115,7 +110,13 @@ const Meeting: React.FC = () => {
select: false,
},
],
[
[{
title: '会议监控',
icon: ImageUrl.icon48,
iconSelect: ImageUrl.icon48Select,
active: false,
select: false,
},
{
title: '成员列表',
icon: ImageUrl.icon30,
@ -494,6 +495,7 @@ const Meeting: React.FC = () => {
await agora.init(true)
agora.registerEventHandler({
onJoinChannelSuccess: async (info: any, _elapsed: any) => {
console.log(info, '本地');
if (info.channelId === state.channelId) {
if (String(info.localUid).length !== 9) {
await getJoin(state.enableMicr, state.enableCamera)
@ -510,6 +512,7 @@ const Meeting: React.FC = () => {
}
},
onUserJoined: async (info: any, remoteUid: any, _elapsed: any) => {
console.log(info, remoteUid, '远端');
if (info.channelId === state.channelId) {
if (String(remoteUid).length === 9) {
setIsShare(remoteUid)
@ -525,6 +528,7 @@ const Meeting: React.FC = () => {
}
},
onUserOffline: async (info: any, remoteUid: any, _reason: any) => {
console.log(info, remoteUid, '离开');
if (info.channelId === state.channelId) {
if (String(remoteUid).length === 9) {
setIsShare(null)
@ -561,7 +565,7 @@ const Meeting: React.FC = () => {
uid: userInfo.uid,
screenShareId: userInfo.screenShareId,
token: state.token,
// tokenA: state.tokenA,
tokenA: state.tokenA,
})
}
// 状态更新
@ -838,6 +842,7 @@ const Meeting: React.FC = () => {
setStatusList({
userList: statusList.userList ? false : true,
userChatList: false,
userVideo: false,
})
storage.setItem('noViewChatList', 0)
setNoViewChatList(0)
@ -846,6 +851,7 @@ const Meeting: React.FC = () => {
setStatusList({
userList: false,
userChatList: statusList.userChatList ? false : true,
userVideo: false,
})
storage.setItem('noViewChatList', 0)
setNoViewChatList(0)
@ -960,9 +966,13 @@ const Meeting: React.FC = () => {
}
break;
case '会议监控':
window.electron.oepnWindow({
url: location.origin + `/#/userVideo?channelId=${state.channelId + '1'}&token=${state.tokenA}`
setStatusList({
userVideo: statusList.userVideo ? false : true,
userChatList: false,
userList: false,
})
storage.setItem('noViewChatList', 0)
setNoViewChatList(0)
break;
}
}
@ -1555,7 +1565,7 @@ const Meeting: React.FC = () => {
</div>
</div>
{
(statusList.userList || statusList.userChatList) ? (
(statusList.userList || statusList.userChatList || statusList.userVideo) ? (
<div className={`${styles.meetingContentBodyRight} drag`}>
{statusList.userList ?
<div className={styles.meetingUserList}>
@ -1565,6 +1575,7 @@ const Meeting: React.FC = () => {
setStatusList({
userList: false,
userChatList: false,
userVideo: false,
})
}} />
</div>
@ -1676,7 +1687,7 @@ const Meeting: React.FC = () => {
<div onClick={() => postOpenMicr(false, user.id, true)}></div>
</div>
</div>
:
: statusList.userChatList ?
<div className={styles.meetingUserChat} id='meetingUserChat'>
<div className={styles.meetingUserChatTitle}>
<span></span>
@ -1684,6 +1695,7 @@ const Meeting: React.FC = () => {
setStatusList({
userList: false,
userChatList: false,
userVideo: false,
})
}} />
</div>
@ -1822,6 +1834,22 @@ const Meeting: React.FC = () => {
<Button type="primary" className='m-ant-btn' style={{ flexShrink: 0, marginTop: '4px' }} onClick={() => sendMsg()}></Button>
</div>
</div>
:
<div className={styles.meetingUserVideoList}>
<div className={styles.meetingUserVideoListTitle}>
<span></span>
<img src={ImageUrl.icon18} alt="" onClick={() => {
setStatusList({
userList: false,
userChatList: false,
userVideo: false,
})
}} />
</div>
<div className={styles.meetingUserVideoListContent}>
<UserVideo />
</div>
</div>
}
</div>
) : null
@ -1914,7 +1942,7 @@ const Meeting: React.FC = () => {
onMouseUp={() => changeFooterListSelect(row, itemIndex, rowIndex, false)}
onMouseLeave={() => changeFooterListSelect(row, itemIndex, rowIndex, false)}
key={rowIndex}>
{row.select ? <img src={row.iconSelect} alt="" /> : <img src={row.active ? row.iconActive : row.icon} alt="" />}
{statusList.userVideo ? <img src={row.iconSelect} alt="" /> : row.select ? <img src={row.iconSelect} alt="" /> : <img src={row.active ? row.iconActive : row.icon} alt="" />}
<span>{row.title}</span>
</div>
}

3
src/render.d.ts vendored
View File

@ -15,9 +15,6 @@ export interface IElectronAPI {
downFile: (callBack: Function) => void;
quitAndInstall: (callBack: Function) => void;
getVersion: () => Promise<string>;
oepnWindow: (data: any) => any;
closeMonitorWindow: () => void
}
declare global {