From ad07bd753fe592315dd94ba2b2b8eaac9ae6a3aa Mon Sep 17 00:00:00 2001 From: yj <1336058017@qq.com> Date: Sat, 12 Oct 2024 16:17:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=88=90=E5=91=98=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.js | 22 +- src/App.tsx | 10 +- src/components/EquipmentManagement/index.tsx | 14 +- src/components/StupWizard/index.tsx | 7 +- .../ShareScreenWindow/index.module.scss | 0 .../{ => Meeting}/ShareScreenWindow/index.tsx | 33 ++- .../Meeting/UserListWindow/index.module.scss | 120 ++++++++++ src/page/Meeting/UserListWindow/index.tsx | 210 ++++++++++++++++++ src/page/Meeting/index.tsx | 76 +++++-- src/utils/package/public.ts | 15 ++ 10 files changed, 465 insertions(+), 42 deletions(-) rename src/page/{ => Meeting}/ShareScreenWindow/index.module.scss (100%) rename src/page/{ => Meeting}/ShareScreenWindow/index.tsx (80%) create mode 100644 src/page/Meeting/UserListWindow/index.module.scss create mode 100644 src/page/Meeting/UserListWindow/index.tsx create mode 100644 src/utils/package/public.ts diff --git a/main.js b/main.js index 57a68f6..0eea96e 100644 --- a/main.js +++ b/main.js @@ -325,9 +325,18 @@ app.on('ready', () => { }); // 关闭子窗口 ipcMain.handle('closeChildWindow', (event, key) => { - childWindow[key].close() - childWindow[key] = "" - mainWindowCenter() + if (key === 'shareScreenWindow') { + for (const k in childWindow) { + if (childWindow[k]){ + childWindow[k].close() + childWindow[k] = "" + } + } + mainWindowCenter() + } else { + childWindow[key].close() + childWindow[key] = "" + } }); // 隐藏主窗口 ipcMain.handle('mainWindowHide', () => { @@ -408,16 +417,13 @@ function quitAndInstall() { function windowOperation(config) { const child = childWindow[config.key]; + child.setResizable(false) + if (env === 'development') child.webContents.openDevTools(); if (config.key === 'shareScreenWindow') { const display = screen.getDisplayMatching({ ...child.getBounds() }); const x = Math.round((display.workArea.width - child.getSize()[0]) / 2); child.setPosition(x, 0); - child.setResizable(false) - child.setMovable(false) mainWindowHide() - if (env === 'development') { - child.webContents.openDevTools() - } } } // 主窗口居中 diff --git a/src/App.tsx b/src/App.tsx index c6d6195..8cdedbe 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -18,7 +18,8 @@ import { agora } from "@/utils/package/agora"; import QuitTips from "@/components/QuitTips"; import { GetLeave } from "@/api/Meeting"; import path from "path"; -import ShareScreenWindow from "./page/ShareScreenWindow"; +import ShareScreenWindow from "@/page/Meeting/ShareScreenWindow"; +import UserListWindow from "@/page/Meeting/UserListWindow"; const fs = require('fs').promises; const { exec } = require('child_process'); const App: React.FC = () => { @@ -33,7 +34,8 @@ const App: React.FC = () => { }); const [spinning, setSpinning] = useState(false); const [isState, setIsState] = useState(true); - if (location.hash.indexOf('shareScreenWindow') == -1) { + const urlHashArr = ['#/userListWindow', '#/shareScreenWindow'] + if (urlHashArr.indexOf(location.hash) == -1) { useEffect(() => { let userInfo = JSON.parse(storage.getItem('user') as string) let loginInfo = JSON.parse(storage.getItem('login') as string) @@ -122,6 +124,9 @@ const App: React.FC = () => { aINoiseReduction: 1, // 降噪模式 })) } + if (!storage.getItem('openChildWindow')) { + storage.setItem('openChildWindow', JSON.stringify({})) + } }, []) useEffect(() => { if (isState) { @@ -243,6 +248,7 @@ const App: React.FC = () => { } /> } /> } /> + } /> } /> diff --git a/src/components/EquipmentManagement/index.tsx b/src/components/EquipmentManagement/index.tsx index cf20fb2..d9f01f9 100644 --- a/src/components/EquipmentManagement/index.tsx +++ b/src/components/EquipmentManagement/index.tsx @@ -1,4 +1,5 @@ import styles from '@/components/EquipmentManagement/index.module.scss' +import { getKeyOpenChildWindow } from '@/utils/package/public'; import { onInvoke } from '@/utils/package/signalr'; import { Button, Modal, Select, Slider, message } from 'antd'; import { useState, useImperativeHandle, forwardRef } from "react"; @@ -21,6 +22,13 @@ const EquipmentManagement = forwardRef((_props: any, ref: any) => { const [callerUid, setCallerUid] = useState('') const [deviceInfo, setDeviceInfo] = useState({}) const [userName, setUserName] = useState({}) + const handleWindowsChange = async (): Promise => { + const isOpen = await getKeyOpenChildWindow('shareScreenWindow') + if (isOpen) { + window.electron.mainWindowHide() + } + setEquipmentManagementModal(false) + } return ( <> { centered width={'500px'} onCancel={() => { - setEquipmentManagementModal(false) + handleWindowsChange() }}>
@@ -87,12 +95,12 @@ const EquipmentManagement = forwardRef((_props: any, ref: any) => { uid: callerUid, driversJsonString: JSON.stringify(deviceInfo) }) - setEquipmentManagementModal(false) + handleWindowsChange() message.success('设置成功') }}>确定 + onClick={() => handleWindowsChange()}>取消
diff --git a/src/components/StupWizard/index.tsx b/src/components/StupWizard/index.tsx index 2eb9138..6dc22d6 100644 --- a/src/components/StupWizard/index.tsx +++ b/src/components/StupWizard/index.tsx @@ -6,6 +6,7 @@ import { agora } from '@/utils/package/agora' import { CloseOutlined, LoadingOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import { storage } from '@/utils'; import path from 'path'; +import { getKeyOpenChildWindow } from '@/utils/package/public'; const fs = require('fs').promises; const { exec } = require('child_process'); @@ -95,13 +96,13 @@ const StupWizard = forwardRef((props: any, ref: any) => { top: '16px', cursor: 'pointer' }} - onClick={() => { + onClick={async () => { if (location.hash.indexOf('/meeting') === -1) { agora.release() } - if (storage.getItem('isOpenChildWindow') === 'true') { + const isOpen = await getKeyOpenChildWindow('shareScreenWindow') + if (isOpen) { window.electron.mainWindowHide() - storage.setItem('isOpenChildWindow', false) } setIsStupWizard(false) }} diff --git a/src/page/ShareScreenWindow/index.module.scss b/src/page/Meeting/ShareScreenWindow/index.module.scss similarity index 100% rename from src/page/ShareScreenWindow/index.module.scss rename to src/page/Meeting/ShareScreenWindow/index.module.scss diff --git a/src/page/ShareScreenWindow/index.tsx b/src/page/Meeting/ShareScreenWindow/index.tsx similarity index 80% rename from src/page/ShareScreenWindow/index.tsx rename to src/page/Meeting/ShareScreenWindow/index.tsx index af5f59a..a6fcbaa 100644 --- a/src/page/ShareScreenWindow/index.tsx +++ b/src/page/Meeting/ShareScreenWindow/index.tsx @@ -1,8 +1,9 @@ import { GetRoomUser, GetRoomUserItem } from '@/api/Meeting'; import { role } from '@/config/role'; -import styles from '@/page/ShareScreenWindow/index.module.scss' +import styles from '@/page/Meeting/ShareScreenWindow/index.module.scss' import { storage } from '@/utils'; import ImageUrl from '@/utils/package/imageUrl'; +import { getKeyOpenChildWindow, setKeyOpenChildWindow } from '@/utils/package/public'; import { Button } from 'antd'; import { useEffect, useState } from "react"; const ShareScreenWindow: React.FC = () => { @@ -54,7 +55,7 @@ const ShareScreenWindow: React.FC = () => { const [time, setTime] = useState('') const [roomUserLists, setRoomUserLists] = useState([]) const channel = new BroadcastChannel('meeting_channel'); - let userInfo = JSON.parse(storage.getItem('user') as string) + const userInfo = JSON.parse(storage.getItem('user') as string) useEffect(() => { getRoomUser() channel.onmessage = function (event) { @@ -106,14 +107,14 @@ const ShareScreenWindow: React.FC = () => { } return ( <> -
+
{time} 共享中
-
+
{footerLists.map((item: any, index: number) => { return (
{ + onClick={async () => { switch (item.title) { case '静音': case '解除静音': @@ -122,15 +123,27 @@ const ShareScreenWindow: React.FC = () => { case '录制': case '录制中': channel.postMessage({ - type: 'footerListsTitle', - footerListsTitle: item.title + type: 'shareScreenWindowfooterListsTitle', + shareScreenWindowfooterListsTitle: item.title }); break; case '设置': channel.postMessage({ - type: 'setting' + type: 'shareScreenWindowSetting' }); break; + case '成员列表': + const isOpen = await getKeyOpenChildWindow('userListWindow') + if (!isOpen) { + window.electron.createChildWindow({ + url: location.origin + `/#/userListWindow`, + width: 340, + height: 540, + key: 'userListWindow', + }) + setKeyOpenChildWindow('userListWindow', true) + } + break; } }} key={index}> @@ -142,13 +155,13 @@ const ShareScreenWindow: React.FC = () => {
-
+
) } diff --git a/src/page/Meeting/UserListWindow/index.module.scss b/src/page/Meeting/UserListWindow/index.module.scss new file mode 100644 index 0000000..2cc7677 --- /dev/null +++ b/src/page/Meeting/UserListWindow/index.module.scss @@ -0,0 +1,120 @@ +.userListWindow { + height: 100%; + width: 100%; + box-sizing: border-box; + background-color: #16191E; + display: flex; + flex-direction: column; + box-sizing: border-box; + padding: 10px 0; + + >div { + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0px; + } + } + + .userListWindowTitle { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: space-between; + box-sizing: border-box; + padding: 0 10px; + + >span { + color: #EEEEEE; + font-size: 18px; + } + + >img { + cursor: pointer; + } + } + + .userListWindowContent { + flex-grow: 1; + height: 0px; + overflow-y: auto; + + >div { + display: flex; + align-items: center; + justify-content: space-between; + position: relative; + box-sizing: border-box; + padding: 4px 10px; + + >div:nth-child(1) { + display: flex; + align-items: center; + + >span { + font-size: 14px; + color: #F3F3F5; + margin-left: 4px; + } + + >div { + flex-shrink: 0; + } + } + + >div:nth-child(2) { + display: flex; + align-items: center; + + >div { + height: 30px; + width: 30px; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + + >img { + width: 20px; + } + + &:hover { + background-color: rgba(0, 0, 0, 0.5); + } + } + } + + &:hover { + background-color: rgb(52, 52, 52); + } + } + } + + .userListWindowFooter { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + + >div { + font-size: 14px; + cursor: pointer; + background-color: #31353A; + color: #EEEEEE; + width: 104px; + height: 30px; + line-height: 30px; + text-align: center; + border-radius: 5px; + + &:hover { + background-color: lighten(#31353A, 4%); + } + + &:active { + background-color: darken(#31353A, 4%); + } + } + } +} \ No newline at end of file diff --git a/src/page/Meeting/UserListWindow/index.tsx b/src/page/Meeting/UserListWindow/index.tsx new file mode 100644 index 0000000..b3bf4bf --- /dev/null +++ b/src/page/Meeting/UserListWindow/index.tsx @@ -0,0 +1,210 @@ +import { role } from '@/config/role'; +import styles from '@/page/Meeting/UserListWindow/index.module.scss' +import ImageUrl from '@/utils/package/imageUrl'; +import { EllipsisOutlined, ExclamationCircleFilled, SearchOutlined } from '@ant-design/icons'; +import { Button, Input, Modal, Popover } from 'antd'; +import Avatar from '@/components/Avatar'; +import { useEffect, useState } from "react"; +import { storage } from '@/utils'; +import { GetRoomUser } from '@/api/Meeting'; +import { setKeyOpenChildWindow } from '@/utils/package/public'; +const { confirm } = Modal; + +const UserListWindow: React.FC = () => { + const [userSearchValue, setUserSearchValue] = useState('') + const [user, setUser] = useState({}); + const [roomUserList, setRoomUserList] = useState([]) + const channel = new BroadcastChannel('meeting_channel'); + const userInfo = JSON.parse(storage.getItem('user') as string) + useEffect(() => { + setUser(userInfo) + getRoomUser() + channel.onmessage = function (event) { + const { type, roomUserList } = event.data; + switch (type) { + case 'roomUserList': + setRoomUserList(roomUserList) + break; + } + } + }, []); + // 获取房间用户 + const getRoomUser = async (): Promise => { + const data = JSON.parse(storage.getItem('stateInfo') as string) + GetRoomUser(data.channelId).then(res => { + if (res.code === 200) { + res.data.forEach((item: any) => { + item.isShow = true; + item.isRoom = true; + item.isAdmin = role.ID.includes(item.roleId) || item.isRoomManager + }) + setRoomUserList(res.data) + } + }) + } + return ( + <> +
+
+ 成员列表 + { + window.electron.closeChildWindow('userListWindow') + setKeyOpenChildWindow('userListWindow', false) + }} /> +
+
+ } + value={userSearchValue} + onChange={(e) => { + setUserSearchValue(e.target.value) + const newRoomUserList = [...roomUserList] + newRoomUserList.forEach(row => { + if (e.target.value) { + if (row.userName.indexOf(e.target.value) !== -1) { + row.isShow = true; + } else { + row.isShow = false; + } + } else { + row.isShow = true; + } + }); + setRoomUserList(newRoomUserList) + }} + /> +
+
+ {roomUserList.map((item: any, index: number) => { + return ( + item.isShow && item.isRoom ?
+
+
+ + {item.userName}{item.uid === user.uid ? '(我)' : ''} + {role.ID.includes(item.roleId) || item.isRoomManager ? + + {role.ID.includes(item.roleId) ? '管理员' : '发言人'} + + : null} + +
+
+ {role.ID.includes(item.roleId) || item.isRoomManager ?
+ { + channel.postMessage({ + type: 'userListWindowPostOpenMicr', + userListWindowPostOpenMicr: { + enableMicr: !item.enableMicr, + uid: item.uid + } + }); + }} title={item.enableMicr ? '静音' : '解除声音'} /> +
: null} + {role.ID.includes(item.roleId) || item.isRoomManager ?
+ { + channel.postMessage({ + type: 'userListWindowPostOpenCamera', + userListWindowPostOpenCamera: { + enableCamera: !item.enableCamera, + uid: item.uid + } + }); + }} title={item.enableCamera ? '关闭视频' : '开启视频'} /> +
: null} + {item.uid !== user.uid && role.ID.includes(user.roleId) ?
+ + {!role.ID.includes(item.roleId) ? : null} + + +
+ }> + + +
: null} +
+
: null + ) + } + )} +
+
+
{ + channel.postMessage({ + type: 'userListWindowAllPostOpenMicr' + }); + }}>全员静音
+
+
+ + ) +} + +export default UserListWindow diff --git a/src/page/Meeting/index.tsx b/src/page/Meeting/index.tsx index 06a65c7..56bbaec 100644 --- a/src/page/Meeting/index.tsx +++ b/src/page/Meeting/index.tsx @@ -22,6 +22,7 @@ import EquipmentManagement from '@/components/EquipmentManagement'; import UserVideo from '@/components/UserVideo'; import { role } from '@/config/role'; import { fixWebmDuration } from "webm-duration-fix-buffer"; +import { getKeyOpenChildWindow, setKeyOpenChildWindow } from '@/utils/package/public'; const { confirm } = Modal; const { exec } = require('child_process'); const fs = require('fs').promises; @@ -201,38 +202,77 @@ const Meeting: React.FC = () => { const container = document.getElementById('videoView') as HTMLElement; container.addEventListener('wheel', handleWheelChange); channel.onmessage = async function (event) { - const { type, footerListsTitle } = event.data; + const { + type, + shareScreenWindowfooterListsTitle, + userListWindowPostOpenMicr, + userListWindowPostOpenCamera, + userListWindowDeleteRoomManager, + userListWindowPostRoomManager, + userListWindowGetRoomKickout, + shareScreenWindowEquipmentManagement + } = event.data; switch (type) { - case 'closeShareScreen': + case 'shareScreenWindowClose': await stopScreenCapture() await allUserLook(userInfo.uid, userInfo.userName) break; - case 'setting': + case 'shareScreenWindowSetting': stupWizardRef.current.changeModal(3); window.electron.mainWindowCenter() break; - case 'footerListsTitle': - switch (footerListsTitle) { + case 'shareScreenWindowfooterListsTitle': + switch (shareScreenWindowfooterListsTitle) { case '静音': case '解除静音': changeStatusList({ - title: footerListsTitle + title: shareScreenWindowfooterListsTitle }, 0, 1) break; case '关闭视频': case '开启视频': changeStatusList({ - title: footerListsTitle + title: shareScreenWindowfooterListsTitle }, 0, 2) break; case '录制': case '录制中': changeStatusList({ - title: footerListsTitle + title: shareScreenWindowfooterListsTitle }, 1, 3) break; } break; + case 'userListWindowPostOpenMicr': + postOpenMicr(userListWindowPostOpenMicr.enableMicr, userListWindowPostOpenMicr.uid) + break; + case 'userListWindowPostOpenCamera': + postOpenCamera(userListWindowPostOpenCamera.enableCamera, userListWindowPostOpenCamera.uid) + break; + case 'userListWindowDeleteRoomManager': + DeleteRoomManager({ + roomId: state.roomId, + roomNum: state.channelId, + userId: userListWindowDeleteRoomManager.uid + }) + break; + case 'userListWindowPostRoomManager': + postRoomManager({ + roomId: state.roomId, + roomNum: state.channelId, + userId: userListWindowPostRoomManager.uid + }) + break; + case 'userListWindowGetRoomKickout': + GetRoomKickout(state.channelId, userListWindowGetRoomKickout.uid) + break; + case 'userListWindowAllPostOpenMicr': + postOpenMicr(false, userInfo.id, true) + break; + case 'shareScreenWindowEquipmentManagement': + equipmentManagement(shareScreenWindowEquipmentManagement.uid, shareScreenWindowEquipmentManagement.userName) + window.electron.mainWindowCenter() + break; } } time = setInterval(() => { @@ -1307,13 +1347,16 @@ const Meeting: React.FC = () => { setIsSharedScreenModal(false) await agora.setDesktopCapturerVideo(sharedScreenItem, isComputerAudio, isFluencyPriority) await allUserLook(user.screenShareId, user.userName) - // window.electron.createChildWindow({ - // url: location.origin + `/#/shareScreenWindow`, - // width: 540, - // height: 70, - // key: 'shareScreenWindow', - // }) - // storage.setItem('isOpenChildWindow', true) + const isOpen = await getKeyOpenChildWindow('shareScreenWindow') + if (!isOpen) { + window.electron.createChildWindow({ + url: location.origin + `/#/shareScreenWindow`, + width: 540, + height: 70, + key: 'shareScreenWindow', + }) + setKeyOpenChildWindow('shareScreenWindow', true) + } } else { message.error('请选择应用!') } @@ -1357,6 +1400,7 @@ const Meeting: React.FC = () => { footerListTemplate[1][0].title = '共享屏幕' setFooterList(footerListTemplate) window.electron.closeChildWindow('shareScreenWindow') + setKeyOpenChildWindow('shareScreenWindow', false) } // 获取房间用户 const getRoomUser = async (): Promise => { @@ -2015,7 +2059,7 @@ const Meeting: React.FC = () => {
: null} {item.uid !== user.uid && role.ID.includes(user.roleId) ?
+
{!role.ID.includes(item.roleId) ?