Compare commits
100 Commits
cb20942024
...
d536e5f100
| Author | SHA1 | Date |
|---|---|---|
|
|
d536e5f100 | |
|
|
69ffdd3c71 | |
|
|
4032056620 | |
|
|
8e65fdf520 | |
|
|
a4589d9f6f | |
|
|
79c4d27a2e | |
|
|
c67585bfec | |
|
|
6b4ee115c4 | |
|
|
30e96acff9 | |
|
|
56abb8a700 | |
|
|
9aaf69b7d7 | |
|
|
af39a32916 | |
|
|
e117c03e37 | |
|
|
e4a6cf69d8 | |
|
|
487675d20e | |
|
|
fa6b85e4f5 | |
|
|
7cae77f946 | |
|
|
5366601bb2 | |
|
|
6449efb56d | |
|
|
70469962be | |
|
|
30d739c894 | |
|
|
cfd0d1ceef | |
|
|
027622f544 | |
|
|
0160d4276f | |
|
|
70dc2321a0 | |
|
|
3423e13487 | |
|
|
b579f77b9f | |
|
|
634d02d28a | |
|
|
004e566964 | |
|
|
f7c9015767 | |
|
|
b7d41eff22 | |
|
|
170ff9b122 | |
|
|
d70f09f82d | |
|
|
e362145da9 | |
|
|
0d9d963593 | |
|
|
45557ef41d | |
|
|
b4493cf5d6 | |
|
|
dabeb8dfef | |
|
|
1bfbb47e95 | |
|
|
7ae50eca41 | |
|
|
b5bbee04b6 | |
|
|
c35b87ba90 | |
|
|
01aad9cb27 | |
|
|
b8aabf878b | |
|
|
f84c4d95b3 | |
|
|
d46b3027b9 | |
|
|
e157d8350c | |
|
|
b8ccf34edb | |
|
|
ed04fd7643 | |
|
|
78c1dd5026 | |
|
|
0776b93130 | |
|
|
a99194b067 | |
|
|
2389c7c48a | |
|
|
58facc62ea | |
|
|
bbc57b5185 | |
|
|
71d5ddffea | |
|
|
c76808939a | |
|
|
beacd92df1 | |
|
|
7020974f1a | |
|
|
c0b55cd062 | |
|
|
b01a0a2035 | |
|
|
d12ef5c633 | |
|
|
dfe7fafc13 | |
|
|
29b06d7903 | |
|
|
6b653becd9 | |
|
|
747fd4d25f | |
|
|
488f4e2763 | |
|
|
5125b83a93 | |
|
|
f08ef19fbe | |
|
|
54fb4ca40f | |
|
|
bcd3adaf26 | |
|
|
6fb7020836 | |
|
|
50e15e43bf | |
|
|
2e438090b8 | |
|
|
0939d6154b | |
|
|
c66f60d2ab | |
|
|
20b7aadeff | |
|
|
b96e5853a1 | |
|
|
e492d0fcf9 | |
|
|
444568b232 | |
|
|
225a8c8e71 | |
|
|
652830f6f1 | |
|
|
3e31304d43 | |
|
|
1fd3f421f8 | |
|
|
a32273eaae | |
|
|
e51a8e9742 | |
|
|
bc0724e428 | |
|
|
fe67cf1d0f | |
|
|
eee14c037d | |
|
|
c5d3804a62 | |
|
|
b42fc7f462 | |
|
|
187e91f07a | |
|
|
a849e8ee1b | |
|
|
6bf24cd85c | |
|
|
d1b58efc52 | |
|
|
ad07bd753f | |
|
|
49d73bedce | |
|
|
e89d820797 | |
|
|
7b01ff6722 | |
|
|
5a54e639a2 |
183
main.js
183
main.js
|
|
@ -52,23 +52,6 @@ class AppWindow extends BrowserWindow {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
function showWindow() {
|
||||
// 如果主窗口已经存在但被最小化了,则恢复显示
|
||||
if (mainWindow && mainWindow.isMinimized()) {
|
||||
mainWindow.show();
|
||||
}
|
||||
// 如果主窗口已存在但不是焦点窗口,则将其置为焦点
|
||||
if (mainWindow && !mainWindow.isFocused()) {
|
||||
mainWindow.show();
|
||||
mainWindow.focus();
|
||||
}
|
||||
// 如果主窗口还没有被创建,则创建它
|
||||
if (!mainWindow) {
|
||||
createWindow();
|
||||
}
|
||||
}
|
||||
|
||||
function quit() {
|
||||
app.quit()
|
||||
}
|
||||
|
|
@ -80,7 +63,7 @@ function createTray() {
|
|||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '打开', click: () => {
|
||||
showWindow()
|
||||
mainWindow.webContents.send('isOpenWindows');
|
||||
},
|
||||
// icon: iconPath,
|
||||
},
|
||||
|
|
@ -100,11 +83,7 @@ function createTray() {
|
|||
tray.setToolTip('智汇享');
|
||||
tray.setContextMenu(contextMenu);
|
||||
tray.on('click', () => {
|
||||
if (mainWindow.isVisible()) {
|
||||
mainWindow.hide()
|
||||
} else {
|
||||
mainWindow.show()
|
||||
}
|
||||
mainWindow.webContents.send('isOpenWindows');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -113,11 +92,6 @@ function createWindow() {
|
|||
mainWindow.focus();
|
||||
}
|
||||
const additionalData = { myKey: 'myValue' }
|
||||
// 退出房间
|
||||
app.on('will-quit', async (event) => {
|
||||
await mainWindow.webContents.send('quitAndInstall');
|
||||
});
|
||||
|
||||
app.on('ready', () => {
|
||||
const gotTheLock = app.requestSingleInstanceLock(additionalData)
|
||||
if (gotTheLock) {
|
||||
|
|
@ -161,7 +135,6 @@ app.on('ready', () => {
|
|||
mainWindow.on('move', () => {
|
||||
// 如果是全屏自动恢复到上次窗口大小
|
||||
if (isMaximized) {
|
||||
mainWindow.setResizable(true)
|
||||
mainWindow.unmaximize()
|
||||
isMaximized = false;
|
||||
}
|
||||
|
|
@ -177,10 +150,8 @@ app.on('ready', () => {
|
|||
break;
|
||||
case 'maximize':
|
||||
mainWindow.maximize()
|
||||
mainWindow.setResizable(false)
|
||||
break;
|
||||
case 'unmaximize':
|
||||
mainWindow.setResizable(true)
|
||||
mainWindow.unmaximize()
|
||||
break;
|
||||
case 'minimize':
|
||||
|
|
@ -189,6 +160,10 @@ app.on('ready', () => {
|
|||
case 'hide':
|
||||
mainWindow.hide()
|
||||
break;
|
||||
case 'show':
|
||||
mainWindow.show()
|
||||
mainWindow.focus();
|
||||
break;
|
||||
}
|
||||
});
|
||||
// 导出是否全屏
|
||||
|
|
@ -199,6 +174,10 @@ app.on('ready', () => {
|
|||
ipcMain.handle('getVersion', () => {
|
||||
return app.getVersion();
|
||||
});
|
||||
// 获取窗口是否显示
|
||||
ipcMain.handle('isVisible', () => {
|
||||
return mainWindow.isVisible();
|
||||
});
|
||||
// 获取共享屏幕列表
|
||||
ipcMain.handle('getSources', async () => {
|
||||
return await desktopCapturer.getSources({
|
||||
|
|
@ -210,9 +189,12 @@ app.on('ready', () => {
|
|||
clipboard.writeText(text)
|
||||
});
|
||||
// 退出
|
||||
ipcMain.handle('quit', async (event) => {
|
||||
await mainWindow.webContents.send('quitAndInstall');
|
||||
quit()
|
||||
ipcMain.handle('quit', async (event, bool) => {
|
||||
if (bool) {
|
||||
quit()
|
||||
} else {
|
||||
await mainWindow.webContents.send('quitAndInstall');
|
||||
}
|
||||
});
|
||||
// 加入房间通知
|
||||
ipcMain.handle('joinNotification', (event, user) => {
|
||||
|
|
@ -273,10 +255,7 @@ app.on('ready', () => {
|
|||
// 设置窗口尺寸
|
||||
mainWindow.setSize(config.width, config.height)
|
||||
// 设置窗口位置使其居中于当前屏幕
|
||||
const display = screen.getDisplayMatching({ ...mainWindow.getBounds() });
|
||||
const x = Math.round((display.workArea.width - mainWindow.getSize()[0]) / 2);
|
||||
const y = Math.round((display.workArea.height - mainWindow.getSize()[1]) / 2);
|
||||
mainWindow.setPosition(x, y);
|
||||
mainWindowCenter()
|
||||
});
|
||||
// 写入注册表
|
||||
ipcMain.handle('setRegistry', (event, uuid) => {
|
||||
|
|
@ -319,19 +298,90 @@ app.on('ready', () => {
|
|||
width: config.width,
|
||||
height: config.height,
|
||||
})
|
||||
child.loadURL(config.url)
|
||||
if (env === 'development') {
|
||||
// 开发
|
||||
child.loadURL(config.url)
|
||||
} else {
|
||||
// 测试 | 生产
|
||||
child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`);
|
||||
}
|
||||
childWindow[config.key] = child
|
||||
child.once('ready-to-show', () => {
|
||||
child.show()
|
||||
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)
|
||||
mainWindow.setPosition(-999999, -999999);
|
||||
if (config.show) {
|
||||
childWindow[config.key].show()
|
||||
}
|
||||
childWindow[config.key].setAlwaysOnTop(true, 'screen-saver')
|
||||
childWindow[config.key].setSkipTaskbar(true)
|
||||
windowOperation(config)
|
||||
})
|
||||
child.webContents.on('before-input-event', (event, input) => {
|
||||
// if (env === 'development') {
|
||||
if (input.key === 'F12') {
|
||||
child.webContents.openDevTools()
|
||||
}
|
||||
// }
|
||||
});
|
||||
});
|
||||
// 关闭子窗口
|
||||
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] = ""
|
||||
}
|
||||
});
|
||||
// 设置子窗口
|
||||
ipcMain.handle('setChildWindow', (event, config) => {
|
||||
switch (config.key) {
|
||||
case 'shareScreenWindow':
|
||||
childWindow[config.key].setBounds({ width: config.width })
|
||||
break;
|
||||
case 'chatSmallWindow':
|
||||
childWindow[config.key].setBounds({ height: config.height })
|
||||
break;
|
||||
case 'noticeWindow':
|
||||
childWindow[config.key].setBounds({ width: config.width, height: config.height })
|
||||
break;
|
||||
}
|
||||
});
|
||||
// 隐藏显示子窗口
|
||||
ipcMain.handle('setChildWindowShow', (event, config) => {
|
||||
if (config.key === 'noticeWindow') {
|
||||
if (config.bool) {
|
||||
childWindow[config.key].show()
|
||||
} else {
|
||||
if (childWindow[config.key].isVisible()) {
|
||||
childWindow[config.key].hide()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (childWindow[config.key].isVisible()) {
|
||||
childWindow[config.key].hide()
|
||||
} else {
|
||||
childWindow[config.key].show()
|
||||
}
|
||||
}
|
||||
});
|
||||
// 隐藏主窗口
|
||||
ipcMain.handle('mainWindowHide', () => {
|
||||
mainWindowHide()
|
||||
});
|
||||
// 居中主窗口
|
||||
ipcMain.handle('mainWindowCenter', () => {
|
||||
mainWindowCenter()
|
||||
});
|
||||
// 窗口通信
|
||||
ipcMain.handle('windowHandleMessage', (event, data) => {
|
||||
if (childWindow[data.key]) {
|
||||
childWindow[data.key].webContents.send('windowHandleMessageCallBack', data)
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -401,3 +451,42 @@ function cancleDownloadUpdate() {
|
|||
function quitAndInstall() {
|
||||
autoUpdater.quitAndInstall();
|
||||
}
|
||||
|
||||
function windowOperation(config) {
|
||||
const child = childWindow[config.key];
|
||||
const display = screen.getDisplayMatching({ ...child.getBounds() });
|
||||
const { width, height } = display.size
|
||||
let x, y;
|
||||
child.setResizable(false)
|
||||
switch (config.key) {
|
||||
case 'shareScreenWindow':
|
||||
x = Math.round((display.workArea.width - child.getSize()[0]) / 2);
|
||||
child.setPosition(x, 0);
|
||||
mainWindowHide()
|
||||
break;
|
||||
case 'chatSmallWindow':
|
||||
y = height - child.getSize()[1];
|
||||
child.setPosition(40, y - 200);
|
||||
break;
|
||||
case 'currentSpeakUserWindow':
|
||||
x = width - child.getSize()[0];
|
||||
child.setPosition(x - 40, 40);
|
||||
break;
|
||||
case 'noticeWindow':
|
||||
x = width - child.getSize()[0];
|
||||
y = height - child.getSize()[1];
|
||||
child.setPosition(x, y - 80);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 主窗口居中
|
||||
function mainWindowCenter() {
|
||||
const display = screen.getDisplayMatching({ ...mainWindow.getBounds() });
|
||||
const x = Math.round((display.workArea.width - mainWindow.getSize()[0]) / 2);
|
||||
const y = Math.round((display.workArea.height - mainWindow.getSize()[1]) / 2);
|
||||
mainWindow.setPosition(x, y);
|
||||
}
|
||||
// 主窗口隐藏
|
||||
function mainWindowHide() {
|
||||
mainWindow.setPosition(-999999, -999999);
|
||||
}
|
||||
40
preload.js
40
preload.js
|
|
@ -21,6 +21,10 @@ window.electron = {
|
|||
getVersion: () => {
|
||||
return ipcRenderer.invoke('getVersion')
|
||||
},
|
||||
// 获取窗口是否显示
|
||||
isVisible: () => {
|
||||
return ipcRenderer.invoke('isVisible')
|
||||
},
|
||||
// 获取共享屏幕列表
|
||||
getSources: () => {
|
||||
return ipcRenderer.invoke('getSources')
|
||||
|
|
@ -38,8 +42,8 @@ window.electron = {
|
|||
ipcRenderer.on('onQuit', callback)
|
||||
},
|
||||
// 退出房间
|
||||
quit: () => {
|
||||
return ipcRenderer.invoke('quit')
|
||||
quit: (bool) => {
|
||||
return ipcRenderer.invoke('quit', bool)
|
||||
},
|
||||
// 监听更新
|
||||
onUpdate: (callback) => {
|
||||
|
|
@ -49,6 +53,10 @@ window.electron = {
|
|||
quitAndInstall: (callback) => {
|
||||
ipcRenderer.on('quitAndInstall', callback)
|
||||
},
|
||||
// 点击任务栏图标是否打开窗口
|
||||
isOpenWindows: (callback) => {
|
||||
ipcRenderer.on('isOpenWindows', callback)
|
||||
},
|
||||
// 通知下载最新的包
|
||||
onDownload: (type) => {
|
||||
ipcRenderer.invoke('updateDownload', type)
|
||||
|
|
@ -77,4 +85,32 @@ window.electron = {
|
|||
createChildWindow: (config) => {
|
||||
ipcRenderer.invoke('createChildWindow', config)
|
||||
},
|
||||
// 关闭子窗口
|
||||
closeChildWindow: (key) => {
|
||||
ipcRenderer.invoke('closeChildWindow', key)
|
||||
},
|
||||
// 设置子窗口
|
||||
setChildWindow: (config) => {
|
||||
ipcRenderer.invoke('setChildWindow', config)
|
||||
},
|
||||
// 隐藏主窗口
|
||||
setChildWindowShow: (config) => {
|
||||
ipcRenderer.invoke('setChildWindowShow', config)
|
||||
},
|
||||
// 隐藏主窗口
|
||||
mainWindowHide: () => {
|
||||
ipcRenderer.invoke('mainWindowHide')
|
||||
},
|
||||
// 居中主窗口
|
||||
mainWindowCenter: () => {
|
||||
ipcRenderer.invoke('mainWindowCenter')
|
||||
},
|
||||
// 窗口通信传参
|
||||
windowHandleMessage: (data) => {
|
||||
ipcRenderer.invoke('windowHandleMessage', data)
|
||||
},
|
||||
// 窗口通信回调
|
||||
windowHandleMessageCallBack: (callback) => {
|
||||
ipcRenderer.on('windowHandleMessageCallBack', callback)
|
||||
},
|
||||
}
|
||||
|
|
|
|||
55
src/App.tsx
55
src/App.tsx
|
|
@ -18,7 +18,13 @@ 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";
|
||||
import ChatSmallWindow from "@/page/Meeting/ChatSmallWindow";
|
||||
import ChatBigWindow from "@/page/Meeting/ChatBigWindow";
|
||||
import CurrentSpeakUserWindow from "@/page/Meeting/CurrentSpeakUserWindow";
|
||||
import NoticeWindow from "@/page/Meeting/NoticeWindow";
|
||||
import { getKeyOpenChildWindow, setKeyOpenChildWindow } from "./utils/package/public";
|
||||
const fs = require('fs').promises;
|
||||
const { exec } = require('child_process');
|
||||
const App: React.FC = () => {
|
||||
|
|
@ -33,7 +39,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', '#/chatSmallWindow', '#/chatBigWindow', '#/currentSpeakUserWindow', '#/noticeWindow']
|
||||
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)
|
||||
|
|
@ -97,7 +104,22 @@ const App: React.FC = () => {
|
|||
storage.setItem('setting', JSON.stringify(setting))
|
||||
})
|
||||
window.electron.quitAndInstall(async (_e: any) => {
|
||||
leaveChannel()
|
||||
let bool = await window.electron.isVisible()
|
||||
if (bool) {
|
||||
storage.setItem('quitMeeting', true)
|
||||
window.electron.setViewStatus('show')
|
||||
}
|
||||
})
|
||||
window.electron.isOpenWindows(async (_e: any) => {
|
||||
let bool = await window.electron.isVisible()
|
||||
if (location.hash.indexOf('/meeting') === -1) {
|
||||
window.electron.setViewStatus(bool ? 'hide' : 'show')
|
||||
} else {
|
||||
let shareScreenWindow = await getKeyOpenChildWindow('shareScreenWindow')
|
||||
if (!shareScreenWindow) {
|
||||
window.electron.setViewStatus(bool ? 'hide' : 'show')
|
||||
}
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
|
|
@ -120,22 +142,26 @@ const App: React.FC = () => {
|
|||
closeSetting: 'hide', //关闭按钮设置
|
||||
isAINoiseReduction: true, //是否开启ai降噪
|
||||
aINoiseReduction: 1, // 降噪模式
|
||||
isRecordingTips: true, //是否开启录制提示
|
||||
}))
|
||||
}
|
||||
if (!storage.getItem('openChildWindow')) {
|
||||
storage.setItem('openChildWindow', JSON.stringify({}))
|
||||
}
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
if (isState) {
|
||||
setIsState(false)
|
||||
window.electron.onQuit(async () => {
|
||||
if (location.hash.indexOf('/login') === 1) {
|
||||
window.electron.quit()
|
||||
window.electron.quit(location.hash.indexOf('/meeting') === -1)
|
||||
} else {
|
||||
if (storage.getItem('isTips') === 'true') {
|
||||
const setting = JSON.parse(storage.getItem('setting') as string)
|
||||
if (setting.closeSetting === 'hide') {
|
||||
window.electron.setViewStatus(setting.closeSetting)
|
||||
} else {
|
||||
window.electron.quit()
|
||||
window.electron.quit(location.hash.indexOf('/meeting') === -1)
|
||||
}
|
||||
} else {
|
||||
quitTipsRef.current.changeModal()
|
||||
|
|
@ -208,6 +234,20 @@ const App: React.FC = () => {
|
|||
};
|
||||
const leaveChannel = async (bool?: boolean): Promise<void> => {
|
||||
if (location.hash.indexOf('/meeting') === 1) {
|
||||
window.electron.closeChildWindow('shareScreenWindow')
|
||||
setKeyOpenChildWindow('shareScreenWindow', false)
|
||||
window.electron.setViewStatus('show')
|
||||
window.electron.getWindowSize().then((res: any) => {
|
||||
window.electron.setMainWindowSize({
|
||||
width: Math.ceil(res.width / 1.5),
|
||||
height: Math.ceil(res.height / 1.3),
|
||||
})
|
||||
window.electron.getIsMaximized().then((b: boolean) => {
|
||||
if (!b) {
|
||||
window.electron.setViewStatus('maximize')
|
||||
}
|
||||
})
|
||||
})
|
||||
const data = JSON.parse(localStorage.stateInfo);
|
||||
if (!bool) {
|
||||
await GetLeave({
|
||||
|
|
@ -243,6 +283,11 @@ 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='/chatSmallWindow' element={<ChatSmallWindow />} />
|
||||
<Route path='/chatBigWindow' element={<ChatBigWindow />} />
|
||||
<Route path='/currentSpeakUserWindow' element={<CurrentSpeakUserWindow />} />
|
||||
<Route path='/noticeWindow' element={<NoticeWindow />} />
|
||||
<Route path='*' element={<NotFound />} />
|
||||
</Routes>
|
||||
<Spin spinning={spinning} fullscreen />
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 430 B |
Binary file not shown.
|
After Width: | Height: | Size: 349 B |
|
|
@ -1,15 +1,21 @@
|
|||
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";
|
||||
const EquipmentManagement = forwardRef((_props: any, ref: any) => {
|
||||
const EquipmentManagement = forwardRef((props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
changeModal: async (uid: string, userName: string) => {
|
||||
setCallerUid(uid)
|
||||
setDeviceInfo({})
|
||||
await onInvoke('getDrivers', {
|
||||
uid
|
||||
})
|
||||
let isOpen = await getKeyOpenChildWindow('shareScreenWindow')
|
||||
if (isOpen) {
|
||||
props.getDriver?.(uid)
|
||||
} else {
|
||||
await onInvoke('getDrivers', {
|
||||
uid
|
||||
})
|
||||
}
|
||||
setUserName(userName)
|
||||
setEquipmentManagementModal(true)
|
||||
},
|
||||
|
|
@ -21,6 +27,9 @@ 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> => {
|
||||
setEquipmentManagementModal(false)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
|
|
@ -30,7 +39,7 @@ const EquipmentManagement = forwardRef((_props: any, ref: any) => {
|
|||
centered
|
||||
width={'500px'}
|
||||
onCancel={() => {
|
||||
setEquipmentManagementModal(false)
|
||||
handleWindowsChange()
|
||||
}}>
|
||||
<div className={styles.equipmentManagement}>
|
||||
<div>
|
||||
|
|
@ -83,16 +92,24 @@ const EquipmentManagement = forwardRef((_props: any, ref: any) => {
|
|||
</div>
|
||||
<div>
|
||||
<Button type="primary" className='m-ant-btn' onClick={async () => {
|
||||
await onInvoke('setDrivers', {
|
||||
uid: callerUid,
|
||||
driversJsonString: JSON.stringify(deviceInfo)
|
||||
})
|
||||
setEquipmentManagementModal(false)
|
||||
let isOpen = await getKeyOpenChildWindow('shareScreenWindow')
|
||||
if (isOpen) {
|
||||
props.setDriver?.({
|
||||
uid: callerUid,
|
||||
driversJsonString: JSON.stringify(deviceInfo)
|
||||
})
|
||||
} else {
|
||||
await onInvoke('setDrivers', {
|
||||
uid: callerUid,
|
||||
driversJsonString: JSON.stringify(deviceInfo)
|
||||
})
|
||||
}
|
||||
handleWindowsChange()
|
||||
message.success('设置成功')
|
||||
}}>确定</Button>
|
||||
<Button type="primary"
|
||||
style={{ backgroundColor: '#31353A', marginLeft: '10px' }}
|
||||
onClick={() => setEquipmentManagementModal(false)}>取消</Button>
|
||||
onClick={() => handleWindowsChange()}>取消</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal >
|
||||
|
|
|
|||
|
|
@ -12,20 +12,26 @@ import { role } from '@/config/role';
|
|||
let time = null as any;
|
||||
const JoinSetting = forwardRef((_props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
changeModal: (roomNum: string = '') => {
|
||||
changeModal: async (roomNum: string = '') => {
|
||||
let userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
setUser(userInfo)
|
||||
setJoinRoomSettingModal(true)
|
||||
if (location.hash.indexOf('/meeting') === -1) {
|
||||
await agora.init()
|
||||
}
|
||||
setJoinRoomSettingForm((res: any) => {
|
||||
res.forEach((item: any) => {
|
||||
item.active = false
|
||||
res.forEach(async (item: any, index: number) => {
|
||||
if (index === 0 && role.ID.includes(userInfo.roleId)) {
|
||||
await agora.getAudioMediaList().then(res => {
|
||||
item.active = res.ecordingList.length ? true : false
|
||||
})
|
||||
} else {
|
||||
item.active = false
|
||||
}
|
||||
});
|
||||
return res
|
||||
})
|
||||
setRoomNumber(roomNum)
|
||||
if (location.hash.indexOf('/meeting') === -1) {
|
||||
agora.init()
|
||||
}
|
||||
getDeviceList()
|
||||
}
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import styles from '@/components/Operation/index.module.scss'
|
||||
import ImageUrl from '@/utils/package/imageUrl';
|
||||
import { useEffect, useState } from "react";
|
||||
type OperationKeyType = 'minimize' | 'quit' | 'maximize' | 'unmaximize' | 'hide';
|
||||
type OperationKeyType = 'minimize' | 'quit' | 'maximize' | 'unmaximize' | 'hide' | 'show';
|
||||
type OperationType = {
|
||||
icon: string;
|
||||
key: OperationKeyType;
|
||||
|
|
@ -46,7 +46,11 @@ const Operation: React.FC = () => {
|
|||
key: 'quit',
|
||||
title: '关闭',
|
||||
onClick: (key: OperationKeyType) => {
|
||||
window.electron.setViewStatus(key)
|
||||
if (location.hash.indexOf('/meeting') === -1) {
|
||||
window.electron.setViewStatus(key)
|
||||
} else {
|
||||
window.electron.quit(false)
|
||||
}
|
||||
},
|
||||
show: true,
|
||||
},])
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { storage } from '@/utils';
|
|||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { Button, Checkbox, Modal, Radio } from 'antd';
|
||||
import { useState, useImperativeHandle, forwardRef } from "react";
|
||||
type OperationKeyType = 'minimize' | 'quit' | 'maximize' | 'unmaximize' | 'hide';
|
||||
type OperationKeyType = 'minimize' | 'quit' | 'maximize' | 'unmaximize' | 'hide' | 'show';
|
||||
const QuitTips = forwardRef((props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
changeModal: () => {
|
||||
|
|
@ -52,7 +52,7 @@ const QuitTips = forwardRef((props: any, ref: any) => {
|
|||
<Button type="primary" className='m-ant-btn' onClick={() => {
|
||||
setIsCloseModal(false)
|
||||
if (optionsValue === 'quit') {
|
||||
window.electron.quit()
|
||||
window.electron.quit(location.hash.indexOf('/meeting') === -1)
|
||||
} else {
|
||||
window.electron.setViewStatus(optionsValue)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,19 +176,23 @@
|
|||
}
|
||||
|
||||
.recordingComponents {
|
||||
>span {
|
||||
color: #bfbfbf;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
>div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
>span {
|
||||
color: #878787;
|
||||
white-space: nowrap;
|
||||
color: #bfbfbf;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
>div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
|
||||
>span {
|
||||
color: #878787;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ const StupWizard = forwardRef((props: any, ref: any) => {
|
|||
top: '16px',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
onClick={() => {
|
||||
onClick={async () => {
|
||||
if (location.hash.indexOf('/meeting') === -1) {
|
||||
agora.release()
|
||||
}
|
||||
|
|
@ -459,6 +459,7 @@ const AudioComponents = () => {
|
|||
}
|
||||
const RecordingComponents = () => {
|
||||
const [filePath, setFilePath] = useState('')
|
||||
const [isRecordingTips, setIsRecordingTips] = useState(false)
|
||||
const setting = JSON.parse(storage.getItem('setting') as string)
|
||||
useEffect(() => {
|
||||
if (!setting.recordingFilesPath) {
|
||||
|
|
@ -467,10 +468,14 @@ const RecordingComponents = () => {
|
|||
const parentDirectory = path.resolve(currentDirectory, '../../Downloads') + '\\';
|
||||
setting.recordingFilesPath = parentDirectory;
|
||||
setFilePath(setting.recordingFilesPath)
|
||||
storage.setItem('setting', JSON.stringify(setting))
|
||||
} else {
|
||||
setFilePath(setting.recordingFilesPath);
|
||||
}
|
||||
if (setting.isRecordingTips === undefined) {
|
||||
setting.isRecordingTips = true;
|
||||
}
|
||||
storage.setItem('setting', JSON.stringify(setting))
|
||||
setIsRecordingTips(setting.isRecordingTips)
|
||||
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
||||
return () => {
|
||||
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
||||
|
|
@ -487,41 +492,53 @@ const RecordingComponents = () => {
|
|||
<div>
|
||||
<span>录制</span>
|
||||
<div className={styles.recordingComponents}>
|
||||
<span>本地录制</span>
|
||||
<div>
|
||||
<span>本地录制文件路径</span>
|
||||
<Input
|
||||
disabled={true}
|
||||
placeholder="请填入文件路径"
|
||||
style={{ margin: '0 14px', flexGrow: 1 }}
|
||||
value={filePath}
|
||||
onChange={async (e) => {
|
||||
setting.recordingFilesPath = e.target.value;
|
||||
<span>本地录制</span>
|
||||
<div>
|
||||
<span>本地录制文件路径</span>
|
||||
<Input
|
||||
disabled={true}
|
||||
placeholder="请填入文件路径"
|
||||
style={{ margin: '0 14px', flexGrow: 1 }}
|
||||
value={filePath}
|
||||
onChange={async (e) => {
|
||||
setting.recordingFilesPath = e.target.value;
|
||||
storage.setItem('setting', JSON.stringify(setting))
|
||||
setFilePath(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<Button type="primary" onClick={() => {
|
||||
window.electron.selectFilePath({
|
||||
key: 'recordingFilesPath',
|
||||
})
|
||||
}} style={{ backgroundColor: '#31353A', marginRight: '10px' }}>选择保存目录</Button>
|
||||
<Button type="primary" onClick={async () => {
|
||||
try {
|
||||
await fs.access(filePath, fs.constants.F_OK);
|
||||
if (process.platform === 'win32') {
|
||||
exec(`explorer "${filePath}"`);
|
||||
} else if (process.platform === 'darwin') {
|
||||
exec(`open "${filePath}"`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.code === 'ENOENT') {
|
||||
message.error('文件夹不存在!')
|
||||
} else {
|
||||
message.error(error)
|
||||
}
|
||||
}
|
||||
}} style={{ backgroundColor: '#31353A' }}>打开</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>录制设置</span>
|
||||
<div>
|
||||
<Checkbox onChange={async (e) => {
|
||||
setting.isRecordingTips = e.target.checked;
|
||||
storage.setItem('setting', JSON.stringify(setting))
|
||||
setFilePath(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<Button type="primary" onClick={() => {
|
||||
window.electron.selectFilePath({
|
||||
key: 'recordingFilesPath',
|
||||
})
|
||||
}} style={{ backgroundColor: '#31353A', marginRight: '10px' }}>选择保存目录</Button>
|
||||
<Button type="primary" onClick={async () => {
|
||||
try {
|
||||
await fs.access(filePath, fs.constants.F_OK);
|
||||
if (process.platform === 'win32') {
|
||||
exec(`explorer "${filePath}"`);
|
||||
} else if (process.platform === 'darwin') {
|
||||
exec(`open "${filePath}"`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.code === 'ENOENT') {
|
||||
message.error('文件夹不存在!')
|
||||
} else {
|
||||
message.error(error)
|
||||
}
|
||||
}
|
||||
}} style={{ backgroundColor: '#31353A' }}>打开</Button>
|
||||
setIsRecordingTips(e.target.checked)
|
||||
}} checked={isRecordingTips}>开启入会录制提示</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ const UserVideo: React.FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
userList.forEach(async (item: any) => {
|
||||
await agora.destroyRendererByConfig(Number('1' + item.screenShareId))
|
||||
await agora.destroyRendererByConfig(Number('1' + item.screenShareId), state.channelId + 'a')
|
||||
await agora.setupRemoteVideoEx({
|
||||
uid: Number('1' + item.screenShareId),
|
||||
view: document.getElementById(`video-${item.screenShareId}`),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
.chatBigWindow {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
padding: 10px 0;
|
||||
|
||||
.chatBigWindowTitle {
|
||||
background-color: #16191E;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
padding: 10px 10px;
|
||||
|
||||
>span {
|
||||
color: #EEEEEE;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
>img {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.chatBigWindowContent {
|
||||
flex-grow: 1;
|
||||
height: 0px;
|
||||
overflow-y: auto;
|
||||
background-color: rgb(31, 33, 37);
|
||||
padding: 10px;
|
||||
|
||||
.chatBigWindowContentLeft {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
>div:nth-child(1) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
>span {
|
||||
font-size: 14px;
|
||||
color: #F3F3F5;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
>div {}
|
||||
}
|
||||
|
||||
>div:nth-child(2) {
|
||||
background-color: #5575F2;
|
||||
color: #F3F3F5;
|
||||
max-width: 266px;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 0 25px 25px 25px;
|
||||
margin: 10px 0 10px 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.chatBigWindowContentRight {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
|
||||
>div:nth-child(1) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
>span {
|
||||
font-size: 14px;
|
||||
color: #F3F3F5;
|
||||
}
|
||||
|
||||
>div {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
>div:nth-child(2) {
|
||||
background-color: #464E6B;
|
||||
color: #F3F3F5;
|
||||
max-width: 266px;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 25px 0 25px 25px;
|
||||
margin: 10px 40px 10px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chatBigWindowButton {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
background-color: rgb(31, 33, 37);
|
||||
|
||||
>button {
|
||||
margin: 4px 4px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.chatBigWindowInput {
|
||||
flex-shrink: 0;
|
||||
padding: 10px 10px;
|
||||
background-color: #16191E;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.chatBigWindowInputPopover {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
>button {
|
||||
margin-bottom: 4px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
import styles from '@/page/Meeting/ChatBigWindow/index.module.scss'
|
||||
import ImageUrl from '@/utils/package/imageUrl';
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { storage } from '@/utils';
|
||||
import { Button, Input, Modal, Popover } from 'antd';
|
||||
import { role } from '@/config/role';
|
||||
import { GetRoomUserItem } from '@/api/Meeting';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import dayjs from 'dayjs';
|
||||
import { ExclamationCircleFilled } from '@ant-design/icons';
|
||||
import EquipmentManagement from '@/components/EquipmentManagement';
|
||||
const { confirm } = Modal;
|
||||
const ChatBigWindow: React.FC = () => {
|
||||
const [inputValue, setInputValue] = useState<string>('')
|
||||
const [chatLists, setChatLists] = useState<any>([])
|
||||
const [user, setUser] = useState<any>({});
|
||||
const [roomUserItem, setRoomUserItem] = useState<any>(null)
|
||||
const [commonlyChatList] = useState<any>([
|
||||
'能听到我说话吗?',
|
||||
'听得到',
|
||||
'听不到',
|
||||
'我要发言',
|
||||
])
|
||||
const equipmentManagementRef = useRef<any>();
|
||||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
const stateInfo = JSON.parse(storage.getItem('stateInfo') as string)
|
||||
const channel = new BroadcastChannel('meeting_channel');
|
||||
useEffect(() => {
|
||||
setUser(userInfo)
|
||||
channel.onmessage = function (event) {
|
||||
const { type, showDriverList } = event.data;
|
||||
switch (type) {
|
||||
case 'showDriverList':
|
||||
equipmentManagementRef.current.setData(showDriverList)
|
||||
break;
|
||||
}
|
||||
}
|
||||
window.electron.windowHandleMessageCallBack((_e: any, data: any) => {
|
||||
setChatLists(data.parmes.chatList)
|
||||
})
|
||||
channel.postMessage({
|
||||
type: 'chatBigWindowSendChannelMsg',
|
||||
chatBigWindowSendChannelMsg: {
|
||||
msg: '',
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
channel.close();
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const chatBigWindowView = document.getElementById('chatBigWindowView') as HTMLElement;
|
||||
if (chatBigWindowView) {
|
||||
chatBigWindowView.scrollTop = chatBigWindowView.scrollHeight;
|
||||
}
|
||||
}, [chatLists]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`${styles.chatBigWindow}`}>
|
||||
<div className={styles.chatBigWindowTitle}>
|
||||
<span>聊天</span>
|
||||
<img src={ImageUrl.icon18} className='drag' alt="" onClick={() => {
|
||||
window.electron.setChildWindowShow({
|
||||
key: 'chatBigWindow',
|
||||
})
|
||||
}} />
|
||||
</div>
|
||||
<div className={`${styles.chatBigWindowContent} drag`} id='chatBigWindowView'>
|
||||
{chatLists.map((item: any, index: number) =>
|
||||
<div
|
||||
key={index}
|
||||
className={`${item.uid !== user.uid ? styles.chatBigWindowContentLeft : styles.chatBigWindowContentRight}`}>
|
||||
{role.ID.includes(user.roleId) ? <Popover
|
||||
placement="bottom"
|
||||
title={''}
|
||||
onOpenChange={(e: boolean) => {
|
||||
if (e) {
|
||||
GetRoomUserItem(stateInfo.channelId, item.uid).then((res: any) => {
|
||||
if (res.code === 200) {
|
||||
setRoomUserItem(res.data)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
setRoomUserItem(null)
|
||||
}
|
||||
}}
|
||||
content={
|
||||
roomUserItem ? <div className={styles.chatBigWindowInputPopover}>
|
||||
{roomUserItem.isRoomManager || role.ID.includes(roomUserItem.roleId) ? <Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
size={'small'}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
channel.postMessage({
|
||||
type: 'chatBigWindowSetAllUserLook',
|
||||
chatBigWindowSetAllUserLook: {
|
||||
roomUserItem,
|
||||
}
|
||||
});
|
||||
}}
|
||||
>全员看Ta</Button> : null}
|
||||
{roomUserItem.uid !== user.uid && !role.ID.includes(roomUserItem.roleId) ? <Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
size={'small'}
|
||||
onClick={async (event) => {
|
||||
event.stopPropagation();
|
||||
if (roomUserItem.isRoomManager) {
|
||||
channel.postMessage({
|
||||
type: 'chatBigWindowDeleteRoomManager',
|
||||
chatBigWindowDeleteRoomManager: {
|
||||
uid: roomUserItem.uid
|
||||
}
|
||||
});
|
||||
} else {
|
||||
channel.postMessage({
|
||||
type: 'chatBigWindowPostRoomManager',
|
||||
chatBigWindowPostRoomManager: {
|
||||
uid: roomUserItem.uid
|
||||
}
|
||||
});
|
||||
}
|
||||
await GetRoomUserItem(stateInfo.channelId, item.uid).then((res: any) => {
|
||||
if (res.code === 200) {
|
||||
setRoomUserItem(res.data)
|
||||
}
|
||||
})
|
||||
}}
|
||||
>{roomUserItem.isRoomManager ? '取消发言' : '允许发言'}</Button> : null}
|
||||
{roomUserItem.isRoomManager ? <Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
size={'small'}
|
||||
onClick={async (event) => {
|
||||
event.stopPropagation();
|
||||
channel.postMessage({
|
||||
type: 'chatBigWindowPostOpenMicr',
|
||||
chatBigWindowPostOpenMicr: {
|
||||
uid: roomUserItem.uid,
|
||||
enableMicr: !roomUserItem.enableMicr
|
||||
}
|
||||
});
|
||||
await GetRoomUserItem(stateInfo.channelId, item.uid).then((res: any) => {
|
||||
if (res.code === 200) {
|
||||
setRoomUserItem(res.data)
|
||||
}
|
||||
})
|
||||
}}
|
||||
>{roomUserItem.enableMicr ? '静音' : '解除静音'}</Button> : null}
|
||||
{roomUserItem.isRoomManager ? <Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
size={'small'}
|
||||
onClick={async (event) => {
|
||||
event.stopPropagation();
|
||||
channel.postMessage({
|
||||
type: 'chatBigWindowPostOpenCamera',
|
||||
chatBigWindowPostOpenCamera: {
|
||||
uid: roomUserItem.uid,
|
||||
enableMicr: !roomUserItem.enableCamera
|
||||
}
|
||||
});
|
||||
await GetRoomUserItem(stateInfo.channelId, item.uid).then((res: any) => {
|
||||
if (res.code === 200) {
|
||||
setRoomUserItem(res.data)
|
||||
}
|
||||
})
|
||||
}}
|
||||
>{roomUserItem.enableCamera ? '关闭视频' : '打开视频'}</Button> : null}
|
||||
{roomUserItem.uid !== user.uid ? <Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
size={'small'}
|
||||
onClick={() => {
|
||||
equipmentManagementRef.current.changeModal(item.uid, item.userName)
|
||||
}}
|
||||
>设备管理</Button> : null}
|
||||
{roomUserItem.uid !== user.uid ? <Button
|
||||
type="primary"
|
||||
style={{ backgroundColor: '#EC3C3C' }}
|
||||
size={'small'}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
confirm({
|
||||
title: '移出会议',
|
||||
icon: <ExclamationCircleFilled />,
|
||||
content: `确定将用户${item.userName}移出会议?`,
|
||||
centered: true,
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
async onOk() {
|
||||
channel.postMessage({
|
||||
type: 'chatBigWindowGetRoomKickout',
|
||||
chatBigWindowGetRoomKickout: {
|
||||
uid: item.uid
|
||||
}
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
|
||||
},
|
||||
});
|
||||
}}
|
||||
>移出会议</Button> : null}
|
||||
</div> : <div style={{ color: 'white' }}>用户不在房间内</div>
|
||||
}>
|
||||
<div>
|
||||
<div><Avatar name={item.userName} /></div>
|
||||
{item.uid !== user.uid ?
|
||||
<span>{item.userName} <span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span></span> :
|
||||
<span> <span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')} </span>{item.userName}</span>
|
||||
}
|
||||
|
||||
</div>
|
||||
</Popover> : <div>
|
||||
<div><Avatar name={item.userName} /></div>
|
||||
{item.uid !== user.uid ?
|
||||
<span>{item.userName}<span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span></span> :
|
||||
<span><span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')} </span>{item.userName}</span>
|
||||
}
|
||||
</div>}
|
||||
<div>{item.message}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={`${styles.chatBigWindowButton} drag`}>
|
||||
{
|
||||
commonlyChatList.map((item: string, index: number) => {
|
||||
return <Button
|
||||
key={index}
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
onClick={() => {
|
||||
channel.postMessage({
|
||||
type: 'chatBigWindowSendChannelMsg',
|
||||
chatBigWindowSendChannelMsg: {
|
||||
msg: item,
|
||||
}
|
||||
});
|
||||
}}
|
||||
>{item}</Button>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<div className={`${styles.chatBigWindowInput} drag`}>
|
||||
<Input.TextArea
|
||||
placeholder="请输入内容"
|
||||
style={{ flexGrow: 1 }}
|
||||
value={inputValue}
|
||||
onChange={(e) => {
|
||||
setInputValue(e.target.value)
|
||||
}}
|
||||
autoSize={{ minRows: 3, maxRows: 3 }} />
|
||||
<Button type="primary" className='m-ant-btn' style={{ flexShrink: 0, marginTop: '4px' }} onClick={() => {
|
||||
channel.postMessage({
|
||||
type: 'chatBigWindowSendChannelMsg',
|
||||
chatBigWindowSendChannelMsg: {
|
||||
msg: inputValue,
|
||||
}
|
||||
});
|
||||
setInputValue('')
|
||||
}}>发送</Button>
|
||||
</div>
|
||||
</div>
|
||||
<EquipmentManagement ref={equipmentManagementRef} getDriver={(uid: string) => {
|
||||
channel.postMessage({
|
||||
type: 'userListWindowEquipmentManagement',
|
||||
userListWindowEquipmentManagement: {
|
||||
uid
|
||||
}
|
||||
});
|
||||
}} setDriver={(data: any) => {
|
||||
channel.postMessage({
|
||||
type: 'userListWindowSetEquipmentManagement',
|
||||
userListWindowSetEquipmentManagement: data
|
||||
});
|
||||
}} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ChatBigWindow
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
.chatSmallWindow {
|
||||
color: white;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
>div:nth-child(1) {
|
||||
flex-grow: 1;
|
||||
overflow-y: hidden;
|
||||
max-height: 80%;
|
||||
padding: 0 4px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
background-color: rgba(40, 40, 44, .4);
|
||||
flex-direction: column-reverse;
|
||||
|
||||
.chatSmallWindowContentLeft {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
>div {
|
||||
background-color: #1e232baf;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 0 15px 15px 15px;
|
||||
margin: 0 0 4px 0;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
|
||||
>span:nth-child(1) {
|
||||
white-space: nowrap;
|
||||
color: #ff970f;
|
||||
}
|
||||
|
||||
>span:nth-child(2) {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chatSmallWindowContentRight {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
|
||||
>div {
|
||||
background-color: #464e6b55;
|
||||
color: black;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 15px 0 15px 15px;
|
||||
margin: 0 0 4px 0;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
>span:nth-child(1) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
>div:nth-child(2) {
|
||||
flex-shrink: 0;
|
||||
opacity: 0.4;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
>div:nth-child(3) {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 0px;
|
||||
transform: translate(-50%, 0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
background-color: rgba(40, 40, 44, .2);
|
||||
padding: 0 6px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(40, 40, 44, 1);
|
||||
}
|
||||
|
||||
>span {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
import styles from '@/page/Meeting/ChatSmallWindow/index.module.scss'
|
||||
import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons';
|
||||
import { Input } from 'antd';
|
||||
import { useEffect, useState } from "react";
|
||||
const ChatSmallWindow: React.FC = () => {
|
||||
const [inputValue, setInputValue] = useState<string>('')
|
||||
const [chatLists, setChatLists] = useState<any>([])
|
||||
const [isExpand, setIsExpand] = useState(false)
|
||||
const channel = new BroadcastChannel('meeting_channel');
|
||||
useEffect(() => {
|
||||
window.electron.windowHandleMessageCallBack((_e: any, data: any) => {
|
||||
setChatLists((newChatList: any) => {
|
||||
data.parmes.chatListIten.timer = setTimeout(() => {
|
||||
removeItemByIndex();
|
||||
}, 10000);
|
||||
return [data.parmes.chatListIten, ...newChatList]
|
||||
})
|
||||
})
|
||||
return () => {
|
||||
channel.close();
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const chatSmallWindowView = document.getElementById('chatSmallWindowView') as HTMLElement;
|
||||
if (chatSmallWindowView) {
|
||||
chatSmallWindowView.scrollTop = chatSmallWindowView.scrollHeight;
|
||||
}
|
||||
}, [chatLists]);
|
||||
const removeItemByIndex = () => {
|
||||
setChatLists((res: any) => {
|
||||
return res.slice(0, -1)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.chatSmallWindow}>
|
||||
<div id='chatSmallWindowView'>
|
||||
{chatLists.map((item: any, index: number) =>
|
||||
<div className={`${styles.chatSmallWindowContentLeft}`} key={index}>
|
||||
<div>
|
||||
<span>{item.userName}:</span>
|
||||
<span>{item.message}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='drag'>
|
||||
<Input
|
||||
placeholder="请输入内容"
|
||||
style={{ flexGrow: 1 }}
|
||||
value={inputValue}
|
||||
onChange={(e) => {
|
||||
setInputValue(e.target.value)
|
||||
}}
|
||||
onPressEnter={() => {
|
||||
if (inputValue) {
|
||||
channel.postMessage({
|
||||
type: 'chatSmallWindowSendChannelMsg',
|
||||
chatSmallWindowSendChannelMsg: {
|
||||
msg: inputValue,
|
||||
}
|
||||
});
|
||||
setInputValue('')
|
||||
}
|
||||
}}
|
||||
suffix={
|
||||
<span
|
||||
style={{ color: '#47D3D0', cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
if (inputValue) {
|
||||
channel.postMessage({
|
||||
type: 'chatSmallWindowSendChannelMsg',
|
||||
chatSmallWindowSendChannelMsg: {
|
||||
msg: inputValue,
|
||||
}
|
||||
});
|
||||
setInputValue('')
|
||||
}
|
||||
}}
|
||||
>发送
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={`drag`} onClick={() => {
|
||||
setIsExpand(!isExpand)
|
||||
window.electron.setChildWindow({
|
||||
height: isExpand ? 150 : 150 / 2,
|
||||
key: 'chatSmallWindow',
|
||||
})
|
||||
}}>
|
||||
<span>{isExpand ? '展开' : '收起'}</span>
|
||||
{isExpand ? <CaretDownOutlined /> : <CaretUpOutlined />}
|
||||
</div>
|
||||
</div >
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ChatSmallWindow
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
.currentSpeakUserWindow {
|
||||
color: white;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: #16191E;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
>div {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
width: fit-content;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import styles from '@/page/Meeting/CurrentSpeakUserWindow/index.module.scss'
|
||||
import { useEffect, useState } from "react";
|
||||
const CurrentSpeakUserWindow: React.FC = () => {
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const channel = new BroadcastChannel('meeting_channel');
|
||||
useEffect(() => {
|
||||
let time: NodeJS.Timeout;
|
||||
time = setInterval(() => {
|
||||
channel.postMessage({
|
||||
type: 'currentSpeakUserWindowGetUserName'
|
||||
})
|
||||
}, 1000)
|
||||
window.electron.windowHandleMessageCallBack((_e: any, data: any) => {
|
||||
if (data.parmes.currentSpeakUser.length) {
|
||||
setInputValue(data.parmes.currentSpeakUser.join(';'))
|
||||
} else {
|
||||
setInputValue('')
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
clearInterval(time)
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.currentSpeakUserWindow}>
|
||||
<div title={`正在说话: ${inputValue}`}>
|
||||
{`正在说话: ${inputValue}`}
|
||||
</div>
|
||||
</div >
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default CurrentSpeakUserWindow
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.noticeWindow {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
import styles from '@/page/Meeting/NoticeWindow/index.module.scss'
|
||||
import { Button, notification } from 'antd';
|
||||
import { useEffect } from "react";
|
||||
|
||||
const NoticeWindow: React.FC = () => {
|
||||
const [api, contextHolder] = notification.useNotification({
|
||||
stack: {
|
||||
threshold: 3
|
||||
}
|
||||
});
|
||||
const channel = new BroadcastChannel('meeting_channel');
|
||||
useEffect(() => {
|
||||
window.electron.setChildWindowShow({
|
||||
key: 'noticeWindow',
|
||||
bool: false
|
||||
})
|
||||
channel.onmessage = function (event) {
|
||||
const { type, noticeItem } = event.data;
|
||||
switch (type) {
|
||||
case 'noticeItem':
|
||||
api.open({
|
||||
message: '',
|
||||
description: <div>
|
||||
<span style={{ fontSize: '16px' }}>{noticeItem.uname}申请发言</span>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
onClick={async (e: any) => {
|
||||
let i = e.nativeEvent.path;
|
||||
if (i) {
|
||||
i.forEach((i: any) => {
|
||||
if (i.className === 'ant-notification-notice ant-notification-notice-closable') {
|
||||
i.childNodes.forEach((row: any) => {
|
||||
if (row.className === 'ant-notification-notice-close') {
|
||||
row.click()
|
||||
channel.postMessage({
|
||||
type: 'noticeWindowPostRoomManager',
|
||||
noticeWindowPostRoomManager: {
|
||||
uid: noticeItem.uid
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}}
|
||||
>接受</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={(e: any) => {
|
||||
let item = e.nativeEvent.path;
|
||||
if (item) {
|
||||
item.forEach((item: any) => {
|
||||
if (item.className === 'ant-notification-notice ant-notification-notice-closable') {
|
||||
item.childNodes.forEach((row: any) => {
|
||||
if (row.className === 'ant-notification-notice-close') {
|
||||
row.click()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}}
|
||||
style={{ backgroundColor: '#EC3C3C', marginLeft: '14px' }}
|
||||
>拒绝</Button>
|
||||
</div>
|
||||
</div>,
|
||||
onClose: () => {
|
||||
const dom = document.getElementsByClassName('ant-notification')
|
||||
if (dom.length === 0) {
|
||||
window.electron.setChildWindowShow({
|
||||
key: 'noticeWindow',
|
||||
bool: false
|
||||
})
|
||||
}
|
||||
},
|
||||
duration: 10,
|
||||
placement: 'bottomRight',
|
||||
showProgress: true,
|
||||
pauseOnHover: false,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
channel.close();
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<div className={`${styles.noticeWindow} drag`}>
|
||||
{contextHolder}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NoticeWindow
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
.shareScreenWindow {
|
||||
color: white;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.shareScreenWindowTitle {
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
background-color: lighten(#07090B, 4%);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
>span:nth-child(2) {
|
||||
cursor: pointer;
|
||||
background-color: #FF5219;
|
||||
padding: 1px 10px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.shareScreenWindowContent {
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #07090B;
|
||||
display: flex;
|
||||
|
||||
.shareScreenWindowContentList {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: space-between;
|
||||
|
||||
>div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
width: calc(100% / 6);
|
||||
|
||||
>div {
|
||||
position: relative;
|
||||
|
||||
>img {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
>div {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-position: bottom center;
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
height: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
>span {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shareScreenWindowExpand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
background-color: lighten(#07090B, 4%);
|
||||
padding: 0 6px;
|
||||
cursor: pointer;
|
||||
|
||||
>span {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
import { GetRoomUser } from '@/api/Meeting';
|
||||
import { role } from '@/config/role';
|
||||
import styles from '@/page/Meeting/ShareScreenWindow/index.module.scss'
|
||||
import { storage } from '@/utils';
|
||||
import ImageUrl from '@/utils/package/imageUrl';
|
||||
import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { useEffect, useState } from "react";
|
||||
const ShareScreenWindow: React.FC = () => {
|
||||
const [footerLists, setFooterLists] = useState<any>([
|
||||
{
|
||||
title: '静音',
|
||||
icon: ImageUrl.icon22,
|
||||
iconActive: ImageUrl.icon22Active,
|
||||
active: false,
|
||||
select: false,
|
||||
},
|
||||
{
|
||||
title: '关闭视频',
|
||||
icon: ImageUrl.icon23,
|
||||
iconActive: ImageUrl.icon23Active,
|
||||
active: false,
|
||||
select: false,
|
||||
},
|
||||
{
|
||||
title: '成员',
|
||||
icon: ImageUrl.icon30,
|
||||
iconSelect: ImageUrl.icon30Select,
|
||||
active: false,
|
||||
select: false,
|
||||
},
|
||||
{
|
||||
title: '聊天',
|
||||
icon: ImageUrl.icon31,
|
||||
iconSelect: ImageUrl.icon31Select,
|
||||
active: false,
|
||||
select: false,
|
||||
},
|
||||
{
|
||||
title: '录制',
|
||||
icon: ImageUrl.icon27,
|
||||
iconSelect: ImageUrl.icon27Select,
|
||||
iconActive: ImageUrl.icon27Active,
|
||||
active: false,
|
||||
select: false,
|
||||
},
|
||||
])
|
||||
const [timeStr, setTimeStr] = useState(0)
|
||||
const [isExpand, setIsExpand] = useState(false)
|
||||
const [roomUserLists, setRoomUserLists] = useState<any>([])
|
||||
const channel = new BroadcastChannel('meeting_channel');
|
||||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
let timeout: NodeJS.Timeout;
|
||||
useEffect(() => {
|
||||
getRoomUser()
|
||||
channel.onmessage = function (event) {
|
||||
let { type, time } = event.data;
|
||||
switch (type) {
|
||||
case 'time':
|
||||
setTimeStr(time)
|
||||
timeout = setInterval(() => {
|
||||
setTimeStr(time++)
|
||||
}, 1000)
|
||||
break;
|
||||
}
|
||||
}
|
||||
channel.postMessage({
|
||||
type: 'shareScreenWindowGetTime'
|
||||
});
|
||||
window.electron.windowHandleMessageCallBack((_e: any, data: any) => {
|
||||
switch (data.parmes.type) {
|
||||
case 'currentSpeakUserMe':
|
||||
let domMe = document.getElementById(`micr-item-${userInfo.uid}`) as HTMLDivElement;
|
||||
if (domMe) {
|
||||
domMe.style.height = `${data.parmes.currentSpeakUserMe}%`
|
||||
}
|
||||
break;
|
||||
case 'footerList':
|
||||
const footerListTemplate = [...footerLists];
|
||||
footerListTemplate[0].title = data.parmes.footerList[0][0].active ? '解除静音' : '静音';
|
||||
footerListTemplate[0].active = data.parmes.footerList[0][0].active;
|
||||
footerListTemplate[1].title = data.parmes.footerList[0][1].active ? '开启视频' : '关闭视频';
|
||||
footerListTemplate[1].active = data.parmes.footerList[0][1].active;
|
||||
footerListTemplate[4].title = data.parmes.footerList[1][3].active ? '录制中' : '录制';
|
||||
footerListTemplate[4].active = data.parmes.footerList[1][3].active;
|
||||
setFooterLists(footerListTemplate)
|
||||
break;
|
||||
case 'roomUserList':
|
||||
setRoomUserLists(data.parmes.roomUserList)
|
||||
break;
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
clearInterval(timeout)
|
||||
channel.close();
|
||||
};
|
||||
}, []);
|
||||
const changeCurrentSeconds = (time: number): string => {
|
||||
const duration = dayjs.duration(time, 'seconds');
|
||||
const hours = duration.hours(); // 整数小时
|
||||
const minutes = duration.minutes(); // 整数分钟
|
||||
const secondsRemaining = duration.seconds(); // 剩余的秒数
|
||||
return `${hours > 9 ? hours : '0' + hours}:${minutes > 9 ? minutes : '0' + minutes}:${secondsRemaining > 9 ? secondsRemaining : '0' + secondsRemaining}`
|
||||
}
|
||||
// 获取房间用户
|
||||
const getRoomUser = async (): Promise<void> => {
|
||||
const data = JSON.parse(storage.getItem('stateInfo') as string)
|
||||
await 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
|
||||
})
|
||||
setRoomUserLists(res.data)
|
||||
channel.postMessage({
|
||||
type: 'shareScreenWindowGetFooterLists',
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
// 底部按钮点击效果
|
||||
const changeFooterListSelect = (item: any, index: number, bool: boolean): void => {
|
||||
let arr = ['静音', '解除静音', '关闭视频', '开启视频']
|
||||
if (arr.indexOf(item.title) === -1) {
|
||||
const footerListTemplate = [...footerLists]
|
||||
footerListTemplate[index].select = bool;
|
||||
setFooterLists(footerListTemplate)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className={styles.shareScreenWindow} style={{ width: isExpand ? '100%' : '100%' }}>
|
||||
<div className={styles.shareScreenWindowTitle}>
|
||||
<span>{changeCurrentSeconds(timeStr)} 共享中</span>
|
||||
{isExpand ? <span className='drag' onClick={() => {
|
||||
channel.postMessage({
|
||||
type: 'shareScreenWindowClose',
|
||||
shareScreenWindowClose: timeStr
|
||||
});
|
||||
}}>结束共享</span> : <span style={{ visibility: 'hidden' }}>结束共享</span>}
|
||||
</div>
|
||||
{isExpand ? null : <div className={`${styles.shareScreenWindowContent} drag`}>
|
||||
<div className={styles.shareScreenWindowContentList}>
|
||||
{footerLists.map((item: any, index: number) => {
|
||||
return (
|
||||
<div
|
||||
onMouseDown={() => changeFooterListSelect(item, index, true)}
|
||||
onMouseUp={() => changeFooterListSelect(item, index, false)}
|
||||
onMouseLeave={() => changeFooterListSelect(item, index, false)}
|
||||
onClick={async () => {
|
||||
switch (item.title) {
|
||||
case '静音':
|
||||
case '解除静音':
|
||||
case '关闭视频':
|
||||
case '开启视频':
|
||||
case '录制':
|
||||
case '录制中':
|
||||
channel.postMessage({
|
||||
type: 'shareScreenWindowfooterListsTitle',
|
||||
shareScreenWindowfooterListsTitle: item.title
|
||||
});
|
||||
break;
|
||||
case '聊天':
|
||||
window.electron.setChildWindowShow({
|
||||
key: 'chatBigWindow',
|
||||
})
|
||||
break;
|
||||
case '成员':
|
||||
window.electron.setChildWindowShow({
|
||||
key: 'userListWindow',
|
||||
})
|
||||
break;
|
||||
}
|
||||
}}
|
||||
key={index}>
|
||||
<div>
|
||||
{!item.active ? <div style={{ backgroundImage: `url(${ImageUrl.icon49})` }} id={`micr-item-${userInfo.uid}`}>
|
||||
</div> : ''}
|
||||
{item.select ? <img src={item.iconSelect} alt="" /> : <img src={item.active ? item.iconActive : item.icon} alt="" />}
|
||||
</div>
|
||||
<span>{item.title}{item.title === '成员' ? `(${roomUserLists.filter((item: any) => item.isRoom).length})` : ''}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<Button type="primary" style={{ backgroundColor: '#FF5219', marginRight: '14px' }} size={'small'} onClick={() => {
|
||||
channel.postMessage({
|
||||
type: 'shareScreenWindowClose',
|
||||
shareScreenWindowClose: timeStr
|
||||
});
|
||||
}}>
|
||||
结束共享
|
||||
</Button>
|
||||
</div>}
|
||||
<div className={`${styles.shareScreenWindowExpand} drag`} onClick={() => {
|
||||
setIsExpand(!isExpand)
|
||||
window.electron.setChildWindow({
|
||||
width: isExpand ? 440 : 440 / 2,
|
||||
key: 'shareScreenWindow',
|
||||
})
|
||||
}}>
|
||||
<span>{isExpand ? '展开' : '收起'}</span>
|
||||
{isExpand ? <CaretDownOutlined /> : <CaretUpOutlined />}
|
||||
</div>
|
||||
</div >
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ShareScreenWindow
|
||||
|
|
@ -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%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
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, useRef } from "react";
|
||||
import { storage } from '@/utils';
|
||||
import EquipmentManagement from '@/components/EquipmentManagement';
|
||||
const { confirm } = Modal;
|
||||
|
||||
const UserListWindow: React.FC = () => {
|
||||
const [userSearchValue, setUserSearchValue] = useState('')
|
||||
const [user, setUser] = useState<any>({});
|
||||
const [roomUserList, setRoomUserList] = useState<any>([])
|
||||
const equipmentManagementRef = useRef<any>();
|
||||
const channel = new BroadcastChannel('meeting_channel');
|
||||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
useEffect(() => {
|
||||
setUser(userInfo)
|
||||
channel.onmessage = function (event) {
|
||||
const { type, showDriverList } = event.data;
|
||||
switch (type) {
|
||||
case 'showDriverList':
|
||||
equipmentManagementRef.current.setData(showDriverList)
|
||||
break;
|
||||
}
|
||||
}
|
||||
channel.postMessage({
|
||||
type: 'userListWindowGetRoomUserList'
|
||||
});
|
||||
window.electron.windowHandleMessageCallBack((_e: any, data: any) => {
|
||||
switch (data.parmes.type) {
|
||||
case 'roomUserList':
|
||||
setRoomUserList(data.parmes.roomUserList)
|
||||
break;
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
channel.close();
|
||||
}
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<div className={`${styles.userListWindow}`}>
|
||||
<div className={styles.userListWindowTitle}>
|
||||
<span>成员列表</span>
|
||||
<img src={ImageUrl.icon18} className='drag' alt="" onClick={() => {
|
||||
window.electron.setChildWindowShow({
|
||||
key: 'userListWindow',
|
||||
})
|
||||
}} />
|
||||
</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' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
style={{ width: '100%' }}
|
||||
size={'small'}
|
||||
onClick={() => {
|
||||
equipmentManagementRef.current.changeModal(item.uid, item.userName)
|
||||
}}
|
||||
>设备管理</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}
|
||||
{item.uid !== user.uid && !role.ID.includes(item.roleId) && role.ID.includes(user.roleId) ? <div onClick={() => {
|
||||
if (item.isRoomManager) {
|
||||
channel.postMessage({
|
||||
type: 'userListWindowDeleteRoomManager',
|
||||
userListWindowDeleteRoomManager: {
|
||||
uid: item.uid
|
||||
}
|
||||
});
|
||||
} else {
|
||||
channel.postMessage({
|
||||
type: 'userListWindowPostRoomManager',
|
||||
userListWindowPostRoomManager: {
|
||||
uid: item.uid
|
||||
}
|
||||
});
|
||||
}
|
||||
}}>
|
||||
{!item.isRoomManager ?
|
||||
<img src={ImageUrl.icon50} alt="" title='允许发言' /> :
|
||||
<img src={ImageUrl.icon51} alt="" title='取消发言' />}
|
||||
</div> : null}
|
||||
</div>
|
||||
</div> : null
|
||||
)
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
<div className={`${styles.userListWindowFooter}`}>
|
||||
<div className='drag' onClick={() => {
|
||||
channel.postMessage({
|
||||
type: 'userListWindowAllPostOpenMicr'
|
||||
});
|
||||
}}>全员静音</div>
|
||||
</div>
|
||||
</div>
|
||||
<EquipmentManagement ref={equipmentManagementRef} getDriver={(uid: string) => {
|
||||
channel.postMessage({
|
||||
type: 'userListWindowEquipmentManagement',
|
||||
userListWindowEquipmentManagement: {
|
||||
uid
|
||||
}
|
||||
});
|
||||
}} setDriver={(data: any) => {
|
||||
channel.postMessage({
|
||||
type: 'userListWindowSetEquipmentManagement',
|
||||
userListWindowSetEquipmentManagement: data
|
||||
});
|
||||
}} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default UserListWindow
|
||||
|
|
@ -937,6 +937,7 @@
|
|||
width: 144px;
|
||||
height: 94px;
|
||||
box-sizing: border-box;
|
||||
border: 5px solid transparent;
|
||||
|
||||
>img {
|
||||
width: 100%;
|
||||
|
|
@ -953,14 +954,16 @@
|
|||
|
||||
&:hover {
|
||||
>div {
|
||||
border: 1px solid #EBEBEB;
|
||||
border: 5px solid yellow;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
>div {
|
||||
border: 1px solid #EBEBEB;
|
||||
border: 5px solid yellow;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { agora } from '@/utils/package/agora'
|
|||
import { onInvoke, onSignalr, offSignalr } from '@/utils/package/signalr';
|
||||
import dayjs from 'dayjs';
|
||||
import durationPlugin from 'dayjs/plugin/duration';
|
||||
import { AudioVolumeInfo, ConnectionChangedReasonType, ConnectionStateType, QualityType, RtcConnection, RtcStats, UserOfflineReasonType, VideoSourceType, VideoStreamType } from 'agora-electron-sdk';
|
||||
import { AudioVolumeInfo, ConnectionChangedReasonType, ConnectionStateType, LocalVideoStreamReason, LocalVideoStreamState, QualityType, RtcConnection, RtcStats, UserOfflineReasonType, VideoSourceType, VideoStreamType } from 'agora-electron-sdk';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import SharedFilesModel from '@/components/SharedFilesModel';
|
||||
import StupWizard from '@/components/StupWizard';
|
||||
|
|
@ -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;
|
||||
|
|
@ -41,6 +42,7 @@ const Meeting: React.FC = () => {
|
|||
userVideo: false,
|
||||
})
|
||||
const [isSharedScreenModal, setIsSharedScreenModal] = useState(false);
|
||||
const [quitMeetingModal, setQuitMeetingModal] = useState(false);
|
||||
const [user, setUser] = useState<any>({});
|
||||
const [sharedScreenList, setSharedScreenList] = useState<any>([]);
|
||||
const [sharedScreenItem, setSharedScreenItem] = useState<any>('');
|
||||
|
|
@ -48,17 +50,17 @@ const Meeting: React.FC = () => {
|
|||
const [footerList, setFooterList] = useState<any>([
|
||||
[
|
||||
{
|
||||
title: '静音',
|
||||
title: '解除静音',
|
||||
icon: ImageUrl.icon22,
|
||||
iconActive: ImageUrl.icon22Active,
|
||||
active: false,
|
||||
active: true,
|
||||
select: false,
|
||||
},
|
||||
{
|
||||
title: '关闭视频',
|
||||
title: '开启视频',
|
||||
icon: ImageUrl.icon23,
|
||||
iconActive: ImageUrl.icon23Active,
|
||||
active: false,
|
||||
active: true,
|
||||
select: false,
|
||||
},
|
||||
{
|
||||
|
|
@ -142,6 +144,7 @@ const Meeting: React.FC = () => {
|
|||
rowIndex: 0,
|
||||
});
|
||||
const [roomUserList, setRoomUserList] = useState<any>([])
|
||||
const [_speackUid, setSpeackUid] = useState<any>([])
|
||||
const [chatList, setChatList] = useState<any>([])
|
||||
const [currentVideoId, setCurrentVideoId] = useState('')
|
||||
let [currentSeconds, setCurrentSeconds] = useState(0)
|
||||
|
|
@ -151,7 +154,8 @@ const Meeting: React.FC = () => {
|
|||
text: '网络质量极好。'
|
||||
})
|
||||
const [networkOther, setNetworkOther] = useState<RtcStats>({})
|
||||
const [isComputerAudio, setIsComputerAudio] = useState(true)
|
||||
const [isComputerAudio, setIsComputerAudio] = useState(false)
|
||||
const [_isScreenCapture, setIsScreenCapture] = useState(false)
|
||||
const [isFluencyPriority, setIsFluencyPriority] = useState(false)
|
||||
const [open, setOpen] = useState(false)
|
||||
const [modeOpen, setModeOpen] = useState(false)
|
||||
|
|
@ -184,46 +188,265 @@ const Meeting: React.FC = () => {
|
|||
const [observer, setObserver] = useState<IntersectionObserver>()
|
||||
let userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
const msgTips = '您不是管理员或发言人,无法开启此功能!'
|
||||
const channel = new BroadcastChannel('meeting_channel');
|
||||
useEffect(() => {
|
||||
let time: NodeJS.Timeout;
|
||||
// let getDesktopCapturerVideoTime: NodeJS.Timeout;
|
||||
setUser(userInfo)
|
||||
window.electron.getIsMaximized().then((res: boolean) => {
|
||||
if (!res) {
|
||||
window.electron.setViewStatus('maximize')
|
||||
}
|
||||
})
|
||||
setKeyOpenChildWindow('shareScreenWindow', false)
|
||||
setMeetingMode('StandardMode');
|
||||
agoraInit()
|
||||
storage.setItem('noViewChatList', 0)
|
||||
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
||||
const container = document.getElementById('videoView') as HTMLElement;
|
||||
container.addEventListener('wheel', handleWheelChange);
|
||||
channel.onmessage = async function (event) {
|
||||
const {
|
||||
type,
|
||||
shareScreenWindowfooterListsTitle,
|
||||
shareScreenWindowClose,
|
||||
userListWindowPostOpenMicr,
|
||||
userListWindowPostOpenCamera,
|
||||
userListWindowDeleteRoomManager,
|
||||
userListWindowPostRoomManager,
|
||||
userListWindowGetRoomKickout,
|
||||
userListWindowEquipmentManagement,
|
||||
userListWindowSetEquipmentManagement,
|
||||
chatSmallWindowSendChannelMsg,
|
||||
chatBigWindowSetAllUserLook,
|
||||
chatBigWindowDeleteRoomManager,
|
||||
chatBigWindowPostRoomManager,
|
||||
chatBigWindowPostOpenMicr,
|
||||
chatBigWindowPostOpenCamera,
|
||||
chatBigWindowGetRoomKickout,
|
||||
chatBigWindowSendChannelMsg,
|
||||
noticeWindowPostRoomManager
|
||||
} = event.data;
|
||||
switch (type) {
|
||||
case 'shareScreenWindowGetTime':
|
||||
setCurrentSeconds((res => {
|
||||
channel.postMessage({
|
||||
type: 'time',
|
||||
time: res,
|
||||
});
|
||||
return res
|
||||
}))
|
||||
break;
|
||||
case 'shareScreenWindowClose':
|
||||
setCurrentSeconds(shareScreenWindowClose)
|
||||
await stopScreenCapture()
|
||||
await allUserLook(userInfo.uid, userInfo.userName)
|
||||
break;
|
||||
case 'shareScreenWindowfooterListsTitle':
|
||||
switch (shareScreenWindowfooterListsTitle) {
|
||||
case '静音':
|
||||
case '解除静音':
|
||||
changeStatusList({
|
||||
title: shareScreenWindowfooterListsTitle
|
||||
}, 0, 1)
|
||||
break;
|
||||
case '关闭视频':
|
||||
case '开启视频':
|
||||
changeStatusList({
|
||||
title: shareScreenWindowfooterListsTitle
|
||||
}, 0, 2)
|
||||
break;
|
||||
case '录制':
|
||||
case '录制中':
|
||||
changeStatusList({
|
||||
title: shareScreenWindowfooterListsTitle
|
||||
}, 1, 3)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'shareScreenWindowGetFooterLists':
|
||||
setFooterList((res: any) => {
|
||||
window.electron.windowHandleMessage({
|
||||
key: 'shareScreenWindow',
|
||||
parmes: {
|
||||
footerList: res,
|
||||
type: 'footerList'
|
||||
}
|
||||
})
|
||||
return res
|
||||
})
|
||||
break;
|
||||
case 'userListWindowEquipmentManagement':
|
||||
await onInvoke('getDrivers', {
|
||||
uid: userListWindowEquipmentManagement.uid,
|
||||
})
|
||||
break;
|
||||
case 'userListWindowSetEquipmentManagement':
|
||||
await onInvoke('setDrivers', {
|
||||
uid: userListWindowSetEquipmentManagement.uid,
|
||||
driversJsonString: userListWindowSetEquipmentManagement.driversJsonString
|
||||
})
|
||||
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 'userListWindowGetRoomUserList':
|
||||
setRoomUserList(((res: any) => {
|
||||
window.electron.windowHandleMessage({
|
||||
key: 'userListWindow',
|
||||
parmes: {
|
||||
roomUserList: res,
|
||||
type: 'roomUserList'
|
||||
}
|
||||
})
|
||||
return res
|
||||
}))
|
||||
break;
|
||||
break;
|
||||
case 'userListWindowGetRoomKickout':
|
||||
GetRoomKickout(state.channelId, userListWindowGetRoomKickout.uid)
|
||||
break;
|
||||
case 'userListWindowAllPostOpenMicr':
|
||||
postOpenMicr(false, userInfo.id, true)
|
||||
break;
|
||||
case 'chatSmallWindowSendChannelMsg':
|
||||
sendMsg(chatSmallWindowSendChannelMsg.msg)
|
||||
break;
|
||||
case 'chatBigWindowSetAllUserLook':
|
||||
setAllUserLook(chatBigWindowSetAllUserLook.roomUserItem)
|
||||
break;
|
||||
case 'chatBigWindowDeleteRoomManager':
|
||||
DeleteRoomManager({
|
||||
roomId: state.roomId,
|
||||
roomNum: state.channelId,
|
||||
userId: chatBigWindowDeleteRoomManager.uid
|
||||
})
|
||||
break;
|
||||
case 'chatBigWindowPostRoomManager':
|
||||
postRoomManager({
|
||||
roomId: state.roomId,
|
||||
roomNum: state.channelId,
|
||||
userId: chatBigWindowPostRoomManager.uid
|
||||
})
|
||||
break;
|
||||
case 'chatBigWindowPostOpenMicr':
|
||||
postOpenMicr(chatBigWindowPostOpenMicr.enableMicr, chatBigWindowPostOpenMicr.uid)
|
||||
break;
|
||||
case 'chatBigWindowPostOpenCamera':
|
||||
postOpenCamera(chatBigWindowPostOpenCamera.enableCamera, chatBigWindowPostOpenCamera.uid)
|
||||
break;
|
||||
case 'chatBigWindowGetRoomKickout':
|
||||
GetRoomKickout(state.channelId, chatBigWindowGetRoomKickout.uid)
|
||||
break;
|
||||
case 'chatBigWindowSendChannelMsg':
|
||||
if (chatBigWindowSendChannelMsg.msg) {
|
||||
sendMsg(chatBigWindowSendChannelMsg.msg)
|
||||
} else[
|
||||
setChatList((res: any) => {
|
||||
window.electron.windowHandleMessage({
|
||||
key: 'chatBigWindow',
|
||||
parmes: {
|
||||
chatList: res,
|
||||
}
|
||||
})
|
||||
return res
|
||||
})
|
||||
]
|
||||
break;
|
||||
case 'noticeWindowPostRoomManager':
|
||||
postRoomManager({
|
||||
roomId: state.roomId,
|
||||
roomNum: state.channelId,
|
||||
userId: noticeWindowPostRoomManager.uid
|
||||
})
|
||||
break;
|
||||
case 'currentSpeakUserWindowGetUserName':
|
||||
setSpeackUid((uids: any) => {
|
||||
const usernames: string[] = [];
|
||||
setRoomUserList((res: any) => {
|
||||
uids.forEach((uid: any) => {
|
||||
const user = res.find((item: any) => item.uid == uid);
|
||||
if (user) {
|
||||
usernames.push(user.userName);
|
||||
}
|
||||
})
|
||||
window.electron.windowHandleMessage({
|
||||
key: 'currentSpeakUserWindow',
|
||||
parmes: {
|
||||
currentSpeakUser: usernames,
|
||||
}
|
||||
})
|
||||
return res
|
||||
});
|
||||
return []
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
time = setInterval(() => {
|
||||
setCurrentSeconds(currentSeconds++)
|
||||
setCurrentSeconds(currentSeconds => {
|
||||
return currentSeconds += 1
|
||||
})
|
||||
}, 1000)
|
||||
// getDesktopCapturerVideoTime = setInterval(() => {
|
||||
// setSharedScreenItem((i: any) => {
|
||||
// if (i && i.type === 0) {
|
||||
// agora.getDesktopCapturerVideo({ width: 0, height: 0 }, { width: 0, height: 0 }, false).then(res => {
|
||||
// if (res.length) {
|
||||
// let row = res.find((item: any) => item.sourceId === i.sourceId)
|
||||
// if (!row) {
|
||||
// stopScreenCapture()
|
||||
// setSharedScreenItem('')
|
||||
// allUserLook(userInfo.uid, userInfo.userName)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// return i
|
||||
// })
|
||||
// }, 3000)
|
||||
// 首次加载图标更新
|
||||
const firstFooterList = [...footerList]
|
||||
firstFooterList[0][0].title = state.enableMicr ? '静音' : '解除静音'
|
||||
firstFooterList[0][0].active = !state.enableMicr
|
||||
firstFooterList[0][1].title = state.enableCamera ? '关闭视频' : '开启视频'
|
||||
firstFooterList[0][1].active = !state.enableCamera
|
||||
agora.muteLocalVideoStream(userInfo, state.enableMicr, state.enableCamera)
|
||||
setFooterList(firstFooterList)
|
||||
setTimeout(async () => {
|
||||
const setting = await JSON.parse(storage.getItem('setting') as string);
|
||||
const stateInfo = await JSON.parse(storage.getItem('stateInfo') as string);
|
||||
if (stateInfo && setting.isRecordingTips && !recorder) {
|
||||
setRecorder((data: any) => {
|
||||
if (!data) {
|
||||
confirm({
|
||||
title: '提示',
|
||||
icon: <ExclamationCircleFilled />,
|
||||
content: `是否录制本次会议?`,
|
||||
centered: true,
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
async onOk() {
|
||||
if (stateInfo) {
|
||||
changeStatusList({
|
||||
title: '录制'
|
||||
}, 1, 3)
|
||||
} else {
|
||||
message.error('当前不在会议室!')
|
||||
}
|
||||
},
|
||||
onCancel() {
|
||||
}
|
||||
})
|
||||
}
|
||||
return data
|
||||
})
|
||||
}
|
||||
}, 10000);
|
||||
return () => {
|
||||
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
||||
window.removeEventListener('wheel', handleWheelChange);
|
||||
clearInterval(time)
|
||||
// clearInterval(getDesktopCapturerVideoTime)
|
||||
channel.close();
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
@ -262,6 +485,17 @@ const Meeting: React.FC = () => {
|
|||
}
|
||||
}, [currentEffective]);
|
||||
|
||||
useEffect(() => {
|
||||
if (chatList.length) {
|
||||
window.electron.windowHandleMessage({
|
||||
key: 'chatBigWindow',
|
||||
parmes: {
|
||||
chatList,
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [chatList]);
|
||||
|
||||
useEffect(() => {
|
||||
let currentVideoUserItem = roomUserList.find((item: any) => item.uid === currentVideoId || item.screenShareId === currentVideoId)
|
||||
if (currentVideoUserItem) {
|
||||
|
|
@ -312,6 +546,12 @@ const Meeting: React.FC = () => {
|
|||
setNoViewChatList(storageNoViewChatList)
|
||||
}
|
||||
setChatList((newChatList: any) => [...newChatList, item])
|
||||
window.electron.windowHandleMessage({
|
||||
key: 'chatSmallWindow',
|
||||
parmes: {
|
||||
chatListIten: item,
|
||||
}
|
||||
})
|
||||
setStatusList((res: any) => {
|
||||
if (!res.userChatList) {
|
||||
api.open({
|
||||
|
|
@ -407,14 +647,14 @@ const Meeting: React.FC = () => {
|
|||
message.success(`操作成功`)
|
||||
await agora.updateChannelMediaOptions(item.user.isRoomManager)
|
||||
await postOpenMicrApi(item.user.isRoomManager, userInfo.uid, false)
|
||||
await postOpenCameraApi(item.user.isRoomManager, userInfo.uid)
|
||||
await postOpenCameraApi(false, userInfo.uid) // 不管身份如何改变都关闭摄像头
|
||||
await stopScreenCapture()
|
||||
} else {
|
||||
message.success(`${item.user.userName}已结束发言`)
|
||||
}
|
||||
} else {
|
||||
if (item.user.uid === userInfo.uid) {
|
||||
if (!item.user.isRoomManager) {
|
||||
if (item.user.isRoomManager) {
|
||||
await agora.allLeaveChannelEx()
|
||||
}
|
||||
message.success(`管理员${item.user.isRoomManager ? '设置' : '取消'}您为发言人`)
|
||||
|
|
@ -426,7 +666,7 @@ const Meeting: React.FC = () => {
|
|||
postOpenMicrApi(item.user.isRoomManager, userInfo.uid, false)
|
||||
} else {
|
||||
postOpenMicrApi(item.user.isRoomManager, userInfo.uid, false)
|
||||
postOpenCameraApi(item.user.isRoomManager, userInfo.uid)
|
||||
postOpenCameraApi(false, userInfo.uid)
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
|
@ -440,59 +680,73 @@ const Meeting: React.FC = () => {
|
|||
break;
|
||||
// 申请发言
|
||||
case 'ApplyToSpeak':
|
||||
api.open({
|
||||
message: '',
|
||||
description: <div>
|
||||
<span style={{ fontSize: '16px' }}>{item.uname}申请发言</span>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
onClick={async (e: any) => {
|
||||
let i = e.nativeEvent.path;
|
||||
if (i) {
|
||||
i.forEach((i: any) => {
|
||||
if (i.className === 'ant-notification-notice ant-notification-notice-closable') {
|
||||
i.childNodes.forEach((row: any) => {
|
||||
if (row.className === 'ant-notification-notice-close') {
|
||||
row.click()
|
||||
postRoomManager({
|
||||
roomId: state.roomId,
|
||||
roomNum: state.channelId,
|
||||
userId: item.uid
|
||||
setIsScreenCapture(bool => {
|
||||
if (bool) {
|
||||
window.electron.setChildWindowShow({
|
||||
key: 'noticeWindow',
|
||||
bool: true
|
||||
})
|
||||
channel.postMessage({
|
||||
type: 'noticeItem',
|
||||
noticeItem: item
|
||||
});
|
||||
} else {
|
||||
api.open({
|
||||
message: '',
|
||||
description: <div>
|
||||
<span style={{ fontSize: '16px' }}>{item.uname}申请发言</span>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
onClick={async (e: any) => {
|
||||
let i = e.nativeEvent.path;
|
||||
if (i) {
|
||||
i.forEach((i: any) => {
|
||||
if (i.className === 'ant-notification-notice ant-notification-notice-closable') {
|
||||
i.childNodes.forEach((row: any) => {
|
||||
if (row.className === 'ant-notification-notice-close') {
|
||||
row.click()
|
||||
postRoomManager({
|
||||
roomId: state.roomId,
|
||||
roomNum: state.channelId,
|
||||
userId: item.uid
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}}
|
||||
>接受</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={(e: any) => {
|
||||
let item = e.nativeEvent.path;
|
||||
if (item) {
|
||||
item.forEach((item: any) => {
|
||||
if (item.className === 'ant-notification-notice ant-notification-notice-closable') {
|
||||
item.childNodes.forEach((row: any) => {
|
||||
if (row.className === 'ant-notification-notice-close') {
|
||||
row.click()
|
||||
}}
|
||||
>接受</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={(e: any) => {
|
||||
let item = e.nativeEvent.path;
|
||||
if (item) {
|
||||
item.forEach((item: any) => {
|
||||
if (item.className === 'ant-notification-notice ant-notification-notice-closable') {
|
||||
item.childNodes.forEach((row: any) => {
|
||||
if (row.className === 'ant-notification-notice-close') {
|
||||
row.click()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}}
|
||||
style={{ backgroundColor: '#EC3C3C', marginLeft: '14px' }}
|
||||
>拒绝</Button>
|
||||
</div>
|
||||
</div>,
|
||||
duration: 10,
|
||||
placement: 'bottomRight',
|
||||
showProgress: true,
|
||||
pauseOnHover: false,
|
||||
});
|
||||
}}
|
||||
style={{ backgroundColor: '#EC3C3C', marginLeft: '14px' }}
|
||||
>拒绝</Button>
|
||||
</div>
|
||||
</div>,
|
||||
duration: 10,
|
||||
placement: 'bottomRight',
|
||||
showProgress: true,
|
||||
pauseOnHover: false,
|
||||
});
|
||||
}
|
||||
return bool
|
||||
})
|
||||
break;
|
||||
// 管理员查看随机用户
|
||||
case 'Watch':
|
||||
|
|
@ -560,7 +814,12 @@ const Meeting: React.FC = () => {
|
|||
case 'ShowDriverList':
|
||||
if (item.driversJsonString) {
|
||||
const data = JSON.parse(item.driversJsonString);
|
||||
equipmentManagementRef.current.setData(data)
|
||||
const isOpen = await getKeyOpenChildWindow('shareScreenWindow')
|
||||
if (isOpen) {
|
||||
channel.postMessage({ type: 'showDriverList', showDriverList: data })
|
||||
} else {
|
||||
equipmentManagementRef.current.setData(data)
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -639,6 +898,27 @@ const Meeting: React.FC = () => {
|
|||
observerObject.observe(element);
|
||||
});
|
||||
}
|
||||
window.electron.windowHandleMessage({
|
||||
key: 'shareScreenWindow',
|
||||
parmes: {
|
||||
footerList,
|
||||
type: 'footerList'
|
||||
}
|
||||
})
|
||||
window.electron.windowHandleMessage({
|
||||
key: 'shareScreenWindow',
|
||||
parmes: {
|
||||
roomUserList,
|
||||
type: 'roomUserList'
|
||||
}
|
||||
})
|
||||
window.electron.windowHandleMessage({
|
||||
key: 'userListWindow',
|
||||
parmes: {
|
||||
roomUserList,
|
||||
type: 'roomUserList'
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
elements.forEach(element => {
|
||||
observer?.unobserve(element);
|
||||
|
|
@ -701,18 +981,30 @@ const Meeting: React.FC = () => {
|
|||
}
|
||||
},
|
||||
onAudioVolumeIndication: async (speakers: AudioVolumeInfo[]) => {
|
||||
speakers.forEach((item: any) => {
|
||||
let domMe = document.getElementById(`micr-item-${userInfo.uid}`);
|
||||
let dom = document.getElementById(`micr-${item.uid ? item.uid : userInfo.uid}`);
|
||||
if (dom) {
|
||||
const percentage = (item.volume / 255) * 100
|
||||
dom.style.height = `${percentage}%`
|
||||
}
|
||||
if (domMe && !item.uid) {
|
||||
const percentage = (item.volume / 255) * 100
|
||||
domMe.style.height = `${percentage}%`
|
||||
}
|
||||
});
|
||||
if (speakers.length) {
|
||||
setSpeackUid((res: any) => {
|
||||
return [...new Set([...res, ...(speakers.filter((item: any) => item.volume)).map(item => item.uid || userInfo.uid)])]
|
||||
})
|
||||
speakers.forEach((item: any) => {
|
||||
let domMe = document.getElementById(`micr-item-${userInfo.uid}`);
|
||||
let dom = document.getElementById(`micr-${item.uid ? item.uid : userInfo.uid}`);
|
||||
if (dom) {
|
||||
const percentage = (item.volume / 255) * 100
|
||||
dom.style.height = `${percentage}%`
|
||||
}
|
||||
if (domMe && !item.uid) {
|
||||
const percentage = (item.volume / 255) * 100
|
||||
domMe.style.height = `${percentage}%`
|
||||
window.electron.windowHandleMessage({
|
||||
key: 'shareScreenWindow',
|
||||
parmes: {
|
||||
currentSpeakUserMe: percentage,
|
||||
type: 'currentSpeakUserMe'
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onNetworkQuality: async (_connection: RtcConnection, remoteUid: number, _txQuality: QualityType, rxQuality: QualityType) => {
|
||||
if (remoteUid === 0) {
|
||||
|
|
@ -748,6 +1040,18 @@ const Meeting: React.FC = () => {
|
|||
message.error('网络断开,请检查网络')
|
||||
}
|
||||
},
|
||||
onLocalVideoStateChanged: async (_source: VideoSourceType, _state: LocalVideoStreamState, reason: LocalVideoStreamReason) => {
|
||||
if (reason === 12) {
|
||||
setIsScreenCapture(bool => {
|
||||
if (bool) {
|
||||
stopScreenCapture()
|
||||
setSharedScreenItem('')
|
||||
allUserLook(userInfo.uid, userInfo.userName)
|
||||
}
|
||||
return bool
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
if (state.enableCamera) {
|
||||
await agora.startCameraCapture()
|
||||
|
|
@ -847,8 +1151,19 @@ const Meeting: React.FC = () => {
|
|||
setRoomUserList((res: any) => {
|
||||
let userItem = res.find((row: any) => row.uid === item.user.uid)
|
||||
if (userItem) {
|
||||
for (const key in item.user) {
|
||||
userItem[key] = item.user[key];
|
||||
for (const keys in item.user) {
|
||||
if (keys !== 'enableCamera' && keys !== 'enableMicr') {
|
||||
userItem[keys] = item.user[keys];
|
||||
}
|
||||
}
|
||||
if (key === 'OperCamera') {
|
||||
userItem.enableCamera = item.user.enableCamera;
|
||||
if (userItem.uid === userInfo.uid) {
|
||||
userItem.enableCamera ? agora.startCameraCapture() : agora.stopCameraCapture()
|
||||
}
|
||||
}
|
||||
if (key === 'OperMicr') {
|
||||
userItem.enableMicr = item.user.enableMicr;
|
||||
}
|
||||
userItem.isAdmin = role.ID.includes(item.user.roleId) || item.user.isRoomManager;
|
||||
refreshVideoView(userItem)
|
||||
|
|
@ -1075,12 +1390,12 @@ const Meeting: React.FC = () => {
|
|||
await allUserLook(userInfo.uid, userInfo.userName)
|
||||
break;
|
||||
case '静音':
|
||||
await postOpenMicr(false, user.uid)
|
||||
await postOpenMicr(false, userInfo.uid)
|
||||
break;
|
||||
case '解除静音':
|
||||
await getUserRoomInfo().then(async (res) => {
|
||||
if (res) {
|
||||
await postOpenMicr(true, user.uid)
|
||||
await postOpenMicr(true, userInfo.uid)
|
||||
} else {
|
||||
if (!isClicked) {
|
||||
setCurrentRequestSpeakType('audio')
|
||||
|
|
@ -1092,12 +1407,12 @@ const Meeting: React.FC = () => {
|
|||
})
|
||||
break;
|
||||
case '关闭视频':
|
||||
await postOpenCamera(false, user.uid)
|
||||
await postOpenCamera(false, userInfo.uid)
|
||||
break;
|
||||
case '开启视频':
|
||||
await getUserRoomInfo().then(async (res) => {
|
||||
if (res) {
|
||||
await postOpenCamera(true, user.uid)
|
||||
await postOpenCamera(true, userInfo.uid)
|
||||
} else {
|
||||
if (!isClicked) {
|
||||
setCurrentRequestSpeakType('video')
|
||||
|
|
@ -1259,6 +1574,52 @@ const Meeting: React.FC = () => {
|
|||
setIsSharedScreenModal(false)
|
||||
await agora.setDesktopCapturerVideo(sharedScreenItem, isComputerAudio, isFluencyPriority)
|
||||
await allUserLook(user.screenShareId, user.userName)
|
||||
const isOpen = await getKeyOpenChildWindow('shareScreenWindow')
|
||||
setIsScreenCapture(true)
|
||||
window.electron.setViewStatus('hide')
|
||||
if (!isOpen) {
|
||||
window.electron.createChildWindow({
|
||||
url: location.origin + `/#/noticeWindow`,
|
||||
width: 388,
|
||||
height: 150,
|
||||
key: 'noticeWindow',
|
||||
show: true,
|
||||
})
|
||||
window.electron.createChildWindow({
|
||||
url: location.origin + `/#/shareScreenWindow`,
|
||||
width: 400,
|
||||
height: 80,
|
||||
key: 'shareScreenWindow',
|
||||
show: true,
|
||||
})
|
||||
window.electron.createChildWindow({
|
||||
url: location.origin + `/#/chatSmallWindow`,
|
||||
width: 200,
|
||||
height: 150,
|
||||
key: 'chatSmallWindow',
|
||||
show: true,
|
||||
})
|
||||
window.electron.createChildWindow({
|
||||
url: location.origin + `/#/currentSpeakUserWindow`,
|
||||
width: 200,
|
||||
height: 30,
|
||||
key: 'currentSpeakUserWindow',
|
||||
show: true,
|
||||
})
|
||||
window.electron.createChildWindow({
|
||||
url: location.origin + `/#/chatBigWindow`,
|
||||
width: 540,
|
||||
height: 640,
|
||||
key: 'chatBigWindow',
|
||||
})
|
||||
window.electron.createChildWindow({
|
||||
url: location.origin + `/#/userListWindow`,
|
||||
width: 440,
|
||||
height: 540,
|
||||
key: 'userListWindow',
|
||||
})
|
||||
setKeyOpenChildWindow('shareScreenWindow', true)
|
||||
}
|
||||
} else {
|
||||
message.error('请选择应用!')
|
||||
}
|
||||
|
|
@ -1267,7 +1628,9 @@ const Meeting: React.FC = () => {
|
|||
const getDesktopCapturerVideo = (): void => {
|
||||
agora.getDesktopCapturerVideo({ width: 300, height: 300 }, { width: 300, height: 300 }, true).then((res: any) => {
|
||||
res.forEach((item: any, index: number) => {
|
||||
item.sourceTitle = '桌面' + (index + 1)
|
||||
if (item.type === 1) {
|
||||
item.sourceTitle = '桌面' + (index + 1)
|
||||
}
|
||||
if (item.thumbImage.buffer) {
|
||||
item.thumbnailUrl = thumbImageBufferToBase64(item.thumbImage)
|
||||
}
|
||||
|
|
@ -1301,6 +1664,25 @@ const Meeting: React.FC = () => {
|
|||
agora.stopScreenCapture()
|
||||
footerListTemplate[1][0].title = '共享屏幕'
|
||||
setFooterList(footerListTemplate)
|
||||
window.electron.closeChildWindow('shareScreenWindow')
|
||||
setKeyOpenChildWindow('shareScreenWindow', false)
|
||||
setIsScreenCapture(bool => {
|
||||
if (bool) {
|
||||
window.electron.setViewStatus('show')
|
||||
window.electron.getWindowSize().then((res: any) => {
|
||||
window.electron.setMainWindowSize({
|
||||
width: Math.ceil(res.width / 1.5),
|
||||
height: Math.ceil(res.height / 1.3),
|
||||
})
|
||||
window.electron.getIsMaximized().then((b: boolean) => {
|
||||
if (!b) {
|
||||
window.electron.setViewStatus('maximize')
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
// 获取房间用户
|
||||
const getRoomUser = async (): Promise<void> => {
|
||||
|
|
@ -1325,6 +1707,11 @@ const Meeting: React.FC = () => {
|
|||
case 'meetingMode':
|
||||
setMeetingMode(e.value)
|
||||
break;
|
||||
case 'quitMeeting':
|
||||
if (e.value) {
|
||||
setQuitMeetingModal(true)
|
||||
}
|
||||
break;
|
||||
case 'reconnect':
|
||||
if (e.value == true) {
|
||||
await onInvoke('joinChannel', {
|
||||
|
|
@ -1342,6 +1729,12 @@ const Meeting: React.FC = () => {
|
|||
userId: userInfo.uid
|
||||
})
|
||||
}
|
||||
setIsScreenCapture(bool => {
|
||||
if (bool) {
|
||||
allUserLook(userItem.screenShareId, userItem.userName)
|
||||
}
|
||||
return bool
|
||||
})
|
||||
return res
|
||||
})
|
||||
|
||||
|
|
@ -1357,12 +1750,19 @@ const Meeting: React.FC = () => {
|
|||
roomNum: state.channelId,
|
||||
msg: msg,
|
||||
})
|
||||
setChatList((newChatList: any) => [...newChatList, {
|
||||
uid: user.uid,
|
||||
userName: user.userName,
|
||||
let item = {
|
||||
uid: userInfo.uid,
|
||||
userName: userInfo.userName,
|
||||
message: msg,
|
||||
timestamp: +new Date()
|
||||
}])
|
||||
}
|
||||
setChatList((newChatList: any) => [...newChatList, item])
|
||||
window.electron.windowHandleMessage({
|
||||
key: 'chatSmallWindow',
|
||||
parmes: {
|
||||
chatListIten: item,
|
||||
}
|
||||
})
|
||||
setTextMsg('');
|
||||
chatScrollBotton()
|
||||
} else {
|
||||
|
|
@ -1783,7 +2183,7 @@ const Meeting: React.FC = () => {
|
|||
})
|
||||
}
|
||||
}}
|
||||
>{item.isRoomManager ? '取消发言人' : '设为发言人'}</Button> : null}
|
||||
>{item.isRoomManager ? '取消发言' : '允许发言'}</Button> : null}
|
||||
{item.isRoomManager ? <Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
|
|
@ -1950,7 +2350,7 @@ const Meeting: React.FC = () => {
|
|||
{role.ID.includes(item.roleId) || item.isRoomManager ? <div>
|
||||
<img src={item.enableMicr ? ImageUrl.icon22 : ImageUrl.icon22Active} alt="" onClick={() => {
|
||||
postOpenMicr(!item.enableMicr, item.uid)
|
||||
}} title={item.enableMicr ? '静音' : '解除声音'} />
|
||||
}} title={item.enableMicr ? '静音' : '解除静音'} />
|
||||
</div> : null}
|
||||
{role.ID.includes(item.roleId) || item.isRoomManager ? <div>
|
||||
<img src={item.enableCamera ? ImageUrl.icon23 : ImageUrl.icon23Active} alt="" onClick={() => {
|
||||
|
|
@ -1959,28 +2359,7 @@ const Meeting: React.FC = () => {
|
|||
</div> : null}
|
||||
{item.uid !== user.uid && role.ID.includes(user.roleId) ? <div>
|
||||
<Popover placement="left" title={''} content={
|
||||
<div>
|
||||
{!role.ID.includes(item.roleId) ? <Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
style={{ marginBottom: '10px', width: '100%' }}
|
||||
size={'small'}
|
||||
onClick={() => {
|
||||
if (item.isRoomManager) {
|
||||
DeleteRoomManager({
|
||||
roomId: state.roomId,
|
||||
roomNum: state.channelId,
|
||||
userId: item.uid
|
||||
})
|
||||
} else {
|
||||
postRoomManager({
|
||||
roomId: state.roomId,
|
||||
roomNum: state.channelId,
|
||||
userId: item.uid
|
||||
})
|
||||
}
|
||||
}}
|
||||
>{item.isRoomManager ? '取消发言人' : '设为发言人'}</Button> : null}
|
||||
<div style={{ width: '100px' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
|
|
@ -2000,12 +2379,29 @@ const Meeting: React.FC = () => {
|
|||
>移出会议</Button>
|
||||
</div>
|
||||
}>
|
||||
<EllipsisOutlined style={{
|
||||
color: '#fff',
|
||||
fontSize: '20px'
|
||||
}} />
|
||||
<EllipsisOutlined style={{ color: '#fff', fontSize: '20px' }} />
|
||||
</Popover>
|
||||
</div> : null}
|
||||
{item.uid !== user.uid && !role.ID.includes(item.roleId) && role.ID.includes(user.roleId) ? <div onClick={() => {
|
||||
if (item.isRoomManager) {
|
||||
DeleteRoomManager({
|
||||
roomId: state.roomId,
|
||||
roomNum: state.channelId,
|
||||
userId: item.uid
|
||||
})
|
||||
} else {
|
||||
postRoomManager({
|
||||
roomId: state.roomId,
|
||||
roomNum: state.channelId,
|
||||
userId: item.uid
|
||||
})
|
||||
}
|
||||
}}>
|
||||
{!item.isRoomManager ?
|
||||
<img src={ImageUrl.icon50} alt="" title='允许发言' /> :
|
||||
<img src={ImageUrl.icon51} alt="" title='取消发言' />
|
||||
}
|
||||
</div> : null}
|
||||
</div>
|
||||
</div> : null
|
||||
)
|
||||
|
|
@ -2092,7 +2488,7 @@ const Meeting: React.FC = () => {
|
|||
}
|
||||
})
|
||||
}}
|
||||
>{roomUserItem.isRoomManager ? '取消发言人' : '设为发言人'}</Button> : null}
|
||||
>{roomUserItem.isRoomManager ? '取消发言' : '允许发言'}</Button> : null}
|
||||
{roomUserItem.isRoomManager ? <Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
|
|
@ -2419,6 +2815,41 @@ const Meeting: React.FC = () => {
|
|||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal
|
||||
title=""
|
||||
open={quitMeetingModal}
|
||||
footer={null}
|
||||
closable={false}
|
||||
centered
|
||||
width={'190px'}
|
||||
>
|
||||
<div>
|
||||
<div className='meetingContentFooterPopover'>
|
||||
{role.ID.includes(user.roleId) ?
|
||||
<Popconfirm
|
||||
title="提示"
|
||||
description={`结束会议后,所有人将退出,是否结束?`}
|
||||
onConfirm={async () => {
|
||||
await GetLeaveAll({
|
||||
roomNum: state.channelId,
|
||||
})
|
||||
}}
|
||||
onCancel={() => {
|
||||
|
||||
}}
|
||||
okText="结束"
|
||||
cancelText="取消"
|
||||
>
|
||||
<div className='meetingContentFooterPopoverDel'>全员结束会议</div>
|
||||
</Popconfirm>
|
||||
: null}
|
||||
<div className='meetingContentFooterPopoverDefault' onClick={async () => {
|
||||
await leaveChannel()
|
||||
}}>仅自己离开</div>
|
||||
<div className='meetingContentFooterPopoverCancel' onClick={() => { setQuitMeetingModal(false) }}>取消</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<SharedFilesModel ref={sharedFilesModelRef} />
|
||||
<SpeakerModeModal ref={speakerModeModalRef} />
|
||||
<InvitingPersonnelModal ref={invitingPersonnelRef} />
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
.shareScreenWindow {
|
||||
background-color: #07090B;
|
||||
color: white;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.shareScreenWindowTitle {
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
background-color: lighten(#07090B, 4%);
|
||||
}
|
||||
|
||||
.shareScreenWindowContent {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.shareScreenWindowContentList {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
|
||||
>div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
>img {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
>span {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
import styles from '@/page/ShareScreenWindow/index.module.scss'
|
||||
import ImageUrl from '@/utils/package/imageUrl';
|
||||
import { Button } from 'antd';
|
||||
import { useEffect, useState } from "react";
|
||||
// window.electron.createChildWindow({
|
||||
// url: location.origin + `/#/shareScreenWindow`,
|
||||
// width: 500,
|
||||
// height: 70,
|
||||
// key: 'shareScreenWindow',
|
||||
// })
|
||||
const ShareScreenWindow: React.FC = () => {
|
||||
const [footerList, setFooterList] = useState<any>([
|
||||
{
|
||||
title: '静音',
|
||||
icon: ImageUrl.icon22,
|
||||
iconActive: ImageUrl.icon22Active,
|
||||
active: false,
|
||||
select: false,
|
||||
},
|
||||
{
|
||||
title: '关闭视频',
|
||||
icon: ImageUrl.icon23,
|
||||
iconActive: ImageUrl.icon23Active,
|
||||
active: false,
|
||||
select: false,
|
||||
},
|
||||
{
|
||||
title: '成员列表',
|
||||
icon: ImageUrl.icon30,
|
||||
iconSelect: ImageUrl.icon30Select,
|
||||
active: false,
|
||||
select: false,
|
||||
},
|
||||
{
|
||||
title: '聊天',
|
||||
icon: ImageUrl.icon31,
|
||||
iconSelect: ImageUrl.icon31Select,
|
||||
active: false,
|
||||
select: false,
|
||||
},
|
||||
{
|
||||
title: '设置',
|
||||
icon: ImageUrl.icon28,
|
||||
iconSelect: ImageUrl.icon28Select,
|
||||
active: false,
|
||||
select: false,
|
||||
},
|
||||
{
|
||||
title: '录制',
|
||||
icon: ImageUrl.icon27,
|
||||
iconSelect: ImageUrl.icon27Select,
|
||||
iconActive: ImageUrl.icon27Active,
|
||||
active: false,
|
||||
select: false,
|
||||
},
|
||||
])
|
||||
useEffect(() => {
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`${styles.shareScreenWindow} drag`}>
|
||||
<div className={styles.shareScreenWindowTitle}>02:38 共享中</div>
|
||||
<div className={styles.shareScreenWindowContent}>
|
||||
<div className={styles.shareScreenWindowContentList}>
|
||||
{footerList.map((item: any, index: number) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
console.log(item, index)
|
||||
}}
|
||||
key={index}>
|
||||
{item.select ? <img src={item.iconSelect} alt="" /> : <img src={item.active ? item.iconActive : item.icon} alt="" />}
|
||||
<span>{item.title}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<Button type="primary" style={{ backgroundColor: '#FF5219', marginRight: '14px' }} onClick={() => {
|
||||
|
||||
}}>
|
||||
结束会议
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ShareScreenWindow
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
export interface IElectronAPI {
|
||||
setMainWindowSize: (config: any) => void;
|
||||
getWindowSize: () => any;
|
||||
setViewStatus: (status: 'quit' | 'maximize' | 'minimize' | 'unmaximize' | 'hide') => void;
|
||||
setViewStatus: (status: 'quit' | 'maximize' | 'minimize' | 'unmaximize' | 'hide' | 'show') => void;
|
||||
getIsMaximized: () => Promise<boolean>;
|
||||
setWriteText: (text: string) => void;
|
||||
onQuit: (callBack: Function) => void;
|
||||
|
|
@ -12,13 +12,22 @@ export interface IElectronAPI {
|
|||
selectFilePath: (data?: any) => void
|
||||
onFilePath: (callBack: Function) => void;
|
||||
getSources: () => any;
|
||||
quit: () => any;
|
||||
quit: (bool) => any;
|
||||
downFile: (callBack: Function) => void;
|
||||
quitAndInstall: (callBack: Function) => void;
|
||||
isOpenWindows: (callBack: Function) => void;
|
||||
getVersion: () => Promise<string>;
|
||||
isVisible: () => Promise<string>;
|
||||
setRegistry: (uuid: string) => any;
|
||||
getRegistry: () => any;
|
||||
createChildWindow: (config: any) => void;
|
||||
setChildWindow: (config: any) => void;
|
||||
setChildWindowShow: (config: any) => void;
|
||||
closeChildWindow: (key: string) => void;
|
||||
mainWindowCenter: () => any;
|
||||
mainWindowHide: () => any;
|
||||
windowHandleMessage: (data: any) => {}
|
||||
windowHandleMessageCallBack: (callBack: Function) => void;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ import {
|
|||
AudioVolumeInfo,
|
||||
UserOfflineReasonType,
|
||||
ConnectionStateType,
|
||||
ConnectionChangedReasonType
|
||||
ConnectionChangedReasonType,
|
||||
LocalVideoStreamReason,
|
||||
LocalVideoStreamState
|
||||
} from "agora-electron-sdk";
|
||||
import { GetRoomRtcToken, GetAgoraConf } from "@/api/Home/Index";
|
||||
import { storage } from '@/utils';
|
||||
|
|
@ -38,7 +40,9 @@ export const agora = {
|
|||
await rtcEngine.initialize({
|
||||
appId: data,
|
||||
});
|
||||
await agora.setDeviceManager(bool)
|
||||
if (bool) {
|
||||
await agora.setDeviceManager()
|
||||
}
|
||||
}
|
||||
},
|
||||
// 获取rtcEngine
|
||||
|
|
@ -46,7 +50,7 @@ export const agora = {
|
|||
return rtcEngine
|
||||
},
|
||||
// 获取当前设备是否存在不存在就获取默认设备
|
||||
setDeviceManager: async (bool: boolean = false) => {
|
||||
setDeviceManager: async () => {
|
||||
const setting = await JSON.parse(storage.getItem('setting') as string)
|
||||
// 摄像头
|
||||
if (setting.videoDeviceId) {
|
||||
|
|
@ -99,19 +103,17 @@ export const agora = {
|
|||
}
|
||||
setTimeout(async () => {
|
||||
storage.setItem('setting', JSON.stringify(setting))
|
||||
if (bool) {
|
||||
const setting = await JSON.parse(storage.getItem('setting') as string)
|
||||
if (setting.videoDeviceId) agora.setVideoDeviceManager(setting.videoDeviceId) //指定摄像头头采集设备
|
||||
if (setting.playBackDeviceId) agora.setPlaybackDevice(setting.playBackDeviceId) //指定播放设备
|
||||
if (setting.playBackVolume) agora.setPlaybackDeviceVolume(setting.playBackVolume) // 设置播放设备音量
|
||||
if (setting.ecordingDeviceId) agora.setRecordingDevice(setting.ecordingDeviceId) // 设置音频采集设备
|
||||
if (setting.ecordingVolume) agora.setRecordingDeviceVolume(setting.ecordingVolume) // 设置音频设备音量
|
||||
if (setting.isAINoiseReduction) agora.setAINSMode(setting.isAINoiseReduction, setting.aINoiseReduction) // 设置ai降噪
|
||||
}
|
||||
const settingData = await JSON.parse(storage.getItem('setting') as string)
|
||||
if (settingData.videoDeviceId) agora.setVideoDeviceManager(settingData.videoDeviceId) //指定摄像头头采集设备
|
||||
if (settingData.playBackDeviceId) agora.setPlaybackDevice(settingData.playBackDeviceId) //指定播放设备
|
||||
if (settingData.playBackVolume) agora.setPlaybackDeviceVolume(settingData.playBackVolume) // 设置播放设备音量
|
||||
if (settingData.ecordingDeviceId) agora.setRecordingDevice(settingData.ecordingDeviceId) // 设置音频采集设备
|
||||
if (settingData.ecordingVolume) agora.setRecordingDeviceVolume(settingData.ecordingVolume) // 设置音频设备音量
|
||||
if (settingData.isAINoiseReduction) agora.setAINSMode(settingData.isAINoiseReduction, settingData.aINoiseReduction) // 设置ai降噪
|
||||
}, 1000);
|
||||
},
|
||||
// 事件回调
|
||||
registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline, onAudioVolumeIndication, onNetworkQuality, onRtcStats, onConnectionStateChanged }: any) => {
|
||||
registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline, onAudioVolumeIndication, onNetworkQuality, onRtcStats, onConnectionStateChanged, onLocalVideoStateChanged }: any) => {
|
||||
rtcEngine.registerEventHandler({
|
||||
// 监听本地用户加入频道事件
|
||||
onJoinChannelSuccess: async (connection: RtcConnection, elapsed: number) => {
|
||||
|
|
@ -153,6 +155,10 @@ export const agora = {
|
|||
onConnectionStateChanged: async (connection: RtcConnection, state: ConnectionStateType, reason: ConnectionChangedReasonType) => {
|
||||
await onConnectionStateChanged?.(connection, state, reason)
|
||||
},
|
||||
// 本地视频状态发生改变回调。
|
||||
onLocalVideoStateChanged: async (source: VideoSourceType, state: LocalVideoStreamState, reason: LocalVideoStreamReason) => {
|
||||
await onLocalVideoStateChanged?.(source, state, reason)
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获取视图模式
|
||||
|
|
@ -165,7 +171,7 @@ export const agora = {
|
|||
},
|
||||
// 本地加入
|
||||
setupLocalVideo: async (item: any) => {
|
||||
if (item.view?.childNodes.length === 1) {
|
||||
if (item.view?.childNodes.length === 1 || item.type) {
|
||||
await rtcEngine.setupLocalVideo({
|
||||
renderMode: agora.getRrenderMode(item.uid),
|
||||
sourceType: item.sourceType,
|
||||
|
|
@ -232,7 +238,7 @@ export const agora = {
|
|||
},
|
||||
// 加入频道
|
||||
joinChannel: async () => {
|
||||
await rtcEngine.enableAudioVolumeIndication(100, 1, true)
|
||||
await rtcEngine.enableAudioVolumeIndication(100, 3, true)
|
||||
await rtcEngine.joinChannel(option.token, option.channelId, option.uid);
|
||||
await rtcEngine.setDualStreamModeEx(
|
||||
SimulcastStreamMode.EnableSimulcastStream,
|
||||
|
|
@ -323,8 +329,8 @@ export const agora = {
|
|||
rtcEngine.muteRemoteVideoStream(uid, mute)
|
||||
},
|
||||
// 销毁视频渲染dom
|
||||
destroyRendererByConfig: async (uid: number) => {
|
||||
await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, option.channelId + 'a', uid);
|
||||
destroyRendererByConfig: async (uid: number, channelId: string) => {
|
||||
await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, channelId, uid);
|
||||
},
|
||||
// ai降噪
|
||||
setAINSMode: async (enabled: boolean, mode: AudioAinsMode) => {
|
||||
|
|
@ -388,7 +394,7 @@ export const agora = {
|
|||
},
|
||||
// 桌面捕获音频和视频的媒体源的信息
|
||||
getDesktopCapturerVideo: async (thumbSize: any, iconSize: any, includeScreen: boolean) => {
|
||||
return await rtcEngine.getScreenCaptureSources(thumbSize, iconSize, includeScreen).filter((item: any) => item.type === 1)
|
||||
return await rtcEngine.getScreenCaptureSources(thumbSize, iconSize, includeScreen)
|
||||
},
|
||||
// 共享屏幕采集
|
||||
setDesktopCapturerVideo: async (targetSource: any, isComputerAudio: boolean, isFluencyPriority: boolean) => {
|
||||
|
|
@ -414,8 +420,6 @@ export const agora = {
|
|||
{},
|
||||
{
|
||||
windowFocus: true,
|
||||
enableHighLight: true,
|
||||
highLightColor: 0xFF99CC00,
|
||||
...data
|
||||
}
|
||||
);
|
||||
|
|
@ -425,8 +429,6 @@ export const agora = {
|
|||
{},
|
||||
{
|
||||
windowFocus: true,
|
||||
enableHighLight: true,
|
||||
highLightColor: 0xFF99CC00,
|
||||
...data
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -71,6 +71,8 @@ import icon47Active from '@/assets/icon47-active.png'
|
|||
import icon48 from '@/assets/icon48.png'
|
||||
import icon48Select from '@/assets/icon48-select.png'
|
||||
import icon49 from '@/assets/icon49.png'
|
||||
import icon50 from '@/assets/icon50.png'
|
||||
import icon51 from '@/assets/icon51.png'
|
||||
export default {
|
||||
loading,
|
||||
icon,
|
||||
|
|
@ -145,4 +147,6 @@ export default {
|
|||
icon48,
|
||||
icon48Select,
|
||||
icon49,
|
||||
icon50,
|
||||
icon51,
|
||||
}
|
||||
|
|
@ -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]
|
||||
};
|
||||
|
|
@ -376,6 +376,7 @@ $pagination-hover-background-color: #5575F2;
|
|||
.ant-message {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
// ant-spin-fullscreen
|
||||
.ant-spin-fullscreen {
|
||||
z-index: 10000;
|
||||
|
|
@ -384,3 +385,7 @@ $pagination-hover-background-color: #5575F2;
|
|||
.ant-picker-dropdown {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.ant-tabs {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
|
@ -70,7 +70,9 @@ export default defineConfig({
|
|||
AudioVolumeInfo,
|
||||
UserOfflineReasonType,
|
||||
ConnectionStateType,
|
||||
ConnectionChangedReasonType
|
||||
ConnectionChangedReasonType,
|
||||
LocalVideoStreamState,
|
||||
LocalVideoStreamReason
|
||||
} = require("agora-electron-sdk")
|
||||
export {
|
||||
createAgoraRtcEngine,
|
||||
|
|
@ -89,7 +91,9 @@ export default defineConfig({
|
|||
AudioVolumeInfo,
|
||||
UserOfflineReasonType,
|
||||
ConnectionStateType,
|
||||
ConnectionChangedReasonType
|
||||
ConnectionChangedReasonType,
|
||||
LocalVideoStreamState,
|
||||
LocalVideoStreamReason
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue