Compare commits
114 Commits
734dc7a41a
...
3b03d6a8c3
| Author | SHA1 | Date |
|---|---|---|
|
|
3b03d6a8c3 | |
|
|
1ecec388ce | |
|
|
27d3a59438 | |
|
|
09aaae8274 | |
|
|
86ccca642d | |
|
|
b746a50d1a | |
|
|
5fabfd7308 | |
|
|
adf86ef62e | |
|
|
6f882d030c | |
|
|
1c5a7aa4a9 | |
|
|
9ab13ca2b4 | |
|
|
ef8e9641ed | |
|
|
d77782916c | |
|
|
092d78bbb9 | |
|
|
cc2cc4c9e5 | |
|
|
2d63cdefdb | |
|
|
3eebab8192 | |
|
|
0f164b9cca | |
|
|
90a0a5f0d0 | |
|
|
41c111ec66 | |
|
|
2102ce9c32 | |
|
|
65ad327f29 | |
|
|
28d8b9d4dc | |
|
|
5a20b9603e | |
|
|
ba9d02f9fa | |
|
|
32308e006b | |
|
|
8d9a018c19 | |
|
|
ac689b9548 | |
|
|
7875e37bd1 | |
|
|
96d17e21a0 | |
|
|
0702536b3c | |
|
|
193c99cbfa | |
|
|
edf7ce3629 | |
|
|
e40ff8c931 | |
|
|
36becdc6c4 | |
|
|
92dea100e8 | |
|
|
b6ba3c8bf6 | |
|
|
6b6d895ee3 | |
|
|
e2dc0a58e0 | |
|
|
954da4a220 | |
|
|
b4618bbf38 | |
|
|
0cda0e7842 | |
|
|
4e9bcb813e | |
|
|
b07ff2e2f1 | |
|
|
953749867b | |
|
|
5a8678a3a1 | |
|
|
051481b631 | |
|
|
939c21cf67 | |
|
|
e322314761 | |
|
|
5822a3b08c | |
|
|
98a15550c0 | |
|
|
b65cd93602 | |
|
|
c659442856 | |
|
|
414d7d1f97 | |
|
|
e3303ad903 | |
|
|
d130da43f0 | |
|
|
2b79c917a1 | |
|
|
4774f875a3 | |
|
|
83efd746e2 | |
|
|
895ee285d3 | |
|
|
6babcdbfd0 | |
|
|
cb2940249c | |
|
|
55bc137c79 | |
|
|
cd1f274644 | |
|
|
eee2c11336 | |
|
|
d397b27d49 | |
|
|
fa309a9985 | |
|
|
98602ad6be | |
|
|
583794ff13 | |
|
|
22c655174c | |
|
|
b27b338e39 | |
|
|
7d58420b77 | |
|
|
e645c13158 | |
|
|
eeccf9a462 | |
|
|
2d7103f7ae | |
|
|
ee3725ded7 | |
|
|
86d4e19bc7 | |
|
|
1f71c35920 | |
|
|
7d7490dc48 | |
|
|
16d4b3e73f | |
|
|
9e1860c722 | |
|
|
91896b8b6b | |
|
|
c713507845 | |
|
|
ff491384be | |
|
|
c6c80119c4 | |
|
|
2034dbed21 | |
|
|
697b2521d5 | |
|
|
eb555d6d66 | |
|
|
1d67a9c88d | |
|
|
2a4f72b621 | |
|
|
ed4106d6e0 | |
|
|
c28481e3ba | |
|
|
a4678aca3b | |
|
|
3bb365f248 | |
|
|
d39906d0b0 | |
|
|
af97c97f6a | |
|
|
1d857aa7cc | |
|
|
5e210ce2cc | |
|
|
8ee9d82e5f | |
|
|
8a159704da | |
|
|
c2d9e2cbea | |
|
|
712b3cfed6 | |
|
|
d1dad93ecd | |
|
|
016f1a8f2f | |
|
|
daf96cd4d5 | |
|
|
8c4ae7532a | |
|
|
308eef3704 | |
|
|
47c4626171 | |
|
|
3998bed6ff | |
|
|
b9d967365e | |
|
|
5a30d69cc8 | |
|
|
f6539d30f8 | |
|
|
75a142d3be | |
|
|
1c0a2a9034 |
|
|
@ -1,5 +1,5 @@
|
||||||
#基础API 绝对的
|
#基础API 绝对的
|
||||||
VITE_BASE_URL_API = 'http://192.168.2.9:5192'
|
VITE_BASE_URL_API = 'https://meeting-api.23544.com/pc'
|
||||||
VITE_BASE_URL_DRAW_API = 'http://192.168.2.9:6555'
|
VITE_BASE_URL_DRAW_API = 'http://192.168.2.9:6555'
|
||||||
#当前IP 相对的
|
#当前IP 相对的
|
||||||
VITE_BASE_CURRENT_API = '.'
|
VITE_BASE_CURRENT_API = '.'
|
||||||
|
|
|
||||||
62
main.js
|
|
@ -16,7 +16,6 @@ const { autoUpdater, CancellationToken } = require('electron-updater');
|
||||||
const cancellationToken = new CancellationToken()
|
const cancellationToken = new CancellationToken()
|
||||||
app.allowRendererProcessReuse = false;
|
app.allowRendererProcessReuse = false;
|
||||||
let mainWindow = null;
|
let mainWindow = null;
|
||||||
let newWindow = null;
|
|
||||||
let isMaximized = false;
|
let isMaximized = false;
|
||||||
let env;
|
let env;
|
||||||
|
|
||||||
|
|
@ -130,6 +129,9 @@ app.on('ready', () => {
|
||||||
}
|
}
|
||||||
createWindow()
|
createWindow()
|
||||||
updateHandle() // 检查更新
|
updateHandle() // 检查更新
|
||||||
|
setInterval(() => {
|
||||||
|
updateHandle() // 每一小时检查更新
|
||||||
|
}, 1000 * 60 * 60)
|
||||||
createTray()
|
createTray()
|
||||||
// 获取当前脚本所在目录的绝对路径
|
// 获取当前脚本所在目录的绝对路径
|
||||||
const currentDirectory = __dirname;
|
const currentDirectory = __dirname;
|
||||||
|
|
@ -142,11 +144,11 @@ app.on('ready', () => {
|
||||||
}
|
}
|
||||||
// 监听f12打开控制台
|
// 监听f12打开控制台
|
||||||
mainWindow.webContents.on('before-input-event', (event, input) => {
|
mainWindow.webContents.on('before-input-event', (event, input) => {
|
||||||
if (env === 'development') {
|
// if (env === 'development') {
|
||||||
if (input.key === 'F12') {
|
if (input.key === 'F12') {
|
||||||
mainWindow.webContents.openDevTools()
|
mainWindow.webContents.openDevTools()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
// 监听移动
|
// 监听移动
|
||||||
mainWindow.on('move', () => {
|
mainWindow.on('move', () => {
|
||||||
|
|
@ -243,6 +245,12 @@ app.on('ready', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// 获取桌面大小
|
||||||
|
ipcMain.handle('getWindowSize', (event, config) => {
|
||||||
|
const primaryDisplay = screen.getPrimaryDisplay()
|
||||||
|
const { width, height } = primaryDisplay.workAreaSize
|
||||||
|
return { width, height }
|
||||||
|
});
|
||||||
// 设置桌面应用基础属性
|
// 设置桌面应用基础属性
|
||||||
ipcMain.handle('setMainWindowSize', (event, config) => {
|
ipcMain.handle('setMainWindowSize', (event, config) => {
|
||||||
// 设置最小窗口尺寸
|
// 设置最小窗口尺寸
|
||||||
|
|
@ -263,46 +271,6 @@ app.on('ready', () => {
|
||||||
const y = Math.round((display.workArea.height - mainWindow.getSize()[1]) / 2);
|
const y = Math.round((display.workArea.height - mainWindow.getSize()[1]) / 2);
|
||||||
mainWindow.setPosition(x, y);
|
mainWindow.setPosition(x, y);
|
||||||
});
|
});
|
||||||
// 打开新窗口
|
|
||||||
ipcMain.handle('oepnWindow', (event, data) => {
|
|
||||||
if (newWindow) {
|
|
||||||
newWindow.focus();
|
|
||||||
} else {
|
|
||||||
newWindow = new BrowserWindow({
|
|
||||||
width: 1200,
|
|
||||||
height: 800,
|
|
||||||
minWidth: 1200,
|
|
||||||
minHeight: 800,
|
|
||||||
webPreferences: {
|
|
||||||
contextIsolation: false,
|
|
||||||
nodeIntegration: true,
|
|
||||||
enableRemoteModule: true,
|
|
||||||
nodeIntegrationInWorker: true,
|
|
||||||
allowMediaDevices: true,
|
|
||||||
preload: path.join(__dirname, 'preload.js')
|
|
||||||
},
|
|
||||||
frame: false,
|
|
||||||
backgroundColor: '#00000000',
|
|
||||||
transparent: true,
|
|
||||||
});
|
|
||||||
newWindow.loadURL(data.url);
|
|
||||||
newWindow.focus();
|
|
||||||
newWindow.webContents.on('before-input-event', (event, input) => {
|
|
||||||
if (env === 'development') {
|
|
||||||
if (input.key === 'F12') {
|
|
||||||
newWindow.webContents.openDevTools()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 关闭会议监控窗口
|
|
||||||
ipcMain.handle('closeMonitorWindow', () => {
|
|
||||||
if (newWindow) {
|
|
||||||
newWindow.close();
|
|
||||||
newWindow = null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
|
// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
|
||||||
|
|
@ -313,9 +281,9 @@ function updateHandle() {
|
||||||
error: '检查更新出错',
|
error: '检查更新出错',
|
||||||
checking: '正在检查更新……',
|
checking: '正在检查更新……',
|
||||||
updateAva: '检测到新版本,正在下载……',
|
updateAva: '检测到新版本,正在下载……',
|
||||||
updateNotAva: '现在使用的就是最新版本,不用更新'
|
updateNotAva: '已经是最新版本,不用更新'
|
||||||
}
|
}
|
||||||
autoUpdater.setFeedURL('https://update.23544.com/metting')
|
autoUpdater.setFeedURL('https://meeting-api.23544.com/meeting/update')
|
||||||
autoUpdater.autoDownload = false // 不自动下载安装包
|
autoUpdater.autoDownload = false // 不自动下载安装包
|
||||||
autoUpdater.autoInstallOnAppQuit = false // 不自动安装
|
autoUpdater.autoInstallOnAppQuit = false // 不自动安装
|
||||||
autoUpdater.on('error', function (error) {
|
autoUpdater.on('error', function (error) {
|
||||||
|
|
|
||||||
11
package.json
|
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "WGShare.Metting",
|
"name": "WGShare.Metting",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.1",
|
"version": "0.1.14",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"authors": "yj",
|
"authors": "yj",
|
||||||
"description": "test",
|
"description": "智汇享",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently \"electron . --env=development\" \"cross-env BROWSER=none vite\"",
|
"dev": "concurrently \"electron . --env=development\" \"cross-env BROWSER=none vite\"",
|
||||||
"test": "concurrently \"electron . --env=test\" \"cross-env BROWSER=none vite\"",
|
"test": "concurrently \"electron . --env=test\" \"cross-env BROWSER=none vite\"",
|
||||||
|
|
@ -69,7 +69,7 @@
|
||||||
"publish": [
|
"publish": [
|
||||||
{
|
{
|
||||||
"provider": "generic",
|
"provider": "generic",
|
||||||
"url": "https://update.23544.com/metting"
|
"url": "https://meeting-api.23544.com/meeting/update"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
|
|
@ -77,6 +77,7 @@
|
||||||
],
|
],
|
||||||
"win": {
|
"win": {
|
||||||
"icon": "build/start.ico",
|
"icon": "build/start.ico",
|
||||||
|
"requestedExecutionLevel": "highestAvailable",
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "nsis",
|
"target": "nsis",
|
||||||
|
|
@ -99,7 +100,9 @@
|
||||||
"createDesktopShortcut": true,
|
"createDesktopShortcut": true,
|
||||||
"createStartMenuShortcut": true,
|
"createStartMenuShortcut": true,
|
||||||
"deleteAppDataOnUninstall": true,
|
"deleteAppDataOnUninstall": true,
|
||||||
"shortcutName": "智汇享"
|
"shortcutName": "智汇享",
|
||||||
|
"allowElevation": true,
|
||||||
|
"perMachine":true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
12
preload.js
|
|
@ -5,6 +5,10 @@ window.electron = {
|
||||||
setMainWindowSize: (config) => {
|
setMainWindowSize: (config) => {
|
||||||
ipcRenderer.invoke('setMainWindowSize', { ...config })
|
ipcRenderer.invoke('setMainWindowSize', { ...config })
|
||||||
},
|
},
|
||||||
|
// 获取窗口大小
|
||||||
|
getWindowSize: (config) => {
|
||||||
|
return ipcRenderer.invoke('getWindowSize')
|
||||||
|
},
|
||||||
// 设置窗口状态
|
// 设置窗口状态
|
||||||
setViewStatus: (status) => {
|
setViewStatus: (status) => {
|
||||||
ipcRenderer.invoke('setViewStatus', status)
|
ipcRenderer.invoke('setViewStatus', status)
|
||||||
|
|
@ -60,13 +64,5 @@ window.electron = {
|
||||||
// 下载文件
|
// 下载文件
|
||||||
downFile: (callback) => {
|
downFile: (callback) => {
|
||||||
ipcRenderer.on('downFile', callback)
|
ipcRenderer.on('downFile', callback)
|
||||||
},
|
|
||||||
// 打开新窗口
|
|
||||||
oepnWindow: (data) => {
|
|
||||||
ipcRenderer.invoke('oepnWindow', data)
|
|
||||||
},
|
|
||||||
// 关闭会议监控窗口
|
|
||||||
closeMonitorWindow: () => {
|
|
||||||
ipcRenderer.invoke('closeMonitorWindow')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
264
src/App.tsx
|
|
@ -17,7 +17,6 @@ import { PostLogin } from "@/api/Login";
|
||||||
import { agora } from "@/utils/package/agora";
|
import { agora } from "@/utils/package/agora";
|
||||||
import QuitTips from "@/components/QuitTips";
|
import QuitTips from "@/components/QuitTips";
|
||||||
import { GetLeave } from "@/api/Meeting";
|
import { GetLeave } from "@/api/Meeting";
|
||||||
import UserVideo from "./page/UserVideo";
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
const fs = require('fs').promises;
|
const fs = require('fs').promises;
|
||||||
const { exec } = require('child_process');
|
const { exec } = require('child_process');
|
||||||
|
|
@ -33,128 +32,126 @@ const App: React.FC = () => {
|
||||||
});
|
});
|
||||||
const [spinning, setSpinning] = useState(false);
|
const [spinning, setSpinning] = useState(false);
|
||||||
const [isState, setIsState] = useState(true);
|
const [isState, setIsState] = useState(true);
|
||||||
if (location.href.indexOf('/userVideo') === -1) {
|
useEffect(() => {
|
||||||
useEffect(() => {
|
let userInfo = JSON.parse(storage.getItem('user') as string)
|
||||||
let userInfo = JSON.parse(storage.getItem('user') as string)
|
let loginInfo = JSON.parse(storage.getItem('login') as string)
|
||||||
let loginInfo = JSON.parse(storage.getItem('login') as string)
|
if (userInfo) {
|
||||||
if (userInfo) {
|
if (loginInfo && loginInfo.isAutoLogin) {
|
||||||
if (loginInfo && loginInfo.isAutoLogin) {
|
PostLogin({
|
||||||
PostLogin({
|
account: loginInfo.account,
|
||||||
account: loginInfo.account,
|
pwd: CryptoJS.MD5(loginInfo.password).toString(CryptoJS.enc.Hex)
|
||||||
pwd: CryptoJS.MD5(loginInfo.password).toString(CryptoJS.enc.Hex)
|
}).then(async (res) => {
|
||||||
}).then(async (res) => {
|
if (res.code === 200) {
|
||||||
if (res.code === 200) {
|
storage.setItem('user', JSON.stringify(res.data))
|
||||||
storage.setItem('user', JSON.stringify(res.data))
|
storage.setItem('userLogin', true)
|
||||||
toSrc('/home')
|
toSrc('/home')
|
||||||
await startSignalr()
|
await startSignalr()
|
||||||
} else {
|
} else {
|
||||||
toSrc('/login')
|
toSrc('/login')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
toSrc('/login')
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
toSrc('/login')
|
toSrc('/login')
|
||||||
}
|
}
|
||||||
window.addEventListener('resize', handleResize);
|
} else {
|
||||||
window.addEventListener('online', handleNetworkChange);
|
toSrc('/login')
|
||||||
window.addEventListener('offline', handleNetworkChange);
|
}
|
||||||
const originalSetItem = window.localStorage.setItem;
|
window.addEventListener('resize', handleResize);
|
||||||
window.localStorage.setItem = function (key, value) {
|
window.addEventListener('online', handleNetworkChange);
|
||||||
originalSetItem.call(this, key, value);
|
window.addEventListener('offline', handleNetworkChange);
|
||||||
const event = new Event('customStorageChange') as any;
|
const originalSetItem = window.localStorage.setItem;
|
||||||
event.key = key
|
window.localStorage.setItem = function (key, value) {
|
||||||
event.value = value
|
originalSetItem.call(this, key, value);
|
||||||
window.dispatchEvent(event);
|
const event = new Event('customStorageChange') as any;
|
||||||
};
|
event.key = key
|
||||||
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
event.value = value
|
||||||
return () => {
|
window.dispatchEvent(event);
|
||||||
window.removeEventListener('resize', handleResize);
|
};
|
||||||
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
||||||
window.removeEventListener('online', handleNetworkChange);
|
return () => {
|
||||||
window.removeEventListener('offline', handleNetworkChange);
|
window.removeEventListener('resize', handleResize);
|
||||||
};
|
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
||||||
}, []);
|
window.removeEventListener('online', handleNetworkChange);
|
||||||
useEffect(() => {
|
window.removeEventListener('offline', handleNetworkChange);
|
||||||
window.electron.downFile(async (_e: any, data: any) => {
|
};
|
||||||
const response = await fetch(data.filePath);
|
}, []);
|
||||||
const arrayBuffer = await response.arrayBuffer();
|
useEffect(() => {
|
||||||
const buffer = Buffer.from(arrayBuffer);
|
window.electron.downFile(async (_e: any, data: any) => {
|
||||||
fs.writeFile(`${data.downFilePaths}${data.fileName}`, buffer, {});
|
const response = await fetch(data.filePath);
|
||||||
message.success(`下载成功!文件已保存至:${data.downFilePaths}`)
|
const arrayBuffer = await response.arrayBuffer();
|
||||||
await fs.access(data.downFilePaths, fs.constants.F_OK);
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
if (process.platform === 'win32') {
|
fs.writeFile(`${data.downFilePaths}${data.fileName}`, buffer, {});
|
||||||
exec(`explorer "${data.downFilePaths}"`);
|
message.success(`下载成功!文件已保存至:${data.downFilePaths}`)
|
||||||
} else if (process.platform === 'darwin') {
|
await fs.access(data.downFilePaths, fs.constants.F_OK);
|
||||||
exec(`open "${data.downFilePaths}"`);
|
if (process.platform === 'win32') {
|
||||||
}
|
exec(`explorer "${data.downFilePaths}"`);
|
||||||
})
|
} else if (process.platform === 'darwin') {
|
||||||
window.electron.onFilePath(async (_e: any, filePath: string, key: string) => {
|
exec(`open "${data.downFilePaths}"`);
|
||||||
const setting = await JSON.parse(storage.getItem('setting') as string)
|
}
|
||||||
if (key === 'recordingFilesPath') {
|
})
|
||||||
setting.recordingFilesPath = filePath
|
window.electron.onFilePath(async (_e: any, filePath: string, key: string) => {
|
||||||
|
const setting = await JSON.parse(storage.getItem('setting') as string)
|
||||||
|
if (key === 'recordingFilesPath') {
|
||||||
|
setting.recordingFilesPath = filePath
|
||||||
|
} else {
|
||||||
|
setting.shareFilesPath = filePath
|
||||||
|
}
|
||||||
|
storage.setItem('setting', JSON.stringify(setting))
|
||||||
|
})
|
||||||
|
window.electron.quitAndInstall(async (_e: any) => {
|
||||||
|
leaveChannel()
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
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({
|
||||||
|
videoDeviceId: '', //摄像头id
|
||||||
|
ecordingDeviceId: "", //输入设备id
|
||||||
|
playBackDeviceId: "", //输出设备id
|
||||||
|
ecordingVolume: '', //输入音量
|
||||||
|
playBackVolume: '', //输出音量
|
||||||
|
autoEcordingVolume: true, //是否自动调整麦克风音量
|
||||||
|
recordingFilesPath: path.resolve(__dirname, '../../Downloads') + '\\', //本地录制保存路径
|
||||||
|
shareFilesPath: path.resolve(__dirname, '../../Downloads/') + '\\', //共享文件保存路径
|
||||||
|
isShareSavePath: true, //是否下载钱询问每个文件保存的位置
|
||||||
|
closeSetting: 'hide', //关闭按钮设置
|
||||||
|
isAINoiseReduction: true, //是否开启ai降噪
|
||||||
|
aINoiseReduction: 1, // 降噪模式
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
useEffect(() => {
|
||||||
|
if (isState) {
|
||||||
|
setIsState(false)
|
||||||
|
window.electron.onQuit(async () => {
|
||||||
|
if (location.hash.indexOf('/login') === 1) {
|
||||||
|
window.electron.quit()
|
||||||
} else {
|
} else {
|
||||||
setting.shareFilesPath = filePath
|
if (storage.getItem('isTips') === 'true') {
|
||||||
}
|
const setting = JSON.parse(storage.getItem('setting') as string)
|
||||||
storage.setItem('setting', JSON.stringify(setting))
|
if (setting.closeSetting === 'hide') {
|
||||||
})
|
window.electron.setViewStatus(setting.closeSetting)
|
||||||
window.electron.quitAndInstall(async (_e: any) => {
|
|
||||||
leaveChannel()
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
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({
|
|
||||||
videoDeviceId: '', //摄像头id
|
|
||||||
ecordingDeviceId: "", //输入设备id
|
|
||||||
playBackDeviceId: "", //输出设备id
|
|
||||||
ecordingVolume: '', //输入音量
|
|
||||||
playBackVolume: '', //输出音量
|
|
||||||
autoEcordingVolume: true, //是否自动调整麦克风音量
|
|
||||||
recordingFilesPath: path.resolve(__dirname, '../../Downloads') + '\\', //本地录制保存路径
|
|
||||||
shareFilesPath: path.resolve(__dirname, '../../Downloads/') + '\\', //共享文件保存路径
|
|
||||||
isShareSavePath: true, //是否下载钱询问每个文件保存的位置
|
|
||||||
closeSetting: 'hide', //关闭按钮设置
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
useEffect(() => {
|
|
||||||
if (isState) {
|
|
||||||
setIsState(false)
|
|
||||||
window.electron.onQuit(async () => {
|
|
||||||
if (location.hash.indexOf('/login') === 1) {
|
|
||||||
window.electron.quit()
|
|
||||||
} else {
|
|
||||||
if (storage.getItem('isTips') === 'true') {
|
|
||||||
const setting = JSON.parse(storage.getItem('setting') as string)
|
|
||||||
if (setting.closeSetting === 'hide') {
|
|
||||||
window.electron.setViewStatus(setting.closeSetting)
|
|
||||||
} else {
|
|
||||||
window.electron.quit()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
quitTipsRef.current.changeModal()
|
window.electron.quit()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
quitTipsRef.current.changeModal()
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
storage.setItem('stateInfo', JSON.stringify(state))
|
}
|
||||||
}, [state])
|
storage.setItem('stateInfo', JSON.stringify(state))
|
||||||
useEffect(() => {
|
}, [state])
|
||||||
if (location.href.indexOf('/login') !== -1) {
|
useEffect(() => {
|
||||||
onStop()
|
if (location.href.indexOf('/login') !== -1) {
|
||||||
}
|
onStop()
|
||||||
if (location.href.indexOf('/meeting') === -1) {
|
}
|
||||||
window.electron.closeMonitorWindow()
|
}, [navigate])
|
||||||
}
|
|
||||||
}, [navigate])
|
|
||||||
}
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.addEventListener('keydown', (event) => {
|
document.addEventListener('keydown', (event) => {
|
||||||
if (event.key === 'F11') {
|
if (event.key === 'F11') {
|
||||||
|
|
@ -200,20 +197,22 @@ const App: React.FC = () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const toSrc = (path: string): void => {
|
const toSrc = (path: string): void => {
|
||||||
switch (path) {
|
window.electron.getWindowSize().then((res: any) => {
|
||||||
case '/login':
|
switch (path) {
|
||||||
storage.removeItem('user')
|
case '/login':
|
||||||
navigate('/login')
|
storage.removeItem('user')
|
||||||
break;
|
storage.setItem('userLogin', false)
|
||||||
case '/home':
|
navigate('/login')
|
||||||
window.electron.setMainWindowSize({
|
break;
|
||||||
width: 1200,
|
case '/home':
|
||||||
height: 800,
|
window.electron.setMainWindowSize({
|
||||||
})
|
width: Math.ceil(res.width / 1.5),
|
||||||
navigate('/home')
|
height: Math.ceil(res.height / 1.3),
|
||||||
break;
|
})
|
||||||
|
navigate('/home')
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
const leaveChannel = async (bool?: boolean): Promise<void> => {
|
const leaveChannel = async (bool?: boolean): Promise<void> => {
|
||||||
if (location.hash.indexOf('/meeting') === 1) {
|
if (location.hash.indexOf('/meeting') === 1) {
|
||||||
|
|
@ -233,6 +232,10 @@ const App: React.FC = () => {
|
||||||
if (Boolean(e.value)) {
|
if (Boolean(e.value)) {
|
||||||
onEventSignalr()
|
onEventSignalr()
|
||||||
}
|
}
|
||||||
|
} else if (e.key === 'userLogin') {
|
||||||
|
if (!Boolean(e.value)) {
|
||||||
|
navigate('/login')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -259,7 +262,6 @@ const App: React.FC = () => {
|
||||||
</Route>
|
</Route>
|
||||||
<Route path='/login' element={<Login />} />
|
<Route path='/login' element={<Login />} />
|
||||||
<Route path='/meeting' element={<Meeting />} />
|
<Route path='/meeting' element={<Meeting />} />
|
||||||
<Route path='/userVideo' element={<UserVideo />} />
|
|
||||||
<Route path='*' element={<NotFound />} />
|
<Route path='*' element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
<Spin spinning={spinning} fullscreen />
|
<Spin spinning={spinning} fullscreen />
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,17 @@ export const GetRoom = (data: { pageIndex: number, pageSize: number }) =>
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
|
|
||||||
export const PostRomm = (data: any) =>
|
export const PostRoom = (data: any) =>
|
||||||
request({
|
request({
|
||||||
url: `/home/room`,
|
url: `/home/room`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
|
export const DeleteRoom = (roomId: string) =>
|
||||||
|
request({
|
||||||
|
url: `/home/room?roomId=${roomId}`,
|
||||||
|
method: 'delete',
|
||||||
|
})
|
||||||
|
|
||||||
export const GetCheckoutRoomNum = (roomNum: string) =>
|
export const GetCheckoutRoomNum = (roomNum: string) =>
|
||||||
request({
|
request({
|
||||||
|
|
@ -30,3 +35,15 @@ export const GetRoomInfo = (roomNum: string) =>
|
||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const GetAgoraConf = () =>
|
||||||
|
request({
|
||||||
|
url: `/home/agora-conf`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const GetRecord = (beginTimestamp: number, endTimestamp: number, roomNum: string) =>
|
||||||
|
request({
|
||||||
|
url: `/home/record?beginTimestamp=${beginTimestamp}&endTimestamp=${endTimestamp}&roomNum=${roomNum}`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,3 +38,10 @@ export const GetRoleDpList = () =>
|
||||||
url: `/pub/role-dp-list`,
|
url: `/pub/role-dp-list`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const PostUserImport = (data: any) =>
|
||||||
|
request({
|
||||||
|
url: `/user/import`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 622 B After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 677 B After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 530 B After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 650 B After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 495 B After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 675 B After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 977 B After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 760 B After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 892 B After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 769 B After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 973 B After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 641 B After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 963 B After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 647 B After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 700 B After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
|
@ -0,0 +1,23 @@
|
||||||
|
.equipmentManagement {
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
>div:nth-child(1) {
|
||||||
|
>div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
>span {
|
||||||
|
color: #EEEEEE;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>div:nth-child(2) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
import styles from '@/components/EquipmentManagement/index.module.scss'
|
||||||
|
import { onInvoke } from '@/utils/package/signalr';
|
||||||
|
import { Button, Modal, Select, Slider, message } from 'antd';
|
||||||
|
import { useState, useImperativeHandle, forwardRef } from "react";
|
||||||
|
const EquipmentManagement = forwardRef((_props: any, ref: any) => {
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
changeModal: async (uid: string, userName: string) => {
|
||||||
|
setCallerUid(uid)
|
||||||
|
setDeviceInfo({})
|
||||||
|
await onInvoke('getDrivers', {
|
||||||
|
uid
|
||||||
|
})
|
||||||
|
setUserName(userName)
|
||||||
|
setEquipmentManagementModal(true)
|
||||||
|
},
|
||||||
|
setData: (data: any) => {
|
||||||
|
setDeviceInfo(data)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
const [equipmentManagementModal, setEquipmentManagementModal] = useState(false)
|
||||||
|
const [callerUid, setCallerUid] = useState('')
|
||||||
|
const [deviceInfo, setDeviceInfo] = useState<any>({})
|
||||||
|
const [userName, setUserName] = useState<any>({})
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
title={`设备管理(${userName})`}
|
||||||
|
open={equipmentManagementModal}
|
||||||
|
footer={null}
|
||||||
|
centered
|
||||||
|
width={'500px'}
|
||||||
|
onCancel={() => {
|
||||||
|
setEquipmentManagementModal(false)
|
||||||
|
}}>
|
||||||
|
<div className={styles.equipmentManagement}>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<span>摄像头</span>
|
||||||
|
<Select
|
||||||
|
placeholder={deviceInfo?.videoList?.length ? '请选择设备' : '未检测到摄像头'}
|
||||||
|
options={deviceInfo?.videoList} style={{ flexGrow: 1 }}
|
||||||
|
value={deviceInfo?.videoDeviceId} onChange={async (e) => {
|
||||||
|
setDeviceInfo({
|
||||||
|
...deviceInfo,
|
||||||
|
videoDeviceId: e
|
||||||
|
})
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>麦克风</span>
|
||||||
|
<Select
|
||||||
|
placeholder={deviceInfo?.ecordingList?.length ? '请选择设备' : '未检测到麦克风'}
|
||||||
|
options={deviceInfo?.ecordingList} style={{ flexGrow: 1 }}
|
||||||
|
value={deviceInfo?.ecordingDeviceId} onChange={async (e) => {
|
||||||
|
setDeviceInfo({
|
||||||
|
...deviceInfo,
|
||||||
|
ecordingDeviceId: e
|
||||||
|
})
|
||||||
|
}} />;
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>输入音量</span>
|
||||||
|
<Slider value={deviceInfo.ecordingVolume} style={{ flexGrow: 1 }} max={255}
|
||||||
|
onChange={async (e) => {
|
||||||
|
setDeviceInfo({
|
||||||
|
...deviceInfo,
|
||||||
|
ecordingVolume: e
|
||||||
|
})
|
||||||
|
}} disabled={!deviceInfo.ecordingDeviceId} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>扬声器</span>
|
||||||
|
<Select
|
||||||
|
placeholder={deviceInfo?.playBackList?.length ? '请选择设备' : '未检测到麦克风'}
|
||||||
|
options={deviceInfo?.playBackList} style={{ flexGrow: 1 }}
|
||||||
|
value={deviceInfo?.playBackDeviceId} onChange={async (e) => {
|
||||||
|
setDeviceInfo({
|
||||||
|
...deviceInfo,
|
||||||
|
playBackDeviceId: e
|
||||||
|
})
|
||||||
|
}} />;
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button type="primary" className='m-ant-btn' onClick={async () => {
|
||||||
|
await onInvoke('setDrivers', {
|
||||||
|
uid: callerUid,
|
||||||
|
driversJsonString: JSON.stringify(deviceInfo)
|
||||||
|
})
|
||||||
|
setEquipmentManagementModal(false)
|
||||||
|
message.success('设置成功')
|
||||||
|
}}>确定</Button>
|
||||||
|
<Button type="primary"
|
||||||
|
style={{ backgroundColor: '#31353A', marginLeft: '10px' }}
|
||||||
|
onClick={() => setEquipmentManagementModal(false)}>取消</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal >
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default EquipmentManagement
|
||||||
|
|
@ -8,6 +8,7 @@ import { PostRefresh } from '@/api/Login';
|
||||||
import Avatar from '@/components/Avatar';
|
import Avatar from '@/components/Avatar';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { agora } from '@/utils/package/agora';
|
import { agora } from '@/utils/package/agora';
|
||||||
|
import { role } from '@/config/role';
|
||||||
let time = null as any;
|
let time = null as any;
|
||||||
const JoinSetting = forwardRef((_props: any, ref: any) => {
|
const JoinSetting = forwardRef((_props: any, ref: any) => {
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
|
|
@ -71,7 +72,7 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => {
|
const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => {
|
||||||
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + '1')]).then(res => {
|
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + 'a')]).then(res => {
|
||||||
if (res[0].code === 200 && res[1].code === 200) {
|
if (res[0].code === 200 && res[1].code === 200) {
|
||||||
callBack({
|
callBack({
|
||||||
token: res[0].data,
|
token: res[0].data,
|
||||||
|
|
@ -84,6 +85,7 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
|
||||||
await PostRefresh(user.refresh_token).then(res => {
|
await PostRefresh(user.refresh_token).then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
storage.setItem('user', JSON.stringify(res.data))
|
storage.setItem('user', JSON.stringify(res.data))
|
||||||
|
storage.setItem('userLogin', true)
|
||||||
callBack(res.data)
|
callBack(res.data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -126,7 +128,7 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
|
||||||
{
|
{
|
||||||
joinRoomSettingForm.map((item, index) => {
|
joinRoomSettingForm.map((item, index) => {
|
||||||
return <div key={index} onClick={async () => {
|
return <div key={index} onClick={async () => {
|
||||||
if (user.roleId === '1') {
|
if (role.ID.includes(user.roleId)) {
|
||||||
let msg = '';
|
let msg = '';
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
await agora.getAudioMediaList().then(res => {
|
await agora.getAudioMediaList().then(res => {
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,15 @@ import {
|
||||||
VerticalAlignBottomOutlined
|
VerticalAlignBottomOutlined
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Button, Input, message, Modal, Pagination, Progress, Table } from 'antd';
|
import { Button, Input, message, Modal, Pagination, Progress, Table } from 'antd';
|
||||||
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
|
import { forwardRef, useEffect, useImperativeHandle, useState, useRef } from "react";
|
||||||
import { DeleteRoomFile, GetRoomFile, GetRoomFileDwUrl, GetRoomUpFileurl, GetRoomUserItem, PostRoomFile } from '@/api/Meeting';
|
import { DeleteRoomFile, GetRoomFile, GetRoomFileDwUrl, GetRoomUpFileurl, GetRoomUserItem, PostRoomFile } from '@/api/Meeting';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { storage } from '@/utils';
|
import { storage } from '@/utils';
|
||||||
|
import StupWizard from '../StupWizard';
|
||||||
|
import { role } from '@/config/role';
|
||||||
const fs = require('fs').promises;
|
const fs = require('fs').promises;
|
||||||
const { exec } = require('child_process');
|
const { exec } = require('child_process');
|
||||||
|
|
||||||
const { Column } = Table
|
const { Column } = Table
|
||||||
|
|
||||||
const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
||||||
|
|
@ -26,8 +27,10 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
||||||
setRoomUserItem(res.data)
|
setRoomUserItem(res.data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
getRoomFile()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
const stupWizardRef = useRef<any>();
|
||||||
const { state } = useLocation();
|
const { state } = useLocation();
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
const [roomUserItem, setRoomUserItem] = useState<any>({})
|
const [roomUserItem, setRoomUserItem] = useState<any>({})
|
||||||
|
|
@ -126,7 +129,7 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}} />
|
}} />
|
||||||
{roomUserItem && roomUserItem.roleId === '1' || roomUserItem.isRoomManager ? <ProfileOutlined title={showRowSelection ? '取消框选' : '显示框选'} onClick={() => {
|
{roomUserItem && role.ID.includes(roomUserItem.roleId) || roomUserItem.isRoomManager ? <ProfileOutlined title={showRowSelection ? '取消框选' : '显示框选'} onClick={() => {
|
||||||
setShowRowSelection(!showRowSelection)
|
setShowRowSelection(!showRowSelection)
|
||||||
}} style={{ color: showRowSelection ? '#5575F2' : 'white' }} /> : null}
|
}} style={{ color: showRowSelection ? '#5575F2' : 'white' }} /> : null}
|
||||||
{showRowSelection ? <DeleteOutlined title='删除' onClick={() => {
|
{showRowSelection ? <DeleteOutlined title='删除' onClick={() => {
|
||||||
|
|
@ -310,7 +313,11 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.code === 'ENOENT') {
|
if (error.code === 'ENOENT') {
|
||||||
message.error('文件夹不存在!')
|
message.error({
|
||||||
|
content: <div>文件夹不存在 <span style={{ color: '#606fc7', cursor: 'pointer' }} onClick={() => {
|
||||||
|
stupWizardRef.current.changeModal(4)
|
||||||
|
}}>前往设置</span></div>,
|
||||||
|
})
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
message.error(error)
|
message.error(error)
|
||||||
|
|
@ -323,7 +330,6 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}} />
|
}} />
|
||||||
{/* <FolderOutlined title='文件' style={{ color: '#FFA000', cursor: 'pointer', marginLeft: '10px' }} /> */}
|
{/* <FolderOutlined title='文件' style={{ color: '#FFA000', cursor: 'pointer', marginLeft: '10px' }} /> */}
|
||||||
</>
|
</>
|
||||||
|
|
@ -344,6 +350,7 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<StupWizard ref={stupWizardRef} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import styles from '@/components/StupWizard/index.module.scss'
|
import styles from '@/components/StupWizard/index.module.scss'
|
||||||
import ImageUrl from '@/utils/package/imageUrl';
|
import ImageUrl from '@/utils/package/imageUrl';
|
||||||
import { Button, Checkbox, Empty, Input, message, Modal, Popover, Radio, Select, Slider } from 'antd';
|
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 } from "react";
|
||||||
import { agora } from '@/utils/package/agora'
|
import { agora } from '@/utils/package/agora'
|
||||||
import { CloseOutlined, LoadingOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
import { CloseOutlined, LoadingOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||||
|
|
@ -11,7 +11,7 @@ const fs = require('fs').promises;
|
||||||
const { exec } = require('child_process');
|
const { exec } = require('child_process');
|
||||||
const StupWizard = forwardRef((props: any, ref: any) => {
|
const StupWizard = forwardRef((props: any, ref: any) => {
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
changeModal: () => {
|
changeModal: (index: number = 0) => {
|
||||||
if (location.hash.indexOf('/meeting') === -1) {
|
if (location.hash.indexOf('/meeting') === -1) {
|
||||||
agora.init()
|
agora.init()
|
||||||
}
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ const StupWizard = forwardRef((props: any, ref: any) => {
|
||||||
res.forEach((item: any) => {
|
res.forEach((item: any) => {
|
||||||
item.active = false
|
item.active = false
|
||||||
});
|
});
|
||||||
res[0].active = true;
|
res[index].active = true;
|
||||||
return res
|
return res
|
||||||
})
|
})
|
||||||
setIsStupWizard(true)
|
setIsStupWizard(true)
|
||||||
|
|
@ -243,6 +243,8 @@ const AudioComponents = () => {
|
||||||
ecordingActive: false,
|
ecordingActive: false,
|
||||||
ecordingVolume: 0,
|
ecordingVolume: 0,
|
||||||
autoEcordingVolume: true,
|
autoEcordingVolume: true,
|
||||||
|
isAINoiseReduction: true,
|
||||||
|
aINoiseReduction: 1
|
||||||
});
|
});
|
||||||
const setting = JSON.parse(storage.getItem('setting') as string)
|
const setting = JSON.parse(storage.getItem('setting') as string)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -288,7 +290,9 @@ const AudioComponents = () => {
|
||||||
ecordingItem: setting.ecordingDeviceId,
|
ecordingItem: setting.ecordingDeviceId,
|
||||||
ecordingVolume: setting.ecordingVolume,
|
ecordingVolume: setting.ecordingVolume,
|
||||||
playBackVolume: setting.playBackVolume,
|
playBackVolume: setting.playBackVolume,
|
||||||
autoEcordingVolume: setting.autoEcordingVolume
|
autoEcordingVolume: setting.autoEcordingVolume,
|
||||||
|
isAINoiseReduction: setting.isAINoiseReduction,
|
||||||
|
aINoiseReduction: setting.aINoiseReduction,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|
@ -350,6 +354,38 @@ const AudioComponents = () => {
|
||||||
})
|
})
|
||||||
}} disabled={!audioDeviceManager.ecordingItem} />
|
}} disabled={!audioDeviceManager.ecordingItem} />
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<Checkbox checked={audioDeviceManager.isAINoiseReduction} onChange={(e) => {
|
||||||
|
setting.isAINoiseReduction = e.target.checked;
|
||||||
|
storage.setItem('setting', JSON.stringify(setting))
|
||||||
|
setAudioDeviceManager({
|
||||||
|
...audioDeviceManager,
|
||||||
|
isAINoiseReduction: e.target.checked
|
||||||
|
})
|
||||||
|
agora.setAINSMode(e.target.checked, audioDeviceManager.aINoiseReduction)
|
||||||
|
}}>
|
||||||
|
AI降噪
|
||||||
|
</Checkbox>
|
||||||
|
</div>
|
||||||
|
<div style={{ margin: '10px 0 0 20px' }}>
|
||||||
|
<Radio.Group onChange={(e) => {
|
||||||
|
setting.aINoiseReduction = e.target.value;
|
||||||
|
storage.setItem('setting', JSON.stringify(setting))
|
||||||
|
setAudioDeviceManager({
|
||||||
|
...audioDeviceManager,
|
||||||
|
aINoiseReduction: e.target.value
|
||||||
|
})
|
||||||
|
agora.setAINSMode(audioDeviceManager.isAINoiseReduction, e.target.value)
|
||||||
|
}} disabled={!audioDeviceManager.isAINoiseReduction} value={audioDeviceManager.aINoiseReduction}>
|
||||||
|
<Space direction="vertical">
|
||||||
|
<Radio value={0}>均衡降噪模式</Radio>
|
||||||
|
<Radio value={1}>强降噪模式</Radio>
|
||||||
|
<Radio value={2}>低延时强降噪模式</Radio>
|
||||||
|
</Space>
|
||||||
|
</Radio.Group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/* <div>
|
{/* <div>
|
||||||
<Checkbox onChange={async (e) => {
|
<Checkbox onChange={async (e) => {
|
||||||
setting.autoEcordingVolume = e.target.checked;
|
setting.autoEcordingVolume = e.target.checked;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ const UpdateModal = forwardRef((props: any, ref: any) => {
|
||||||
const [updateContent, setUpdateContent] = useState('') // 版本更新内容
|
const [updateContent, setUpdateContent] = useState('') // 版本更新内容
|
||||||
|
|
||||||
function getContent() {
|
function getContent() {
|
||||||
fetch(`https://update.23544.com/metting/update.txt?t=${+new Date()}`) // 配置服务器地址
|
fetch(`https://meeting-api.23544.com/meeting/update/update.txt?t=${+new Date()}`) // 配置服务器地址
|
||||||
.then(async response => {
|
.then(async response => {
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return setUpdateContent(await response.text())
|
return setUpdateContent(await response.text())
|
||||||
|
|
@ -67,7 +67,7 @@ const UpdateModal = forwardRef((props: any, ref: any) => {
|
||||||
>立即更新</Button>
|
>立即更新</Button>
|
||||||
<div className={styles.button2} onClick={() => setIsUpdateModal(false)}>暂不更新</div>
|
<div className={styles.button2} onClick={() => setIsUpdateModal(false)}>暂不更新</div>
|
||||||
</div> : progress < 100 ?
|
</div> : progress < 100 ?
|
||||||
<div style={{ marginTop: '20px' }}>
|
<div style={{ margin: '20px 0' }}>
|
||||||
下载进度:{progress}%
|
下载进度:{progress}%
|
||||||
<Flex gap="small" vertical>
|
<Flex gap="small" vertical>
|
||||||
<Progress percent={progress} showInfo={false} />
|
<Progress percent={progress} showInfo={false} />
|
||||||
|
|
@ -75,7 +75,7 @@ const UpdateModal = forwardRef((props: any, ref: any) => {
|
||||||
</div> :
|
</div> :
|
||||||
<Button type="primary"
|
<Button type="primary"
|
||||||
onClick={() => window.electron.onDownload('2')}
|
onClick={() => window.electron.onDownload('2')}
|
||||||
style={{ width: '100%', height: '40px', marginTop: '20px' }}
|
style={{ width: '100%', height: '40px', margin: '20px 0' }}
|
||||||
className={`m-ant-btn`}
|
className={`m-ant-btn`}
|
||||||
>下载完成,点击安装</Button>
|
>下载完成,点击安装</Button>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,40 +2,24 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgb(7, 9, 11);
|
background-color: rgb(7, 9, 11);
|
||||||
padding: 20px;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.userVideoTitle {
|
|
||||||
text-align: center;
|
|
||||||
color: white;
|
|
||||||
font-size: 20px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
>span {
|
|
||||||
cursor: pointer;
|
|
||||||
position: absolute;
|
|
||||||
right: 20px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(0, -50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.userVideoContent {
|
.userVideoContent {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 20px 40px 0;
|
|
||||||
|
|
||||||
.userVideoContentHeader {
|
.userVideoContentHeader {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
flex-shrink: 0;
|
padding: 10px;
|
||||||
margin-bottom: 10px;
|
box-sizing: border-box;
|
||||||
padding: 0 10px;
|
|
||||||
|
|
||||||
>div:nth-child(1) {
|
>div:nth-child(1) {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -45,12 +29,16 @@
|
||||||
background-color: #181A1D;
|
background-color: #181A1D;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
margin: 0 10px 0 0;
|
margin: 0 10px 0 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
>span {
|
>span {
|
||||||
color: #EEEEEE;
|
color: #EEEEEE;
|
||||||
font-size: 14px;
|
font-size: 12px;
|
||||||
margin-right: 10px;
|
white-space: nowrap;
|
||||||
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -61,8 +49,8 @@
|
||||||
|
|
||||||
>span {
|
>span {
|
||||||
color: #EEEEEE;
|
color: #EEEEEE;
|
||||||
font-size: 14px;
|
font-size: 12px;
|
||||||
margin-right: 10px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -77,9 +65,9 @@
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|
||||||
.userVideoContentListItem {
|
.userVideoContentListItem {
|
||||||
height: 20%;
|
height: 32%;
|
||||||
width: calc(100% / 4 - 20px);
|
width: calc(100% / 4 - 8px);
|
||||||
padding: 10px;
|
padding: 4px;
|
||||||
|
|
||||||
.userVideoContentListItemVideo {
|
.userVideoContentListItemVideo {
|
||||||
background-color: #101317;
|
background-color: #101317;
|
||||||
|
|
@ -87,6 +75,18 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
>div:nth-child(1) {
|
||||||
|
background-color: rgb(253, 194, 41);
|
||||||
|
color: black;
|
||||||
|
position: absolute;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
z-index: 1;
|
||||||
|
padding: 2px 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
|
|
||||||
|
import styles from '@/components/UserVideo/index.module.scss'
|
||||||
import { GetPolling } from '@/api/Meeting';
|
import { GetPolling } from '@/api/Meeting';
|
||||||
import styles from '@/page/UserVideo/index.module.scss'
|
|
||||||
import { storage } from '@/utils';
|
|
||||||
import { agora } from '@/utils/package/agora';
|
import { agora } from '@/utils/package/agora';
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Empty, Select, message } from 'antd';
|
import { Button, Empty, Select, message } from 'antd';
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { useLocation } from 'react-router';
|
||||||
|
import { VideoStreamType } from 'agora-electron-sdk';
|
||||||
const UserVideo: React.FC = () => {
|
const UserVideo: React.FC = () => {
|
||||||
let userInfo = JSON.parse(storage.getItem('user') as string)
|
const { state } = useLocation();
|
||||||
const [user, setUser] = useState<any>({});
|
|
||||||
const [from, setFrom] = useState<any>({
|
const [from, setFrom] = useState<any>({
|
||||||
cycleIntervalList: [
|
cycleIntervalList: [
|
||||||
{ value: 30, label: '30秒' },
|
{ value: 30, label: '30秒' },
|
||||||
|
|
@ -18,19 +17,16 @@ const UserVideo: React.FC = () => {
|
||||||
],
|
],
|
||||||
cycleIntervalValue: 30,
|
cycleIntervalValue: 30,
|
||||||
viewPeople: [
|
viewPeople: [
|
||||||
{ value: 4, label: '4人' },
|
{ value: 6, label: '6人' },
|
||||||
{ value: 8, label: '8人' },
|
|
||||||
{ value: 12, label: '12人' },
|
{ value: 12, label: '12人' },
|
||||||
{ value: 16, label: '16人' },
|
// { value: 20, label: '20人' },
|
||||||
{ value: 20, label: '20人' },
|
|
||||||
],
|
],
|
||||||
viewPeopleValue: 4,
|
viewPeopleValue: 6,
|
||||||
})
|
})
|
||||||
const [timeNumber, setTimeNumber] = useState(30);
|
const [timeNumber, setTimeNumber] = useState(30);
|
||||||
const [timeStatus, setTimeStatus] = useState(false);
|
const [timeStatus, setTimeStatus] = useState(false);
|
||||||
const [userList, setUserList] = useState([]);
|
const [userList, setUserList] = useState([]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUser(userInfo)
|
|
||||||
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
||||||
|
|
@ -63,12 +59,14 @@ const UserVideo: React.FC = () => {
|
||||||
}, [from.viewPeopleValue])
|
}, [from.viewPeopleValue])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
userList.forEach((item: any) => {
|
userList.forEach(async (item: any) => {
|
||||||
// agora.meetingMonitoringSetupRemoteVideoJoin({
|
await agora.destroyRendererByConfig(Number('1' + item.screenShareId))
|
||||||
// uid: Number('1' + item.screenShareId),
|
await agora.setupRemoteVideoEx({
|
||||||
// view: document.getElementById(`video-${item.uid}`),
|
uid: Number('1' + item.screenShareId),
|
||||||
// channelId: getQueryParameterRegex('channelId'),
|
view: document.getElementById(`video-${item.screenShareId}`),
|
||||||
// })
|
channelId: state.channelId + 'a',
|
||||||
|
})
|
||||||
|
await agora.setRemoteVideoStreamType(Number('1' + item.screenShareId), VideoStreamType.VideoStreamLow, false)
|
||||||
})
|
})
|
||||||
}, [userList])
|
}, [userList])
|
||||||
// 监听缓存变化
|
// 监听缓存变化
|
||||||
|
|
@ -77,30 +75,22 @@ const UserVideo: React.FC = () => {
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// 获取地址栏参数
|
|
||||||
const getQueryParameterRegex = (name: string): string | null => {
|
|
||||||
const reg = new RegExp(`[?&]${name}=([^&#]*)`);
|
|
||||||
const results = window.location.href.match(reg);
|
|
||||||
return results === null ? null : results[1];
|
|
||||||
}
|
|
||||||
// 获取轮训用户
|
// 获取轮训用户
|
||||||
const getPolling = async (): Promise<void> => {
|
const getPolling = async (): Promise<void> => {
|
||||||
GetPolling(getQueryParameterRegex('channelId')?.split('1')[0] as string, from.viewPeopleValue).then((res: any) => {
|
setUserList([])
|
||||||
if (res.code === 200) {
|
setFrom((res: any) => {
|
||||||
setUserList(res.data)
|
GetPolling(state.channelId?.split('a')[0] as string, res.viewPeopleValue).then((res: any) => {
|
||||||
}
|
if (res.code === 200) {
|
||||||
|
setUserList(res.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return res
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.userVideo}>
|
<div className={styles.userVideo}>
|
||||||
<div className={styles.userVideoTitle}>
|
<div className={`${styles.userVideoContent}`}>
|
||||||
会议监控
|
|
||||||
<CloseOutlined className='drag' onClick={() => {
|
|
||||||
window.electron.closeMonitorWindow()
|
|
||||||
}} />
|
|
||||||
</div>
|
|
||||||
<div className={`${styles.userVideoContent} drag`}>
|
|
||||||
<div className={styles.userVideoContentHeader}>
|
<div className={styles.userVideoContentHeader}>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -108,6 +98,7 @@ const UserVideo: React.FC = () => {
|
||||||
<Select
|
<Select
|
||||||
placeholder='请选择循环间隔'
|
placeholder='请选择循环间隔'
|
||||||
options={from.cycleIntervalList}
|
options={from.cycleIntervalList}
|
||||||
|
size={'small'}
|
||||||
value={from.cycleIntervalValue} onChange={(e) => {
|
value={from.cycleIntervalValue} onChange={(e) => {
|
||||||
setFrom({ ...from, cycleIntervalValue: e })
|
setFrom({ ...from, cycleIntervalValue: e })
|
||||||
setTimeNumber(e)
|
setTimeNumber(e)
|
||||||
|
|
@ -118,6 +109,7 @@ const UserVideo: React.FC = () => {
|
||||||
<Select
|
<Select
|
||||||
placeholder='请选择查看人数'
|
placeholder='请选择查看人数'
|
||||||
options={from.viewPeople}
|
options={from.viewPeople}
|
||||||
|
size={'small'}
|
||||||
value={from.viewPeopleValue} onChange={(e) => {
|
value={from.viewPeopleValue} onChange={(e) => {
|
||||||
setFrom({ ...from, viewPeopleValue: e })
|
setFrom({ ...from, viewPeopleValue: e })
|
||||||
}} />
|
}} />
|
||||||
|
|
@ -127,6 +119,7 @@ const UserVideo: React.FC = () => {
|
||||||
<span>{timeNumber}秒后刷新</span>
|
<span>{timeNumber}秒后刷新</span>
|
||||||
{timeStatus ? <Button
|
{timeStatus ? <Button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
size={'small'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTimeStatus(!timeStatus)
|
setTimeStatus(!timeStatus)
|
||||||
}}
|
}}
|
||||||
|
|
@ -135,6 +128,7 @@ const UserVideo: React.FC = () => {
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
className='m-ant-btn'
|
className='m-ant-btn'
|
||||||
|
size={'small'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTimeStatus(!timeStatus)
|
setTimeStatus(!timeStatus)
|
||||||
}}
|
}}
|
||||||
|
|
@ -145,8 +139,8 @@ const UserVideo: React.FC = () => {
|
||||||
{
|
{
|
||||||
userList.map((item: any, index: number) => {
|
userList.map((item: any, index: number) => {
|
||||||
return <div className={styles.userVideoContentListItem} key={index}>
|
return <div className={styles.userVideoContentListItem} key={index}>
|
||||||
<div className={styles.userVideoContentListItemVideo} id={`video-${item.uid}`}>
|
<div className={styles.userVideoContentListItemVideo} id={`video-${item.screenShareId}`}>
|
||||||
|
<div>{item.userName}{item.isRoomManager ? '(发言中)' : ''}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const role = {
|
||||||
|
ID: ['1', '3']
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,21 @@
|
||||||
import styles from '@/page/Home/Index/index.module.scss'
|
import styles from '@/page/Home/Index/index.module.scss'
|
||||||
import { useEffect, useState, useRef } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
import Operation from '@/components/Operation';
|
import Operation from '@/components/Operation';
|
||||||
import { Button, Input, Modal, Pagination, Empty, message } from "antd";
|
import { Button, Input, Modal, Pagination, Empty, message, Popover, Popconfirm, DatePicker } from "antd";
|
||||||
import { GetRoom, PostRomm, GetCheckoutRoomNum, GetRoomRtcToken } from '@/api/Home/Index';
|
import { GetRoom, PostRoom, GetCheckoutRoomNum, GetRoomRtcToken, DeleteRoom, GetRecord } from '@/api/Home/Index';
|
||||||
import ImageUrl from '@/utils/package/imageUrl'
|
import ImageUrl from '@/utils/package/imageUrl'
|
||||||
import { ReloadOutlined } from '@ant-design/icons';
|
import { ExclamationCircleFilled, ReloadOutlined } from '@ant-design/icons';
|
||||||
import JoinSetting from '@/components/JoinSetting';
|
import JoinSetting from '@/components/JoinSetting';
|
||||||
import { storage } from '@/utils';
|
import { storage } from '@/utils';
|
||||||
import { PostRefresh } from '@/api/Login';
|
import { PostRefresh } from '@/api/Login';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { role } from '@/config/role';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import StupWizard from '@/components/StupWizard';
|
||||||
|
const fs = require('fs').promises;
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
const { confirm } = Modal;
|
||||||
const Index: React.FC = () => {
|
const Index: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [list, setList] = useState({
|
const [list, setList] = useState({
|
||||||
|
|
@ -18,12 +25,15 @@ const Index: React.FC = () => {
|
||||||
pageSize: 12,
|
pageSize: 12,
|
||||||
})
|
})
|
||||||
const [createRoomModal, setCreateRoomModal] = useState(false)
|
const [createRoomModal, setCreateRoomModal] = useState(false)
|
||||||
|
const [timeSelectModal, setTimeSelectModal] = useState(false)
|
||||||
const [createRoomFrom, setCreateRoomFrom] = useState<{ roomName: string, roomNum: string }>({
|
const [createRoomFrom, setCreateRoomFrom] = useState<{ roomName: string, roomNum: string }>({
|
||||||
roomName: "",
|
roomName: "",
|
||||||
roomNum: ""
|
roomNum: ""
|
||||||
})
|
})
|
||||||
const joinSettingRef = useRef<any>();
|
const joinSettingRef = useRef<any>();
|
||||||
|
const stupWizardRef = useRef<any>();
|
||||||
const [user, setUser] = useState<any>({});
|
const [user, setUser] = useState<any>({});
|
||||||
|
const [currentRoomInfo, setCurrentRoomInfo] = useState<any>({});
|
||||||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUser(userInfo)
|
setUser(userInfo)
|
||||||
|
|
@ -70,7 +80,7 @@ const Index: React.FC = () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => {
|
const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => {
|
||||||
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + '1')]).then(res => {
|
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + 'a')]).then(res => {
|
||||||
if (res[0].code === 200 && res[1].code === 200) {
|
if (res[0].code === 200 && res[1].code === 200) {
|
||||||
callBack({
|
callBack({
|
||||||
token: res[0].data,
|
token: res[0].data,
|
||||||
|
|
@ -83,10 +93,59 @@ const Index: React.FC = () => {
|
||||||
await PostRefresh(user.refresh_token).then(res => {
|
await PostRefresh(user.refresh_token).then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
storage.setItem('user', JSON.stringify(res.data))
|
storage.setItem('user', JSON.stringify(res.data))
|
||||||
|
storage.setItem('userLogin', true)
|
||||||
callBack(res.data)
|
callBack(res.data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeOpen = (index: number, bool: boolean): void => {
|
||||||
|
const newList = [...list.data] as any;
|
||||||
|
newList[index].open = bool
|
||||||
|
setList({
|
||||||
|
...list,
|
||||||
|
data: newList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileUpLoad = async (data: { url: string, content: string, fileName: string }): Promise<void> => {
|
||||||
|
const setting = await JSON.parse(storage.getItem('setting') as string)
|
||||||
|
try {
|
||||||
|
const response = await fetch(data.url);
|
||||||
|
const arrayBuffer = await response.arrayBuffer();
|
||||||
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
await fs.writeFile(`${setting.shareFilesPath}\\${data.fileName}`, buffer, {});
|
||||||
|
confirm({
|
||||||
|
title: '提示',
|
||||||
|
icon: <ExclamationCircleFilled />,
|
||||||
|
content: data.content,
|
||||||
|
centered: true,
|
||||||
|
okText: '打开文件夹',
|
||||||
|
cancelText: '关闭',
|
||||||
|
async onOk() {
|
||||||
|
await fs.access(setting.shareFilesPath, fs.constants.F_OK);
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
exec(`explorer "${setting.shareFilesPath}"`);
|
||||||
|
} else if (process.platform === 'darwin') {
|
||||||
|
exec(`open "${setting.shareFilesPath}"`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.code === 'ENOENT') {
|
||||||
|
message.error({
|
||||||
|
content: <div>文件夹不存在 <span style={{ color: '#606fc7', cursor: 'pointer' }} onClick={() => {
|
||||||
|
stupWizardRef.current.changeModal(4)
|
||||||
|
}}>前往设置</span></div>
|
||||||
|
})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
message.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.index}>
|
<div className={styles.index}>
|
||||||
|
|
@ -147,12 +206,52 @@ const Index: React.FC = () => {
|
||||||
<img src={ImageUrl.icon10} alt="" />
|
<img src={ImageUrl.icon10} alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<Popover
|
||||||
{/* <Button type="primary" danger>设置</Button> */}
|
content={
|
||||||
|
<div className='meetingContentFooterPopover'>
|
||||||
|
<Popconfirm
|
||||||
|
title="提示"
|
||||||
|
description={`确定删除该会议吗`}
|
||||||
|
onConfirm={async () => {
|
||||||
|
DeleteRoom(item.id).then((res) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
message.success('删除成功')
|
||||||
|
changeOpen(index, false)
|
||||||
|
getRoomList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
onCancel={() => {
|
||||||
|
changeOpen(index, false)
|
||||||
|
}}
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<div>删除会议室</div>
|
||||||
|
</Popconfirm>
|
||||||
|
<div onClick={() => {
|
||||||
|
changeOpen(index, false)
|
||||||
|
setTimeSelectModal(true)
|
||||||
|
}}>导出参会记录</div>
|
||||||
|
<div onClick={() => {
|
||||||
|
changeOpen(index, false)
|
||||||
|
}}>取消</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
title=""
|
||||||
|
trigger="click"
|
||||||
|
open={item.open}
|
||||||
|
onOpenChange={() => {
|
||||||
|
setCurrentRoomInfo(list.data[index])
|
||||||
|
changeOpen(index, true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button type="primary" danger>更多</Button>
|
||||||
|
</Popover>
|
||||||
<Button type="primary"
|
<Button type="primary"
|
||||||
iconPosition={'end'}
|
iconPosition={'end'}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (userInfo.roleId === '1') {
|
if (role.ID.includes(userInfo.roleId)) {
|
||||||
joinSettingRef.current.changeModal(item.roomNum)
|
joinSettingRef.current.changeModal(item.roomNum)
|
||||||
} else {
|
} else {
|
||||||
postRefresh(() => {
|
postRefresh(() => {
|
||||||
|
|
@ -267,7 +366,7 @@ const Index: React.FC = () => {
|
||||||
if (bool) {
|
if (bool) {
|
||||||
message.error('房间号已存在!')
|
message.error('房间号已存在!')
|
||||||
} else {
|
} else {
|
||||||
PostRomm(createRoomFrom).then(res => {
|
PostRoom(createRoomFrom).then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
message.success('创建成功!')
|
message.success('创建成功!')
|
||||||
setCreateRoomModal(false)
|
setCreateRoomModal(false)
|
||||||
|
|
@ -280,7 +379,32 @@ const Index: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<Modal title="选择时间段" destroyOnClose={true} open={timeSelectModal} footer={null} onCancel={() => setTimeSelectModal(false)} centered width={'400px'}>
|
||||||
|
<div>
|
||||||
|
<RangePicker
|
||||||
|
showTime={{ format: 'YYYY-MM-DD HH:mm:ss' }}
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
onChange={(_value, dateString) => {
|
||||||
|
const setting = JSON.parse(storage.getItem('setting') as string)
|
||||||
|
if (dateString.length === 2) {
|
||||||
|
GetRecord(dayjs(dateString[0]).unix(), dayjs(dateString[1]).unix(), currentRoomInfo.roomNum).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
const fileName = res.data.split('/').pop().split('?')[0];
|
||||||
|
fileUpLoad({
|
||||||
|
url: res.data,
|
||||||
|
content: `下载参会记录成功!文件已保存至:${setting.shareFilesPath}`,
|
||||||
|
fileName
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setTimeSelectModal(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
<JoinSetting ref={joinSettingRef} />
|
<JoinSetting ref={joinSettingRef} />
|
||||||
|
<StupWizard ref={stupWizardRef} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,14 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
>button {
|
||||||
|
margin-right: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
.userBtnsDel {
|
.userBtnsDel {
|
||||||
background-color: #3A1457;
|
background-color: #3A1457;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
color: white;
|
color: white;
|
||||||
margin-left: 22px;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: lighten(#3A1457, 5%) !important;
|
background-color: lighten(#3A1457, 5%) !important;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
import styles from '@/page/Home/User/index.module.scss'
|
import styles from '@/page/Home/User/index.module.scss'
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
import Operation from '@/components/Operation';
|
import Operation from '@/components/Operation';
|
||||||
import { Button, Input, Table, Pagination, Modal, message, Select } from "antd";
|
import { Button, Input, Table, Pagination, Modal, message, Select } from "antd";
|
||||||
import { SearchOutlined } from '@ant-design/icons';
|
import { ExclamationCircleFilled, SearchOutlined } from '@ant-design/icons';
|
||||||
import { GetUserList, PostUser, PutUser, DeleteUser, PutUserPwd, GetRoleDpList } from '@/api/Home/User';
|
import { GetUserList, PostUser, PutUser, DeleteUser, PutUserPwd, GetRoleDpList, PostUserImport } from '@/api/Home/User';
|
||||||
import * as CryptoJS from 'crypto-js';
|
import * as CryptoJS from 'crypto-js';
|
||||||
import ImageUrl from '@/utils/package/imageUrl';
|
import ImageUrl from '@/utils/package/imageUrl';
|
||||||
|
import { storage } from '@/utils';
|
||||||
|
import StupWizard from '@/components/StupWizard';
|
||||||
const { Column } = Table
|
const { Column } = Table
|
||||||
|
const { confirm } = Modal;
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const fs = require('fs').promises;
|
||||||
const User: React.FC = () => {
|
const User: React.FC = () => {
|
||||||
|
const stupWizardRef = useRef<any>();
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
const [isCreateUser, setIsCreateUser] = useState(false);
|
const [isCreateUser, setIsCreateUser] = useState(false);
|
||||||
const [list, setList] = useState({
|
const [list, setList] = useState({
|
||||||
|
|
@ -27,6 +33,7 @@ const User: React.FC = () => {
|
||||||
UserName: ""
|
UserName: ""
|
||||||
})
|
})
|
||||||
const [changeUserPawModal, setChangeUserPawModal] = useState(false)
|
const [changeUserPawModal, setChangeUserPawModal] = useState(false)
|
||||||
|
const [changeImportModal, setChangeImportModal] = useState(false)
|
||||||
const [changeUserPawFrom, setChangeUserPawFrom] = useState({
|
const [changeUserPawFrom, setChangeUserPawFrom] = useState({
|
||||||
Pwd: "",
|
Pwd: "",
|
||||||
newPwd: '',
|
newPwd: '',
|
||||||
|
|
@ -72,6 +79,46 @@ const User: React.FC = () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const fileUpLoad = async (data: { url: string, content: string, fileName: string }): Promise<void> => {
|
||||||
|
const setting = await JSON.parse(storage.getItem('setting') as string)
|
||||||
|
try {
|
||||||
|
const response = await fetch(data.url);
|
||||||
|
const arrayBuffer = await response.arrayBuffer();
|
||||||
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
await fs.writeFile(`${setting.shareFilesPath}\\${data.fileName}`, buffer, {});
|
||||||
|
setChangeImportModal(false)
|
||||||
|
confirm({
|
||||||
|
title: '提示',
|
||||||
|
icon: <ExclamationCircleFilled />,
|
||||||
|
content: data.content,
|
||||||
|
centered: true,
|
||||||
|
okText: '打开文件夹',
|
||||||
|
cancelText: '关闭',
|
||||||
|
async onOk() {
|
||||||
|
await fs.access(setting.shareFilesPath, fs.constants.F_OK);
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
exec(`explorer "${setting.shareFilesPath}"`);
|
||||||
|
} else if (process.platform === 'darwin') {
|
||||||
|
exec(`open "${setting.shareFilesPath}"`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.code === 'ENOENT') {
|
||||||
|
message.error({
|
||||||
|
content: <div>文件夹不存在 <span style={{ color: '#606fc7', cursor: 'pointer' }} onClick={() => {
|
||||||
|
stupWizardRef.current.changeModal(4)
|
||||||
|
}}>前往设置</span></div>
|
||||||
|
})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
message.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.user}>
|
<div className={styles.user}>
|
||||||
|
|
@ -100,6 +147,13 @@ const User: React.FC = () => {
|
||||||
className='m-ant-btn'>
|
className='m-ant-btn'>
|
||||||
添加用户
|
添加用户
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
setChangeImportModal(true)
|
||||||
|
}}
|
||||||
|
className='m-ant-btn'>
|
||||||
|
批量导入用户
|
||||||
|
</Button>
|
||||||
<Button type="primary"
|
<Button type="primary"
|
||||||
icon={<img src={ImageUrl.icon21} alt="" />}
|
icon={<img src={ImageUrl.icon21} alt="" />}
|
||||||
className={styles.userBtnsDel}
|
className={styles.userBtnsDel}
|
||||||
|
|
@ -221,17 +275,14 @@ const User: React.FC = () => {
|
||||||
<Input
|
<Input
|
||||||
style={{ flexGrow: 1 }}
|
style={{ flexGrow: 1 }}
|
||||||
placeholder="请输入账号"
|
placeholder="请输入账号"
|
||||||
maxLength={11}
|
maxLength={30}
|
||||||
showCount={true}
|
showCount={true}
|
||||||
value={addUserFrom.Account}
|
value={addUserFrom.Account}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const regex = /^[0-9]*$/;
|
setAddUserFrom({
|
||||||
if (regex.test(e.target.value)) {
|
...addUserFrom,
|
||||||
setAddUserFrom({
|
Account: e.target.value,
|
||||||
...addUserFrom,
|
});
|
||||||
Account: e.target.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -400,6 +451,69 @@ const User: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<Modal title='批量导入用户' open={changeImportModal} onCancel={() => setChangeImportModal(false)} footer={null} centered width={'300px'}>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<Button type="primary" className='m-ant-btn' style={{ width: '100%', marginBottom: '10px' }}
|
||||||
|
onClick={async () => {
|
||||||
|
const setting = await JSON.parse(storage.getItem('setting') as string)
|
||||||
|
await fileUpLoad({
|
||||||
|
url: 'https://wgshare.oss-cn-chengdu.aliyuncs.com/%E7%94%A8%E6%88%B7%E6%89%B9%E9%87%8F%E5%AF%BC%E5%85%A5%E6%A8%A1%E6%9D%BF%E8%A1%A8.xlsx',
|
||||||
|
content: `下载导入模板成功!文件已保存至:${setting.shareFilesPath}`,
|
||||||
|
fileName: `用户批量导入模板表_${+new Date()}.xlsx`
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
下载导入模版
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button type="primary" className='m-ant-btn' style={{ width: '100%' }}
|
||||||
|
onClick={() => {
|
||||||
|
const file = document.createElement("input") as any;
|
||||||
|
file.type = "file";
|
||||||
|
file.accept = ".xls,.xlsx";
|
||||||
|
file.onchange = async () => {
|
||||||
|
const setting = await JSON.parse(storage.getItem('setting') as string)
|
||||||
|
const fileInfo = file.files[0];
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", fileInfo);
|
||||||
|
await PostUserImport(formData).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
if (res.data.item1) {
|
||||||
|
if (list.pageIndex === 1) {
|
||||||
|
getUserList()
|
||||||
|
} else {
|
||||||
|
setList({
|
||||||
|
...list,
|
||||||
|
pageIndex: 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
message.success(res.data.item3)
|
||||||
|
} else {
|
||||||
|
if (res.data.item2) {
|
||||||
|
const fileName = res.data.item2.split('/').pop().split('?')[0];
|
||||||
|
fileUpLoad({
|
||||||
|
url: res.data.item2,
|
||||||
|
content: `导入模板失败!失败文件已保存至:${setting.shareFilesPath}`,
|
||||||
|
fileName
|
||||||
|
})
|
||||||
|
}
|
||||||
|
message.error(res.data.item3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setChangeImportModal(false)
|
||||||
|
};
|
||||||
|
file.click();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
选择导入文件
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
<StupWizard ref={stupWizardRef} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,8 +139,8 @@ const Home: React.FC = () => {
|
||||||
title="提示"
|
title="提示"
|
||||||
description="确认退出吗?"
|
description="确认退出吗?"
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
navigate('/login')
|
|
||||||
storage.removeItem('user')
|
storage.removeItem('user')
|
||||||
|
storage.setItem('userLogin', false)
|
||||||
}}
|
}}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -140,10 +140,13 @@ const Login: React.FC = () => {
|
||||||
optionsValue: operation.optionsValue,
|
optionsValue: operation.optionsValue,
|
||||||
}))
|
}))
|
||||||
storage.setItem('user', JSON.stringify(res.data))
|
storage.setItem('user', JSON.stringify(res.data))
|
||||||
|
storage.setItem('userLogin', true)
|
||||||
try {
|
try {
|
||||||
window.electron.setMainWindowSize({
|
window.electron.getWindowSize().then((res: any) => {
|
||||||
width: 1200,
|
window.electron.setMainWindowSize({
|
||||||
height: 800,
|
width: Math.ceil(res.width / 1.5),
|
||||||
|
height: Math.ceil(res.height / 1.3),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,30 @@
|
||||||
|
|
||||||
>img {
|
>img {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
margin-right: 4px;
|
}
|
||||||
|
|
||||||
|
>label {
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
>img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>div {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 0%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: bottom center;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>span {
|
>span {
|
||||||
|
|
@ -160,6 +183,7 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.standardModeIcon {
|
.standardModeIcon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
@ -209,6 +233,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 自由者模式
|
// 自由者模式
|
||||||
.meetingContentBodyLeftFreedomMode {
|
.meetingContentBodyLeftFreedomMode {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -282,7 +307,7 @@
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: calc(100% - 160px) !important;
|
height: calc(100% - 170px) !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
@ -343,6 +368,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.meetingContentSwiperCardFullScreenIcon {
|
.meetingContentSwiperCardFullScreenIcon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
|
|
@ -394,33 +420,14 @@
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meetingContentSwiperCardBorder {
|
|
||||||
position: absolute;
|
|
||||||
height: 3px;
|
|
||||||
width: calc(100% - 20px);
|
|
||||||
background-color: #2C2C2C;
|
|
||||||
overflow: hidden;
|
|
||||||
left: 10px;
|
|
||||||
z-index: 1;
|
|
||||||
bottom: 9px;
|
|
||||||
|
|
||||||
>div {
|
|
||||||
background-color: #5575F2;
|
|
||||||
position: absolute;
|
|
||||||
height: 3px;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.meetingContentBodyRight {
|
.meetingContentBodyRight {
|
||||||
width: 300px;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.meetingUserList {
|
.meetingUserList {
|
||||||
|
width: 300px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 10px 0 20px;
|
padding: 10px 0 20px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
@ -535,6 +542,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.meetingUserChat {
|
.meetingUserChat {
|
||||||
|
width: 300px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: #16191E;
|
background-color: #16191E;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -653,6 +661,39 @@
|
||||||
border-top: 1px solid #23272E;
|
border-top: 1px solid #23272E;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.meetingUserVideoList {
|
||||||
|
padding: 10px 10px 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #16191E;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.meetingUserVideoListTitle {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
>span {
|
||||||
|
color: #EEEEEE;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>img {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.meetingUserVideoListContent {
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 0px;
|
||||||
|
overflow-y: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -684,7 +725,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
>img {
|
>img {
|
||||||
height: 30px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
>span {
|
>span {
|
||||||
|
|
@ -705,6 +746,31 @@
|
||||||
right: -10px;
|
right: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
>label {
|
||||||
|
height: 50px;
|
||||||
|
height: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
>img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>div {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 0%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: bottom center;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #161A29;
|
background-color: #161A29;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// electron-env.d.ts
|
// electron-env.d.ts
|
||||||
export interface IElectronAPI {
|
export interface IElectronAPI {
|
||||||
setMainWindowSize: (config: any) => void;
|
setMainWindowSize: (config: any) => void;
|
||||||
|
getWindowSize: () => any;
|
||||||
setViewStatus: (status: 'quit' | 'maximize' | 'minimize' | 'unmaximize' | 'hide') => void;
|
setViewStatus: (status: 'quit' | 'maximize' | 'minimize' | 'unmaximize' | 'hide') => void;
|
||||||
getIsMaximized: () => Promise<boolean>;
|
getIsMaximized: () => Promise<boolean>;
|
||||||
setWriteText: (text: string) => void;
|
setWriteText: (text: string) => void;
|
||||||
|
|
@ -15,9 +16,6 @@ export interface IElectronAPI {
|
||||||
downFile: (callBack: Function) => void;
|
downFile: (callBack: Function) => void;
|
||||||
quitAndInstall: (callBack: Function) => void;
|
quitAndInstall: (callBack: Function) => void;
|
||||||
getVersion: () => Promise<string>;
|
getVersion: () => Promise<string>;
|
||||||
oepnWindow: (data: any) => any;
|
|
||||||
|
|
||||||
closeMonitorWindow: () => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,16 @@ import {
|
||||||
VideoViewSetupMode,
|
VideoViewSetupMode,
|
||||||
ScreenCaptureSourceType,
|
ScreenCaptureSourceType,
|
||||||
RenderModeType,
|
RenderModeType,
|
||||||
ChannelProfileType
|
ChannelProfileType,
|
||||||
|
AudioAinsMode,
|
||||||
|
SimulcastStreamMode,
|
||||||
|
VideoStreamType
|
||||||
} from "agora-electron-sdk";
|
} from "agora-electron-sdk";
|
||||||
import { GetRoomRtcToken } from "@/api/Home/Index";
|
import { GetRoomRtcToken, GetAgoraConf } from "@/api/Home/Index";
|
||||||
import { storage } from '@/utils';
|
import { storage } from '@/utils';
|
||||||
|
import { role } from "@/config/role";
|
||||||
const option: any = {
|
const option: any = {
|
||||||
appId: 'dcfc466a6ecb4a1f972630065dfb1e75',
|
appId: '',
|
||||||
token: '',
|
token: '',
|
||||||
tokenA: '',
|
tokenA: '',
|
||||||
channelId: '',
|
channelId: '',
|
||||||
|
|
@ -21,11 +25,14 @@ let rtcEngine: any = '';
|
||||||
export const agora = {
|
export const agora = {
|
||||||
// 初始化
|
// 初始化
|
||||||
init: async (bool: boolean = false) => {
|
init: async (bool: boolean = false) => {
|
||||||
rtcEngine = createAgoraRtcEngine();
|
const { data } = await GetAgoraConf();
|
||||||
await rtcEngine.initialize({
|
if (data) {
|
||||||
appId: option.appId,
|
rtcEngine = createAgoraRtcEngine();
|
||||||
});
|
await rtcEngine.initialize({
|
||||||
await agora.setDeviceManager(bool)
|
appId: data,
|
||||||
|
});
|
||||||
|
await agora.setDeviceManager(bool)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// 获取rtcEngine
|
// 获取rtcEngine
|
||||||
getRtcEngine: () => {
|
getRtcEngine: () => {
|
||||||
|
|
@ -92,6 +99,7 @@ export const agora = {
|
||||||
if (setting.playBackVolume) agora.setPlaybackDeviceVolume(setting.playBackVolume) // 设置播放设备音量
|
if (setting.playBackVolume) agora.setPlaybackDeviceVolume(setting.playBackVolume) // 设置播放设备音量
|
||||||
if (setting.ecordingDeviceId) agora.setRecordingDevice(setting.ecordingDeviceId) // 设置音频采集设备
|
if (setting.ecordingDeviceId) agora.setRecordingDevice(setting.ecordingDeviceId) // 设置音频采集设备
|
||||||
if (setting.ecordingVolume) agora.setRecordingDeviceVolume(setting.ecordingVolume) // 设置音频设备音量
|
if (setting.ecordingVolume) agora.setRecordingDeviceVolume(setting.ecordingVolume) // 设置音频设备音量
|
||||||
|
if (setting.isAINoiseReduction) agora.setAINSMode(setting.isAINoiseReduction, setting.aINoiseReduction) // 设置ai降噪
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
},
|
},
|
||||||
|
|
@ -163,6 +171,20 @@ export const agora = {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
setupRemoteVideoEx: async (item: any) => {
|
||||||
|
if (item.view?.childNodes.length === 1) {
|
||||||
|
await rtcEngine.setupRemoteVideoEx(
|
||||||
|
{
|
||||||
|
renderMode: agora.getRrenderMode(item.uid),
|
||||||
|
sourceType: VideoSourceType.VideoSourceRemote,
|
||||||
|
uid: item.uid,
|
||||||
|
view: item.view,
|
||||||
|
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
|
||||||
|
},
|
||||||
|
{ channelId: item.channelId },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
// 退出
|
// 退出
|
||||||
setupRemoteVideo: async (item: any) => {
|
setupRemoteVideo: async (item: any) => {
|
||||||
await rtcEngine.setupRemoteVideo(
|
await rtcEngine.setupRemoteVideo(
|
||||||
|
|
@ -193,6 +215,17 @@ export const agora = {
|
||||||
joinChannel: async () => {
|
joinChannel: async () => {
|
||||||
await rtcEngine.enableAudioVolumeIndication(100, 1, true)
|
await rtcEngine.enableAudioVolumeIndication(100, 1, true)
|
||||||
await rtcEngine.joinChannel(option.token, option.channelId, option.uid);
|
await rtcEngine.joinChannel(option.token, option.channelId, option.uid);
|
||||||
|
await rtcEngine.setDualStreamModeEx(
|
||||||
|
SimulcastStreamMode.EnableSimulcastStream,
|
||||||
|
{
|
||||||
|
dimensions: {
|
||||||
|
width: 320,
|
||||||
|
height: 180
|
||||||
|
},
|
||||||
|
framerate: 5,
|
||||||
|
},
|
||||||
|
{ channelId: option.channelId, localUid: Number(option.uid) }
|
||||||
|
);
|
||||||
},
|
},
|
||||||
// 更新频道配置
|
// 更新频道配置
|
||||||
updateChannelMediaOptions: async (bool: boolean) => {
|
updateChannelMediaOptions: async (bool: boolean) => {
|
||||||
|
|
@ -205,6 +238,14 @@ export const agora = {
|
||||||
publishScreenTrack: false,//设置是否发布屏幕采集的视频
|
publishScreenTrack: false,//设置是否发布屏幕采集的视频
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
// 设置接收大小流
|
||||||
|
setRemoteVideoStreamType: async (uid: number, type: VideoStreamType, bool: boolean) => {
|
||||||
|
await rtcEngine.setRemoteVideoStreamTypeEx(
|
||||||
|
Number(uid),
|
||||||
|
type,
|
||||||
|
bool ? { channelId: option.channelId, localUid: Number(option.uid) } : { channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) }
|
||||||
|
)
|
||||||
|
},
|
||||||
// 共享屏幕单独用户
|
// 共享屏幕单独用户
|
||||||
joinChannelEx: async (uid: any) => {
|
joinChannelEx: async (uid: any) => {
|
||||||
await agora.leaveChannelEx(uid)
|
await agora.leaveChannelEx(uid)
|
||||||
|
|
@ -222,26 +263,53 @@ export const agora = {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
// 所有用户加入的第二个房间
|
// 所有用户加入的第二个房间
|
||||||
allJoinChannelEx: async () => {
|
allJoinChannelEx: async (bool: boolean = false) => {
|
||||||
|
const user = await JSON.parse(storage.getItem('user') as string)
|
||||||
|
await agora.startCameraCapture(true)
|
||||||
await rtcEngine.joinChannelEx(
|
await rtcEngine.joinChannelEx(
|
||||||
option.tokenA,
|
option.tokenA,
|
||||||
{ channelId: option.channelId + '1', localUid: Number('1' + option.screenShareId) },
|
{ channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) },
|
||||||
{}
|
{
|
||||||
|
clientRoleType: bool ? ClientRoleType.ClientRoleAudience : ClientRoleType.ClientRoleBroadcaster, //用户角色 ClientRoleBroadcaster 主播 ClientRoleAudience 观众
|
||||||
|
autoSubscribeAudio: false,//设置是否自动订阅所有音频流
|
||||||
|
autoSubscribeVideo: role.ID.includes(user.roleId) ? true : false,//设置是否自动订阅所有视频流
|
||||||
|
publishMicrophoneTrack: false,//设置是否发布麦克风采集到的音频
|
||||||
|
publishCameraTrack: true,//设置是否发布摄像头采集的视频
|
||||||
|
publishScreenTrack: false,//设置是否发布屏幕采集的视频
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await rtcEngine.setDualStreamModeEx(
|
||||||
|
SimulcastStreamMode.EnableSimulcastStream,
|
||||||
|
{
|
||||||
|
dimensions: {
|
||||||
|
width: 320,
|
||||||
|
height: 180
|
||||||
|
},
|
||||||
|
framerate: 5,
|
||||||
|
},
|
||||||
|
{ channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) }
|
||||||
);
|
);
|
||||||
await agora.updateChannelMediaOptionsEx(false)
|
|
||||||
},
|
},
|
||||||
updateChannelMediaOptionsEx: async (bool: boolean) => {
|
// 退出第二个房间
|
||||||
rtcEngine.updateChannelMediaOptionsEx({
|
allLeaveChannelEx: async () => {
|
||||||
clientRoleType: bool ? ClientRoleType.ClientRoleBroadcaster : ClientRoleType.ClientRoleAudience, //用户角色 ClientRoleBroadcaster 主播 ClientRoleAudience 观众
|
await agora.stopCameraCapture();
|
||||||
autoSubscribeAudio: false,//设置是否自动订阅所有音频流
|
await rtcEngine.leaveChannelEx({ channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) })
|
||||||
autoSubscribeVideo: true,//设置是否自动订阅所有视频流
|
},
|
||||||
publishMicrophoneTrack: true,//设置是否发布麦克风采集到的音频
|
// 停止/恢复接收指定的视频流。
|
||||||
publishCameraTrack: true,//设置是否发布摄像头采集的视频
|
muteRemoteVideoStreamEx: async (uid: number, mute: boolean) => {
|
||||||
publishScreenTrack: false,//设置是否发布屏幕采集的视频
|
await rtcEngine.muteRemoteVideoStreamEx(uid, mute, { channelId: option.channelId, localUid: Number(option.uid) })
|
||||||
}, {
|
},
|
||||||
channelId: option.channelId + '1',
|
// 取消或恢复订阅指定远端用户的音频流
|
||||||
localUid: Number('1' + option.screenShareId)
|
muteRemoteVideoStream: async (uid: number, mute: boolean) => {
|
||||||
})
|
rtcEngine.muteRemoteVideoStream(uid, mute)
|
||||||
|
},
|
||||||
|
// 销毁视频渲染dom
|
||||||
|
destroyRendererByConfig: async (uid: number) => {
|
||||||
|
await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, option.channelId + 'a', uid);
|
||||||
|
},
|
||||||
|
// ai降噪
|
||||||
|
setAINSMode: async (enabled: boolean, mode: AudioAinsMode) => {
|
||||||
|
rtcEngine.setAINSMode(enabled, mode)
|
||||||
},
|
},
|
||||||
// 离开共享屏幕频道
|
// 离开共享屏幕频道
|
||||||
leaveChannelEx: async (uid: any) => {
|
leaveChannelEx: async (uid: any) => {
|
||||||
|
|
@ -253,19 +321,27 @@ export const agora = {
|
||||||
rtcEngine.enableLoopbackRecording(false)
|
rtcEngine.enableLoopbackRecording(false)
|
||||||
},
|
},
|
||||||
// 取消或恢复发布本地音频流
|
// 取消或恢复发布本地音频流
|
||||||
muteLocalAudioStream: async (mute: any) => {
|
muteLocalAudioStream: async (data: any, mute: any) => {
|
||||||
await rtcEngine.muteLocalAudioStream(mute)
|
// await rtcEngine.muteLocalAudioStream(mute)
|
||||||
|
await rtcEngine.updateChannelMediaOptions({
|
||||||
|
clientRoleType: data ? ClientRoleType.ClientRoleBroadcaster : ClientRoleType.ClientRoleAudience, //用户角色 ClientRoleBroadcaster 主播 ClientRoleAudience 观众
|
||||||
|
autoSubscribeAudio: true,//设置是否自动订阅所有音频流
|
||||||
|
autoSubscribeVideo: true,//设置是否自动订阅所有视频流
|
||||||
|
publishMicrophoneTrack: mute,//设置是否发布麦克风采集到的音频
|
||||||
|
publishCameraTrack: true,//设置是否发布摄像头采集的视频
|
||||||
|
publishScreenTrack: false,//设置是否发布屏幕采集的视频
|
||||||
|
})
|
||||||
},
|
},
|
||||||
// 取消或恢复发布本地视频流
|
// 取消或恢复发布本地视频流
|
||||||
muteLocalVideoStream: async (mute: any) => {
|
muteLocalVideoStream: async (mute: any) => {
|
||||||
await rtcEngine.muteLocalVideoStream(mute)
|
await rtcEngine.muteLocalVideoStream(mute)
|
||||||
},
|
},
|
||||||
// 摄像头采集
|
// 摄像头采集
|
||||||
startCameraCapture: async () => {
|
startCameraCapture: async (bool: boolean = false) => {
|
||||||
await rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, {
|
await rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, {
|
||||||
format: {
|
format: {
|
||||||
width: 640,
|
width: bool ? 160 : 1280,
|
||||||
height: 360,
|
height: bool ? 160 : 720,
|
||||||
fps: 15,
|
fps: 15,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -282,13 +358,10 @@ export const agora = {
|
||||||
option.uid = Number(data.uid);
|
option.uid = Number(data.uid);
|
||||||
option.screenShareId = data.screenShareId;
|
option.screenShareId = data.screenShareId;
|
||||||
await agora.joinChannel()
|
await agora.joinChannel()
|
||||||
if (data.tokenA) {
|
|
||||||
await agora.allJoinChannelEx()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// 桌面捕获音频和视频的媒体源的信息
|
// 桌面捕获音频和视频的媒体源的信息
|
||||||
getDesktopCapturerVideo: async () => {
|
getDesktopCapturerVideo: async (thumbSize: any, iconSize: any, includeScreen: boolean) => {
|
||||||
return rtcEngine.getScreenCaptureSources({ width: 300, height: 300 }, { width: 300, height: 300 }, true);
|
return await rtcEngine.getScreenCaptureSources(thumbSize, iconSize, includeScreen).filter((item: any) => item.type === 1)
|
||||||
},
|
},
|
||||||
// 共享屏幕采集
|
// 共享屏幕采集
|
||||||
setDesktopCapturerVideo: async (targetSource: any, isComputerAudio: boolean, isFluencyPriority: boolean) => {
|
setDesktopCapturerVideo: async (targetSource: any, isComputerAudio: boolean, isFluencyPriority: boolean) => {
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ import icon47 from '@/assets/icon47.png'
|
||||||
import icon47Active from '@/assets/icon47-active.png'
|
import icon47Active from '@/assets/icon47-active.png'
|
||||||
import icon48 from '@/assets/icon48.png'
|
import icon48 from '@/assets/icon48.png'
|
||||||
import icon48Select from '@/assets/icon48-select.png'
|
import icon48Select from '@/assets/icon48-select.png'
|
||||||
|
import icon49 from '@/assets/icon49.png'
|
||||||
export default {
|
export default {
|
||||||
loading,
|
loading,
|
||||||
icon,
|
icon,
|
||||||
|
|
@ -142,5 +143,6 @@ export default {
|
||||||
icon47,
|
icon47,
|
||||||
icon47Active,
|
icon47Active,
|
||||||
icon48,
|
icon48,
|
||||||
icon48Select
|
icon48Select,
|
||||||
|
icon49,
|
||||||
}
|
}
|
||||||
|
|
@ -160,6 +160,27 @@ export const onSignalr = (callBack: Function) => {
|
||||||
watchUids
|
watchUids
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
// 设备列表
|
||||||
|
connection.on("DriverList", (callerUid: string) => {
|
||||||
|
callBack({
|
||||||
|
key: 'DriverList',
|
||||||
|
callerUid
|
||||||
|
})
|
||||||
|
});
|
||||||
|
// 设置设备
|
||||||
|
connection.on("SaveDriver", (driver: string) => {
|
||||||
|
callBack({
|
||||||
|
key: 'SaveDriver',
|
||||||
|
driver
|
||||||
|
})
|
||||||
|
});
|
||||||
|
// 显示设备列表
|
||||||
|
connection.on("ShowDriverList", (driversJsonString: string) => {
|
||||||
|
callBack({
|
||||||
|
key: 'ShowDriverList',
|
||||||
|
driversJsonString
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const offSignalr = () => {
|
export const offSignalr = () => {
|
||||||
|
|
@ -178,6 +199,9 @@ export const offSignalr = () => {
|
||||||
connection.off('ManagerRefresh');
|
connection.off('ManagerRefresh');
|
||||||
connection.off('ApplyToSpeak');
|
connection.off('ApplyToSpeak');
|
||||||
connection.off('Watch');
|
connection.off('Watch');
|
||||||
|
connection.off('DriverList');
|
||||||
|
connection.off('SetDriver');
|
||||||
|
connection.off('ShowDriverList');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const onInvoke = async (str: string, data: any) => {
|
export const onInvoke = async (str: string, data: any) => {
|
||||||
|
|
@ -189,6 +213,18 @@ export const onInvoke = async (str: string, data: any) => {
|
||||||
// 4:屏幕共享
|
// 4:屏幕共享
|
||||||
await connection.invoke(str, data.roomNum, data.type)
|
await connection.invoke(str, data.roomNum, data.type)
|
||||||
break;
|
break;
|
||||||
|
case 'getDrivers':
|
||||||
|
// 获取某个人的设备列表
|
||||||
|
await connection.invoke(str, data.uid)
|
||||||
|
break;
|
||||||
|
case 'sendDrivers':
|
||||||
|
// 发送设备列表给某个人
|
||||||
|
await connection.invoke(str, data.uid, data.driversJsonString)
|
||||||
|
break;
|
||||||
|
case 'setDrivers':
|
||||||
|
// 设置某个人的设备列表
|
||||||
|
await connection.invoke(str, data.uid, data.driversJsonString)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const onStop = async () => {
|
export const onStop = async () => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
class LocalStorage {
|
class LocalStorage {
|
||||||
private constructor() {}
|
private constructor() { }
|
||||||
|
|
||||||
private static instance: LocalStorage | null = null
|
private static instance: LocalStorage | null = null
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class Request {
|
||||||
message.error(resData.message)
|
message.error(resData.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (resData.code === 1403 || resData.code === 1000) {
|
if (resData.code === 1403) {
|
||||||
toLogin()
|
toLogin()
|
||||||
}
|
}
|
||||||
return resData
|
return resData
|
||||||
|
|
@ -113,7 +113,7 @@ class Request {
|
||||||
}
|
}
|
||||||
function toLogin() {
|
function toLogin() {
|
||||||
storage.removeItem('user')
|
storage.removeItem('user')
|
||||||
location.href = location.origin + '/#/login'
|
storage.setItem('userLogin', false)
|
||||||
}
|
}
|
||||||
function updatePostRefresh() {
|
function updatePostRefresh() {
|
||||||
let user = JSON.parse(storage.getItem('user') as string);
|
let user = JSON.parse(storage.getItem('user') as string);
|
||||||
|
|
@ -121,6 +121,7 @@ function updatePostRefresh() {
|
||||||
PostRefresh(user.refresh_token).then((res) => {
|
PostRefresh(user.refresh_token).then((res) => {
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
storage.setItem('user', JSON.stringify(res.data))
|
storage.setItem('user', JSON.stringify(res.data))
|
||||||
|
storage.setItem('userLogin', true)
|
||||||
} else {
|
} else {
|
||||||
toLogin()
|
toLogin()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -343,11 +343,16 @@ $pagination-hover-background-color: #5575F2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-radio-checked .ant-radio-inner {
|
.ant-radio-checked .ant-radio-inner {
|
||||||
border-color: $btn-background-color;
|
|
||||||
background-color: $btn-background-color;
|
background-color: $btn-background-color;
|
||||||
|
border-color: $btn-background-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:where(.css-dev-only-do-not-override-98ntnt).ant-radio-wrapper .ant-radio-disabled .ant-radio-inner {
|
||||||
|
background-color: #28282C;
|
||||||
|
border-color: #28282C;
|
||||||
|
}
|
||||||
|
|
||||||
// ant-notification
|
// ant-notification
|
||||||
.ant-notification {
|
.ant-notification {
|
||||||
.ant-notification-notice-wrapper {
|
.ant-notification-notice-wrapper {
|
||||||
|
|
@ -371,3 +376,8 @@ $pagination-hover-background-color: #5575F2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ant-message
|
||||||
|
.ant-message {
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
@ -60,7 +60,10 @@ export default defineConfig({
|
||||||
RenderModeType,
|
RenderModeType,
|
||||||
ScreenCaptureSourceType,
|
ScreenCaptureSourceType,
|
||||||
VideoSourceType,
|
VideoSourceType,
|
||||||
VideoViewSetupMode
|
VideoViewSetupMode,
|
||||||
|
AudioAinsMode,
|
||||||
|
SimulcastStreamMode,
|
||||||
|
VideoStreamType
|
||||||
} = require("agora-electron-sdk")
|
} = require("agora-electron-sdk")
|
||||||
export {
|
export {
|
||||||
createAgoraRtcEngine,
|
createAgoraRtcEngine,
|
||||||
|
|
@ -69,7 +72,10 @@ export default defineConfig({
|
||||||
RenderModeType,
|
RenderModeType,
|
||||||
ScreenCaptureSourceType,
|
ScreenCaptureSourceType,
|
||||||
VideoSourceType,
|
VideoSourceType,
|
||||||
VideoViewSetupMode
|
VideoViewSetupMode,
|
||||||
|
AudioAinsMode,
|
||||||
|
SimulcastStreamMode,
|
||||||
|
VideoStreamType
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
|
|
|
||||||