Compare commits

...

22 Commits

Author SHA1 Message Date
yj 5db98e2b46 Merge branch '宫格模式' of https://gitea.23544.com/marking/WGShare.Client.Electron into 视频监控 2025-03-21 11:16:45 +08:00
yj 431bad2b9a 参数 2025-03-21 11:14:50 +08:00
yj ed33bf6fa6 优化缓存 2025-03-21 11:06:53 +08:00
yj c7189dd63e 隐藏自动调整麦克风 2025-03-21 09:41:10 +08:00
yj 9e6a862d49 Merge branch '宫格模式' of https://gitea.23544.com/marking/WGShare.Client.Electron into 视频监控 2025-03-20 09:55:10 +08:00
yj b6775f8e40 登录页添加小版本 2025-03-20 09:52:49 +08:00
yj 6cc27d8020 显示小版本时间 2025-03-20 09:45:46 +08:00
yj b45d2fdfa6 Merge branch '宫格模式' of https://gitea.23544.com/marking/WGShare.Client.Electron into 视频监控 2025-03-19 17:26:48 +08:00
yj 2faafe8647 优化 2025-03-19 17:17:18 +08:00
yj 26ae33e0aa bug修改 2025-03-18 10:37:17 +08:00
yj 76e37eff1d 去除多余代码 2025-03-17 17:50:33 +08:00
yj 883525787d 优化 2025-03-17 17:45:57 +08:00
yj 58542e80b3 优化 2025-03-17 17:31:16 +08:00
yj c2ce66ed0d 优化 2025-03-17 17:09:09 +08:00
yj 15233cf8de 优化 2025-03-17 16:56:35 +08:00
yj 621ef06aba 优化 2025-03-17 16:50:10 +08:00
yj 2c11b8e726 新增宫格显示模式 2025-03-17 16:32:49 +08:00
yj 3ad59ff992 Merge branch 'master' of https://gitea.23544.com/marking/WGShare.Client.Electron into 视频监控 2025-03-17 10:10:18 +08:00
yj 60f2311570 自动采集麦克风音量大小 2025-03-17 10:03:28 +08:00
yj 14e90cab7e 视频监控窗口逻辑优化 2025-03-12 15:56:35 +08:00
yj 9924d7d14e 修改地址 2025-03-10 14:38:46 +08:00
yj 3a43f75c91 视频监控 2025-03-10 14:19:16 +08:00
21 changed files with 1473 additions and 213 deletions

15
build.js Normal file
View File

@ -0,0 +1,15 @@
const fs = require('fs');
const path = require('path');
const indexPath = path.resolve(__dirname, 'dist/index.html');
let indexHtml = fs.readFileSync(indexPath, 'utf-8');
const timestamp = new Date().getTime();
// 正则表达式匹配JS文件引用并添加时间戳
indexHtml = indexHtml.replace(/<script type="module" crossorigin src="([^"]+)">/g, (match, p1) => {
if (p1.endsWith('.js')) {
return `<script type="module" crossorigin src="${p1}?t=${timestamp}"></script>`;
}
return match;
});
fs.writeFileSync(indexPath, indexHtml);

View File

@ -6,6 +6,10 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self'"> <meta http-equiv="Content-Security-Policy" content="script-src 'self'">
<meta name="build-time" content="<%- buildTime%>" id="build-time">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<!-- <meta http-equiv="Content-Security-Policy" <!-- <meta http-equiv="Content-Security-Policy"
content="script-src 'self' https://www.google-analytics.com; style-src 'self' https://animate.style"> --> content="script-src 'self' https://www.google-analytics.com; style-src 'self' https://animate.style"> -->
<title></title> <title></title>

43
main.js
View File

