diff --git a/.env.development b/.env.development deleted file mode 100644 index db62f58..0000000 --- a/.env.development +++ /dev/null @@ -1,6 +0,0 @@ -#基础API 绝对的 -VITE_BASE_URL_API = 'http://192.168.2.9:5192' -#当前IP 相对的 -VITE_BASE_CURRENT_API = '.' -#开发环境 -VITE_ENV = 'development' diff --git a/.env.production b/.env.production deleted file mode 100644 index 9f463c7..0000000 --- a/.env.production +++ /dev/null @@ -1,6 +0,0 @@ -#基础API 绝对的 -VITE_BASE_URL_API = 'https://meeting-api.23544.com/pc' -#当前IP 相对的 -VITE_BASE_CURRENT_API = '.' -#生产环境 -VITE_ENV = 'production' diff --git a/.env.xy b/.env.xy deleted file mode 100644 index 45335d0..0000000 --- a/.env.xy +++ /dev/null @@ -1,6 +0,0 @@ -#基础API 绝对的 -VITE_BASE_URL_API = 'https://meeting-api.23544.com/pc' -#当前IP 相对的 -VITE_BASE_CURRENT_API = '.' -#测试环境 -VITE_ENV = 'xy' diff --git a/build/install.ico b/build/install.ico deleted file mode 100644 index 873d7e8..0000000 Binary files a/build/install.ico and /dev/null differ diff --git a/build/install.nsh b/build/install.nsh new file mode 100644 index 0000000..2589d94 --- /dev/null +++ b/build/install.nsh @@ -0,0 +1,16 @@ +!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 diff --git a/config/development.json b/config/development.json new file mode 100644 index 0000000..23f8f45 --- /dev/null +++ b/config/development.json @@ -0,0 +1,57 @@ +{ + "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" + } +} \ No newline at end of file diff --git a/config/build.json b/config/production.json similarity index 84% rename from config/build.json rename to config/production.json index df6adec..b358bc5 100644 --- a/config/build.json +++ b/config/production.json @@ -42,15 +42,16 @@ ], "nsis": { "oneClick": false, - "installerIcon": "build/install.ico", - "uninstallerIcon": "build/install.ico", - "installerHeaderIcon": "build/install.ico", + "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 + "perMachine": true, + "include": "build/install.nsh" } } \ No newline at end of file diff --git a/config/xy.json b/config/xy.json index 643c891..2acb33e 100644 --- a/config/xy.json +++ b/config/xy.json @@ -51,6 +51,7 @@ "deleteAppDataOnUninstall": true, "shortcutName": "湖北襄阳四中教研平台", "allowElevation": true, - "perMachine": true + "perMachine": true, + "include": "build/install.nsh" } } \ No newline at end of file diff --git a/main.js b/main.js index 86d7857..f5fecb8 100644 --- a/main.js +++ b/main.js @@ -11,6 +11,7 @@ const { crashReporter, desktopCapturer, powerSaveBlocker, + net } = require('electron'); const path = require('node:path') const updateJs = require('./src/utils/package/update') @@ -18,16 +19,15 @@ const fs = require('fs'); const Registry = require('winreg'); const { autoUpdater, CancellationToken } = require('electron-updater'); const signalR = require('@microsoft/signalr'); -const { setTimeout, setInterval, clearTimeout, clearInterval } = require('timers'); +const { setTimeout, setInterval } = require('timers'); const cancellationToken = new CancellationToken() app.allowRendererProcessReuse = false; let mainWindow = null; let childWindow = {} let isMaximized = false; -let env; +let env = 'development'; //development production xy let regKey; let connection = null; -let envStr; let startNumber = 0; let buildStatus = false; //true 打包开发版本 false 本地开发 powerSaveBlocker.start('prevent-display-sleep') @@ -53,16 +53,15 @@ class AppWindow extends BrowserWindow { const finalConfig = { ...basicConfig, ...config }; super(finalConfig); if (env === 'development') { - // 开发 if (buildStatus) { - this.loadFile(path.resolve(__dirname, './dist/index.html')); + this.loadURL('http://192.168.2.9:8827/'); } else { this.loadURL('http://localhost:3000'); } } else { - // 测试 | 生产 - this.loadFile(path.resolve(__dirname, './dist/index.html')); + this.loadURL('https://meeting-api.23544.com/') } + // this.loadFile(path.resolve(__dirname, './dist/index.html')) this.once('ready-to-show', () => { this.show(); }); @@ -72,11 +71,20 @@ function quit() { app.quit() } let tray; +// 检查网络状态 +function checkNetworkStatus() { + if (!net.isOnline()) { + dialog.showErrorBox(`${env === 'xy' ? '湖北襄阳四中教研平台' : '智汇享'}-网络连接错误', '当前无网络连接,请检查您的网络设置。`); + app.quit(); + return false; + } + return true; +} function createTray() { - const iconPath = `${__dirname}/src/assets/${updateJs.getIcon(envStr)}.png`; + const iconPath = `${__dirname}/src/assets/${updateJs.getIcon(env)}.png`; const trayIcon = nativeImage.createFromPath(iconPath); tray = new Tray(trayIcon); - tray.setToolTip(updateJs.getTitle(envStr)); + tray.setToolTip(updateJs.getTitle(env)); tray.on('click', () => { mainWindow.webContents.send('isOpenWindows'); }); @@ -95,6 +103,10 @@ function createWindow() { } const additionalData = { myKey: 'myValue' } app.on('ready', () => { + // 检查网络状态 + if (!checkNetworkStatus()) { + return; + } // const gotTheLock = true const gotTheLock = app.requestSingleInstanceLock(additionalData) if (gotTheLock) { @@ -103,15 +115,14 @@ app.on('ready', () => { uploadToServer: false, ignoreSystemCrashHandler: false }) - env = process.argv.find((arg) => arg.startsWith('--env='))?.split('=')[1]; - if (env === 'development') { - Object.defineProperty(app, 'isPackaged', { - get() { - return true - } - }) - autoUpdater.updateConfigPath = path.join('latest.yml') - } + // if (!buildStatus) { + // Object.defineProperty(app, 'isPackaged', { + // get() { + // return true + // } + // }) + // autoUpdater.updateConfigPath = path.join('latest.yml') + // } createWindow() regKey = new Registry({ hive: Registry.HKCU, @@ -149,12 +160,11 @@ app.on('ready', () => { isMaximized = true; } }); - ipcMain.handle('setEnv', (event, str) => { - envStr = str; + ipcMain.handle('startLoad', (event) => { if (startNumber === 0) { updateHandle() // 检查更新 setInterval(() => { - updateHandle() // 每一小时检查更新 + autoUpdater.checkForUpdates() }, 1000 * 60 * 60) createTray() startNumber++ @@ -162,7 +172,7 @@ app.on('ready', () => { }); // 更新 ipcMain.handle('updateHandle', () => { - updateHandle() + autoUpdater.checkForUpdates() }); // socket ipcMain.handle('startSignalr', (event, user) => { @@ -191,6 +201,8 @@ app.on('ready', () => { connection.off('JoinChannelCallback'); connection.off('ExitSharedScreen'); connection.off('SetSpeaker'); + connection.off('ReceivedOperation'); + connection.off('StopedSharedScreen'); } }); ipcMain.handle('onStop', (event) => { @@ -210,7 +222,7 @@ app.on('ready', () => { await connection.invoke(str, data.roomNum, data.msg) break; case 'sendOper': - await connection.invoke(str, data.roomNum, data.type) + await connection.invoke(str, data.roomNum, data.contentString) break; case 'getDrivers': // 获取某个人的设备列表 @@ -236,6 +248,10 @@ app.on('ready', () => { // 发言人设置成功 await connection.invoke(str, data) break; + case 'sendOper2User': + // 扩展参数 + await connection.invoke(str, data.uid, data.contentString) + break; } }); ipcMain.handle('onOtherSignalr', (event) => { @@ -266,10 +282,10 @@ app.on('ready', () => { }) }); // 扩展操作 - connection.on("Operation", (type) => { + connection.on("Operation", (contentString) => { mainWindow.webContents.send('onSignalr', { key: 'Operation', - type + contentString }) }); // 移出会议 @@ -411,6 +427,20 @@ app.on('ready', () => { RoomManagerInputDTO }) }); + // 扩展参数 + connection.on("ReceivedOperation", (contentString) => { + mainWindow.webContents.send('onSignalr', { + key: 'ReceivedOperation', + contentString + }) + }); + // 共享人取消共享屏幕 + connection.on("StopedSharedScreen", (ScreenShareId) => { + mainWindow.webContents.send('onSignalr', { + key: 'StopedSharedScreen', + ScreenShareId + }) + }); } }); // 放大缩小退出窗口 @@ -452,6 +482,10 @@ app.on('ready', () => { ipcMain.handle('getVersion', () => { return app.getVersion(); }); + // 获取环境 + ipcMain.handle('getEnv', () => { + return env; + }); // 获取窗口是否显示 ipcMain.handle('isVisible', () => { return mainWindow.isVisible(); @@ -487,6 +521,11 @@ app.on('ready', () => { downloadUpdate() } else if (data === '2') { // 下载完成 点击安装 quitAndInstall() + } else if (data === '3') { // 打开弹窗 + let message = JSON.stringify({ + type: '3', + }) + sendUpdateMessage(message) } }); // 选择文件夹 @@ -604,16 +643,18 @@ app.on('ready', () => { width: config.width, height: config.height, }) - if (envStr === 'development') { + if (env === 'development') { // 开发 if (buildStatus) { - child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`); + child.loadURL(`http://192.168.2.9:8827/#/${config.key}`); + // child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`); } else { child.loadURL(config.url) } } else { // 测试 | 生产 - child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`); + child.loadURL(`https://meeting-api.23544.com/#/${config.key}`); + // child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`); } child.hookWindowMessage(278, function (e) { child.setEnabled(false);//窗口禁用 @@ -715,7 +756,6 @@ app.on('ready', () => { }); // 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写 function updateHandle() { - autoUpdater.checkForUpdates() // autoUpdater.checkForUpdatesAndNotify().catch(); const message = { error: '检查更新出错', @@ -723,11 +763,11 @@ function updateHandle() { updateAva: '检测到新版本,正在下载……', updateNotAva: '已经是最新版本,不用更新' } - autoUpdater.setFeedURL(updateJs.getUpdateUrl(envStr)) + autoUpdater.setFeedURL(updateJs.getUpdateUrl(env)) autoUpdater.autoDownload = false // 不自动下载安装包 autoUpdater.autoInstallOnAppQuit = false // 不自动安装 autoUpdater.on('error', function (error) { - sendUpdateMessage(message.error) + sendUpdateMessage(error) }) autoUpdater.on('checking-for-update', function () { sendUpdateMessage(message.checking) @@ -735,9 +775,14 @@ function updateHandle() { autoUpdater.on('update-available', function (info) { let messageStr = JSON.stringify({ type: '0' }) sendUpdateMessage(messageStr) + mainWindow.webContents.send('changeLocalStorage', { + isUpdate: true, + }); }) autoUpdater.on('update-not-available', function (info) { - + mainWindow.webContents.send('changeLocalStorage', { + isUpdate: false, + }); }) // 更新下载进度事件 autoUpdater.on('download-progress', function (progressObj) { @@ -810,7 +855,7 @@ function mainWindowCenter() { const startSignalr = async (user) => { connection = new signalR.HubConnectionBuilder() - .withUrl(`${envStr === 'development' ? 'http://192.168.2.9:5192' : 'https://meeting-api.23544.com/pc'}/session-manage`, { + .withUrl(`${env === 'development' ? 'http://192.168.2.9:5192' : 'https://meeting-api.23544.com/pc'}/session-manage`, { skipNegotiation: true, transport: signalR.HttpTransportType.WebSockets, accessTokenFactory: () => user.token diff --git a/package-lock.json b/package-lock.json index 8e15458..ebeb9ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { "name": "WGShare.Metting", - "version": "0.4.8", + "version": "0.7.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "WGShare.Metting", - "version": "0.4.8", + "version": "0.7.1", "dependencies": { "@ant-design/icons": "^5.3.7", "@microsoft/signalr": "^8.0.0", "@types/node": "^20.14.9", - "agora-electron-sdk": "^4.4.0", + "agora-electron-sdk": "4.4.0", "animate.css": "^4.1.1", "antd": "^5.18.2", "axios": "^1.7.2", @@ -19,6 +19,7 @@ "dayjs": "^1.11.11", "electron-squirrel-startup": "^1.0.1", "electron-updater": "^6.2.1", + "js-yaml": "^4.1.0", "os": "^0.1.2", "path": "^0.12.7", "postcss-px-to-viewport-8-plugin": "^1.2.5", diff --git a/package.json b/package.json index af25b5e..44f05de 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,17 @@ { "name": "WGShare.Metting", "private": true, - "version": "0.7.1", + "version": "0.8.0", "main": "main.js", "authors": "yj", "description": "智汇享", "scripts": { - "dev": "concurrently \"electron . --env=development\" \"cross-env BROWSER=none vite\"", - "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", + "dev": "concurrently \"electron .\" \"cross-env BROWSER=none vite\"", + "build": "vite build", "preview": "vite preview", - "build:dev-win": "vite build --mode development & electron-builder -w --config=./config/build.json", - "build:prod-win": "vite build --mode production & electron-builder -w --config=./config/build.json", - "build:prod-win-xy": "vite build --mode xy & electron-builder -w --config=./config/xy.json" + "build:dev": "vite build & electron-builder -w --config=./config/development.json", + "build:prod": "vite build & electron-builder -w --config=./config/production.json", + "build:xy": "vite build & electron-builder -w --config=./config/xy.json" }, "agora_electron": { "platform": "win32", @@ -34,6 +30,7 @@ "dayjs": "^1.11.11", "electron-squirrel-startup": "^1.0.1", "electron-updater": "^6.2.1", + "js-yaml": "^4.1.0", "os": "^0.1.2", "path": "^0.12.7", "postcss-px-to-viewport-8-plugin": "^1.2.5", diff --git a/preload.js b/preload.js index 22bc873..34b5281 100644 --- a/preload.js +++ b/preload.js @@ -51,6 +51,10 @@ window.electron = { getVersion: () => { return ipcRenderer.invoke('getVersion') }, + // 获取环境 + getEnv: () => { + return ipcRenderer.invoke('getEnv') + }, // 获取窗口是否显示 isVisible: () => { return ipcRenderer.invoke('isVisible') @@ -87,9 +91,9 @@ window.electron = { isOpenWindows: (callback) => { ipcRenderer.on('isOpenWindows', callback) }, - // 设置环境变量 - setEnv: (str) => { - ipcRenderer.invoke('setEnv', str) + // 首次加载 + startLoad: () => { + ipcRenderer.invoke('startLoad') }, // 更新 updateHandle: () => { @@ -134,7 +138,7 @@ window.electron = { ipcRenderer.invoke('createChildWindow', { url: location.origin + `/#/noticeWindow`, width: 388, - height: 150, + height: 180, key: 'noticeWindow', }) ipcRenderer.invoke('createChildWindow', { diff --git a/src/App.tsx b/src/App.tsx index 3e264ca..6e2d77e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,20 +8,21 @@ import Login from '@/page/Login/index' import Meeting from '@/page/Meeting/index' import NotFound from '@/page/NotFound/index' import { storage } from '@/utils' -import { message, Spin } from "antd"; +import { message, Modal, Spin } from "antd"; import JoinMeetingModal from "@/components/JoinMeetingModal"; import UpdateModal from "@/components/UpdateModal"; import * as CryptoJS from 'crypto-js'; -import { PostLogin } from "@/api/Login"; +import { GetCheckOnline, PostLogin } from "@/api/Login"; import { agora } from "@/utils/package/agora"; import QuitTips from "@/components/QuitTips"; -import { GetLeave } from "@/api/Meeting"; import ShareScreenWindow from "@/page/Meeting/ShareScreenWindow"; import UserListWindow from "@/page/Meeting/UserListWindow"; import ChatSmallWindow from "@/page/Meeting/ChatSmallWindow"; import ChatBigWindow from "@/page/Meeting/ChatBigWindow"; import NoticeWindow from "@/page/Meeting/NoticeWindow"; import { getKeyOpenChildWindow, getTitle, setKeyOpenChildWindow, storageSeeting } from "./utils/package/public"; +import { ExclamationCircleFilled } from "@ant-design/icons"; +const { confirm } = Modal; const fs = require('fs').promises; const { exec } = require('child_process'); const App: React.FC = () => { @@ -41,20 +42,43 @@ const App: React.FC = () => { useEffect(() => { let userInfo = JSON.parse(storage.getItem('user') as string) let loginInfo = JSON.parse(storage.getItem('login') as string) - window.electron.setEnv(import.meta.env.VITE_ENV); + const login = () => { + 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 (loginInfo && loginInfo.isAutoLogin) { - 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') + GetCheckOnline(loginInfo.account).then(req => { + if (req.code === 200) { + if (req.data) { + confirm({ + title: '提示', + icon: , + content: `账号已在其他地方登录,是否强制登陆?`, + centered: true, + okText: '确定', + cancelText: '取消', + async onOk() { + login() + }, + onCancel() { + toSrc('/login') + } + }) + } else { + login() + } } }) } else { @@ -79,6 +103,7 @@ const App: React.FC = () => { }; }, []); useEffect(() => { + window.electron.startLoad(); window.electron.downFile(async (_e: any, data: any) => { const response = await fetch(data.filePath); const arrayBuffer = await response.arrayBuffer(); @@ -127,9 +152,7 @@ const App: React.FC = () => { }, []) useEffect(() => { window.electron.onUpdate((_e: any, data: any) => { - if (location.hash.indexOf('/meeting') === -1) { - updateModalRef.current.changeModal(data) - } + updateModalRef.current.changeModal(data) }) if (!storage.getItem('setting')) { storage.setItem('setting', JSON.stringify(storageSeeting)) @@ -164,16 +187,24 @@ const App: React.FC = () => { if (location.href.indexOf('/login') !== -1) { window.electron.onStop() } + if (location.hash && location.hash.indexOf('/meeting') === -1) { + window.electron.updateHandle() + } message.destroy('cameraTemporarily') }, [navigate]) } useEffect(() => { - document.addEventListener('keydown', (event) => { - if (event.key === 'F11') { + document.addEventListener('keydown', async (event) => { + if (event.keyCode == 122) { event.preventDefault(); + } else if (((event.ctrlKey && event.keyCode == 82) || event.keyCode == 116)) { + let env = await window.electron.getEnv() + if (env !== 'development') { + event.preventDefault(); + } } }); - document.getElementsByTagName('title')[0].innerText = getTitle(import.meta.env.VITE_ENV) + getTitle() }, []) const handleResize = (): void => { setWindowSize({ @@ -200,8 +231,10 @@ const App: React.FC = () => { if (item.msg) { message.error(item.msg) } - await leaveChannel(true) - toSrc('/login') + await leaveChannel() + setTimeout(() => { + toSrc('/login') + }, 5000); break; } }) @@ -223,7 +256,7 @@ const App: React.FC = () => { } }) }; - const leaveChannel = async (bool?: boolean): Promise => { + const leaveChannel = async (): Promise => { if (location.hash.indexOf('/meeting') === 1) { window.electron.closeChildWindow('shareScreenWindow') setKeyOpenChildWindow('shareScreenWindow', false) @@ -240,11 +273,9 @@ const App: React.FC = () => { }) }) const data = JSON.parse(localStorage.stateInfo); - if (!bool) { - await GetLeave({ - roomNum: data.channelId, - }) - } + await window.electron.onInvoke('levelChannel', { + roomNum: data.channelId + }) await agora.leaveChannel() } }; @@ -260,12 +291,8 @@ const App: React.FC = () => { storage.removeItem('user') navigate('/login') } - } else if (e.key === 'reconnect') { - if (e.value == true) { - if (location.hash.indexOf('/meeting') === -1) { - window.electron.updateHandle() - } - } + } else if (e.key === 'env') { + } }; return ( diff --git a/src/api/Login/index.ts b/src/api/Login/index.ts index ce1a034..b7356c1 100644 --- a/src/api/Login/index.ts +++ b/src/api/Login/index.ts @@ -22,4 +22,10 @@ export const PostAnonLogin = (data: any) => url: `/auth/anon-login`, method: 'post', data, + }) + +export const GetCheckOnline = (account: string) => + request({ + url: `/auth/check-online?account=${account}`, + method: 'get' }) \ No newline at end of file diff --git a/src/api/Meeting/index.ts b/src/api/Meeting/index.ts index 7005eba..e8c28a6 100644 --- a/src/api/Meeting/index.ts +++ b/src/api/Meeting/index.ts @@ -170,4 +170,15 @@ export const PostSharedScreen = (roomNum: string) => request({ url: `/room/shared-screen?roomNum=${roomNum}`, 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 }) \ No newline at end of file diff --git a/src/assets/icon1.png b/src/assets/icon1.png index b276450..937a7c7 100644 Binary files a/src/assets/icon1.png and b/src/assets/icon1.png differ diff --git a/src/assets/icon53.png b/src/assets/icon53.png index ff50e9e..e6736fe 100644 Binary files a/src/assets/icon53.png and b/src/assets/icon53.png differ diff --git a/src/assets/virtualBackground/1.png b/src/assets/virtualBackground/1.png index a29260e..a34bbf0 100644 Binary files a/src/assets/virtualBackground/1.png and b/src/assets/virtualBackground/1.png differ diff --git a/src/assets/virtualBackground/2.png b/src/assets/virtualBackground/2.png index 8a025ca..0ca8531 100644 Binary files a/src/assets/virtualBackground/2.png and b/src/assets/virtualBackground/2.png differ diff --git a/src/assets/virtualBackground/3.png b/src/assets/virtualBackground/3.png index 4211d42..6bbd4dc 100644 Binary files a/src/assets/virtualBackground/3.png and b/src/assets/virtualBackground/3.png differ diff --git a/src/assets/virtualBackground/4.png b/src/assets/virtualBackground/4.png index 692d2fd..b012459 100644 Binary files a/src/assets/virtualBackground/4.png and b/src/assets/virtualBackground/4.png differ diff --git a/src/assets/virtualBackground/5.png b/src/assets/virtualBackground/5.png index ec14085..48c2191 100644 Binary files a/src/assets/virtualBackground/5.png and b/src/assets/virtualBackground/5.png differ diff --git a/src/assets/virtualBackground/6.png b/src/assets/virtualBackground/6.png index c9dffc7..c6cfdcc 100644 Binary files a/src/assets/virtualBackground/6.png and b/src/assets/virtualBackground/6.png differ diff --git a/src/components/Code/index.module.scss b/src/components/Code/index.module.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/components/Code/index.tsx b/src/components/Code/index.tsx new file mode 100644 index 0000000..dfdb306 --- /dev/null +++ b/src/components/Code/index.tsx @@ -0,0 +1,48 @@ +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 ( + <> + { + 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 ?
+ +
+ 微信中长按图片识别小程序码
+ 加入会议 +
+
:
+ +
+ } + > +
+ +
+
+ + ) +}) + +export default memo(Code) \ No newline at end of file diff --git a/src/components/JoinSetting/index.tsx b/src/components/JoinSetting/index.tsx index 1bf4e7a..e5e6d22 100644 --- a/src/components/JoinSetting/index.tsx +++ b/src/components/JoinSetting/index.tsx @@ -9,6 +9,7 @@ import Avatar from '@/components/Avatar'; import { useNavigate } from 'react-router-dom'; import { agora } from '@/utils/package/agora'; import { role } from '@/config/role'; +import { PostHomeVerLog } from '@/api/Meeting'; const { setInterval, clearInterval } = require('timers'); let time = null as any; const JoinSetting = forwardRef((_props: any, ref: any) => { @@ -201,6 +202,13 @@ const JoinSetting = forwardRef((_props: any, ref: any) => { GetRoomInfo(roomNumber).then(async (res) => { if (res.code === 200) { await agora.release() + await window.electron.getVersion().then(async req => { + await PostHomeVerLog({ + version: req, + platformType: 1, + roomNum: roomNumber, + }) + }) navigate(`/meeting`, { state: { channelId: roomNumber, diff --git a/src/components/StupWizard/index.tsx b/src/components/StupWizard/index.tsx index 1dd1df4..4520f80 100644 --- a/src/components/StupWizard/index.tsx +++ b/src/components/StupWizard/index.tsx @@ -34,6 +34,9 @@ const StupWizard = forwardRef((_props: any, ref: any) => { } } storage.setItem('setting', JSON.stringify(setting)) + }, + getStupWizardModal: () => { + return isStupWizard } })) const [list, setList] = useState([ @@ -130,9 +133,11 @@ const StupWizard = forwardRef((_props: any, ref: any) => { }) const CurrencyComponents = () => { const [optionsValue, setOperationValue] = useState<'hide' | 'quit'>('hide'); + const [voiceStimulation, setVoiceStimulation] = useState(true); const setting = JSON.parse(storage.getItem('setting') as string) useEffect(() => { setOperationValue(setting.closeSetting) + setVoiceStimulation(setting.voiceStimulation) }, []); return ( <> @@ -151,6 +156,33 @@ const CurrencyComponents = () => { 不退出程序,最小化到托盘 +
+ 语音激励 + 开启语音激励后,会优先显示正在说话的与会成员。 + + } + title="" + > + + + { + setting.voiceStimulation = e.target.value; + storage.setItem('setting', JSON.stringify(setting)) + setVoiceStimulation(e.target.value) + }} style={{ flexShrink: 0, margin: '10px 0' }} value={voiceStimulation}> + 开启 + 关闭 + +
@@ -213,23 +245,25 @@ const VideoComponents = () => { }, [darkLightEnhancement]); useEffect(() => { if (typeof virtualBackground.sourceIndex === 'number') { - if (import.meta.env.VITE_ENV === 'development') { - window.electron.getAppPath().then((res: string) => { - const imagePath = path.join(res, 'src', 'assets', 'virtualBackground', `${virtualBackground.sourceIndex + 1}.png`); + window.electron.getEnv().then(res=>{ + if (res === 'development') { + window.electron.getAppPath().then((res: string) => { + const imagePath = path.join(res, 'src', 'assets', 'virtualBackground', `${virtualBackground.sourceIndex + 1}.png`); + agora.enableVirtualBackground(virtualBackground.isVirtualBackground, { + source: imagePath, + background_source_type: 2, + color: Number(virtualBackground.color), + }) + }) + } else { + const imagePath = path.join((process as any).resourcesPath, 'images', `${virtualBackground.sourceIndex + 1}.png`); agora.enableVirtualBackground(virtualBackground.isVirtualBackground, { source: imagePath, background_source_type: 2, color: Number(virtualBackground.color), }) - }) - } else { - const imagePath = path.join((process as any).resourcesPath, 'images', `${virtualBackground.sourceIndex + 1}.png`); - agora.enableVirtualBackground(virtualBackground.isVirtualBackground, { - source: imagePath, - background_source_type: 2, - color: Number(virtualBackground.color), - }) - } + } + }) } else { agora.enableVirtualBackground(virtualBackground.isVirtualBackground, { background_source_type: 1, @@ -696,7 +730,18 @@ const AudioComponents = () => { ecordingVolume: e, }) }} disabled={!audioDeviceManager.ecordingItem} /> + {/* || audioDeviceManager.autoEcordingVolume */} + {/*
+ { + setting.autoEcordingVolume = e.target.checked; + storage.setItem('setting', JSON.stringify(setting)) + setAudioDeviceManager({ + ...audioDeviceManager, + autoEcordingVolume: e.target.checked + }) + }} checked={audioDeviceManager.autoEcordingVolume}>自动调整麦克风音量 +
*/}
{ @@ -729,16 +774,6 @@ const AudioComponents = () => {
- {/*
- { - setting.autoEcordingVolume = e.target.checked; - storage.setItem('setting', JSON.stringify(setting)) - setAudioDeviceManager({ - ...audioDeviceManager, - autoEcordingVolume: e.target.checked - }) - }} checked={audioDeviceManager.autoEcordingVolume}>自动调整麦克风音量 -
*/}
diff --git a/src/components/UpdateModal/index.tsx b/src/components/UpdateModal/index.tsx index cf5adf4..7a72508 100644 --- a/src/components/UpdateModal/index.tsx +++ b/src/components/UpdateModal/index.tsx @@ -1,45 +1,106 @@ import styles from '@/components/UpdateModal/index.module.scss' import ImageUrl from '@/utils/package/imageUrl'; -import { getUpdateUrl } from '@/utils/package/public'; +import { getUpdateUrl, isVersion } from '@/utils/package/public'; +import { ExclamationCircleFilled } from '@ant-design/icons'; import { Button, Flex, Modal, Progress } from 'antd'; -import { forwardRef, useImperativeHandle, useState, memo } from "react"; +import { forwardRef, useImperativeHandle, useState, memo, useEffect } from "react"; +const { confirm } = Modal; -const UpdateModal = forwardRef((props: any, ref: any) => { +const UpdateModal = forwardRef((_props: any, ref: any) => { useImperativeHandle(ref, () => ({ changeModal: (data: any) => { - let dataJson = JSON.parse(data) - getContent() - if (dataJson.type === '0') { // 打开弹窗 - setIsUpdateModal(true) - } else if (dataJson.type === '1') { // 下载中 返回进度值 - setProgress(dataJson.value.toFixed(2)) - } else if (dataJson.type === '2') { // 下载完成 - setProgress(100) + if (JSON.stringify(data).includes('Error') || JSON.stringify(data).includes('{}')) { + setIsError(res => { + if (res) { + setIsError(false) + confirm({ + keyboard: false, + title: '提示', + icon: , + 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 [progress, setProgress] = useState(0); // 下载进度值 const [updateContent, setUpdateContent] = useState('') // 版本更新内容 - + const [env, setEnv] = useState('') + const [_isError, setIsError] = useState(true) + useEffect(() => { + window.electron.getEnv().then(res => { + setEnv(res) + }) + }, []) function getContent() { - fetch(`${getUpdateUrl(import.meta.env.VITE_ENV)}/update.txt?t=${+new Date()}`) // 配置服务器地址 - .then(async response => { - if (response.status === 200) { - return setUpdateContent(await response.text()) - } - throw new Error('Network response was not ok.'); - }) - .then(textContent => { - }) - .catch(error => { - }); + window.electron.getEnv().then(res => { + fetch(`${getUpdateUrl(res)}/update.txt?t=${+new Date()}`) // 配置服务器地址 + .then(async response => { + if (response.status === 200) { + return setUpdateContent(await response.text()) + } + throw new Error('Network response was not ok.'); + }) + .then(_textContent => { + }) + .catch(_error => { + }); + }) } function closeModal() { - if (progress != 100) { - window.electron.onDownload('0') // 取消下载 - } + // if (progress != 100) { + // window.electron.onDownload('0') // 取消下载 + // } setIsUpdateModal(false) } @@ -49,12 +110,12 @@ const UpdateModal = forwardRef((props: any, ref: any) => { title="" open={isUpdateModal} footer={null} - // onCancel={() => closeModal()} + onCancel={() => closeModal()} centered width={'400px'} className='modal-padding' maskClosable={false} - closeIcon={false} + closeIcon={progress === 100 ? false : true} >
@@ -67,7 +128,7 @@ const UpdateModal = forwardRef((props: any, ref: any) => { style={{ width: '100%', height: '40px', marginBottom: '10px' }} className={`m-ant-btn`} >立即更新 - {import.meta.env.VITE_ENV === "development" ?
setIsUpdateModal(false)}>暂不更新
: null} + {env === "development" ?
setIsUpdateModal(false)}>暂不更新
: null}
: progress < 100 ?
下载进度:{progress}% diff --git a/src/components/UserName/index.tsx b/src/components/UserName/index.tsx index e60c209..6189dbb 100644 --- a/src/components/UserName/index.tsx +++ b/src/components/UserName/index.tsx @@ -47,6 +47,10 @@ const UserName = forwardRef((props: any, ref: any) => { onClick={async () => { const stateInfo = await JSON.parse(storage.getItem('stateInfo') as string); if (info.userName) { + if (info.userName.length > 10) { + message.error('用户名称最多10个字!') + return + } PutAlterUname({ nickName: info.userName, roomNum: stateInfo.channelId, diff --git a/src/page/Home/Index/index.module.scss b/src/page/Home/Index/index.module.scss index f8603d3..58b0224 100644 --- a/src/page/Home/Index/index.module.scss +++ b/src/page/Home/Index/index.module.scss @@ -121,14 +121,6 @@ font-size: 14px; } } - - >div:nth-child(2) { - cursor: pointer; - - >img { - width: 16px; - } - } } >div:nth-child(2) { diff --git a/src/page/Home/Index/index.tsx b/src/page/Home/Index/index.tsx index a745085..7f4bac8 100644 --- a/src/page/Home/Index/index.tsx +++ b/src/page/Home/Index/index.tsx @@ -2,7 +2,7 @@ import styles from '@/page/Home/Index/index.module.scss' import { useEffect, useState, useRef } from "react"; import Operation from '@/components/Operation'; import { Button, Input, Modal, Pagination, Empty, message, Popover, Popconfirm, DatePicker, Select, Radio } from "antd"; -import { GetRoom, PostRoom, GetCheckoutRoomNum, GetRoomRtcToken, DeleteRoom, GetRecord, PostRoomInfo, GetQrcode } from '@/api/Home/Index'; +import { GetRoom, PostRoom, GetCheckoutRoomNum, GetRoomRtcToken, DeleteRoom, GetRecord, PostRoomInfo } from '@/api/Home/Index'; import ImageUrl from '@/utils/package/imageUrl' import { ExclamationCircleFilled, ReloadOutlined } from '@ant-design/icons'; import JoinSetting from '@/components/JoinSetting'; @@ -14,6 +14,10 @@ import dayjs from 'dayjs'; import StupWizard from '@/components/StupWizard'; import { GetSubDpList } from '@/api/Home/User'; 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 fs = require('fs').promises; const { exec } = require('child_process'); @@ -46,7 +50,6 @@ const Index: React.FC = () => { const [timeData, setTimeData] = useState([]); const [isCreateRoom, setIsCreateRoom] = useState(false); const [allowAnonymous, setAllowAnonymous] = useState(true); - const [baseImage, setBaseImage] = useState(''); const userInfo = JSON.parse(storage.getItem('user') as string) useEffect(() => { setUser(userInfo) @@ -245,35 +248,7 @@ const Index: React.FC = () => { {item.roomNum}
- { - 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 ?
- -
- 微信中长按图片识别小程序码
- 加入会议 -
-
:
- -
- } - > -
- -
-
- +
{role.ID.includes(userInfo.roleId) ? {
-
- 版本号:{version} +
+
+ 版本号:{version} + {update ? new : null} +
+ {update ?
+ +
: null}
+ } >
diff --git a/src/page/Login/index.tsx b/src/page/Login/index.tsx index e722f49..03d9011 100644 --- a/src/page/Login/index.tsx +++ b/src/page/Login/index.tsx @@ -4,11 +4,14 @@ import { useEffect, useState } from "react"; import { useNavigate } from 'react-router-dom'; import { Input, Button, Checkbox, message, Modal } from "antd" import { storage } from '@/utils' -import { GetCheckUser, PostAnonLogin, PostLogin } from '@/api/Login' +import { GetCheckOnline, GetCheckUser, PostAnonLogin, PostLogin } from '@/api/Login' import * as CryptoJS from 'crypto-js'; import ImageUrl from '@/utils/package/imageUrl' import { v4 as uuidv4 } from 'uuid'; 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 navigate = useNavigate(); const [accountPasswordStatus, setAccountPasswordStatus] = useState(false); @@ -37,13 +40,16 @@ const Login: React.FC = () => { roomNum: '', }) const [nameModal, setNameModal] = useState(false) - + const [env, setEnv] = useState('') useEffect(() => { window.electron.setMainWindowSize({ width: 752, height: 520, key: 'login' }) + window.electron.getEnv().then(res => { + setEnv(res) + }) if (storage.getItem('login')) { const login = JSON.parse(storage.getItem('login') as string); const data = { @@ -100,7 +106,32 @@ const Login: React.FC = () => { } GetCheckUser(operation.account).then(res => { if (res.code === 200) { - res.data ? setAccountPasswordStatus(true) : message.error('账号不存在!') + if (res.data) { + GetCheckOnline(operation.account).then(req => { + if (req.code === 200) { + if (req.data) { + confirm({ + title: '提示', + icon: , + content: `账号已在其他地方登录,是否强制登陆?`, + centered: true, + okText: '确定', + cancelText: '取消', + async onOk() { + setAccountPasswordStatus(true) + }, + onCancel() { + + } + }) + } else { + setAccountPasswordStatus(true) + } + } + }) + } else { + message.error('账号不存在!') + } } }) } @@ -173,7 +204,7 @@ const Login: React.FC = () => { <>
- + {env ? : null}
@@ -315,47 +346,58 @@ const Login: React.FC = () => { if (!anonInfo.nickName) { return message.error('请输入参会昵称!') } + if (anonInfo.nickName.length > 10) { + return message.error('参会昵称最多10个字!') + } storage.setItem('loading', true) - PostAnonLogin(anonInfo).then(async (res) => { - if (res.code == 200) { - storage.setItem('user', JSON.stringify(res.data)) - storage.setItem('userLogin', true) - await window.electron.startSignalr(res.data) - getRoomRtcToken(anonInfo.roomNum, (options: any) => { - if (options) { - GetRoomInfo(anonInfo.roomNum).then(async (res) => { - if (res.code === 200) { - setTimeout(() => { + isVersion((bool: boolean) => { + storage.setItem('loading', false) + if (bool) { + window.electron.onDownload('3') + } else { + storage.setItem('loading', true) + PostAnonLogin(anonInfo).then(async (res) => { + if (res.code == 200) { + storage.setItem('user', JSON.stringify(res.data)) + storage.setItem('userLogin', true) + 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) - 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) }) }}>进入
diff --git a/src/page/Meeting/ChatBigWindow/index.module.scss b/src/page/Meeting/ChatBigWindow/index.module.scss index 6b65512..6c6b70b 100644 --- a/src/page/Meeting/ChatBigWindow/index.module.scss +++ b/src/page/Meeting/ChatBigWindow/index.module.scss @@ -46,6 +46,8 @@ font-size: 14px; color: #F3F3F5; margin-left: 4px; + display: flex; + align-items: center; } >div {} @@ -77,6 +79,8 @@ >span { font-size: 14px; color: #F3F3F5; + display: flex; + align-items: center; } >div { diff --git a/src/page/Meeting/ChatBigWindow/index.tsx b/src/page/Meeting/ChatBigWindow/index.tsx index e87418b..a777ce3 100644 --- a/src/page/Meeting/ChatBigWindow/index.tsx +++ b/src/page/Meeting/ChatBigWindow/index.tsx @@ -206,19 +206,30 @@ const ChatBigWindow: React.FC = () => { >移出会议 : null}
:
用户不在房间内
}> -
+
{item.uid !== user.uid ? - {item.userName} {dayjs(item.timestamp).format('HH:mm:ss')} : - {dayjs(item.timestamp).format('HH:mm:ss')} {item.userName} + + {item.userName} + {dayjs(item.timestamp).format('HH:mm:ss')} + : + + {dayjs(item.timestamp).format('HH:mm:ss')} + {item.userName} + } -
- :
+ :
{item.uid !== user.uid ? - {item.userName}{dayjs(item.timestamp).format('HH:mm:ss')} : - {dayjs(item.timestamp).format('HH:mm:ss')} {item.userName} + + {item.userName} + {dayjs(item.timestamp).format('HH:mm:ss')} + : + + {dayjs(item.timestamp).format('HH:mm:ss')} + {item.userName} + }
}
{item.message}
diff --git a/src/page/Meeting/ChatSmallWindow/index.module.scss b/src/page/Meeting/ChatSmallWindow/index.module.scss index a5de0b3..b0dd6c1 100644 --- a/src/page/Meeting/ChatSmallWindow/index.module.scss +++ b/src/page/Meeting/ChatSmallWindow/index.module.scss @@ -31,7 +31,7 @@ display: flex; >span:nth-child(1) { - white-space: nowrap; + word-break: break-all; color: #ff970f; } diff --git a/src/page/Meeting/NoticeWindow/index.tsx b/src/page/Meeting/NoticeWindow/index.tsx index 6a00b7b..58930d2 100644 --- a/src/page/Meeting/NoticeWindow/index.tsx +++ b/src/page/Meeting/NoticeWindow/index.tsx @@ -21,7 +21,10 @@ const NoticeWindow: React.FC = () => { api.open({ message: '', description:
- {noticeItem.uname}申请发言 +
+
{noticeItem.uname}
+
申请发言
+
- -
-
, - duration: 10, - placement: 'bottomRight', - showProgress: true, - pauseOnHover: false, - }); - } - return bool + window.electron.setChildWindowShow({ + key: 'noticeWindow', + bool: true }) + channel.postMessage({ + type: 'noticeItem', + noticeItem: item + }); break; // 管理员查看随机用户 case 'Watch': @@ -932,7 +885,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, - ecordingVolume: res[1].ecordingVolume + ecordingVolume: setting.ecordingVolume } window.electron.onInvoke('sendDrivers', { uid: item.callerUid, @@ -1023,10 +976,35 @@ const Meeting: React.FC = () => { return res }) 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 () => { @@ -1182,6 +1160,61 @@ const Meeting: React.FC = () => { return () => clearTimeout(timer); }, [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: , + // 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) { @@ -1313,6 +1346,32 @@ const Meeting: React.FC = () => { channelId: connection.channelId, 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); } } @@ -1369,6 +1428,20 @@ const Meeting: React.FC = () => { } else if (state === 3) { meetingDisconnectedRef.current.changeModal(false) setIsAgoraDisconnected(false) + } else if (state === 5) { + confirm({ + keyboard: false, + title: '提示', + icon: , + content: `重连失败,请退出房间重试!`, + centered: true, + okText: '退出', + wrapClassName: 'hideCancelText', + cancelText: '', + async onOk() { + leaveChannel() + }, + }) } }, onConnectionLost: () => { @@ -1388,18 +1461,33 @@ const Meeting: React.FC = () => { if (bool) { stopScreenCapture() setSharedScreenItem('') - allUserLook(userInfo.uid, userInfo.userName) + allUserLook(userInfo.uid, userInfo.userName, true) } return bool }) } else if (reason === 3 || reason === 4) { message.error({ - content:
请检查摄像头是否正常工作,检查摄像头是否被其他应用占用,或者尝试重新加入频道, { + content:
检查摄像头是否正常、未被占用,或尝试重新加入频道。 { stupWizardRef.current.changeModal(1); }}>前往修改摄像头
, - duration: 60, + duration: 15, key: 'cameraTemporarily' }); + } else if (_state === 3) { + if (!stupWizardRef.current.getStupWizardModal()) { + await PostOpenCamera({ + roomNum: state.channelId, + uid: userInfo.uid, + enableCamera: false + }) + message.error({ + content:
检查摄像头是否正常、未被占用,或尝试重新加入频道。 { + stupWizardRef.current.changeModal(1); + }}>前往修改摄像头
, + duration: 15, + key: 'cameraTemporarily' + }); + } } }, onTokenPrivilegeWillExpire: async (connection: RtcConnection, _token: string) => { @@ -1411,6 +1499,23 @@ 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) { @@ -1664,6 +1769,20 @@ 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 duration = dayjs.duration(currentSeconds, 'seconds'); @@ -1731,8 +1850,8 @@ const Meeting: React.FC = () => { if (res) { GetSharedScreen(state.channelId).then(req => { if (req.code === 200) { - if (res.data) { - setIsShare(res.data) + if (req.data) { + setIsShare(req.data) } getDesktopCapturerVideo() setIsSharedScreenModal(true) @@ -1753,7 +1872,7 @@ const Meeting: React.FC = () => { } }) if (row.title === '停止共享') { - await allUserLook(userInfo.uid, userInfo.userName) + await allUserLook(userInfo.uid, userInfo.userName, true) } break; case '静音': @@ -2020,8 +2139,12 @@ const Meeting: React.FC = () => { }) }; // 设置全员看谁 - const allUserLook = async (uid: string, name: string): Promise => { - await PostShowUser(state.channelId, uid, name) + const allUserLook = async (uid: string, name: string, bool?: boolean): Promise => { + if (bool) { + await PostStopSharedScreen(state.channelId) + } else { + await PostShowUser(state.channelId, uid, name) + } } // 设置发言人 const postRoomManager = async (data: any): Promise => { @@ -2083,7 +2206,25 @@ const Meeting: React.FC = () => { item.isRoom = true; item.isAdmin = role.ID.includes(item.roleId) || item.isRoomManager }) - setRoomUserList(res.data) + setRoomUserList((req: any) => { + 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) => { await agora.updateChannelMediaOptions(res ? true : false) changeAgoraDevice() @@ -2115,7 +2256,7 @@ const Meeting: React.FC = () => { enableCamera: !storeDevice[0][1].active, isRoomManager: userItem ? userItem.isRoomManager : false, }) - await getShowUser() + setVideoUser() if (userItem.isRoomManager) { await postOpenMicr(!storeDevice[0][0].active, userInfo.uid) await postOpenCamera(!storeDevice[0][1].active, userInfo.uid) @@ -2147,25 +2288,28 @@ const Meeting: React.FC = () => { const sendMsg = (text?: string): void => { let msg = text ? text : textMsg; if (msg) { - window.electron.onInvoke('sendChannelMsg', { - roomNum: state.channelId, - msg: msg, - }) - 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, + setRoomUserList((res: any) => { + let row = res.find((item: any) => item.uid === userInfo.uid) + window.electron.onInvoke('sendChannelMsg', { + 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 }) - setTextMsg(''); - chatScrollBotton() } else { message.error('请输入内容!') } @@ -2213,8 +2357,6 @@ const Meeting: React.FC = () => { } }) } - - // 开关麦克风 const postOpenMicrApi = async (enableMicr: boolean, uid: string, isAll: boolean, isMessage: boolean = false): Promise => { if (isAll) { @@ -2223,11 +2365,20 @@ const Meeting: React.FC = () => { enableMicr }) } else { - await PostOpenMicr({ - roomNum: state.channelId, + await window.electron.onInvoke('sendOper2User', { uid, - enableMicr + contentString: JSON.stringify({ + roomNum: state.channelId, + uid, + enableMicr, + type: 'audio' + }) }) + // await PostOpenMicr({ + // roomNum: state.channelId, + // uid, + // enableMicr + // }) } if (isMessage) { // message.success('操作成功') @@ -2268,11 +2419,20 @@ const Meeting: React.FC = () => { } else { await agora.stopCameraCapture(); } - await PostOpenCamera({ - roomNum: state.channelId, + await window.electron.onInvoke('sendOper2User', { uid, - enableCamera + contentString: JSON.stringify({ + roomNum: state.channelId, + uid, + enableCamera, + type: 'video' + }) }) + // await PostOpenCamera({ + // roomNum: state.channelId, + // uid, + // enableCamera + // }) if (isMessage) { // message.success('操作成功') } @@ -2376,7 +2536,7 @@ const Meeting: React.FC = () => {
//右 } else { - return
setIsVideoFullScreen(true)}> + return
setIsVideoFullScreen(true)}> @@ -2415,6 +2575,14 @@ const Meeting: React.FC = () => { } 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 => { confirm({ @@ -2601,7 +2769,9 @@ const Meeting: React.FC = () => { }
{changeCurrentSeconds()}
-
会议号:{state.channelId} 会议名称:{state.roomName}
+
会议号:{state.channelId} 会议名称:{state.roomName} + +
{
: null) } )} - {isAdmin > 6 ?
+ {hasScrollbar() ?
{meetingMode === "StandardMode" ?
{ const container = document.getElementById('videoView') as HTMLElement; container.scrollLeft -= 100 }}> -
:
{ +
:
{ const container = document.getElementById('videoView') as HTMLElement; container.scrollTop -= 100 }}> @@ -2786,7 +2956,7 @@ const Meeting: React.FC = () => { container.scrollLeft += 100 }}> -
:
{ +
:
{ const container = document.getElementById('videoView') as HTMLElement; container.scrollTop += 100 }}> @@ -2900,7 +3070,7 @@ const Meeting: React.FC = () => {
- {item.userName}{item.uid === user.uid ? '(我)' : ''} + {item.userName}{item.uid === user.uid ? '(我)' : ''} {role.ID.includes(item.roleId) || item.isRoomManager ? {role.ID.includes(item.roleId) ? '管理员' : '发言人'} @@ -3120,19 +3290,30 @@ const Meeting: React.FC = () => { >移出会议 : null}
:
用户不在房间内
}> -
+
{item.uid !== user.uid ? - {item.userName} {dayjs(item.timestamp).format('HH:mm:ss')} : - {dayjs(item.timestamp).format('HH:mm:ss')} {item.userName} + + {item.userName} + {dayjs(item.timestamp).format('HH:mm:ss')} + : + + {dayjs(item.timestamp).format('HH:mm:ss')} + {item.userName} + } -
- :
+ :
{item.uid !== user.uid ? - {item.userName}{dayjs(item.timestamp).format('HH:mm:ss')} : - {dayjs(item.timestamp).format('HH:mm:ss')} {item.userName} + + {item.userName} + {dayjs(item.timestamp).format('HH:mm:ss')} + : + + {dayjs(item.timestamp).format('HH:mm:ss')} + {item.userName} + }
}
{item.message}
@@ -3157,7 +3338,10 @@ const Meeting: React.FC = () => { { setTextMsg(e.target.value) }}> - +
: @@ -3327,7 +3511,7 @@ const Meeting: React.FC = () => { key={rowIndex}>