Compare commits
No commits in common. "3b03d6a8c36352e79d2a940e4c63472fb579be03" and "734dc7a41a911fc89aaa6ce0c59fd159a7bed9f3" have entirely different histories.
3b03d6a8c3
...
734dc7a41a
|
|
@ -1,5 +1,5 @@
|
|||
#基础API 绝对的
|
||||
VITE_BASE_URL_API = 'https://meeting-api.23544.com/pc'
|
||||
VITE_BASE_URL_API = 'http://192.168.2.9:5192'
|
||||
VITE_BASE_URL_DRAW_API = 'http://192.168.2.9:6555'
|
||||
#当前IP 相对的
|
||||
VITE_BASE_CURRENT_API = '.'
|
||||
|
|
|
|||
62
main.js
|
|
@ -16,6 +16,7 @@ const { autoUpdater, CancellationToken } = require('electron-updater');
|
|||
const cancellationToken = new CancellationToken()
|
||||
app.allowRendererProcessReuse = false;
|
||||
let mainWindow = null;
|
||||
let newWindow = null;
|
||||
let isMaximized = false;
|
||||
let env;
|
||||
|
||||
|
|
@ -129,9 +130,6 @@ app.on('ready', () => {
|
|||
}
|
||||
createWindow()
|
||||
updateHandle() // 检查更新
|
||||
setInterval(() => {
|
||||
updateHandle() // 每一小时检查更新
|
||||
}, 1000 * 60 * 60)
|
||||
createTray()
|
||||
// 获取当前脚本所在目录的绝对路径
|
||||
const currentDirectory = __dirname;
|
||||
|
|
@ -144,11 +142,11 @@ app.on('ready', () => {
|
|||
}
|
||||
// 监听f12打开控制台
|
||||
mainWindow.webContents.on('before-input-event', (event, input) => {
|
||||
// if (env === 'development') {
|
||||
if (input.key === 'F12') {
|
||||
mainWindow.webContents.openDevTools()
|
||||
if (env === 'development') {
|
||||
if (input.key === 'F12') {
|
||||
mainWindow.webContents.openDevTools()
|
||||
}
|
||||
}
|
||||
// }
|
||||
});
|
||||
// 监听移动
|
||||
mainWindow.on('move', () => {
|
||||
|
|
@ -245,12 +243,6 @@ 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) => {
|
||||
// 设置最小窗口尺寸
|
||||
|
|
@ -271,6 +263,46 @@ app.on('ready', () => {
|
|||
const y = Math.round((display.workArea.height - mainWindow.getSize()[1]) / 2);
|
||||
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事件触发后的操作自行编写
|
||||
|
|
@ -281,9 +313,9 @@ function updateHandle() {
|
|||
error: '检查更新出错',
|
||||
checking: '正在检查更新……',
|
||||
updateAva: '检测到新版本,正在下载……',
|
||||
updateNotAva: '已经是最新版本,不用更新'
|
||||
updateNotAva: '现在使用的就是最新版本,不用更新'
|
||||
}
|
||||
autoUpdater.setFeedURL('https://meeting-api.23544.com/meeting/update')
|
||||
autoUpdater.setFeedURL('https://update.23544.com/metting')
|
||||
autoUpdater.autoDownload = false // 不自动下载安装包
|
||||
autoUpdater.autoInstallOnAppQuit = false // 不自动安装
|
||||
autoUpdater.on('error', function (error) {
|
||||
|
|
|
|||
11
package.json
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"name": "WGShare.Metting",
|
||||
"private": true,
|
||||
"version": "0.1.14",
|
||||
"version": "0.0.1",
|
||||
"main": "main.js",
|
||||
"authors": "yj",
|
||||
"description": "智汇享",
|
||||
"description": "test",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"electron . --env=development\" \"cross-env BROWSER=none vite\"",
|
||||
"test": "concurrently \"electron . --env=test\" \"cross-env BROWSER=none vite\"",
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
"publish": [
|
||||
{
|
||||
"provider": "generic",
|
||||
"url": "https://meeting-api.23544.com/meeting/update"
|
||||
"url": "https://update.23544.com/metting"
|
||||
}
|
||||
],
|
||||
"files": [
|
||||
|
|
@ -77,7 +77,6 @@
|
|||
],
|
||||
"win": {
|
||||
"icon": "build/start.ico",
|
||||
"requestedExecutionLevel": "highestAvailable",
|
||||
"target": [
|
||||
{
|
||||
"target": "nsis",
|
||||
|
|
@ -100,9 +99,7 @@
|
|||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true,
|
||||
"deleteAppDataOnUninstall": true,
|
||||
"shortcutName": "智汇享",
|
||||
"allowElevation": true,
|
||||
"perMachine":true
|
||||
"shortcutName": "智汇享"
|
||||
}
|
||||
}
|
||||
}
|
||||
12
preload.js
|
|
@ -5,10 +5,6 @@ window.electron = {
|
|||
setMainWindowSize: (config) => {
|
||||
ipcRenderer.invoke('setMainWindowSize', { ...config })
|
||||
},
|
||||
// 获取窗口大小
|
||||
getWindowSize: (config) => {
|
||||
return ipcRenderer.invoke('getWindowSize')
|
||||
},
|
||||
// 设置窗口状态
|
||||
setViewStatus: (status) => {
|
||||
ipcRenderer.invoke('setViewStatus', status)
|
||||
|
|
@ -64,5 +60,13 @@ window.electron = {
|
|||
// 下载文件
|
||||
downFile: (callback) => {
|
||||
ipcRenderer.on('downFile', callback)
|
||||
},
|
||||
// 打开新窗口
|
||||
oepnWindow: (data) => {
|
||||
ipcRenderer.invoke('oepnWindow', data)
|
||||
},
|
||||
// 关闭会议监控窗口
|
||||
closeMonitorWindow: () => {
|
||||
ipcRenderer.invoke('closeMonitorWindow')
|
||||
}
|
||||
}
|
||||
|
|
|
|||
268
src/App.tsx
|
|
@ -17,6 +17,7 @@ import { PostLogin } from "@/api/Login";
|
|||
import { agora } from "@/utils/package/agora";
|
||||
import QuitTips from "@/components/QuitTips";
|
||||
import { GetLeave } from "@/api/Meeting";
|
||||
import UserVideo from "./page/UserVideo";
|
||||
import path from "path";
|
||||
const fs = require('fs').promises;
|
||||
const { exec } = require('child_process');
|
||||
|
|
@ -32,126 +33,128 @@ const App: React.FC = () => {
|
|||
});
|
||||
const [spinning, setSpinning] = useState(false);
|
||||
const [isState, setIsState] = useState(true);
|
||||
useEffect(() => {
|
||||
let userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
let loginInfo = JSON.parse(storage.getItem('login') as string)
|
||||
if (userInfo) {
|
||||
if (loginInfo && loginInfo.isAutoLogin) {
|
||||
PostLogin({
|
||||
account: loginInfo.account,
|
||||
pwd: CryptoJS.MD5(loginInfo.password).toString(CryptoJS.enc.Hex)
|
||||
}).then(async (res) => {
|
||||
if (res.code === 200) {
|
||||
storage.setItem('user', JSON.stringify(res.data))
|
||||
storage.setItem('userLogin', true)
|
||||
toSrc('/home')
|
||||
await startSignalr()
|
||||
} else {
|
||||
toSrc('/login')
|
||||
}
|
||||
})
|
||||
if (location.href.indexOf('/userVideo') === -1) {
|
||||
useEffect(() => {
|
||||
let userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
let loginInfo = JSON.parse(storage.getItem('login') as string)
|
||||
if (userInfo) {
|
||||
if (loginInfo && loginInfo.isAutoLogin) {
|
||||
PostLogin({
|
||||
account: loginInfo.account,
|
||||
pwd: CryptoJS.MD5(loginInfo.password).toString(CryptoJS.enc.Hex)
|
||||
}).then(async (res) => {
|
||||
if (res.code === 200) {
|
||||
storage.setItem('user', JSON.stringify(res.data))
|
||||
toSrc('/home')
|
||||
await startSignalr()
|
||||
} else {
|
||||
toSrc('/login')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
toSrc('/login')
|
||||
}
|
||||
} else {
|
||||
toSrc('/login')
|
||||
}
|
||||
} else {
|
||||
toSrc('/login')
|
||||
}
|
||||
window.addEventListener('resize', handleResize);
|
||||
window.addEventListener('online', handleNetworkChange);
|
||||
window.addEventListener('offline', handleNetworkChange);
|
||||
const originalSetItem = window.localStorage.setItem;
|
||||
window.localStorage.setItem = function (key, value) {
|
||||
originalSetItem.call(this, key, value);
|
||||
const event = new Event('customStorageChange') as any;
|
||||
event.key = key
|
||||
event.value = value
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
||||
window.removeEventListener('online', handleNetworkChange);
|
||||
window.removeEventListener('offline', handleNetworkChange);
|
||||
};
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
window.electron.downFile(async (_e: any, data: any) => {
|
||||
const response = await fetch(data.filePath);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
fs.writeFile(`${data.downFilePaths}${data.fileName}`, buffer, {});
|
||||
message.success(`下载成功!文件已保存至:${data.downFilePaths}`)
|
||||
await fs.access(data.downFilePaths, fs.constants.F_OK);
|
||||
if (process.platform === 'win32') {
|
||||
exec(`explorer "${data.downFilePaths}"`);
|
||||
} else if (process.platform === 'darwin') {
|
||||
exec(`open "${data.downFilePaths}"`);
|
||||
}
|
||||
})
|
||||
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 {
|
||||
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 {
|
||||
quitTipsRef.current.changeModal()
|
||||
}
|
||||
window.addEventListener('resize', handleResize);
|
||||
window.addEventListener('online', handleNetworkChange);
|
||||
window.addEventListener('offline', handleNetworkChange);
|
||||
const originalSetItem = window.localStorage.setItem;
|
||||
window.localStorage.setItem = function (key, value) {
|
||||
originalSetItem.call(this, key, value);
|
||||
const event = new Event('customStorageChange') as any;
|
||||
event.key = key
|
||||
event.value = value
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
||||
window.removeEventListener('online', handleNetworkChange);
|
||||
window.removeEventListener('offline', handleNetworkChange);
|
||||
};
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
window.electron.downFile(async (_e: any, data: any) => {
|
||||
const response = await fetch(data.filePath);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
fs.writeFile(`${data.downFilePaths}${data.fileName}`, buffer, {});
|
||||
message.success(`下载成功!文件已保存至:${data.downFilePaths}`)
|
||||
await fs.access(data.downFilePaths, fs.constants.F_OK);
|
||||
if (process.platform === 'win32') {
|
||||
exec(`explorer "${data.downFilePaths}"`);
|
||||
} else if (process.platform === 'darwin') {
|
||||
exec(`open "${data.downFilePaths}"`);
|
||||
}
|
||||
})
|
||||
}
|
||||
storage.setItem('stateInfo', JSON.stringify(state))
|
||||
}, [state])
|
||||
useEffect(() => {
|
||||
if (location.href.indexOf('/login') !== -1) {
|
||||
onStop()
|
||||
}
|
||||
}, [navigate])
|
||||
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', //关闭按钮设置
|
||||
}))
|
||||
}
|
||||
}, [])
|
||||
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 {
|
||||
quitTipsRef.current.changeModal()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
storage.setItem('stateInfo', JSON.stringify(state))
|
||||
}, [state])
|
||||
useEffect(() => {
|
||||
if (location.href.indexOf('/login') !== -1) {
|
||||
onStop()
|
||||
}
|
||||
if (location.href.indexOf('/meeting') === -1) {
|
||||
window.electron.closeMonitorWindow()
|
||||
}
|
||||
}, [navigate])
|
||||
}
|
||||
useEffect(() => {
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'F11') {
|
||||
|
|
@ -197,22 +200,20 @@ const App: React.FC = () => {
|
|||
})
|
||||
}
|
||||
const toSrc = (path: string): void => {
|
||||
window.electron.getWindowSize().then((res: any) => {
|
||||
switch (path) {
|
||||
case '/login':
|
||||
storage.removeItem('user')
|
||||
storage.setItem('userLogin', false)
|
||||
navigate('/login')
|
||||
break;
|
||||
case '/home':
|
||||
window.electron.setMainWindowSize({
|
||||
width: Math.ceil(res.width / 1.5),
|
||||
height: Math.ceil(res.height / 1.3),
|
||||
})
|
||||
navigate('/home')
|
||||
break;
|
||||
}
|
||||
})
|
||||
switch (path) {
|
||||
case '/login':
|
||||
storage.removeItem('user')
|
||||
navigate('/login')
|
||||
break;
|
||||
case '/home':
|
||||
window.electron.setMainWindowSize({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
})
|
||||
navigate('/home')
|
||||
break;
|
||||
|
||||
}
|
||||
};
|
||||
const leaveChannel = async (bool?: boolean): Promise<void> => {
|
||||
if (location.hash.indexOf('/meeting') === 1) {
|
||||
|
|
@ -232,10 +233,6 @@ const App: React.FC = () => {
|
|||
if (Boolean(e.value)) {
|
||||
onEventSignalr()
|
||||
}
|
||||
} else if (e.key === 'userLogin') {
|
||||
if (!Boolean(e.value)) {
|
||||
navigate('/login')
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -262,6 +259,7 @@ const App: React.FC = () => {
|
|||
</Route>
|
||||
<Route path='/login' element={<Login />} />
|
||||
<Route path='/meeting' element={<Meeting />} />
|
||||
<Route path='/userVideo' element={<UserVideo />} />
|
||||
<Route path='*' element={<NotFound />} />
|
||||
</Routes>
|
||||
<Spin spinning={spinning} fullscreen />
|
||||
|
|
|
|||
|
|
@ -5,17 +5,12 @@ export const GetRoom = (data: { pageIndex: number, pageSize: number }) =>
|
|||
method: 'get'
|
||||
})
|
||||
|
||||
export const PostRoom = (data: any) =>
|
||||
export const PostRomm = (data: any) =>
|
||||
request({
|
||||
url: `/home/room`,
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
export const DeleteRoom = (roomId: string) =>
|
||||
request({
|
||||
url: `/home/room?roomId=${roomId}`,
|
||||
method: 'delete',
|
||||
})
|
||||
|
||||
export const GetCheckoutRoomNum = (roomNum: string) =>
|
||||
request({
|
||||
|
|
@ -35,15 +30,3 @@ export const GetRoomInfo = (roomNum: string) =>
|
|||
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,10 +38,3 @@ export const GetRoleDpList = () =>
|
|||
url: `/pub/role-dp-list`,
|
||||
method: 'get',
|
||||
})
|
||||
|
||||
export const PostUserImport = (data: any) =>
|
||||
request({
|
||||
url: `/user/import`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 622 B |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 477 B |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 677 B |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 530 B |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 650 B |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 495 B |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 675 B |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 977 B |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 760 B |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 892 B |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 572 B |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 769 B |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 973 B |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 641 B |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 963 B |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 647 B |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 700 B |
|
Before Width: | Height: | Size: 8.0 KiB |
|
|
@ -1,23 +0,0 @@
|
|||
.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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
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,7 +8,6 @@ import { PostRefresh } from '@/api/Login';
|
|||
import Avatar from '@/components/Avatar';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { agora } from '@/utils/package/agora';
|
||||
import { role } from '@/config/role';
|
||||
let time = null as any;
|
||||
const JoinSetting = forwardRef((_props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
|
|
@ -72,7 +71,7 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
|
|||
})
|
||||
}
|
||||
const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => {
|
||||
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + 'a')]).then(res => {
|
||||
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + '1')]).then(res => {
|
||||
if (res[0].code === 200 && res[1].code === 200) {
|
||||
callBack({
|
||||
token: res[0].data,
|
||||
|
|
@ -85,7 +84,6 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
|
|||
await PostRefresh(user.refresh_token).then(res => {
|
||||
if (res.code === 200) {
|
||||
storage.setItem('user', JSON.stringify(res.data))
|
||||
storage.setItem('userLogin', true)
|
||||
callBack(res.data)
|
||||
}
|
||||
})
|
||||
|
|
@ -128,7 +126,7 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
|
|||
{
|
||||
joinRoomSettingForm.map((item, index) => {
|
||||
return <div key={index} onClick={async () => {
|
||||
if (role.ID.includes(user.roleId)) {
|
||||
if (user.roleId === '1') {
|
||||
let msg = '';
|
||||
if (index === 0) {
|
||||
await agora.getAudioMediaList().then(res => {
|
||||
|
|
|
|||
|
|
@ -7,15 +7,14 @@ import {
|
|||
VerticalAlignBottomOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Input, message, Modal, Pagination, Progress, Table } from 'antd';
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState, useRef } from "react";
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
|
||||
import { DeleteRoomFile, GetRoomFile, GetRoomFileDwUrl, GetRoomUpFileurl, GetRoomUserItem, PostRoomFile } from '@/api/Meeting';
|
||||
import axios from 'axios';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { storage } from '@/utils';
|
||||
import StupWizard from '../StupWizard';
|
||||
import { role } from '@/config/role';
|
||||
const fs = require('fs').promises;
|
||||
const { exec } = require('child_process');
|
||||
|
||||
const { Column } = Table
|
||||
|
||||
const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
||||
|
|
@ -27,10 +26,8 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
|||
setRoomUserItem(res.data)
|
||||
}
|
||||
})
|
||||
getRoomFile()
|
||||
}
|
||||
}))
|
||||
const stupWizardRef = useRef<any>();
|
||||
const { state } = useLocation();
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
const [roomUserItem, setRoomUserItem] = useState<any>({})
|
||||
|
|
@ -129,7 +126,7 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
|||
})
|
||||
}
|
||||
}} />
|
||||
{roomUserItem && role.ID.includes(roomUserItem.roleId) || roomUserItem.isRoomManager ? <ProfileOutlined title={showRowSelection ? '取消框选' : '显示框选'} onClick={() => {
|
||||
{roomUserItem && roomUserItem.roleId === '1' || roomUserItem.isRoomManager ? <ProfileOutlined title={showRowSelection ? '取消框选' : '显示框选'} onClick={() => {
|
||||
setShowRowSelection(!showRowSelection)
|
||||
}} style={{ color: showRowSelection ? '#5575F2' : 'white' }} /> : null}
|
||||
{showRowSelection ? <DeleteOutlined title='删除' onClick={() => {
|
||||
|
|
@ -313,11 +310,7 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
|||
}
|
||||
} catch (error: any) {
|
||||
if (error.code === 'ENOENT') {
|
||||
message.error({
|
||||
content: <div>文件夹不存在 <span style={{ color: '#606fc7', cursor: 'pointer' }} onClick={() => {
|
||||
stupWizardRef.current.changeModal(4)
|
||||
}}>前往设置</span></div>,
|
||||
})
|
||||
message.error('文件夹不存在!')
|
||||
return
|
||||
} else {
|
||||
message.error(error)
|
||||
|
|
@ -330,6 +323,7 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
|||
})
|
||||
}
|
||||
})
|
||||
|
||||
}} />
|
||||
{/* <FolderOutlined title='文件' style={{ color: '#FFA000', cursor: 'pointer', marginLeft: '10px' }} /> */}
|
||||
</>
|
||||
|
|
@ -350,7 +344,6 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
|
|||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<StupWizard ref={stupWizardRef} />
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import styles from '@/components/StupWizard/index.module.scss'
|
||||
import ImageUrl from '@/utils/package/imageUrl';
|
||||
import { Button, Checkbox, Empty, Input, message, Modal, Popover, Radio, Select, Slider, Space } from 'antd';
|
||||
import { Button, Checkbox, Empty, Input, message, Modal, Popover, Radio, Select, Slider } from 'antd';
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
|
||||
import { agora } from '@/utils/package/agora'
|
||||
import { CloseOutlined, LoadingOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
|
|
@ -11,7 +11,7 @@ const fs = require('fs').promises;
|
|||
const { exec } = require('child_process');
|
||||
const StupWizard = forwardRef((props: any, ref: any) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
changeModal: (index: number = 0) => {
|
||||
changeModal: () => {
|
||||
if (location.hash.indexOf('/meeting') === -1) {
|
||||
agora.init()
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ const StupWizard = forwardRef((props: any, ref: any) => {
|
|||
res.forEach((item: any) => {
|
||||
item.active = false
|
||||
});
|
||||
res[index].active = true;
|
||||
res[0].active = true;
|
||||
return res
|
||||
})
|
||||
setIsStupWizard(true)
|
||||
|
|
@ -243,8 +243,6 @@ const AudioComponents = () => {
|
|||
ecordingActive: false,
|
||||
ecordingVolume: 0,
|
||||
autoEcordingVolume: true,
|
||||
isAINoiseReduction: true,
|
||||
aINoiseReduction: 1
|
||||
});
|
||||
const setting = JSON.parse(storage.getItem('setting') as string)
|
||||
useEffect(() => {
|
||||
|
|
@ -290,9 +288,7 @@ const AudioComponents = () => {
|
|||
ecordingItem: setting.ecordingDeviceId,
|
||||
ecordingVolume: setting.ecordingVolume,
|
||||
playBackVolume: setting.playBackVolume,
|
||||
autoEcordingVolume: setting.autoEcordingVolume,
|
||||
isAINoiseReduction: setting.isAINoiseReduction,
|
||||
aINoiseReduction: setting.aINoiseReduction,
|
||||
autoEcordingVolume: setting.autoEcordingVolume
|
||||
})
|
||||
}
|
||||
return (
|
||||
|
|
@ -354,38 +350,6 @@ const AudioComponents = () => {
|
|||
})
|
||||
}} disabled={!audioDeviceManager.ecordingItem} />
|
||||
</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>
|
||||
<Checkbox onChange={async (e) => {
|
||||
setting.autoEcordingVolume = e.target.checked;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const UpdateModal = forwardRef((props: any, ref: any) => {
|
|||
const [updateContent, setUpdateContent] = useState('') // 版本更新内容
|
||||
|
||||
function getContent() {
|
||||
fetch(`https://meeting-api.23544.com/meeting/update/update.txt?t=${+new Date()}`) // 配置服务器地址
|
||||
fetch(`https://update.23544.com/metting/update.txt?t=${+new Date()}`) // 配置服务器地址
|
||||
.then(async response => {
|
||||
if (response.status === 200) {
|
||||
return setUpdateContent(await response.text())
|
||||
|
|
@ -67,7 +67,7 @@ const UpdateModal = forwardRef((props: any, ref: any) => {
|
|||
>立即更新</Button>
|
||||
<div className={styles.button2} onClick={() => setIsUpdateModal(false)}>暂不更新</div>
|
||||
</div> : progress < 100 ?
|
||||
<div style={{ margin: '20px 0' }}>
|
||||
<div style={{ marginTop: '20px' }}>
|
||||
下载进度:{progress}%
|
||||
<Flex gap="small" vertical>
|
||||
<Progress percent={progress} showInfo={false} />
|
||||
|
|
@ -75,7 +75,7 @@ const UpdateModal = forwardRef((props: any, ref: any) => {
|
|||
</div> :
|
||||
<Button type="primary"
|
||||
onClick={() => window.electron.onDownload('2')}
|
||||
style={{ width: '100%', height: '40px', margin: '20px 0' }}
|
||||
style={{ width: '100%', height: '40px', marginTop: '20px' }}
|
||||
className={`m-ant-btn`}
|
||||
>下载完成,点击安装</Button>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
export const role = {
|
||||
ID: ['1', '3']
|
||||
}
|
||||
|
|
@ -1,21 +1,14 @@
|
|||
import styles from '@/page/Home/Index/index.module.scss'
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import Operation from '@/components/Operation';
|
||||
import { Button, Input, Modal, Pagination, Empty, message, Popover, Popconfirm, DatePicker } from "antd";
|
||||
import { GetRoom, PostRoom, GetCheckoutRoomNum, GetRoomRtcToken, DeleteRoom, GetRecord } from '@/api/Home/Index';
|
||||
import { Button, Input, Modal, Pagination, Empty, message } from "antd";
|
||||
import { GetRoom, PostRomm, GetCheckoutRoomNum, GetRoomRtcToken } from '@/api/Home/Index';
|
||||
import ImageUrl from '@/utils/package/imageUrl'
|
||||
import { ExclamationCircleFilled, ReloadOutlined } from '@ant-design/icons';
|
||||
import { ReloadOutlined } from '@ant-design/icons';
|
||||
import JoinSetting from '@/components/JoinSetting';
|
||||
import { storage } from '@/utils';
|
||||
import { PostRefresh } from '@/api/Login';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { 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 navigate = useNavigate();
|
||||
const [list, setList] = useState({
|
||||
|
|
@ -25,15 +18,12 @@ const Index: React.FC = () => {
|
|||
pageSize: 12,
|
||||
})
|
||||
const [createRoomModal, setCreateRoomModal] = useState(false)
|
||||
const [timeSelectModal, setTimeSelectModal] = useState(false)
|
||||
const [createRoomFrom, setCreateRoomFrom] = useState<{ roomName: string, roomNum: string }>({
|
||||
roomName: "",
|
||||
roomNum: ""
|
||||
})
|
||||
const joinSettingRef = useRef<any>();
|
||||
const stupWizardRef = useRef<any>();
|
||||
const [user, setUser] = useState<any>({});
|
||||
const [currentRoomInfo, setCurrentRoomInfo] = useState<any>({});
|
||||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
useEffect(() => {
|
||||
setUser(userInfo)
|
||||
|
|
@ -80,7 +70,7 @@ const Index: React.FC = () => {
|
|||
})
|
||||
}
|
||||
const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => {
|
||||
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + 'a')]).then(res => {
|
||||
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + '1')]).then(res => {
|
||||
if (res[0].code === 200 && res[1].code === 200) {
|
||||
callBack({
|
||||
token: res[0].data,
|
||||
|
|
@ -93,59 +83,10 @@ const Index: React.FC = () => {
|
|||
await PostRefresh(user.refresh_token).then(res => {
|
||||
if (res.code === 200) {
|
||||
storage.setItem('user', JSON.stringify(res.data))
|
||||
storage.setItem('userLogin', true)
|
||||
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 (
|
||||
<>
|
||||
<div className={styles.index}>
|
||||
|
|
@ -206,52 +147,12 @@ const Index: React.FC = () => {
|
|||
<img src={ImageUrl.icon10} alt="" />
|
||||
</div>
|
||||
<div>
|
||||
<Popover
|
||||
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" danger>设置</Button> */}
|
||||
<Button type="primary"
|
||||
iconPosition={'end'}
|
||||
onClick={async () => {
|
||||
if (role.ID.includes(userInfo.roleId)) {
|
||||
if (userInfo.roleId === '1') {
|
||||
joinSettingRef.current.changeModal(item.roomNum)
|
||||
} else {
|
||||
postRefresh(() => {
|
||||
|
|
@ -366,7 +267,7 @@ const Index: React.FC = () => {
|
|||
if (bool) {
|
||||
message.error('房间号已存在!')
|
||||
} else {
|
||||
PostRoom(createRoomFrom).then(res => {
|
||||
PostRomm(createRoomFrom).then(res => {
|
||||
if (res.code === 200) {
|
||||
message.success('创建成功!')
|
||||
setCreateRoomModal(false)
|
||||
|
|
@ -379,32 +280,7 @@ const Index: React.FC = () => {
|
|||
</div>
|
||||
</div>
|
||||
</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} />
|
||||
<StupWizard ref={stupWizardRef} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,14 +21,11 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
>button {
|
||||
margin-right: 22px;
|
||||
}
|
||||
|
||||
.userBtnsDel {
|
||||
background-color: #3A1457;
|
||||
box-shadow: none;
|
||||
color: white;
|
||||
margin-left: 22px;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten(#3A1457, 5%) !important;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,13 @@
|
|||
import styles from '@/page/Home/User/index.module.scss'
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import Operation from '@/components/Operation';
|
||||
import { Button, Input, Table, Pagination, Modal, message, Select } from "antd";
|
||||
import { ExclamationCircleFilled, SearchOutlined } from '@ant-design/icons';
|
||||
import { GetUserList, PostUser, PutUser, DeleteUser, PutUserPwd, GetRoleDpList, PostUserImport } from '@/api/Home/User';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import { GetUserList, PostUser, PutUser, DeleteUser, PutUserPwd, GetRoleDpList } from '@/api/Home/User';
|
||||
import * as CryptoJS from 'crypto-js';
|
||||
import ImageUrl from '@/utils/package/imageUrl';
|
||||
import { storage } from '@/utils';
|
||||
import StupWizard from '@/components/StupWizard';
|
||||
const { Column } = Table
|
||||
const { confirm } = Modal;
|
||||
const { exec } = require('child_process');
|
||||
const fs = require('fs').promises;
|
||||
const User: React.FC = () => {
|
||||
const stupWizardRef = useRef<any>();
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
const [isCreateUser, setIsCreateUser] = useState(false);
|
||||
const [list, setList] = useState({
|
||||
|
|
@ -33,7 +27,6 @@ const User: React.FC = () => {
|
|||
UserName: ""
|
||||
})
|
||||
const [changeUserPawModal, setChangeUserPawModal] = useState(false)
|
||||
const [changeImportModal, setChangeImportModal] = useState(false)
|
||||
const [changeUserPawFrom, setChangeUserPawFrom] = useState({
|
||||
Pwd: "",
|
||||
newPwd: '',
|
||||
|
|
@ -79,46 +72,6 @@ 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 (
|
||||
<>
|
||||
<div className={styles.user}>
|
||||
|
|
@ -147,13 +100,6 @@ const User: React.FC = () => {
|
|||
className='m-ant-btn'>
|
||||
添加用户
|
||||
</Button>
|
||||
<Button type="primary"
|
||||
onClick={() => {
|
||||
setChangeImportModal(true)
|
||||
}}
|
||||
className='m-ant-btn'>
|
||||
批量导入用户
|
||||
</Button>
|
||||
<Button type="primary"
|
||||
icon={<img src={ImageUrl.icon21} alt="" />}
|
||||
className={styles.userBtnsDel}
|
||||
|
|
@ -275,14 +221,17 @@ const User: React.FC = () => {
|
|||
<Input
|
||||
style={{ flexGrow: 1 }}
|
||||
placeholder="请输入账号"
|
||||
maxLength={30}
|
||||
maxLength={11}
|
||||
showCount={true}
|
||||
value={addUserFrom.Account}
|
||||
onChange={(e) => {
|
||||
setAddUserFrom({
|
||||
...addUserFrom,
|
||||
Account: e.target.value,
|
||||
});
|
||||
const regex = /^[0-9]*$/;
|
||||
if (regex.test(e.target.value)) {
|
||||
setAddUserFrom({
|
||||
...addUserFrom,
|
||||
Account: e.target.value,
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -451,69 +400,6 @@ const User: React.FC = () => {
|
|||
</div>
|
||||
</div>
|
||||
</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="提示"
|
||||
description="确认退出吗?"
|
||||
onConfirm={() => {
|
||||
navigate('/login')
|
||||
storage.removeItem('user')
|
||||
storage.setItem('userLogin', false)
|
||||
}}
|
||||
onCancel={() => {
|
||||
|
||||
|
|
|
|||
|
|
@ -140,13 +140,10 @@ const Login: React.FC = () => {
|
|||
optionsValue: operation.optionsValue,
|
||||
}))
|
||||
storage.setItem('user', JSON.stringify(res.data))
|
||||
storage.setItem('userLogin', true)
|
||||
try {
|
||||
window.electron.getWindowSize().then((res: any) => {
|
||||
window.electron.setMainWindowSize({
|
||||
width: Math.ceil(res.width / 1.5),
|
||||
height: Math.ceil(res.height / 1.3),
|
||||
})
|
||||
window.electron.setMainWindowSize({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
})
|
||||
} catch {
|
||||
|
||||
|
|
|
|||
|
|
@ -31,30 +31,7 @@
|
|||
|
||||
>img {
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
>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;
|
||||
}
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
>span {
|
||||
|
|
@ -183,7 +160,6 @@
|
|||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.standardModeIcon {
|
||||
position: absolute;
|
||||
|
|
@ -233,7 +209,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// 自由者模式
|
||||
.meetingContentBodyLeftFreedomMode {
|
||||
width: 100%;
|
||||
|
|
@ -307,7 +282,7 @@
|
|||
position: absolute !important;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: calc(100% - 170px) !important;
|
||||
height: calc(100% - 160px) !important;
|
||||
width: 100% !important;
|
||||
z-index: 2;
|
||||
}
|
||||
|
|
@ -368,7 +343,6 @@
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
.meetingContentSwiperCardFullScreenIcon {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
|
|
@ -420,14 +394,33 @@
|
|||
padding: 10px 20px;
|
||||
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 {
|
||||
width: 300px;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
|
||||
.meetingUserList {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
padding: 10px 0 20px;
|
||||
box-sizing: border-box;
|
||||
|
|
@ -542,7 +535,6 @@
|
|||
}
|
||||
|
||||
.meetingUserChat {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
background-color: #16191E;
|
||||
display: flex;
|
||||
|
|
@ -661,39 +653,6 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -725,7 +684,7 @@
|
|||
}
|
||||
|
||||
>img {
|
||||
height: 50px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
>span {
|
||||
|
|
@ -746,31 +705,6 @@
|
|||
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 {
|
||||
background-color: #161A29;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,24 +2,40 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(7, 9, 11);
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
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 {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px 40px 0;
|
||||
|
||||
.userVideoContentHeader {
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 10px;
|
||||
padding: 0 10px;
|
||||
|
||||
>div:nth-child(1) {
|
||||
display: flex;
|
||||
|
|
@ -29,16 +45,12 @@
|
|||
background-color: #181A1D;
|
||||
border-radius: 4px;
|
||||
padding: 4px 10px;
|
||||
box-sizing: border-box;
|
||||
margin: 0 10px 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
>span {
|
||||
color: #EEEEEE;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
margin-right: 4px;
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -49,8 +61,8 @@
|
|||
|
||||
>span {
|
||||
color: #EEEEEE;
|
||||
font-size: 12px;
|
||||
margin-right: 4px;
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -65,9 +77,9 @@
|
|||
height: 0;
|
||||
|
||||
.userVideoContentListItem {
|
||||
height: 32%;
|
||||
width: calc(100% / 4 - 8px);
|
||||
padding: 4px;
|
||||
height: 20%;
|
||||
width: calc(100% / 4 - 20px);
|
||||
padding: 10px;
|
||||
|
||||
.userVideoContentListItemVideo {
|
||||
background-color: #101317;
|
||||
|
|
@ -75,18 +87,6 @@
|
|||
box-sizing: border-box;
|
||||
width: 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,13 +1,14 @@
|
|||
|
||||
import styles from '@/components/UserVideo/index.module.scss'
|
||||
import { GetPolling } from '@/api/Meeting';
|
||||
import styles from '@/page/UserVideo/index.module.scss'
|
||||
import { storage } from '@/utils';
|
||||
import { agora } from '@/utils/package/agora';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { Button, Empty, Select, message } from 'antd';
|
||||
import { useEffect, useState } from "react";
|
||||
import { useLocation } from 'react-router';
|
||||
import { VideoStreamType } from 'agora-electron-sdk';
|
||||
const UserVideo: React.FC = () => {
|
||||
const { state } = useLocation();
|
||||
let userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
const [user, setUser] = useState<any>({});
|
||||
const [from, setFrom] = useState<any>({
|
||||
cycleIntervalList: [
|
||||
{ value: 30, label: '30秒' },
|
||||
|
|
@ -17,16 +18,19 @@ const UserVideo: React.FC = () => {
|
|||
],
|
||||
cycleIntervalValue: 30,
|
||||
viewPeople: [
|
||||
{ value: 6, label: '6人' },
|
||||
{ value: 4, label: '4人' },
|
||||
{ value: 8, label: '8人' },
|
||||
{ value: 12, label: '12人' },
|
||||
// { value: 20, label: '20人' },
|
||||
{ value: 16, label: '16人' },
|
||||
{ value: 20, label: '20人' },
|
||||
],
|
||||
viewPeopleValue: 6,
|
||||
viewPeopleValue: 4,
|
||||
})
|
||||
const [timeNumber, setTimeNumber] = useState(30);
|
||||
const [timeStatus, setTimeStatus] = useState(false);
|
||||
const [userList, setUserList] = useState([]);
|
||||
useEffect(() => {
|
||||
setUser(userInfo)
|
||||
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
||||
return () => {
|
||||
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
||||
|
|
@ -59,14 +63,12 @@ const UserVideo: React.FC = () => {
|
|||
}, [from.viewPeopleValue])
|
||||
|
||||
useEffect(() => {
|
||||
userList.forEach(async (item: any) => {
|
||||
await agora.destroyRendererByConfig(Number('1' + item.screenShareId))
|
||||
await agora.setupRemoteVideoEx({
|
||||
uid: Number('1' + item.screenShareId),
|
||||
view: document.getElementById(`video-${item.screenShareId}`),
|
||||
channelId: state.channelId + 'a',
|
||||
})
|
||||
await agora.setRemoteVideoStreamType(Number('1' + item.screenShareId), VideoStreamType.VideoStreamLow, false)
|
||||
userList.forEach((item: any) => {
|
||||
// agora.meetingMonitoringSetupRemoteVideoJoin({
|
||||
// uid: Number('1' + item.screenShareId),
|
||||
// view: document.getElementById(`video-${item.uid}`),
|
||||
// channelId: getQueryParameterRegex('channelId'),
|
||||
// })
|
||||
})
|
||||
}, [userList])
|
||||
// 监听缓存变化
|
||||
|
|
@ -75,22 +77,30 @@ 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> => {
|
||||
setUserList([])
|
||||
setFrom((res: any) => {
|
||||
GetPolling(state.channelId?.split('a')[0] as string, res.viewPeopleValue).then((res: any) => {
|
||||
if (res.code === 200) {
|
||||
setUserList(res.data)
|
||||
}
|
||||
})
|
||||
return res
|
||||
GetPolling(getQueryParameterRegex('channelId')?.split('1')[0] as string, from.viewPeopleValue).then((res: any) => {
|
||||
if (res.code === 200) {
|
||||
setUserList(res.data)
|
||||
}
|
||||
})
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className={styles.userVideo}>
|
||||
<div className={`${styles.userVideoContent}`}>
|
||||
<div className={styles.userVideoTitle}>
|
||||
会议监控
|
||||
<CloseOutlined className='drag' onClick={() => {
|
||||
window.electron.closeMonitorWindow()
|
||||
}} />
|
||||
</div>
|
||||
<div className={`${styles.userVideoContent} drag`}>
|
||||
<div className={styles.userVideoContentHeader}>
|
||||
<div>
|
||||
<div>
|
||||
|
|
@ -98,7 +108,6 @@ const UserVideo: React.FC = () => {
|
|||
<Select
|
||||
placeholder='请选择循环间隔'
|
||||
options={from.cycleIntervalList}
|
||||
size={'small'}
|
||||
value={from.cycleIntervalValue} onChange={(e) => {
|
||||
setFrom({ ...from, cycleIntervalValue: e })
|
||||
setTimeNumber(e)
|
||||
|
|
@ -109,7 +118,6 @@ const UserVideo: React.FC = () => {
|
|||
<Select
|
||||
placeholder='请选择查看人数'
|
||||
options={from.viewPeople}
|
||||
size={'small'}
|
||||
value={from.viewPeopleValue} onChange={(e) => {
|
||||
setFrom({ ...from, viewPeopleValue: e })
|
||||
}} />
|
||||
|
|
@ -119,7 +127,6 @@ const UserVideo: React.FC = () => {
|
|||
<span>{timeNumber}秒后刷新</span>
|
||||
{timeStatus ? <Button
|
||||
type="primary"
|
||||
size={'small'}
|
||||
onClick={() => {
|
||||
setTimeStatus(!timeStatus)
|
||||
}}
|
||||
|
|
@ -128,7 +135,6 @@ const UserVideo: React.FC = () => {
|
|||
<Button
|
||||
type="primary"
|
||||
className='m-ant-btn'
|
||||
size={'small'}
|
||||
onClick={() => {
|
||||
setTimeStatus(!timeStatus)
|
||||
}}
|
||||
|
|
@ -139,8 +145,8 @@ const UserVideo: React.FC = () => {
|
|||
{
|
||||
userList.map((item: any, index: number) => {
|
||||
return <div className={styles.userVideoContentListItem} key={index}>
|
||||
<div className={styles.userVideoContentListItemVideo} id={`video-${item.screenShareId}`}>
|
||||
<div>{item.userName}{item.isRoomManager ? '(发言中)' : ''}</div>
|
||||
<div className={styles.userVideoContentListItemVideo} id={`video-${item.uid}`}>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
})
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
// electron-env.d.ts
|
||||
export interface IElectronAPI {
|
||||
setMainWindowSize: (config: any) => void;
|
||||
getWindowSize: () => any;
|
||||
setViewStatus: (status: 'quit' | 'maximize' | 'minimize' | 'unmaximize' | 'hide') => void;
|
||||
getIsMaximized: () => Promise<boolean>;
|
||||
setWriteText: (text: string) => void;
|
||||
|
|
@ -16,6 +15,9 @@ export interface IElectronAPI {
|
|||
downFile: (callBack: Function) => void;
|
||||
quitAndInstall: (callBack: Function) => void;
|
||||
getVersion: () => Promise<string>;
|
||||
oepnWindow: (data: any) => any;
|
||||
|
||||
closeMonitorWindow: () => void
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
|
|
|||
|
|
@ -5,16 +5,12 @@ import {
|
|||
VideoViewSetupMode,
|
||||
ScreenCaptureSourceType,
|
||||
RenderModeType,
|
||||
ChannelProfileType,
|
||||
AudioAinsMode,
|
||||
SimulcastStreamMode,
|
||||
VideoStreamType
|
||||
ChannelProfileType
|
||||
} from "agora-electron-sdk";
|
||||
import { GetRoomRtcToken, GetAgoraConf } from "@/api/Home/Index";
|
||||
import { GetRoomRtcToken } from "@/api/Home/Index";
|
||||
import { storage } from '@/utils';
|
||||
import { role } from "@/config/role";
|
||||
const option: any = {
|
||||
appId: '',
|
||||
appId: 'dcfc466a6ecb4a1f972630065dfb1e75',
|
||||
token: '',
|
||||
tokenA: '',
|
||||
channelId: '',
|
||||
|
|
@ -25,14 +21,11 @@ let rtcEngine: any = '';
|
|||
export const agora = {
|
||||
// 初始化
|
||||
init: async (bool: boolean = false) => {
|
||||
const { data } = await GetAgoraConf();
|
||||
if (data) {
|
||||
rtcEngine = createAgoraRtcEngine();
|
||||
await rtcEngine.initialize({
|
||||
appId: data,
|
||||
});
|
||||
await agora.setDeviceManager(bool)
|
||||
}
|
||||
rtcEngine = createAgoraRtcEngine();
|
||||
await rtcEngine.initialize({
|
||||
appId: option.appId,
|
||||
});
|
||||
await agora.setDeviceManager(bool)
|
||||
},
|
||||
// 获取rtcEngine
|
||||
getRtcEngine: () => {
|
||||
|
|
@ -99,7 +92,6 @@ export const agora = {
|
|||
if (setting.playBackVolume) agora.setPlaybackDeviceVolume(setting.playBackVolume) // 设置播放设备音量
|
||||
if (setting.ecordingDeviceId) agora.setRecordingDevice(setting.ecordingDeviceId) // 设置音频采集设备
|
||||
if (setting.ecordingVolume) agora.setRecordingDeviceVolume(setting.ecordingVolume) // 设置音频设备音量
|
||||
if (setting.isAINoiseReduction) agora.setAINSMode(setting.isAINoiseReduction, setting.aINoiseReduction) // 设置ai降噪
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
|
|
@ -171,20 +163,6 @@ 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) => {
|
||||
await rtcEngine.setupRemoteVideo(
|
||||
|
|
@ -215,17 +193,6 @@ export const agora = {
|
|||
joinChannel: async () => {
|
||||
await rtcEngine.enableAudioVolumeIndication(100, 1, true)
|
||||
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) => {
|
||||
|
|
@ -238,14 +205,6 @@ export const agora = {
|
|||
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) => {
|
||||
await agora.leaveChannelEx(uid)
|
||||
|
|
@ -263,53 +222,26 @@ export const agora = {
|
|||
);
|
||||
},
|
||||
// 所有用户加入的第二个房间
|
||||
allJoinChannelEx: async (bool: boolean = false) => {
|
||||
const user = await JSON.parse(storage.getItem('user') as string)
|
||||
await agora.startCameraCapture(true)
|
||||
allJoinChannelEx: async () => {
|
||||
await rtcEngine.joinChannelEx(
|
||||
option.tokenA,
|
||||
{ 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) }
|
||||
{ channelId: option.channelId + '1', localUid: Number('1' + option.screenShareId) },
|
||||
{}
|
||||
);
|
||||
await agora.updateChannelMediaOptionsEx(false)
|
||||
},
|
||||
// 退出第二个房间
|
||||
allLeaveChannelEx: async () => {
|
||||
await agora.stopCameraCapture();
|
||||
await rtcEngine.leaveChannelEx({ channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) })
|
||||
},
|
||||
// 停止/恢复接收指定的视频流。
|
||||
muteRemoteVideoStreamEx: async (uid: number, mute: boolean) => {
|
||||
await rtcEngine.muteRemoteVideoStreamEx(uid, mute, { channelId: option.channelId, localUid: Number(option.uid) })
|
||||
},
|
||||
// 取消或恢复订阅指定远端用户的音频流
|
||||
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)
|
||||
updateChannelMediaOptionsEx: async (bool: boolean) => {
|
||||
rtcEngine.updateChannelMediaOptionsEx({
|
||||
clientRoleType: bool ? ClientRoleType.ClientRoleBroadcaster : ClientRoleType.ClientRoleAudience, //用户角色 ClientRoleBroadcaster 主播 ClientRoleAudience 观众
|
||||
autoSubscribeAudio: false,//设置是否自动订阅所有音频流
|
||||
autoSubscribeVideo: true,//设置是否自动订阅所有视频流
|
||||
publishMicrophoneTrack: true,//设置是否发布麦克风采集到的音频
|
||||
publishCameraTrack: true,//设置是否发布摄像头采集的视频
|
||||
publishScreenTrack: false,//设置是否发布屏幕采集的视频
|
||||
}, {
|
||||
channelId: option.channelId + '1',
|
||||
localUid: Number('1' + option.screenShareId)
|
||||
})
|
||||
},
|
||||
// 离开共享屏幕频道
|
||||
leaveChannelEx: async (uid: any) => {
|
||||
|
|
@ -321,27 +253,19 @@ export const agora = {
|
|||
rtcEngine.enableLoopbackRecording(false)
|
||||
},
|
||||
// 取消或恢复发布本地音频流
|
||||
muteLocalAudioStream: async (data: any, mute: any) => {
|
||||
// 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,//设置是否发布屏幕采集的视频
|
||||
})
|
||||
muteLocalAudioStream: async (mute: any) => {
|
||||
await rtcEngine.muteLocalAudioStream(mute)
|
||||
},
|
||||
// 取消或恢复发布本地视频流
|
||||
muteLocalVideoStream: async (mute: any) => {
|
||||
await rtcEngine.muteLocalVideoStream(mute)
|
||||
},
|
||||
// 摄像头采集
|
||||
startCameraCapture: async (bool: boolean = false) => {
|
||||
startCameraCapture: async () => {
|
||||
await rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, {
|
||||
format: {
|
||||
width: bool ? 160 : 1280,
|
||||
height: bool ? 160 : 720,
|
||||
width: 640,
|
||||
height: 360,
|
||||
fps: 15,
|
||||
}
|
||||
})
|
||||
|
|
@ -358,10 +282,13 @@ export const agora = {
|
|||
option.uid = Number(data.uid);
|
||||
option.screenShareId = data.screenShareId;
|
||||
await agora.joinChannel()
|
||||
if (data.tokenA) {
|
||||
await agora.allJoinChannelEx()
|
||||
}
|
||||
},
|
||||
// 桌面捕获音频和视频的媒体源的信息
|
||||
getDesktopCapturerVideo: async (thumbSize: any, iconSize: any, includeScreen: boolean) => {
|
||||
return await rtcEngine.getScreenCaptureSources(thumbSize, iconSize, includeScreen).filter((item: any) => item.type === 1)
|
||||
getDesktopCapturerVideo: async () => {
|
||||
return rtcEngine.getScreenCaptureSources({ width: 300, height: 300 }, { width: 300, height: 300 }, true);
|
||||
},
|
||||
// 共享屏幕采集
|
||||
setDesktopCapturerVideo: async (targetSource: any, isComputerAudio: boolean, isFluencyPriority: boolean) => {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ import icon47 from '@/assets/icon47.png'
|
|||
import icon47Active from '@/assets/icon47-active.png'
|
||||
import icon48 from '@/assets/icon48.png'
|
||||
import icon48Select from '@/assets/icon48-select.png'
|
||||
import icon49 from '@/assets/icon49.png'
|
||||
export default {
|
||||
loading,
|
||||
icon,
|
||||
|
|
@ -143,6 +142,5 @@ export default {
|
|||
icon47,
|
||||
icon47Active,
|
||||
icon48,
|
||||
icon48Select,
|
||||
icon49,
|
||||
icon48Select
|
||||
}
|
||||
|
|
@ -160,27 +160,6 @@ export const onSignalr = (callBack: Function) => {
|
|||
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 = () => {
|
||||
|
|
@ -199,9 +178,6 @@ export const offSignalr = () => {
|
|||
connection.off('ManagerRefresh');
|
||||
connection.off('ApplyToSpeak');
|
||||
connection.off('Watch');
|
||||
connection.off('DriverList');
|
||||
connection.off('SetDriver');
|
||||
connection.off('ShowDriverList');
|
||||
}
|
||||
}
|
||||
export const onInvoke = async (str: string, data: any) => {
|
||||
|
|
@ -213,18 +189,6 @@ export const onInvoke = async (str: string, data: any) => {
|
|||
// 4:屏幕共享
|
||||
await connection.invoke(str, data.roomNum, data.type)
|
||||
break;
|
||||
case 'getDrivers':
|
||||
// 获取某个人的设备列表
|
||||
await connection.invoke(str, data.uid)
|
||||
break;
|
||||
case 'sendDrivers':
|
||||
// 发送设备列表给某个人
|
||||
await connection.invoke(str, data.uid, data.driversJsonString)
|
||||
break;
|
||||
case 'setDrivers':
|
||||
// 设置某个人的设备列表
|
||||
await connection.invoke(str, data.uid, data.driversJsonString)
|
||||
break;
|
||||
}
|
||||
}
|
||||
export const onStop = async () => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
class LocalStorage {
|
||||
private constructor() { }
|
||||
private constructor() {}
|
||||
|
||||
private static instance: LocalStorage | null = null
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ class Request {
|
|||
message.error(resData.message)
|
||||
}
|
||||
}
|
||||
if (resData.code === 1403) {
|
||||
if (resData.code === 1403 || resData.code === 1000) {
|
||||
toLogin()
|
||||
}
|
||||
return resData
|
||||
|
|
@ -113,7 +113,7 @@ class Request {
|
|||
}
|
||||
function toLogin() {
|
||||
storage.removeItem('user')
|
||||
storage.setItem('userLogin', false)
|
||||
location.href = location.origin + '/#/login'
|
||||
}
|
||||
function updatePostRefresh() {
|
||||
let user = JSON.parse(storage.getItem('user') as string);
|
||||
|
|
@ -121,7 +121,6 @@ function updatePostRefresh() {
|
|||
PostRefresh(user.refresh_token).then((res) => {
|
||||
if (res.code == 200) {
|
||||
storage.setItem('user', JSON.stringify(res.data))
|
||||
storage.setItem('userLogin', true)
|
||||
} else {
|
||||
toLogin()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -343,16 +343,11 @@ $pagination-hover-background-color: #5575F2;
|
|||
}
|
||||
|
||||
.ant-radio-checked .ant-radio-inner {
|
||||
background-color: $btn-background-color;
|
||||
border-color: $btn-background-color;
|
||||
background-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-notice-wrapper {
|
||||
|
|
@ -376,8 +371,3 @@ $pagination-hover-background-color: #5575F2;
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ant-message
|
||||
.ant-message {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
|
@ -60,10 +60,7 @@ export default defineConfig({
|
|||
RenderModeType,
|
||||
ScreenCaptureSourceType,
|
||||
VideoSourceType,
|
||||
VideoViewSetupMode,
|
||||
AudioAinsMode,
|
||||
SimulcastStreamMode,
|
||||
VideoStreamType
|
||||
VideoViewSetupMode
|
||||
} = require("agora-electron-sdk")
|
||||
export {
|
||||
createAgoraRtcEngine,
|
||||
|
|
@ -72,10 +69,7 @@ export default defineConfig({
|
|||
RenderModeType,
|
||||
ScreenCaptureSourceType,
|
||||
VideoSourceType,
|
||||
VideoViewSetupMode,
|
||||
AudioAinsMode,
|
||||
SimulcastStreamMode,
|
||||
VideoStreamType
|
||||
VideoViewSetupMode
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
|
|
|||