yangjie #22

Merged
yangqiang merged 99 commits from yangjie into master 2024-10-22 16:11:46 +08:00
10 changed files with 465 additions and 42 deletions
Showing only changes of commit ad07bd753f - Show all commits

18
main.js
View File

@ -325,9 +325,18 @@ app.on('ready', () => {
});
// 关闭子窗口
ipcMain.handle('closeChildWindow', (event, key) => {
if (key === 'shareScreenWindow') {
for (const k in childWindow) {
if (childWindow[k]){
childWindow[k].close()
childWindow[k] = ""
}
}
mainWindowCenter()
} else {
childWindow[key].close()
childWindow[key] = ""
mainWindowCenter()
}
});
// 隐藏主窗口
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()
}
}
}
// 主窗口居中

View File

@ -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 = () => {
<Route path='/login' element={<Login />} />
<Route path='/meeting' element={<Meeting />} />
<Route path='/shareScreenWindow' element={<ShareScreenWindow />} />
<Route path='/userListWindow' element={<UserListWindow />} />
<Route path='*' element={<NotFound />} />
</Routes>
<Spin spinning={spinning} fullscreen />

View File

@ -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<any>({})
const [userName, setUserName] = useState<any>({})
const handleWindowsChange = async (): Promise<void> => {
const isOpen = await getKeyOpenChildWindow('shareScreenWindow')
if (isOpen) {
window.electron.mainWindowHide()
}
setEquipmentManagementModal(false)
}
return (
<>
<Modal
@ -30,7 +38,7 @@ const EquipmentManagement = forwardRef((_props: any, ref: any) => {
centered
width={'500px'}
onCancel={() => {
setEquipmentManagementModal(false)
handleWindowsChange()
}}>
<div className={styles.equipmentManagement}>
<div>
@ -87,12 +95,12 @@ const EquipmentManagement = forwardRef((_props: any, ref: any) => {
uid: callerUid,
driversJsonString: JSON.stringify(deviceInfo)
})
setEquipmentManagementModal(false)
handleWindowsChange()
message.success('设置成功')
}}></Button>
<Button type="primary"
style={{ backgroundColor: '#31353A', marginLeft: '10px' }}
onClick={() => setEquipmentManagementModal(false)}></Button>
onClick={() => handleWindowsChange()}></Button>
</div>
</div>
</Modal >

View File

@ -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)
}}

View File

