Compare commits

..

No commits in common. "master" and "0.7.0" have entirely different histories.

63 changed files with 477 additions and 1214 deletions

6
.env.development Normal file
View File

@ -0,0 +1,6 @@
#基础API 绝对的
VITE_BASE_URL_API = 'http://192.168.2.9:5192'
#当前IP 相对的
VITE_BASE_CURRENT_API = '.'
#开发环境
VITE_ENV = 'development'

6
.env.production Normal file
View File

@ -0,0 +1,6 @@
#基础API 绝对的
VITE_BASE_URL_API = 'https://meeting-api.23544.com/pc'
#当前IP 相对的
VITE_BASE_CURRENT_API = '.'
#生产环境
VITE_ENV = 'production'

6
.env.xy Normal file
View File

@ -0,0 +1,6 @@
#基础API 绝对的
VITE_BASE_URL_API = 'https://meeting-api.23544.com/pc'
#当前IP 相对的
VITE_BASE_CURRENT_API = '.'
#测试环境
VITE_ENV = 'xy'

BIN
build/install.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -1,16 +0,0 @@
!macro customFinishPage
AutoCloseWindow true
Function StartApp
${if} ${isUpdated}
StrCpy $1 "--updated"
${else}
StrCpy $1 ""
${endif}
${StdUtils.ExecShellAsUser} $0 "$launchLink" "open" "$1"
FunctionEnd
Function .onInstSuccess
Call StartApp
FunctionEnd
!macroend

View File

@ -42,16 +42,15 @@
], ],
"nsis": { "nsis": {
"oneClick": false, "oneClick": false,
"installerIcon": "build/start.ico", "installerIcon": "build/install.ico",
"uninstallerIcon": "build/start.ico", "uninstallerIcon": "build/install.ico",
"installerHeaderIcon": "build/start.ico", "installerHeaderIcon": "build/install.ico",
"allowToChangeInstallationDirectory": true, "allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true, "createDesktopShortcut": true,
"createStartMenuShortcut": true, "createStartMenuShortcut": true,
"deleteAppDataOnUninstall": true, "deleteAppDataOnUninstall": true,
"shortcutName": "智汇享", "shortcutName": "智汇享",
"allowElevation": true, "allowElevation": true,
"perMachine": true, "perMachine": true
"include": "build/install.nsh"
} }
} }

View File

@ -1,57 +0,0 @@
{
"appId": "agora.io.ElectronApiExample",
"asar": true,
"asarUnpack": [
"node_modules/agora-electron-sdk"
],
"buildDependenciesFromSource": true,
"compression": "normal",
"productName": "智汇享",
"publish": [
{
"provider": "generic",
"url": "http://192.168.2.9:8827"
}
],
"files": [
"!*.log"
],
"win": {
"icon": "build/start.ico",
"requestedExecutionLevel": "highestAvailable",
"target": [
{
"target": "nsis",
"arch": [
"ia32"
]
}
]
},
"directories": {
"output": "electron"
},
"extraResources": [
{
"from": "src/assets/virtualBackground",
"to": "images",
"filter": [
"**/*"
]
}
],
"nsis": {
"oneClick": false,
"installerIcon": "build/start.ico",
"uninstallerIcon": "build/start.ico",
"installerHeaderIcon": "build/start.ico",
"allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"deleteAppDataOnUninstall": true,
"shortcutName": "智汇享",
"allowElevation": true,
"perMachine": true,
"include": "build/install.nsh"
}
}

View File

@ -51,7 +51,6 @@
"deleteAppDataOnUninstall": true, "deleteAppDataOnUninstall": true,
"shortcutName": "湖北襄阳四中教研平台", "shortcutName": "湖北襄阳四中教研平台",
"allowElevation": true, "allowElevation": true,
"perMachine": true, "perMachine": true
"include": "build/install.nsh"
} }
} }

130
main.js
View File

