Compare commits
133 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
016dcf6e09 | |
|
|
bde5002e1e | |
|
|
bcfc790dd3 | |
|
|
55deae9654 | |
|
|
a08e0a6ece | |
|
|
00c16d44ff | |
|
|
e90186e4b9 | |
|
|
c9a6984a02 | |
|
|
440f19b436 | |
|
|
f0c79d64cd | |
|
|
f6aca11a83 | |
|
|
c69d965183 | |
|
|
489d5c8511 | |
|
|
76e968322d | |
|
|
ef55fab17d | |
|
|
1f897ad6b3 | |
|
|
56e3a19356 | |
|
|
a02232a770 | |
|
|
715ee0be43 | |
|
|
d6e9de24bf | |
|
|
860f141782 | |
|
|
882bacb2d5 | |
|
|
700566fd7e | |
|
|
f438a90c76 | |
|
|
b185bb7d67 | |
|
|
06a2e6e4ac | |
|
|
4b59b1a18d | |
|
|
ed2c39e7fc | |
|
|
1e3107d0e2 | |
|
|
37c06ca19f | |
|
|
7093367904 | |
|
|
4121283ef1 | |
|
|
705e5f1b01 | |
|
|
864f305acf | |
|
|
2b4a3cdbca | |
|
|
e01c308800 | |
|
|
62846483fd | |
|
|
8094a2669a | |
|
|
034d46b39e | |
|
|
f9dc5830aa | |
|
|
13de6c510d | |
|
|
b4d03efec7 | |
|
|
550073a73a | |
|
|
40342246cc | |
|
|
d11f437e2d | |
|
|
ac00b4b2a9 | |
|
|
6d6a9756e8 | |
|
|
b689fa0f76 | |
|
|
caba67da14 | |
|
|
fce3209e59 | |
|
|
67682defa8 | |
|
|
32d238fb7f | |
|
|
4520bba95d | |
|
|
32acac493d | |
|
|
1a98b6e7d4 | |
|
|
aa76736cf4 | |
|
|
79d393ab14 | |
|
|
c79c06aaa3 | |
|
|
0fbcb32565 | |
|
|
dc9ee39a1e | |
|
|
f709f3db42 | |
|
|
2b97e5d93b | |
|
|
4776b15026 | |
|
|
1ec1c1f12f | |
|
|
a80ee1efab | |
|
|
3c2356087d | |
|
|
7363635217 | |
|
|
4cf65d50a3 | |
|
|
0fb2b4bf75 | |
|
|
7f6c0bffb4 | |
|
|
1b62f6a9ff | |
|
|
0d50925d0b | |
|
|
2598ecbcea | |
|
|
fe1ed45d94 | |
|
|
52d8ff14b1 | |
|
|
7f9e62027a | |
|
|
e0ac4b2223 | |
|
|
05c4cfdd95 | |
|
|
369aaa420c | |
|
|
cc69c3d864 | |
|
|
f70074aa6d | |
|
|
0c052a6cd0 | |
|
|
d45cc15b1f | |
|
|
f24150af42 | |
|
|
fb05838a68 | |
|
|
c0b6b7afd1 | |
|
|
7fd79873f3 | |
|
|
c71ea10a4b | |
|
|
3c9b3ac48a | |
|
|
7b4cfbeb1d | |
|
|
25e9f16af0 | |
|
|
fd809034c1 | |
|
|
26ce3707d7 | |
|
|
74760e6382 | |
|
|
2e2074fda3 | |
|
|
0e37a751ea | |
|
|
70c54d71fd | |
|
|
a4ae5577d4 | |
|
|
2164fcfde4 | |
|
|
c8dc0e3276 | |
|
|
24a174c7b5 | |
|
|
e5c4b85dc4 | |
|
|
8bd09d6f01 | |
|
|
54a5b442cd | |
|
|
1d2f1072ef | |
|
|
c2b36c0b3f | |
|
|
dae9b35802 | |
|
|
9b58afece8 | |
|
|
75396341eb | |
|
|
678962d1e9 | |
|
|
dc619db987 | |
|
|
982867dfe1 | |
|
|
96959a3e6f | |
|
|
2afcef025b | |
|
|
1887bb39bc | |
|
|
bfb85e53a9 | |
|
|
88cad54e52 | |
|
|
ad16a0867b | |
|
|
6d98f0fc0f | |
|
|
c6887b2747 | |
|
|
36e1bda441 | |
|
|
f993e740d7 | |
|
|
a5e2e8b036 | |
|
|
9080065f60 | |
|
|
9f045f7531 | |
|
|
d10fdd1d5c | |
|
|
3b79abb8ee | |
|
|
c299f6f5af | |
|
|
496a4dd28f | |
|
|
956f045fad | |
|
|
2abf010867 | |
|
|
20604bb28a | |
|
|
9a6a5904a4 |
|
|
@ -1,6 +0,0 @@
|
|||
#基础API 绝对的
|
||||
VITE_BASE_URL_API = 'http://192.168.2.9:5192'
|
||||
#当前IP 相对的
|
||||
VITE_BASE_CURRENT_API = '.'
|
||||
#开发环境
|
||||
VITE_ENV = 'development'
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#基础API 绝对的
|
||||
VITE_BASE_URL_API = 'https://meeting-api.23544.com/pc'
|
||||
#当前IP 相对的
|
||||
VITE_BASE_CURRENT_API = '.'
|
||||
#生产环境
|
||||
VITE_ENV = 'production'
|
||||
6
.env.xy
|
|
@ -1,6 +0,0 @@
|
|||
#基础API 绝对的
|
||||
VITE_BASE_URL_API = 'https://meeting-api.23544.com/pc'
|
||||
#当前IP 相对的
|
||||
VITE_BASE_CURRENT_API = '.'
|
||||
#测试环境
|
||||
VITE_ENV = 'xy'
|
||||
|
Before Width: | Height: | Size: 162 KiB |
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -51,6 +51,7 @@
|
|||
"deleteAppDataOnUninstall": true,
|
||||
"shortcutName": "湖北襄阳四中教研平台",
|
||||
"allowElevation": true,
|
||||
"perMachine": true
|
||||
"perMachine": true,
|
||||
"include": "build/install.nsh"
|
||||
}
|
||||
}
|
||||
227
main.js
|
|
@ -10,6 +10,8 @@ const {
|
|||
dialog,
|
||||
crashReporter,
|
||||
desktopCapturer,
|
||||
powerSaveBlocker,
|
||||
net
|
||||
} = require('electron');
|
||||
const path = require('node:path')
|
||||
const updateJs = require('./src/utils/package/update')
|
||||
|
|
@ -17,17 +19,20 @@ 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')
|
||||
const id = powerSaveBlocker.start('prevent-display-sleep')
|
||||
powerSaveBlocker.stop(id)
|
||||
|
||||
class AppWindow extends BrowserWindow {
|
||||
constructor(config) {
|
||||
|
|
@ -48,12 +53,15 @@ class AppWindow extends BrowserWindow {
|
|||
const finalConfig = { ...basicConfig, ...config };
|
||||
super(finalConfig);
|
||||
if (env === 'development') {
|
||||
// 开发
|
||||
this.loadURL('http://localhost:3000');
|
||||
if (buildStatus) {
|
||||
this.loadURL('http://192.168.2.9:8827/');
|
||||
} else {
|
||||
// 测试 | 生产
|
||||
this.loadFile(path.resolve(__dirname, './dist/index.html'));
|
||||
this.loadURL('http://localhost:3000');
|
||||
}
|
||||
} else {
|
||||
this.loadURL('https://meeting-api.23544.com/')
|
||||
}
|
||||
// this.loadFile(path.resolve(__dirname, './dist/index.html'))
|
||||
this.once('ready-to-show', () => {
|
||||
this.show();
|
||||
});
|
||||
|
|
@ -62,33 +70,21 @@ class AppWindow extends BrowserWindow {
|
|||
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);
|
||||
const tray = new Tray(trayIcon);
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '打开', click: () => {
|
||||
mainWindow.webContents.send('isOpenWindows');
|
||||
},
|
||||
// icon: iconPath,
|
||||
},
|
||||
{
|
||||
label: '最小化到系统托盘', click: () => {
|
||||
mainWindow.hide();
|
||||
},
|
||||
// icon: iconPath,
|
||||
},
|
||||
{
|
||||
label: '退出', click: async () => {
|
||||
quit()
|
||||
},
|
||||
// icon: iconPath,
|
||||
},
|
||||
]);
|
||||
tray.setToolTip(updateJs.getTitle(envStr));
|
||||
tray.setContextMenu(contextMenu);
|
||||
tray = new Tray(trayIcon);
|
||||
tray.setToolTip(updateJs.getTitle(env));
|
||||
tray.on('click', () => {
|
||||
mainWindow.webContents.send('isOpenWindows');
|
||||
});
|
||||
|
|
@ -97,9 +93,21 @@ function createTray() {
|
|||
function createWindow() {
|
||||
mainWindow = new AppWindow();
|
||||
mainWindow.focus();
|
||||
mainWindow.hookWindowMessage(278, function (e) {
|
||||
mainWindow.setEnabled(false);//窗口禁用
|
||||
setTimeout(() => {
|
||||
mainWindow.setEnabled(true);//窗口启用
|
||||
}, 100);
|
||||
return true;
|
||||
})
|
||||
}
|
||||
const additionalData = { myKey: 'myValue' }
|
||||
app.on('ready', () => {
|
||||
// 检查网络状态
|
||||
if (!checkNetworkStatus()) {
|
||||
return;
|
||||
}
|
||||
// const gotTheLock = true
|
||||
const gotTheLock = app.requestSingleInstanceLock(additionalData)
|
||||
if (gotTheLock) {
|
||||
app.getPath('crashDumps')
|
||||
|
|
@ -107,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,
|
||||
|
|
@ -136,6 +143,12 @@ app.on('ready', () => {
|
|||
mainWindow.webContents.openDevTools()
|
||||
}
|
||||
});
|
||||
mainWindow.on('focus', () => {
|
||||
mainWindow.show()
|
||||
});
|
||||
mainWindow.on('maximize', () => {
|
||||
mainWindow.show()
|
||||
});
|
||||
// 监听移动
|
||||
mainWindow.on('move', () => {
|
||||
// 如果是全屏自动恢复到上次窗口大小
|
||||
|
|
@ -147,17 +160,20 @@ 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++
|
||||
}
|
||||
});
|
||||
// 更新
|
||||
ipcMain.handle('updateHandle', () => {
|
||||
autoUpdater.checkForUpdates()
|
||||
});
|
||||
// socket
|
||||
ipcMain.handle('startSignalr', (event, user) => {
|
||||
startSignalr(user)
|
||||
|
|
@ -182,6 +198,11 @@ app.on('ready', () => {
|
|||
connection.off('SetDriver');
|
||||
connection.off('ShowDriverList');
|
||||
connection.off('ModifyNickName');
|
||||
connection.off('JoinChannelCallback');
|
||||
connection.off('ExitSharedScreen');
|
||||
connection.off('SetSpeaker');
|
||||
connection.off('ReceivedOperation');
|
||||
connection.off('StopedSharedScreen');
|
||||
}
|
||||
});
|
||||
ipcMain.handle('onStop', (event) => {
|
||||
|
|
@ -201,8 +222,7 @@ app.on('ready', () => {
|
|||
await connection.invoke(str, data.roomNum, data.msg)
|
||||
break;
|
||||
case 'sendOper':
|
||||
// 4:屏幕共享
|
||||
await connection.invoke(str, data.roomNum, data.type)
|
||||
await connection.invoke(str, data.roomNum, data.contentString)
|
||||
break;
|
||||
case 'getDrivers':
|
||||
// 获取某个人的设备列表
|
||||
|
|
@ -217,13 +237,21 @@ app.on('ready', () => {
|
|||
await connection.invoke(str, data.uid, data.driversJsonString)
|
||||
break;
|
||||
case 'joinChannel':
|
||||
// 设置某个人的设备列表
|
||||
// 加入房间
|
||||
await connection.invoke(str, data.roomNum, data.enableMicr, data.enableCamera, data.isRoomManager || false)
|
||||
break;
|
||||
case 'levelChannel':
|
||||
// 设置某个人的设备列表
|
||||
// 退出房间
|
||||
await connection.invoke(str, data.roomNum)
|
||||
break;
|
||||
case 'SetSpeakerCallback':
|
||||
// 发言人设置成功
|
||||
await connection.invoke(str, data)
|
||||
break;
|
||||
case 'sendOper2User':
|
||||
// 扩展参数
|
||||
await connection.invoke(str, data.uid, data.contentString)
|
||||
break;
|
||||
}
|
||||
});
|
||||
ipcMain.handle('onOtherSignalr', (event) => {
|
||||
|
|
@ -254,10 +282,10 @@ app.on('ready', () => {
|
|||
})
|
||||
});
|
||||
// 扩展操作
|
||||
connection.on("Operation", (type) => {
|
||||
connection.on("Operation", (contentString) => {
|
||||
mainWindow.webContents.send('onSignalr', {
|
||||
key: 'Operation',
|
||||
type
|
||||
contentString
|
||||
})
|
||||
});
|
||||
// 移出会议
|
||||
|
|
@ -379,6 +407,40 @@ app.on('ready', () => {
|
|||
nickName
|
||||
})
|
||||
});
|
||||
// 加入房间回调
|
||||
connection.on("JoinChannelCallback", (isSuccess) => {
|
||||
mainWindow.webContents.send('onSignalr', {
|
||||
key: 'JoinChannelCallback',
|
||||
isSuccess,
|
||||
})
|
||||
});
|
||||
// 退出共享
|
||||
connection.on("ExitSharedScreen", () => {
|
||||
mainWindow.webContents.send('onSignalr', {
|
||||
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
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
// 放大缩小退出窗口
|
||||
|
|
@ -420,6 +482,10 @@ app.on('ready', () => {
|
|||
ipcMain.handle('getVersion', () => {
|
||||
return app.getVersion();
|
||||
});
|
||||
// 获取环境
|
||||
ipcMain.handle('getEnv', () => {
|
||||
return env;
|
||||
});
|
||||
// 获取窗口是否显示
|
||||
ipcMain.handle('isVisible', () => {
|
||||
return mainWindow.isVisible();
|
||||
|
|
@ -455,6 +521,11 @@ app.on('ready', () => {
|
|||
downloadUpdate()
|
||||
} else if (data === '2') { // 下载完成 点击安装
|
||||
quitAndInstall()
|
||||
} else if (data === '3') { // 打开弹窗
|
||||
let message = JSON.stringify({
|
||||
type: '3',
|
||||
})
|
||||
sendUpdateMessage(message)
|
||||
}
|
||||
});
|
||||
// 选择文件夹
|
||||
|
|
@ -489,9 +560,32 @@ app.on('ready', () => {
|
|||
// 设置桌面应用基础属性
|
||||
ipcMain.handle('setMainWindowSize', (event, config) => {
|
||||
if (config.width === 250) {
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '退出',
|
||||
click: () => quit(),
|
||||
},
|
||||
]);
|
||||
tray.setContextMenu(contextMenu);
|
||||
mainWindow.setSkipTaskbar(true)
|
||||
mainWindow.setResizable(false)
|
||||
mainWindow.setAlwaysOnTop(true, 'screen-saver')
|
||||
} else {
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '打开',
|
||||
click: () => mainWindow.webContents.send('isOpenWindows'),
|
||||
},
|
||||
{
|
||||
label: '最小化到系统托盘',
|
||||
click: () => mainWindow.hide(),
|
||||
},
|
||||
{
|
||||
label: '退出',
|
||||
click: () => quit(),
|
||||
},
|
||||
]);
|
||||
tray.setContextMenu(contextMenu);
|
||||
}
|
||||
// 设置最小窗口尺寸
|
||||
mainWindow.setMinimumSize(config.width, config.height);
|
||||
|
|
@ -549,13 +643,26 @@ app.on('ready', () => {
|
|||
width: config.width,
|
||||
height: config.height,
|
||||
})
|
||||
if (envStr === 'development') {
|
||||
if (env === 'development') {
|
||||
// 开发
|
||||
if (buildStatus) {
|
||||
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);//窗口禁用
|
||||
setTimeout(() => {
|
||||
child.setEnabled(true);//窗口启用
|
||||
}, 100);
|
||||
return true;
|
||||
})
|
||||
childWindow[config.key] = child
|
||||
child.once('ready-to-show', () => {
|
||||
if (config.show) {
|
||||
|
|
@ -649,7 +756,6 @@ app.on('ready', () => {
|
|||
});
|
||||
// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
|
||||
function updateHandle() {
|
||||
autoUpdater.checkForUpdates()
|
||||
// autoUpdater.checkForUpdatesAndNotify().catch();
|
||||
const message = {
|
||||
error: '检查更新出错',
|
||||
|
|
@ -657,23 +763,26 @@ 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)
|
||||
})
|
||||
autoUpdater.on('update-available', function (info) {
|
||||
let messageStr = JSON.stringify({ type: '0' })
|
||||
setTimeout(() => {
|
||||
sendUpdateMessage(messageStr)
|
||||
}, 5000)
|
||||
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) {
|
||||
|
|
@ -746,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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
17
package.json
|
|
@ -1,21 +1,17 @@
|
|||
{
|
||||
"name": "WGShare.Metting",
|
||||
"private": true,
|
||||
"version": "0.6.3",
|
||||
"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",
|
||||
|
|
|
|||
16
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,13 @@ window.electron = {
|
|||
isOpenWindows: (callback) => {
|
||||
ipcRenderer.on('isOpenWindows', callback)
|
||||
},
|
||||
// 设置环境变量
|
||||
setEnv: (str) => {
|
||||
ipcRenderer.invoke('setEnv', str)
|
||||
// 首次加载
|
||||
startLoad: () => {
|
||||
ipcRenderer.invoke('startLoad')
|
||||
},
|
||||
// 更新
|
||||
updateHandle: () => {
|
||||
ipcRenderer.invoke('updateHandle')
|
||||
},
|
||||
// 通知下载最新的包
|
||||
onDownload: (type) => {
|
||||
|
|
@ -130,7 +138,7 @@ window.electron = {
|
|||
ipcRenderer.invoke('createChildWindow', {
|
||||
url: location.origin + `/#/noticeWindow`,
|
||||
width: 388,
|
||||
height: 150,
|
||||
height: 180,
|
||||
key: 'noticeWindow',
|
||||
})
|
||||
ipcRenderer.invoke('createChildWindow', {
|
||||
|
|
|
|||
67
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,9 +42,7 @@ 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);
|
||||
if (userInfo && !userInfo.isAnonymous) {
|
||||
if (loginInfo && loginInfo.isAutoLogin) {
|
||||
const login = () => {
|
||||
PostLogin({
|
||||
account: loginInfo.account,
|
||||
pwd: CryptoJS.MD5(loginInfo.password).toString(CryptoJS.enc.Hex)
|
||||
|
|
@ -57,6 +56,31 @@ const App: React.FC = () => {
|
|||
toSrc('/login')
|
||||
}
|
||||
})
|
||||
}
|
||||
if (userInfo && !userInfo.isAnonymous) {
|
||||
if (loginInfo && loginInfo.isAutoLogin) {
|
||||
GetCheckOnline(loginInfo.account).then(req => {
|
||||
if (req.code === 200) {
|
||||
if (req.data) {
|
||||
confirm({
|
||||
title: '提示',
|
||||
icon: <ExclamationCircleFilled />,
|
||||
content: `账号已在其他地方登录,是否强制登陆?`,
|
||||
centered: true,
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
async onOk() {
|
||||
login()
|
||||
},
|
||||
onCancel() {
|
||||
toSrc('/login')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
login()
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
toSrc('/login')
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
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)
|
||||
await leaveChannel()
|
||||
setTimeout(() => {
|
||||
toSrc('/login')
|
||||
}, 5000);
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
|
@ -223,7 +256,7 @@ const App: React.FC = () => {
|
|||
}
|
||||
})
|
||||
};
|
||||
const leaveChannel = async (bool?: boolean): Promise<void> => {
|
||||
const leaveChannel = async (): Promise<void> => {
|
||||
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,6 +291,8 @@ const App: React.FC = () => {
|
|||
storage.removeItem('user')
|
||||
navigate('/login')
|
||||
}
|
||||
} else if (e.key === 'env') {
|
||||
|
||||
}
|
||||
};
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ export const GetRoom = (data: { pageIndex: number, pageSize: number }) =>
|
|||
method: 'get'
|
||||
})
|
||||
|
||||
export const PostFeedback = (data: any) =>
|
||||
request({
|
||||
url: `/home/feedback`,
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
export const PostRoom = (data: any) =>
|
||||
request({
|
||||
url: `/home/room`,
|
||||
|
|
|
|||
|
|
@ -23,3 +23,9 @@ export const PostAnonLogin = (data: any) =>
|
|||
method: 'post',
|
||||
data,
|
||||
})
|
||||
|
||||
export const GetCheckOnline = (account: string) =>
|
||||
request({
|
||||
url: `/auth/check-online?account=${account}`,
|
||||
method: 'get'
|
||||
})
|
||||
|
|
@ -71,14 +71,20 @@ export const PostRoomManager = (data: any) =>
|
|||
request({
|
||||
url: `/room/manager`,
|
||||
method: 'post',
|
||||
data
|
||||
data: {
|
||||
...data,
|
||||
SettingUserId: ''
|
||||
}
|
||||
})
|
||||
|
||||
export const DeleteRoomManager = (data: any) =>
|
||||
request({
|
||||
url: `/room/manager`,
|
||||
method: 'delete',
|
||||
data
|
||||
data: {
|
||||
...data,
|
||||
SettingUserId: ''
|
||||
}
|
||||
})
|
||||
|
||||
export const GetRoomKickout = (roomNum: string, kickUid: string) =>
|
||||
|
|
@ -155,3 +161,24 @@ export const PutAlterUname = (data: any) =>
|
|||
method: 'put',
|
||||
data
|
||||
})
|
||||
export const GetSharedScreen = (roomNum: string) =>
|
||||
request({
|
||||
url: `/room/shared-screen?roomNum=${roomNum}`,
|
||||
method: 'get'
|
||||
})
|
||||
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
|
||||
})
|
||||
|
Before Width: | Height: | Size: 275 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 712 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 838 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 600 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 921 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 58 KiB |
|
|
@ -1,5 +1,5 @@
|
|||
import styles from '@/components/Avatar/index.module.scss'
|
||||
import { useState, useImperativeHandle, forwardRef } from "react";
|
||||
import { memo, useImperativeHandle, forwardRef } from "react";
|
||||
const Avatar = forwardRef((props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
getData: () => {
|
||||
|
|
@ -15,4 +15,4 @@ const Avatar = forwardRef((props: any, ref: any) => {
|
|||
)
|
||||
})
|
||||
|
||||
export default Avatar
|
||||
export default memo(Avatar)
|
||||
|
|
@ -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 (
|
||||
<>
|
||||
<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)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import styles from '@/components/EquipmentManagement/index.module.scss'
|
||||
import { getKeyOpenChildWindow } from '@/utils/package/public';
|
||||
import { Button, Modal, Select, Slider, message } from 'antd';
|
||||
import { useState, useImperativeHandle, forwardRef } from "react";
|
||||
import { useState, useImperativeHandle, forwardRef, memo } from "react";
|
||||
const EquipmentManagement = forwardRef((props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
changeModal: async (uid: string, userName: string) => {
|
||||
|
|
@ -116,4 +116,4 @@ const EquipmentManagement = forwardRef((props: any, ref: any) => {
|
|||
)
|
||||
})
|
||||
|
||||
export default EquipmentManagement
|
||||
export default memo(EquipmentManagement)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
.feedBackModel {
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.feedBackModelContent {
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
margin: 10px 0;
|
||||
|
||||
.feedBackModelContentRate {
|
||||
margin-bottom: 20px;
|
||||
background-color: #101215;
|
||||
padding: 10px 20px 30px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.feedBackModelContentList {
|
||||
margin-bottom: 20px;
|
||||
|
||||
>div:nth-child(2) {
|
||||
>div {
|
||||
background-color: #101215;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
color: #7F859B;
|
||||
font-size: 14px;
|
||||
padding: 4px 8px;
|
||||
box-sizing: border-box;
|
||||
border: 1px transparent solid;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: white;
|
||||
border: 1px #495EAD solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.feedBackModelFooter {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
import { PostFeedback } from '@/api/Home/Index';
|
||||
import styles from '@/components/FeedBackModel/index.module.scss'
|
||||
import { Button, message, Modal, Rate } from 'antd';
|
||||
import TextArea from 'antd/es/input/TextArea';
|
||||
import { useState, useImperativeHandle, forwardRef, memo } from "react";
|
||||
const FeedBackModel = forwardRef((_props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
changeModal: () => {
|
||||
setIsFeedBackModel(true)
|
||||
},
|
||||
}))
|
||||
const [isFeedBackModel, setIsFeedBackModel] = useState(false);
|
||||
const [feedBackForm, setFeedBackForm] = useState({
|
||||
rateValue: 0,
|
||||
otherContent: '',
|
||||
});
|
||||
const [feedBackList, setFeedBackList] = useState([
|
||||
{
|
||||
text: "软件卡顿",
|
||||
value: 2,
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
text: "设计不合理",
|
||||
value: 3,
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
text: "功能太少",
|
||||
value: 4,
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
text: "通话不流畅",
|
||||
value: 5,
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
text: "视频卡顿",
|
||||
value: 6,
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
text: "操作麻烦",
|
||||
value: 7,
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
text: "其他,需要手动填写",
|
||||
value: 1,
|
||||
active: false,
|
||||
},
|
||||
]);
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title="反馈建议评分"
|
||||
open={isFeedBackModel}
|
||||
footer={null}
|
||||
destroyOnClose={true}
|
||||
onCancel={() => setIsFeedBackModel(false)}
|
||||
centered
|
||||
width={'500px'}
|
||||
>
|
||||
<div className={styles.feedBackModel}>
|
||||
<div className={styles.feedBackModelContent}>
|
||||
<div className={styles.feedBackModelContentRate}>
|
||||
<div style={{ color: 'white', fontSize: '14px', marginBottom: '4px' }}>评分:</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Rate value={feedBackForm.rateValue} allowHalf style={{ transform: 'scale(2)' }} allowClear onChange={(e) => {
|
||||
setFeedBackForm({
|
||||
...feedBackForm,
|
||||
rateValue: e
|
||||
})
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.feedBackModelContentList}>
|
||||
<div style={{ color: 'white', fontSize: '14px', marginBottom: '4px' }}>建议:</div>
|
||||
<div>
|
||||
{
|
||||
feedBackList.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className={item.active ? styles.active : ''} onClick={() => {
|
||||
const feedBackListTemp = [...feedBackList]
|
||||
feedBackListTemp[index].active = !feedBackListTemp[index].active
|
||||
setFeedBackList(feedBackListTemp)
|
||||
}}>
|
||||
<span>{item.text}</span>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.feedBackModelContentList} style={{ visibility: feedBackList[feedBackList.length - 1].active ? 'visible' : 'hidden' }}>
|
||||
<TextArea
|
||||
placeholder="填写意见"
|
||||
value={feedBackForm.otherContent}
|
||||
autoSize={{ minRows: 3, maxRows: 6 }}
|
||||
onChange={(e) => {
|
||||
setFeedBackForm({
|
||||
...feedBackForm,
|
||||
otherContent: e.target.value
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.feedBackModelFooter}>
|
||||
<Button type="primary" className='m-ant-btn'
|
||||
onClick={() => {
|
||||
let parmes = {
|
||||
score: feedBackForm.rateValue,
|
||||
otherContent: feedBackList[feedBackList.length - 1].active ? feedBackForm.otherContent : '',
|
||||
types: feedBackList.filter(row => row.active).map((item: any) => item.value),
|
||||
}
|
||||
if (feedBackForm.rateValue === 0) {
|
||||
message.error('请选择评分')
|
||||
return
|
||||
}
|
||||
PostFeedback(parmes).then(res => {
|
||||
if (res.code === 200) {
|
||||
message.success('提交成功!')
|
||||
setIsFeedBackModel(false)
|
||||
}
|
||||
})
|
||||
}}>提交</Button>
|
||||
<Button type="primary" style={{ backgroundColor: 'rgb(16,20,24)', marginLeft: '20px' }}
|
||||
onClick={() => setIsFeedBackModel(false)}>关闭</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export default memo(FeedBackModel)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import styles from '@/components/InvitingPersonnelModal/index.module.scss'
|
||||
import { Button, Checkbox, Input, Modal, Pagination, Radio, message } from 'antd';
|
||||
import { useState, useImperativeHandle, forwardRef, useEffect } from "react";
|
||||
import { useState, useImperativeHandle, forwardRef, useEffect, memo } from "react";
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import { GetUserList } from '@/api/Home/User';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
|
@ -197,4 +197,4 @@ const InvitingPersonnelModal = forwardRef((props: any, ref: any) => {
|
|||
})
|
||||
|
||||
|
||||
export default InvitingPersonnelModal
|
||||
export default memo(InvitingPersonnelModal)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import styles from '@/components/JoinMeetingModal/index.module.scss'
|
||||
import ImageUrl from '@/utils/package/imageUrl';
|
||||
import { Modal, message } from 'antd';
|
||||
import { useState, useImperativeHandle, forwardRef, useRef } from "react";
|
||||
import { useState, useImperativeHandle, forwardRef, useRef, memo } from "react";
|
||||
import Avatar from '@/components/Avatar';
|
||||
import JoinSetting from '../JoinSetting';
|
||||
const JoinMeetingModal = forwardRef((props: any, ref: any) => {
|
||||
|
|
@ -55,4 +55,4 @@ const JoinMeetingModal = forwardRef((props: any, ref: any) => {
|
|||
)
|
||||
})
|
||||
|
||||
export default JoinMeetingModal
|
||||
export default memo(JoinMeetingModal)
|
||||
|
|
@ -3,12 +3,13 @@ import { storage } from '@/utils';
|
|||
import ImageUrl from '@/utils/package/imageUrl';
|
||||
import { GetCheckoutRoomNum, GetRoomRtcToken, GetRoomInfo } from '@/api/Home/Index';
|
||||
import { Button, Modal, message } from 'antd';
|
||||
import { useState, useImperativeHandle, forwardRef } from "react";
|
||||
import { useState, useImperativeHandle, forwardRef, memo } from "react";
|
||||
import { PostRefresh } from '@/api/Login';
|
||||
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) => {
|
||||
|
|
@ -170,7 +171,7 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
|
|||
setJoinRoomSettingForm(list)
|
||||
if (index === 1) {
|
||||
if (list[index].active) {
|
||||
agora.startPreview('videoPreview', Number(user.screenShareId))
|
||||
agora.startPreview('videoPreview', Number(user.screenShareId), +new Date())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -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,
|
||||
|
|
@ -236,4 +244,4 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
|
|||
)
|
||||
})
|
||||
|
||||
export default JoinSetting
|
||||
export default memo(JoinSetting)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import styles from '@/components/MeetingDisconnected/index.module.scss'
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { useState, useImperativeHandle, forwardRef } from "react";
|
||||
import { useState, useImperativeHandle, forwardRef, memo } from "react";
|
||||
const MeetingDisconnected = forwardRef((props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
changeModal: (bool: boolean) => {
|
||||
|
|
@ -17,4 +17,4 @@ const MeetingDisconnected = forwardRef((props: any, ref: any) => {
|
|||
)
|
||||
})
|
||||
|
||||
export default MeetingDisconnected
|
||||
export default memo(MeetingDisconnected)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import styles from '@/components/Operation/index.module.scss'
|
||||
import ImageUrl from '@/utils/package/imageUrl';
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, memo } from "react";
|
||||
type OperationKeyType = 'minimize' | 'quit' | 'maximize' | 'unmaximize' | 'hide' | 'show';
|
||||
type OperationType = {
|
||||
icon: string;
|
||||
|
|
@ -100,4 +100,4 @@ const Operation: React.FC = () => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
export default Operation
|
||||
export default memo(Operation)
|
||||
|
|
@ -2,7 +2,7 @@ import styles from '@/components/QuitTips/index.module.scss'
|
|||
import { storage } from '@/utils';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { Button, Checkbox, Modal, Radio } from 'antd';
|
||||
import { useState, useImperativeHandle, forwardRef } from "react";
|
||||
import { useState, useImperativeHandle, forwardRef, memo } from "react";
|
||||
type OperationKeyType = 'minimize' | 'quit' | 'maximize' | 'unmaximize' | 'hide' | 'show';
|
||||
const QuitTips = forwardRef((props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
|
|
@ -66,4 +66,4 @@ const QuitTips = forwardRef((props: any, ref: any) => {
|
|||
)
|
||||
})
|
||||
|
||||
export default QuitTips
|
||||
export default memo(QuitTips)
|
||||
|
|
@ -8,7 +8,7 @@ import {
|
|||
VerticalAlignBottomOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Input, message, Modal, Pagination, Popconfirm, Progress, Table } from 'antd';
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState, useRef } from "react";
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState, useRef, memo } from "react";
|
||||
import { DeleteRoomFile, GetRoomFile, GetRoomFileDwUrl, GetRoomUpFileurl, GetRoomUserItem, PostRoomFile } from '@/api/Meeting';
|
||||
import axios from 'axios';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
|
@ -393,4 +393,4 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
|||
)
|
||||
})
|
||||
|
||||
export default SharedFilesModel
|
||||
export default memo(SharedFilesModel)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { GetRoomSingnIn, PostRoomSingnIn } from '@/api/Meeting';
|
|||
import styles from '@/components/SingIn/index.module.scss'
|
||||
import { storage } from '@/utils';
|
||||
import { Button, message, Modal } from 'antd';
|
||||
import { useState, useImperativeHandle, forwardRef } from "react";
|
||||
import { useState, useImperativeHandle, forwardRef, memo } from "react";
|
||||
const SingIn = forwardRef((props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
changeModal: () => {
|
||||
|
|
@ -85,4 +85,4 @@ const SingIn = forwardRef((props: any, ref: any) => {
|
|||
)
|
||||
})
|
||||
|
||||
export default SingIn
|
||||
export default memo(SingIn)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import styles from '@/components/SpeakerModeModal/index.module.scss'
|
||||
import { Checkbox, Modal } from 'antd';
|
||||
import { useState, useImperativeHandle, forwardRef } from "react";
|
||||
import { useState, useImperativeHandle, forwardRef, memo } from "react";
|
||||
import { storage } from '@/utils';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { GetSyncView } from '@/api/Meeting';
|
||||
|
|
@ -147,4 +147,4 @@ const FourScreenMode: React.FC<Props> = ({ onClick, meetingMode }) => {
|
|||
)
|
||||
}
|
||||
|
||||
export default SpeakerModeModal
|
||||
export default memo(SpeakerModeModal)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import styles from '@/components/StupWizard/index.module.scss'
|
||||
import ImageUrl from '@/utils/package/imageUrl';
|
||||
import { Button, Checkbox, Empty, Input, message, Modal, Popover, Radio, Select, Slider, Space } from 'antd';
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState, memo } from "react";
|
||||
import { agora } from '@/utils/package/agora'
|
||||
import { CloseOutlined, LoadingOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { storage } from '@/utils';
|
||||
|
|
@ -11,6 +11,7 @@ import { storageSeeting } from '@/utils/package/public';
|
|||
let meetingUserInfo = '' as any;
|
||||
const fs = require('fs').promises;
|
||||
const { exec } = require('child_process');
|
||||
let c = +new Date();
|
||||
const StupWizard = forwardRef((_props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
changeModal: (index: number = 0, data: any) => {
|
||||
|
|
@ -33,6 +34,9 @@ const StupWizard = forwardRef((_props: any, ref: any) => {
|
|||
}
|
||||
}
|
||||
storage.setItem('setting', JSON.stringify(setting))
|
||||
},
|
||||
getStupWizardModal: () => {
|
||||
return isStupWizard
|
||||
}
|
||||
}))
|
||||
const [list, setList] = useState([
|
||||
|
|
@ -84,6 +88,8 @@ const StupWizard = forwardRef((_props: any, ref: any) => {
|
|||
{list.map((row: any, index: number) => {
|
||||
return (
|
||||
<div key={index} className={`${row.active ? styles.active : ''}`} onClick={async () => {
|
||||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
await agora.destroyRendererByConfigPreview(Number(userInfo.screenShareId), c)
|
||||
const newList = [...list];
|
||||
newList.forEach(item => item.active = false);
|
||||
newList[index].active = true;
|
||||
|
|
@ -106,9 +112,11 @@ const StupWizard = forwardRef((_props: any, ref: any) => {
|
|||
cursor: 'pointer'
|
||||
}}
|
||||
onClick={async () => {
|
||||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
if (location.hash.indexOf('/meeting') === -1) {
|
||||
agora.release()
|
||||
}
|
||||
await agora.destroyRendererByConfigPreview(Number(userInfo.screenShareId), c)
|
||||
setIsStupWizard(false)
|
||||
}}
|
||||
/>
|
||||
|
|
@ -125,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 (
|
||||
<>
|
||||
|
|
@ -146,6 +156,33 @@ const CurrencyComponents = () => {
|
|||
<Radio value={'hide'}>不退出程序,最小化到托盘</Radio>
|
||||
</Radio.Group>
|
||||
</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>
|
||||
|
|
@ -208,7 +245,8 @@ const VideoComponents = () => {
|
|||
}, [darkLightEnhancement]);
|
||||
useEffect(() => {
|
||||
if (typeof virtualBackground.sourceIndex === 'number') {
|
||||
if (import.meta.env.VITE_ENV === 'development') {
|
||||
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, {
|
||||
|
|
@ -225,6 +263,7 @@ const VideoComponents = () => {
|
|||
color: Number(virtualBackground.color),
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
agora.enableVirtualBackground(virtualBackground.isVirtualBackground, {
|
||||
background_source_type: 1,
|
||||
|
|
@ -247,7 +286,7 @@ const VideoComponents = () => {
|
|||
})
|
||||
if (setting.videoDeviceId && list.length) {
|
||||
await agora.setVideoDeviceManager(setting.videoDeviceId)
|
||||
await agora.startPreview('videoPreview', Number(userInfo.screenShareId))
|
||||
await agora.startPreview('videoPreview', Number(userInfo.screenShareId), c)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -305,7 +344,7 @@ const VideoComponents = () => {
|
|||
agora.setVideoDeviceManager(e)
|
||||
if (!setting.videoDeviceId) {
|
||||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
await agora.startPreview('videoPreview', Number(userInfo.screenShareId))
|
||||
await agora.startPreview('videoPreview', Number(userInfo.screenShareId), c)
|
||||
}
|
||||
setting.videoDeviceId = e;
|
||||
storage.setItem('setting', JSON.stringify(setting))
|
||||
|
|
@ -691,7 +730,18 @@ const AudioComponents = () => {
|
|||
ecordingVolume: e,
|
||||
})
|
||||
}} disabled={!audioDeviceManager.ecordingItem} />
|
||||
{/* || audioDeviceManager.autoEcordingVolume */}
|
||||
</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>
|
||||
<Checkbox checked={audioDeviceManager.isAINoiseReduction} onChange={(e) => {
|
||||
|
|
@ -724,16 +774,6 @@ const AudioComponents = () => {
|
|||
</Radio.Group>
|
||||
</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 className={styles.audioComponentsSelect}>
|
||||
|
|
@ -964,4 +1004,4 @@ const FileComponents = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export default StupWizard
|
||||
export default memo(StupWizard)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect } from "react";
|
||||
import { useEffect, memo } from "react";
|
||||
import '@/components/TldrawView/index.scss'
|
||||
import {
|
||||
Tldraw,
|
||||
|
|
@ -44,4 +44,4 @@ const TldrawView: React.FC = () => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
export default TldrawView
|
||||
export default memo(TldrawView)
|
||||
|
|
@ -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 } 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) => {
|
||||
if (JSON.stringify(data).includes('Error') || JSON.stringify(data).includes('{}')) {
|
||||
setIsError(res => {
|
||||
if (res) {
|
||||
setIsError(false)
|
||||
confirm({
|
||||
keyboard: false,
|
||||
title: '提示',
|
||||
icon: <ExclamationCircleFilled />,
|
||||
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)
|
||||
getContent()
|
||||
if (dataJson.type === '0') { // 打开弹窗
|
||||
setIsUpdateModal(true)
|
||||
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()}`) // 配置服务器地址
|
||||
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 => {
|
||||
.then(_textContent => {
|
||||
})
|
||||
.catch(error => {
|
||||
.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}
|
||||
>
|
||||
<div className={styles.isUpdateModal} style={{ backgroundImage: `url(${ImageUrl.icon7})` }}>
|
||||
<div className={styles.remarks} dangerouslySetInnerHTML={{ __html: updateContent }}>
|
||||
|
|
@ -67,7 +128,7 @@ const UpdateModal = forwardRef((props: any, ref: any) => {
|
|||
style={{ width: '100%', height: '40px', marginBottom: '10px' }}
|
||||
className={`m-ant-btn`}
|
||||
>立即更新</Button>
|
||||
{import.meta.env.VITE_ENV === "development" ? <div className={styles.button2} onClick={() => setIsUpdateModal(false)}>暂不更新</div> : null}
|
||||
{env === "development" ? <div className={styles.button2} onClick={() => setIsUpdateModal(false)}>暂不更新</div> : null}
|
||||
</div> : progress < 100 ?
|
||||
<div style={{ margin: '20px 0' }}>
|
||||
下载进度:{progress}%
|
||||
|
|
@ -87,4 +148,4 @@ const UpdateModal = forwardRef((props: any, ref: any) => {
|
|||
)
|
||||
})
|
||||
|
||||
export default UpdateModal
|
||||
export default memo(UpdateModal)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { PutAlterUname } from '@/api/Meeting';
|
|||
import styles from '@/components/UserName/index.module.scss'
|
||||
import { storage } from '@/utils';
|
||||
import { Button, Input, message, Modal } from 'antd';
|
||||
import { useState, useImperativeHandle, forwardRef } from "react";
|
||||
import { useState, useImperativeHandle, forwardRef, memo } from "react";
|
||||
const UserName = forwardRef((props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
changeModal: (data: any) => {
|
||||
|
|
@ -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,
|
||||
|
|
@ -71,4 +75,4 @@ const UserName = forwardRef((props: any, ref: any) => {
|
|||
)
|
||||
})
|
||||
|
||||
export default UserName
|
||||
export default memo(UserName)
|
||||
|
|
@ -3,7 +3,7 @@ import styles from '@/components/UserVideo/index.module.scss'
|
|||
import { GetPolling } from '@/api/Meeting';
|
||||
import { agora } from '@/utils/package/agora';
|
||||
import { Button, Empty, Select, message } from 'antd';
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, memo } from "react";
|
||||
import { useLocation } from 'react-router';
|
||||
import { VideoStreamType } from 'agora-electron-sdk';
|
||||
const { setInterval, clearInterval } = require('timers');
|
||||
|
|
@ -154,4 +154,4 @@ const UserVideo: React.FC = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export default UserVideo
|
||||
export default memo(UserVideo)
|
||||
|
|
|
|||
|
|
@ -121,14 +121,6 @@
|
|||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
>div:nth-child(2) {
|
||||
cursor: pointer;
|
||||
|
||||
>img {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
>div:nth-child(2) {
|
||||
|
|
|
|||
|
|
@ -2,17 +2,22 @@ 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';
|
||||
import { storage } from '@/utils';
|
||||
import { PostRefresh } from '@/api/Login';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { role } from '@/config/role';
|
||||
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');
|
||||
|
|
@ -20,6 +25,7 @@ const { RangePicker } = DatePicker;
|
|||
const { confirm } = Modal;
|
||||
const Index: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { state } = useLocation();
|
||||
const [list, setList] = useState({
|
||||
data: [],
|
||||
total: 0,
|
||||
|
|
@ -37,16 +43,19 @@ const Index: React.FC = () => {
|
|||
})
|
||||
const joinSettingRef = useRef<any>();
|
||||
const stupWizardRef = useRef<any>();
|
||||
const feedBackModelRef = useRef<any>();
|
||||
const [user, setUser] = useState<any>({});
|
||||
const [currentRoomInfo, setCurrentRoomInfo] = useState<any>({});
|
||||
const [subjectList, setSubjectList] = useState<any>([]);
|
||||
const [timeData, setTimeData] = useState<any>([]);
|
||||
const [isCreateRoom, setIsCreateRoom] = useState<boolean>(false);
|
||||
const [allowAnonymous, setAllowAnonymous] = useState(true);
|
||||
const [baseImage, setBaseImage] = useState('');
|
||||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
useEffect(() => {
|
||||
setUser(userInfo)
|
||||
if (state?.currentSeconds >= 600) {
|
||||
feedBackModelRef.current.changeModal()
|
||||
}
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
let time = null as any
|
||||
|
|
@ -239,35 +248,7 @@ const Index: React.FC = () => {
|
|||
<span>{item.roomNum}</span>
|
||||
<img src={ImageUrl.icon10} alt="" />
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<Code roomNum={item.roomNum}></Code>
|
||||
</div>
|
||||
<div>
|
||||
{role.ID.includes(userInfo.roleId) ? <Popover
|
||||
|
|
@ -329,13 +310,26 @@ const Index: React.FC = () => {
|
|||
<Button type="primary"
|
||||
iconPosition={'end'}
|
||||
onClick={async () => {
|
||||
storage.setItem('loading', true)
|
||||
isVersion((bool: boolean) => {
|
||||
storage.setItem('loading', false)
|
||||
if (bool) {
|
||||
window.electron.onDownload('3')
|
||||
} else {
|
||||
if (role.ID.includes(userInfo.roleId)) {
|
||||
joinSettingRef.current.changeModal(item.roomNum)
|
||||
} else {
|
||||
storage.setItem('loading', true)
|
||||
postRefresh(() => {
|
||||
getRoomRtcToken(item.roomNum, (options: any) => {
|
||||
getRoomRtcToken(item.roomNum, async (options: any) => {
|
||||
if (options) {
|
||||
await window.electron.getVersion().then(async req => {
|
||||
await PostHomeVerLog({
|
||||
version: req,
|
||||
platformType: 1,
|
||||
roomNum: item.roomNum,
|
||||
})
|
||||
})
|
||||
navigate(`/meeting`, {
|
||||
state: {
|
||||
channelId: item.roomNum,
|
||||
|
|
@ -351,6 +345,8 @@ const Index: React.FC = () => {
|
|||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}}
|
||||
icon={<img src={ImageUrl.icon9} alt="" />}
|
||||
className='m-ant-btn'>
|
||||
|
|
@ -551,6 +547,7 @@ const Index: React.FC = () => {
|
|||
</Modal>
|
||||
<JoinSetting ref={joinSettingRef} />
|
||||
<StupWizard ref={stupWizardRef} />
|
||||
<FeedBackModel ref={feedBackModelRef} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -458,6 +458,9 @@ const User: React.FC = () => {
|
|||
if (!addUserFrom.UserName && isCreateUser !== 'batch') {
|
||||
return message.error('请输入用户名称!')
|
||||
}
|
||||
if (addUserFrom.UserName.length > 10) {
|
||||
return message.error('用户名称最多10个字!')
|
||||
}
|
||||
if (addUserFrom.year === '') {
|
||||
return message.error('请输入届!')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,8 +120,26 @@
|
|||
|
||||
@else if $i ==4 {
|
||||
flex-shrink: 0;
|
||||
|
||||
>div:nth-child(1) {
|
||||
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 {
|
||||
|
|
@ -130,7 +148,7 @@
|
|||
padding-top: 10px;
|
||||
margin-top: 10px;
|
||||
|
||||
@for $i from 1 through 2 {
|
||||
@for $i from 1 through 3 {
|
||||
>div:nth-child(#{$i}) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -144,13 +162,16 @@
|
|||
height: 16px;
|
||||
|
||||
@if $i ==1 {
|
||||
background: url('/src/assets/icon16.png') no-repeat center/cover;
|
||||
background: url('/src/assets/icon56.png') no-repeat center/120%;
|
||||
}
|
||||
|
||||
@else if $i ==2 {
|
||||
background: url('/src/assets/icon15.png') no-repeat center/cover;
|
||||
background: url('/src/assets/icon16.png') no-repeat center/cover;
|
||||
}
|
||||
|
||||
@else if $i ==3 {
|
||||
background: url('/src/assets/icon15.png') no-repeat center/cover;
|
||||
}
|
||||
}
|
||||
|
||||
>span {
|
||||
|
|
@ -162,10 +183,14 @@
|
|||
&:hover {
|
||||
>div {
|
||||
@if $i ==1 {
|
||||
background: url('/src/assets/icon16-active.png') no-repeat center/cover;
|
||||
background: url('/src/assets/icon56-active.png') no-repeat center/120%;
|
||||
}
|
||||
|
||||
@else if $i ==2 {
|
||||
background: url('/src/assets/icon16-active.png') no-repeat center/cover;
|
||||
}
|
||||
|
||||
@else if $i ==3 {
|
||||
background: url('/src/assets/icon15-active.png') no-repeat center/cover;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import styles from '@/page/Home/index.module.scss'
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { Outlet, useNavigate } from 'react-router-dom';
|
||||
import { Popconfirm } from 'antd';
|
||||
import { Button, Popconfirm, Popover } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/zh-cn'
|
||||
import { storage } from '@/utils';
|
||||
|
|
@ -45,6 +45,7 @@ const Home: React.FC = () => {
|
|||
]);
|
||||
const [userInfo, setUserInfo] = useState<any>({})
|
||||
const [version, setVersion] = useState<string>('')
|
||||
const [update, setUpdate] = useState(false)
|
||||
const [dateInfo, setDateInfo] = useState<{
|
||||
work: string;
|
||||
time: string;
|
||||
|
|
@ -67,11 +68,18 @@ const Home: React.FC = () => {
|
|||
})
|
||||
};
|
||||
const timer = setInterval(updateTime, 1000);
|
||||
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
||||
return () => {
|
||||
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
||||
clearInterval(timer);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleCustomStorageChange = (e: any): void => {
|
||||
if (e.key === 'isUpdate') {
|
||||
setUpdate(e.value)
|
||||
}
|
||||
};
|
||||
const changtNavList = (index: number, bool?: boolean): void => {
|
||||
const newNavList = [...navList];
|
||||
if (typeof bool === 'boolean') {
|
||||
|
|
@ -126,10 +134,29 @@ const Home: React.FC = () => {
|
|||
)
|
||||
})}
|
||||
</div>
|
||||
<div className='drag'>
|
||||
<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>
|
||||
<Popover
|
||||
placement="right"
|
||||
content={
|
||||
<img style={{ width: '400px' }} src={`https://meeting-api.23544.com/meeting/update/ddq.png?t=${+new Date()}`} alt="" />
|
||||
}
|
||||
>
|
||||
<div className='drag' title='反馈建议'>
|
||||
<div></div>
|
||||
<span>反馈建议</span>
|
||||
</div>
|
||||
</Popover>
|
||||
<div className='drag' title='设置' onClick={() => {
|
||||
stupWizardRef.current.changeModal()
|
||||
}}>
|
||||
|
|
|
|||
|
|
@ -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<boolean>(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: <ExclamationCircleFilled />,
|
||||
content: `账号已在其他地方登录,是否强制登陆?`,
|
||||
centered: true,
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
async onOk() {
|
||||
setAccountPasswordStatus(true)
|
||||
},
|
||||
onCancel() {
|
||||
|
||||
}
|
||||
})
|
||||
} else {
|
||||
setAccountPasswordStatus(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
message.error('账号不存在!')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -173,7 +204,7 @@ const Login: React.FC = () => {
|
|||
<>
|
||||
<div className={styles.login}>
|
||||
<div className={styles.loginBg}>
|
||||
<img src={import.meta.env.VITE_ENV === 'xy' ? ImageUrl.icon53 : ImageUrl.icon1} alt="" />
|
||||
{env ? <img src={env === 'xy' ? ImageUrl.icon53 : ImageUrl.icon1} alt="" /> : null}
|
||||
</div>
|
||||
<div className={styles.loginContent}>
|
||||
<div>
|
||||
|
|
@ -315,6 +346,15 @@ const Login: React.FC = () => {
|
|||
if (!anonInfo.nickName) {
|
||||
return message.error('请输入参会昵称!')
|
||||
}
|
||||
if (anonInfo.nickName.length > 10) {
|
||||
return message.error('参会昵称最多10个字!')
|
||||
}
|
||||
storage.setItem('loading', true)
|
||||
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) {
|
||||
|
|
@ -357,6 +397,8 @@ const Login: React.FC = () => {
|
|||
}).catch(() => {
|
||||
storage.setItem('loading', false)
|
||||
})
|
||||
}
|
||||
})
|
||||
}}>进入</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -206,19 +206,30 @@ const ChatBigWindow: React.FC = () => {
|
|||
>移出会议</Button> : null}
|
||||
</div> : <div style={{ color: 'white' }}>用户不在房间内</div>
|
||||
}>
|
||||
<div>
|
||||
<div title={item.userName}>
|
||||
<div><Avatar name={item.userName} /></div>
|
||||
{item.uid !== user.uid ?
|
||||
<span>{item.userName} <span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span></span> :
|
||||
<span> <span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')} </span>{item.userName}</span>
|
||||
<span>
|
||||
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '150px' }}>{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>
|
||||
</Popover> : <div>
|
||||
</Popover> : <div title={item.userName}>
|
||||
<div><Avatar name={item.userName} /></div>
|
||||
{item.uid !== user.uid ?
|
||||
<span>{item.userName}<span style={{ fontSize: '12px', color: '#ccc', marginLeft: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')}</span></span> :
|
||||
<span><span style={{ fontSize: '12px', color: '#ccc', marginRight: '4px' }}>{dayjs(item.timestamp).format('HH:mm:ss')} </span>{item.userName}</span>
|
||||
<span>
|
||||
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '150px' }}>{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>{item.message}</div>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
display: flex;
|
||||
|
||||
>span:nth-child(1) {
|
||||
white-space: nowrap;
|
||||
word-break: break-all;
|
||||
color: #ff970f;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,10 @@ const NoticeWindow: React.FC = () => {
|
|||
api.open({
|
||||
message: '',
|
||||
description: <div>
|
||||
<span style={{ fontSize: '16px' }}>{noticeItem.uname}申请发言</span>
|
||||
<div style={{ fontSize: '16px' }}>
|
||||
<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'>
|
||||
<Button
|
||||
type="primary"
|
||||
|
|
@ -68,6 +71,7 @@ const NoticeWindow: React.FC = () => {
|
|||
</div>
|
||||
</div>,
|
||||
onClose: () => {
|
||||
setTimeout(() => {
|
||||
const dom = document.getElementsByClassName('ant-notification')
|
||||
if (dom.length === 0) {
|
||||
window.electron.setChildWindowShow({
|
||||
|
|
@ -75,6 +79,7 @@ const NoticeWindow: React.FC = () => {
|
|||
bool: false
|
||||
})
|
||||
}
|
||||
}, 500);
|
||||
},
|
||||
duration: 10,
|
||||
placement: 'bottomRight',
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import styles from '@/page/Meeting/ShareScreenWindow/index.module.scss'
|
|||
import { storage } from '@/utils';
|
||||
import ImageUrl from '@/utils/package/imageUrl';
|
||||
import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons';
|
||||
import { RtcStats } from 'agora-electron-sdk';
|
||||
import { Button } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { useEffect, useState } from "react";
|
||||
|
|
@ -50,6 +51,12 @@ const ShareScreenWindow: React.FC = () => {
|
|||
const [timeStr, setTimeStr] = useState(0)
|
||||
const [isExpand, setIsExpand] = useState(false)
|
||||
const [roomUserLists, setRoomUserLists] = useState<any>([])
|
||||
const [currentEffective, setCurrentEffective] = useState(3)
|
||||
const [networkOther, setNetworkOther] = useState<RtcStats>({})
|
||||
const [networkQuality, setNetworkQuality] = useState({
|
||||
level: '佳',
|
||||
text: '网络质量极好'
|
||||
})
|
||||
const channel = new BroadcastChannel('meeting_channel');
|
||||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
let timeout: NodeJS.Timeout;
|
||||
|
|
@ -97,6 +104,11 @@ const ShareScreenWindow: React.FC = () => {
|
|||
case 'roomUserList':
|
||||
setRoomUserLists(data.parmes.roomUserList)
|
||||
break;
|
||||
case 'nnetworkStatus':
|
||||
setCurrentEffective(data.parmes.currentEffective)
|
||||
setNetworkQuality(data.parmes.networkQuality)
|
||||
setNetworkOther(data.parmes.networkOther)
|
||||
break;
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
|
|
@ -141,13 +153,19 @@ const ShareScreenWindow: React.FC = () => {
|
|||
<>
|
||||
<div className={styles.shareScreenWindow} style={{ width: isExpand ? '100%' : '100%' }}>
|
||||
<div className={styles.shareScreenWindowTitle}>
|
||||
<span>{changeCurrentSeconds(timeStr)} 共享中</span>
|
||||
{isExpand ? <span className='drag' onClick={() => {
|
||||
<span>{changeCurrentSeconds(timeStr)} {!isExpand ? '共享中' : ''}
|
||||
{networkIcon(currentEffective)}
|
||||
<span style={{ color: 'white', marginLeft: '30px' }}>
|
||||
{!isExpand ? <span style={{ marginRight: '10px' }}>网络质量:{networkQuality.level}</span> : ''}
|
||||
<span>延迟:{networkOther.lastmileDelay}ms</span>
|
||||
</span>
|
||||
</span>
|
||||
{isExpand ? <span className='drag' style={{ flexShrink: 0 }} onClick={() => {
|
||||
channel.postMessage({
|
||||
type: 'shareScreenWindowClose',
|
||||
shareScreenWindowClose: timeStr
|
||||
});
|
||||
}}>结束共享</span> : <span style={{ visibility: 'hidden' }}>结束共享</span>}
|
||||
}}>结束共享</span> : <span style={{ visibility: 'hidden', flexShrink: 0 }}>结束共享</span>}
|
||||
</div>
|
||||
{isExpand ? null : <div className={`${styles.shareScreenWindowContent} drag`}>
|
||||
<div className={styles.shareScreenWindowContentList}>
|
||||
|
|
@ -184,7 +202,7 @@ const ShareScreenWindow: React.FC = () => {
|
|||
}}
|
||||
key={index}>
|
||||
<div>
|
||||
{!item.active ? <div style={{ backgroundImage: `url(${ImageUrl.icon49})` }} id={`micr-item-${userInfo.uid}`}>
|
||||
{!item.active ? <div style={{ backgroundImage: `url(${ImageUrl.icon49})`, transition: '0.18s' }} id={`micr-item-${userInfo.uid}`}>
|
||||
</div> : ''}
|
||||
{item.select ? <img src={item.iconSelect} alt="" /> : <img src={item.active ? item.iconActive : item.icon} alt="" />}
|
||||
</div>
|
||||
|
|
@ -205,7 +223,7 @@ const ShareScreenWindow: React.FC = () => {
|
|||
<div className={`${styles.shareScreenWindowExpand} drag`} onClick={() => {
|
||||
setIsExpand(!isExpand)
|
||||
window.electron.setChildWindow({
|
||||
width: isExpand ? 440 : 440 / 2,
|
||||
width: isExpand ? 440 : 440 / 1.6,
|
||||
key: 'shareScreenWindow',
|
||||
})
|
||||
}}>
|
||||
|
|
@ -217,4 +235,60 @@ const ShareScreenWindow: React.FC = () => {
|
|||
)
|
||||
}
|
||||
|
||||
const networkIcon = (network: number) => {
|
||||
switch (network) {
|
||||
case 0:
|
||||
return <svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg" className='drag' style={{ transform: 'scale(0.5)', position: 'absolute', top: '-4px' }}>
|
||||
<g clip-path="url(#clip0_21262_3609)">
|
||||
<path d="M4.44094 32C3.03695 32 1.90039 31.15 1.90039 30.1V15.3C1.90039 14.25 3.03695 13.4 4.44094 13.4C5.84492 13.4 6.98149 14.25 6.98149 15.3V30.1C6.98149 31.15 5.84492 32 4.44094 32ZM17.01 32C15.606 32 14.4694 31.15 14.4694 30.1V8.9C14.4694 7.85 15.606 7 17.01 7C18.4139 7 19.5505 7.85 19.5505 8.9V30.1C19.5505 31.15 18.4139 32 17.01 32ZM29.579 32C28.175 32 27.0384 31.15 27.0384 30.1V1.9C27.0384 0.85 28.175 0 29.579 0C30.983 0 32.1195 0.85 32.1195 1.9V30.1C32.1195 31.15 30.983 32 29.579 32Z" fill="#7C8280" />
|
||||
<path d="M7.00124 2.11509L5.01758 4.09875L3.03391 2.11509C2.77629 1.85747 2.35122 1.85747 2.0936 2.11509C1.83599 2.37271 1.83599 2.79778 2.0936 3.0554L4.07727 5.03906L2.0936 7.02273C1.83599 7.28035 1.83599 7.70542 2.0936 7.96304C2.35122 8.22065 2.77629 8.22065 3.03391 7.96304L5.01758 5.97937L7.00124 7.96304C7.25886 8.22065 7.68393 8.22065 7.94155 7.96304C8.19917 7.70542 8.19917 7.28035 7.94155 7.02273L5.95789 5.03906L7.94155 3.0554C8.19917 2.79778 8.19917 2.37271 7.94155 2.11509C7.68393 1.85747 7.25886 1.85747 7.00124 2.11509Z" fill="#F90000" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_21262_3609">
|
||||
<rect width="32" height="32" fill="white" transform="translate(0.119141)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
case 1:
|
||||
return <svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg" className='drag' style={{ transform: 'scale(0.5)', position: 'absolute', top: '-4px' }}>
|
||||
<g clip-path="url(#clip0_21262_3615)">
|
||||
<path d="M26.9316 30.1C26.9316 31.15 28.0918 32 29.5249 32C30.9581 32 32.1182 31.15 32.1182 30.1V1.9C32.1182 0.85 30.9581 0 29.5249 0C28.0918 0 26.9316 0.85 26.9316 1.9V30.1Z" fill="#7C8280" />
|
||||
<path d="M14.1035 30.1C14.1035 31.15 15.2637 32 16.6968 32C18.1299 32 19.2901 31.15 19.2901 30.1V8.9C19.2901 7.85 18.1299 7 16.6968 7C15.2637 7 14.1035 7.85 14.1035 8.9V30.1Z" fill="#7C8280" />
|
||||
<path d="M1.27344 30.1004C1.27344 31.1504 2.4336 32.0004 3.86673 32.0004C5.29986 32.0004 6.46002 31.1504 6.46002 30.1004V15.3004C6.46002 14.2504 5.29986 13.4004 3.86673 13.4004C2.4336 13.4004 1.27344 14.2504 1.27344 15.3004V30.1004Z" fill="#FF800B" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_21262_3615">
|
||||
<rect width="32" height="32" fill="white" transform="translate(0.119141)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
case 2:
|
||||
return <svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg" className='drag' style={{ transform: 'scale(0.5)', position: 'absolute', top: '-4px' }}>
|
||||
<g clip-path="url(#clip0_21262_3621)">
|
||||
<path d="M26.9316 30.1C26.9316 31.15 28.0918 32 29.5249 32C30.9581 32 32.1182 31.15 32.1182 30.1V1.9C32.1182 0.85 30.9581 0 29.5249 0C28.0918 0 26.9316 0.85 26.9316 1.9V30.1Z" fill="#7C8280" />
|
||||
<path d="M14.1035 30.1C14.1035 31.15 15.2637 32 16.6968 32C18.1299 32 19.2901 31.15 19.2901 30.1V8.9C19.2901 7.85 18.1299 7 16.6968 7C15.2637 7 14.1035 7.85 14.1035 8.9V30.1Z" fill="#FF800B" />
|
||||
<path d="M1.27344 30.1004C1.27344 31.1504 2.4336 32.0004 3.86673 32.0004C5.29986 32.0004 6.46002 31.1504 6.46002 30.1004V15.3004C6.46002 14.2504 5.29986 13.4004 3.86673 13.4004C2.4336 13.4004 1.27344 14.2504 1.27344 15.3004V30.1004Z" fill="#FF800B" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_21262_3621">
|
||||
<rect width="32" height="32" fill="white" transform="translate(0.119141)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
case 3:
|
||||
return <svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg" className='drag' style={{ transform: 'scale(0.5)', position: 'absolute', top: '-4px' }}>
|
||||
<g clip-path="url(#clip0_21262_3625)">
|
||||
<path d="M26.9316 30.1C26.9316 31.15 28.0918 32 29.5249 32C30.9581 32 32.1182 31.15 32.1182 30.1V1.9C32.1182 0.85 30.9581 0 29.5249 0C28.0918 0 26.9316 0.85 26.9316 1.9V30.1Z" fill="#02B188" />
|
||||
<path d="M14.1035 30.1C14.1035 31.15 15.2637 32 16.6968 32C18.1299 32 19.2901 31.15 19.2901 30.1V8.9C19.2901 7.85 18.1299 7 16.6968 7C15.2637 7 14.1035 7.85 14.1035 8.9V30.1Z" fill="#02B188" />
|
||||
<path d="M1.27344 30.1004C1.27344 31.1504 2.4336 32.0004 3.86673 32.0004C5.29986 32.0004 6.46002 31.1504 6.46002 30.1004V15.3004C6.46002 14.2504 5.29986 13.4004 3.86673 13.4004C2.4336 13.4004 1.27344 14.2504 1.27344 15.3004V30.1004Z" fill="#02B188" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_21262_3625">
|
||||
<rect width="32" height="32" fill="white" transform="translate(0.119141)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
export default ShareScreenWindow
|
||||
|
|
|
|||
|
|
@ -232,6 +232,11 @@
|
|||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
#videoView {
|
||||
position: relative;
|
||||
border: 1px red solid;
|
||||
}
|
||||
|
||||
.standardModeIcon {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
|
|
@ -298,13 +303,17 @@
|
|||
|
||||
// 演讲者模式
|
||||
.meetingContentBodyLeftSpeakerMode {
|
||||
width: 18%;
|
||||
width: 270px;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
|
||||
.meetingContentSwiperCard {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.meetingContentSwiperCard {
|
||||
height: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
// 单画面模式
|
||||
|
|
@ -370,7 +379,7 @@
|
|||
right: 0;
|
||||
bottom: 0;
|
||||
height: 100% !important;
|
||||
width: calc(100% - 18%) !important;
|
||||
width: calc(100% - 270px) !important;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
|
|
@ -381,7 +390,7 @@
|
|||
|
||||
.meetingContentSwiperCard {
|
||||
height: 160px;
|
||||
width: calc(100% / 6);
|
||||
width: 270px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
|
@ -427,6 +436,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
.meetingContentSwiperCaret {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
color: white;
|
||||
border: 1px white solid;
|
||||
font-size: 20px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.meetingContentBodyLeftBlock {
|
||||
position: absolute;
|
||||
background-color: #1F2022;
|
||||
|
|
@ -524,6 +549,8 @@
|
|||
font-size: 14px;
|
||||
color: #F3F3F5;
|
||||
margin-left: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
>div {
|
||||
|
|
@ -635,6 +662,8 @@
|
|||
font-size: 14px;
|
||||
color: #F3F3F5;
|
||||
margin-left: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -664,6 +693,8 @@
|
|||
>span {
|
||||
font-size: 14px;
|
||||
color: #F3F3F5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
>div {
|
||||
|
|
@ -773,7 +804,7 @@
|
|||
}
|
||||
|
||||
>img {
|
||||
height: 50px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
>span {
|
||||
|
|
@ -795,8 +826,8 @@
|
|||
}
|
||||
|
||||
>label {
|
||||
height: 50px;
|
||||
height: 50px;
|
||||
height: 35px;
|
||||
height: 35px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ export interface IElectronAPI {
|
|||
downFile: (callBack: Function) => void;
|
||||
quitAndInstall: (callBack: Function) => void;
|
||||
isOpenWindows: (callBack: Function) => void;
|
||||
setEnv: (str: string) => any;
|
||||
startLoad: () => any;
|
||||
updateHandle: () => any;
|
||||
getVersion: () => Promise<string>;
|
||||
getEnv: () => Promise<string>;
|
||||
isVisible: () => Promise<string>;
|
||||
setRegistry: (uuid: string) => any;
|
||||
getRegistry: () => any;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
declare module 'react-dom/client';
|
||||
declare module 'crypto-js';
|
||||
declare module 'js-yaml';
|
||||
declare module 'uuid';
|
||||
|
|
@ -9,7 +9,6 @@ import {
|
|||
AudioAinsMode,
|
||||
SimulcastStreamMode,
|
||||
VideoStreamType,
|
||||
QualityType,
|
||||
RtcConnection,
|
||||
RtcStats,
|
||||
AudioVolumeInfo,
|
||||
|
|
@ -22,7 +21,8 @@ import {
|
|||
ColorEnhanceOptions,
|
||||
LowlightEnhanceOptions,
|
||||
VirtualBackgroundSource,
|
||||
AudienceLatencyLevelType
|
||||
AudienceLatencyLevelType,
|
||||
StreamPublishState
|
||||
} from "agora-electron-sdk";
|
||||
import { GetRoomRtcToken, GetAgoraConf } from "@/api/Home/Index";
|
||||
import { storage } from '@/utils';
|
||||
|
|
@ -32,7 +32,6 @@ const os = require("os");
|
|||
const option: any = {
|
||||
appId: '',
|
||||
token: '',
|
||||
tokenA: '',
|
||||
channelId: '',
|
||||
uid: '',
|
||||
screenShareId: '',
|
||||
|
|
@ -126,7 +125,8 @@ export const agora = {
|
|||
if (settingData.darkLightEnhancement) agora.setLowlightEnhanceOptions(settingData.darkLightEnhancement.isDarkLightEnhancement, settingData.darkLightEnhancement)
|
||||
if (settingData.virtualBackground) {
|
||||
if (typeof settingData.virtualBackground.sourceIndex === 'number') {
|
||||
if (import.meta.env.VITE_ENV === 'development') {
|
||||
window.electron.getEnv().then(res => {
|
||||
if (res === 'development') {
|
||||
window.electron.getAppPath().then((res: string) => {
|
||||
const imagePath = path.join(res, 'src', 'assets', 'virtualBackground', `${settingData.virtualBackground.sourceIndex + 1}.png`);
|
||||
agora.enableVirtualBackground(settingData.virtualBackground.isVirtualBackground, {
|
||||
|
|
@ -143,6 +143,7 @@ export const agora = {
|
|||
color: Number(settingData.virtualBackground.color),
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
agora.enableVirtualBackground(settingData.virtualBackground.isVirtualBackground, {
|
||||
background_source_type: 1,
|
||||
|
|
@ -153,7 +154,7 @@ export const agora = {
|
|||
}, 1000);
|
||||
},
|
||||
// 事件回调
|
||||
registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline, onAudioVolumeIndication, onNetworkQuality, onRtcStats, onConnectionStateChanged, onLocalVideoStateChanged, onConnectionLost }: any) => {
|
||||
registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline, onAudioVolumeIndication, onRtcStats, onConnectionStateChanged, onLocalVideoStateChanged, onConnectionLost, onTokenPrivilegeWillExpire, onActiveSpeaker, onVideoPublishStateChanged, onAudioPublishStateChanged }: any) => {
|
||||
rtcEngine.registerEventHandler({
|
||||
// 监听本地用户加入频道事件
|
||||
onJoinChannelSuccess: async (connection: RtcConnection, elapsed: number) => {
|
||||
|
|
@ -168,25 +169,17 @@ export const agora = {
|
|||
await onUserOffline?.(connection, remoteUid, reason)
|
||||
},
|
||||
// // 视频发布状态改变回调
|
||||
// onVideoPublishStateChanged: (source: any, channel: any, oldState: any, newState: any, elapseSinceLastState: any) => {
|
||||
// if (newState === 1) {
|
||||
|
||||
// }
|
||||
// },
|
||||
onVideoPublishStateChanged: (source: VideoSourceType, channel: string, oldState: StreamPublishState, newState: StreamPublishState, elapseSinceLastState: number) => {
|
||||
onVideoPublishStateChanged?.(source, channel, oldState, newState, elapseSinceLastState)
|
||||
},
|
||||
// // 音频发布状态改变回调
|
||||
// onAudioPublishStateChanged: (channel: any, oldState: any, newState: any, elapseSinceLastState: any) => {
|
||||
// if (newState === 1) {
|
||||
|
||||
// }
|
||||
// },
|
||||
onAudioPublishStateChanged: (channel: string, oldState: StreamPublishState, newState: StreamPublishState, elapseSinceLastState: number) => {
|
||||
onAudioPublishStateChanged?.(channel, oldState, newState, elapseSinceLastState)
|
||||
},
|
||||
// // 用户音量提示回调。
|
||||
onAudioVolumeIndication: async (_connection: RtcConnection, speakers: AudioVolumeInfo[], _speakerNumber: number, _totalVolume: number) => {
|
||||
await onAudioVolumeIndication?.(speakers)
|
||||
},
|
||||
//通话中每个用户的网络上下行 last mile 质量报告回调。
|
||||
onNetworkQuality: async (connection: RtcConnection, remoteUid: number, txQuality: QualityType, rxQuality: QualityType) => {
|
||||
await onNetworkQuality?.(connection, remoteUid, txQuality, rxQuality)
|
||||
},
|
||||
//当前通话相关的统计信息回调。
|
||||
onRtcStats: async (_connection: RtcConnection, stats: RtcStats) => {
|
||||
await onRtcStats?.(stats)
|
||||
|
|
@ -202,9 +195,26 @@ export const agora = {
|
|||
// 网络连接中断,且 SDK 无法在 10 秒内连接服务器回调。
|
||||
onConnectionLost: (_connection: RtcConnection) => {
|
||||
onConnectionLost?.()
|
||||
},
|
||||
// Token 即将在 30s 内过期回调。
|
||||
onTokenPrivilegeWillExpire: (connection: RtcConnection, token: string) => {
|
||||
onTokenPrivilegeWillExpire?.(connection, token)
|
||||
},
|
||||
// 监测到远端最活跃用户回调。
|
||||
onActiveSpeaker: (connection: RtcConnection, uid: number) => {
|
||||
const setting = JSON.parse(storage.getItem('setting') as string);
|
||||
if (setting.voiceStimulation) {
|
||||
onActiveSpeaker?.(connection, uid)
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 刷新token
|
||||
refreshToken: async (data: any) => {
|
||||
await rtcEngine.updateChannelMediaOptionsEx({
|
||||
token: data.token,
|
||||
}, data.connection);
|
||||
},
|
||||
// 获取视图模式
|
||||
getRrenderMode: (uid: number) => {
|
||||
if (String(uid).length === 9) {
|
||||
|
|
@ -220,7 +230,7 @@ export const agora = {
|
|||
return
|
||||
}
|
||||
await rtcEngine.setupLocalVideo({
|
||||
renderMode: agora.getRrenderMode(item.uid),
|
||||
renderMode: item.renderMode || agora.getRrenderMode(item.uid),
|
||||
sourceType: item.sourceType,
|
||||
uid: item.uid,
|
||||
view: item.view,
|
||||
|
|
@ -233,7 +243,7 @@ export const agora = {
|
|||
if (item.view?.childNodes.length === 1) {
|
||||
await rtcEngine.setupRemoteVideo(
|
||||
{
|
||||
renderMode: agora.getRrenderMode(item.uid),
|
||||
renderMode: item.renderMode || agora.getRrenderMode(item.uid),
|
||||
sourceType: VideoSourceType.VideoSourceRemote,
|
||||
uid: item.uid,
|
||||
view: item.view,
|
||||
|
|
@ -247,7 +257,7 @@ export const agora = {
|
|||
if (item.view?.childNodes.length === 1) {
|
||||
await rtcEngine.setupRemoteVideoEx(
|
||||
{
|
||||
renderMode: agora.getRrenderMode(item.uid),
|
||||
renderMode: item.renderMode || agora.getRrenderMode(item.uid),
|
||||
sourceType: VideoSourceType.VideoSourceRemote,
|
||||
uid: item.uid,
|
||||
view: item.view,
|
||||
|
|
@ -261,7 +271,7 @@ export const agora = {
|
|||
setupRemoteVideo: async (item: any) => {
|
||||
await rtcEngine.setupRemoteVideo(
|
||||
{
|
||||
renderMode: agora.getRrenderMode(item.uid),
|
||||
renderMode: item.renderMode || agora.getRrenderMode(item.uid),
|
||||
sourceType: VideoSourceType.VideoSourceRemote,
|
||||
uid: item.uid,
|
||||
view: item.view,
|
||||
|
|
@ -285,7 +295,7 @@ export const agora = {
|
|||
},
|
||||
// 加入频道
|
||||
joinChannel: async () => {
|
||||
await rtcEngine.enableAudioVolumeIndication(100, 3, true)
|
||||
await rtcEngine.enableAudioVolumeIndication(200, 3, true)
|
||||
await rtcEngine.joinChannel(option.token, option.channelId, option.uid);
|
||||
await rtcEngine.setDualStreamModeEx(
|
||||
SimulcastStreamMode.EnableSimulcastStream,
|
||||
|
|
@ -298,6 +308,7 @@ export const agora = {
|
|||
},
|
||||
{ channelId: option.channelId, localUid: Number(option.uid) }
|
||||
);
|
||||
await rtcEngine.setAudioScenario(8)
|
||||
},
|
||||
// 更新频道配置
|
||||
updateChannelMediaOptions: async (bool: boolean) => {
|
||||
|
|
@ -320,10 +331,10 @@ export const agora = {
|
|||
)
|
||||
},
|
||||
// 共享屏幕单独用户
|
||||
joinChannelEx: async (uid: any) => {
|
||||
joinChannelEx: async (uid: any, token: string) => {
|
||||
await agora.leaveChannelEx(uid)
|
||||
await rtcEngine.joinChannelEx(
|
||||
option.token,
|
||||
token,
|
||||
{ channelId: option.channelId, localUid: Number(uid) },
|
||||
{
|
||||
autoSubscribeAudio: false,//设置是否自动订阅所有音频流
|
||||
|
|
@ -336,11 +347,11 @@ export const agora = {
|
|||
);
|
||||
},
|
||||
// 所有用户加入的第二个房间
|
||||
allJoinChannelEx: async (bool: boolean = false) => {
|
||||
allJoinChannelEx: async (bool: boolean = false, token: string) => {
|
||||
const user = await JSON.parse(storage.getItem('user') as string)
|
||||
await agora.startCameraCapture(true)
|
||||
await rtcEngine.joinChannelEx(
|
||||
option.tokenA,
|
||||
token,
|
||||
{ channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) },
|
||||
{
|
||||
clientRoleType: bool ? ClientRoleType.ClientRoleAudience : ClientRoleType.ClientRoleBroadcaster, //用户角色 ClientRoleBroadcaster 主播 ClientRoleAudience 观众
|
||||
|
|
@ -382,10 +393,14 @@ export const agora = {
|
|||
},
|
||||
// 销毁视频渲染dom
|
||||
destroyRendererByConfig: async (uid: number, channelId?: string) => {
|
||||
await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, channelId, uid);
|
||||
await rtcEngine.destroyRendererByConfig(option.uid === uid ? VideoSourceType.VideoSourceCameraPrimary : VideoSourceType.VideoSourceRemote, channelId, uid);
|
||||
},
|
||||
destroyRendererByView: async () => {
|
||||
let dom = document.getElementById(`meetingAbsoluteVideo`);
|
||||
destroyRendererByConfigPreview: async (uid: number, channelId: number) => {
|
||||
await agora.destroyRendererByView('videoPreview')
|
||||
await rtcEngine.leaveChannelEx({ channelId: `${channelId + uid}`, localUid: Number(uid) })
|
||||
},
|
||||
destroyRendererByView: async (key: string) => {
|
||||
let dom = document.getElementById(key);
|
||||
if (dom) {
|
||||
await rtcEngine.destroyRendererByView(dom);
|
||||
}
|
||||
|
|
@ -433,8 +448,8 @@ export const agora = {
|
|||
startCameraCapture: async (bool: boolean = false) => {
|
||||
await rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, {
|
||||
format: {
|
||||
width: bool ? 160 : 1280,
|
||||
height: bool ? 160 : 720,
|
||||
width: bool ? 160 : 1920,
|
||||
height: bool ? 160 : 1080,
|
||||
fps: 15,
|
||||
}
|
||||
})
|
||||
|
|
@ -446,7 +461,6 @@ export const agora = {
|
|||
// 加入频道
|
||||
setJoinChannel: async (data: any) => {
|
||||
option.token = data.token;
|
||||
option.tokenA = data.tokenA;
|
||||
option.channelId = data.channelId;
|
||||
option.uid = Number(data.uid);
|
||||
option.screenShareId = data.screenShareId;
|
||||
|
|
@ -457,7 +471,7 @@ export const agora = {
|
|||
return await rtcEngine.getScreenCaptureSources(thumbSize, iconSize, includeScreen)
|
||||
},
|
||||
// 共享屏幕采集
|
||||
setDesktopCapturerVideo: async (targetSource: any, isComputerAudio: boolean, isFluencyPriority: boolean) => {
|
||||
setDesktopCapturerVideo: async (targetSource: any, isComputerAudio: boolean, isFluencyPriority: boolean, token: string) => {
|
||||
const user = JSON.parse(storage.getItem('user') as string)
|
||||
agora.stopScreenCapture();
|
||||
if (isComputerAudio) {
|
||||
|
|
@ -465,7 +479,7 @@ export const agora = {
|
|||
}
|
||||
|
||||
let data = {
|
||||
frameRate: isFluencyPriority ? 30 : 15,
|
||||
frameRate: isFluencyPriority ? 15 : 7,
|
||||
dimensions: {
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
|
|
@ -493,7 +507,7 @@ export const agora = {
|
|||
}
|
||||
);
|
||||
}
|
||||
await agora.joinChannelEx(user.screenShareId)
|
||||
await agora.joinChannelEx(user.screenShareId, token)
|
||||
},
|
||||
// 获取系统中所有的视频设备列表。
|
||||
getVideoDeviceManager: async (): Promise<any> => {
|
||||
|
|
@ -507,12 +521,12 @@ export const agora = {
|
|||
await rtcEngine.getVideoDeviceManager().setDevice(deviceIdUTF8)
|
||||
},
|
||||
// 开启本地视频预览
|
||||
startPreview: async (id: string, uid: number): Promise<void> => {
|
||||
startPreview: async (id: string, uid: number, channelId: number): Promise<void> => {
|
||||
rtcEngine.enableVideo();
|
||||
rtcEngine.startPreview();
|
||||
await GetRoomRtcToken(`${+new Date()}`).then(async (res) => {
|
||||
await GetRoomRtcToken(`${channelId + uid}`).then(async (res) => {
|
||||
await rtcEngine.joinChannelEx(res.data, {
|
||||
channelId: `${+new Date() + uid}`,
|
||||
channelId: `${channelId + uid}`,
|
||||
localUid: uid,
|
||||
}, {
|
||||
channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ import icon52Select from '@/assets/icon52-select.png'
|
|||
import icon53 from '@/assets/icon53.png'
|
||||
import icon54 from '@/assets/icon54.png'
|
||||
import icon55 from '@/assets/icon55.png'
|
||||
import icon56 from '@/assets/icon56.png'
|
||||
import icon56Active from '@/assets/icon56-active.png'
|
||||
export default {
|
||||
loading,
|
||||
icon,
|
||||
|
|
@ -170,5 +172,7 @@ export default {
|
|||
icon52Select,
|
||||
icon53,
|
||||
icon54,
|
||||
icon55
|
||||
icon55,
|
||||
icon56,
|
||||
icon56Active
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import path from "path";
|
||||
import storage from "./storage";
|
||||
import axios from "axios";
|
||||
import yaml from 'js-yaml'
|
||||
export const setKeyOpenChildWindow = async (key: string, bool: boolean) => {
|
||||
const openChildWindow = await JSON.parse(storage.getItem('openChildWindow') as string)
|
||||
openChildWindow[key] = bool;
|
||||
|
|
@ -27,6 +29,7 @@ export const storageSeeting: any = {
|
|||
shareFilesPath: path.resolve(__dirname, '../../Downloads/') + '\\', //共享文件保存路径
|
||||
isShareSavePath: true, //是否下载钱询问每个文件保存的位置
|
||||
closeSetting: 'hide', //关闭按钮设置
|
||||
voiceStimulation: true, //语音激励
|
||||
isAINoiseReduction: true, //是否开启ai降噪
|
||||
aINoiseReduction: 1, // 降噪模式
|
||||
isRecordingTips: true, //是否开启录制提示
|
||||
|
|
@ -59,15 +62,61 @@ export const getUpdateUrl = (env: string) => {
|
|||
switch (env) {
|
||||
case 'xy':
|
||||
return 'https://meeting-api.23544.com/meeting/xysz'
|
||||
case 'development':
|
||||
return 'http://192.168.2.9:8827'
|
||||
default:
|
||||
return 'https://meeting-api.23544.com/meeting/update'
|
||||
}
|
||||
}
|
||||
export const getTitle = (env: string) => {
|
||||
export const getTitle = async () => {
|
||||
let env = await window.electron.getEnv()
|
||||
let str;
|
||||
switch (env) {
|
||||
case 'xy':
|
||||
return '湖北襄阳四中教研平台'
|
||||
str = '湖北襄阳四中教研平台'
|
||||
break;
|
||||
case 'development':
|
||||
str = '智汇享'
|
||||
break;
|
||||
default:
|
||||
return '智汇享'
|
||||
str = '智汇享'
|
||||
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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ module.exports = {
|
|||
switch (env) {
|
||||
case 'xy':
|
||||
return 'https://meeting-api.23544.com/meeting/xysz'
|
||||
case 'development':
|
||||
return 'http://192.168.2.9:8827'
|
||||
default:
|
||||
return 'https://meeting-api.23544.com/meeting/update'
|
||||
}
|
||||
|
|
@ -11,6 +13,8 @@ module.exports = {
|
|||
switch (env) {
|
||||
case 'xy':
|
||||
return '湖北襄阳四中教研平台'
|
||||
case 'development':
|
||||
return '智汇享'
|
||||
default:
|
||||
return '智汇享'
|
||||
}
|
||||
|
|
@ -19,6 +23,8 @@ module.exports = {
|
|||
switch (env) {
|
||||
case 'xy':
|
||||
return 'icon54'
|
||||
case 'development':
|
||||
return 'icon'
|
||||
default:
|
||||
return 'icon'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import Request from './request'
|
||||
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({
|
||||
baseURL: import.meta.env.VITE_BASE_URL_API,
|
||||
baseURL,
|
||||
timeout: constant.CONFIG_REQUEST_TIMEOUT_TIME as number,
|
||||
interceptors: {
|
||||
// 请求拦截器
|
||||
|
|
@ -22,5 +22,4 @@ const request = (config: any) => {
|
|||
}
|
||||
return req.request<any>(config)
|
||||
}
|
||||
|
||||
export default request
|
||||
|
|
|
|||
|
|
@ -77,6 +77,9 @@ class Request {
|
|||
case 403:
|
||||
updatePostRefresh()
|
||||
break
|
||||
case 502:
|
||||
message.error('网络已断开,请检查网络状态')
|
||||
break
|
||||
default:
|
||||
message.error(err.message)
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -405,3 +405,16 @@ $pagination-hover-background-color: #5575F2;
|
|||
.ant-tabs {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.ant-rate .ant-rate-star-first,
|
||||
.ant-rate .ant-rate-star-second {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.hideCancelText {
|
||||
.ant-modal-confirm-btns {
|
||||
>button:nth-child(1){
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +64,6 @@ export default defineConfig({
|
|||
AudioAinsMode,
|
||||
SimulcastStreamMode,
|
||||
VideoStreamType,
|
||||
QualityType,
|
||||
RtcConnection,
|
||||
RtcStats,
|
||||
AudioVolumeInfo,
|
||||
|
|
@ -77,7 +76,9 @@ export default defineConfig({
|
|||
ColorEnhanceOptions,
|
||||
LowlightEnhanceOptions,
|
||||
VirtualBackgroundSource,
|
||||
AudienceLatencyLevelType
|
||||
AudienceLatencyLevelType,
|
||||
StreamPublishState,
|
||||
IMediaEngine
|
||||
} = require("agora-electron-sdk")
|
||||
export {
|
||||
createAgoraRtcEngine,
|
||||
|
|
@ -90,7 +91,6 @@ export default defineConfig({
|
|||
AudioAinsMode,
|
||||
SimulcastStreamMode,
|
||||
VideoStreamType,
|
||||
QualityType,
|
||||
RtcConnection,
|
||||
RtcStats,
|
||||
AudioVolumeInfo,
|
||||
|
|
@ -103,7 +103,9 @@ export default defineConfig({
|
|||
ColorEnhanceOptions,
|
||||
LowlightEnhanceOptions,
|
||||
VirtualBackgroundSource,
|
||||
AudienceLatencyLevelType
|
||||
AudienceLatencyLevelType,
|
||||
StreamPublishState,
|
||||
IMediaEngine
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
|
|
|||