diff --git a/main.js b/main.js index af61aba..fb62569 100644 --- a/main.js +++ b/main.js @@ -14,6 +14,7 @@ const path = require('node:path') const fs = require('fs'); const Registry = require('winreg'); const { autoUpdater, CancellationToken } = require('electron-updater'); +const signalR = require('@microsoft/signalr'); const cancellationToken = new CancellationToken() app.allowRendererProcessReuse = false; let mainWindow = null; @@ -21,6 +22,7 @@ let childWindow = {} let isMaximized = false; let env; let regKey; +let connection = null; class AppWindow extends BrowserWindow { constructor(config) { @@ -142,6 +144,220 @@ app.on('ready', () => { 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) => { switch (status) { @@ -170,6 +386,10 @@ app.on('ready', () => { ipcMain.handle('getIsMaximized', () => { return mainWindow.isMaximized(); }); + // 获取app路径 + ipcMain.handle('getAppPath', () => { + return app.getAppPath(); + }); // 获取版本号 ipcMain.handle('getVersion', () => { return app.getVersion(); @@ -489,4 +709,40 @@ function mainWindowCenter() { // 主窗口隐藏 function mainWindowHide() { 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); + } + } } \ No newline at end of file diff --git a/package.json b/package.json index 0d504e9..52a3a82 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,15 @@ "directories": { "output": "electron" }, - "extraResources": [], + "extraResources": [ + { + "from": "src/assets/virtualBackground", + "to": "images", + "filter": [ + "**/*" + ] + } + ], "nsis": { "oneClick": false, "installerIcon": "build/install.ico", diff --git a/preload.js b/preload.js index ba34b71..4cae81d 100644 --- a/preload.js +++ b/preload.js @@ -1,6 +1,32 @@ // // 在 preload 脚本中。 const { ipcRenderer } = require('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) => { ipcRenderer.invoke('setMainWindowSize', { ...config }) @@ -17,6 +43,10 @@ window.electron = { getIsMaximized: () => { return ipcRenderer.invoke('getIsMaximized') }, + // 获取app路径 + getAppPath: () => { + return ipcRenderer.invoke('getAppPath') + }, // 获取版本号 getVersion: () => { return ipcRenderer.invoke('getVersion') diff --git a/src/App.tsx b/src/App.tsx index 82e43fc..5e0b4ef 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,7 +9,6 @@ import Meeting from '@/page/Meeting/index' import NotFound from '@/page/NotFound/index' import { storage } from '@/utils' import { message, Spin } from "antd"; -import { onOtherSignalr, onStop, startSignalr } from "@/utils/package/signalr"; import JoinMeetingModal from "@/components/JoinMeetingModal"; import UpdateModal from "@/components/UpdateModal"; import * as CryptoJS from 'crypto-js'; @@ -24,7 +23,7 @@ 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"; +import { getKeyOpenChildWindow, setKeyOpenChildWindow, storageSeeting } from "./utils/package/public"; const fs = require('fs').promises; const { exec } = require('child_process'); const App: React.FC = () => { @@ -54,7 +53,7 @@ const App: React.FC = () => { storage.setItem('user', JSON.stringify(res.data)) storage.setItem('userLogin', true) toSrc('/home') - await startSignalr() + await window.electron.startSignalr(res.data) } else { 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(() => { window.electron.onUpdate((_e: any, data: any) => { @@ -129,21 +133,7 @@ const App: React.FC = () => { } }) if (!storage.getItem('setting')) { - storage.setItem('setting', JSON.stringify({ - 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, //是否开启录制提示 - })) + storage.setItem('setting', JSON.stringify(storageSeeting)) } if (!storage.getItem('openChildWindow')) { storage.setItem('openChildWindow', JSON.stringify({})) @@ -173,7 +163,7 @@ const App: React.FC = () => { }, [state]) useEffect(() => { if (location.href.indexOf('/login') !== -1) { - onStop() + window.electron.onStop() } }, [navigate]) } @@ -196,7 +186,7 @@ const App: React.FC = () => { }; const onEventSignalr = (): void => { - onOtherSignalr(async (item: any) => { + window.electron.onOtherSignalr(async (_e: any, item: any) => { switch (item.key) { case 'Invitation': window.electron.joinNotification({ diff --git a/src/assets/virtualBackground/1.png b/src/assets/virtualBackground/1.png new file mode 100644 index 0000000..a29260e Binary files /dev/null and b/src/assets/virtualBackground/1.png differ diff --git a/src/assets/virtualBackground/2.png b/src/assets/virtualBackground/2.png new file mode 100644 index 0000000..8a025ca Binary files /dev/null and b/src/assets/virtualBackground/2.png differ diff --git a/src/assets/virtualBackground/3.png b/src/assets/virtualBackground/3.png new file mode 100644 index 0000000..4211d42 Binary files /dev/null and b/src/assets/virtualBackground/3.png differ diff --git a/src/assets/virtualBackground/4.png b/src/assets/virtualBackground/4.png new file mode 100644 index 0000000..692d2fd Binary files /dev/null and b/src/assets/virtualBackground/4.png differ diff --git a/src/assets/virtualBackground/5.png b/src/assets/virtualBackground/5.png new file mode 100644 index 0000000..ec14085 Binary files /dev/null and b/src/assets/virtualBackground/5.png differ diff --git a/src/assets/virtualBackground/6.png b/src/assets/virtualBackground/6.png new file mode 100644 index 0000000..c9dffc7 Binary files /dev/null and b/src/assets/virtualBackground/6.png differ diff --git a/src/components/EquipmentManagement/index.tsx b/src/components/EquipmentManagement/index.tsx index 2872586..4f740ee 100644 --- a/src/components/EquipmentManagement/index.tsx +++ b/src/components/EquipmentManagement/index.tsx @@ -1,6 +1,5 @@ import styles from '@/components/EquipmentManagement/index.module.scss' import { getKeyOpenChildWindow } from '@/utils/package/public'; -import { onInvoke } from '@/utils/package/signalr'; import { Button, Modal, Select, Slider, message } from 'antd'; import { useState, useImperativeHandle, forwardRef } from "react"; const EquipmentManagement = forwardRef((props: any, ref: any) => { @@ -12,7 +11,7 @@ const EquipmentManagement = forwardRef((props: any, ref: any) => { if (isOpen) { props.getDriver?.(uid) } else { - await onInvoke('getDrivers', { + await window.electron.onInvoke('getDrivers', { uid }) } @@ -99,7 +98,7 @@ const EquipmentManagement = forwardRef((props: any, ref: any) => { driversJsonString: JSON.stringify(deviceInfo) }) } else { - await onInvoke('setDrivers', { + await window.electron.onInvoke('setDrivers', { uid: callerUid, driversJsonString: JSON.stringify(deviceInfo) }) diff --git a/src/components/StupWizard/index.module.scss b/src/components/StupWizard/index.module.scss index 5a760a6..2bf9ef3 100644 --- a/src/components/StupWizard/index.module.scss +++ b/src/components/StupWizard/index.module.scss @@ -1,6 +1,6 @@ // 设置向导 .stupWizard { - height: 70vh; + height: 90vh; display: flex; .stupWizardLeft { @@ -68,6 +68,10 @@ } .videoComponents { + >div { + margin-bottom: 10px; + } + >div:nth-child(1) { width: 100%; height: 296px; @@ -88,7 +92,6 @@ >div:nth-child(2) { width: 100%; - margin-top: 10px; display: flex; align-items: center; @@ -96,6 +99,82 @@ 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 { diff --git a/src/components/StupWizard/index.tsx b/src/components/StupWizard/index.tsx index 558125e..030bdaf 100644 --- a/src/components/StupWizard/index.tsx +++ b/src/components/StupWizard/index.tsx @@ -6,12 +6,15 @@ import { agora } from '@/utils/package/agora' import { CloseOutlined, LoadingOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import { storage } from '@/utils'; 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 { exec } = require('child_process'); -const StupWizard = forwardRef((props: any, ref: any) => { +const StupWizard = forwardRef((_props: any, ref: any) => { useImperativeHandle(ref, () => ({ - changeModal: (index: number = 0) => { + changeModal: (index: number = 0, data: any) => { + meetingUserInfo = data; if (location.hash.indexOf('/meeting') === -1) { agora.init() } @@ -23,47 +26,9 @@ const StupWizard = forwardRef((props: any, ref: any) => { return res }) const setting = JSON.parse(storage.getItem('setting') as string) - const obj: 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: '', // 背景图下标 - }, - } - for (const key in obj) { + for (const key in storageSeeting) { if (setting[key] === undefined) { - setting[key] = obj[key] + setting[key] = storageSeeting[key] } } storage.setItem('setting', JSON.stringify(setting)) @@ -192,10 +157,81 @@ const VideoComponents = () => { list: [], 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({ + isBeautyEffect: false, //是否打开美颜 + lighteningContrastLevel: 1, //对比度 + lighteningLevel: 0, //美白程度 + smoothnessLevel: 0, //磨皮程度 + rednessLevel: 0, //红润度 + sharpnessLevel: 0, //锐化程度 + }); + const [colorEnhancement, setColorEnhancement] = useState({ + isColorEnhancement: false, //是否打开色彩增强 + strengthLevel: 0.5, //色彩增强程度 + skinProtectLevel: 1, //肤色保护程度 + }); + const [darkLightEnhancement, setDarkLightEnhancement] = useState({ + isDarkLightEnhancement: false, //是否打开暗光增强 + level: 0, //暗光增强等级 + mode: 0, //暗光增强模式 + }); + const [virtualBackground, setVirtualBackground] = useState({ + isVirtualBackground: false, //是否打开虚拟背景 + color: '0xFFFFFF', // 纯色 + sourceIndex: '', // 背景图下标 + }); const setting = JSON.parse(storage.getItem('setting') as string) useEffect(() => { + setBeautyEffect(setting.beautyEffect) + setColorEnhancement(setting.colorEnhancement) + setDarkLightEnhancement(setting.darkLightEnhancement) + setVirtualBackground(setting.virtualBackground) 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 => { const userInfo = JSON.parse(storage.getItem('user') as string) agora.getVideoDeviceManager().then(async (res) => { @@ -261,17 +297,267 @@ const VideoComponents = () => { { + let hexInt = parseInt('0x' + e.target.value.split('#')[1], 16) + setting.virtualBackground.color = hexInt + storage.setItem('setting', JSON.stringify(setting)) + setVirtualBackground({ + ...virtualBackground, + color: hexInt + }) + }} /> + + + + { + virtualBackgroundImg.map((item, index) => { + return ( +
{ + setting.virtualBackground.sourceIndex = index; + storage.setItem('setting', JSON.stringify(setting)) + setVirtualBackground({ + ...virtualBackground, + sourceIndex: index + }) + }}> + +
+ ) + }) + } + + : null} +
+ {videoKey === 'colorEnhancement' ?
+ + { + setting.beautyEffect.isBeautyEffect = e.target.checked; + storage.setItem('setting', JSON.stringify(setting)) + setBeautyEffect({ + ...beautyEffect, + isBeautyEffect: e.target.checked + }) + }}> + + 美颜效果 + +
+
+ 对比度 +
+ { + 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}> + 低对比度 + 正常对比度 + 高对比度 + +
+
+
+ 美白程度 +
+ { + setting.beautyEffect.lighteningLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setBeautyEffect({ + ...beautyEffect, + lighteningLevel: e + }) + }} disabled={!beautyEffect.isBeautyEffect} /> +
+
+
+ 磨皮程度 +
+ { + setting.beautyEffect.smoothnessLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setBeautyEffect({ + ...beautyEffect, + smoothnessLevel: e + }) + }} disabled={!beautyEffect.isBeautyEffect} /> +
+
+
+ 红润度 +
+ { + setting.beautyEffect.rednessLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setBeautyEffect({ + ...beautyEffect, + rednessLevel: e + }) + }} disabled={!beautyEffect.isBeautyEffect} /> +
+
+
+ 锐化程度 +
+ { + setting.beautyEffect.sharpnessLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setBeautyEffect({ + ...beautyEffect, + sharpnessLevel: e + }) + }} disabled={!beautyEffect.isBeautyEffect} /> +
+
+
+
: null} + {videoKey === 'darkLightEnhancement' ?
+ + { + setting.colorEnhancement.isColorEnhancement = e.target.checked; + storage.setItem('setting', JSON.stringify(setting)) + setColorEnhancement({ + ...colorEnhancement, + isColorEnhancement: e.target.checked + }) + }}> + + 色彩增强 + +
+
+ 色彩增强程度 +
+ { + setting.colorEnhancement.strengthLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setColorEnhancement({ + ...colorEnhancement, + strengthLevel: e + }) + }} disabled={!colorEnhancement.isColorEnhancement} /> +
+
+
+ 肤色保护程度 +
+ { + setting.colorEnhancement.skinProtectLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setColorEnhancement({ + ...colorEnhancement, + skinProtectLevel: e + }) + }} disabled={!colorEnhancement.isColorEnhancement} /> +
+
+
+
: null} + {videoKey === 'virtualBackground' ?
+ + { + setting.darkLightEnhancement.isDarkLightEnhancement = e.target.checked; + storage.setItem('setting', JSON.stringify(setting)) + setDarkLightEnhancement({ + ...darkLightEnhancement, + isDarkLightEnhancement: e.target.checked + }) + }}> + + 暗光增强 + +
+
+ 暗光增强等级 +
+ { + 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}> + (默认)优先画质的暗光增强,会处理视频图像的亮度、细节、噪声,消耗的性能适中,处理速度适中,综合画质最优。 + 优先性能的暗光增强,会处理视频图像的亮度、细节,消耗的性能较少,处理速度较快。 + +
+
+
+ 暗光增强模式 +
+ { + 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}> + (默认)自动模式。SDK 会根据环境光亮度自动开启或关闭暗光增强功能,以适时补光和防止过曝。 + 手动模式。用户需手动开启或关闭暗光增强功能。 + +
+
+
+
: null} +
@@ -296,12 +582,15 @@ const AudioComponents = () => { getAudioMediaList() agora.registerEventHandler({ onAudioVolumeIndication: (speakers: any) => { - const totalVolume = speakers.length ? speakers[0].volume : 0 - const percentage = (totalVolume / 255) * 100 - const dom = document.getElementById('deviceTest') as any; - if (dom) { - dom.style.width = `${percentage}%` - } + speakers.forEach((item: any) => { + if (item.uid === 0 || item.uid === 1) { + const percentage = (item.volume / 255) * 100 + const dom = document.getElementById('deviceTest') as any; + if (dom) { + dom.style.width = `${percentage}%` + } + } + }); } }) }, []); @@ -351,7 +640,7 @@ const AudioComponents = () => { { + value={audioDeviceManager.playBackItem || null} onChange={async (e) => { setting.playBackDeviceId = e; storage.setItem('setting', JSON.stringify(setting)) setAudioDeviceManager({ diff --git a/src/page/Home/Index/index.module.scss b/src/page/Home/Index/index.module.scss index 6ee0fb1..0a634c8 100644 --- a/src/page/Home/Index/index.module.scss +++ b/src/page/Home/Index/index.module.scss @@ -168,4 +168,10 @@ width: 70px; } } +} + +@media screen and (max-width: 1154px) { + .indexContentListItem { + width: calc(98% / 2) !important; + } } \ No newline at end of file diff --git a/src/page/Login/index.tsx b/src/page/Login/index.tsx index 20d88fb..6ac5ca2 100644 --- a/src/page/Login/index.tsx +++ b/src/page/Login/index.tsx @@ -7,7 +7,6 @@ import { storage } from '@/utils' import { GetCheckUser, PostAnonLogin, PostLogin } from '@/api/Login' import * as CryptoJS from 'crypto-js'; import ImageUrl from '@/utils/package/imageUrl' -import { startSignalr } from '@/utils/package/signalr'; import { v4 as uuidv4 } from 'uuid'; import { GetCheckoutRoomNum, GetRoomInfo, GetRoomRtcToken } from '@/api/Home/Index'; const Login: React.FC = () => { @@ -164,7 +163,7 @@ const Login: React.FC = () => { height: Math.ceil(res.height / 1.3), }) }) - startSignalr() + window.electron.startSignalr(res.data) navigate('/home') } }) @@ -321,7 +320,7 @@ const Login: React.FC = () => { if (res.code == 200) { storage.setItem('user', JSON.stringify(res.data)) storage.setItem('userLogin', true) - await startSignalr() + await window.electron.startSignalr(res.data) getRoomRtcToken(anonInfo.roomNum, (options: any) => { if (options) { GetRoomInfo(anonInfo.roomNum).then(async (res) => { diff --git a/src/page/Meeting/index.tsx b/src/page/Meeting/index.tsx index 4ce44d2..3123ec0 100644 --- a/src/page/Meeting/index.tsx +++ b/src/page/Meeting/index.tsx @@ -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 ImageUrl from '@/utils/package/imageUrl' 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, LocalVideoStreamReason, LocalVideoStreamState, QualityType, RtcConnection, RtcStats, UserOfflineReasonType, VideoSourceType, VideoStreamType } from 'agora-electron-sdk'; @@ -276,12 +275,12 @@ const Meeting: React.FC = () => { }) break; case 'userListWindowEquipmentManagement': - await onInvoke('getDrivers', { + await window.electron.onInvoke('getDrivers', { uid: userListWindowEquipmentManagement.uid, }) break; case 'userListWindowSetEquipmentManagement': - await onInvoke('setDrivers', { + await window.electron.onInvoke('setDrivers', { uid: userListWindowSetEquipmentManagement.uid, driversJsonString: userListWindowSetEquipmentManagement.driversJsonString }) @@ -410,7 +409,6 @@ const Meeting: React.FC = () => { 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); @@ -534,7 +532,7 @@ const Meeting: React.FC = () => { }, [currentVideoId, isShare]); useEffect(() => { - onSignalr(async (item: any) => { + window.electron.onSignalr(async (_e: any, item: any) => { const setting = JSON.parse(storage.getItem('setting') as string) switch (item.key) { // 聊天 @@ -573,7 +571,7 @@ const Meeting: React.FC = () => { setIsShare((res: any) => { if (userInfo.screenShareId === String(res)) { changeStatusList({ - title: '停止共享' + title: '共享冲突停止共享', }, 1, 0) } 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, ecordingVolume: res[1].ecordingVolume } - onInvoke('sendDrivers', { + window.electron.onInvoke('sendDrivers', { uid: item.callerUid, driversJsonString: JSON.stringify(data) }) @@ -825,7 +823,7 @@ const Meeting: React.FC = () => { } }) return () => { - offSignalr() + window.electron.offSignalr() } }, []) @@ -930,6 +928,7 @@ const Meeting: React.FC = () => { // 声网初始化 const agoraInit = async () => { await agora.init(true) + await agora.muteLocalVideoStream(userInfo, state.enableMicr, state.enableCamera) await getJoin(state.enableMicr, state.enableCamera) agora.registerEventHandler({ onJoinChannelSuccess: async (connection: RtcConnection, _elapsed: number) => { @@ -963,6 +962,7 @@ const Meeting: React.FC = () => { } }, onUserOffline: async (connection: RtcConnection, remoteUid: number, _reason: UserOfflineReasonType) => { + await agora.destroyRendererByConfig(Number(remoteUid), connection.channelId) if (connection.channelId === state.channelId) { if (String(remoteUid).length === 9) { setIsShare(null) @@ -1380,6 +1380,7 @@ const Meeting: React.FC = () => { }) break; case '停止共享': + case '共享冲突停止共享': await getUserRoomInfo().then(async (res) => { if (res) { await stopScreenCapture() @@ -1387,7 +1388,9 @@ const Meeting: React.FC = () => { message.error(msgTips) } }) - await allUserLook(userInfo.uid, userInfo.userName) + if (row.title === '停止共享') { + await allUserLook(userInfo.uid, userInfo.userName) + } break; case '静音': await postOpenMicr(false, userInfo.uid) @@ -1424,7 +1427,10 @@ const Meeting: React.FC = () => { }) break; case '设置': - stupWizardRef.current.changeModal() + await getUserRoomInfo().then(async (res) => { + stupWizardRef.current.changeModal(0, res) + }) + break; case '邀请人员': await getUserRoomInfo().then(async (res) => { @@ -1550,11 +1556,8 @@ const Meeting: React.FC = () => { // 退出房间 const leaveChannel = async (bool: boolean = true): Promise => { await stopRecorderMedia() - try { - if (bool) { - await getLeave() - } - } catch (error) { + if (bool) { + await getLeave() } await stopScreenCapture() await agora.leaveChannel() @@ -1715,7 +1718,7 @@ const Meeting: React.FC = () => { break; case 'reconnect': if (e.value == true) { - await onInvoke('joinChannel', { + await window.electron.onInvoke('joinChannel', { roomNum: state.channelId, enableMicr: !footerList[0][0].active, enableCamera: !footerList[0][1].active @@ -1747,7 +1750,7 @@ const Meeting: React.FC = () => { const sendMsg = (text?: string): void => { let msg = text ? text : textMsg; if (msg) { - onInvoke('sendChannelMsg', { + window.electron.onInvoke('sendChannelMsg', { roomNum: state.channelId, msg: msg, }) @@ -1991,7 +1994,7 @@ const Meeting: React.FC = () => { // enableMicr, // enableCamera // }) - await onInvoke('joinChannel', { + await window.electron.onInvoke('joinChannel', { roomNum: state.channelId, enableMicr, enableCamera @@ -2003,7 +2006,7 @@ const Meeting: React.FC = () => { // await GetLeave({ // roomNum: state.channelId, // }) - await onInvoke('levelChannel', { + await window.electron.onInvoke('levelChannel', { roomNum: state.channelId }) } @@ -2786,13 +2789,13 @@ const Meeting: React.FC = () => { setIsSharePopConfirm(false) setIsSharedScreenModal(false) }} style={{ backgroundColor: '#31353A', marginRight: '14px' }}>取消 - {isShare ? { setIsSharePopConfirm(false) - await onInvoke('sendOper', { + await window.electron.onInvoke('sendOper', { roomNum: state.channelId, type: 4, }) diff --git a/src/render.d.ts b/src/render.d.ts index 3be8a2c..aa4f3ef 100644 --- a/src/render.d.ts +++ b/src/render.d.ts @@ -1,9 +1,17 @@ // electron-env.d.ts export interface IElectronAPI { + startSignalr: (user: any) => Promise; + offSignalr: () => Promise; + onStop: () => Promise; + onInvoke: (str: string, data: any) => Promise; + changeLocalStorage: (callBack: Function) => void; + onOtherSignalr: (callBack: Function) => void; + onSignalr: (callBack: Function) => void; setMainWindowSize: (config: any) => void; getWindowSize: () => any; setViewStatus: (status: 'quit' | 'maximize' | 'minimize' | 'unmaximize' | 'hide' | 'show') => void; getIsMaximized: () => Promise; + getAppPath: () => Promise; setWriteText: (text: string) => void; onQuit: (callBack: Function) => void; onUpdate: (callBack: Function) => void; diff --git a/src/utils/package/agora.ts b/src/utils/package/agora.ts index 4c45306..63b7516 100644 --- a/src/utils/package/agora.ts +++ b/src/utils/package/agora.ts @@ -17,11 +17,16 @@ import { ConnectionStateType, ConnectionChangedReasonType, LocalVideoStreamReason, - LocalVideoStreamState + LocalVideoStreamState, + BeautyOptions, + ColorEnhanceOptions, + LowlightEnhanceOptions, + VirtualBackgroundSource } from "agora-electron-sdk"; import { GetRoomRtcToken, GetAgoraConf } from "@/api/Home/Index"; import { storage } from '@/utils'; import { role } from "@/config/role"; +import path from "path"; const option: any = { appId: '', token: '', @@ -64,8 +69,11 @@ export const agora = { } }) } else { - await agora.setVideoDeviceManager(await rtcEngine.getVideoDeviceManager().getDevice()) - setting.videoDeviceId = await rtcEngine.getVideoDeviceManager().getDevice() + try { + await agora.setVideoDeviceManager(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.ecordingVolume) agora.setRecordingDeviceVolume(settingData.ecordingVolume) // 设置音频设备音量 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); }, // 事件回调 @@ -329,7 +366,7 @@ export const agora = { rtcEngine.muteRemoteVideoStream(uid, mute) }, // 销毁视频渲染dom - destroyRendererByConfig: async (uid: number, channelId: string) => { + destroyRendererByConfig: async (uid: number, channelId?: string) => { await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, channelId, uid); }, // ai降噪 @@ -512,5 +549,22 @@ export const agora = { stopRecordingDeviceTest: async () => { 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 + }) + }, } \ No newline at end of file diff --git a/src/utils/package/imageUrl.ts b/src/utils/package/imageUrl.ts index e0c31e2..2722e37 100644 --- a/src/utils/package/imageUrl.ts +++ b/src/utils/package/imageUrl.ts @@ -73,6 +73,12 @@ 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' +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 { loading, icon, @@ -149,4 +155,10 @@ export default { icon49, icon50, icon51, + virtualBackground1, + virtualBackground2, + virtualBackground3, + virtualBackground4, + virtualBackground5, + virtualBackground6, } \ No newline at end of file diff --git a/src/utils/package/public.ts b/src/utils/package/public.ts index 6a22ec2..82f5aba 100644 --- a/src/utils/package/public.ts +++ b/src/utils/package/public.ts @@ -1,3 +1,4 @@ +import path from "path"; import storage from "./storage"; export const setKeyOpenChildWindow = async (key: string, bool: boolean) => { const openChildWindow = await JSON.parse(storage.getItem('openChildWindow') as string) @@ -13,3 +14,43 @@ export const getKeyOpenChildWindow = async (key: string): Promise => { const openChildWindow = await JSON.parse(storage.getItem('openChildWindow') as string) 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: '', // 背景图下标 + }, +} \ No newline at end of file diff --git a/src/utils/package/signalr.ts b/src/utils/package/signalr.ts deleted file mode 100644 index 0d0652b..0000000 --- a/src/utils/package/signalr.ts +++ /dev/null @@ -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 = "" - } -} - diff --git a/src/utils/styles/App.scss b/src/utils/styles/App.scss index 1c1cf96..dc87e74 100644 --- a/src/utils/styles/App.scss +++ b/src/utils/styles/App.scss @@ -350,6 +350,7 @@ $pagination-hover-background-color: #5575F2; // ant-notification .ant-notification { + -webkit-app-region: no-drag; .ant-notification-notice-wrapper { background-color: #1F2022; diff --git a/vite.config.ts b/vite.config.ts index f52346c..f19a94c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -72,7 +72,11 @@ export default defineConfig({ ConnectionStateType, ConnectionChangedReasonType, LocalVideoStreamState, - LocalVideoStreamReason + LocalVideoStreamReason, + BeautyOptions, + ColorEnhanceOptions, + LowlightEnhanceOptions, + VirtualBackgroundSource } = require("agora-electron-sdk") export { createAgoraRtcEngine, @@ -93,7 +97,11 @@ export default defineConfig({ ConnectionStateType, ConnectionChangedReasonType, LocalVideoStreamState, - LocalVideoStreamReason + LocalVideoStreamReason, + BeautyOptions, + ColorEnhanceOptions, + LowlightEnhanceOptions, + VirtualBackgroundSource } `, })