Compare commits

...

33 Commits

Author SHA1 Message Date
yangqiang d3fb9068c5 Merge pull request 'yangjie' (#28) from yangjie into master
Reviewed-on: #28
2024-10-29 15:15:48 +08:00
yj d16af1d2ea Merge branch 'master' of https://gitea.23544.com/marking/WGShare.Client.Electron into yangjie 2024-10-29 13:50:22 +08:00
yj 418445df55 优化 2024-10-29 12:17:41 +08:00
yj 6319565329 1 2024-10-29 11:39:22 +08:00
yj 9008cdadd1 1 2024-10-29 11:38:58 +08:00
yj f3c53678c9 优化 2024-10-29 11:30:48 +08:00
yj 368ce598d9 修复无缓存时报错 2024-10-29 11:26:11 +08:00
yj bf07fb75c4 Merge branch 'yangjie' of https://gitea.23544.com/marking/WGShare.Client.Electron into yangjie 2024-10-29 10:55:50 +08:00
yj b84ca8613f 优化 2024-10-29 10:55:45 +08:00
yangqiang f216cb47e0 Merge pull request 'master' (#25) from master into yangjie
Reviewed-on: #25
2024-10-29 09:33:09 +08:00
yj d4b0437964 优化 2024-10-29 09:29:56 +08:00
yj 67529e7efe 优化 2024-10-29 09:25:26 +08:00
yj 9c19b41e2e 优化 2024-10-28 15:34:33 +08:00
yj c15fcb9d19 回退代码 2024-10-28 15:19:17 +08:00
yj 3a43c130b7 优化 2024-10-28 15:12:28 +08:00
yj 471645b1e8 样式修改 2024-10-28 14:30:11 +08:00
yj 830dc551e6 样式修改 2024-10-28 14:24:02 +08:00
yj 2c15e96c77 优化 2024-10-28 13:43:01 +08:00
yj d925cdb74d 优化 2024-10-28 10:24:40 +08:00
yj 8b9b330ff3 优化 2024-10-28 10:08:11 +08:00
yj bcc964fe7d socket 优化 2024-10-28 10:06:04 +08:00
yj ba62fe4220 还原 2024-10-25 16:15:54 +08:00
yj 48909d9a63 优化 2024-10-25 15:50:40 +08:00
yj 42c62643e5 优化 2024-10-25 15:10:08 +08:00
yj 32af68379a 优化 2024-10-25 15:01:55 +08:00
yj 2fc1301cb9 样式优化 2024-10-25 14:24:57 +08:00
yj 42059e3c6f 优化共享冲突 2024-10-25 10:53:09 +08:00
yj a0b6893f8e 优化 2024-10-24 09:24:13 +08:00
yj a12848ff68 去除多余代码 2024-10-23 17:57:17 +08:00
yj 3a5e299975 虚拟渲染优化 2024-10-23 16:22:37 +08:00
yj 4232afef8a 优化 2024-10-23 15:42:19 +08:00
yj fbf40ccf41 虚拟背景+美颜 2024-10-23 15:32:19 +08:00
yj 2ccdb9ae38 删除多余代码 2024-10-23 08:58:31 +08:00
23 changed files with 914 additions and 369 deletions

256
main.js
View File

@ -14,6 +14,7 @@ const path = require('node:path')
const fs = require('fs'); const fs = require('fs');
const Registry = require('winreg'); const Registry = require('winreg');
const { autoUpdater, CancellationToken } = require('electron-updater'); const { autoUpdater, CancellationToken } = require('electron-updater');
const signalR = require('@microsoft/signalr');
const cancellationToken = new CancellationToken() const cancellationToken = new CancellationToken()
app.allowRendererProcessReuse = false; app.allowRendererProcessReuse = false;
let mainWindow = null; let mainWindow = null;
@ -21,6 +22,7 @@ let childWindow = {}
let isMaximized = false; let isMaximized = false;
let env; let env;
let regKey; let regKey;
let connection = null;
class AppWindow extends BrowserWindow { class AppWindow extends BrowserWindow {
constructor(config) { constructor(config) {
@ -142,6 +144,220 @@ app.on('ready', () => {
isMaximized = true; isMaximized = true;
} }
}); });
// socket
ipcMain.handle('startSignalr', (event, user) => {
startSignalr(user)
});
ipcMain.handle('offSignalr', (event) => {
if (connection) {
connection.off('ReceiveMessage');
connection.off('Operation');
connection.off('ForceExitRoom');
connection.off('AllLeave');
connection.off('ShowUser');
connection.off('RefreshView');
connection.off('UserJoined');
connection.off('UserLeave');
connection.off('OperAllMicr');
connection.off('OperMicr');
connection.off('OperCamera');
connection.off('ManagerRefresh');
connection.off('ApplyToSpeak');
connection.off('Watch');
connection.off('DriverList');
connection.off('SetDriver');
connection.off('ShowDriverList');
}
});
ipcMain.handle('onStop', (event) => {
if (connection) {
mainWindow.webContents.send('changeLocalStorage', {
isSignalr: false,
});
connection.off('Invitation');
connection.off('ForceLogout');
connection.stop()
connection = ""
}
});
ipcMain.handle('onInvoke', async (event, str, data) => {
switch (str) {
case 'sendChannelMsg':
await connection.invoke(str, data.roomNum, data.msg)
break;
case 'sendOper':
// 4:屏幕共享
await connection.invoke(str, data.roomNum, data.type)
break;
case 'getDrivers':
// 获取某个人的设备列表
await connection.invoke(str, data.uid)
break;
case 'sendDrivers':
// 发送设备列表给某个人
await connection.invoke(str, data.uid, data.driversJsonString)
break;
case 'setDrivers':
// 设置某个人的设备列表
await connection.invoke(str, data.uid, data.driversJsonString)
break;
case 'joinChannel':
// 设置某个人的设备列表
await connection.invoke(str, data.roomNum, data.enableMicr, data.enableCamera)
break;
case 'levelChannel':
// 设置某个人的设备列表
await connection.invoke(str, data.roomNum)
break;
}
});
ipcMain.handle('onOtherSignalr', (event) => {
if (connection) {
// 邀请
connection.on("Invitation", (roomNum, roomName, InviterName) => {
mainWindow.webContents.send('onOtherSignalr', {
key: 'Invitation',
roomNum, roomName, InviterName
});
});
// 退出
connection.on("ForceLogout", (msg) => {
mainWindow.webContents.send('onOtherSignalr', {
key: 'ForceLogout',
msg
});
});
}
});
ipcMain.handle('onSignalr', (event) => {
if (connection) {
// 聊天
connection.on("ReceiveMessage", (uid, userName, message, timestamp) => {
mainWindow.webContents.send('onSignalr', {
key: 'ReceiveMessage',
uid, message, userName, timestamp
})
});
// 扩展操作
connection.on("Operation", (type) => {
mainWindow.webContents.send('onSignalr', {
key: 'Operation',
type
})
});
// 移出会议
connection.on("ForceExitRoom", () => {
mainWindow.webContents.send('onSignalr', {
key: 'ForceExitRoom',
})
});
// 全员离开房间
connection.on("AllLeave", () => {
mainWindow.webContents.send('onSignalr', {
key: 'AllLeave',
})
});
// 全员看他
connection.on("ShowUser", (uid, uname, operUid, operUserName) => {
mainWindow.webContents.send('onSignalr', {
key: 'ShowUser',
uid,
uname,
operUid,
operUserName,
})
});
// 更新视图模式
connection.on("RefreshView", (type) => {
mainWindow.webContents.send('onSignalr', {
key: 'RefreshView',
type
})
});
// 用户加入频道回调
connection.on("UserJoined", (user) => {
mainWindow.webContents.send('onSignalr', {
key: 'UserJoined',
user,
})
});
// 用户退出频道回调
connection.on("UserLeave", (uid) => {
mainWindow.webContents.send('onSignalr', {
key: 'UserLeave',
uid,
})
});
// 所有用户开闭麦
connection.on("OperAllMicr", (enableMicr, uid) => {
mainWindow.webContents.send('onSignalr', {
key: 'OperAllMicr',
enableMicr,
uid
})
});
// 用户关闭开启麦克风
connection.on("OperMicr", (user, operUid) => {
mainWindow.webContents.send('onSignalr', {
key: 'OperMicr',
user,
operUid
})
});
// 用户开启关闭摄像头
connection.on("OperCamera", (user, operUid) => {
mainWindow.webContents.send('onSignalr', {
key: 'OperCamera',
user,
operUid
})
});
// 发言人用户信息刷新
connection.on("ManagerRefresh", (user, uid) => {
mainWindow.webContents.send('onSignalr', {
key: 'ManagerRefresh',
user,
uid
})
});
// 申请发言
connection.on("ApplyToSpeak", (uid, uname) => {
mainWindow.webContents.send('onSignalr', {
key: 'ApplyToSpeak',
uid,
uname
})
});
// 管理员查看随机用户
connection.on("Watch", (watchUids) => {
mainWindow.webContents.send('onSignalr', {
key: 'Watch',
watchUids
})
});
// 设备列表
connection.on("DriverList", (callerUid) => {
mainWindow.webContents.send('onSignalr', {
key: 'DriverList',
callerUid
})
});
// 设置设备
connection.on("SaveDriver", (driver) => {
mainWindow.webContents.send('onSignalr', {
key: 'SaveDriver',
driver
})
});
// 显示设备列表
connection.on("ShowDriverList", (driversJsonString) => {
mainWindow.webContents.send('onSignalr', {
key: 'ShowDriverList',
driversJsonString
})
});
}
});
// 放大缩小退出窗口 // 放大缩小退出窗口
ipcMain.handle('setViewStatus', async (event, status) => { ipcMain.handle('setViewStatus', async (event, status) => {
switch (status) { switch (status) {
@ -170,6 +386,10 @@ app.on('ready', () => {
ipcMain.handle('getIsMaximized', () => { ipcMain.handle('getIsMaximized', () => {
return mainWindow.isMaximized(); return mainWindow.isMaximized();
}); });
// 获取app路径
ipcMain.handle('getAppPath', () => {
return app.getAppPath();
});
// 获取版本号 // 获取版本号
ipcMain.handle('getVersion', () => { ipcMain.handle('getVersion', () => {
return app.getVersion(); return app.getVersion();
@ -490,3 +710,39 @@ function mainWindowCenter() {
function mainWindowHide() { function mainWindowHide() {
mainWindow.setPosition(-999999, -999999); mainWindow.setPosition(-999999, -999999);
} }
const startSignalr = async (user) => {
connection = new signalR.HubConnectionBuilder().withAutomaticReconnect()
.withUrl(`${env === 'development' ? 'http://192.168.2.9:5192' : 'https://meeting-api.23544.com/pc'}/session-manage`, {
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets,
accessTokenFactory: () => user.token
})
.build();
onStart()
mainWindow.webContents.send('changeLocalStorage', {
isSignalr: true,
reconnect: true,
});
connection.onreconnected(async () => {
mainWindow.webContents.send('changeLocalStorage', {
reconnect: true,
});
});
connection.onreconnecting(async () => {
onStart()
mainWindow.webContents.send('changeLocalStorage', {
reconnect: false,
});
});
}
const onStart = async () => {
if (connection) {
if (connection.state !== signalR.HubConnectionState.Connected) {
connection.start();
} else {
setTimeout(onStart, 3000);
}
}
}

View File

@ -91,7 +91,15 @@
"directories": { "directories": {
"output": "electron" "output": "electron"
}, },
"extraResources": [], "extraResources": [
{
"from": "src/assets/virtualBackground",
"to": "images",
"filter": [
"**/*"
]
}
],
"nsis": { "nsis": {
"oneClick": false, "oneClick": false,
"installerIcon": "build/install.ico", "installerIcon": "build/install.ico",

View File

@ -1,6 +1,32 @@
// // 在 preload 脚本中。 // // 在 preload 脚本中。
const { ipcRenderer } = require('electron') const { ipcRenderer } = require('electron')
window.electron = { window.electron = {
// socket
startSignalr: (user) => {
ipcRenderer.invoke('startSignalr', user)
},
offSignalr: () => {
ipcRenderer.invoke('offSignalr')
},
onStop: () => {
ipcRenderer.invoke('onStop')
},
onInvoke: (str, data) => {
ipcRenderer.invoke('onInvoke', str, data)
},
changeLocalStorage: (callback) => {
ipcRenderer.on('changeLocalStorage', callback)
},
onOtherSignalr: (callback) => {
ipcRenderer.invoke('onOtherSignalr')
ipcRenderer.removeAllListeners('onOtherSignalr')
ipcRenderer.on('onOtherSignalr', callback)
},
onSignalr: (callback) => {
ipcRenderer.invoke('onSignalr')
ipcRenderer.removeAllListeners('onSignalr')
ipcRenderer.on('onSignalr', callback)
},
// 设置窗口大小 // 设置窗口大小
setMainWindowSize: (config) => { setMainWindowSize: (config) => {
ipcRenderer.invoke('setMainWindowSize', { ...config }) ipcRenderer.invoke('setMainWindowSize', { ...config })
@ -17,6 +43,10 @@ window.electron = {
getIsMaximized: () => { getIsMaximized: () => {
return ipcRenderer.invoke('getIsMaximized') return ipcRenderer.invoke('getIsMaximized')
}, },
// 获取app路径
getAppPath: () => {
return ipcRenderer.invoke('getAppPath')
},
// 获取版本号 // 获取版本号
getVersion: () => { getVersion: () => {
return ipcRenderer.invoke('getVersion') return ipcRenderer.invoke('getVersion')

View File

@ -9,7 +9,6 @@ import Meeting from '@/page/Meeting/index'
import NotFound from '@/page/NotFound/index' import NotFound from '@/page/NotFound/index'
import { storage } from '@/utils' import { storage } from '@/utils'
import { message, Spin } from "antd"; import { message, Spin } from "antd";
import { onOtherSignalr, onStop, startSignalr } from "@/utils/package/signalr";
import JoinMeetingModal from "@/components/JoinMeetingModal"; import JoinMeetingModal from "@/components/JoinMeetingModal";
import UpdateModal from "@/components/UpdateModal"; import UpdateModal from "@/components/UpdateModal";
import * as CryptoJS from 'crypto-js'; import * as CryptoJS from 'crypto-js';
@ -24,7 +23,7 @@ import ChatSmallWindow from "@/page/Meeting/ChatSmallWindow";
import ChatBigWindow from "@/page/Meeting/ChatBigWindow"; import ChatBigWindow from "@/page/Meeting/ChatBigWindow";
import CurrentSpeakUserWindow from "@/page/Meeting/CurrentSpeakUserWindow"; import CurrentSpeakUserWindow from "@/page/Meeting/CurrentSpeakUserWindow";
import NoticeWindow from "@/page/Meeting/NoticeWindow"; import NoticeWindow from "@/page/Meeting/NoticeWindow";
import { getKeyOpenChildWindow, setKeyOpenChildWindow } from "./utils/package/public"; import { getKeyOpenChildWindow, setKeyOpenChildWindow, storageSeeting } from "./utils/package/public";
const fs = require('fs').promises; const fs = require('fs').promises;
const { exec } = require('child_process'); const { exec } = require('child_process');
const App: React.FC = () => { const App: React.FC = () => {
@ -54,7 +53,7 @@ const App: React.FC = () => {
storage.setItem('user', JSON.stringify(res.data)) storage.setItem('user', JSON.stringify(res.data))
storage.setItem('userLogin', true) storage.setItem('userLogin', true)
toSrc('/home') toSrc('/home')
await startSignalr() await window.electron.startSignalr(res.data)
} else { } else {
toSrc('/login') toSrc('/login')
} }
@ -121,6 +120,11 @@ const App: React.FC = () => {
} }
} }
}) })
window.electron.changeLocalStorage(async (_e: any, data: any) => {
for (const key in data) {
storage.setItem(key, data[key])
}
})
}, []) }, [])
useEffect(() => { useEffect(() => {
window.electron.onUpdate((_e: any, data: any) => { window.electron.onUpdate((_e: any, data: any) => {
@ -129,21 +133,7 @@ const App: React.FC = () => {
} }
}) })
if (!storage.getItem('setting')) { if (!storage.getItem('setting')) {
storage.setItem('setting', JSON.stringify({ storage.setItem('setting', JSON.stringify(storageSeeting))
videoDeviceId: '', //摄像头id
ecordingDeviceId: "", //输入设备id
playBackDeviceId: "", //输出设备id
ecordingVolume: '', //输入音量
playBackVolume: '', //输出音量
autoEcordingVolume: true, //是否自动调整麦克风音量
recordingFilesPath: path.resolve(__dirname, '../../Downloads') + '\\', //本地录制保存路径
shareFilesPath: path.resolve(__dirname, '../../Downloads/') + '\\', //共享文件保存路径
isShareSavePath: true, //是否下载钱询问每个文件保存的位置
closeSetting: 'hide', //关闭按钮设置
isAINoiseReduction: true, //是否开启ai降噪
aINoiseReduction: 1, // 降噪模式
isRecordingTips: true, //是否开启录制提示
}))
} }
if (!storage.getItem('openChildWindow')) { if (!storage.getItem('openChildWindow')) {
storage.setItem('openChildWindow', JSON.stringify({})) storage.setItem('openChildWindow', JSON.stringify({}))
@ -173,7 +163,7 @@ const App: React.FC = () => {
}, [state]) }, [state])
useEffect(() => { useEffect(() => {
if (location.href.indexOf('/login') !== -1) { if (location.href.indexOf('/login') !== -1) {
onStop() window.electron.onStop()
} }
}, [navigate]) }, [navigate])
} }
@ -196,7 +186,7 @@ const App: React.FC = () => {
}; };
const onEventSignalr = (): void => { const onEventSignalr = (): void => {
onOtherSignalr(async (item: any) => { window.electron.onOtherSignalr(async (_e: any, item: any) => {
switch (item.key) { switch (item.key) {
case 'Invitation': case 'Invitation':
window.electron.joinNotification({ window.electron.joinNotification({

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 921 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -1,6 +1,5 @@
import styles from '@/components/EquipmentManagement/index.module.scss' import styles from '@/components/EquipmentManagement/index.module.scss'
import { getKeyOpenChildWindow } from '@/utils/package/public'; import { getKeyOpenChildWindow } from '@/utils/package/public';
import { onInvoke } from '@/utils/package/signalr';
import { Button, Modal, Select, Slider, message } from 'antd'; import { Button, Modal, Select, Slider, message } from 'antd';
import { useState, useImperativeHandle, forwardRef } from "react"; import { useState, useImperativeHandle, forwardRef } from "react";
const EquipmentManagement = forwardRef((props: any, ref: any) => { const EquipmentManagement = forwardRef((props: any, ref: any) => {
@ -12,7 +11,7 @@ const EquipmentManagement = forwardRef((props: any, ref: any) => {
if (isOpen) { if (isOpen) {
props.getDriver?.(uid) props.getDriver?.(uid)
} else { } else {
await onInvoke('getDrivers', { await window.electron.onInvoke('getDrivers', {
uid uid
}) })
} }
@ -99,7 +98,7 @@ const EquipmentManagement = forwardRef((props: any, ref: any) => {
driversJsonString: JSON.stringify(deviceInfo) driversJsonString: JSON.stringify(deviceInfo)
}) })
} else { } else {
await onInvoke('setDrivers', { await window.electron.onInvoke('setDrivers', {
uid: callerUid, uid: callerUid,
driversJsonString: JSON.stringify(deviceInfo) driversJsonString: JSON.stringify(deviceInfo)
}) })

View File

@ -1,6 +1,6 @@
// 设置向导 // 设置向导
.stupWizard { .stupWizard {
height: 70vh; height: 90vh;
display: flex; display: flex;
.stupWizardLeft { .stupWizardLeft {
@ -68,6 +68,10 @@
} }
.videoComponents { .videoComponents {
>div {
margin-bottom: 10px;
}
>div:nth-child(1) { >div:nth-child(1) {
width: 100%; width: 100%;
height: 296px; height: 296px;
@ -88,7 +92,6 @@
>div:nth-child(2) { >div:nth-child(2) {
width: 100%; width: 100%;
margin-top: 10px;
display: flex; display: flex;
align-items: center; align-items: center;
@ -96,6 +99,82 @@
color: white; color: white;
} }
} }
>div:nth-child(3) {}
.beautyEffect {
>span {
color: white;
}
>div {
display: flex;
flex-wrap: wrap;
>div {
border: 1px transparent solid;
width: calc(100% /6);
padding: 5px;
box-sizing: border-box;
cursor: pointer;
>div {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
>label {
color: white;
background-color: rgba(0, 0, 0, 0.5);
}
>input {
position: absolute;
visibility: hidden;
}
}
>img {
width: 100%;
}
}
.active {
border: 1px white solid;
}
}
}
.otherVideoSeeting {
>div {
margin-bottom: 20px;
>span {
color: white;
}
>div {
margin-left: 40px;
flex-grow: 1;
>div {
display: flex;
align-items: center;
>span {
color: white;
white-space: nowrap;
}
>div {
flex-grow: 1;
margin-left: 10px;
}
}
}
}
}
} }
.audioComponents { .audioComponents {

View File

@ -6,12 +6,15 @@ import { agora } from '@/utils/package/agora'
import { CloseOutlined, LoadingOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import { CloseOutlined, LoadingOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import { storage } from '@/utils'; import { storage } from '@/utils';
import path from 'path'; import path from 'path';
import { role } from '@/config/role';
import { storageSeeting } from '@/utils/package/public';
let meetingUserInfo = '' as any;
const fs = require('fs').promises; const fs = require('fs').promises;
const { exec } = require('child_process'); const { exec } = require('child_process');
const StupWizard = forwardRef((props: any, ref: any) => { const StupWizard = forwardRef((_props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeModal: (index: number = 0) => { changeModal: (index: number = 0, data: any) => {
meetingUserInfo = data;
if (location.hash.indexOf('/meeting') === -1) { if (location.hash.indexOf('/meeting') === -1) {
agora.init() agora.init()
} }
@ -23,47 +26,9 @@ const StupWizard = forwardRef((props: any, ref: any) => {
return res return res
}) })
const setting = JSON.parse(storage.getItem('setting') as string) const setting = JSON.parse(storage.getItem('setting') as string)
const obj: any = { for (const key in storageSeeting) {
videoDeviceId: '', //摄像头id
ecordingDeviceId: "", //输入设备id
playBackDeviceId: "", //输出设备id
ecordingVolume: '', //输入音量
playBackVolume: '', //输出音量
autoEcordingVolume: true, //是否自动调整麦克风音量
recordingFilesPath: path.resolve(__dirname, '../../Downloads') + '\\', //本地录制保存路径
shareFilesPath: path.resolve(__dirname, '../../Downloads/') + '\\', //共享文件保存路径
isShareSavePath: true, //是否下载钱询问每个文件保存的位置
closeSetting: 'hide', //关闭按钮设置
isAINoiseReduction: true, //是否开启ai降噪
aINoiseReduction: 1, // 降噪模式
isRecordingTips: true, //是否开启录制提示
beautyEffect: { //美颜效果
isBeautyEffect: false, //是否打开美颜
lighteningContrastLevel: 1, //对比度
lighteningLevel: 0, //美白程度
smoothnessLevel: 0, //磨皮程度
rednessLevel: 0, //红润度
sharpnessLevel: 0, //锐化程度
},
colorEnhancement: { //色彩增强
isColorEnhancement: false, //是否打开色彩增强
strengthLevel: 0.5, //色彩增强程度
skinProtectLevel: 1, //肤色保护程度
},
darkLightEnhancement: { //暗光增强
isDarkLightEnhancement: false, //是否打开暗光增强
level: 0, //暗光增强等级
mode: 0, //暗光增强模式
},
virtualBackground: { //虚拟背景
isVirtualBackground: false, //是否打开虚拟背景
color: '0xFFFFFF', // 纯色
sourceIndex: '', // 背景图下标
},
}
for (const key in obj) {
if (setting[key] === undefined) { if (setting[key] === undefined) {
setting[key] = obj[key] setting[key] = storageSeeting[key]
} }
} }
storage.setItem('setting', JSON.stringify(setting)) storage.setItem('setting', JSON.stringify(setting))
@ -192,10 +157,81 @@ const VideoComponents = () => {
list: [], list: [],
item: null, item: null,
}); });
const [videoKey, setVideoKey] = useState('beautyEffect');
const [virtualBackgroundImg] = useState([
ImageUrl.virtualBackground1,
ImageUrl.virtualBackground2,
ImageUrl.virtualBackground3,
ImageUrl.virtualBackground4,
ImageUrl.virtualBackground5,
ImageUrl.virtualBackground6,
]);
const [beautyEffect, setBeautyEffect] = useState<any>({
isBeautyEffect: false, //是否打开美颜
lighteningContrastLevel: 1, //对比度
lighteningLevel: 0, //美白程度
smoothnessLevel: 0, //磨皮程度
rednessLevel: 0, //红润度
sharpnessLevel: 0, //锐化程度
});
const [colorEnhancement, setColorEnhancement] = useState<any>({
isColorEnhancement: false, //是否打开色彩增强
strengthLevel: 0.5, //色彩增强程度
skinProtectLevel: 1, //肤色保护程度
});
const [darkLightEnhancement, setDarkLightEnhancement] = useState<any>({
isDarkLightEnhancement: false, //是否打开暗光增强
level: 0, //暗光增强等级
mode: 0, //暗光增强模式
});
const [virtualBackground, setVirtualBackground] = useState<any>({
isVirtualBackground: false, //是否打开虚拟背景
color: '0xFFFFFF', // 纯色
sourceIndex: '', // 背景图下标
});
const setting = JSON.parse(storage.getItem('setting') as string) const setting = JSON.parse(storage.getItem('setting') as string)
useEffect(() => { useEffect(() => {
setBeautyEffect(setting.beautyEffect)
setColorEnhancement(setting.colorEnhancement)
setDarkLightEnhancement(setting.darkLightEnhancement)
setVirtualBackground(setting.virtualBackground)
getVideoDeviceList() getVideoDeviceList()
}, []); }, []);
useEffect(() => {
agora.setBeautyEffectOptions(beautyEffect.isBeautyEffect, beautyEffect)
}, [beautyEffect]);
useEffect(() => {
agora.setColorEnhanceOptions(colorEnhancement.isColorEnhancement, colorEnhancement)
}, [colorEnhancement]);
useEffect(() => {
agora.setLowlightEnhanceOptions(darkLightEnhancement.isDarkLightEnhancement, darkLightEnhancement)
}, [darkLightEnhancement]);
useEffect(() => {
if (typeof virtualBackground.sourceIndex === 'number') {
if (import.meta.env.VITE_ENV === 'development') {
window.electron.getAppPath().then((res: string) => {
const imagePath = path.join(res, 'src', 'assets', 'virtualBackground', `${virtualBackground.sourceIndex + 1}.png`);
agora.enableVirtualBackground(virtualBackground.isVirtualBackground, {
source: imagePath,
background_source_type: 2,
color: Number(virtualBackground.color),
})
})
} else {
const imagePath = path.join((process as any).resourcesPath, 'images', `${virtualBackground.sourceIndex + 1}.png`);
agora.enableVirtualBackground(virtualBackground.isVirtualBackground, {
source: imagePath,
background_source_type: 2,
color: Number(virtualBackground.color),
})
}
} else {
agora.enableVirtualBackground(virtualBackground.isVirtualBackground, {
background_source_type: 1,
color: Number(virtualBackground.color),
})
}
}, [virtualBackground]);
const getVideoDeviceList = async (): Promise<void> => { const getVideoDeviceList = async (): Promise<void> => {
const userInfo = JSON.parse(storage.getItem('user') as string) const userInfo = JSON.parse(storage.getItem('user') as string)
agora.getVideoDeviceManager().then(async (res) => { agora.getVideoDeviceManager().then(async (res) => {
@ -261,17 +297,267 @@ const VideoComponents = () => {
<Select <Select
placeholder={videoDeviceManager.list.length ? '请选择设备' : '未检测到摄像头'} placeholder={videoDeviceManager.list.length ? '请选择设备' : '未检测到摄像头'}
options={videoDeviceManager.list} style={{ flexGrow: 1, marginRight: '10px' }} options={videoDeviceManager.list} style={{ flexGrow: 1, marginRight: '10px' }}
value={videoDeviceManager.item} onChange={async (e) => { value={videoDeviceManager.item || null} onChange={async (e) => {
setting.videoDeviceId = e;
storage.setItem('setting', JSON.stringify(setting))
setVideoDeviceManager({ setVideoDeviceManager({
...videoDeviceManager, ...videoDeviceManager,
item: e item: e
}) })
agora.setVideoDeviceManager(e) agora.setVideoDeviceManager(e)
if (!setting.videoDeviceId) {
const userInfo = JSON.parse(storage.getItem('user') as string)
await agora.startPreview('videoPreview', Number(userInfo.screenShareId))
}
setting.videoDeviceId = e;
storage.setItem('setting', JSON.stringify(setting))
}} /> }} />
</div> </div>
<div>
<Radio.Group buttonStyle="solid" value={videoKey} onChange={(e) => {
setVideoKey(e.target.value)
}}>
<Radio.Button value="beautyEffect"></Radio.Button>
<Radio.Button value="colorEnhancement"></Radio.Button>
<Radio.Button value="darkLightEnhancement"></Radio.Button>
<Radio.Button value="virtualBackground"></Radio.Button>
</Radio.Group>
</div>
{videoKey === 'beautyEffect' ? <div className={styles.beautyEffect}>
<span>
<Checkbox style={{ marginRight: '10px' }} checked={virtualBackground.isVirtualBackground} onChange={(e) => {
setting.virtualBackground.isVirtualBackground = e.target.checked;
storage.setItem('setting', JSON.stringify(setting))
setVirtualBackground({
...virtualBackground,
isVirtualBackground: e.target.checked
})
}}>
</Checkbox>
</span>
<div>
<div className={virtualBackground.sourceIndex === '' ? styles.active : ''}>
<div style={{ backgroundColor: '#' + Number(virtualBackground.color)?.toString(16).padStart(6, '0') }} onClick={() => {
setting.virtualBackground.sourceIndex = '';
storage.setItem('setting', JSON.stringify(setting))
setVirtualBackground({
...virtualBackground,
sourceIndex: ''
})
}}>
<input type="color" name="" id="color-select" onChange={(e) => {
let hexInt = parseInt('0x' + e.target.value.split('#')[1], 16)
setting.virtualBackground.color = hexInt
storage.setItem('setting', JSON.stringify(setting))
setVirtualBackground({
...virtualBackground,
color: hexInt
})
}} />
<label htmlFor='color-select'>()</label>
</div>
</div>
{
virtualBackgroundImg.map((item, index) => {
return (
<div key={index} className={virtualBackground.sourceIndex === index ? styles.active : ''} onClick={() => {
setting.virtualBackground.sourceIndex = index;
storage.setItem('setting', JSON.stringify(setting))
setVirtualBackground({
...virtualBackground,
sourceIndex: index
})
}}>
<img src={item} alt="" />
</div>
)
})
}
</div>
</div> : null}
<div className={styles.otherVideoSeeting}>
{videoKey === 'colorEnhancement' ? <div>
<span>
<Checkbox style={{ marginRight: '10px' }} checked={beautyEffect.isBeautyEffect} onChange={(e) => {
setting.beautyEffect.isBeautyEffect = e.target.checked;
storage.setItem('setting', JSON.stringify(setting))
setBeautyEffect({
...beautyEffect,
isBeautyEffect: e.target.checked
})
}}>
</Checkbox>
</span>
<div>
<div>
<span></span>
<div>
<Radio.Group disabled={!beautyEffect.isBeautyEffect} onChange={(e: any) => {
setting.beautyEffect.lighteningContrastLevel = e.target.value;
storage.setItem('setting', JSON.stringify(setting))
setBeautyEffect({
...beautyEffect,
lighteningContrastLevel: e.target.value
})
}} style={{ flexShrink: 0, margin: '10px 0' }} value={beautyEffect.lighteningContrastLevel}>
<Radio value={0}></Radio>
<Radio value={1}></Radio>
<Radio value={2}></Radio>
</Radio.Group>
</div>
</div>
<div>
<span></span>
<div>
<Slider value={beautyEffect.lighteningLevel} step={0.1} style={{ width: '100%' }} max={1}
onChange={async (e) => {
setting.beautyEffect.lighteningLevel = e
storage.setItem('setting', JSON.stringify(setting))
setBeautyEffect({
...beautyEffect,
lighteningLevel: e
})
}} disabled={!beautyEffect.isBeautyEffect} />
</div>
</div>
<div>
<span></span>
<div>
<Slider value={beautyEffect.smoothnessLevel} step={0.1} style={{ width: '100%' }} max={1}
onChange={async (e) => {
setting.beautyEffect.smoothnessLevel = e
storage.setItem('setting', JSON.stringify(setting))
setBeautyEffect({
...beautyEffect,
smoothnessLevel: e
})
}} disabled={!beautyEffect.isBeautyEffect} />
</div>
</div>
<div>
<span></span>
<div>
<Slider value={beautyEffect.rednessLevel} step={0.1} style={{ width: '100%' }} max={1}
onChange={async (e) => {
setting.beautyEffect.rednessLevel = e
storage.setItem('setting', JSON.stringify(setting))
setBeautyEffect({
...beautyEffect,
rednessLevel: e
})
}} disabled={!beautyEffect.isBeautyEffect} />
</div>
</div>
<div>
<span></span>
<div>
<Slider value={beautyEffect.sharpnessLevel} step={0.1} style={{ width: '100%' }} max={1}
onChange={async (e) => {
setting.beautyEffect.sharpnessLevel = e
storage.setItem('setting', JSON.stringify(setting))
setBeautyEffect({
...beautyEffect,
sharpnessLevel: e
})
}} disabled={!beautyEffect.isBeautyEffect} />
</div>
</div>
</div>
</div> : null}
{videoKey === 'darkLightEnhancement' ? <div>
<span>
<Checkbox style={{ marginRight: '10px' }} checked={colorEnhancement.isColorEnhancement} onChange={(e) => {
setting.colorEnhancement.isColorEnhancement = e.target.checked;
storage.setItem('setting', JSON.stringify(setting))
setColorEnhancement({
...colorEnhancement,
isColorEnhancement: e.target.checked
})
}}>
</Checkbox>
</span>
<div>
<div>
<span></span>
<div>
<Slider value={colorEnhancement.strengthLevel} step={0.1} style={{ width: '100%' }} max={1}
onChange={async (e) => {
setting.colorEnhancement.strengthLevel = e
storage.setItem('setting', JSON.stringify(setting))
setColorEnhancement({
...colorEnhancement,
strengthLevel: e
})
}} disabled={!colorEnhancement.isColorEnhancement} />
</div>
</div>
<div>
<span></span>
<div>
<Slider value={colorEnhancement.skinProtectLevel} step={0.1} style={{ width: '100%' }} max={1}
onChange={async (e) => {
setting.colorEnhancement.skinProtectLevel = e
storage.setItem('setting', JSON.stringify(setting))
setColorEnhancement({
...colorEnhancement,
skinProtectLevel: e
})
}} disabled={!colorEnhancement.isColorEnhancement} />
</div>
</div>
</div>
</div> : null}
{videoKey === 'virtualBackground' ? <div>
<span>
<Checkbox style={{ marginRight: '10px' }} checked={darkLightEnhancement.isDarkLightEnhancement} onChange={(e) => {
setting.darkLightEnhancement.isDarkLightEnhancement = e.target.checked;
storage.setItem('setting', JSON.stringify(setting))
setDarkLightEnhancement({
...darkLightEnhancement,
isDarkLightEnhancement: e.target.checked
})
}}>
</Checkbox>
</span>
<div>
<div>
<span></span>
<div>
<Radio.Group disabled={!darkLightEnhancement.isDarkLightEnhancement} onChange={(e: any) => {
setting.darkLightEnhancement.level = e.target.value;
storage.setItem('setting', JSON.stringify(setting))
setDarkLightEnhancement({
...darkLightEnhancement,
level: e.target.value
})
}} style={{ flexShrink: 0, margin: '10px 0' }} value={darkLightEnhancement.level}>
<Radio value={0}>()</Radio>
<Radio value={1}></Radio>
</Radio.Group>
</div>
</div>
<div>
<span></span>
<div>
<Radio.Group disabled={!darkLightEnhancement.isDarkLightEnhancement} onChange={(e: any) => {
setting.darkLightEnhancement.mode = e.target.value;
storage.setItem('setting', JSON.stringify(setting))
setDarkLightEnhancement({
...darkLightEnhancement,
mode: e.target.value
})
}} style={{ flexShrink: 0, margin: '10px 0' }} value={darkLightEnhancement.mode}>
<Radio value={0}>()SDK </Radio>
<Radio value={1}></Radio>
</Radio.Group>
</div>
</div>
</div>
</div> : null}
</div>
</div> </div>
</div> </div>
</> </>
@ -296,13 +582,16 @@ const AudioComponents = () => {
getAudioMediaList() getAudioMediaList()
agora.registerEventHandler({ agora.registerEventHandler({
onAudioVolumeIndication: (speakers: any) => { onAudioVolumeIndication: (speakers: any) => {
const totalVolume = speakers.length ? speakers[0].volume : 0 speakers.forEach((item: any) => {
const percentage = (totalVolume / 255) * 100 if (item.uid === 0 || item.uid === 1) {
const percentage = (item.volume / 255) * 100
const dom = document.getElementById('deviceTest') as any; const dom = document.getElementById('deviceTest') as any;
if (dom) { if (dom) {
dom.style.width = `${percentage}%` dom.style.width = `${percentage}%`
} }
} }
});
}
}) })
}, []); }, []);
const getAudioMediaList = async (): Promise<void> => { const getAudioMediaList = async (): Promise<void> => {
@ -351,7 +640,7 @@ const AudioComponents = () => {
<Select <Select
placeholder={audioDeviceManager.ecordingList.length ? '请选择设备' : '未检测到麦克风'} placeholder={audioDeviceManager.ecordingList.length ? '请选择设备' : '未检测到麦克风'}
options={audioDeviceManager.ecordingList} style={{ flexGrow: 1 }} options={audioDeviceManager.ecordingList} style={{ flexGrow: 1 }}
value={audioDeviceManager.ecordingItem} onChange={async (e) => { value={audioDeviceManager.ecordingItem || null} onChange={async (e) => {
setting.ecordingDeviceId = e; setting.ecordingDeviceId = e;
storage.setItem('setting', JSON.stringify(setting)) storage.setItem('setting', JSON.stringify(setting))
setAudioDeviceManager({ setAudioDeviceManager({
@ -360,7 +649,11 @@ const AudioComponents = () => {
}) })
agora.setRecordingDevice(e) agora.setRecordingDevice(e)
}} />; }} />;
{audioDeviceManager.ecordingActive ? <div onClick={() => { {(meetingUserInfo &&
(role.ID.includes(meetingUserInfo.roleId) || meetingUserInfo.isRoomManager) &&
meetingUserInfo.enableMicr) ||
location.hash.indexOf('/meeting') === -1
? audioDeviceManager.ecordingActive ? <div onClick={() => {
agora.stopRecordingDeviceTest() agora.stopRecordingDeviceTest()
setAudioDeviceManager({ setAudioDeviceManager({
...audioDeviceManager, ...audioDeviceManager,
@ -375,7 +668,7 @@ const AudioComponents = () => {
playBackActive: false, playBackActive: false,
ecordingActive: true, ecordingActive: true,
}) })
}}></div>} }}></div> : null}
</div> </div>
{audioDeviceManager.ecordingActive ? <div className={styles.audioComponentsVolume}> {audioDeviceManager.ecordingActive ? <div className={styles.audioComponentsVolume}>
<img src={ImageUrl.icon36} alt="" /> <img src={ImageUrl.icon36} alt="" />
@ -448,7 +741,7 @@ const AudioComponents = () => {
<Select <Select
placeholder={audioDeviceManager.playBackList.length ? '请选择设备' : '未检测到麦克风'} placeholder={audioDeviceManager.playBackList.length ? '请选择设备' : '未检测到麦克风'}
options={audioDeviceManager.playBackList} style={{ flexGrow: 1 }} options={audioDeviceManager.playBackList} style={{ flexGrow: 1 }}
value={audioDeviceManager.playBackItem} onChange={async (e) => { value={audioDeviceManager.playBackItem || null} onChange={async (e) => {
setting.playBackDeviceId = e; setting.playBackDeviceId = e;
storage.setItem('setting', JSON.stringify(setting)) storage.setItem('setting', JSON.stringify(setting))
setAudioDeviceManager({ setAudioDeviceManager({

View File

@ -169,3 +169,9 @@
} }
} }
} }
@media screen and (max-width: 1154px) {
.indexContentListItem {
width: calc(98% / 2) !important;
}
}

View File

@ -7,7 +7,6 @@ import { storage } from '@/utils'
import { GetCheckUser, PostAnonLogin, PostLogin } from '@/api/Login' import { GetCheckUser, PostAnonLogin, PostLogin } from '@/api/Login'
import * as CryptoJS from 'crypto-js'; import * as CryptoJS from 'crypto-js';
import ImageUrl from '@/utils/package/imageUrl' import ImageUrl from '@/utils/package/imageUrl'
import { startSignalr } from '@/utils/package/signalr';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { GetCheckoutRoomNum, GetRoomInfo, GetRoomRtcToken } from '@/api/Home/Index'; import { GetCheckoutRoomNum, GetRoomInfo, GetRoomRtcToken } from '@/api/Home/Index';
const Login: React.FC = () => { const Login: React.FC = () => {
@ -164,7 +163,7 @@ const Login: React.FC = () => {
height: Math.ceil(res.height / 1.3), height: Math.ceil(res.height / 1.3),
}) })
}) })
startSignalr() window.electron.startSignalr(res.data)
navigate('/home') navigate('/home')
} }
}) })
@ -321,7 +320,7 @@ const Login: React.FC = () => {
if (res.code == 200) { if (res.code == 200) {
storage.setItem('user', JSON.stringify(res.data)) storage.setItem('user', JSON.stringify(res.data))
storage.setItem('userLogin', true) storage.setItem('userLogin', true)
await startSignalr() await window.electron.startSignalr(res.data)
getRoomRtcToken(anonInfo.roomNum, (options: any) => { getRoomRtcToken(anonInfo.roomNum, (options: any) => {
if (options) { if (options) {
GetRoomInfo(anonInfo.roomNum).then(async (res) => { GetRoomInfo(anonInfo.roomNum).then(async (res) => {

View File

@ -11,7 +11,6 @@ import { storage } from '@/utils';
import { GetRoomUser, PostOpenMicr, PostOpenCamera, GetLeaveAll, PostRoomManager, DeleteRoomManager, GetRoomKickout, GetShowUser, PostShowUser, PostMuteAll, GetRoomUserItem, GetApplySpeak } from '@/api/Meeting'; import { GetRoomUser, PostOpenMicr, PostOpenCamera, GetLeaveAll, PostRoomManager, DeleteRoomManager, GetRoomKickout, GetShowUser, PostShowUser, PostMuteAll, GetRoomUserItem, GetApplySpeak } from '@/api/Meeting';
import ImageUrl from '@/utils/package/imageUrl' import ImageUrl from '@/utils/package/imageUrl'
import { agora } from '@/utils/package/agora' import { agora } from '@/utils/package/agora'
import { onInvoke, onSignalr, offSignalr } from '@/utils/package/signalr';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import durationPlugin from 'dayjs/plugin/duration'; import durationPlugin from 'dayjs/plugin/duration';
import { AudioVolumeInfo, ConnectionChangedReasonType, ConnectionStateType, LocalVideoStreamReason, LocalVideoStreamState, 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';
@ -276,12 +275,12 @@ const Meeting: React.FC = () => {
}) })
break; break;
case 'userListWindowEquipmentManagement': case 'userListWindowEquipmentManagement':
await onInvoke('getDrivers', { await window.electron.onInvoke('getDrivers', {
uid: userListWindowEquipmentManagement.uid, uid: userListWindowEquipmentManagement.uid,
}) })
break; break;
case 'userListWindowSetEquipmentManagement': case 'userListWindowSetEquipmentManagement':
await onInvoke('setDrivers', { await window.electron.onInvoke('setDrivers', {
uid: userListWindowSetEquipmentManagement.uid, uid: userListWindowSetEquipmentManagement.uid,
driversJsonString: userListWindowSetEquipmentManagement.driversJsonString driversJsonString: userListWindowSetEquipmentManagement.driversJsonString
}) })
@ -410,7 +409,6 @@ const Meeting: React.FC = () => {
firstFooterList[0][0].active = !state.enableMicr firstFooterList[0][0].active = !state.enableMicr
firstFooterList[0][1].title = state.enableCamera ? '关闭视频' : '开启视频' firstFooterList[0][1].title = state.enableCamera ? '关闭视频' : '开启视频'
firstFooterList[0][1].active = !state.enableCamera firstFooterList[0][1].active = !state.enableCamera
agora.muteLocalVideoStream(userInfo, state.enableMicr, state.enableCamera)
setFooterList(firstFooterList) setFooterList(firstFooterList)
setTimeout(async () => { setTimeout(async () => {
const setting = await JSON.parse(storage.getItem('setting') as string); const setting = await JSON.parse(storage.getItem('setting') as string);
@ -534,7 +532,7 @@ const Meeting: React.FC = () => {
}, [currentVideoId, isShare]); }, [currentVideoId, isShare]);
useEffect(() => { useEffect(() => {
onSignalr(async (item: any) => { window.electron.onSignalr(async (_e: any, item: any) => {
const setting = JSON.parse(storage.getItem('setting') as string) const setting = JSON.parse(storage.getItem('setting') as string)
switch (item.key) { switch (item.key) {
// 聊天 // 聊天
@ -573,7 +571,7 @@ const Meeting: React.FC = () => {
setIsShare((res: any) => { setIsShare((res: any) => {
if (userInfo.screenShareId === String(res)) { if (userInfo.screenShareId === String(res)) {
changeStatusList({ changeStatusList({
title: '停止共享' title: '共享冲突停止共享',
}, 1, 0) }, 1, 0)
} }
return res return res
@ -789,7 +787,7 @@ const Meeting: React.FC = () => {
playBackDeviceId: res[1].ecordingList.find((row: any) => row.deviceId === setting.playBackDeviceId) ? setting.playBackDeviceId : res[1].playBackList.length ? res[1].playBackList[0].deviceId : null, playBackDeviceId: res[1].ecordingList.find((row: any) => row.deviceId === setting.playBackDeviceId) ? setting.playBackDeviceId : res[1].playBackList.length ? res[1].playBackList[0].deviceId : null,
ecordingVolume: res[1].ecordingVolume ecordingVolume: res[1].ecordingVolume
} }
onInvoke('sendDrivers', { window.electron.onInvoke('sendDrivers', {
uid: item.callerUid, uid: item.callerUid,
driversJsonString: JSON.stringify(data) driversJsonString: JSON.stringify(data)
}) })
@ -825,7 +823,7 @@ const Meeting: React.FC = () => {
} }
}) })
return () => { return () => {
offSignalr() window.electron.offSignalr()
} }
}, []) }, [])
@ -930,6 +928,7 @@ const Meeting: React.FC = () => {
// 声网初始化 // 声网初始化
const agoraInit = async () => { const agoraInit = async () => {
await agora.init(true) await agora.init(true)
await agora.muteLocalVideoStream(userInfo, state.enableMicr, state.enableCamera)
await getJoin(state.enableMicr, state.enableCamera) await getJoin(state.enableMicr, state.enableCamera)
agora.registerEventHandler({ agora.registerEventHandler({
onJoinChannelSuccess: async (connection: RtcConnection, _elapsed: number) => { onJoinChannelSuccess: async (connection: RtcConnection, _elapsed: number) => {
@ -963,6 +962,7 @@ const Meeting: React.FC = () => {
} }
}, },
onUserOffline: async (connection: RtcConnection, remoteUid: number, _reason: UserOfflineReasonType) => { onUserOffline: async (connection: RtcConnection, remoteUid: number, _reason: UserOfflineReasonType) => {
await agora.destroyRendererByConfig(Number(remoteUid), connection.channelId)
if (connection.channelId === state.channelId) { if (connection.channelId === state.channelId) {
if (String(remoteUid).length === 9) { if (String(remoteUid).length === 9) {
setIsShare(null) setIsShare(null)
@ -1380,6 +1380,7 @@ const Meeting: React.FC = () => {
}) })
break; break;
case '停止共享': case '停止共享':
case '共享冲突停止共享':
await getUserRoomInfo().then(async (res) => { await getUserRoomInfo().then(async (res) => {
if (res) { if (res) {
await stopScreenCapture() await stopScreenCapture()
@ -1387,7 +1388,9 @@ const Meeting: React.FC = () => {
message.error(msgTips) message.error(msgTips)
} }
}) })
if (row.title === '停止共享') {
await allUserLook(userInfo.uid, userInfo.userName) await allUserLook(userInfo.uid, userInfo.userName)
}
break; break;
case '静音': case '静音':
await postOpenMicr(false, userInfo.uid) await postOpenMicr(false, userInfo.uid)
@ -1424,7 +1427,10 @@ const Meeting: React.FC = () => {
}) })
break; break;
case '设置': case '设置':
stupWizardRef.current.changeModal() await getUserRoomInfo().then(async (res) => {
stupWizardRef.current.changeModal(0, res)
})
break; break;
case '邀请人员': case '邀请人员':
await getUserRoomInfo().then(async (res) => { await getUserRoomInfo().then(async (res) => {
@ -1550,12 +1556,9 @@ const Meeting: React.FC = () => {
// 退出房间 // 退出房间
const leaveChannel = async (bool: boolean = true): Promise<void> => { const leaveChannel = async (bool: boolean = true): Promise<void> => {
await stopRecorderMedia() await stopRecorderMedia()
try {
if (bool) { if (bool) {
await getLeave() await getLeave()
} }
} catch (error) {
}
await stopScreenCapture() await stopScreenCapture()
await agora.leaveChannel() await agora.leaveChannel()
setTimeout(() => { setTimeout(() => {
@ -1715,7 +1718,7 @@ const Meeting: React.FC = () => {
break; break;
case 'reconnect': case 'reconnect':
if (e.value == true) { if (e.value == true) {
await onInvoke('joinChannel', { await window.electron.onInvoke('joinChannel', {
roomNum: state.channelId, roomNum: state.channelId,
enableMicr: !footerList[0][0].active, enableMicr: !footerList[0][0].active,
enableCamera: !footerList[0][1].active enableCamera: !footerList[0][1].active
@ -1747,7 +1750,7 @@ const Meeting: React.FC = () => {
const sendMsg = (text?: string): void => { const sendMsg = (text?: string): void => {
let msg = text ? text : textMsg; let msg = text ? text : textMsg;
if (msg) { if (msg) {
onInvoke('sendChannelMsg', { window.electron.onInvoke('sendChannelMsg', {
roomNum: state.channelId, roomNum: state.channelId,
msg: msg, msg: msg,
}) })
@ -1991,7 +1994,7 @@ const Meeting: React.FC = () => {
// enableMicr, // enableMicr,
// enableCamera // enableCamera
// }) // })
await onInvoke('joinChannel', { await window.electron.onInvoke('joinChannel', {
roomNum: state.channelId, roomNum: state.channelId,
enableMicr, enableMicr,
enableCamera enableCamera
@ -2003,7 +2006,7 @@ const Meeting: React.FC = () => {
// await GetLeave({ // await GetLeave({
// roomNum: state.channelId, // roomNum: state.channelId,
// }) // })
await onInvoke('levelChannel', { await window.electron.onInvoke('levelChannel', {
roomNum: state.channelId roomNum: state.channelId
}) })
} }
@ -2786,13 +2789,13 @@ const Meeting: React.FC = () => {
setIsSharePopConfirm(false) setIsSharePopConfirm(false)
setIsSharedScreenModal(false) setIsSharedScreenModal(false)
}} style={{ backgroundColor: '#31353A', marginRight: '14px' }}></Button> }} style={{ backgroundColor: '#31353A', marginRight: '14px' }}></Button>
{isShare ? <Popconfirm {isShare && sharedScreenItem ? <Popconfirm
title="提示" title="提示"
description={`这将停止[${isShareUser?.userName}]的共享,是否继续?`} description={`这将停止[${isShareUser?.userName}]的共享,是否继续?`}
open={isSharePopConfirm} open={isSharePopConfirm}
onConfirm={async () => { onConfirm={async () => {
setIsSharePopConfirm(false) setIsSharePopConfirm(false)
await onInvoke('sendOper', { await window.electron.onInvoke('sendOper', {
roomNum: state.channelId, roomNum: state.channelId,
type: 4, type: 4,
}) })

8
src/render.d.ts vendored
View File

@ -1,9 +1,17 @@
// electron-env.d.ts // electron-env.d.ts
export interface IElectronAPI { export interface IElectronAPI {
startSignalr: (user: any) => Promise<void>;
offSignalr: () => Promise<void>;
onStop: () => Promise<void>;
onInvoke: (str: string, data: any) => Promise<void>;
changeLocalStorage: (callBack: Function) => void;
onOtherSignalr: (callBack: Function) => void;
onSignalr: (callBack: Function) => void;
setMainWindowSize: (config: any) => void; setMainWindowSize: (config: any) => void;
getWindowSize: () => any; getWindowSize: () => any;
setViewStatus: (status: 'quit' | 'maximize' | 'minimize' | 'unmaximize' | 'hide' | 'show') => void; setViewStatus: (status: 'quit' | 'maximize' | 'minimize' | 'unmaximize' | 'hide' | 'show') => void;
getIsMaximized: () => Promise<boolean>; getIsMaximized: () => Promise<boolean>;
getAppPath: () => Promise<string>;
setWriteText: (text: string) => void; setWriteText: (text: string) => void;
onQuit: (callBack: Function) => void; onQuit: (callBack: Function) => void;
onUpdate: (callBack: Function) => void; onUpdate: (callBack: Function) => void;

View File

@ -17,11 +17,16 @@ import {
ConnectionStateType, ConnectionStateType,
ConnectionChangedReasonType, ConnectionChangedReasonType,
LocalVideoStreamReason, LocalVideoStreamReason,
LocalVideoStreamState LocalVideoStreamState,
BeautyOptions,
ColorEnhanceOptions,
LowlightEnhanceOptions,
VirtualBackgroundSource
} from "agora-electron-sdk"; } from "agora-electron-sdk";
import { GetRoomRtcToken, GetAgoraConf } from "@/api/Home/Index"; import { GetRoomRtcToken, GetAgoraConf } from "@/api/Home/Index";
import { storage } from '@/utils'; import { storage } from '@/utils';
import { role } from "@/config/role"; import { role } from "@/config/role";
import path from "path";
const option: any = { const option: any = {
appId: '', appId: '',
token: '', token: '',
@ -64,8 +69,11 @@ export const agora = {
} }
}) })
} else { } else {
try {
await agora.setVideoDeviceManager(await rtcEngine.getVideoDeviceManager().getDevice()) await agora.setVideoDeviceManager(await rtcEngine.getVideoDeviceManager().getDevice())
setting.videoDeviceId = await rtcEngine.getVideoDeviceManager().getDevice() setting.videoDeviceId = await rtcEngine.getVideoDeviceManager().getDevice()
} catch (error) {
}
} }
// 播放设备 // 播放设备
@ -110,6 +118,35 @@ export const agora = {
if (settingData.ecordingDeviceId) agora.setRecordingDevice(settingData.ecordingDeviceId) // 设置音频采集设备 if (settingData.ecordingDeviceId) agora.setRecordingDevice(settingData.ecordingDeviceId) // 设置音频采集设备
if (settingData.ecordingVolume) agora.setRecordingDeviceVolume(settingData.ecordingVolume) // 设置音频设备音量 if (settingData.ecordingVolume) agora.setRecordingDeviceVolume(settingData.ecordingVolume) // 设置音频设备音量
if (settingData.isAINoiseReduction) agora.setAINSMode(settingData.isAINoiseReduction, settingData.aINoiseReduction) // 设置ai降噪 if (settingData.isAINoiseReduction) agora.setAINSMode(settingData.isAINoiseReduction, settingData.aINoiseReduction) // 设置ai降噪
if (settingData.beautyEffect) agora.setBeautyEffectOptions(settingData.beautyEffect.isBeautyEffect, settingData.beautyEffect)
if (settingData.colorEnhancement) agora.setColorEnhanceOptions(settingData.colorEnhancement.isColorEnhancement, settingData.colorEnhancement)
if (settingData.darkLightEnhancement) agora.setLowlightEnhanceOptions(settingData.darkLightEnhancement.isDarkLightEnhancement, settingData.darkLightEnhancement)
if (settingData.virtualBackground) {
if (typeof settingData.virtualBackground.sourceIndex === 'number') {
if (import.meta.env.VITE_ENV === 'development') {
window.electron.getAppPath().then((res: string) => {
const imagePath = path.join(res, 'src', 'assets', 'virtualBackground', `${settingData.virtualBackground.sourceIndex + 1}.png`);
agora.enableVirtualBackground(settingData.virtualBackground.isVirtualBackground, {
source: imagePath,
background_source_type: 2,
color: Number(settingData.virtualBackground.color),
})
})
} else {
const imagePath = path.join((process as any).resourcesPath, 'images', `${settingData.virtualBackground.sourceIndex + 1}.png`);
agora.enableVirtualBackground(settingData.virtualBackground.isVirtualBackground, {
source: imagePath,
background_source_type: 2,
color: Number(settingData.virtualBackground.color),
})
}
} else {
agora.enableVirtualBackground(settingData.virtualBackground.isVirtualBackground, {
background_source_type: 1,
color: Number(settingData.virtualBackground.color),
})
}
}
}, 1000); }, 1000);
}, },
// 事件回调 // 事件回调
@ -329,7 +366,7 @@ export const agora = {
rtcEngine.muteRemoteVideoStream(uid, mute) rtcEngine.muteRemoteVideoStream(uid, mute)
}, },
// 销毁视频渲染dom // 销毁视频渲染dom
destroyRendererByConfig: async (uid: number, channelId: string) => { destroyRendererByConfig: async (uid: number, channelId?: string) => {
await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, channelId, uid); await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, channelId, uid);
}, },
// ai降噪 // ai降噪
@ -512,5 +549,22 @@ export const agora = {
stopRecordingDeviceTest: async () => { stopRecordingDeviceTest: async () => {
await rtcEngine.getAudioDeviceManager().stopRecordingDeviceTest() await rtcEngine.getAudioDeviceManager().stopRecordingDeviceTest()
}, },
// 设置美颜效果
setBeautyEffectOptions: async (enabled: boolean, options: BeautyOptions) => {
await rtcEngine.setBeautyEffectOptions(enabled, options)
},
// 设置色彩增强
setColorEnhanceOptions: async (enabled: boolean, options: ColorEnhanceOptions) => {
await rtcEngine.setColorEnhanceOptions(enabled, options)
},
// 设置暗光增强
setLowlightEnhanceOptions: async (enabled: boolean, options: LowlightEnhanceOptions) => {
await rtcEngine.setLowlightEnhanceOptions(enabled, options)
},
// 开启/关闭虚拟背景。
enableVirtualBackground: async (enabled: boolean, backgroundSource: VirtualBackgroundSource) => {
await rtcEngine.enableVirtualBackground(enabled, backgroundSource, {
greenCapacity: 1
})
},
} }

View File

@ -73,6 +73,12 @@ import icon48Select from '@/assets/icon48-select.png'
import icon49 from '@/assets/icon49.png' import icon49 from '@/assets/icon49.png'
import icon50 from '@/assets/icon50.png' import icon50 from '@/assets/icon50.png'
import icon51 from '@/assets/icon51.png' import icon51 from '@/assets/icon51.png'
import virtualBackground1 from '@/assets/virtualBackground/1.png'
import virtualBackground2 from '@/assets/virtualBackground/2.png'
import virtualBackground3 from '@/assets/virtualBackground/3.png'
import virtualBackground4 from '@/assets/virtualBackground/4.png'
import virtualBackground5 from '@/assets/virtualBackground/5.png'
import virtualBackground6 from '@/assets/virtualBackground/6.png'
export default { export default {
loading, loading,
icon, icon,
@ -149,4 +155,10 @@ export default {
icon49, icon49,
icon50, icon50,
icon51, icon51,
virtualBackground1,
virtualBackground2,
virtualBackground3,
virtualBackground4,
virtualBackground5,
virtualBackground6,
} }

View File

@ -1,3 +1,4 @@
import path from "path";
import storage from "./storage"; import storage from "./storage";
export const setKeyOpenChildWindow = async (key: string, bool: boolean) => { export const setKeyOpenChildWindow = async (key: string, bool: boolean) => {
const openChildWindow = await JSON.parse(storage.getItem('openChildWindow') as string) const openChildWindow = await JSON.parse(storage.getItem('openChildWindow') as string)
@ -13,3 +14,43 @@ export const getKeyOpenChildWindow = async (key: string): Promise<boolean> => {
const openChildWindow = await JSON.parse(storage.getItem('openChildWindow') as string) const openChildWindow = await JSON.parse(storage.getItem('openChildWindow') as string)
return openChildWindow[key] return openChildWindow[key]
}; };
export const storageSeeting: any = {
videoDeviceId: '', //摄像头id
ecordingDeviceId: "", //输入设备id
playBackDeviceId: "", //输出设备id
ecordingVolume: '', //输入音量
playBackVolume: '', //输出音量
autoEcordingVolume: true, //是否自动调整麦克风音量
recordingFilesPath: path.resolve(__dirname, '../../Downloads') + '\\', //本地录制保存路径
shareFilesPath: path.resolve(__dirname, '../../Downloads/') + '\\', //共享文件保存路径
isShareSavePath: true, //是否下载钱询问每个文件保存的位置
closeSetting: 'hide', //关闭按钮设置
isAINoiseReduction: true, //是否开启ai降噪
aINoiseReduction: 1, // 降噪模式
isRecordingTips: true, //是否开启录制提示
beautyEffect: { //美颜效果
isBeautyEffect: false, //是否打开美颜
lighteningContrastLevel: 1, //对比度
lighteningLevel: 0, //美白程度
smoothnessLevel: 0, //磨皮程度
rednessLevel: 0, //红润度
sharpnessLevel: 0, //锐化程度
},
colorEnhancement: { //色彩增强
isColorEnhancement: false, //是否打开色彩增强
strengthLevel: 0.5, //色彩增强程度
skinProtectLevel: 1, //肤色保护程度
},
darkLightEnhancement: { //暗光增强
isDarkLightEnhancement: false, //是否打开暗光增强
level: 0, //暗光增强等级
mode: 0, //暗光增强模式
},
virtualBackground: { //虚拟背景
isVirtualBackground: false, //是否打开虚拟背景
color: '0xFFFFFF', // 纯色
sourceIndex: '', // 背景图下标
},
}

View File

@ -1,242 +0,0 @@
import * as signalR from '@microsoft/signalr';
import storage from './storage';
let connection = '' as any;
export const startSignalr = async () => {
const user = await JSON.parse(storage.getItem('user') as string);
connection = new signalR.HubConnectionBuilder().withAutomaticReconnect()
.withUrl(`${import.meta.env.VITE_BASE_URL_API}/session-manage`, {
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets,
accessTokenFactory: () => user.token
})
.build();
onStart()
storage.setItem('isSignalr', true)
storage.setItem('reconnect', true)
connection.onreconnected(async () => {
storage.setItem('reconnect', true)
});
connection.onreconnecting(async () => {
onStart()
storage.setItem('reconnect', false)
});
}
export const onStart = async () => {
if (connection) {
if (connection.state !== signalR.HubConnectionState.Connected) {
connection.start();
} else {
setTimeout(onStart, 3000);
}
}
}
export const onOtherSignalr = (callBack: Function) => {
if (connection) {
// 邀请
connection.on("Invitation", (roomNum: string, roomName: string, InviterName: string) => {
callBack({
key: 'Invitation',
roomNum, roomName, InviterName
})
});
// 退出
connection.on("ForceLogout", (msg: string) => {
callBack({
key: 'ForceLogout',
msg
})
});
}
}
export const onSignalr = (callBack: Function) => {
if (connection) {
// 聊天
connection.on("ReceiveMessage", (uid: string, userName: string, message: string, timestamp: string) => {
callBack({
key: 'ReceiveMessage',
uid, message, userName, timestamp
})
});
// 扩展操作
connection.on("Operation", (type: number) => {
callBack({
key: 'Operation',
type
})
});
// 移出会议
connection.on("ForceExitRoom", () => {
callBack({
key: 'ForceExitRoom',
})
});
// 全员离开房间
connection.on("AllLeave", () => {
callBack({
key: 'AllLeave',
})
});
// 全员看他
connection.on("ShowUser", (uid: string, uname: string, operUid: string, operUserName: string) => {
callBack({
key: 'ShowUser',
uid,
uname,
operUid,
operUserName,
})
});
// 更新视图模式
connection.on("RefreshView", (type: string) => {
callBack({
key: 'RefreshView',
type
})
});
// 用户加入频道回调
connection.on("UserJoined", (user: any) => {
callBack({
key: 'UserJoined',
user,
})
});
// 用户退出频道回调
connection.on("UserLeave", (uid: string,) => {
callBack({
key: 'UserLeave',
uid,
})
});
// 所有用户开闭麦
connection.on("OperAllMicr", (enableMicr: boolean, uid: string) => {
callBack({
key: 'OperAllMicr',
enableMicr,
uid
})
});
// 用户关闭开启麦克风
connection.on("OperMicr", (user: any, operUid: string) => {
callBack({
key: 'OperMicr',
user,
operUid
})
});
// 用户开启关闭摄像头
connection.on("OperCamera", (user: any, operUid: string) => {
callBack({
key: 'OperCamera',
user,
operUid
})
});
// 发言人用户信息刷新
connection.on("ManagerRefresh", (user: any, uid: string) => {
callBack({
key: 'ManagerRefresh',
user,
uid
})
});
// 申请发言
connection.on("ApplyToSpeak", (uid: string, uname: string) => {
callBack({
key: 'ApplyToSpeak',
uid,
uname
})
});
// 管理员查看随机用户
connection.on("Watch", (watchUids: string[]) => {
callBack({
key: 'Watch',
watchUids
})
});
// 设备列表
connection.on("DriverList", (callerUid: string) => {
callBack({
key: 'DriverList',
callerUid
})
});
// 设置设备
connection.on("SaveDriver", (driver: string) => {
callBack({
key: 'SaveDriver',
driver
})
});
// 显示设备列表
connection.on("ShowDriverList", (driversJsonString: string) => {
callBack({
key: 'ShowDriverList',
driversJsonString
})
});
}
}
export const offSignalr = () => {
if (connection) {
connection.off('ReceiveMessage');
connection.off('Operation');
connection.off('ForceExitRoom');
connection.off('AllLeave');
connection.off('ShowUser');
connection.off('RefreshView');
connection.off('UserJoined');
connection.off('UserLeave');
connection.off('OperAllMicr');
connection.off('OperMicr');
connection.off('OperCamera');
connection.off('ManagerRefresh');
connection.off('ApplyToSpeak');
connection.off('Watch');
connection.off('DriverList');
connection.off('SetDriver');
connection.off('ShowDriverList');
}
}
export const onInvoke = async (str: string, data: any) => {
switch (str) {
case 'sendChannelMsg':
await connection.invoke(str, data.roomNum, data.msg)
break;
case 'sendOper':
// 4:屏幕共享
await connection.invoke(str, data.roomNum, data.type)
break;
case 'getDrivers':
// 获取某个人的设备列表
await connection.invoke(str, data.uid)
break;
case 'sendDrivers':
// 发送设备列表给某个人
await connection.invoke(str, data.uid, data.driversJsonString)
break;
case 'setDrivers':
// 设置某个人的设备列表
await connection.invoke(str, data.uid, data.driversJsonString)
break;
case 'joinChannel':
// 设置某个人的设备列表
await connection.invoke(str, data.roomNum, data.enableMicr, data.enableCamera)
break;
case 'levelChannel':
// 设置某个人的设备列表
await connection.invoke(str, data.roomNum)
break;
}
}
export const onStop = async () => {
if (connection) {
storage.setItem('isSignalr', false)
connection.off('Invitation');
connection.off('ForceLogout');
connection.stop()
connection = ""
}
}

View File

@ -350,6 +350,7 @@ $pagination-hover-background-color: #5575F2;
// ant-notification // ant-notification
.ant-notification { .ant-notification {
-webkit-app-region: no-drag;
.ant-notification-notice-wrapper { .ant-notification-notice-wrapper {
background-color: #1F2022; background-color: #1F2022;

View File

@ -72,7 +72,11 @@ export default defineConfig({
ConnectionStateType, ConnectionStateType,
ConnectionChangedReasonType, ConnectionChangedReasonType,
LocalVideoStreamState, LocalVideoStreamState,
LocalVideoStreamReason LocalVideoStreamReason,
BeautyOptions,
ColorEnhanceOptions,
LowlightEnhanceOptions,
VirtualBackgroundSource
} = require("agora-electron-sdk") } = require("agora-electron-sdk")
export { export {
createAgoraRtcEngine, createAgoraRtcEngine,
@ -93,7 +97,11 @@ export default defineConfig({
ConnectionStateType, ConnectionStateType,
ConnectionChangedReasonType, ConnectionChangedReasonType,
LocalVideoStreamState, LocalVideoStreamState,
LocalVideoStreamReason LocalVideoStreamReason,
BeautyOptions,
ColorEnhanceOptions,
LowlightEnhanceOptions,
VirtualBackgroundSource
} }
`, `,
}) })