@ -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<any>([])
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 (
<>
<div className={`${styles.shareScreenWindow} drag`}>
<div className={styles.shareScreenWindow}>
<div className={styles.shareScreenWindowTitle}>{time} </div>
<div className={styles.shareScreenWindowContent}>
<div className={`${styles.shareScreenWindowContent} drag`}>
<div className={styles.shareScreenWindowContentList}>
{footerLists.map((item: any, index: number) => {
return (
<div
onClick={() => {
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 = () => {
</div>
<Button type="primary" style={{ backgroundColor: '#FF5219', marginRight: '14px' }} onClick={() => {
channel.postMessage({
type: 'closeShareScreen'
type: 'shareScreenWindowClose'
});
}}>
</Button>
</div>
</div>
</div >
</>
)
}

View File

@ -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%);
}
}
}
}

View File

@ -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<any>({});
const [roomUserList, setRoomUserList] = useState<any>([])
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<void> => {
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 (
<>
<div className={`${styles.userListWindow}`}>
<div className={styles.userListWindowTitle}>
<span></span>
<img src={ImageUrl.icon18} className='drag' alt="" onClick={() => {
window.electron.closeChildWindow('userListWindow')
setKeyOpenChildWindow('userListWindow', false)
}} />
</div>
<div className='drag' style={{ padding: '0 10px' }}>
<Input
placeholder="请输入用户名"
prefix={<SearchOutlined style={{ color: 'white' }} />}
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)
}}
/>
</div>
<div className={`${styles.userListWindowContent} drag`}>
{roomUserList.map((item: any, index: number) => {
return (
item.isShow && item.isRoom ? <div key={index + item.uid}>
<div>
<div><Avatar name={item.userName} /></div>
<span>
{item.userName}{item.uid === user.uid ? '(我)' : ''}
{role.ID.includes(item.roleId) || item.isRoomManager ?
<span style={{ color: '#02B188', marginLeft: '4px' }}>
{role.ID.includes(item.roleId) ? '管理员' : '发言人'}
</span>
: null}
</span>
</div>
<div>
{role.ID.includes(item.roleId) || item.isRoomManager ? <div>
<img src={item.enableMicr ? ImageUrl.icon22 : ImageUrl.icon22Active} alt="" onClick={() => {
channel.postMessage({
type: 'userListWindowPostOpenMicr',
userListWindowPostOpenMicr: {
enableMicr: !item.enableMicr,
uid: item.uid
}
});
}} title={item.enableMicr ? '静音' : '解除声音'} />
</div> : null}
{role.ID.includes(item.roleId) || item.isRoomManager ? <div>
<img src={item.enableCamera ? ImageUrl.icon23 : ImageUrl.icon23Active} alt="" onClick={() => {
channel.postMessage({
type: 'userListWindowPostOpenCamera',
userListWindowPostOpenCamera: {
enableCamera: !item.enableCamera,
uid: item.uid
}
});
}} title={item.enableCamera ? '关闭视频' : '开启视频'} />
</div> : null}
{item.uid !== user.uid && role.ID.includes(user.roleId) ? <div>
<Popover placement="left" title={''} content={
<div style={{ width: '100px' }}>
{!role.ID.includes(item.roleId) ? <Button
type="primary"
className='m-ant-btn'
style={{ marginBottom: '10px', width: '100%' }}
size={'small'}
onClick={() => {
if (item.isRoomManager) {
channel.postMessage({
type: 'userListWindowDeleteRoomManager',
userListWindowDeleteRoomManager: {
uid: item.uid
}
});
} else {
channel.postMessage({
type: 'userListWindowPostRoomManager',
userListWindowPostRoomManager: {
uid: item.uid
}
});
}
}}
>{item.isRoomManager ? '取消发言人' : '设为发言人'}</Button> : null}
<Button
type="primary"
className='m-ant-btn'
style={{ width: '100%' }}
size={'small'}
onClick={() => {
channel.postMessage({
type: 'shareScreenWindowEquipmentManagement',
shareScreenWindowEquipmentManagement: {
uid: item.uid,
userName: item.userName,
}
});
window.electron.closeChildWindow('userListWindow')
setKeyOpenChildWindow('userListWindow', false)
}}
></Button>
<Button
type="primary"
style={{ backgroundColor: '#EC3C3C', width: '100%', marginTop: '10px' }}
size={'small'}
onClick={() => {
confirm({
title: '移出会议',
icon: <ExclamationCircleFilled />,
content: `确定将用户${item.userName}移出会议?`,
centered: true,
okText: '确定',
cancelText: '取消',
async onOk() {
channel.postMessage({
type: 'userListWindowGetRoomKickout',
userListWindowGetRoomKickout: {
uid: item.uid
}
});
},
onCancel() {
},
});
}}
></Button>
</div>
}>
<EllipsisOutlined style={{
color: '#fff',
fontSize: '20px'
}} />
</Popover>
</div> : null}
</div>
</div> : null
)
}
)}
</div>
<div className={`${styles.userListWindowFooter}`}>
<div className='drag' onClick={() => {
channel.postMessage({
type: 'userListWindowAllPostOpenMicr'
});
}}></div>
</div>
</div>
</>
)
}
export default UserListWindow

View File

@ -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<void> => {
@ -2015,7 +2059,7 @@ const Meeting: React.FC = () => {
</div> : null}
{item.uid !== user.uid && role.ID.includes(user.roleId) ? <div>
<Popover placement="left" title={''} content={
<div>
<div style={{ width: '100px' }}>
{!role.ID.includes(item.roleId) ? <Button
type="primary"
className='m-ant-btn'

View File

@ -0,0 +1,15 @@
import storage from "./storage";
export const setKeyOpenChildWindow = async (key: string, bool: boolean) => {
const openChildWindow = await JSON.parse(storage.getItem('openChildWindow') as string)
openChildWindow[key] = bool;
if (key === 'shareScreenWindow' && !bool) {
for (const k in openChildWindow) {
openChildWindow[k] = false
}
}
storage.setItem('openChildWindow', JSON.stringify(openChildWindow))
};
export const getKeyOpenChildWindow = async (key: string): Promise<boolean> => {
const openChildWindow = await JSON.parse(storage.getItem('openChildWindow') as string)
return openChildWindow[key]
};