@ -24,6 +24,7 @@ const cancellationToken = new CancellationToken()
app.allowRendererProcessReuse = false; app.allowRendererProcessReuse = false;
let mainWindow = null; let mainWindow = null;
let childWindow = {} let childWindow = {}
let customizeChildWindow = {}
let isMaximized = false; let isMaximized = false;
let env = 'development'; //development production xy let env = 'development'; //development production xy
let regKey; let regKey;
@ -624,6 +625,48 @@ app.on('ready', () => {
}) })
}); });
}); });
// 创建自定义子窗口
ipcMain.handle('customizeCreateChildWindow', (event, config) => {
if (config.open) {
customizeChildWindow[config.key].show()
customizeChildWindow[config.key].focus()
} else {
const customizeChild = new BrowserWindow({
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
enableRemoteModule: true,
nodeIntegrationInWorker: true,
allowMediaDevices: true,
},
frame: true,
width: config.width,
height: config.height,
minWidth: config.width,
minHeight: config.height,
title: '会议监控',
})
customizeChildWindow[config.key] = customizeChild;
customizeChild.loadURL(config.url)
customizeChild.once('ready-to-show', () => {
customizeChild.show()
customizeChild.setMenuBarVisibility(false)
})
customizeChild.on('close', () => {
customizeChildWindow[config.key] = null
})
}
})
// 判断自定义窗口是否打开
ipcMain.handle('isCustomizeCreateChildOpen', (event, key) => {
return customizeChildWindow[key] ? true : false
})
// 关闭自定义窗口
ipcMain.handle('closeCustomizeCreateChildWindow', (event, key) => {
if (customizeChildWindow[key]) {
customizeChildWindow[key].close()
}
})
// 创建子窗口 // 创建子窗口
ipcMain.handle('createChildWindow', (event, config) => { ipcMain.handle('createChildWindow', (event, config) => {
const child = new BrowserWindow({ const child = new BrowserWindow({

960
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,9 +9,9 @@
"dev": "concurrently \"electron .\" \"cross-env BROWSER=none vite\"", "dev": "concurrently \"electron .\" \"cross-env BROWSER=none vite\"",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"build:dev": "vite build & electron-builder -w --config=./config/development.json", "build:dev": "vite build & node build.js & electron-builder -w --config=./config/development.json",
"build:prod": "vite build & electron-builder -w --config=./config/production.json", "build:prod": "vite build & node build.js & electron-builder -w --config=./config/production.json",
"build:xy": "vite build & electron-builder -w --config=./config/xy.json" "build:xy": "vite build & node build.js & electron-builder -w --config=./config/xy.json"
}, },
"agora_electron": { "agora_electron": {
"platform": "win32", "platform": "win32",
@ -53,6 +53,7 @@
"electron-builder": "^23.1.0", "electron-builder": "^23.1.0",
"typescript": "^4.5.4", "typescript": "^4.5.4",
"vite": "^2.8.0", "vite": "^2.8.0",
"vite-plugin-html": "^3.2.2",
"vite-plugin-resolve": "^2.5.1" "vite-plugin-resolve": "^2.5.1"
} }
} }

View File

@ -123,6 +123,18 @@ window.electron = {
setRegistry: (uuid) => { setRegistry: (uuid) => {
ipcRenderer.invoke('setRegistry', uuid) ipcRenderer.invoke('setRegistry', uuid)
}, },
// 创建自定义子窗口
customizeCreateChildWindow: (config) => {
ipcRenderer.invoke('customizeCreateChildWindow', config)
},
// 判断自定义窗口是否打开
isCustomizeCreateChildOpen: (key) => {
return ipcRenderer.invoke('isCustomizeCreateChildOpen', key)
},
// 关闭自定义窗口
closeCustomizeCreateChildWindow: (key) => {
ipcRenderer.invoke('closeCustomizeCreateChildWindow', key)
},
// 创建子窗口 // 创建子窗口
createChildWindow: (str) => { createChildWindow: (str) => {
switch (str) { switch (str) {

BIN
src/assets/icon57.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

View File

@ -83,11 +83,10 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
}) })
} }
const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => { const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => {
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + 'a')]).then(res => { GetRoomRtcToken(roomNum).then(res => {
if (res[0].code === 200 && res[1].code === 200) { if (res.code === 200) {
callBack({ callBack({
token: res[0].data, token: res.data,
tokenA: res[1].data,
}) })
} else { } else {
storage.setItem('loading', false) storage.setItem('loading', false)
@ -213,7 +212,6 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
state: { state: {
channelId: roomNumber, channelId: roomNumber,
token: options.token, token: options.token,
tokenA: options.tokenA,
roomId: res.data.id, roomId: res.data.id,
roomName: res.data.roomName, roomName: res.data.roomName,
enableMicr: joinRoomSettingForm[0].active, enableMicr: joinRoomSettingForm[0].active,

View File

@ -53,14 +53,14 @@ const SpeakerModeModal = forwardRef((props: any, ref: any) => {
}) })
const FreedomMode: React.FC<Props> = ({ onClick, meetingMode }) => { const FreedomMode: React.FC<Props> = ({ onClick, meetingMode }) => {
// 自由者模式 // 宫格模式
return ( return (
<> <>
<div className={styles.freedomMode} onClick={onClick}> <div className={styles.freedomMode} onClick={onClick}>
<div className={`${meetingMode === 'FreedomMode' ? styles.active : ''}`}> <div className={`${meetingMode === 'FreedomMode' ? styles.active : ''}`}>
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16].map(item => <div key={item}></div>)} {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16].map(item => <div key={item}></div>)}
</div> </div>
<span></span> <span></span>
</div> </div>
</> </>
) )

View File

@ -730,7 +730,6 @@ const AudioComponents = () => {
ecordingVolume: e, ecordingVolume: e,
}) })
}} disabled={!audioDeviceManager.ecordingItem} /> }} disabled={!audioDeviceManager.ecordingItem} />
{/* || audioDeviceManager.autoEcordingVolume */}
</div> </div>
{/* <div style={{ marginBottom: '10px' }}> {/* <div style={{ marginBottom: '10px' }}>
<Checkbox onChange={async (e) => { <Checkbox onChange={async (e) => {

View File

@ -111,11 +111,10 @@ const Index: React.FC = () => {
}) })
} }
const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => { const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => {
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + 'a')]).then(res => { GetRoomRtcToken(roomNum).then(res => {
if (res[0].code === 200 && res[1].code === 200) { if (res.code === 200) {
callBack({ callBack({
token: res[0].data, token: res.data,
tokenA: res[1].data,
}) })
} }
}).finally(() => { }).finally(() => {
@ -334,7 +333,6 @@ const Index: React.FC = () => {
state: { state: {
channelId: item.roomNum, channelId: item.roomNum,
token: options.token, token: options.token,
tokenA: options.tokenA,
roomId: item.id, roomId: item.id,
roomName: item.roomName, roomName: item.roomName,
enableMicr: false, enableMicr: false,

View File

@ -123,7 +123,7 @@
>div:nth-child(1) { >div:nth-child(1) {
color: #ccc; color: #ccc;
font-size: 16px; font-size: 14px;
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -45,6 +45,7 @@ const Home: React.FC = () => {
]); ]);
const [userInfo, setUserInfo] = useState<any>({}) const [userInfo, setUserInfo] = useState<any>({})
const [version, setVersion] = useState<string>('') const [version, setVersion] = useState<string>('')
const [buildTime, setBuildTime] = useState<string>('0')
const [update, setUpdate] = useState(false) const [update, setUpdate] = useState(false)
const [dateInfo, setDateInfo] = useState<{ const [dateInfo, setDateInfo] = useState<{
work: string; work: string;
@ -60,6 +61,13 @@ const Home: React.FC = () => {
window.electron.getVersion().then(res => { window.electron.getVersion().then(res => {
setVersion(res) setVersion(res)
}) })
try {
const buildStr = document.getElementById('build-time')?.getAttribute('content');
const formattedTime = dayjs(Number(buildStr)).format('YYYYMMDDHHmm');
setBuildTime(formattedTime)
} catch {
}
const updateTime = () => { const updateTime = () => {
setDateInfo({ setDateInfo({
work: dayjs().format('ddd'), work: dayjs().format('ddd'),
@ -136,7 +144,9 @@ const Home: React.FC = () => {
</div> </div>
<div className='drag'> <div className='drag'>
<div> <div>
<span>:{version}</span> <span>V{version}-
<span style={{ fontSize: '12px', color: 'gray' }}>{buildTime}</span>
</span>
{update ? <span>new</span> : null} {update ? <span>new</span> : null}
</div> </div>
{update ? <div> {update ? <div>

View File

@ -129,7 +129,7 @@
flex-shrink: 0; flex-shrink: 0;
color: #ccc; color: #ccc;
text-align: right; text-align: right;
font-size: 16px; font-size: 14px;
} }
} }
} }

View File

@ -11,6 +11,7 @@ import { v4 as uuidv4 } from 'uuid';
import { GetCheckoutRoomNum, GetRoomInfo, GetRoomRtcToken } from '@/api/Home/Index'; import { GetCheckoutRoomNum, GetRoomInfo, GetRoomRtcToken } from '@/api/Home/Index';
import { ExclamationCircleFilled } from '@ant-design/icons'; import { ExclamationCircleFilled } from '@ant-design/icons';
import { isVersion } from '@/utils/package/public'; import { isVersion } from '@/utils/package/public';
import dayjs from 'dayjs';
const { confirm } = Modal; const { confirm } = Modal;
const Login: React.FC = () => { const Login: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -39,6 +40,7 @@ const Login: React.FC = () => {
nickName: '', nickName: '',
roomNum: '', roomNum: '',
}) })
const [buildTime, setBuildTime] = useState<string>('0')
const [nameModal, setNameModal] = useState(false) const [nameModal, setNameModal] = useState(false)
const [env, setEnv] = useState('') const [env, setEnv] = useState('')
useEffect(() => { useEffect(() => {
@ -50,6 +52,13 @@ const Login: React.FC = () => {
window.electron.getEnv().then(res => { window.electron.getEnv().then(res => {
setEnv(res) setEnv(res)
}) })
try {
const buildStr = document.getElementById('build-time')?.getAttribute('content');
const formattedTime = dayjs(Number(buildStr)).format('YYYYMMDDHHmm');
setBuildTime(formattedTime)
} catch {
}
if (storage.getItem('login')) { if (storage.getItem('login')) {
const login = JSON.parse(storage.getItem('login') as string); const login = JSON.parse(storage.getItem('login') as string);
const data = { const data = {
@ -319,7 +328,8 @@ const Login: React.FC = () => {
><img src={ImageUrl.icon3} alt="" /></div> ><img src={ImageUrl.icon3} alt="" /></div>
</div> </div>
<div> <div>
{version} V{version}-
<span style={{ fontSize: '12px', color: 'gray' }}>{buildTime}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -10,7 +10,7 @@
.meetingContentUserName { .meetingContentUserName {
display: flex; display: flex;
align-items: center; align-items: center;
background-color: #0000009E; background-color: rgba(0, 0, 0, 0.62);
border-radius: 6px; border-radius: 6px;
height: 24px; height: 24px;
padding: 0 4px; padding: 0 4px;
@ -285,14 +285,126 @@
} }
} }
// 宫格模式
// 1
.meetingContentBodyLeftFreedomModeOne {
width: 100%;
height: 100%;
// 自由者模式 .meetingContentSwiperCard {
.meetingContentBodyLeftFreedomMode { width: 100%;
height: 100%;
}
}
// 2
.meetingContentBodyLeftFreedomModeTwo {
width: 100%;
height: 100%;
display: flex;
.meetingContentSwiperCard {
width: 50%;
height: 100%;
}
}
// 3 4
.meetingContentBodyLeftFreedomModeThree {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
overflow-y: auto; justify-content: center;
.meetingContentSwiperCard {
width: 50%;
height: 50%;
}
}
//5 6
.meetingContentBodyLeftFreedomModeFour {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
.meetingContentSwiperCard {
width: calc(100% / 3);
height: 50%;
}
}
// 7
.meetingContentBodyLeftFreedomModeFive {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
.meetingContentSwiperCard {
width: calc(100% / 4);
height: 50%;
}
}
//8 9
.meetingContentBodyLeftFreedomModeSix {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
.meetingContentSwiperCard {
width: calc(100% / 3);
height: calc(100% / 3);
}
}
// 10 11 12
.meetingContentBodyLeftFreedomModeSeven {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
.meetingContentSwiperCard {
width: calc(100% / 4);
height: calc(100% / 3);
}
}
// 13 14 15 16
.meetingContentBodyLeftFreedomModeEight {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
.meetingContentSwiperCard {
width: calc(100% / 4);
height: calc(100% / 4);
}
}
// 17 18 19 20
.meetingContentBodyLeftFreedomModeNine {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
.meetingContentSwiperCard {
width: calc(100% / 5);
height: calc(100% / 4);
}
} }
// 标准模式 // 标准模式
@ -309,9 +421,6 @@
.meetingContentSwiperCard { .meetingContentSwiperCard {
width: 100%; width: 100%;
}
.meetingContentSwiperCard {
height: 160px; height: 160px;
} }
} }
@ -741,38 +850,6 @@
} }
} }
.meetingUserVideoList {
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;
}
}
} }
} }
@ -920,28 +997,33 @@
} }
.modePopover { .modePopover {
display: flex;
flex-direction: column;
align-items: center;
>div { >div {
width: 140px; display: flex;
height: 30px; align-items: center;
>div {
width: 100px;
padding: 10px 0;
line-height: 30px; line-height: 30px;
border-radius: 5px; border-radius: 5px;
margin-bottom: 8px;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
flex-direction: column;
justify-content: center; justify-content: center;
margin-right: 20px;
>span { >span {
color: #EEEEEE; color: #EEEEEE;
} }
>img { >img {
height: 16px; height: 30px;
margin-right: 10px; margin-bottom: 4px;
}
&:last-child {
margin: 0;
} }
} }
@ -956,6 +1038,33 @@
background-color: darken(#101418, 4%); background-color: darken(#101418, 4%);
} }
} }
.active {
background-color: lighten(#101418, 8%);
cursor: not-allowed;
&:hover {
background-color: lighten(#101418, 8%);
}
&:active {
background-color: lighten(#101418, 8%);
}
}
:last-child {
margin-right: 0px;
}
}
>span {
margin-top: 4px;
color: white;
>span {
margin-left: 4px;
}
}
} }
} }

View File

@ -18,7 +18,6 @@ import Avatar from '@/components/Avatar';
import SharedFilesModel from '@/components/SharedFilesModel'; import SharedFilesModel from '@/components/SharedFilesModel';
import StupWizard from '@/components/StupWizard'; import StupWizard from '@/components/StupWizard';
import EquipmentManagement from '@/components/EquipmentManagement'; import EquipmentManagement from '@/components/EquipmentManagement';
import UserVideo from '@/components/UserVideo';
import { role } from '@/config/role'; import { role } from '@/config/role';
import { fixWebmDuration } from "webm-duration-fix-buffer"; import { fixWebmDuration } from "webm-duration-fix-buffer";
import { getKeyOpenChildWindow, setKeyOpenChildWindow } from '@/utils/package/public'; import { getKeyOpenChildWindow, setKeyOpenChildWindow } from '@/utils/package/public';
@ -47,8 +46,7 @@ const Meeting: React.FC = () => {
const [isClickedMediaSteam, setIsClickedMediaSteam] = useState(false); const [isClickedMediaSteam, setIsClickedMediaSteam] = useState(false);
const [statusList, setStatusList] = useState({ const [statusList, setStatusList] = useState({
userList: false, userList: false,
userChatList: false, userChatList: false
userVideo: false,
}) })
const [isSharedScreenModal, setIsSharedScreenModal] = useState(false); const [isSharedScreenModal, setIsSharedScreenModal] = useState(false);
const [quitMeetingModal, setQuitMeetingModal] = useState(false); const [quitMeetingModal, setQuitMeetingModal] = useState(false);
@ -171,6 +169,7 @@ const Meeting: React.FC = () => {
const [currentVideoUid, setCurrentVideoUid] = useState('') const [currentVideoUid, setCurrentVideoUid] = useState('')
let [currentSeconds, setCurrentSeconds] = useState(0) let [currentSeconds, setCurrentSeconds] = useState(0)
const [isNetworkQuality, setIsNetworkQuality] = useState(false) const [isNetworkQuality, setIsNetworkQuality] = useState(false)
const [userVideoWindow, _setUserVideoWindow] = useState(false)
const [currentEffective, setCurrentEffective] = useState(3) const [currentEffective, setCurrentEffective] = useState(3)
const [networkQuality, setNetworkQuality] = useState({ const [networkQuality, setNetworkQuality] = useState({
level: '佳', level: '佳',
@ -184,7 +183,7 @@ const Meeting: React.FC = () => {
const [isClickLock, setIsClickLock] = useState(false) const [isClickLock, setIsClickLock] = useState(false)
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [modeOpen, setModeOpen] = useState(false) const [modeOpen, setModeOpen] = useState(false)
const [meetingMode, setMeetingMode] = useState('') const [meetingMode, setMeetingMode] = useState('FreedomMode')
const [userSearchValue, setUserSearchValue] = useState('') const [userSearchValue, setUserSearchValue] = useState('')
const [noViewChatList, setNoViewChatList] = useState(0) const [noViewChatList, setNoViewChatList] = useState(0)
const [currentLookUserAccount, setCurrentLookUserAccount] = useState<any>('') const [currentLookUserAccount, setCurrentLookUserAccount] = useState<any>('')
@ -217,6 +216,7 @@ const Meeting: React.FC = () => {
} }
}); });
const [isVideoFullScreen, setIsVideoFullScreen] = useState<boolean>(false) const [isVideoFullScreen, setIsVideoFullScreen] = useState<boolean>(false)
const [_freedomModeStatus, setFreedomModeStatus] = useState<boolean>(false)
const [observer, setObserver] = useState<IntersectionObserver>() const [observer, setObserver] = useState<IntersectionObserver>()
const [_activeSpeaker, setActiveSpeaker] = useState('') const [_activeSpeaker, setActiveSpeaker] = useState('')
let userInfo = JSON.parse(storage.getItem('user') as string) let userInfo = JSON.parse(storage.getItem('user') as string)
@ -235,7 +235,6 @@ const Meeting: React.FC = () => {
} }
}) })
setKeyOpenChildWindow('shareScreenWindow', false) setKeyOpenChildWindow('shareScreenWindow', false)
setMeetingMode('StandardMode');
agoraInit() agoraInit()
storage.setItem('noViewChatList', 0) storage.setItem('noViewChatList', 0)
window.addEventListener('customStorageChange', handleCustomStorageChange); window.addEventListener('customStorageChange', handleCustomStorageChange);
@ -639,18 +638,36 @@ const Meeting: React.FC = () => {
if (isShare) { if (isShare) {
const item = roomUserList.find((item: any) => item.screenShareId === String(isShare)) const item = roomUserList.find((item: any) => item.screenShareId === String(isShare))
setIsShareUser(item || null) setIsShareUser(item || null)
setMeetingMode('StandardMode')
} }
}, [isShare, roomUserList]); }, [isShare, roomUserList]);
useEffect(() => {
setFreedomModeStatus((res: boolean) => {
if (meetingMode === 'FreedomMode') {
return true
} else {
if (res) {
getShowUser(true)
}
return false
}
})
}, [meetingMode]);
useEffect(() => { useEffect(() => {
roomUserList.forEach(async (item: any) => { roomUserList.forEach(async (item: any) => {
if (meetingMode === "FreedomMode") {
await agora.setRemoteVideoStreamType(item.uid, VideoStreamType.VideoStreamHigh, true)
} else {
if (item.uid === currentVideoId) { if (item.uid === currentVideoId) {
await agora.setRemoteVideoStreamType(item.uid, VideoStreamType.VideoStreamHigh, true) await agora.setRemoteVideoStreamType(item.uid, VideoStreamType.VideoStreamHigh, true)
} else { } else {
await agora.setRemoteVideoStreamType(item.uid, VideoStreamType.VideoStreamLow, true) await agora.setRemoteVideoStreamType(item.uid, VideoStreamType.VideoStreamLow, true)
} }
}
}); });
}, [currentVideoId, roomUserList]); }, [currentVideoId, roomUserList, meetingMode]);
useEffect(() => { useEffect(() => {
let item = roomUserList.find((item: any) => currentVideoId == item.uid) let item = roomUserList.find((item: any) => currentVideoId == item.uid)
@ -723,7 +740,13 @@ const Meeting: React.FC = () => {
break; break;
// 扩展操作 // 扩展操作
case 'Operation': case 'Operation':
switch (item.contentString) { try {
const temp = JSON.parse(item.contentString)
if (temp.type === 'mode') {
temp.msg ? message.success(`管理员已将会议室显示模式更新为${getMeetingContentBodyLeftModeText(temp.mode)}`) : null;
setMeetingMode(temp.mode)
}
} catch (error) {
} }
break; break;
@ -739,7 +762,7 @@ const Meeting: React.FC = () => {
break; break;
// 更新视图模式 // 更新视图模式
case 'RefreshView': case 'RefreshView':
setMeetingMode(item.type)
break; break;
// 全员看他 // 全员看他
case 'ShowUser': case 'ShowUser':
@ -797,7 +820,7 @@ const Meeting: React.FC = () => {
setAllUserListData('ManagerRefresh', item, async () => { setAllUserListData('ManagerRefresh', item, async () => {
if (item.user.uid === item.uid) { if (item.user.uid === item.uid) {
if (item.user.uid === userInfo.uid) { if (item.user.uid === userInfo.uid) {
await agora.allLeaveChannelEx() await agora.allJoinChannelEx(item.user, false)
message.success(`操作成功`) message.success(`操作成功`)
await agora.updateChannelMediaOptions(item.user.isRoomManager) await agora.updateChannelMediaOptions(item.user.isRoomManager)
await postOpenMicrApi(item.user.isRoomManager, userInfo.uid, false) await postOpenMicrApi(item.user.isRoomManager, userInfo.uid, false)
@ -808,9 +831,7 @@ const Meeting: React.FC = () => {
} }
} else { } else {
if (item.user.uid === userInfo.uid) { if (item.user.uid === userInfo.uid) {
if (item.user.isRoomManager) { await agora.allJoinChannelEx(item.user, false)
await agora.allLeaveChannelEx()
}
message.success(`管理员${item.user.isRoomManager ? '设置' : '取消'}您为发言人`) message.success(`管理员${item.user.isRoomManager ? '设置' : '取消'}您为发言人`)
await agora.updateChannelMediaOptions(item.user.isRoomManager) await agora.updateChannelMediaOptions(item.user.isRoomManager)
setCurrentRequestSpeakType(res => { setCurrentRequestSpeakType(res => {
@ -846,15 +867,15 @@ const Meeting: React.FC = () => {
case 'Watch': case 'Watch':
if (!role.ID.includes(userInfo.roleId)) { if (!role.ID.includes(userInfo.roleId)) {
let userId = item.watchUids.find((uid: any) => uid === userInfo.uid) let userId = item.watchUids.find((uid: any) => uid === userInfo.uid)
setRoomUserList((res: any) => {
let userRow = res.find((row: any) => row.uid === userId)
if (userId) { if (userId) {
GetRoomRtcToken(state.channelId + 'a').then(async res => { agora.allJoinChannelEx(userRow, true)
if (res.code === 200) {
await agora.allJoinChannelEx(false, res.data)
}
})
} else { } else {
await agora.allLeaveChannelEx() agora.allJoinChannelEx(userRow, false)
} }
return res
})
} }
break; break;
// 设备列表 // 设备列表
@ -990,7 +1011,7 @@ const Meeting: React.FC = () => {
uid: temp.uid, uid: temp.uid,
enableMicr: temp.enableMicr enableMicr: temp.enableMicr
}) })
} else { } else if (temp.type === 'video') {
await PostOpenCamera({ await PostOpenCamera({
roomNum: temp.roomNum, roomNum: temp.roomNum,
uid: temp.uid, uid: temp.uid,
@ -1406,6 +1427,11 @@ const Meeting: React.FC = () => {
const percentage = (item.volume / 255) * 100 const percentage = (item.volume / 255) * 100
if (domMe) { if (domMe) {
domMe.style.height = `${percentage}%` domMe.style.height = `${percentage}%`
// if (item.volume > 180) {
// agora.adjustRecordingSignalVolume(60)
// } else {
// agora.adjustRecordingSignalVolume(100)
// }
} }
window.electron.windowHandleMessage({ window.electron.windowHandleMessage({
key: 'shareScreenWindow', key: 'shareScreenWindow',
@ -1581,7 +1607,11 @@ const Meeting: React.FC = () => {
item.isShow = true; item.isShow = true;
} }
}); });
setIsAdmin(res.filter((item: any) => (role.ID.includes(item.roleId) || item.isRoomManager) && item.isRoom).length) const peoPleLength = res.filter((item: any) => (role.ID.includes(item.roleId) || item.isRoomManager) && item.isRoom).length
setIsAdmin(peoPleLength)
if (peoPleLength > 6) {
setMeetingMode('StandardMode')
}
return res return res
}) })
} }
@ -1713,7 +1743,8 @@ const Meeting: React.FC = () => {
}) })
} }
// 渲染视频 // 渲染视频
const renderVideo = async (uid: string = ''): Promise<void> => { const renderVideo = async (uid: string = '', bool: boolean = false): Promise<void> => {
if (!bool) {
if (isClickLock) { if (isClickLock) {
return return
} }
@ -1724,6 +1755,7 @@ const Meeting: React.FC = () => {
} else { } else {
uid = userInfo.uid uid = userInfo.uid
} }
}
await agora.destroyRendererByView(`video-source-camera-primary`) await agora.destroyRendererByView(`video-source-camera-primary`)
await agora.destroyRendererByView(`video-source-screen`) await agora.destroyRendererByView(`video-source-screen`)
await agora.destroyRendererByView(`video-source-remote-screen`) await agora.destroyRendererByView(`video-source-remote-screen`)
@ -1760,11 +1792,11 @@ const Meeting: React.FC = () => {
}, 500); }, 500);
} }
// 全员观看 // 全员观看
const getShowUser = async (): Promise<void> => { const getShowUser = async (bool: boolean = false): Promise<void> => {
if (location.href.indexOf('/meeting') !== -1) { if (location.href.indexOf('/meeting') !== -1) {
await GetShowUser(state.channelId).then(async (res) => { await GetShowUser(state.channelId).then(async (res) => {
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
renderVideo(res.data) renderVideo(res.data, bool)
} }
}) })
} }
@ -1832,14 +1864,12 @@ const Meeting: React.FC = () => {
setStatusList({ setStatusList({
userList: statusList.userList ? false : true, userList: statusList.userList ? false : true,
userChatList: false, userChatList: false,
userVideo: false,
}) })
break; break;
case '聊天': case '聊天':
setStatusList({ setStatusList({
userList: false, userList: false,
userChatList: statusList.userChatList ? false : true, userChatList: statusList.userChatList ? false : true,
userVideo: false,
}) })
storage.setItem('noViewChatList', 0) storage.setItem('noViewChatList', 0)
setNoViewChatList(0) setNoViewChatList(0)
@ -2012,22 +2042,20 @@ const Meeting: React.FC = () => {
} }
break; break;
case '会议监控': case '会议监控':
if (!statusList.userVideo) { window.electron.isCustomizeCreateChildOpen('meetingMonitoring').then((req: boolean) => {
GetRoomRtcToken(state.channelId + 'a').then(async res => { window.electron.getWindowSize().then((res: any) => {
if (res.code === 200) { window.electron.customizeCreateChildWindow({
await agora.allJoinChannelEx(true, res.data) url: location.hostname.includes('meeting-api.23544.com') ?
} `http://192.168.2.9:8828?uid=${Number('1' + userInfo.screenShareId)}&channel=${state.channelId}&token=${userInfo.token}` :
`http://192.168.2.9:8828?uid=${Number('1' + userInfo.screenShareId)}&channel=${state.channelId}&token=${userInfo.token}`,
key: 'meetingMonitoring',
title: '会议监控',
width: Math.ceil(res.width / 1.5),
height: Math.ceil(res.height / 1.3),
open: req,
})
}) })
} else {
await agora.allLeaveChannelEx()
}
setStatusList({
userVideo: statusList.userVideo ? false : true,
userChatList: false,
userList: false,
}) })
storage.setItem('noViewChatList', 0)
setNoViewChatList(0)
break; break;
case '签到': case '签到':
singInRef.current.changeModal() singInRef.current.changeModal()
@ -2144,6 +2172,14 @@ const Meeting: React.FC = () => {
await PostStopSharedScreen(state.channelId) await PostStopSharedScreen(state.channelId)
} else { } else {
await PostShowUser(state.channelId, uid, name) await PostShowUser(state.channelId, uid, name)
await window.electron.onInvoke('sendOper', {
roomNum: state.channelId,
contentString: JSON.stringify({
mode: 'StandardMode',
type: 'mode',
msg: false
})
})
} }
} }
// 设置发言人 // 设置发言人
@ -2456,10 +2492,37 @@ const Meeting: React.FC = () => {
}) })
} }
// 获取当前模式样式 // 获取当前模式样式
const getMeetingContentBodyLeftModeClass = (): string => { const getMeetingContentBodyLeftModeClass = (people: Number): string => {
switch (meetingMode) { switch (meetingMode) {
case 'FreedomMode': case 'FreedomMode':
return styles.meetingContentBodyLeftFreedomMode switch (people) {
case 1:
return styles.meetingContentBodyLeftFreedomModeOne;
case 2:
return styles.meetingContentBodyLeftFreedomModeTwo;
case 3:
case 4:
return styles.meetingContentBodyLeftFreedomModeThree;
case 5:
case 6:
return styles.meetingContentBodyLeftFreedomModeFour;
case 7:
return styles.meetingContentBodyLeftFreedomModeFive;
case 8:
case 9:
return styles.meetingContentBodyLeftFreedomModeSix;
case 10:
case 11:
case 12:
return styles.meetingContentBodyLeftFreedomModeSeven;
case 13:
case 14:
case 15:
case 16:
return styles.meetingContentBodyLeftFreedomModeEight;
default:
return styles.meetingContentBodyLeftFreedomModeNine;
}
case 'StandardMode': case 'StandardMode':
return styles.meetingContentBodyLeftStandardMode return styles.meetingContentBodyLeftStandardMode
case 'SpeakerMode': case 'SpeakerMode':
@ -2474,10 +2537,10 @@ const Meeting: React.FC = () => {
return '' return ''
} }
// 获取当前模式文字 // 获取当前模式文字
const getMeetingContentBodyLeftModeText = (): string => { const getMeetingContentBodyLeftModeText = (mode?: string): string => {
switch (meetingMode) { switch (mode || meetingMode) {
case 'FreedomMode': case 'FreedomMode':
return '自由者模式' return '宫格模式'
case 'StandardMode': case 'StandardMode':
return '标准模式' return '标准模式'
case 'SpeakerMode': case 'SpeakerMode':
@ -2575,6 +2638,31 @@ const Meeting: React.FC = () => {
} }
message.success('操作成功') message.success('操作成功')
} }
// 设置模式
const setSyncView = (mode: string) => {
if (meetingMode === mode) {
setModeOpen(false)
return message.error(`${getMeetingContentBodyLeftModeText(mode)}已开启,请勿重复操作!`)
}
if (isAdmin > 6 && mode === 'FreedomMode') {
setModeOpen(false)
return message.error('发言人数超过6人,无法使用宫格模式!')
}
if (isShare && mode === 'FreedomMode') {
setModeOpen(false)
return message.error('共享中,无法切换模式')
}
window.electron.onInvoke('sendOper', {
roomNum: state.channelId,
contentString: JSON.stringify({
mode,
type: 'mode',
msg: true,
})
})
setModeOpen(false)
storage.setItem('meetingMode', mode)
}
// 判断是否出现滚动条 // 判断是否出现滚动条
const hasScrollbar = () => { const hasScrollbar = () => {
let element = document.getElementById('videoView') as HTMLDivElement let element = document.getElementById('videoView') as HTMLDivElement
@ -2604,6 +2692,9 @@ const Meeting: React.FC = () => {
<> <>
<div className={styles.meeting} onClick={() => { <div className={styles.meeting} onClick={() => {
setContextMenu('') setContextMenu('')
if (modeOpen) {
setModeOpen(false)
}
setIsNetworkQuality(false) setIsNetworkQuality(false)
}}> }}>
{isScreenCapture ? <div className={`${styles.meetingAbsolute}`} id='meetingAbsoluteVideo'> {isScreenCapture ? <div className={`${styles.meetingAbsolute}`} id='meetingAbsoluteVideo'>
@ -2773,28 +2864,33 @@ const Meeting: React.FC = () => {
<span className='drag' style={{ marginTop: '2px', marginLeft: '4px' }}><Code roomNum={state.channelId}></Code></span> <span className='drag' style={{ marginTop: '2px', marginLeft: '4px' }}><Code roomNum={state.channelId}></Code></span>
</div> </div>
<div className='drag'> <div className='drag'>
<Popover {role.ID.includes(userInfo.roleId) ? <Popover
content={ content={
<div className='modePopover'> <div className='modePopover'>
<div onClick={() => { <div>
setModeOpen(false) <div className={meetingMode === 'FreedomMode' ? 'active' : ''} onClick={() => {
storage.setItem('meetingMode', 'StandardMode') setSyncView('FreedomMode')
}}>
<img src={ImageUrl.icon57} alt="" />
<span></span>
</div>
<div className={meetingMode === 'StandardMode' ? 'active' : ''} onClick={() => {
setSyncView('StandardMode')
}}> }}>
<img src={ImageUrl.icon43} alt="" /> <img src={ImageUrl.icon43} alt="" />
<span></span> <span></span>
</div> </div>
<div onClick={() => { <div className={meetingMode === 'SpeakerMode' ? 'active' : ''} onClick={() => {
setModeOpen(false) setSyncView('SpeakerMode')
storage.setItem('meetingMode', 'SpeakerMode')
}}> }}>
<img src={ImageUrl.icon44} alt="" /> <img src={ImageUrl.icon44} alt="" />
<span></span> <span></span>
</div> </div>
<div onClick={() => {
setModeOpen(false)
}}>
<span></span>
</div> </div>
<span>
<ExclamationCircleFilled />
<span></span>
</span>
</div> </div>
} }
title="" title=""
@ -2803,10 +2899,14 @@ const Meeting: React.FC = () => {
onOpenChange={() => setModeOpen(true)} onOpenChange={() => setModeOpen(true)}
> >
<div className={styles.meetingGrayButton}> <div className={styles.meetingGrayButton}>
{meetingMode === 'StandardMode' ? <img src={ImageUrl.icon43} alt="" /> : <img src={ImageUrl.icon44} alt="" />} {meetingMode === 'StandardMode' ?
<img src={ImageUrl.icon43} alt="" /> :
meetingMode === 'FreedomMode' ?
<img src={ImageUrl.icon57} alt="" /> :
<img src={ImageUrl.icon44} alt="" />}
<span>{getMeetingContentBodyLeftModeText()}</span> <span>{getMeetingContentBodyLeftModeText()}</span>
</div> </div>
</Popover> </Popover> : null}
<Operation></Operation> <Operation></Operation>
</div> </div>
</div> </div>
@ -2814,7 +2914,7 @@ const Meeting: React.FC = () => {
<div className={styles.meetingContentBody}> <div className={styles.meetingContentBody}>
<div className={`${styles.meetingContentBodyLeft} drag`}> <div className={`${styles.meetingContentBodyLeft} drag`}>
{isAdmin && currentLookUserAccount ? getSettingIcon() : null} {isAdmin && currentLookUserAccount ? getSettingIcon() : null}
<div className={getMeetingContentBodyLeftModeClass()} id='videoView' style={meetingMode === 'SpeakerMode' && isVideoFullScreen ? { width: '0' } : {}}> <div className={getMeetingContentBodyLeftModeClass(isAdmin)} id='videoView' style={meetingMode === 'SpeakerMode' && isVideoFullScreen ? { width: '0' } : {}}>
{roomUserList.map((item: any, index: number) => { {roomUserList.map((item: any, index: number) => {
return (item.isRoom && item.isAdmin ? <div return (item.isRoom && item.isAdmin ? <div
id={item.uid} id={item.uid}
@ -2939,7 +3039,13 @@ const Meeting: React.FC = () => {
</div> : null) </div> : null)
} }
)} )}
{hasScrollbar() ? <div> {/* <div className={`${styles.meetingContentSwiperCard}`}>
<div className={`${styles.meetingContentSwiperCardVdeio}`}>
<div className={styles.meetingContentSwiperCardVdeioLoading}>
</div>
</div>
</div> */}
{meetingMode !== "FreedomMode" && hasScrollbar() ? <div>
{meetingMode === "StandardMode" ? <div className={`${styles.meetingContentSwiperCaret}`} style={{ left: '20px', top: '66px' }} onClick={() => { {meetingMode === "StandardMode" ? <div className={`${styles.meetingContentSwiperCaret}`} style={{ left: '20px', top: '66px' }} onClick={() => {
const container = document.getElementById('videoView') as HTMLElement; const container = document.getElementById('videoView') as HTMLElement;
container.scrollLeft -= 100 container.scrollLeft -= 100
@ -2963,7 +3069,7 @@ const Meeting: React.FC = () => {
<CaretDownOutlined /> <CaretDownOutlined />
</div>} </div>}
</div> : null} </div> : null}
{currentLookUserStatus === 0 ? {meetingMode !== "FreedomMode" && currentLookUserStatus === 0 ?
<div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass()} ${setMeetingContentSwiperCardFullScreenClass()}`}> <div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass()} ${setMeetingContentSwiperCardFullScreenClass()}`}>
<div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-camera-primary'> <div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-camera-primary'>
{<div className={styles.meetingContentSwiperCardVdeioLoading}> {<div className={styles.meetingContentSwiperCardVdeioLoading}>
@ -2974,7 +3080,7 @@ const Meeting: React.FC = () => {
<FullscreenOutlined className={styles.meetingContentSwiperCardFullScreenIcon} title='全屏' onClick={() => setIsVideoFullScreen(true)} />} <FullscreenOutlined className={styles.meetingContentSwiperCardFullScreenIcon} title='全屏' onClick={() => setIsVideoFullScreen(true)} />}
{meetingContentUser(currentLookUserAccount, true)} {meetingContentUser(currentLookUserAccount, true)}
</div> : null} </div> : null}
{currentLookUserStatus === 1 ? {meetingMode !== "FreedomMode" && currentLookUserStatus === 1 ?
<div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass()} ${setMeetingContentSwiperCardFullScreenClass()}`}> <div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass()} ${setMeetingContentSwiperCardFullScreenClass()}`}>
<div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-camera-primary'> <div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-camera-primary'>
{<div className={styles.meetingContentSwiperCardVdeioLoading}> {<div className={styles.meetingContentSwiperCardVdeioLoading}>
@ -2986,7 +3092,7 @@ const Meeting: React.FC = () => {
{meetingContentUser(currentLookUserAccount, true)} {meetingContentUser(currentLookUserAccount, true)}
{currentLookUserAccount.enableCamera ? null : meetingContentError(currentLookUserAccount)} {currentLookUserAccount.enableCamera ? null : meetingContentError(currentLookUserAccount)}
</div> : null} </div> : null}
{currentLookUserStatus === 2 ? {meetingMode !== "FreedomMode" && currentLookUserStatus === 2 ?
<div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass()} ${setMeetingContentSwiperCardFullScreenClass()}`}> <div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass()} ${setMeetingContentSwiperCardFullScreenClass()}`}>
<div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-screen'> <div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-screen'>
<div className={styles.meetingContentSwiperCardVdeioLoading}> <div className={styles.meetingContentSwiperCardVdeioLoading}>
@ -2997,7 +3103,7 @@ const Meeting: React.FC = () => {
<FullscreenOutlined className={styles.meetingContentSwiperCardFullScreenIcon} title='全屏' onClick={() => setIsVideoFullScreen(true)} />} <FullscreenOutlined className={styles.meetingContentSwiperCardFullScreenIcon} title='全屏' onClick={() => setIsVideoFullScreen(true)} />}
{meetingContentUser(currentLookUserAccount, true)} {meetingContentUser(currentLookUserAccount, true)}
</div> : null} </div> : null}
{currentLookUserStatus === 3 ? {meetingMode !== "FreedomMode" && currentLookUserStatus === 3 ?
<div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass()} ${setMeetingContentSwiperCardFullScreenClass()}`}> <div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass()} ${setMeetingContentSwiperCardFullScreenClass()}`}>
<div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-remote-screen'> <div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-remote-screen'>
<div className={styles.meetingContentSwiperCardVdeioLoading}> <div className={styles.meetingContentSwiperCardVdeioLoading}>
@ -3008,7 +3114,7 @@ const Meeting: React.FC = () => {
<FullscreenOutlined className={styles.meetingContentSwiperCardFullScreenIcon} title='全屏' onClick={() => setIsVideoFullScreen(true)} />} <FullscreenOutlined className={styles.meetingContentSwiperCardFullScreenIcon} title='全屏' onClick={() => setIsVideoFullScreen(true)} />}
{meetingContentUser(currentLookUserAccount, true)} {meetingContentUser(currentLookUserAccount, true)}
</div> : null} </div> : null}
{currentLookUserStatus === 4 ? {meetingMode !== "FreedomMode" && currentLookUserStatus === 4 ?
<div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass()} ${setMeetingContentSwiperCardFullScreenClass()}`}> <div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass()} ${setMeetingContentSwiperCardFullScreenClass()}`}>
<div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-remote-camera'> <div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-remote-camera'>
<div className={styles.meetingContentSwiperCardVdeioLoading}> <div className={styles.meetingContentSwiperCardVdeioLoading}>
@ -3026,7 +3132,7 @@ const Meeting: React.FC = () => {
</div> </div>
</div> </div>
{ {
(statusList.userList || statusList.userChatList || statusList.userVideo) ? ( (statusList.userList || statusList.userChatList) ? (
<div className={`${styles.meetingContentBodyRight} drag`}> <div className={`${styles.meetingContentBodyRight} drag`}>
{statusList.userList ? {statusList.userList ?
<div className={styles.meetingUserList}> <div className={styles.meetingUserList}>
@ -3036,7 +3142,6 @@ const Meeting: React.FC = () => {
setStatusList({ setStatusList({
userList: false, userList: false,
userChatList: false, userChatList: false,
userVideo: false,
}) })
}} /> }} />
</div> </div>
@ -3182,7 +3287,6 @@ const Meeting: React.FC = () => {
setStatusList({ setStatusList({
userList: false, userList: false,
userChatList: false, userChatList: false,
userVideo: false,
}) })
}} /> }} />
</div> </div>
@ -3345,22 +3449,7 @@ const Meeting: React.FC = () => {
</div> </div>
</div> </div>
: :
<div className={styles.meetingUserVideoList} style={{ width: statusList.userVideo ? '500px' : '500px' }}> null
<div className={styles.meetingUserVideoListTitle}>
<span></span>
<img src={ImageUrl.icon18} alt="" onClick={async () => {
await agora.allLeaveChannelEx()
setStatusList({
userList: false,
userChatList: false,
userVideo: false,
})
}} />
</div>
<div className={styles.meetingUserVideoListContent}>
<UserVideo />
</div>
</div>
} }
</div> </div>
) : null ) : null
@ -3466,7 +3555,7 @@ const Meeting: React.FC = () => {
onMouseUp={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} onMouseUp={() => changeFooterListSelect(row, itemIndex, rowIndex, false)}
onMouseLeave={() => changeFooterListSelect(row, itemIndex, rowIndex, false)} onMouseLeave={() => changeFooterListSelect(row, itemIndex, rowIndex, false)}
key={rowIndex}> key={rowIndex}>
{statusList.userVideo ? <img src={row.iconSelect} alt="" /> : row.select ? <img src={row.iconSelect} alt="" /> : <img src={row.active ? row.iconActive : row.icon} alt="" />} {userVideoWindow ? <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> <span>{row.title}</span>
</div> </div>
} }