@ -11,7 +11,6 @@ const {
crashReporter, crashReporter,
desktopCapturer, desktopCapturer,
powerSaveBlocker, powerSaveBlocker,
net
} = require('electron'); } = require('electron');
const path = require('node:path') const path = require('node:path')
const updateJs = require('./src/utils/package/update') const updateJs = require('./src/utils/package/update')
@ -19,15 +18,16 @@ 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 signalR = require('@microsoft/signalr');
const { setTimeout, setInterval } = require('timers'); const { setTimeout, setInterval, clearTimeout, clearInterval } = require('timers');
const cancellationToken = new CancellationToken() const cancellationToken = new CancellationToken()
app.allowRendererProcessReuse = false; app.allowRendererProcessReuse = false;
let mainWindow = null; let mainWindow = null;
let childWindow = {} let childWindow = {}
let isMaximized = false; let isMaximized = false;
let env = 'development'; //development production xy let env;
let regKey; let regKey;
let connection = null; let connection = null;
let envStr;
let startNumber = 0; let startNumber = 0;
let buildStatus = false; //true 打包开发版本 false 本地开发 let buildStatus = false; //true 打包开发版本 false 本地开发
powerSaveBlocker.start('prevent-display-sleep') powerSaveBlocker.start('prevent-display-sleep')
@ -53,15 +53,16 @@ class AppWindow extends BrowserWindow {
const finalConfig = { ...basicConfig, ...config }; const finalConfig = { ...basicConfig, ...config };
super(finalConfig); super(finalConfig);
if (env === 'development') { if (env === 'development') {
// 开发
if (buildStatus) { if (buildStatus) {
this.loadURL('http://192.168.2.9:8827/'); this.loadFile(path.resolve(__dirname, './dist/index.html'));
} else { } else {
this.loadURL('http://localhost:3000'); this.loadURL('http://localhost:3000');
} }
} else { } else {
this.loadURL('https://meeting-api.23544.com/') // 测试 | 生产
this.loadFile(path.resolve(__dirname, './dist/index.html'));
} }
// this.loadFile(path.resolve(__dirname, './dist/index.html'))
this.once('ready-to-show', () => { this.once('ready-to-show', () => {
this.show(); this.show();
}); });
@ -71,20 +72,11 @@ function quit() {
app.quit() app.quit()
} }
let tray; let tray;
// 检查网络状态
function checkNetworkStatus() {
if (!net.isOnline()) {
dialog.showErrorBox(`${env === 'xy' ? '湖北襄阳四中教研平台' : '智汇享'}-网络连接错误', '当前无网络连接,请检查您的网络设置。`);
app.quit();
return false;
}
return true;
}
function createTray() { function createTray() {
const iconPath = `${__dirname}/src/assets/${updateJs.getIcon(env)}.png`; const iconPath = `${__dirname}/src/assets/${updateJs.getIcon(envStr)}.png`;
const trayIcon = nativeImage.createFromPath(iconPath); const trayIcon = nativeImage.createFromPath(iconPath);
tray = new Tray(trayIcon); tray = new Tray(trayIcon);
tray.setToolTip(updateJs.getTitle(env)); tray.setToolTip(updateJs.getTitle(envStr));
tray.on('click', () => { tray.on('click', () => {
mainWindow.webContents.send('isOpenWindows'); mainWindow.webContents.send('isOpenWindows');
}); });
@ -103,11 +95,6 @@ function createWindow() {
} }
const additionalData = { myKey: 'myValue' } const additionalData = { myKey: 'myValue' }
app.on('ready', () => { app.on('ready', () => {
// 检查网络状态
if (!checkNetworkStatus()) {
return;
}
// const gotTheLock = true
const gotTheLock = app.requestSingleInstanceLock(additionalData) const gotTheLock = app.requestSingleInstanceLock(additionalData)
if (gotTheLock) { if (gotTheLock) {
app.getPath('crashDumps') app.getPath('crashDumps')
@ -115,14 +102,15 @@ app.on('ready', () => {
uploadToServer: false, uploadToServer: false,
ignoreSystemCrashHandler: false ignoreSystemCrashHandler: false
}) })
// if (!buildStatus) { env = process.argv.find((arg) => arg.startsWith('--env='))?.split('=')[1];
// Object.defineProperty(app, 'isPackaged', { if (env === 'development') {
// get() { Object.defineProperty(app, 'isPackaged', {
// return true get() {
// } return true
// }) }
// autoUpdater.updateConfigPath = path.join('latest.yml') })
// } autoUpdater.updateConfigPath = path.join('latest.yml')
}
createWindow() createWindow()
regKey = new Registry({ regKey = new Registry({
hive: Registry.HKCU, hive: Registry.HKCU,
@ -160,20 +148,17 @@ app.on('ready', () => {
isMaximized = true; isMaximized = true;
} }
}); });
ipcMain.handle('startLoad', (event) => { ipcMain.handle('setEnv', (event, str) => {
envStr = str;
if (startNumber === 0) { if (startNumber === 0) {
updateHandle() // 检查更新 updateHandle() // 检查更新
setInterval(() => { setInterval(() => {
autoUpdater.checkForUpdates() updateHandle() // 每一小时检查更新
}, 1000 * 60 * 60) }, 1000 * 60 * 60)
createTray() createTray()
startNumber++ startNumber++
} }
}); });
// 更新
ipcMain.handle('updateHandle', () => {
autoUpdater.checkForUpdates()
});
// socket // socket
ipcMain.handle('startSignalr', (event, user) => { ipcMain.handle('startSignalr', (event, user) => {
startSignalr(user) startSignalr(user)
@ -200,9 +185,6 @@ app.on('ready', () => {
connection.off('ModifyNickName'); connection.off('ModifyNickName');
connection.off('JoinChannelCallback'); connection.off('JoinChannelCallback');
connection.off('ExitSharedScreen'); connection.off('ExitSharedScreen');
connection.off('SetSpeaker');
connection.off('ReceivedOperation');
connection.off('StopedSharedScreen');
} }
}); });
ipcMain.handle('onStop', (event) => { ipcMain.handle('onStop', (event) => {
@ -222,7 +204,7 @@ app.on('ready', () => {
await connection.invoke(str, data.roomNum, data.msg) await connection.invoke(str, data.roomNum, data.msg)
break; break;
case 'sendOper': case 'sendOper':
await connection.invoke(str, data.roomNum, data.contentString) await connection.invoke(str, data.roomNum, data.type)
break; break;
case 'getDrivers': case 'getDrivers':
// 获取某个人的设备列表 // 获取某个人的设备列表
@ -244,14 +226,6 @@ app.on('ready', () => {
// 退出房间 // 退出房间
await connection.invoke(str, data.roomNum) await connection.invoke(str, data.roomNum)
break; break;
case 'SetSpeakerCallback':
// 发言人设置成功
await connection.invoke(str, data)
break;
case 'sendOper2User':
// 扩展参数
await connection.invoke(str, data.uid, data.contentString)
break;
} }
}); });
ipcMain.handle('onOtherSignalr', (event) => { ipcMain.handle('onOtherSignalr', (event) => {
@ -282,10 +256,10 @@ app.on('ready', () => {
}) })
}); });
// 扩展操作 // 扩展操作
connection.on("Operation", (contentString) => { connection.on("Operation", (type) => {
mainWindow.webContents.send('onSignalr', { mainWindow.webContents.send('onSignalr', {
key: 'Operation', key: 'Operation',
contentString type
}) })
}); });
// 移出会议 // 移出会议
@ -420,27 +394,6 @@ app.on('ready', () => {
key: 'ExitSharedScreen' key: 'ExitSharedScreen'
}) })
}); });
// 设置发言人
connection.on("SetSpeaker", (RoomManagerInputDTO) => {
mainWindow.webContents.send('onSignalr', {
key: 'SetSpeaker',
RoomManagerInputDTO
})
});
// 扩展参数
connection.on("ReceivedOperation", (contentString) => {
mainWindow.webContents.send('onSignalr', {
key: 'ReceivedOperation',
contentString
})
});
// 共享人取消共享屏幕
connection.on("StopedSharedScreen", (ScreenShareId) => {
mainWindow.webContents.send('onSignalr', {
key: 'StopedSharedScreen',
ScreenShareId
})
});
} }
}); });
// 放大缩小退出窗口 // 放大缩小退出窗口
@ -482,10 +435,6 @@ app.on('ready', () => {
ipcMain.handle('getVersion', () => { ipcMain.handle('getVersion', () => {
return app.getVersion(); return app.getVersion();
}); });
// 获取环境
ipcMain.handle('getEnv', () => {
return env;
});
// 获取窗口是否显示 // 获取窗口是否显示
ipcMain.handle('isVisible', () => { ipcMain.handle('isVisible', () => {
return mainWindow.isVisible(); return mainWindow.isVisible();
@ -521,11 +470,6 @@ app.on('ready', () => {
downloadUpdate() downloadUpdate()
} else if (data === '2') { // 下载完成 点击安装 } else if (data === '2') { // 下载完成 点击安装
quitAndInstall() quitAndInstall()
} else if (data === '3') { // 打开弹窗
let message = JSON.stringify({
type: '3',
})
sendUpdateMessage(message)
} }
}); });
// 选择文件夹 // 选择文件夹
@ -643,18 +587,16 @@ app.on('ready', () => {
width: config.width, width: config.width,
height: config.height, height: config.height,
}) })
if (env === 'development') { if (envStr === 'development') {
// 开发 // 开发
if (buildStatus) { if (buildStatus) {
child.loadURL(`http://192.168.2.9:8827/#/${config.key}`); child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`);
// child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`);
} else { } else {
child.loadURL(config.url) child.loadURL(config.url)
} }
} else { } else {
// 测试 | 生产 // 测试 | 生产
child.loadURL(`https://meeting-api.23544.com/#/${config.key}`); child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`);
// child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`);
} }
child.hookWindowMessage(278, function (e) { child.hookWindowMessage(278, function (e) {
child.setEnabled(false);//窗口禁用 child.setEnabled(false);//窗口禁用
@ -756,6 +698,7 @@ app.on('ready', () => {
}); });
// 检测更新在你想要检查更新的时候执行renderer事件触发后的操作自行编写 // 检测更新在你想要检查更新的时候执行renderer事件触发后的操作自行编写
function updateHandle() { function updateHandle() {
autoUpdater.checkForUpdates()
// autoUpdater.checkForUpdatesAndNotify().catch(); // autoUpdater.checkForUpdatesAndNotify().catch();
const message = { const message = {
error: '检查更新出错', error: '检查更新出错',
@ -763,26 +706,23 @@ function updateHandle() {
updateAva: '检测到新版本,正在下载……', updateAva: '检测到新版本,正在下载……',
updateNotAva: '已经是最新版本,不用更新' updateNotAva: '已经是最新版本,不用更新'
} }
autoUpdater.setFeedURL(updateJs.getUpdateUrl(env)) autoUpdater.setFeedURL(updateJs.getUpdateUrl(envStr))
autoUpdater.autoDownload = false // 不自动下载安装包 autoUpdater.autoDownload = false // 不自动下载安装包
autoUpdater.autoInstallOnAppQuit = false // 不自动安装 autoUpdater.autoInstallOnAppQuit = false // 不自动安装
autoUpdater.on('error', function (error) { autoUpdater.on('error', function (error) {
sendUpdateMessage(error) sendUpdateMessage(message.error)
}) })
autoUpdater.on('checking-for-update', function () { autoUpdater.on('checking-for-update', function () {
sendUpdateMessage(message.checking) sendUpdateMessage(message.checking)
}) })
autoUpdater.on('update-available', function (info) { autoUpdater.on('update-available', function (info) {
let messageStr = JSON.stringify({ type: '0' }) let messageStr = JSON.stringify({ type: '0' })
sendUpdateMessage(messageStr) setTimeout(() => {
mainWindow.webContents.send('changeLocalStorage', { sendUpdateMessage(messageStr)
isUpdate: true, }, 5000)
});
}) })
autoUpdater.on('update-not-available', function (info) { autoUpdater.on('update-not-available', function (info) {
mainWindow.webContents.send('changeLocalStorage', {
isUpdate: false,
});
}) })
// 更新下载进度事件 // 更新下载进度事件
autoUpdater.on('download-progress', function (progressObj) { autoUpdater.on('download-progress', function (progressObj) {
@ -855,7 +795,7 @@ function mainWindowCenter() {
const startSignalr = async (user) => { const startSignalr = async (user) => {
connection = new signalR.HubConnectionBuilder() connection = new signalR.HubConnectionBuilder()
.withUrl(`${env === 'development' ? 'http://192.168.2.9:5192' : 'https://meeting-api.23544.com/pc'}/session-manage`, { .withUrl(`${envStr === 'development' ? 'http://192.168.2.9:5192' : 'https://meeting-api.23544.com/pc'}/session-manage`, {
skipNegotiation: true, skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets, transport: signalR.HttpTransportType.WebSockets,
accessTokenFactory: () => user.token accessTokenFactory: () => user.token

7
package-lock.json generated
View File

@ -1,17 +1,17 @@
{ {
"name": "WGShare.Metting", "name": "WGShare.Metting",
"version": "0.7.1", "version": "0.4.8",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "WGShare.Metting", "name": "WGShare.Metting",
"version": "0.7.1", "version": "0.4.8",
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.3.7", "@ant-design/icons": "^5.3.7",
"@microsoft/signalr": "^8.0.0", "@microsoft/signalr": "^8.0.0",
"@types/node": "^20.14.9", "@types/node": "^20.14.9",
"agora-electron-sdk": "4.4.0", "agora-electron-sdk": "^4.4.0",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"antd": "^5.18.2", "antd": "^5.18.2",
"axios": "^1.7.2", "axios": "^1.7.2",
@ -19,7 +19,6 @@
"dayjs": "^1.11.11", "dayjs": "^1.11.11",
"electron-squirrel-startup": "^1.0.1", "electron-squirrel-startup": "^1.0.1",
"electron-updater": "^6.2.1", "electron-updater": "^6.2.1",
"js-yaml": "^4.1.0",
"os": "^0.1.2", "os": "^0.1.2",
"path": "^0.12.7", "path": "^0.12.7",
"postcss-px-to-viewport-8-plugin": "^1.2.5", "postcss-px-to-viewport-8-plugin": "^1.2.5",

View File

@ -1,17 +1,21 @@
{ {
"name": "WGShare.Metting", "name": "WGShare.Metting",
"private": true, "private": true,
"version": "0.8.0", "version": "0.6.5",
"main": "main.js", "main": "main.js",
"authors": "yj", "authors": "yj",
"description": "智汇享", "description": "智汇享",
"scripts": { "scripts": {
"dev": "concurrently \"electron .\" \"cross-env BROWSER=none vite\"", "dev": "concurrently \"electron . --env=development\" \"cross-env BROWSER=none vite\"",
"build": "vite build", "prod": "concurrently \"electron . --env=production\" \"cross-env BROWSER=none vite\"",
"xy": "concurrently \"electron . --env=xy\" \"cross-env BROWSER=none vite\"",
"build": "vite build --mode development",
"build:prod": "vite build --mode production",
"build:xy": "vite build --mode xy",
"preview": "vite preview", "preview": "vite preview",
"build:dev": "vite build & electron-builder -w --config=./config/development.json", "build:dev-win": "vite build --mode development & electron-builder -w --config=./config/build.json",
"build:prod": "vite build & electron-builder -w --config=./config/production.json", "build:prod-win": "vite build --mode production & electron-builder -w --config=./config/build.json",
"build:xy": "vite build & electron-builder -w --config=./config/xy.json" "build:prod-win-xy": "vite build --mode xy & electron-builder -w --config=./config/xy.json"
}, },
"agora_electron": { "agora_electron": {
"platform": "win32", "platform": "win32",
@ -30,7 +34,6 @@
"dayjs": "^1.11.11", "dayjs": "^1.11.11",
"electron-squirrel-startup": "^1.0.1", "electron-squirrel-startup": "^1.0.1",
"electron-updater": "^6.2.1", "electron-updater": "^6.2.1",
"js-yaml": "^4.1.0",
"os": "^0.1.2", "os": "^0.1.2",
"path": "^0.12.7", "path": "^0.12.7",
"postcss-px-to-viewport-8-plugin": "^1.2.5", "postcss-px-to-viewport-8-plugin": "^1.2.5",

View File

@ -51,10 +51,6 @@ window.electron = {
getVersion: () => { getVersion: () => {
return ipcRenderer.invoke('getVersion') return ipcRenderer.invoke('getVersion')
}, },
// 获取环境
getEnv: () => {
return ipcRenderer.invoke('getEnv')
},
// 获取窗口是否显示 // 获取窗口是否显示
isVisible: () => { isVisible: () => {
return ipcRenderer.invoke('isVisible') return ipcRenderer.invoke('isVisible')
@ -91,13 +87,9 @@ window.electron = {
isOpenWindows: (callback) => { isOpenWindows: (callback) => {
ipcRenderer.on('isOpenWindows', callback) ipcRenderer.on('isOpenWindows', callback)
}, },
// 首次加载 // 设置环境变量
startLoad: () => { setEnv: (str) => {
ipcRenderer.invoke('startLoad') ipcRenderer.invoke('setEnv', str)
},
// 更新
updateHandle: () => {
ipcRenderer.invoke('updateHandle')
}, },
// 通知下载最新的包 // 通知下载最新的包
onDownload: (type) => { onDownload: (type) => {
@ -138,7 +130,7 @@ window.electron = {
ipcRenderer.invoke('createChildWindow', { ipcRenderer.invoke('createChildWindow', {
url: location.origin + `/#/noticeWindow`, url: location.origin + `/#/noticeWindow`,
width: 388, width: 388,
height: 180, height: 150,
key: 'noticeWindow', key: 'noticeWindow',
}) })
ipcRenderer.invoke('createChildWindow', { ipcRenderer.invoke('createChildWindow', {

View File

@ -8,21 +8,20 @@ import Login from '@/page/Login/index'
import Meeting from '@/page/Meeting/index' 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, Modal, Spin } from "antd"; import { message, Spin } from "antd";
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';
import { GetCheckOnline, PostLogin } from "@/api/Login"; import { PostLogin } from "@/api/Login";
import { agora } from "@/utils/package/agora"; import { agora } from "@/utils/package/agora";
import QuitTips from "@/components/QuitTips"; import QuitTips from "@/components/QuitTips";
import { GetLeave } from "@/api/Meeting";
import ShareScreenWindow from "@/page/Meeting/ShareScreenWindow"; import ShareScreenWindow from "@/page/Meeting/ShareScreenWindow";
import UserListWindow from "@/page/Meeting/UserListWindow"; import UserListWindow from "@/page/Meeting/UserListWindow";
import ChatSmallWindow from "@/page/Meeting/ChatSmallWindow"; import ChatSmallWindow from "@/page/Meeting/ChatSmallWindow";
import ChatBigWindow from "@/page/Meeting/ChatBigWindow"; import ChatBigWindow from "@/page/Meeting/ChatBigWindow";
import NoticeWindow from "@/page/Meeting/NoticeWindow"; import NoticeWindow from "@/page/Meeting/NoticeWindow";
import { getKeyOpenChildWindow, getTitle, setKeyOpenChildWindow, storageSeeting } from "./utils/package/public"; import { getKeyOpenChildWindow, getTitle, setKeyOpenChildWindow, storageSeeting } from "./utils/package/public";
import { ExclamationCircleFilled } from "@ant-design/icons";
const { confirm } = Modal;
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 = () => {
@ -42,43 +41,20 @@ const App: React.FC = () => {
useEffect(() => { useEffect(() => {
let userInfo = JSON.parse(storage.getItem('user') as string) let userInfo = JSON.parse(storage.getItem('user') as string)
let loginInfo = JSON.parse(storage.getItem('login') as string) let loginInfo = JSON.parse(storage.getItem('login') as string)
const login = () => { window.electron.setEnv(import.meta.env.VITE_ENV);
PostLogin({
account: loginInfo.account,
pwd: CryptoJS.MD5(loginInfo.password).toString(CryptoJS.enc.Hex)
}).then(async (res) => {
if (res.code === 200) {
storage.setItem('user', JSON.stringify(res.data))
storage.setItem('userLogin', true)
toSrc('/home')
await window.electron.startSignalr(res.data)
} else {
toSrc('/login')
}
})
}
if (userInfo && !userInfo.isAnonymous) { if (userInfo && !userInfo.isAnonymous) {
if (loginInfo && loginInfo.isAutoLogin) { if (loginInfo && loginInfo.isAutoLogin) {
GetCheckOnline(loginInfo.account).then(req => { PostLogin({
if (req.code === 200) { account: loginInfo.account,
if (req.data) { pwd: CryptoJS.MD5(loginInfo.password).toString(CryptoJS.enc.Hex)
confirm({ }).then(async (res) => {
title: '提示', if (res.code === 200) {
icon: <ExclamationCircleFilled />, storage.setItem('user', JSON.stringify(res.data))
content: `账号已在其他地方登录,是否强制登陆?`, storage.setItem('userLogin', true)
centered: true, toSrc('/home')
okText: '确定', await window.electron.startSignalr(res.data)
cancelText: '取消', } else {
async onOk() { toSrc('/login')
login()
},
onCancel() {
toSrc('/login')
}
})
} else {
login()
}
} }
}) })
} else { } else {
@ -103,7 +79,6 @@ const App: React.FC = () => {
}; };
}, []); }, []);
useEffect(() => { useEffect(() => {
window.electron.startLoad();
window.electron.downFile(async (_e: any, data: any) => { window.electron.downFile(async (_e: any, data: any) => {
const response = await fetch(data.filePath); const response = await fetch(data.filePath);
const arrayBuffer = await response.arrayBuffer(); const arrayBuffer = await response.arrayBuffer();
@ -152,7 +127,9 @@ const App: React.FC = () => {
}, []) }, [])
useEffect(() => { useEffect(() => {
window.electron.onUpdate((_e: any, data: any) => { window.electron.onUpdate((_e: any, data: any) => {
updateModalRef.current.changeModal(data) if (location.hash.indexOf('/meeting') === -1) {
updateModalRef.current.changeModal(data)
}
}) })
if (!storage.getItem('setting')) { if (!storage.getItem('setting')) {
storage.setItem('setting', JSON.stringify(storageSeeting)) storage.setItem('setting', JSON.stringify(storageSeeting))
@ -187,24 +164,16 @@ const App: React.FC = () => {
if (location.href.indexOf('/login') !== -1) { if (location.href.indexOf('/login') !== -1) {
window.electron.onStop() window.electron.onStop()
} }
if (location.hash && location.hash.indexOf('/meeting') === -1) {
window.electron.updateHandle()
}
message.destroy('cameraTemporarily') message.destroy('cameraTemporarily')
}, [navigate]) }, [navigate])
} }
useEffect(() => { useEffect(() => {
document.addEventListener('keydown', async (event) => { document.addEventListener('keydown', (event) => {
if (event.keyCode == 122) { if (event.key === 'F11') {
event.preventDefault(); event.preventDefault();
} else if (((event.ctrlKey && event.keyCode == 82) || event.keyCode == 116)) {
let env = await window.electron.getEnv()
if (env !== 'development') {
event.preventDefault();
}
} }
}); });
getTitle() document.getElementsByTagName('title')[0].innerText = getTitle(import.meta.env.VITE_ENV)
}, []) }, [])
const handleResize = (): void => { const handleResize = (): void => {
setWindowSize({ setWindowSize({
@ -231,10 +200,8 @@ const App: React.FC = () => {
if (item.msg) { if (item.msg) {
message.error(item.msg) message.error(item.msg)
} }
await leaveChannel() await leaveChannel(true)
setTimeout(() => { toSrc('/login')
toSrc('/login')
}, 5000);
break; break;
} }
}) })
@ -256,7 +223,7 @@ const App: React.FC = () => {
} }
}) })
}; };
const leaveChannel = async (): Promise<void> => { const leaveChannel = async (bool?: boolean): Promise<void> => {
if (location.hash.indexOf('/meeting') === 1) { if (location.hash.indexOf('/meeting') === 1) {
window.electron.closeChildWindow('shareScreenWindow') window.electron.closeChildWindow('shareScreenWindow')
setKeyOpenChildWindow('shareScreenWindow', false) setKeyOpenChildWindow('shareScreenWindow', false)
@ -273,9 +240,11 @@ const App: React.FC = () => {
}) })
}) })
const data = JSON.parse(localStorage.stateInfo); const data = JSON.parse(localStorage.stateInfo);
await window.electron.onInvoke('levelChannel', { if (!bool) {
roomNum: data.channelId await GetLeave({
}) roomNum: data.channelId,
})
}
await agora.leaveChannel() await agora.leaveChannel()
} }
}; };
@ -291,8 +260,6 @@ const App: React.FC = () => {
storage.removeItem('user') storage.removeItem('user')
navigate('/login') navigate('/login')
} }
} else if (e.key === 'env') {
} }
}; };
return ( return (

View File

@ -22,10 +22,4 @@ export const PostAnonLogin = (data: any) =>
url: `/auth/anon-login`, url: `/auth/anon-login`,
method: 'post', method: 'post',
data, data,
})
export const GetCheckOnline = (account: string) =>
request({
url: `/auth/check-online?account=${account}`,
method: 'get'
}) })

View File

@ -71,20 +71,14 @@ export const PostRoomManager = (data: any) =>
request({ request({
url: `/room/manager`, url: `/room/manager`,
method: 'post', method: 'post',
data: { data
...data,
SettingUserId: ''
}
}) })
export const DeleteRoomManager = (data: any) => export const DeleteRoomManager = (data: any) =>
request({ request({
url: `/room/manager`, url: `/room/manager`,
method: 'delete', method: 'delete',
data: { data
...data,
SettingUserId: ''
}
}) })
export const GetRoomKickout = (roomNum: string, kickUid: string) => export const GetRoomKickout = (roomNum: string, kickUid: string) =>
@ -170,15 +164,4 @@ export const PostSharedScreen = (roomNum: string) =>
request({ request({
url: `/room/shared-screen?roomNum=${roomNum}`, url: `/room/shared-screen?roomNum=${roomNum}`,
method: 'post' method: 'post'
})
export const PostStopSharedScreen = (roomNum: string) =>
request({
url: `/room/stop-shared-screen?roomNum=${roomNum}`,
method: 'post'
})
export const PostHomeVerLog = (data: any) =>
request({
url: `/home/ver-log`,
method: 'post',
data
}) })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 712 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 838 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 921 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -1,5 +1,5 @@
import styles from '@/components/Avatar/index.module.scss' import styles from '@/components/Avatar/index.module.scss'
import { memo, useImperativeHandle, forwardRef } from "react"; import { useState, useImperativeHandle, forwardRef } from "react";
const Avatar = forwardRef((props: any, ref: any) => { const Avatar = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
getData: () => { getData: () => {
@ -15,4 +15,4 @@ const Avatar = forwardRef((props: any, ref: any) => {
) )
}) })
export default memo(Avatar) export default Avatar

View File

@ -1,48 +0,0 @@
import ImageUrl from '@/utils/package/imageUrl';
import { Empty, Popover } from 'antd';
import { GetQrcode } from '@/api/Home/Index';
import { memo, useImperativeHandle, forwardRef, useState } from "react";
const Code = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({
getData: () => {
}
}))
const [baseImage, setBaseImage] = useState('');
const [roomNum, setRoomNum] = useState(props.roomNum);
return (
<>
<Popover
placement="bottom"
onOpenChange={async (e: boolean) => {
setBaseImage('')
if (e) {
let env = await window.electron.getEnv()
GetQrcode(roomNum, env === 'development' ? 'trial' : 'release').then(res => {
if (res.code === 200) {
setBaseImage(res.data)
}
})
}
}}
content={
baseImage ? <div>
<img style={{ width: '200px', margin: '0 auto' }} src={`data:image/png;base64,${baseImage}`} alt="" />
<div style={{ color: 'white', textAlign: 'center', fontSize: '16px', marginTop: '10px' }}>
<span></span><br />
<span></span>
</div>
</div> : <div>
<Empty description={'二维码加载中,请稍后'} />
</div>
}
>
<div title='小程序'>
<img src={ImageUrl.icon55} alt="" style={{ width: '16px' }} />
</div>
</Popover>
</>
)
})
export default memo(Code)

View File

@ -1,7 +1,7 @@
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 { Button, Modal, Select, Slider, message } from 'antd'; import { Button, Modal, Select, Slider, message } from 'antd';
import { useState, useImperativeHandle, forwardRef, memo } from "react"; import { useState, useImperativeHandle, forwardRef } from "react";
const EquipmentManagement = forwardRef((props: any, ref: any) => { const EquipmentManagement = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeModal: async (uid: string, userName: string) => { changeModal: async (uid: string, userName: string) => {
@ -116,4 +116,4 @@ const EquipmentManagement = forwardRef((props: any, ref: any) => {
) )
}) })
export default memo(EquipmentManagement) export default EquipmentManagement

View File

@ -2,7 +2,7 @@ import { PostFeedback } from '@/api/Home/Index';
import styles from '@/components/FeedBackModel/index.module.scss' import styles from '@/components/FeedBackModel/index.module.scss'
import { Button, message, Modal, Rate } from 'antd'; import { Button, message, Modal, Rate } from 'antd';
import TextArea from 'antd/es/input/TextArea'; import TextArea from 'antd/es/input/TextArea';
import { useState, useImperativeHandle, forwardRef, memo } from "react"; import { useState, useImperativeHandle, forwardRef } from "react";
const FeedBackModel = forwardRef((_props: any, ref: any) => { const FeedBackModel = forwardRef((_props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeModal: () => { changeModal: () => {
@ -135,4 +135,4 @@ const FeedBackModel = forwardRef((_props: any, ref: any) => {
) )
}) })
export default memo(FeedBackModel) export default FeedBackModel

View File

@ -1,6 +1,6 @@
import styles from '@/components/InvitingPersonnelModal/index.module.scss' import styles from '@/components/InvitingPersonnelModal/index.module.scss'
import { Button, Checkbox, Input, Modal, Pagination, Radio, message } from 'antd'; import { Button, Checkbox, Input, Modal, Pagination, Radio, message } from 'antd';
import { useState, useImperativeHandle, forwardRef, useEffect, memo } from "react"; import { useState, useImperativeHandle, forwardRef, useEffect } from "react";
import { SearchOutlined } from '@ant-design/icons'; import { SearchOutlined } from '@ant-design/icons';
import { GetUserList } from '@/api/Home/User'; import { GetUserList } from '@/api/Home/User';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
@ -197,4 +197,4 @@ const InvitingPersonnelModal = forwardRef((props: any, ref: any) => {
}) })
export default memo(InvitingPersonnelModal) export default InvitingPersonnelModal

View File

@ -1,7 +1,7 @@
import styles from '@/components/JoinMeetingModal/index.module.scss' import styles from '@/components/JoinMeetingModal/index.module.scss'
import ImageUrl from '@/utils/package/imageUrl'; import ImageUrl from '@/utils/package/imageUrl';
import { Modal, message } from 'antd'; import { Modal, message } from 'antd';
import { useState, useImperativeHandle, forwardRef, useRef, memo } from "react"; import { useState, useImperativeHandle, forwardRef, useRef } from "react";
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import JoinSetting from '../JoinSetting'; import JoinSetting from '../JoinSetting';
const JoinMeetingModal = forwardRef((props: any, ref: any) => { const JoinMeetingModal = forwardRef((props: any, ref: any) => {
@ -55,4 +55,4 @@ const JoinMeetingModal = forwardRef((props: any, ref: any) => {
) )
}) })
export default memo(JoinMeetingModal) export default JoinMeetingModal

View File

@ -3,13 +3,12 @@ import { storage } from '@/utils';
import ImageUrl from '@/utils/package/imageUrl'; import ImageUrl from '@/utils/package/imageUrl';
import { GetCheckoutRoomNum, GetRoomRtcToken, GetRoomInfo } from '@/api/Home/Index'; import { GetCheckoutRoomNum, GetRoomRtcToken, GetRoomInfo } from '@/api/Home/Index';
import { Button, Modal, message } from 'antd'; import { Button, Modal, message } from 'antd';
import { useState, useImperativeHandle, forwardRef, memo } from "react"; import { useState, useImperativeHandle, forwardRef } from "react";
import { PostRefresh } from '@/api/Login'; import { PostRefresh } from '@/api/Login';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { agora } from '@/utils/package/agora'; import { agora } from '@/utils/package/agora';
import { role } from '@/config/role'; import { role } from '@/config/role';
import { PostHomeVerLog } from '@/api/Meeting';
const { setInterval, clearInterval } = require('timers'); const { setInterval, clearInterval } = require('timers');
let time = null as any; let time = null as any;
const JoinSetting = forwardRef((_props: any, ref: any) => { const JoinSetting = forwardRef((_props: any, ref: any) => {
@ -202,13 +201,6 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
GetRoomInfo(roomNumber).then(async (res) => { GetRoomInfo(roomNumber).then(async (res) => {
if (res.code === 200) { if (res.code === 200) {
await agora.release() await agora.release()
await window.electron.getVersion().then(async req => {
await PostHomeVerLog({
version: req,
platformType: 1,
roomNum: roomNumber,
})
})
navigate(`/meeting`, { navigate(`/meeting`, {
state: { state: {
channelId: roomNumber, channelId: roomNumber,
@ -244,4 +236,4 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
) )
}) })
export default memo(JoinSetting) export default JoinSetting

View File

@ -1,6 +1,6 @@
import styles from '@/components/MeetingDisconnected/index.module.scss' import styles from '@/components/MeetingDisconnected/index.module.scss'
import { InfoCircleOutlined } from '@ant-design/icons'; import { InfoCircleOutlined } from '@ant-design/icons';
import { useState, useImperativeHandle, forwardRef, memo } from "react"; import { useState, useImperativeHandle, forwardRef } from "react";
const MeetingDisconnected = forwardRef((props: any, ref: any) => { const MeetingDisconnected = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeModal: (bool: boolean) => { changeModal: (bool: boolean) => {
@ -17,4 +17,4 @@ const MeetingDisconnected = forwardRef((props: any, ref: any) => {
) )
}) })
export default memo(MeetingDisconnected) export default MeetingDisconnected

View File

@ -1,6 +1,6 @@
import styles from '@/components/Operation/index.module.scss' import styles from '@/components/Operation/index.module.scss'
import ImageUrl from '@/utils/package/imageUrl'; import ImageUrl from '@/utils/package/imageUrl';
import { useEffect, useState, memo } from "react"; import { useEffect, useState } from "react";
type OperationKeyType = 'minimize' | 'quit' | 'maximize' | 'unmaximize' | 'hide' | 'show'; type OperationKeyType = 'minimize' | 'quit' | 'maximize' | 'unmaximize' | 'hide' | 'show';
type OperationType = { type OperationType = {
icon: string; icon: string;
@ -100,4 +100,4 @@ const Operation: React.FC = () => {
</> </>
) )
} }
export default memo(Operation) export default Operation

View File

@ -2,7 +2,7 @@ import styles from '@/components/QuitTips/index.module.scss'
import { storage } from '@/utils'; import { storage } from '@/utils';
import { InfoCircleOutlined } from '@ant-design/icons'; import { InfoCircleOutlined } from '@ant-design/icons';
import { Button, Checkbox, Modal, Radio } from 'antd'; import { Button, Checkbox, Modal, Radio } from 'antd';
import { useState, useImperativeHandle, forwardRef, memo } from "react"; import { useState, useImperativeHandle, forwardRef } from "react";
type OperationKeyType = 'minimize' | 'quit' | 'maximize' | 'unmaximize' | 'hide' | 'show'; type OperationKeyType = 'minimize' | 'quit' | 'maximize' | 'unmaximize' | 'hide' | 'show';
const QuitTips = forwardRef((props: any, ref: any) => { const QuitTips = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
@ -66,4 +66,4 @@ const QuitTips = forwardRef((props: any, ref: any) => {
) )
}) })
export default memo(QuitTips) export default QuitTips

View File

@ -8,7 +8,7 @@ import {
VerticalAlignBottomOutlined VerticalAlignBottomOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import { Button, Input, message, Modal, Pagination, Popconfirm, Progress, Table } from 'antd'; import { Button, Input, message, Modal, Pagination, Popconfirm, Progress, Table } from 'antd';
import { forwardRef, useEffect, useImperativeHandle, useState, useRef, memo } from "react"; import { forwardRef, useEffect, useImperativeHandle, useState, useRef } from "react";
import { DeleteRoomFile, GetRoomFile, GetRoomFileDwUrl, GetRoomUpFileurl, GetRoomUserItem, PostRoomFile } from '@/api/Meeting'; import { DeleteRoomFile, GetRoomFile, GetRoomFileDwUrl, GetRoomUpFileurl, GetRoomUserItem, PostRoomFile } from '@/api/Meeting';
import axios from 'axios'; import axios from 'axios';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
@ -393,4 +393,4 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
) )
}) })
export default memo(SharedFilesModel) export default SharedFilesModel

View File

@ -2,7 +2,7 @@ import { GetRoomSingnIn, PostRoomSingnIn } from '@/api/Meeting';
import styles from '@/components/SingIn/index.module.scss' import styles from '@/components/SingIn/index.module.scss'
import { storage } from '@/utils'; import { storage } from '@/utils';
import { Button, message, Modal } from 'antd'; import { Button, message, Modal } from 'antd';
import { useState, useImperativeHandle, forwardRef, memo } from "react"; import { useState, useImperativeHandle, forwardRef } from "react";
const SingIn = forwardRef((props: any, ref: any) => { const SingIn = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeModal: () => { changeModal: () => {
@ -85,4 +85,4 @@ const SingIn = forwardRef((props: any, ref: any) => {
) )
}) })
export default memo(SingIn) export default SingIn

View File

@ -1,6 +1,6 @@
import styles from '@/components/SpeakerModeModal/index.module.scss' import styles from '@/components/SpeakerModeModal/index.module.scss'
import { Checkbox, Modal } from 'antd'; import { Checkbox, Modal } from 'antd';
import { useState, useImperativeHandle, forwardRef, memo } from "react"; import { useState, useImperativeHandle, forwardRef } from "react";
import { storage } from '@/utils'; import { storage } from '@/utils';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { GetSyncView } from '@/api/Meeting'; import { GetSyncView } from '@/api/Meeting';
@ -147,4 +147,4 @@ const FourScreenMode: React.FC<Props> = ({ onClick, meetingMode }) => {
) )
} }
export default memo(SpeakerModeModal) export default SpeakerModeModal

View File

@ -1,7 +1,7 @@
import styles from '@/components/StupWizard/index.module.scss' import styles from '@/components/StupWizard/index.module.scss'
import ImageUrl from '@/utils/package/imageUrl'; import ImageUrl from '@/utils/package/imageUrl';
import { Button, Checkbox, Empty, Input, message, Modal, Popover, Radio, Select, Slider, Space } from 'antd'; import { Button, Checkbox, Empty, Input, message, Modal, Popover, Radio, Select, Slider, Space } from 'antd';
import { forwardRef, useEffect, useImperativeHandle, useState, memo } from "react"; import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { agora } from '@/utils/package/agora' 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';
@ -34,9 +34,6 @@ const StupWizard = forwardRef((_props: any, ref: any) => {
} }
} }
storage.setItem('setting', JSON.stringify(setting)) storage.setItem('setting', JSON.stringify(setting))
},
getStupWizardModal: () => {
return isStupWizard
} }
})) }))
const [list, setList] = useState([ const [list, setList] = useState([
@ -133,11 +130,9 @@ const StupWizard = forwardRef((_props: any, ref: any) => {
}) })
const CurrencyComponents = () => { const CurrencyComponents = () => {
const [optionsValue, setOperationValue] = useState<'hide' | 'quit'>('hide'); const [optionsValue, setOperationValue] = useState<'hide' | 'quit'>('hide');
const [voiceStimulation, setVoiceStimulation] = useState(true);
const setting = JSON.parse(storage.getItem('setting') as string) const setting = JSON.parse(storage.getItem('setting') as string)
useEffect(() => { useEffect(() => {
setOperationValue(setting.closeSetting) setOperationValue(setting.closeSetting)
setVoiceStimulation(setting.voiceStimulation)
}, []); }, []);
return ( return (
<> <>
@ -156,33 +151,6 @@ const CurrencyComponents = () => {
<Radio value={'hide'}>退</Radio> <Radio value={'hide'}>退</Radio>
</Radio.Group> </Radio.Group>
</div> </div>
<div>
<span> <Popover
content={
<span
style={{
color: 'white'
}}>
</span>
}
title=""
>
<QuestionCircleOutlined style={{
color: 'white',
cursor: 'pointer',
marginRight: '10px'
}} />
</Popover></span>
<Radio.Group onChange={(e: any) => {
setting.voiceStimulation = e.target.value;
storage.setItem('setting', JSON.stringify(setting))
setVoiceStimulation(e.target.value)
}} style={{ flexShrink: 0, margin: '10px 0' }} value={voiceStimulation}>
<Radio value={true}></Radio>
<Radio value={false}></Radio>
</Radio.Group>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -245,25 +213,23 @@ const VideoComponents = () => {
}, [darkLightEnhancement]); }, [darkLightEnhancement]);
useEffect(() => { useEffect(() => {
if (typeof virtualBackground.sourceIndex === 'number') { if (typeof virtualBackground.sourceIndex === 'number') {
window.electron.getEnv().then(res=>{ if (import.meta.env.VITE_ENV === 'development') {
if (res === 'development') { window.electron.getAppPath().then((res: string) => {
window.electron.getAppPath().then((res: string) => { const imagePath = path.join(res, 'src', 'assets', 'virtualBackground', `${virtualBackground.sourceIndex + 1}.png`);
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, { agora.enableVirtualBackground(virtualBackground.isVirtualBackground, {
source: imagePath, source: imagePath,
background_source_type: 2, background_source_type: 2,
color: Number(virtualBackground.color), 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 { } else {
agora.enableVirtualBackground(virtualBackground.isVirtualBackground, { agora.enableVirtualBackground(virtualBackground.isVirtualBackground, {
background_source_type: 1, background_source_type: 1,
@ -730,18 +696,7 @@ const AudioComponents = () => {
ecordingVolume: e, ecordingVolume: e,
}) })
}} disabled={!audioDeviceManager.ecordingItem} /> }} disabled={!audioDeviceManager.ecordingItem} />
{/* || audioDeviceManager.autoEcordingVolume */}
</div> </div>
{/* <div style={{ marginBottom: '10px' }}>
<Checkbox onChange={async (e) => {
setting.autoEcordingVolume = e.target.checked;
storage.setItem('setting', JSON.stringify(setting))
setAudioDeviceManager({
...audioDeviceManager,
autoEcordingVolume: e.target.checked
})
}} checked={audioDeviceManager.autoEcordingVolume}></Checkbox>
</div> */}
<div> <div>
<div> <div>
<Checkbox checked={audioDeviceManager.isAINoiseReduction} onChange={(e) => { <Checkbox checked={audioDeviceManager.isAINoiseReduction} onChange={(e) => {
@ -774,6 +729,16 @@ const AudioComponents = () => {
</Radio.Group> </Radio.Group>
</div> </div>
</div> </div>
{/* <div>
<Checkbox onChange={async (e) => {
setting.autoEcordingVolume = e.target.checked;
storage.setItem('setting', JSON.stringify(setting))
setAudioDeviceManager({
...audioDeviceManager,
autoEcordingVolume: e.target.checked
})
}} checked={audioDeviceManager.autoEcordingVolume}></Checkbox>
</div> */}
</div> </div>
<div> <div>
<div className={styles.audioComponentsSelect}> <div className={styles.audioComponentsSelect}>
@ -1004,4 +969,4 @@ const FileComponents = () => {
) )
} }
export default memo(StupWizard) export default StupWizard

View File

@ -1,4 +1,4 @@
import { useEffect, memo } from "react"; import { useEffect } from "react";
import '@/components/TldrawView/index.scss' import '@/components/TldrawView/index.scss'
import { import {
Tldraw, Tldraw,
@ -44,4 +44,4 @@ const TldrawView: React.FC = () => {
</> </>
) )
} }
export default memo(TldrawView) export default TldrawView

View File

@ -1,106 +1,45 @@
import styles from '@/components/UpdateModal/index.module.scss' import styles from '@/components/UpdateModal/index.module.scss'
import ImageUrl from '@/utils/package/imageUrl'; import ImageUrl from '@/utils/package/imageUrl';
import { getUpdateUrl, isVersion } from '@/utils/package/public'; import { getUpdateUrl } from '@/utils/package/public';
import { ExclamationCircleFilled } from '@ant-design/icons';
import { Button, Flex, Modal, Progress } from 'antd'; import { Button, Flex, Modal, Progress } from 'antd';
import { forwardRef, useImperativeHandle, useState, memo, useEffect } from "react"; import { forwardRef, useImperativeHandle, useState } from "react";
const { confirm } = Modal;
const UpdateModal = forwardRef((_props: any, ref: any) => { const UpdateModal = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeModal: (data: any) => { changeModal: (data: any) => {
if (JSON.stringify(data).includes('Error') || JSON.stringify(data).includes('{}')) { let dataJson = JSON.parse(data)
setIsError(res => { getContent()
if (res) { if (dataJson.type === '0') { // 打开弹窗
setIsError(false) setIsUpdateModal(true)
confirm({ } else if (dataJson.type === '1') { // 下载中 返回进度值
keyboard: false, setProgress(dataJson.value.toFixed(2))
title: '提示', } else if (dataJson.type === '2') { // 下载完成
icon: <ExclamationCircleFilled />, setProgress(100)
content: `更新失败,请尝试手动更新!`,
centered: true,
okText: '立即更新',
wrapClassName: 'hideCancelText',
cancelText: '',
async onOk() {
isVersion((bool: boolean, req: any) => {
if (bool && req) {
window.electron.getEnv().then(res => {
location.href = `${getUpdateUrl(res)}/${req.path}`
})
}
})
},
})
}
return res
})
} else {
try {
let dataJson = JSON.parse(data)
if (dataJson.type === '0') { // 打开弹窗
setProgress(res => {
if (res) {
} else {
window.electron.onDownload('1')
}
return res
})
} else if (dataJson.type === '1') { // 下载中 返回进度值
if (dataJson.value === 100 && location.hash.indexOf('/meeting') === -1) {
setIsUpdateModal(true)
getContent()
}
setProgress(dataJson.value.toFixed(2))
} else if (dataJson.type === '2') { // 下载完成
setProgress(100)
if (location.hash.indexOf('/meeting') === -1) {
setIsUpdateModal(true)
getContent()
}
} else if (dataJson.type === '3') {
if (location.hash.indexOf('/meeting') === -1) {
setIsUpdateModal(true)
getContent()
}
}
} catch (error) {
}
} }
} }
})) }))
const [isUpdateModal, setIsUpdateModal] = useState(false); const [isUpdateModal, setIsUpdateModal] = useState(false);
const [progress, setProgress] = useState(0); // 下载进度值 const [progress, setProgress] = useState(0); // 下载进度值
const [updateContent, setUpdateContent] = useState('') // 版本更新内容 const [updateContent, setUpdateContent] = useState('') // 版本更新内容
const [env, setEnv] = useState('')
const [_isError, setIsError] = useState(true)
useEffect(() => {
window.electron.getEnv().then(res => {
setEnv(res)
})
}, [])
function getContent() { function getContent() {
window.electron.getEnv().then(res => { fetch(`${getUpdateUrl(import.meta.env.VITE_ENV)}/update.txt?t=${+new Date()}`) // 配置服务器地址
fetch(`${getUpdateUrl(res)}/update.txt?t=${+new Date()}`) // 配置服务器地址 .then(async response => {
.then(async response => { if (response.status === 200) {
if (response.status === 200) { return setUpdateContent(await response.text())
return setUpdateContent(await response.text()) }
} throw new Error('Network response was not ok.');
throw new Error('Network response was not ok.'); })
}) .then(textContent => {
.then(_textContent => { })
}) .catch(error => {
.catch(_error => { });
});
})
} }
function closeModal() { function closeModal() {
// if (progress != 100) { if (progress != 100) {
// window.electron.onDownload('0') // 取消下载 window.electron.onDownload('0') // 取消下载
// } }
setIsUpdateModal(false) setIsUpdateModal(false)
} }
@ -110,12 +49,12 @@ const UpdateModal = forwardRef((_props: any, ref: any) => {
title="" title=""
open={isUpdateModal} open={isUpdateModal}
footer={null} footer={null}
onCancel={() => closeModal()} // onCancel={() => closeModal()}
centered centered
width={'400px'} width={'400px'}
className='modal-padding' className='modal-padding'
maskClosable={false} maskClosable={false}
closeIcon={progress === 100 ? false : true} closeIcon={false}
> >
<div className={styles.isUpdateModal} style={{ backgroundImage: `url(${ImageUrl.icon7})` }}> <div className={styles.isUpdateModal} style={{ backgroundImage: `url(${ImageUrl.icon7})` }}>
<div className={styles.remarks} dangerouslySetInnerHTML={{ __html: updateContent }}> <div className={styles.remarks} dangerouslySetInnerHTML={{ __html: updateContent }}>
@ -128,7 +67,7 @@ const UpdateModal = forwardRef((_props: any, ref: any) => {
style={{ width: '100%', height: '40px', marginBottom: '10px' }} style={{ width: '100%', height: '40px', marginBottom: '10px' }}
className={`m-ant-btn`} className={`m-ant-btn`}
></Button> ></Button>
{env === "development" ? <div className={styles.button2} onClick={() => setIsUpdateModal(false)}></div> : null} {import.meta.env.VITE_ENV === "development" ? <div className={styles.button2} onClick={() => setIsUpdateModal(false)}></div> : null}
</div> : progress < 100 ? </div> : progress < 100 ?
<div style={{ margin: '20px 0' }}> <div style={{ margin: '20px 0' }}>
{progress}% {progress}%
@ -148,4 +87,4 @@ const UpdateModal = forwardRef((_props: any, ref: any) => {
) )
}) })
export default memo(UpdateModal) export default UpdateModal

View File

@ -2,7 +2,7 @@ import { PutAlterUname } from '@/api/Meeting';
import styles from '@/components/UserName/index.module.scss' import styles from '@/components/UserName/index.module.scss'
import { storage } from '@/utils'; import { storage } from '@/utils';
import { Button, Input, message, Modal } from 'antd'; import { Button, Input, message, Modal } from 'antd';
import { useState, useImperativeHandle, forwardRef, memo } from "react"; import { useState, useImperativeHandle, forwardRef } from "react";
const UserName = forwardRef((props: any, ref: any) => { const UserName = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeModal: (data: any) => { changeModal: (data: any) => {
@ -47,10 +47,6 @@ const UserName = forwardRef((props: any, ref: any) => {
onClick={async () => { onClick={async () => {
const stateInfo = await JSON.parse(storage.getItem('stateInfo') as string); const stateInfo = await JSON.parse(storage.getItem('stateInfo') as string);
if (info.userName) { if (info.userName) {
if (info.userName.length > 10) {
message.error('用户名称最多10个字')
return
}
PutAlterUname({ PutAlterUname({
nickName: info.userName, nickName: info.userName,
roomNum: stateInfo.channelId, roomNum: stateInfo.channelId,
@ -75,4 +71,4 @@ const UserName = forwardRef((props: any, ref: any) => {
) )
}) })
export default memo(UserName) export default UserName

View File

@ -3,7 +3,7 @@ import styles from '@/components/UserVideo/index.module.scss'
import { GetPolling } from '@/api/Meeting'; import { GetPolling } from '@/api/Meeting';
import { agora } from '@/utils/package/agora'; import { agora } from '@/utils/package/agora';
import { Button, Empty, Select, message } from 'antd'; import { Button, Empty, Select, message } from 'antd';
import { useEffect, useState, memo } from "react"; import { useEffect, useState } from "react";
import { useLocation } from 'react-router'; import { useLocation } from 'react-router';
import { VideoStreamType } from 'agora-electron-sdk'; import { VideoStreamType } from 'agora-electron-sdk';
const { setInterval, clearInterval } = require('timers'); const { setInterval, clearInterval } = require('timers');
@ -154,4 +154,4 @@ const UserVideo: React.FC = () => {
) )
} }
export default memo(UserVideo) export default UserVideo

View File

@ -121,6 +121,14 @@
font-size: 14px; font-size: 14px;
} }
} }
>div:nth-child(2) {
cursor: pointer;
>img {
width: 16px;
}
}
} }
>div:nth-child(2) { >div:nth-child(2) {

View File

@ -2,7 +2,7 @@ import styles from '@/page/Home/Index/index.module.scss'
import { useEffect, useState, useRef } from "react"; import { useEffect, useState, useRef } from "react";
import Operation from '@/components/Operation'; import Operation from '@/components/Operation';
import { Button, Input, Modal, Pagination, Empty, message, Popover, Popconfirm, DatePicker, Select, Radio } from "antd"; import { Button, Input, Modal, Pagination, Empty, message, Popover, Popconfirm, DatePicker, Select, Radio } from "antd";
import { GetRoom, PostRoom, GetCheckoutRoomNum, GetRoomRtcToken, DeleteRoom, GetRecord, PostRoomInfo } from '@/api/Home/Index'; import { GetRoom, PostRoom, GetCheckoutRoomNum, GetRoomRtcToken, DeleteRoom, GetRecord, PostRoomInfo, GetQrcode } from '@/api/Home/Index';
import ImageUrl from '@/utils/package/imageUrl' import ImageUrl from '@/utils/package/imageUrl'
import { ExclamationCircleFilled, ReloadOutlined } from '@ant-design/icons'; import { ExclamationCircleFilled, ReloadOutlined } from '@ant-design/icons';
import JoinSetting from '@/components/JoinSetting'; import JoinSetting from '@/components/JoinSetting';
@ -14,10 +14,6 @@ import dayjs from 'dayjs';
import StupWizard from '@/components/StupWizard'; import StupWizard from '@/components/StupWizard';
import { GetSubDpList } from '@/api/Home/User'; import { GetSubDpList } from '@/api/Home/User';
import FeedBackModel from '@/components/FeedBackModel'; import FeedBackModel from '@/components/FeedBackModel';
import { PostHomeVerLog } from '@/api/Meeting';
import Code from '@/components/Code';
import { isVersion } from "@/utils/package/public";
const { setInterval, clearInterval } = require('timers'); const { setInterval, clearInterval } = require('timers');
const fs = require('fs').promises; const fs = require('fs').promises;
const { exec } = require('child_process'); const { exec } = require('child_process');
@ -50,6 +46,7 @@ const Index: React.FC = () => {
const [timeData, setTimeData] = useState<any>([]); const [timeData, setTimeData] = useState<any>([]);
const [isCreateRoom, setIsCreateRoom] = useState<boolean>(false); const [isCreateRoom, setIsCreateRoom] = useState<boolean>(false);
const [allowAnonymous, setAllowAnonymous] = useState(true); const [allowAnonymous, setAllowAnonymous] = useState(true);
const [baseImage, setBaseImage] = useState('');
const userInfo = JSON.parse(storage.getItem('user') as string) const userInfo = JSON.parse(storage.getItem('user') as string)
useEffect(() => { useEffect(() => {
setUser(userInfo) setUser(userInfo)
@ -248,7 +245,35 @@ const Index: React.FC = () => {
<span>{item.roomNum}</span> <span>{item.roomNum}</span>
<img src={ImageUrl.icon10} alt="" /> <img src={ImageUrl.icon10} alt="" />
</div> </div>
<Code roomNum={item.roomNum}></Code> <Popover
placement="bottom"
onOpenChange={(e: boolean) => {
setBaseImage('')
if (e) {
GetQrcode(item.roomNum, import.meta.env.VITE_ENV === 'development' ? 'trial' : 'release').then(res => {
if (res.code === 200) {
setBaseImage(res.data)
}
})
}
}}
content={
baseImage ? <div>
<img style={{ width: '200px', margin: '0 auto' }} src={`data:image/png;base64,${baseImage}`} alt="" />
<div style={{ color: 'white', textAlign: 'center', fontSize: '16px', marginTop: '10px' }}>
<span></span><br />
<span></span>
</div>
</div> : <div>
<Empty description={'二维码加载中,请稍后'} />
</div>
}
>
<div title='小程序'>
<img src={ImageUrl.icon55} alt="" />
</div>
</Popover>
</div> </div>
<div> <div>
{role.ID.includes(userInfo.roleId) ? <Popover {role.ID.includes(userInfo.roleId) ? <Popover
@ -310,43 +335,28 @@ const Index: React.FC = () => {
<Button type="primary" <Button type="primary"
iconPosition={'end'} iconPosition={'end'}
onClick={async () => { onClick={async () => {
storage.setItem('loading', true) if (role.ID.includes(userInfo.roleId)) {
isVersion((bool: boolean) => { joinSettingRef.current.changeModal(item.roomNum)
storage.setItem('loading', false) } else {
if (bool) { storage.setItem('loading', true)
window.electron.onDownload('3') postRefresh(() => {
} else { getRoomRtcToken(item.roomNum, (options: any) => {
if (role.ID.includes(userInfo.roleId)) { if (options) {
joinSettingRef.current.changeModal(item.roomNum) navigate(`/meeting`, {
} else { state: {
storage.setItem('loading', true) channelId: item.roomNum,
postRefresh(() => { token: options.token,
getRoomRtcToken(item.roomNum, async (options: any) => { tokenA: options.tokenA,
if (options) { roomId: item.id,
await window.electron.getVersion().then(async req => { roomName: item.roomName,
await PostHomeVerLog({ enableMicr: false,
version: req, enableCamera: false,
platformType: 1,
roomNum: item.roomNum,
})
})
navigate(`/meeting`, {
state: {
channelId: item.roomNum,
token: options.token,
tokenA: options.tokenA,
roomId: item.id,
roomName: item.roomName,
enableMicr: false,
enableCamera: false,
}
})
} }
}) })
}) }
} })
} })
}) }
}} }}
icon={<img src={ImageUrl.icon9} alt="" />} icon={<img src={ImageUrl.icon9} alt="" />}
className='m-ant-btn'> className='m-ant-btn'>

View File

@ -458,9 +458,6 @@ const User: React.FC = () => {
if (!addUserFrom.UserName && isCreateUser !== 'batch') { if (!addUserFrom.UserName && isCreateUser !== 'batch') {
return message.error('请输入用户名称!') return message.error('请输入用户名称!')
} }
if (addUserFrom.UserName.length > 10) {
return message.error('用户名称最多10个字')
}
if (addUserFrom.year === '') { if (addUserFrom.year === '') {
return message.error('请输入届!') return message.error('请输入届!')
} }

View File

@ -120,26 +120,8 @@
@else if $i ==4 { @else if $i ==4 {
flex-shrink: 0; flex-shrink: 0;
color: #ccc;
>div:nth-child(1) { font-size: 16px;
color: #ccc;
font-size: 16px;
display: flex;
align-items: center;
>span:nth-child(2) {
background-color: red;
font-size: 12px;
padding: 0px 4px;
border-radius: 10px;
margin-left: 4px;
}
}
>div:nth-child(2) {
width: 100%;
margin-top: 10px;
}
} }
@else if $i ==5 { @else if $i ==5 {

View File

@ -1,7 +1,7 @@
import styles from '@/page/Home/index.module.scss' import styles from '@/page/Home/index.module.scss'
import { useEffect, useState, useRef } from "react"; import { useEffect, useState, useRef } from "react";
import { Outlet, useNavigate } from 'react-router-dom'; import { Outlet, useNavigate } from 'react-router-dom';
import { Button, Popconfirm, Popover } from 'antd'; import { Popconfirm, Popover } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn' import 'dayjs/locale/zh-cn'
import { storage } from '@/utils'; import { storage } from '@/utils';
@ -45,7 +45,6 @@ const Home: React.FC = () => {
]); ]);
const [userInfo, setUserInfo] = useState<any>({}) const [userInfo, setUserInfo] = useState<any>({})
const [version, setVersion] = useState<string>('') const [version, setVersion] = useState<string>('')
const [update, setUpdate] = useState(false)
const [dateInfo, setDateInfo] = useState<{ const [dateInfo, setDateInfo] = useState<{
work: string; work: string;
time: string; time: string;
@ -68,18 +67,11 @@ const Home: React.FC = () => {
}) })
}; };
const timer = setInterval(updateTime, 1000); const timer = setInterval(updateTime, 1000);
window.addEventListener('customStorageChange', handleCustomStorageChange);
return () => { return () => {
window.removeEventListener('customStorageChange', handleCustomStorageChange);
clearInterval(timer); clearInterval(timer);
}; };
}, []); }, []);
const handleCustomStorageChange = (e: any): void => {
if (e.key === 'isUpdate') {
setUpdate(e.value)
}
};
const changtNavList = (index: number, bool?: boolean): void => { const changtNavList = (index: number, bool?: boolean): void => {
const newNavList = [...navList]; const newNavList = [...navList];
if (typeof bool === 'boolean') { if (typeof bool === 'boolean') {
@ -134,22 +126,14 @@ const Home: React.FC = () => {
) )
})} })}
</div> </div>
<div className='drag'> <div>
<div> {version}
<span>:{version}</span>
{update ? <span>new</span> : null}
</div>
{update ? <div>
<Button type="primary" className='m-ant-btn' style={{ width: '100%' }} onClick={() => {
window.electron.onDownload('3')
}}></Button>
</div> : null}
</div> </div>
<div> <div>
<Popover <Popover
placement="right" placement="right"
content={ content={
<img style={{ width: '400px' }} src={`https://meeting-api.23544.com/meeting/update/ddq.png?t=${+new Date()}`} alt="" /> <img style={{ width: '400px' }} src={'https://meeting-api.23544.com/meeting/update/ddq.png'} alt="" />
} }
> >
<div className='drag' title='反馈建议'> <div className='drag' title='反馈建议'>

View File

@ -4,14 +4,11 @@ import { useEffect, useState } from "react";
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Input, Button, Checkbox, message, Modal } from "antd" import { Input, Button, Checkbox, message, Modal } from "antd"
import { storage } from '@/utils' import { storage } from '@/utils'
import { GetCheckOnline, 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 { 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';
import { ExclamationCircleFilled } from '@ant-design/icons';
import { isVersion } from '@/utils/package/public';
const { confirm } = Modal;
const Login: React.FC = () => { const Login: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [accountPasswordStatus, setAccountPasswordStatus] = useState<boolean>(false); const [accountPasswordStatus, setAccountPasswordStatus] = useState<boolean>(false);
@ -40,16 +37,13 @@ const Login: React.FC = () => {
roomNum: '', roomNum: '',
}) })
const [nameModal, setNameModal] = useState(false) const [nameModal, setNameModal] = useState(false)
const [env, setEnv] = useState('')
useEffect(() => { useEffect(() => {
window.electron.setMainWindowSize({ window.electron.setMainWindowSize({
width: 752, width: 752,
height: 520, height: 520,
key: 'login' key: 'login'
}) })
window.electron.getEnv().then(res => {
setEnv(res)
})
if (storage.getItem('login')) { if (storage.getItem('login')) {
const login = JSON.parse(storage.getItem('login') as string); const login = JSON.parse(storage.getItem('login') as string);
const data = { const data = {
@ -106,32 +100,7 @@ const Login: React.FC = () => {
} }
GetCheckUser(operation.account).then(res => { GetCheckUser(operation.account).then(res => {
if (res.code === 200) { if (res.code === 200) {
if (res.data) { res.data ? setAccountPasswordStatus(true) : message.error('账号不存在!')
GetCheckOnline(operation.account).then(req => {
if (req.code === 200) {
if (req.data) {
confirm({
title: '提示',
icon: <ExclamationCircleFilled />,
content: `账号已在其他地方登录,是否强制登陆?`,
centered: true,
okText: '确定',
cancelText: '取消',
async onOk() {
setAccountPasswordStatus(true)
},
onCancel() {
}
})
} else {
setAccountPasswordStatus(true)
}
}
})
} else {
message.error('账号不存在!')
}
} }
}) })
} }
@ -204,7 +173,7 @@ const Login: React.FC = () => {
<> <>
<div className={styles.login}> <div className={styles.login}>
<div className={styles.loginBg}> <div className={styles.loginBg}>
{env ? <img src={env === 'xy' ? ImageUrl.icon53 : ImageUrl.icon1} alt="" /> : null} <img src={import.meta.env.VITE_ENV === 'xy' ? ImageUrl.icon53 : ImageUrl.icon1} alt="" />
</div> </div>
<div className={styles.loginContent}> <div className={styles.loginContent}>
<div> <div>
@ -346,58 +315,47 @@ const Login: React.FC = () => {
if (!anonInfo.nickName) { if (!anonInfo.nickName) {
return message.error('请输入参会昵称!') return message.error('请输入参会昵称!')
} }
if (anonInfo.nickName.length > 10) {
return message.error('参会昵称最多10个字')
}
storage.setItem('loading', true) storage.setItem('loading', true)
isVersion((bool: boolean) => { PostAnonLogin(anonInfo).then(async (res) => {
storage.setItem('loading', false) if (res.code == 200) {
if (bool) { storage.setItem('user', JSON.stringify(res.data))
window.electron.onDownload('3') storage.setItem('userLogin', true)
} else { await window.electron.startSignalr(res.data)
storage.setItem('loading', true) getRoomRtcToken(anonInfo.roomNum, (options: any) => {
PostAnonLogin(anonInfo).then(async (res) => { if (options) {
if (res.code == 200) { GetRoomInfo(anonInfo.roomNum).then(async (res) => {
storage.setItem('user', JSON.stringify(res.data)) if (res.code === 200) {
storage.setItem('userLogin', true) setTimeout(() => {
await window.electron.startSignalr(res.data)
getRoomRtcToken(anonInfo.roomNum, (options: any) => {
if (options) {
GetRoomInfo(anonInfo.roomNum).then(async (res) => {
if (res.code === 200) {
setTimeout(() => {
storage.setItem('loading', false)
window.electron.getWindowSize().then((res: any) => {
window.electron.setMainWindowSize({
width: Math.ceil(res.width / 1.5),
height: Math.ceil(res.height / 1.3),
})
})
navigate(`/meeting`, {
state: {
channelId: anonInfo.roomNum,
token: options.token,
tokenA: options.tokenA,
roomId: res.data.id,
roomName: res.data.roomName,
enableMicr: false,
enableCamera: false,
}
})
}, 2000)
} else {
storage.setItem('loading', false)
}
}).catch(() => {
storage.setItem('loading', false) storage.setItem('loading', false)
}) window.electron.getWindowSize().then((res: any) => {
window.electron.setMainWindowSize({
width: Math.ceil(res.width / 1.5),
height: Math.ceil(res.height / 1.3),
})
})
navigate(`/meeting`, {
state: {
channelId: anonInfo.roomNum,
token: options.token,
tokenA: options.tokenA,
roomId: res.data.id,
roomName: res.data.roomName,
enableMicr: false,
enableCamera: false,
}
})
}, 2000)
} else {
storage.setItem('loading', false)
} }
}).catch(() => {
storage.setItem('loading', false)
}) })
} }
}).catch(() => {
storage.setItem('loading', false)
}) })
} }
}).catch(() => {
storage.setItem('loading', false)
}) })
}}></Button> }}></Button>
</div> </div>

View File

@ -46,8 +46,6 @@
font-size: 14px; font-size: 14px;
color: #F3F3F5; color: #F3F3F5;
margin-left: 4px; margin-left: 4px;
display: flex;
align-items: center;
} }
>div {} >div {}
@ -79,8 +77,6 @@
>span { >span {
font-size: 14px; font-size: 14px;
color: #F3F3F5; color: #F3F3F5;
display: flex;
align-items: center;
} }
>div { >div {

View File

@ -206,30 +206,19 @@ const ChatBigWindow: React.FC = () => {
></Button> : null} ></Button> : null}
</div> : <div style={{ color: 'white' }}></div> </div> : <div style={{ color: 'white' }}></div>
}> }>
<div title={item.userName}> <div>
<div><Avatar name={item.userName} /></div> <div><Avatar name={item.userName} /></div>
{item.uid !== user.uid ? {item.uid !== user.uid ?
<span> <span>{item.userName} <span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span></span> :
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '150px' }}>{item.userName}</span> <span> <span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')} </span>{item.userName}</span>
<span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span>
</span> :
<span>
<span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span>
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '150px' }}>{item.userName}</span>
</span>
} }
</div> </div>
</Popover> : <div title={item.userName}> </Popover> : <div>
<div><Avatar name={item.userName} /></div> <div><Avatar name={item.userName} /></div>
{item.uid !== user.uid ? {item.uid !== user.uid ?
<span> <span>{item.userName}<span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span></span> :
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '150px' }}>{item.userName}</span> <span><span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')} </span>{item.userName}</span>
<span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span>
</span> :
<span>
<span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span>
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '150px' }}>{item.userName}</span>
</span>
} }
</div>} </div>}
<div>{item.message}</div> <div>{item.message}</div>

View File

@ -31,7 +31,7 @@
display: flex; display: flex;
>span:nth-child(1) { >span:nth-child(1) {
word-break: break-all; white-space: nowrap;
color: #ff970f; color: #ff970f;
} }

View File

@ -21,10 +21,7 @@ const NoticeWindow: React.FC = () => {
api.open({ api.open({
message: '', message: '',
description: <div> description: <div>
<div style={{ fontSize: '16px' }}> <span style={{ fontSize: '16px' }}>{noticeItem.uname}</span>
<div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={noticeItem.uname}>{noticeItem.uname}</div>
<div></div>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }} className='drag'> <div style={{ display: 'flex', justifyContent: 'flex-end' }} className='drag'>
<Button <Button
type="primary" type="primary"

View File

@ -153,10 +153,10 @@ const ShareScreenWindow: React.FC = () => {
<> <>
<div className={styles.shareScreenWindow} style={{ width: isExpand ? '100%' : '100%' }}> <div className={styles.shareScreenWindow} style={{ width: isExpand ? '100%' : '100%' }}>
<div className={styles.shareScreenWindowTitle}> <div className={styles.shareScreenWindowTitle}>
<span>{changeCurrentSeconds(timeStr)} {!isExpand ? '共享中' : ''} <span>{changeCurrentSeconds(timeStr)}
{networkIcon(currentEffective)} {networkIcon(currentEffective)}
<span style={{ color: 'white', marginLeft: '30px' }}> <span style={{ color: 'white', marginLeft: '30px' }}>
{!isExpand ? <span style={{ marginRight: '10px' }}>{networkQuality.level}</span> : ''} <span style={{ marginRight: '10px' }}>{networkQuality.level}</span>
<span>{networkOther.lastmileDelay}ms</span> <span>{networkOther.lastmileDelay}ms</span>
</span> </span>
</span> </span>
@ -202,7 +202,7 @@ const ShareScreenWindow: React.FC = () => {
}} }}
key={index}> key={index}>
<div> <div>
{!item.active ? <div style={{ backgroundImage: `url(${ImageUrl.icon49})`, transition: '0.18s' }} id={`micr-item-${userInfo.uid}`}> {!item.active ? <div style={{ backgroundImage: `url(${ImageUrl.icon49})` }} id={`micr-item-${userInfo.uid}`}>
</div> : ''} </div> : ''}
{item.select ? <img src={item.iconSelect} alt="" /> : <img src={item.active ? item.iconActive : item.icon} alt="" />} {item.select ? <img src={item.iconSelect} alt="" /> : <img src={item.active ? item.iconActive : item.icon} alt="" />}
</div> </div>
@ -223,7 +223,7 @@ const ShareScreenWindow: React.FC = () => {
<div className={`${styles.shareScreenWindowExpand} drag`} onClick={() => { <div className={`${styles.shareScreenWindowExpand} drag`} onClick={() => {
setIsExpand(!isExpand) setIsExpand(!isExpand)
window.electron.setChildWindow({ window.electron.setChildWindow({
width: isExpand ? 440 : 440 / 1.6, width: isExpand ? 440 : 440 / 2,
key: 'shareScreenWindow', key: 'shareScreenWindow',
}) })
}}> }}>

View File

@ -303,7 +303,7 @@
// 演讲者模式 // 演讲者模式
.meetingContentBodyLeftSpeakerMode { .meetingContentBodyLeftSpeakerMode {
width: 270px; width: 18%;
overflow-y: auto; overflow-y: auto;
height: 100%; height: 100%;
@ -312,7 +312,7 @@
} }
.meetingContentSwiperCard { .meetingContentSwiperCard {
height: 160px; height: calc(100% / 6);
} }
} }
@ -379,7 +379,7 @@
right: 0; right: 0;
bottom: 0; bottom: 0;
height: 100% !important; height: 100% !important;
width: calc(100% - 270px) !important; width: calc(100% - 18%) !important;
z-index: 2; z-index: 2;
} }
@ -390,7 +390,7 @@
.meetingContentSwiperCard { .meetingContentSwiperCard {
height: 160px; height: 160px;
width: 270px; width: calc(100% / 6);
border-radius: 10px; border-radius: 10px;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
@ -444,8 +444,8 @@
color: white; color: white;
border: 1px white solid; border: 1px white solid;
font-size: 20px; font-size: 20px;
width: 25px; width: 30px;
height: 25px; height: 30px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@ -549,8 +549,6 @@
font-size: 14px; font-size: 14px;
color: #F3F3F5; color: #F3F3F5;
margin-left: 4px; margin-left: 4px;
display: flex;
align-items: center;
} }
>div { >div {
@ -662,8 +660,6 @@
font-size: 14px; font-size: 14px;
color: #F3F3F5; color: #F3F3F5;
margin-left: 4px; margin-left: 4px;
display: flex;
align-items: center;
} }
} }
@ -693,8 +689,6 @@
>span { >span {
font-size: 14px; font-size: 14px;
color: #F3F3F5; color: #F3F3F5;
display: flex;
align-items: center;
} }
>div { >div {
@ -804,7 +798,7 @@
} }
>img { >img {
height: 35px; height: 50px;
} }
>span { >span {
@ -826,8 +820,8 @@
} }
>label { >label {
height: 35px; height: 50px;
height: 35px; height: 50px;
cursor: pointer; cursor: pointer;
position: relative; position: relative;

View File

@ -8,12 +8,12 @@ import { SearchOutlined, EllipsisOutlined, ExclamationCircleFilled, FullscreenEx
import { useLocation, useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import { thumbImageBufferToBase64 } from '@/utils/package/base64' import { thumbImageBufferToBase64 } from '@/utils/package/base64'
import { storage } from '@/utils'; import { storage } from '@/utils';
import { GetRoomUser, PostOpenMicr, GetSharedScreen, PostOpenCamera, GetLeaveAll, PostRoomManager, DeleteRoomManager, GetRoomKickout, GetShowUser, PostShowUser, PostMuteAll, GetRoomUserItem, GetApplySpeak, PostSharedScreen, PostStopSharedScreen } from '@/api/Meeting'; import { GetRoomUser, PostOpenMicr, PostOpenCamera, GetLeaveAll, PostRoomManager, DeleteRoomManager, GetRoomKickout, GetShowUser, PostShowUser, PostMuteAll, GetRoomUserItem, GetApplySpeak, PostSharedScreen } 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 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, RenderModeType, RtcConnection, RtcStats, StreamPublishState, UserOfflineReasonType, VideoSourceType, VideoStreamType } from 'agora-electron-sdk'; import { AudioVolumeInfo, ConnectionChangedReasonType, ConnectionStateType, LocalVideoStreamReason, LocalVideoStreamState, RenderModeType, RtcConnection, RtcStats, UserOfflineReasonType, VideoSourceType, VideoStreamType } from 'agora-electron-sdk';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import SharedFilesModel from '@/components/SharedFilesModel'; import SharedFilesModel from '@/components/SharedFilesModel';
import StupWizard from '@/components/StupWizard'; import StupWizard from '@/components/StupWizard';
@ -26,7 +26,6 @@ import MeetingDisconnected from '@/components/MeetingDisconnected';
import SingIn from '@/components/SingIn'; import SingIn from '@/components/SingIn';
import UserName from '@/components/UserName'; import UserName from '@/components/UserName';
import { GetRoomRtcToken } from '@/api/Home/Index'; import { GetRoomRtcToken } from '@/api/Home/Index';
import Code from '@/components/Code';
const { setTimeout, setInterval, clearTimeout, clearInterval } = require('timers'); const { setTimeout, setInterval, clearTimeout, clearInterval } = require('timers');
const { confirm } = Modal; const { confirm } = Modal;
const { exec } = require('child_process'); const { exec } = require('child_process');
@ -159,13 +158,10 @@ const Meeting: React.FC = () => {
itemIndex: 0, itemIndex: 0,
rowIndex: 0, rowIndex: 0,
}); });
const [_audioStatus, setAudioStatus] = useState<StreamPublishState>(1);
const [_videoStatus, setVideoStatus] = useState<StreamPublishState>(1);
const [roomUserList, setRoomUserList] = useState<any>([]) const [roomUserList, setRoomUserList] = useState<any>([])
const [_speackUid, setSpeackUid] = useState<any>([]) const [_speackUid, setSpeackUid] = useState<any>([])
const [currentSpeakUser, setCurrentSpeakUser] = useState<any>([]) const [currentSpeakUser, setCurrentSpeakUser] = useState<any>([])
const [chatList, setChatList] = useState<any>([]) const [chatList, setChatList] = useState<any>([])
const [applyUserList, setApplyUserList] = useState<any>([])
const [isExpand, setIsExpand] = useState(false) const [isExpand, setIsExpand] = useState(false)
const [currentVideoId, setCurrentVideoId] = useState('') const [currentVideoId, setCurrentVideoId] = useState('')
const [currentVideoUid, setCurrentVideoUid] = useState('') const [currentVideoUid, setCurrentVideoUid] = useState('')
@ -218,7 +214,6 @@ const Meeting: React.FC = () => {
}); });
const [isVideoFullScreen, setIsVideoFullScreen] = useState<boolean>(false) const [isVideoFullScreen, setIsVideoFullScreen] = useState<boolean>(false)
const [observer, setObserver] = useState<IntersectionObserver>() const [observer, setObserver] = useState<IntersectionObserver>()
const [_activeSpeaker, setActiveSpeaker] = useState('')
let userInfo = JSON.parse(storage.getItem('user') as string) let userInfo = JSON.parse(storage.getItem('user') as string)
const msgTips = '您不是管理员或发言人,无法开启此功能!' const msgTips = '您不是管理员或发言人,无法开启此功能!'
const channel = new BroadcastChannel('meeting_channel'); const channel = new BroadcastChannel('meeting_channel');
@ -276,7 +271,7 @@ const Meeting: React.FC = () => {
case 'shareScreenWindowClose': case 'shareScreenWindowClose':
setCurrentSeconds(shareScreenWindowClose) setCurrentSeconds(shareScreenWindowClose)
await stopScreenCapture() await stopScreenCapture()
await allUserLook(userInfo.uid, userInfo.userName, true) await allUserLook(userInfo.uid, userInfo.userName)
break; break;
case 'shareScreenWindowfooterListsTitle': case 'shareScreenWindowfooterListsTitle':
switch (shareScreenWindowfooterListsTitle) { switch (shareScreenWindowfooterListsTitle) {
@ -723,7 +718,7 @@ const Meeting: React.FC = () => {
break; break;
// 扩展操作 // 扩展操作
case 'Operation': case 'Operation':
switch (item.contentString) { switch (item.type) {
} }
break; break;
@ -786,14 +781,6 @@ const Meeting: React.FC = () => {
break; break;
// 发言人用户信息刷新 // 发言人用户信息刷新
case 'ManagerRefresh': case 'ManagerRefresh':
if (!item.user.isRoomManager) {
setCurrentVideoId((res: any) => {
if (res === String(item.user.uid)) {
getShowUser()
}
return res
})
}
setAllUserListData('ManagerRefresh', item, async () => { setAllUserListData('ManagerRefresh', item, async () => {
if (item.user.uid === item.uid) { if (item.user.uid === item.uid) {
if (item.user.uid === userInfo.uid) { if (item.user.uid === userInfo.uid) {
@ -833,14 +820,73 @@ const Meeting: React.FC = () => {
break; break;
// 申请发言 // 申请发言
case 'ApplyToSpeak': case 'ApplyToSpeak':
window.electron.setChildWindowShow({ setIsScreenCapture(bool => {
key: 'noticeWindow', if (bool) {
bool: true window.electron.setChildWindowShow({
key: 'noticeWindow',
bool: true
})
channel.postMessage({
type: 'noticeItem',
noticeItem: item
});
} else {
api.open({
message: '',
description: <div>
<span style={{ fontSize: '16px' }}>{item.uname}</span>
<div style={{ display: 'flex', justifyContent: 'flex-end' }} className='drag'>
<Button
type="primary"
className='m-ant-btn'
onClick={async (e: any) => {
let i = e.nativeEvent.path;
if (i) {
i.forEach((i: any) => {
if (i.className === 'ant-notification-notice ant-notification-notice-closable') {
i.childNodes.forEach((row: any) => {
if (row.className === 'ant-notification-notice-close') {
row.click()
postRoomManager({
roomId: state.roomId,
roomNum: state.channelId,
userId: item.uid
})
}
})
}
})
}
}}
></Button>
<Button
type="primary"
onClick={(e: any) => {
let item = e.nativeEvent.path;
if (item) {
item.forEach((item: any) => {
if (item.className === 'ant-notification-notice ant-notification-notice-closable') {
item.childNodes.forEach((row: any) => {
if (row.className === 'ant-notification-notice-close') {
row.click()
}
})
}
})
}
}}
style={{ backgroundColor: '#EC3C3C', marginLeft: '14px' }}
></Button>
</div>
</div>,
duration: 10,
placement: 'bottomRight',
showProgress: true,
pauseOnHover: false,
});
}
return bool
}) })
channel.postMessage({
type: 'noticeItem',
noticeItem: item
});
break; break;
// 管理员查看随机用户 // 管理员查看随机用户
case 'Watch': case 'Watch':
@ -885,7 +931,7 @@ const Meeting: React.FC = () => {
} }
}), }),
playBackDeviceId: res[1].playBackList.find((row: any) => row.deviceId === setting.playBackDeviceId) ? setting.playBackDeviceId : res[1].playBackList.length ? res[1].playBackList[0].deviceId : null, playBackDeviceId: res[1].playBackList.find((row: any) => row.deviceId === setting.playBackDeviceId) ? setting.playBackDeviceId : res[1].playBackList.length ? res[1].playBackList[0].deviceId : null,
ecordingVolume: setting.ecordingVolume ecordingVolume: res[1].ecordingVolume
} }
window.electron.onInvoke('sendDrivers', { window.electron.onInvoke('sendDrivers', {
uid: item.callerUid, uid: item.callerUid,
@ -976,35 +1022,6 @@ const Meeting: React.FC = () => {
return res return res
}) })
break; break;
// 设置发言人
case 'SetSpeaker':
window.electron.onInvoke('SetSpeakerCallback', item.RoomManagerInputDTO)
break;
// 扩展参数
case 'ReceivedOperation':
try {
const temp = JSON.parse(item.contentString)
if (temp.type === 'audio') {
await PostOpenMicr({
roomNum: temp.roomNum,
uid: temp.uid,
enableMicr: temp.enableMicr
})
} else {
await PostOpenCamera({
roomNum: temp.roomNum,
uid: temp.uid,
enableCamera: temp.enableCamera
})
}
} catch (error) {
}
break;
// 共享人取消共享屏幕
case 'StopedSharedScreen':
setVideoUser()
break;
} }
}) })
return () => { return () => {
@ -1160,104 +1177,6 @@ const Meeting: React.FC = () => {
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [isClickedMediaSteam]); }, [isClickedMediaSteam]);
// useEffect(() => {
// let timer: NodeJS.Timeout | string = '';
// if (audioStatus === 1 && videoStatus === 1) {
// if (timer) {
// clearTimeout(timer)
// timer = ''
// }
// timer = setTimeout(() => {
// setIsShare((req: any) => {
// if (!req) {
// setRoomUserList((res: any) => {
// let userItem = res.find((item: any) => item.uid === userInfo.uid)
// if (!role.ID.includes(userInfo.roleId) && userItem && userItem.isRoomManager) {
// DeleteRoomManager({
// roomId: state.roomId,
// roomNum: state.channelId,
// userId: userInfo.uid
// })
// confirm({
// title: '提示',
// icon: <ExclamationCircleFilled />,
// content: `由于您长时间未发言,已自动取消发言权限,是否重新申请发言?`,
// centered: true,
// okText: '确定',
// cancelText: '取消',
// async onOk() {
// GetApplySpeak(state.channelId).then(res => {
// if (res.code === 200) {
// setIsClicked(true);
// message.success('申请发言成功')
// }
// })
// },
// onCancel() {
// }
// })
// }
// clearTimeout(timer)
// timer = ''
// return res
// })
// }
// return req
// })
// }, 1000 * 60 * 5);
// } else {
// if (timer) {
// clearTimeout(timer)
// timer = ''
// }
// }
// return () => timer ? clearTimeout(timer) : '';
// }, [audioStatus, videoStatus]);
useEffect(() => {
let timer: NodeJS.Timeout | undefined;
if (timer) {
clearInterval(timer)
timer = undefined;
}
if (applyUserList.length) {
timer = setInterval(() => {
setRoomUserList((list: any) => {
let newApplyUserList = [...applyUserList]
newApplyUserList.forEach((item: any, index: number) => {
const user = list.find((i: any) => i.uid === item.uid)
if (user) {
if (user.isRoomManager) {
newApplyUserList.splice(index, 1)
} else {
item.status--
if (item.status <= 0) {
message.error(`设置${user.userName}发言人失败!`)
newApplyUserList.splice(index, 1)
}
}
} else {
newApplyUserList.splice(index, 1)
}
});
if (newApplyUserList.length === 0) {
clearInterval(timer)
timer = undefined;
}
setApplyUserList(newApplyUserList)
return list
})
}, 1000);
} else {
if (timer) {
clearInterval(timer)
timer = undefined;
}
}
return () => timer ? clearTimeout(timer) : null;
}, [applyUserList]);
useEffect(() => { useEffect(() => {
const elements = document.querySelectorAll('.intersectionObserver-view'); const elements = document.querySelectorAll('.intersectionObserver-view');
if (elements.length && currentVideoId) { if (elements.length && currentVideoId) {
@ -1346,32 +1265,6 @@ const Meeting: React.FC = () => {
channelId: connection.channelId, channelId: connection.channelId,
renderMode: RenderModeType.RenderModeFit renderMode: RenderModeType.RenderModeFit
}) })
setCurrentVideoId((res: any) => {
if (res === String(remoteUid)) {
let dom: any;
setCurrentLookUserStatus(req => {
switch (req) {
case 1:
dom = document.getElementById(`video-source-camera-primary`) as HTMLElement
break;
case 2:
dom = document.getElementById(`video-source-screen`) as HTMLElement;
break;
case 3:
dom = document.getElementById(`video-source-remote-screen`) as HTMLElement
break;
case 4:
dom = document.getElementById(`video-source-remote-camera`) as HTMLElement
break;
}
if (dom && dom.childNodes.length === 1) {
renderVideo(String(remoteUid))
}
return req
})
}
return res
})
}, 1000); }, 1000);
} }
} }
@ -1428,20 +1321,6 @@ const Meeting: React.FC = () => {
} else if (state === 3) { } else if (state === 3) {
meetingDisconnectedRef.current.changeModal(false) meetingDisconnectedRef.current.changeModal(false)
setIsAgoraDisconnected(false) setIsAgoraDisconnected(false)
} else if (state === 5) {
confirm({
keyboard: false,
title: '提示',
icon: <ExclamationCircleFilled />,
content: `重连失败,请退出房间重试!`,
centered: true,
okText: '退出',
wrapClassName: 'hideCancelText',
cancelText: '',
async onOk() {
leaveChannel()
},
})
} }
}, },
onConnectionLost: () => { onConnectionLost: () => {
@ -1461,33 +1340,18 @@ const Meeting: React.FC = () => {
if (bool) { if (bool) {
stopScreenCapture() stopScreenCapture()
setSharedScreenItem('') setSharedScreenItem('')
allUserLook(userInfo.uid, userInfo.userName, true) allUserLook(userInfo.uid, userInfo.userName)
} }
return bool return bool
}) })
} else if (reason === 3 || reason === 4) { } else if (reason === 3 || reason === 4) {
message.error({ message.error({
content: <div><span style={{ color: '#606fc7', cursor: 'pointer' }} onClick={() => { content: <div><span style={{ color: '#606fc7', cursor: 'pointer' }} onClick={() => {
stupWizardRef.current.changeModal(1); stupWizardRef.current.changeModal(1);
}}></span></div>, }}></span></div>,
duration: 15, duration: 60,
key: 'cameraTemporarily' key: 'cameraTemporarily'
}); });
} else if (_state === 3) {
if (!stupWizardRef.current.getStupWizardModal()) {
await PostOpenCamera({
roomNum: state.channelId,
uid: userInfo.uid,
enableCamera: false
})
message.error({
content: <div><span style={{ color: '#606fc7', cursor: 'pointer' }} onClick={() => {
stupWizardRef.current.changeModal(1);
}}></span></div>,
duration: 15,
key: 'cameraTemporarily'
});
}
} }
}, },
onTokenPrivilegeWillExpire: async (connection: RtcConnection, _token: string) => { onTokenPrivilegeWillExpire: async (connection: RtcConnection, _token: string) => {
@ -1499,23 +1363,6 @@ const Meeting: React.FC = () => {
}) })
} }
}) })
},
onVideoPublishStateChanged: (_source: VideoSourceType, _channel: string, _oldState: StreamPublishState, newState: StreamPublishState, _elapseSinceLastState: number) => {
setVideoStatus(newState)
},
onAudioPublishStateChanged: (_channel: string, _oldState: StreamPublishState, newState: StreamPublishState, _elapseSinceLastState: number) => {
setAudioStatus(newState)
},
onActiveSpeaker: (_connection: RtcConnection, uid: number) => {
if (String(uid).length !== 9) {
setActiveSpeaker(String(uid))
}
setIsShare((res: any) => {
if (!res && String(uid).length !== 9) {
renderVideo(String(uid))
}
return res
})
} }
}) })
if (state.enableCamera) { if (state.enableCamera) {
@ -1769,20 +1616,6 @@ const Meeting: React.FC = () => {
}) })
} }
} }
const setVideoUser = () => {
setRoomUserList((newChatList: any) => {
setActiveSpeaker(res => {
let item = newChatList.find((item: any) => item.uid === res)
if (item && item.isRoom && item.isAdmin) {
renderVideo(res)
} else {
getShowUser()
}
return res
})
return newChatList
})
}
// 加入房间时间 // 加入房间时间
const changeCurrentSeconds = (): string => { const changeCurrentSeconds = (): string => {
const duration = dayjs.duration(currentSeconds, 'seconds'); const duration = dayjs.duration(currentSeconds, 'seconds');
@ -1848,15 +1681,8 @@ const Meeting: React.FC = () => {
case '共享屏幕': case '共享屏幕':
await getUserRoomInfo().then(async (res) => { await getUserRoomInfo().then(async (res) => {
if (res) { if (res) {
GetSharedScreen(state.channelId).then(req => { getDesktopCapturerVideo()
if (req.code === 200) { setIsSharedScreenModal(true)
if (req.data) {
setIsShare(req.data)
}
getDesktopCapturerVideo()
setIsSharedScreenModal(true)
}
})
} else { } else {
message.error(msgTips) message.error(msgTips)
} }
@ -1872,7 +1698,7 @@ const Meeting: React.FC = () => {
} }
}) })
if (row.title === '停止共享') { if (row.title === '停止共享') {
await allUserLook(userInfo.uid, userInfo.userName, true) await allUserLook(userInfo.uid, userInfo.userName)
} }
break; break;
case '静音': case '静音':
@ -2139,12 +1965,8 @@ const Meeting: React.FC = () => {
}) })
}; };
// 设置全员看谁 // 设置全员看谁
const allUserLook = async (uid: string, name: string, bool?: boolean): Promise<void> => { const allUserLook = async (uid: string, name: string): Promise<void> => {
if (bool) { await PostShowUser(state.channelId, uid, name)
await PostStopSharedScreen(state.channelId)
} else {
await PostShowUser(state.channelId, uid, name)
}
} }
// 设置发言人 // 设置发言人
const postRoomManager = async (data: any): Promise<void> => { const postRoomManager = async (data: any): Promise<void> => {
@ -2155,13 +1977,6 @@ const Meeting: React.FC = () => {
roomId: data.roomId, roomId: data.roomId,
roomNum: data.roomNum, roomNum: data.roomNum,
userId: data.userId userId: data.userId
}).then(res => {
if (res.code === 200) {
setApplyUserList((newChatList: any) => [...newChatList, {
uid: data.userId,
status: 5
}])
}
}) })
} }
} }
@ -2206,25 +2021,7 @@ const Meeting: React.FC = () => {
item.isRoom = true; item.isRoom = true;
item.isAdmin = role.ID.includes(item.roleId) || item.isRoomManager item.isAdmin = role.ID.includes(item.roleId) || item.isRoomManager
}) })
setRoomUserList((req: any) => { setRoomUserList(res.data)
if (req.length) {
let arr: any = []
res.data.forEach((item: any) => {
let userItem = req.find((row: any) => row.uid == item.uid);
if (userItem) {
userItem.enableCamera = item.enableCamera;
userItem.enableMicr = item.enableMicr;
userItem.isRoomManager = item.isRoomManager;
userItem.isAdmin = role.ID.includes(item.roleId) || item.isRoomManager;
} else {
arr.push(item)
}
});
return [...req, ...arr]
} else {
return res.data
}
})
getUserRoomInfo().then(async (res) => { getUserRoomInfo().then(async (res) => {
await agora.updateChannelMediaOptions(res ? true : false) await agora.updateChannelMediaOptions(res ? true : false)
changeAgoraDevice() changeAgoraDevice()
@ -2256,7 +2053,7 @@ const Meeting: React.FC = () => {
enableCamera: !storeDevice[0][1].active, enableCamera: !storeDevice[0][1].active,
isRoomManager: userItem ? userItem.isRoomManager : false, isRoomManager: userItem ? userItem.isRoomManager : false,
}) })
setVideoUser() await getShowUser()
if (userItem.isRoomManager) { if (userItem.isRoomManager) {
await postOpenMicr(!storeDevice[0][0].active, userInfo.uid) await postOpenMicr(!storeDevice[0][0].active, userInfo.uid)
await postOpenCamera(!storeDevice[0][1].active, userInfo.uid) await postOpenCamera(!storeDevice[0][1].active, userInfo.uid)
@ -2288,28 +2085,25 @@ 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) {
setRoomUserList((res: any) => { window.electron.onInvoke('sendChannelMsg', {
let row = res.find((item: any) => item.uid === userInfo.uid) roomNum: state.channelId,
window.electron.onInvoke('sendChannelMsg', { msg: msg,
roomNum: state.channelId,
msg: msg,
})
let item = {
uid: userInfo.uid,
userName: row.userName,
message: msg,
timestamp: +new Date()
}
setChatList((newChatList: any) => [...newChatList, item])
window.electron.windowHandleMessage({
key: 'chatSmallWindow',
parmes: {
chatListIten: item,
}
})
chatScrollBotton()
return res
}) })
let item = {
uid: userInfo.uid,
userName: userInfo.userName,
message: msg,
timestamp: +new Date()
}
setChatList((newChatList: any) => [...newChatList, item])
window.electron.windowHandleMessage({
key: 'chatSmallWindow',
parmes: {
chatListIten: item,
}
})
setTextMsg('');
chatScrollBotton()
} else { } else {
message.error('请输入内容!') message.error('请输入内容!')
} }
@ -2357,6 +2151,8 @@ const Meeting: React.FC = () => {
} }
}) })
} }
// 开关麦克风 // 开关麦克风
const postOpenMicrApi = async (enableMicr: boolean, uid: string, isAll: boolean, isMessage: boolean = false): Promise<void> => { const postOpenMicrApi = async (enableMicr: boolean, uid: string, isAll: boolean, isMessage: boolean = false): Promise<void> => {
if (isAll) { if (isAll) {
@ -2365,20 +2161,11 @@ const Meeting: React.FC = () => {
enableMicr enableMicr
}) })
} else { } else {
await window.electron.onInvoke('sendOper2User', { await PostOpenMicr({
roomNum: state.channelId,
uid, uid,
contentString: JSON.stringify({ enableMicr
roomNum: state.channelId,
uid,
enableMicr,
type: 'audio'
})
}) })
// await PostOpenMicr({
// roomNum: state.channelId,
// uid,
// enableMicr
// })
} }
if (isMessage) { if (isMessage) {
// message.success('操作成功') // message.success('操作成功')
@ -2419,20 +2206,11 @@ const Meeting: React.FC = () => {
} else { } else {
await agora.stopCameraCapture(); await agora.stopCameraCapture();
} }
await window.electron.onInvoke('sendOper2User', { await PostOpenCamera({
roomNum: state.channelId,
uid, uid,
contentString: JSON.stringify({ enableCamera
roomNum: state.channelId,
uid,
enableCamera,
type: 'video'
})
}) })
// await PostOpenCamera({
// roomNum: state.channelId,
// uid,
// enableCamera
// })
if (isMessage) { if (isMessage) {
// message.success('操作成功') // message.success('操作成功')
} }
@ -2536,7 +2314,7 @@ const Meeting: React.FC = () => {
</svg> </svg>
</div> //右 </div> //右
} else { } else {
return <div className={`${styles.speakerModeIcon} drag`} style={{ left: 'calc(270px - 4px)' }} title='收起' onClick={() => setIsVideoFullScreen(true)}> return <div className={`${styles.speakerModeIcon} drag`} style={{ left: 'calc(18% - 4px)' }} title='收起' onClick={() => setIsVideoFullScreen(true)}>
<svg width="12" height="22" viewBox="0 0 12 22" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="12" height="22" viewBox="0 0 12 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.9355 1.45949L10.7759 0.299805L0.0636029 11.0143L10.7759 21.7275L11.9355 20.5679L2.38107 11.0136L11.9355 1.45949Z" /> <path d="M11.9355 1.45949L10.7759 0.299805L0.0636029 11.0143L10.7759 21.7275L11.9355 20.5679L2.38107 11.0136L11.9355 1.45949Z" />
</svg> </svg>
@ -2575,14 +2353,6 @@ const Meeting: React.FC = () => {
} }
message.success('操作成功') message.success('操作成功')
} }
// 判断是否出现滚动条
const hasScrollbar = () => {
let element = document.getElementById('videoView') as HTMLDivElement
if (element) {
return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
}
return false
}
// 移出房间 // 移出房间
const getRoomKickout = async (channelId: string, uid: string, userName: string): Promise<void> => { const getRoomKickout = async (channelId: string, uid: string, userName: string): Promise<void> => {
confirm({ confirm({
@ -2769,9 +2539,7 @@ const Meeting: React.FC = () => {
</Popover>} </Popover>}
<div>{changeCurrentSeconds()}</div> <div>{changeCurrentSeconds()}</div>
</div> </div>
<div style={{ display: 'flex', alignItems: 'center' }}>{state.channelId} {state.roomName} <div>{state.channelId} {state.roomName}</div>
<span className='drag' style={{ marginTop: '2px', marginLeft: '4px' }}><Code roomNum={state.channelId}></Code></span>
</div>
<div className='drag'> <div className='drag'>
<Popover <Popover
content={ content={
@ -2816,7 +2584,7 @@ const Meeting: React.FC = () => {
{isAdmin && currentLookUserAccount ? getSettingIcon() : null} {isAdmin && currentLookUserAccount ? getSettingIcon() : null}
<div className={getMeetingContentBodyLeftModeClass()} id='videoView' style={meetingMode === 'SpeakerMode' && isVideoFullScreen ? { width: '0' } : {}}> <div className={getMeetingContentBodyLeftModeClass()} id='videoView' style={meetingMode === 'SpeakerMode' && isVideoFullScreen ? { width: '0' } : {}}>
{roomUserList.map((item: any, index: number) => { {roomUserList.map((item: any, index: number) => {
return (item.isRoom && item.isAdmin ? <div return (index <= 19 && item.isRoom && item.isAdmin ? <div
id={item.uid} id={item.uid}
className={`${styles.meetingContentSwiperCard} intersectionObserver-view`} className={`${styles.meetingContentSwiperCard} intersectionObserver-view`}
key={index} key={index}
@ -2939,13 +2707,13 @@ const Meeting: React.FC = () => {
</div> : null) </div> : null)
} }
)} )}
{hasScrollbar() ? <div> {roomUserList.length > 6 ? <div>
{meetingMode === "StandardMode" ? <div className={`${styles.meetingContentSwiperCaret}`} style={{ left: '20px', top: '66px' }} onClick={() => { {meetingMode === "StandardMode" ? <div className={`${styles.meetingContentSwiperCaret}`} style={{ left: '20px', top: '66px' }} onClick={() => {
const container = document.getElementById('videoView') as HTMLElement; const container = document.getElementById('videoView') as HTMLElement;
container.scrollLeft -= 100 container.scrollLeft -= 100
}}> }}>
<CaretLeftOutlined /> <CaretLeftOutlined />
</div> : <div className={`${styles.meetingContentSwiperCaret}`} style={{ left: '115px', top: '20px' }} onClick={() => { </div> : <div className={`${styles.meetingContentSwiperCaret}`} style={{ left: '8.2%', top: '20px' }} onClick={() => {
const container = document.getElementById('videoView') as HTMLElement; const container = document.getElementById('videoView') as HTMLElement;
container.scrollTop -= 100 container.scrollTop -= 100
}}> }}>
@ -2956,7 +2724,7 @@ const Meeting: React.FC = () => {
container.scrollLeft += 100 container.scrollLeft += 100
}}> }}>
<CaretRightOutlined /> <CaretRightOutlined />
</div> : <div className={`${styles.meetingContentSwiperCaret}`} style={{ left: '115px', bottom: '20px' }} onClick={() => { </div> : <div className={`${styles.meetingContentSwiperCaret}`} style={{ left: '8.2%', bottom: '20px' }} onClick={() => {
const container = document.getElementById('videoView') as HTMLElement; const container = document.getElementById('videoView') as HTMLElement;
container.scrollTop += 100 container.scrollTop += 100
}}> }}>
@ -3070,7 +2838,7 @@ const Meeting: React.FC = () => {
<div> <div>
<div><Avatar name={item.userName} /></div> <div><Avatar name={item.userName} /></div>
<span> <span>
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: role.ID.includes(item.roleId) || item.isRoomManager ? '50px' : '80px' }} title={item.userName}>{item.userName}</span>{item.uid === user.uid ? '(我)' : ''} {item.userName}{item.uid === user.uid ? '(我)' : ''}
{role.ID.includes(item.roleId) || item.isRoomManager ? {role.ID.includes(item.roleId) || item.isRoomManager ?
<span style={{ color: '#02B188', marginLeft: '4px' }}> <span style={{ color: '#02B188', marginLeft: '4px' }}>
{role.ID.includes(item.roleId) ? '管理员' : '发言人'} {role.ID.includes(item.roleId) ? '管理员' : '发言人'}
@ -3290,30 +3058,19 @@ const Meeting: React.FC = () => {
></Button> : null} ></Button> : null}
</div> : <div style={{ color: 'white' }}></div> </div> : <div style={{ color: 'white' }}></div>
}> }>
<div title={item.userName}> <div>
<div><Avatar name={item.userName} /></div> <div><Avatar name={item.userName} /></div>
{item.uid !== user.uid ? {item.uid !== user.uid ?
<span> <span>{item.userName} <span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span></span> :
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '150px' }}>{item.userName}</span> <span> <span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')} </span>{item.userName}</span>
<span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span>
</span> :
<span>
<span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span>
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '150px' }}>{item.userName}</span>
</span>
} }
</div> </div>
</Popover> : <div title={item.userName}> </Popover> : <div>
<div><Avatar name={item.userName} /></div> <div><Avatar name={item.userName} /></div>
{item.uid !== user.uid ? {item.uid !== user.uid ?
<span> <span>{item.userName}<span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span></span> :
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '150px' }}>{item.userName}</span> <span><span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')} </span>{item.userName}</span>
<span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span>
</span> :
<span>
<span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span>
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '150px' }}>{item.userName}</span>
</span>
} }
</div>} </div>}
<div>{item.message}</div> <div>{item.message}</div>
@ -3338,10 +3095,7 @@ const Meeting: React.FC = () => {
<Input.TextArea placeholder="请输入消息" value={textMsg} style={{ flexGrow: 1 }} onChange={(e) => { <Input.TextArea placeholder="请输入消息" value={textMsg} style={{ flexGrow: 1 }} onChange={(e) => {
setTextMsg(e.target.value) setTextMsg(e.target.value)
}}></Input.TextArea> }}></Input.TextArea>
<Button type="primary" className='m-ant-btn' style={{ flexShrink: 0, marginTop: '4px' }} onClick={() => { <Button type="primary" className='m-ant-btn' style={{ flexShrink: 0, marginTop: '4px' }} onClick={() => sendMsg()}></Button>
sendMsg()
setTextMsg('');
}}></Button>
</div> </div>
</div> </div>
: :
@ -3511,7 +3265,7 @@ const Meeting: React.FC = () => {
key={rowIndex}> key={rowIndex}>
<label> <label>
<img src={row.active ? row.iconActive : row.icon} alt="" /> <img src={row.active ? row.iconActive : row.icon} alt="" />
{!row.active ? <div style={{ backgroundImage: `url(${ImageUrl.icon49})`, transition: '0.18s' }} id={`micr-item-${userInfo.uid}`}> {!row.active ? <div style={{ backgroundImage: `url(${ImageUrl.icon49})` }} id={`micr-item-${userInfo.uid}`}>
</div> : ''} </div> : ''}
</label> </label>
<span>{row.title}</span> <span>{row.title}</span>
@ -3678,7 +3432,7 @@ const meetingContentUser = (item: any, bool?: boolean) => {
<> <>
<div className={styles.meetingContentUser}> <div className={styles.meetingContentUser}>
<div className={styles.meetingContentUserName}> <div className={styles.meetingContentUserName}>
{role.ID.includes(item.roleId) ? {role.ID.includes(item.roleId) || item.isRoomManager ?
<div style={{ background: role.ID.includes(item.roleId) ? '#FDC229' : '#3F51B5' }}> <div style={{ background: role.ID.includes(item.roleId) ? '#FDC229' : '#3F51B5' }}>
<img src={ImageUrl.icon32} alt="" /> <img src={ImageUrl.icon32} alt="" />
</div> : null} </div> : null}
@ -3686,7 +3440,7 @@ const meetingContentUser = (item: any, bool?: boolean) => {
bool ? !item.enableMicr ? <img src={item.enableMicr ? ImageUrl.icon22 : ImageUrl.icon22Active} alt="" /> : '' : bool ? !item.enableMicr ? <img src={item.enableMicr ? ImageUrl.icon22 : ImageUrl.icon22Active} alt="" /> : '' :
<label> <label>
<img src={item.enableMicr ? ImageUrl.icon22 : ImageUrl.icon22Active} alt="" /> <img src={item.enableMicr ? ImageUrl.icon22 : ImageUrl.icon22Active} alt="" />
{item.enableMicr ? <div style={{ backgroundImage: `url(${ImageUrl.icon49})`, transition: '0.18s' }} id={`micr-${item.uid}`}> {item.enableMicr ? <div style={{ backgroundImage: `url(${ImageUrl.icon49})` }} id={`micr-${item.uid}`}>
</div> : ''} </div> : ''}
</label> </label>
} }

4
src/render.d.ts vendored
View File

@ -24,10 +24,8 @@ export interface IElectronAPI {
downFile: (callBack: Function) => void; downFile: (callBack: Function) => void;
quitAndInstall: (callBack: Function) => void; quitAndInstall: (callBack: Function) => void;
isOpenWindows: (callBack: Function) => void; isOpenWindows: (callBack: Function) => void;
startLoad: () => any; setEnv: (str: string) => any;
updateHandle: () => any;
getVersion: () => Promise<string>; getVersion: () => Promise<string>;
getEnv: () => Promise<string>;
isVisible: () => Promise<string>; isVisible: () => Promise<string>;
setRegistry: (uuid: string) => any; setRegistry: (uuid: string) => any;
getRegistry: () => any; getRegistry: () => any;

View File

@ -1,4 +1,3 @@
declare module 'react-dom/client'; declare module 'react-dom/client';
declare module 'crypto-js'; declare module 'crypto-js';
declare module 'js-yaml';
declare module 'uuid'; declare module 'uuid';

View File

@ -21,8 +21,7 @@ import {
ColorEnhanceOptions, ColorEnhanceOptions,
LowlightEnhanceOptions, LowlightEnhanceOptions,
VirtualBackgroundSource, VirtualBackgroundSource,
AudienceLatencyLevelType, AudienceLatencyLevelType
StreamPublishState
} 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';
@ -125,25 +124,23 @@ export const agora = {
if (settingData.darkLightEnhancement) agora.setLowlightEnhanceOptions(settingData.darkLightEnhancement.isDarkLightEnhancement, settingData.darkLightEnhancement) if (settingData.darkLightEnhancement) agora.setLowlightEnhanceOptions(settingData.darkLightEnhancement.isDarkLightEnhancement, settingData.darkLightEnhancement)
if (settingData.virtualBackground) { if (settingData.virtualBackground) {
if (typeof settingData.virtualBackground.sourceIndex === 'number') { if (typeof settingData.virtualBackground.sourceIndex === 'number') {
window.electron.getEnv().then(res => { if (import.meta.env.VITE_ENV === 'development') {
if (res === 'development') { window.electron.getAppPath().then((res: string) => {
window.electron.getAppPath().then((res: string) => { const imagePath = path.join(res, 'src', 'assets', 'virtualBackground', `${settingData.virtualBackground.sourceIndex + 1}.png`);
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, { agora.enableVirtualBackground(settingData.virtualBackground.isVirtualBackground, {
source: imagePath, source: imagePath,
background_source_type: 2, background_source_type: 2,
color: Number(settingData.virtualBackground.color), 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 { } else {
agora.enableVirtualBackground(settingData.virtualBackground.isVirtualBackground, { agora.enableVirtualBackground(settingData.virtualBackground.isVirtualBackground, {
background_source_type: 1, background_source_type: 1,
@ -154,7 +151,7 @@ export const agora = {
}, 1000); }, 1000);
}, },
// 事件回调 // 事件回调
registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline, onAudioVolumeIndication, onRtcStats, onConnectionStateChanged, onLocalVideoStateChanged, onConnectionLost, onTokenPrivilegeWillExpire, onActiveSpeaker, onVideoPublishStateChanged, onAudioPublishStateChanged }: any) => { registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline, onAudioVolumeIndication, onRtcStats, onConnectionStateChanged, onLocalVideoStateChanged, onConnectionLost, onTokenPrivilegeWillExpire }: any) => {
rtcEngine.registerEventHandler({ rtcEngine.registerEventHandler({
// 监听本地用户加入频道事件 // 监听本地用户加入频道事件
onJoinChannelSuccess: async (connection: RtcConnection, elapsed: number) => { onJoinChannelSuccess: async (connection: RtcConnection, elapsed: number) => {
@ -169,13 +166,17 @@ export const agora = {
await onUserOffline?.(connection, remoteUid, reason) await onUserOffline?.(connection, remoteUid, reason)
}, },
// // 视频发布状态改变回调 // // 视频发布状态改变回调
onVideoPublishStateChanged: (source: VideoSourceType, channel: string, oldState: StreamPublishState, newState: StreamPublishState, elapseSinceLastState: number) => { // onVideoPublishStateChanged: (source: any, channel: any, oldState: any, newState: any, elapseSinceLastState: any) => {
onVideoPublishStateChanged?.(source, channel, oldState, newState, elapseSinceLastState) // if (newState === 1) {
},
// }
// },
// // 音频发布状态改变回调 // // 音频发布状态改变回调
onAudioPublishStateChanged: (channel: string, oldState: StreamPublishState, newState: StreamPublishState, elapseSinceLastState: number) => { // onAudioPublishStateChanged: (channel: any, oldState: any, newState: any, elapseSinceLastState: any) => {
onAudioPublishStateChanged?.(channel, oldState, newState, elapseSinceLastState) // if (newState === 1) {
},
// }
// },
// // 用户音量提示回调。 // // 用户音量提示回调。
onAudioVolumeIndication: async (_connection: RtcConnection, speakers: AudioVolumeInfo[], _speakerNumber: number, _totalVolume: number) => { onAudioVolumeIndication: async (_connection: RtcConnection, speakers: AudioVolumeInfo[], _speakerNumber: number, _totalVolume: number) => {
await onAudioVolumeIndication?.(speakers) await onAudioVolumeIndication?.(speakers)
@ -199,13 +200,6 @@ export const agora = {
// Token 即将在 30s 内过期回调。 // Token 即将在 30s 内过期回调。
onTokenPrivilegeWillExpire: (connection: RtcConnection, token: string) => { onTokenPrivilegeWillExpire: (connection: RtcConnection, token: string) => {
onTokenPrivilegeWillExpire?.(connection, token) onTokenPrivilegeWillExpire?.(connection, token)
},
// 监测到远端最活跃用户回调。
onActiveSpeaker: (connection: RtcConnection, uid: number) => {
const setting = JSON.parse(storage.getItem('setting') as string);
if (setting.voiceStimulation) {
onActiveSpeaker?.(connection, uid)
}
} }
}); });
}, },
@ -295,7 +289,7 @@ export const agora = {
}, },
// 加入频道 // 加入频道
joinChannel: async () => { joinChannel: async () => {
await rtcEngine.enableAudioVolumeIndication(200, 3, true) await rtcEngine.enableAudioVolumeIndication(100, 3, true)
await rtcEngine.joinChannel(option.token, option.channelId, option.uid); await rtcEngine.joinChannel(option.token, option.channelId, option.uid);
await rtcEngine.setDualStreamModeEx( await rtcEngine.setDualStreamModeEx(
SimulcastStreamMode.EnableSimulcastStream, SimulcastStreamMode.EnableSimulcastStream,
@ -308,7 +302,6 @@ export const agora = {
}, },
{ channelId: option.channelId, localUid: Number(option.uid) } { channelId: option.channelId, localUid: Number(option.uid) }
); );
await rtcEngine.setAudioScenario(8)
}, },
// 更新频道配置 // 更新频道配置
updateChannelMediaOptions: async (bool: boolean) => { updateChannelMediaOptions: async (bool: boolean) => {
@ -448,8 +441,8 @@ export const agora = {
startCameraCapture: async (bool: boolean = false) => { startCameraCapture: async (bool: boolean = false) => {
await rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, { await rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, {
format: { format: {
width: bool ? 160 : 1920, width: bool ? 160 : 1280,
height: bool ? 160 : 1080, height: bool ? 160 : 720,
fps: 15, fps: 15,
} }
}) })
@ -479,7 +472,7 @@ export const agora = {
} }
let data = { let data = {
frameRate: isFluencyPriority ? 15 : 7, frameRate: isFluencyPriority ? 30 : 15,
dimensions: { dimensions: {
width: 1920, width: 1920,
height: 1080, height: 1080,

View File

@ -1,7 +1,5 @@
import path from "path"; import path from "path";
import storage from "./storage"; import storage from "./storage";
import axios from "axios";
import yaml from 'js-yaml'
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)
openChildWindow[key] = bool; openChildWindow[key] = bool;
@ -29,7 +27,6 @@ export const storageSeeting: any = {
shareFilesPath: path.resolve(__dirname, '../../Downloads/') + '\\', //共享文件保存路径 shareFilesPath: path.resolve(__dirname, '../../Downloads/') + '\\', //共享文件保存路径
isShareSavePath: true, //是否下载钱询问每个文件保存的位置 isShareSavePath: true, //是否下载钱询问每个文件保存的位置
closeSetting: 'hide', //关闭按钮设置 closeSetting: 'hide', //关闭按钮设置
voiceStimulation: true, //语音激励
isAINoiseReduction: true, //是否开启ai降噪 isAINoiseReduction: true, //是否开启ai降噪
aINoiseReduction: 1, // 降噪模式 aINoiseReduction: 1, // 降噪模式
isRecordingTips: true, //是否开启录制提示 isRecordingTips: true, //是否开启录制提示
@ -62,61 +59,15 @@ export const getUpdateUrl = (env: string) => {
switch (env) { switch (env) {
case 'xy': case 'xy':
return 'https://meeting-api.23544.com/meeting/xysz' return 'https://meeting-api.23544.com/meeting/xysz'
case 'development':
return 'http://192.168.2.9:8827'
default: default:
return 'https://meeting-api.23544.com/meeting/update' return 'https://meeting-api.23544.com/meeting/update'
} }
} }
export const getTitle = async () => { export const getTitle = (env: string) => {
let env = await window.electron.getEnv()
let str;
switch (env) { switch (env) {
case 'xy': case 'xy':
str = '湖北襄阳四中教研平台' return '湖北襄阳四中教研平台'
break;
case 'development':
str = '智汇享'
break;
default: default:
str = '智汇享' return '智汇享'
break;
} }
document.getElementsByTagName('title')[0].innerText = str
}
export const compareVersions = (version1: string, version2: string): number => {
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
const maxLength = Math.max(v1Parts.length, v2Parts.length);
for (let i = 0; i < maxLength; i++) {
const v1 = v1Parts[i] || 0;
const v2 = v2Parts[i] || 0;
if (v1 > v2) {
return 1;
} else if (v1 < v2) {
return -1;
}
}
return 0;
}
export const isVersion = (callBack: Function) => {
window.electron.getEnv().then(res => {
axios.get(`${getUpdateUrl(res)}/latest.yml`).then(res => {
if (res.status === 200 && res.data) {
const data = yaml.load(res.data); // 解析 YAML 内容
window.electron.getVersion().then(req => {
if (compareVersions(data.version, req) == 1) {
callBack(true, data)
} else {
callBack(false, data)
}
})
} else {
callBack(false)
}
}).catch(() => {
callBack(false)
})
})
} }

View File

@ -3,8 +3,6 @@ module.exports = {
switch (env) { switch (env) {
case 'xy': case 'xy':
return 'https://meeting-api.23544.com/meeting/xysz' return 'https://meeting-api.23544.com/meeting/xysz'
case 'development':
return 'http://192.168.2.9:8827'
default: default:
return 'https://meeting-api.23544.com/meeting/update' return 'https://meeting-api.23544.com/meeting/update'
} }
@ -13,8 +11,6 @@ module.exports = {
switch (env) { switch (env) {
case 'xy': case 'xy':
return '湖北襄阳四中教研平台' return '湖北襄阳四中教研平台'
case 'development':
return '智汇享'
default: default:
return '智汇享' return '智汇享'
} }
@ -23,8 +19,6 @@ module.exports = {
switch (env) { switch (env) {
case 'xy': case 'xy':
return 'icon54' return 'icon54'
case 'development':
return 'icon'
default: default:
return 'icon' return 'icon'
} }

View File

@ -1,10 +1,10 @@
import { AxiosRequestConfig, AxiosResponse } from 'axios' import { AxiosRequestConfig, AxiosResponse } from 'axios'
import Request from './request' import Request from './request'
import { constant } from '@/config' import { constant } from '@/config'
let baseURL = !location.hostname.includes('meeting-api.23544.com') ? 'http://192.168.2.9:5192' : 'https://meeting-api.23544.com/pc'
// 实例化 // 实例化
const req = new Request({ const req = new Request({
baseURL, baseURL: import.meta.env.VITE_BASE_URL_API,
timeout: constant.CONFIG_REQUEST_TIMEOUT_TIME as number, timeout: constant.CONFIG_REQUEST_TIMEOUT_TIME as number,
interceptors: { interceptors: {
// 请求拦截器 // 请求拦截器
@ -22,4 +22,5 @@ const request = (config: any) => {
} }
return req.request<any>(config) return req.request<any>(config)
} }
export default request export default request

View File

@ -77,9 +77,6 @@ class Request {
case 403: case 403:
updatePostRefresh() updatePostRefresh()
break break
case 502:
message.error('网络已断开,请检查网络状态')
break
default: default:
message.error(err.message) message.error(err.message)
break; break;

View File

@ -76,9 +76,7 @@ export default defineConfig({
ColorEnhanceOptions, ColorEnhanceOptions,
LowlightEnhanceOptions, LowlightEnhanceOptions,
VirtualBackgroundSource, VirtualBackgroundSource,
AudienceLatencyLevelType, AudienceLatencyLevelType
StreamPublishState,
IMediaEngine
} = require("agora-electron-sdk") } = require("agora-electron-sdk")
export { export {
createAgoraRtcEngine, createAgoraRtcEngine,
@ -103,9 +101,7 @@ export default defineConfig({
ColorEnhanceOptions, ColorEnhanceOptions,
LowlightEnhanceOptions, LowlightEnhanceOptions,
VirtualBackgroundSource, VirtualBackgroundSource,
AudienceLatencyLevelType, AudienceLatencyLevelType
StreamPublishState,
IMediaEngine
} }
`, `,
}) })