3
src/render.d.ts vendored
View File

@ -32,6 +32,9 @@ export interface IElectronAPI {
setRegistry: (uuid: string) => any; setRegistry: (uuid: string) => any;
getRegistry: () => any; getRegistry: () => any;
createChildWindow: (str: string) => void; createChildWindow: (str: string) => void;
isCustomizeCreateChildOpen: (str: string) => Promise<boolean>;
closeCustomizeCreateChildWindow: (str: string) => void;
customizeCreateChildWindow: (config: any) => void;
setChildWindow: (config: any) => void; setChildWindow: (config: any) => void;
setChildWindowShow: (config: any) => void; setChildWindowShow: (config: any) => void;
closeChildWindow: (key: string) => void; closeChildWindow: (key: string) => void;

View File

@ -292,6 +292,7 @@ export const agora = {
}) })
agora.stopScreenCapture() agora.stopScreenCapture()
agora.release() agora.release()
window.electron.closeCustomizeCreateChildWindow('meetingMonitoring')
}, },
// 加入频道 // 加入频道
joinChannel: async () => { joinChannel: async () => {
@ -346,34 +347,20 @@ export const agora = {
} }
); );
}, },
// 所有用户加入的第二个房间 // 监控
allJoinChannelEx: async (bool: boolean = false, token: string) => { allJoinChannelEx: async (user: any, bool: boolean) => {
const user = await JSON.parse(storage.getItem('user') as string) if (!role.ID.includes(user.roleId) || !user.isRoomManager) {
await agora.startCameraCapture(true) await rtcEngine.updateChannelMediaOptions({
await rtcEngine.joinChannelEx( clientRoleType: bool ? ClientRoleType.ClientRoleBroadcaster : ClientRoleType.ClientRoleAudience, //用户角色 ClientRoleBroadcaster 主播 ClientRoleAudience 观众
token,
{ channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) },
{
clientRoleType: bool ? ClientRoleType.ClientRoleAudience : ClientRoleType.ClientRoleBroadcaster, //用户角色 ClientRoleBroadcaster 主播 ClientRoleAudience 观众
autoSubscribeAudio: false,//设置是否自动订阅所有音频流 autoSubscribeAudio: false,//设置是否自动订阅所有音频流
autoSubscribeVideo: role.ID.includes(user.roleId) ? true : false,//设置是否自动订阅所有视频流 autoSubscribeVideo: false,//设置是否自动订阅所有视频流
publishMicrophoneTrack: false,//设置是否发布麦克风采集到的音频 publishMicrophoneTrack: false,//设置是否发布麦克风采集到的音频
publishCameraTrack: true,//设置是否发布摄像头采集的视频 publishCameraTrack: true,//设置是否发布摄像头采集的视频
publishScreenTrack: false,//设置是否发布屏幕采集的视频 publishScreenTrack: false,//设置是否发布屏幕采集的视频
audienceLatencyLevel: bool ? AudienceLatencyLevelType.AudienceLatencyLevelLowLatency : AudienceLatencyLevelType.AudienceLatencyLevelUltraLowLatency, audienceLatencyLevel: bool ? AudienceLatencyLevelType.AudienceLatencyLevelUltraLowLatency : AudienceLatencyLevelType.AudienceLatencyLevelLowLatency,
})
bool ? agora.startCameraCapture() : agora.stopCameraCapture()
} }
);
await rtcEngine.setDualStreamModeEx(
SimulcastStreamMode.EnableSimulcastStream,
{
dimensions: {
width: 320,
height: 180
},
framerate: 5,
},
{ channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) }
);
}, },
// 退出第二个房间 // 退出第二个房间
allLeaveChannelEx: async () => { allLeaveChannelEx: async () => {
@ -604,4 +591,13 @@ export const agora = {
greenCapacity: 1 greenCapacity: 1
}) })
}, },
// 调节音频采集信号音量。
adjustRecordingSignalVolume: async (number: number) => {
const setting = await JSON.parse(storage.getItem('setting') as string)
if (setting.autoEcordingVolume) {
await rtcEngine.adjustRecordingSignalVolume(number)
} else {
await rtcEngine.adjustRecordingSignalVolume(100)
}
}
} }

View File

@ -86,6 +86,7 @@ import icon54 from '@/assets/icon54.png'
import icon55 from '@/assets/icon55.png' import icon55 from '@/assets/icon55.png'
import icon56 from '@/assets/icon56.png' import icon56 from '@/assets/icon56.png'
import icon56Active from '@/assets/icon56-active.png' import icon56Active from '@/assets/icon56-active.png'
import icon57 from '@/assets/icon57.png'
export default { export default {
loading, loading,
icon, icon,
@ -174,5 +175,6 @@ export default {
icon54, icon54,
icon55, icon55,
icon56, icon56,
icon56Active icon56Active,
icon57,
} }

View File

@ -3,6 +3,7 @@ import react from '@vitejs/plugin-react'
import pxtovw from 'postcss-px-to-viewport-8-plugin' import pxtovw from 'postcss-px-to-viewport-8-plugin'
import { resolve as resolvePath } from 'path' import { resolve as resolvePath } from 'path'
import resolve from 'vite-plugin-resolve'; import resolve from 'vite-plugin-resolve';
import { createHtmlPlugin } from 'vite-plugin-html';
const loder_pxtovw = pxtovw({ const loder_pxtovw = pxtovw({
viewportWidth: 1900, viewportWidth: 1900,
viewportUnit: 'vw', viewportUnit: 'vw',
@ -51,6 +52,20 @@ export default defineConfig({
base: './', // 这里更改打包相对绝对路径 base: './', // 这里更改打包相对绝对路径
plugins: [ plugins: [
react(), react(),
createHtmlPlugin({
minify: false,
pages: [
{
template: 'index.html',
filename: 'index.html',
injectOptions: {
data: {
buildTime: +new Date()
}
}
}
]
}),
resolve({ resolve({
'agora-electron-sdk': ` 'agora-electron-sdk': `
const { const {