Compare commits

..

50 Commits

Author SHA1 Message Date
yangqiang f24150af42 Merge pull request 'yangjie' (#47) from yangjie into master
Reviewed-on: #47
2025-01-24 13:43:09 +08:00
yj fb05838a68 打包参数 2025-01-24 11:18:08 +08:00
yj c0b6b7afd1 去除多余代码 2025-01-24 11:12:11 +08:00
yj 7fd79873f3 菜单自定义 2025-01-24 11:11:43 +08:00
yj c71ea10a4b 优化 2025-01-24 10:34:35 +08:00
yj 3c9b3ac48a 优化 2025-01-24 10:22:32 +08:00
yj 7b4cfbeb1d 优化 2025-01-24 09:07:07 +08:00
yj 25e9f16af0 优化 2025-01-23 14:41:09 +08:00
yj fd809034c1 去除多余代码 2025-01-23 12:38:42 +08:00
yj 26ce3707d7 优化 2025-01-23 12:38:13 +08:00
yj 74760e6382 优化 2025-01-23 12:35:43 +08:00
yj 2e2074fda3 优化 2025-01-23 12:34:26 +08:00
yj 0e37a751ea 优化 2025-01-23 11:45:55 +08:00
yj 70c54d71fd 优化 2025-01-23 11:36:49 +08:00
yj a4ae5577d4 优化 2025-01-23 11:14:18 +08:00
yj 2164fcfde4 优化 2025-01-22 17:58:22 +08:00
yj c8dc0e3276 优化 2025-01-22 17:30:50 +08:00
yj 24a174c7b5 优化 2025-01-22 17:11:07 +08:00
yj e5c4b85dc4 优化 2025-01-22 16:35:45 +08:00
yj 8bd09d6f01 重连 2025-01-22 15:20:50 +08:00
yj 54a5b442cd 右下角通知弹窗关闭 2025-01-22 09:58:00 +08:00
yj 1d2f1072ef 小视频滚动条加按钮 2025-01-22 09:41:57 +08:00
yj c2b36c0b3f 优化 2025-01-21 15:05:54 +08:00
yj dae9b35802 优化模式 2025-01-21 12:25:43 +08:00
yj 9b58afece8 优化 2025-01-21 11:43:30 +08:00
yj 75396341eb 修改视图显示模式 2025-01-21 10:52:33 +08:00
yj 678962d1e9 优化 2025-01-21 10:29:43 +08:00
yj dc619db987 优化 2025-01-20 17:46:09 +08:00
yj 982867dfe1 优化 2025-01-20 15:14:05 +08:00
yj 96959a3e6f 优化 2025-01-20 14:04:39 +08:00
yj 2afcef025b 优化 2025-01-20 10:43:12 +08:00
yj 1887bb39bc 优化 2025-01-20 10:26:31 +08:00
yj bfb85e53a9 优化 2025-01-17 17:55:29 +08:00
yj 88cad54e52 优化 2025-01-17 17:50:59 +08:00
yj ad16a0867b 优化 2025-01-17 16:52:07 +08:00
yj 6d98f0fc0f 去除多余代码 2025-01-17 15:08:43 +08:00
yj c6887b2747 修改共享屏幕token 2025-01-17 15:08:27 +08:00
yj 36e1bda441 更新token 2025-01-16 14:50:10 +08:00
yj f993e740d7 阻止息屏 2025-01-16 14:06:25 +08:00
yj a5e2e8b036 优化 2025-01-16 13:59:23 +08:00
yj 9080065f60 新增每30秒刷新一次房间用户&优化应用不自动进入休眠模式 2025-01-16 11:49:08 +08:00
yj 9f045f7531 优化 2025-01-08 10:53:02 +08:00
yj d10fdd1d5c 更新token 2025-01-08 10:30:58 +08:00
yj 3b79abb8ee 优化 2025-01-06 14:30:54 +08:00
yj c299f6f5af 优化 2025-01-06 14:26:24 +08:00
yj 496a4dd28f 优化 2025-01-06 14:26:15 +08:00
yj 956f045fad 优化 2025-01-06 14:08:07 +08:00
yj 2abf010867 修改样式 2025-01-06 14:03:49 +08:00
yj 20604bb28a 修改意见反馈打开时间 2025-01-06 11:19:20 +08:00
yj 9a6a5904a4 意见反馈 2025-01-06 11:18:53 +08:00
21 changed files with 1085 additions and 312 deletions

101
main.js
View File

@ -10,6 +10,7 @@ const {
dialog, dialog,
crashReporter, crashReporter,
desktopCapturer, desktopCapturer,
powerSaveBlocker,
} = require('electron'); } = require('electron');
const path = require('node:path') const path = require('node:path')
const updateJs = require('./src/utils/package/update') const updateJs = require('./src/utils/package/update')
@ -28,6 +29,10 @@ let regKey;
let connection = null; let connection = null;
let envStr; let envStr;
let startNumber = 0; let startNumber = 0;
let buildStatus = false; //true 打包开发版本 false 本地开发
powerSaveBlocker.start('prevent-display-sleep')
const id = powerSaveBlocker.start('prevent-display-sleep')
powerSaveBlocker.stop(id)
class AppWindow extends BrowserWindow { class AppWindow extends BrowserWindow {
constructor(config) { constructor(config) {
@ -49,7 +54,11 @@ class AppWindow extends BrowserWindow {
super(finalConfig); super(finalConfig);
if (env === 'development') { if (env === 'development') {
// 开发 // 开发
if (buildStatus) {
this.loadFile(path.resolve(__dirname, './dist/index.html'));
} else {
this.loadURL('http://localhost:3000'); this.loadURL('http://localhost:3000');
}
} else { } else {
// 测试 | 生产 // 测试 | 生产
this.loadFile(path.resolve(__dirname, './dist/index.html')); this.loadFile(path.resolve(__dirname, './dist/index.html'));
@ -62,33 +71,12 @@ class AppWindow extends BrowserWindow {
function quit() { function quit() {
app.quit() app.quit()
} }
let tray;
function createTray() { function createTray() {
const iconPath = `${__dirname}/src/assets/${updateJs.getIcon(envStr)}.png`; const iconPath = `${__dirname}/src/assets/${updateJs.getIcon(envStr)}.png`;
const trayIcon = nativeImage.createFromPath(iconPath); const trayIcon = nativeImage.createFromPath(iconPath);
const tray = new Tray(trayIcon); tray = new Tray(trayIcon);
const contextMenu = Menu.buildFromTemplate([
{
label: '打开', click: () => {
mainWindow.webContents.send('isOpenWindows');
},
// icon: iconPath,
},
{
label: '最小化到系统托盘', click: () => {
mainWindow.hide();
},
// icon: iconPath,
},
{
label: '退出', click: async () => {
quit()
},
// icon: iconPath,
},
]);
tray.setToolTip(updateJs.getTitle(envStr)); tray.setToolTip(updateJs.getTitle(envStr));
tray.setContextMenu(contextMenu);
tray.on('click', () => { tray.on('click', () => {
mainWindow.webContents.send('isOpenWindows'); mainWindow.webContents.send('isOpenWindows');
}); });
@ -97,6 +85,13 @@ function createTray() {
function createWindow() { function createWindow() {
mainWindow = new AppWindow(); mainWindow = new AppWindow();
mainWindow.focus(); mainWindow.focus();
mainWindow.hookWindowMessage(278, function (e) {
mainWindow.setEnabled(false);//窗口禁用
setTimeout(() => {
mainWindow.setEnabled(true);//窗口启用
}, 100);
return true;
})
} }
const additionalData = { myKey: 'myValue' } const additionalData = { myKey: 'myValue' }
app.on('ready', () => { app.on('ready', () => {
@ -136,6 +131,12 @@ app.on('ready', () => {
mainWindow.webContents.openDevTools() mainWindow.webContents.openDevTools()
} }
}); });
mainWindow.on('focus', () => {
mainWindow.show()
});
mainWindow.on('maximize', () => {
mainWindow.show()
});
// 监听移动 // 监听移动
mainWindow.on('move', () => { mainWindow.on('move', () => {
// 如果是全屏自动恢复到上次窗口大小 // 如果是全屏自动恢复到上次窗口大小
@ -182,6 +183,8 @@ app.on('ready', () => {
connection.off('SetDriver'); connection.off('SetDriver');
connection.off('ShowDriverList'); connection.off('ShowDriverList');
connection.off('ModifyNickName'); connection.off('ModifyNickName');
connection.off('JoinChannelCallback');
connection.off('ExitSharedScreen');
} }
}); });
ipcMain.handle('onStop', (event) => { ipcMain.handle('onStop', (event) => {
@ -201,7 +204,6 @@ app.on('ready', () => {
await connection.invoke(str, data.roomNum, data.msg) await connection.invoke(str, data.roomNum, data.msg)
break; break;
case 'sendOper': case 'sendOper':
// 4:屏幕共享
await connection.invoke(str, data.roomNum, data.type) await connection.invoke(str, data.roomNum, data.type)
break; break;
case 'getDrivers': case 'getDrivers':
@ -217,11 +219,11 @@ app.on('ready', () => {
await connection.invoke(str, data.uid, data.driversJsonString) await connection.invoke(str, data.uid, data.driversJsonString)
break; break;
case 'joinChannel': case 'joinChannel':
// 设置某个人的设备列表 // 加入房间
await connection.invoke(str, data.roomNum, data.enableMicr, data.enableCamera, data.isRoomManager || false) await connection.invoke(str, data.roomNum, data.enableMicr, data.enableCamera, data.isRoomManager || false)
break; break;
case 'levelChannel': case 'levelChannel':
// 设置某个人的设备列表 // 退出房间
await connection.invoke(str, data.roomNum) await connection.invoke(str, data.roomNum)
break; break;
} }
@ -379,6 +381,19 @@ app.on('ready', () => {
nickName nickName
}) })
}); });
// 加入房间回调
connection.on("JoinChannelCallback", (isSuccess) => {
mainWindow.webContents.send('onSignalr', {
key: 'JoinChannelCallback',
isSuccess,
})
});
// 退出共享
connection.on("ExitSharedScreen", () => {
mainWindow.webContents.send('onSignalr', {
key: 'ExitSharedScreen'
})
});
} }
}); });
// 放大缩小退出窗口 // 放大缩小退出窗口
@ -489,9 +504,32 @@ app.on('ready', () => {
// 设置桌面应用基础属性 // 设置桌面应用基础属性
ipcMain.handle('setMainWindowSize', (event, config) => { ipcMain.handle('setMainWindowSize', (event, config) => {
if (config.width === 250) { if (config.width === 250) {
const contextMenu = Menu.buildFromTemplate([
{
label: '退出',
click: () => quit(),
},
]);
tray.setContextMenu(contextMenu);
mainWindow.setSkipTaskbar(true) mainWindow.setSkipTaskbar(true)
mainWindow.setResizable(false) mainWindow.setResizable(false)
mainWindow.setAlwaysOnTop(true, 'screen-saver') mainWindow.setAlwaysOnTop(true, 'screen-saver')
} else {
const contextMenu = Menu.buildFromTemplate([
{
label: '打开',
click: () => mainWindow.webContents.send('isOpenWindows'),
},
{
label: '最小化到系统托盘',
click: () => mainWindow.hide(),
},
{
label: '退出',
click: () => quit(),
},
]);
tray.setContextMenu(contextMenu);
} }
// 设置最小窗口尺寸 // 设置最小窗口尺寸
mainWindow.setMinimumSize(config.width, config.height); mainWindow.setMinimumSize(config.width, config.height);
@ -551,11 +589,22 @@ app.on('ready', () => {
}) })
if (envStr === 'development') { if (envStr === 'development') {
// 开发 // 开发
if (buildStatus) {
child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`);
} else {
child.loadURL(config.url) child.loadURL(config.url)
}
} else { } else {
// 测试 | 生产 // 测试 | 生产
child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`); child.loadURL(`file://${path.join(__dirname, './dist/index.html')}#/${config.key}`);
} }
child.hookWindowMessage(278, function (e) {
child.setEnabled(false);//窗口禁用
setTimeout(() => {
child.setEnabled(true);//窗口启用
}, 100);
return true;
})
childWindow[config.key] = child childWindow[config.key] = child
child.once('ready-to-show', () => { child.once('ready-to-show', () => {
if (config.show) { if (config.show) {

View File

@ -1,7 +1,7 @@
{ {
"name": "WGShare.Metting", "name": "WGShare.Metting",
"private": true, "private": true,
"version": "0.6.3", "version": "0.6.5",
"main": "main.js", "main": "main.js",
"authors": "yj", "authors": "yj",
"description": "智汇享", "description": "智汇享",

View File

@ -5,6 +5,12 @@ export const GetRoom = (data: { pageIndex: number, pageSize: number }) =>
method: 'get' method: 'get'
}) })
export const PostFeedback = (data: any) =>
request({
url: `/home/feedback`,
method: 'post',
data,
})
export const PostRoom = (data: any) => export const PostRoom = (data: any) =>
request({ request({
url: `/home/room`, url: `/home/room`,

View File

@ -155,3 +155,13 @@ export const PutAlterUname = (data: any) =>
method: 'put', method: 'put',
data data
}) })
export const GetSharedScreen = (roomNum: string) =>
request({
url: `/room/shared-screen?roomNum=${roomNum}`,
method: 'get'
})
export const PostSharedScreen = (roomNum: string) =>
request({
url: `/room/shared-screen?roomNum=${roomNum}`,
method: 'post'
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
src/assets/icon56.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,45 @@
.feedBackModel {
max-height: 80vh;
display: flex;
flex-direction: column;
.feedBackModelContent {
flex-grow: 1;
overflow-y: auto;
margin: 10px 0;
.feedBackModelContentRate {
margin-bottom: 20px;
background-color: #101215;
padding: 10px 20px 30px;
box-sizing: border-box;
}
.feedBackModelContentList {
margin-bottom: 20px;
>div:nth-child(2) {
>div {
background-color: #101215;
margin-bottom: 10px;
cursor: pointer;
color: #7F859B;
font-size: 14px;
padding: 4px 8px;
box-sizing: border-box;
border: 1px transparent solid;
}
.active {
color: white;
border: 1px #495EAD solid;
}
}
}
}
.feedBackModelFooter {
flex-shrink: 0;
display: flex;
justify-content: flex-end;
}
}

View File

@ -0,0 +1,138 @@
import { PostFeedback } from '@/api/Home/Index';
import styles from '@/components/FeedBackModel/index.module.scss'
import { Button, message, Modal, Rate } from 'antd';
import TextArea from 'antd/es/input/TextArea';
import { useState, useImperativeHandle, forwardRef } from "react";
const FeedBackModel = forwardRef((_props: any, ref: any) => {
useImperativeHandle(ref, () => ({
changeModal: () => {
setIsFeedBackModel(true)
},
}))
const [isFeedBackModel, setIsFeedBackModel] = useState(false);
const [feedBackForm, setFeedBackForm] = useState({
rateValue: 0,
otherContent: '',
});
const [feedBackList, setFeedBackList] = useState([
{
text: "软件卡顿",
value: 2,
active: false,
},
{
text: "设计不合理",
value: 3,
active: false,
},
{
text: "功能太少",
value: 4,
active: false,
},
{
text: "通话不流畅",
value: 5,
active: false,
},
{
text: "视频卡顿",
value: 6,
active: false,
},
{
text: "操作麻烦",
value: 7,
active: false,
},
{
text: "其他,需要手动填写",
value: 1,
active: false,
},
]);
return (
<>
<Modal
title="反馈建议评分"
open={isFeedBackModel}
footer={null}
destroyOnClose={true}
onCancel={() => setIsFeedBackModel(false)}
centered
width={'500px'}
>
<div className={styles.feedBackModel}>
<div className={styles.feedBackModelContent}>
<div className={styles.feedBackModelContentRate}>
<div style={{ color: 'white', fontSize: '14px', marginBottom: '4px' }}>:</div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Rate value={feedBackForm.rateValue} allowHalf style={{ transform: 'scale(2)' }} allowClear onChange={(e) => {
setFeedBackForm({
...feedBackForm,
rateValue: e
})
}} />
</div>
</div>
<div className={styles.feedBackModelContentList}>
<div style={{ color: 'white', fontSize: '14px', marginBottom: '4px' }}>:</div>
<div>
{
feedBackList.map((item, index) => {
return (
<div key={index} className={item.active ? styles.active : ''} onClick={() => {
const feedBackListTemp = [...feedBackList]
feedBackListTemp[index].active = !feedBackListTemp[index].active
setFeedBackList(feedBackListTemp)
}}>
<span>{item.text}</span>
</div>
)
})
}
</div>
</div>
<div className={styles.feedBackModelContentList} style={{ visibility: feedBackList[feedBackList.length - 1].active ? 'visible' : 'hidden' }}>
<TextArea
placeholder="填写意见"
value={feedBackForm.otherContent}
autoSize={{ minRows: 3, maxRows: 6 }}
onChange={(e) => {
setFeedBackForm({
...feedBackForm,
otherContent: e.target.value
})
}}
/>
</div>
</div>
<div className={styles.feedBackModelFooter}>
<Button type="primary" className='m-ant-btn'
onClick={() => {
let parmes = {
score: feedBackForm.rateValue,
otherContent: feedBackList[feedBackList.length - 1].active ? feedBackForm.otherContent : '',
types: feedBackList.filter(row => row.active).map((item: any) => item.value),
}
if (feedBackForm.rateValue === 0) {
message.error('请选择评分')
return
}
PostFeedback(parmes).then(res => {
if (res.code === 200) {
message.success('提交成功!')
setIsFeedBackModel(false)
}
})
}}></Button>
<Button type="primary" style={{ backgroundColor: 'rgb(16,20,24)', marginLeft: '20px' }}
onClick={() => setIsFeedBackModel(false)}></Button>
</div>
</div>
</Modal>
</>
)
})
export default FeedBackModel

View File

@ -170,7 +170,7 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
setJoinRoomSettingForm(list) setJoinRoomSettingForm(list)
if (index === 1) { if (index === 1) {
if (list[index].active) { if (list[index].active) {
agora.startPreview('videoPreview', Number(user.screenShareId)) agora.startPreview('videoPreview', Number(user.screenShareId), +new Date())
} }
} }
} else { } else {

View File

@ -11,6 +11,7 @@ import { storageSeeting } from '@/utils/package/public';
let meetingUserInfo = '' as any; let meetingUserInfo = '' as any;
const fs = require('fs').promises; const fs = require('fs').promises;
const { exec } = require('child_process'); const { exec } = require('child_process');
let c = +new Date();
const StupWizard = forwardRef((_props: any, ref: any) => { const StupWizard = forwardRef((_props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeModal: (index: number = 0, data: any) => { changeModal: (index: number = 0, data: any) => {
@ -84,6 +85,8 @@ const StupWizard = forwardRef((_props: any, ref: any) => {
{list.map((row: any, index: number) => { {list.map((row: any, index: number) => {
return ( return (
<div key={index} className={`${row.active ? styles.active : ''}`} onClick={async () => { <div key={index} className={`${row.active ? styles.active : ''}`} onClick={async () => {
const userInfo = JSON.parse(storage.getItem('user') as string)
await agora.destroyRendererByConfigPreview(Number(userInfo.screenShareId), c)
const newList = [...list]; const newList = [...list];
newList.forEach(item => item.active = false); newList.forEach(item => item.active = false);
newList[index].active = true; newList[index].active = true;
@ -106,9 +109,11 @@ const StupWizard = forwardRef((_props: any, ref: any) => {
cursor: 'pointer' cursor: 'pointer'
}} }}
onClick={async () => { onClick={async () => {
const userInfo = JSON.parse(storage.getItem('user') as string)
if (location.hash.indexOf('/meeting') === -1) { if (location.hash.indexOf('/meeting') === -1) {
agora.release() agora.release()
} }
await agora.destroyRendererByConfigPreview(Number(userInfo.screenShareId), c)
setIsStupWizard(false) setIsStupWizard(false)
}} }}
/> />
@ -247,7 +252,7 @@ const VideoComponents = () => {
}) })
if (setting.videoDeviceId && list.length) { if (setting.videoDeviceId && list.length) {
await agora.setVideoDeviceManager(setting.videoDeviceId) await agora.setVideoDeviceManager(setting.videoDeviceId)
await agora.startPreview('videoPreview', Number(userInfo.screenShareId)) await agora.startPreview('videoPreview', Number(userInfo.screenShareId), c)
} }
}) })
} }
@ -305,7 +310,7 @@ const VideoComponents = () => {
agora.setVideoDeviceManager(e) agora.setVideoDeviceManager(e)
if (!setting.videoDeviceId) { if (!setting.videoDeviceId) {
const userInfo = JSON.parse(storage.getItem('user') as string) const userInfo = JSON.parse(storage.getItem('user') as string)
await agora.startPreview('videoPreview', Number(userInfo.screenShareId)) await agora.startPreview('videoPreview', Number(userInfo.screenShareId), c)
} }
setting.videoDeviceId = e; setting.videoDeviceId = e;
storage.setItem('setting', JSON.stringify(setting)) storage.setItem('setting', JSON.stringify(setting))

View File

@ -8,11 +8,12 @@ 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 { useLocation, useNavigate } from 'react-router-dom';
import { role } from '@/config/role'; import { role } from '@/config/role';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import StupWizard from '@/components/StupWizard'; import StupWizard from '@/components/StupWizard';
import { GetSubDpList } from '@/api/Home/User'; import { GetSubDpList } from '@/api/Home/User';
import FeedBackModel from '@/components/FeedBackModel';
const { setInterval, clearInterval } = require('timers'); const { setInterval, clearInterval } = require('timers');
const fs = require('fs').promises; const fs = require('fs').promises;
const { exec } = require('child_process'); const { exec } = require('child_process');
@ -20,6 +21,7 @@ const { RangePicker } = DatePicker;
const { confirm } = Modal; const { confirm } = Modal;
const Index: React.FC = () => { const Index: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { state } = useLocation();
const [list, setList] = useState({ const [list, setList] = useState({
data: [], data: [],
total: 0, total: 0,
@ -37,6 +39,7 @@ const Index: React.FC = () => {
}) })
const joinSettingRef = useRef<any>(); const joinSettingRef = useRef<any>();
const stupWizardRef = useRef<any>(); const stupWizardRef = useRef<any>();
const feedBackModelRef = useRef<any>();
const [user, setUser] = useState<any>({}); const [user, setUser] = useState<any>({});
const [currentRoomInfo, setCurrentRoomInfo] = useState<any>({}); const [currentRoomInfo, setCurrentRoomInfo] = useState<any>({});
const [subjectList, setSubjectList] = useState<any>([]); const [subjectList, setSubjectList] = useState<any>([]);
@ -47,6 +50,9 @@ const Index: React.FC = () => {
const userInfo = JSON.parse(storage.getItem('user') as string) const userInfo = JSON.parse(storage.getItem('user') as string)
useEffect(() => { useEffect(() => {
setUser(userInfo) setUser(userInfo)
if (state?.currentSeconds >= 600) {
feedBackModelRef.current.changeModal()
}
}, []) }, [])
useEffect(() => { useEffect(() => {
let time = null as any let time = null as any
@ -551,6 +557,7 @@ const Index: React.FC = () => {
</Modal> </Modal>
<JoinSetting ref={joinSettingRef} /> <JoinSetting ref={joinSettingRef} />
<StupWizard ref={stupWizardRef} /> <StupWizard ref={stupWizardRef} />
<FeedBackModel ref={feedBackModelRef} />
</> </>
) )
} }

View File

@ -130,7 +130,7 @@
padding-top: 10px; padding-top: 10px;
margin-top: 10px; margin-top: 10px;
@for $i from 1 through 2 { @for $i from 1 through 3 {
>div:nth-child(#{$i}) { >div:nth-child(#{$i}) {
display: flex; display: flex;
align-items: center; align-items: center;
@ -144,13 +144,16 @@
height: 16px; height: 16px;
@if $i ==1 { @if $i ==1 {
background: url('/src/assets/icon16.png') no-repeat center/cover; background: url('/src/assets/icon56.png') no-repeat center/120%;
} }
@else if $i ==2 { @else if $i ==2 {
background: url('/src/assets/icon15.png') no-repeat center/cover; background: url('/src/assets/icon16.png') no-repeat center/cover;
} }
@else if $i ==3 {
background: url('/src/assets/icon15.png') no-repeat center/cover;
}
} }
>span { >span {
@ -162,10 +165,14 @@
&:hover { &:hover {
>div { >div {
@if $i ==1 { @if $i ==1 {
background: url('/src/assets/icon16-active.png') no-repeat center/cover; background: url('/src/assets/icon56-active.png') no-repeat center/120%;
} }
@else if $i ==2 { @else if $i ==2 {
background: url('/src/assets/icon16-active.png') no-repeat center/cover;
}
@else if $i ==3 {
background: url('/src/assets/icon15-active.png') no-repeat center/cover; background: url('/src/assets/icon15-active.png') no-repeat center/cover;
} }
} }

View File

@ -1,7 +1,7 @@
import styles from '@/page/Home/index.module.scss' import styles from '@/page/Home/index.module.scss'
import { useEffect, useState, useRef } from "react"; import { useEffect, useState, useRef } from "react";
import { Outlet, useNavigate } from 'react-router-dom'; import { Outlet, useNavigate } from 'react-router-dom';
import { Popconfirm } from 'antd'; import { Popconfirm, Popover } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn' import 'dayjs/locale/zh-cn'
import { storage } from '@/utils'; import { storage } from '@/utils';
@ -130,6 +130,17 @@ const Home: React.FC = () => {
{version} {version}
</div> </div>
<div> <div>
<Popover
placement="right"
content={
<img style={{ width: '400px' }} src={'https://meeting-api.23544.com/meeting/update/ddq.png'} alt="" />
}
>
<div className='drag' title='反馈建议'>
<div></div>
<span></span>
</div>
</Popover>
<div className='drag' title='设置' onClick={() => { <div className='drag' title='设置' onClick={() => {
stupWizardRef.current.changeModal() stupWizardRef.current.changeModal()
}}> }}>

View File

@ -68,6 +68,7 @@ const NoticeWindow: React.FC = () => {
</div> </div>
</div>, </div>,
onClose: () => { onClose: () => {
setTimeout(() => {
const dom = document.getElementsByClassName('ant-notification') const dom = document.getElementsByClassName('ant-notification')
if (dom.length === 0) { if (dom.length === 0) {
window.electron.setChildWindowShow({ window.electron.setChildWindowShow({
@ -75,6 +76,7 @@ const NoticeWindow: React.FC = () => {
bool: false bool: false
}) })
} }
}, 500);
}, },
duration: 10, duration: 10,
placement: 'bottomRight', placement: 'bottomRight',

View File

@ -4,6 +4,7 @@ import styles from '@/page/Meeting/ShareScreenWindow/index.module.scss'
import { storage } from '@/utils'; import { storage } from '@/utils';
import ImageUrl from '@/utils/package/imageUrl'; import ImageUrl from '@/utils/package/imageUrl';
import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons'; import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons';
import { RtcStats } from 'agora-electron-sdk';
import { Button } from 'antd'; import { Button } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
@ -50,6 +51,12 @@ const ShareScreenWindow: React.FC = () => {
const [timeStr, setTimeStr] = useState(0) const [timeStr, setTimeStr] = useState(0)
const [isExpand, setIsExpand] = useState(false) const [isExpand, setIsExpand] = useState(false)
const [roomUserLists, setRoomUserLists] = useState<any>([]) const [roomUserLists, setRoomUserLists] = useState<any>([])
const [currentEffective, setCurrentEffective] = useState(3)
const [networkOther, setNetworkOther] = useState<RtcStats>({})
const [networkQuality, setNetworkQuality] = useState({
level: '佳',
text: '网络质量极好'
})
const channel = new BroadcastChannel('meeting_channel'); const channel = new BroadcastChannel('meeting_channel');
const userInfo = JSON.parse(storage.getItem('user') as string) const userInfo = JSON.parse(storage.getItem('user') as string)
let timeout: NodeJS.Timeout; let timeout: NodeJS.Timeout;
@ -97,6 +104,11 @@ const ShareScreenWindow: React.FC = () => {
case 'roomUserList': case 'roomUserList':
setRoomUserLists(data.parmes.roomUserList) setRoomUserLists(data.parmes.roomUserList)
break; break;
case 'nnetworkStatus':
setCurrentEffective(data.parmes.currentEffective)
setNetworkQuality(data.parmes.networkQuality)
setNetworkOther(data.parmes.networkOther)
break;
} }
}) })
return () => { return () => {
@ -141,13 +153,19 @@ const ShareScreenWindow: React.FC = () => {
<> <>
<div className={styles.shareScreenWindow} style={{ width: isExpand ? '100%' : '100%' }}> <div className={styles.shareScreenWindow} style={{ width: isExpand ? '100%' : '100%' }}>
<div className={styles.shareScreenWindowTitle}> <div className={styles.shareScreenWindowTitle}>
<span>{changeCurrentSeconds(timeStr)} </span> <span>{changeCurrentSeconds(timeStr)}
{isExpand ? <span className='drag' onClick={() => { {networkIcon(currentEffective)}
<span style={{ color: 'white', marginLeft: '30px' }}>
<span style={{ marginRight: '10px' }}>{networkQuality.level}</span>
<span>{networkOther.lastmileDelay}ms</span>
</span>
</span>
{isExpand ? <span className='drag' style={{ flexShrink: 0 }} onClick={() => {
channel.postMessage({ channel.postMessage({
type: 'shareScreenWindowClose', type: 'shareScreenWindowClose',
shareScreenWindowClose: timeStr shareScreenWindowClose: timeStr
}); });
}}></span> : <span style={{ visibility: 'hidden' }}></span>} }}></span> : <span style={{ visibility: 'hidden', flexShrink: 0 }}></span>}
</div> </div>
{isExpand ? null : <div className={`${styles.shareScreenWindowContent} drag`}> {isExpand ? null : <div className={`${styles.shareScreenWindowContent} drag`}>
<div className={styles.shareScreenWindowContentList}> <div className={styles.shareScreenWindowContentList}>
@ -217,4 +235,60 @@ const ShareScreenWindow: React.FC = () => {
) )
} }
const networkIcon = (network: number) => {
switch (network) {
case 0:
return <svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg" className='drag' style={{ transform: 'scale(0.5)', position: 'absolute', top: '-4px' }}>
<g clip-path="url(#clip0_21262_3609)">
<path d="M4.44094 32C3.03695 32 1.90039 31.15 1.90039 30.1V15.3C1.90039 14.25 3.03695 13.4 4.44094 13.4C5.84492 13.4 6.98149 14.25 6.98149 15.3V30.1C6.98149 31.15 5.84492 32 4.44094 32ZM17.01 32C15.606 32 14.4694 31.15 14.4694 30.1V8.9C14.4694 7.85 15.606 7 17.01 7C18.4139 7 19.5505 7.85 19.5505 8.9V30.1C19.5505 31.15 18.4139 32 17.01 32ZM29.579 32C28.175 32 27.0384 31.15 27.0384 30.1V1.9C27.0384 0.85 28.175 0 29.579 0C30.983 0 32.1195 0.85 32.1195 1.9V30.1C32.1195 31.15 30.983 32 29.579 32Z" fill="#7C8280" />
<path d="M7.00124 2.11509L5.01758 4.09875L3.03391 2.11509C2.77629 1.85747 2.35122 1.85747 2.0936 2.11509C1.83599 2.37271 1.83599 2.79778 2.0936 3.0554L4.07727 5.03906L2.0936 7.02273C1.83599 7.28035 1.83599 7.70542 2.0936 7.96304C2.35122 8.22065 2.77629 8.22065 3.03391 7.96304L5.01758 5.97937L7.00124 7.96304C7.25886 8.22065 7.68393 8.22065 7.94155 7.96304C8.19917 7.70542 8.19917 7.28035 7.94155 7.02273L5.95789 5.03906L7.94155 3.0554C8.19917 2.79778 8.19917 2.37271 7.94155 2.11509C7.68393 1.85747 7.25886 1.85747 7.00124 2.11509Z" fill="#F90000" />
</g>
<defs>
<clipPath id="clip0_21262_3609">
<rect width="32" height="32" fill="white" transform="translate(0.119141)" />
</clipPath>
</defs>
</svg>
case 1:
return <svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg" className='drag' style={{ transform: 'scale(0.5)', position: 'absolute', top: '-4px' }}>
<g clip-path="url(#clip0_21262_3615)">
<path d="M26.9316 30.1C26.9316 31.15 28.0918 32 29.5249 32C30.9581 32 32.1182 31.15 32.1182 30.1V1.9C32.1182 0.85 30.9581 0 29.5249 0C28.0918 0 26.9316 0.85 26.9316 1.9V30.1Z" fill="#7C8280" />
<path d="M14.1035 30.1C14.1035 31.15 15.2637 32 16.6968 32C18.1299 32 19.2901 31.15 19.2901 30.1V8.9C19.2901 7.85 18.1299 7 16.6968 7C15.2637 7 14.1035 7.85 14.1035 8.9V30.1Z" fill="#7C8280" />
<path d="M1.27344 30.1004C1.27344 31.1504 2.4336 32.0004 3.86673 32.0004C5.29986 32.0004 6.46002 31.1504 6.46002 30.1004V15.3004C6.46002 14.2504 5.29986 13.4004 3.86673 13.4004C2.4336 13.4004 1.27344 14.2504 1.27344 15.3004V30.1004Z" fill="#FF800B" />
</g>
<defs>
<clipPath id="clip0_21262_3615">
<rect width="32" height="32" fill="white" transform="translate(0.119141)" />
</clipPath>
</defs>
</svg>
case 2:
return <svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg" className='drag' style={{ transform: 'scale(0.5)', position: 'absolute', top: '-4px' }}>
<g clip-path="url(#clip0_21262_3621)">
<path d="M26.9316 30.1C26.9316 31.15 28.0918 32 29.5249 32C30.9581 32 32.1182 31.15 32.1182 30.1V1.9C32.1182 0.85 30.9581 0 29.5249 0C28.0918 0 26.9316 0.85 26.9316 1.9V30.1Z" fill="#7C8280" />
<path d="M14.1035 30.1C14.1035 31.15 15.2637 32 16.6968 32C18.1299 32 19.2901 31.15 19.2901 30.1V8.9C19.2901 7.85 18.1299 7 16.6968 7C15.2637 7 14.1035 7.85 14.1035 8.9V30.1Z" fill="#FF800B" />
<path d="M1.27344 30.1004C1.27344 31.1504 2.4336 32.0004 3.86673 32.0004C5.29986 32.0004 6.46002 31.1504 6.46002 30.1004V15.3004C6.46002 14.2504 5.29986 13.4004 3.86673 13.4004C2.4336 13.4004 1.27344 14.2504 1.27344 15.3004V30.1004Z" fill="#FF800B" />
</g>
<defs>
<clipPath id="clip0_21262_3621">
<rect width="32" height="32" fill="white" transform="translate(0.119141)" />
</clipPath>
</defs>
</svg>
case 3:
return <svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg" className='drag' style={{ transform: 'scale(0.5)', position: 'absolute', top: '-4px' }}>
<g clip-path="url(#clip0_21262_3625)">
<path d="M26.9316 30.1C26.9316 31.15 28.0918 32 29.5249 32C30.9581 32 32.1182 31.15 32.1182 30.1V1.9C32.1182 0.85 30.9581 0 29.5249 0C28.0918 0 26.9316 0.85 26.9316 1.9V30.1Z" fill="#02B188" />
<path d="M14.1035 30.1C14.1035 31.15 15.2637 32 16.6968 32C18.1299 32 19.2901 31.15 19.2901 30.1V8.9C19.2901 7.85 18.1299 7 16.6968 7C15.2637 7 14.1035 7.85 14.1035 8.9V30.1Z" fill="#02B188" />
<path d="M1.27344 30.1004C1.27344 31.1504 2.4336 32.0004 3.86673 32.0004C5.29986 32.0004 6.46002 31.1504 6.46002 30.1004V15.3004C6.46002 14.2504 5.29986 13.4004 3.86673 13.4004C2.4336 13.4004 1.27344 14.2504 1.27344 15.3004V30.1004Z" fill="#02B188" />
</g>
<defs>
<clipPath id="clip0_21262_3625">
<rect width="32" height="32" fill="white" transform="translate(0.119141)" />
</clipPath>
</defs>
</svg>
}
}
export default ShareScreenWindow export default ShareScreenWindow

View File

@ -232,6 +232,11 @@
position: relative; position: relative;
overflow: hidden; overflow: hidden;
#videoView {
position: relative;
border: 1px red solid;
}
.standardModeIcon { .standardModeIcon {
position: absolute; position: absolute;
left: 50%; left: 50%;
@ -305,6 +310,10 @@
.meetingContentSwiperCard { .meetingContentSwiperCard {
width: 100%; width: 100%;
} }
.meetingContentSwiperCard {
height: calc(100% / 6);
}
} }
// 单画面模式 // 单画面模式
@ -427,6 +436,22 @@
} }
} }
.meetingContentSwiperCaret {
position: absolute;
z-index: 2;
cursor: pointer;
background-color: rgba(0, 0, 0, 0.5);
color: white;
border: 1px white solid;
font-size: 20px;
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
}
.meetingContentBodyLeftBlock { .meetingContentBodyLeftBlock {
position: absolute; position: absolute;
background-color: #1F2022; background-color: #1F2022;

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,6 @@ import {
AudioAinsMode, AudioAinsMode,
SimulcastStreamMode, SimulcastStreamMode,
VideoStreamType, VideoStreamType,
QualityType,
RtcConnection, RtcConnection,
RtcStats, RtcStats,
AudioVolumeInfo, AudioVolumeInfo,
@ -32,7 +31,6 @@ const os = require("os");
const option: any = { const option: any = {
appId: '', appId: '',
token: '', token: '',
tokenA: '',
channelId: '', channelId: '',
uid: '', uid: '',
screenShareId: '', screenShareId: '',
@ -153,7 +151,7 @@ export const agora = {
}, 1000); }, 1000);
}, },
// 事件回调 // 事件回调
registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline, onAudioVolumeIndication, onNetworkQuality, onRtcStats, onConnectionStateChanged, onLocalVideoStateChanged, onConnectionLost }: any) => { registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline, onAudioVolumeIndication, onRtcStats, onConnectionStateChanged, onLocalVideoStateChanged, onConnectionLost, onTokenPrivilegeWillExpire }: any) => {
rtcEngine.registerEventHandler({ rtcEngine.registerEventHandler({
// 监听本地用户加入频道事件 // 监听本地用户加入频道事件
onJoinChannelSuccess: async (connection: RtcConnection, elapsed: number) => { onJoinChannelSuccess: async (connection: RtcConnection, elapsed: number) => {
@ -183,10 +181,6 @@ export const agora = {
onAudioVolumeIndication: async (_connection: RtcConnection, speakers: AudioVolumeInfo[], _speakerNumber: number, _totalVolume: number) => { onAudioVolumeIndication: async (_connection: RtcConnection, speakers: AudioVolumeInfo[], _speakerNumber: number, _totalVolume: number) => {
await onAudioVolumeIndication?.(speakers) await onAudioVolumeIndication?.(speakers)
}, },
//通话中每个用户的网络上下行 last mile 质量报告回调。
onNetworkQuality: async (connection: RtcConnection, remoteUid: number, txQuality: QualityType, rxQuality: QualityType) => {
await onNetworkQuality?.(connection, remoteUid, txQuality, rxQuality)
},
//当前通话相关的统计信息回调。 //当前通话相关的统计信息回调。
onRtcStats: async (_connection: RtcConnection, stats: RtcStats) => { onRtcStats: async (_connection: RtcConnection, stats: RtcStats) => {
await onRtcStats?.(stats) await onRtcStats?.(stats)
@ -202,9 +196,19 @@ export const agora = {
// 网络连接中断,且 SDK 无法在 10 秒内连接服务器回调。 // 网络连接中断,且 SDK 无法在 10 秒内连接服务器回调。
onConnectionLost: (_connection: RtcConnection) => { onConnectionLost: (_connection: RtcConnection) => {
onConnectionLost?.() onConnectionLost?.()
},
// Token 即将在 30s 内过期回调。
onTokenPrivilegeWillExpire: (connection: RtcConnection, token: string) => {
onTokenPrivilegeWillExpire?.(connection, token)
} }
}); });
}, },
// 刷新token
refreshToken: async (data: any) => {
await rtcEngine.updateChannelMediaOptionsEx({
token: data.token,
}, data.connection);
},
// 获取视图模式 // 获取视图模式
getRrenderMode: (uid: number) => { getRrenderMode: (uid: number) => {
if (String(uid).length === 9) { if (String(uid).length === 9) {
@ -220,7 +224,7 @@ export const agora = {
return return
} }
await rtcEngine.setupLocalVideo({ await rtcEngine.setupLocalVideo({
renderMode: agora.getRrenderMode(item.uid), renderMode: item.renderMode || agora.getRrenderMode(item.uid),
sourceType: item.sourceType, sourceType: item.sourceType,
uid: item.uid, uid: item.uid,
view: item.view, view: item.view,
@ -233,7 +237,7 @@ export const agora = {
if (item.view?.childNodes.length === 1) { if (item.view?.childNodes.length === 1) {
await rtcEngine.setupRemoteVideo( await rtcEngine.setupRemoteVideo(
{ {
renderMode: agora.getRrenderMode(item.uid), renderMode: item.renderMode || agora.getRrenderMode(item.uid),
sourceType: VideoSourceType.VideoSourceRemote, sourceType: VideoSourceType.VideoSourceRemote,
uid: item.uid, uid: item.uid,
view: item.view, view: item.view,
@ -247,7 +251,7 @@ export const agora = {
if (item.view?.childNodes.length === 1) { if (item.view?.childNodes.length === 1) {
await rtcEngine.setupRemoteVideoEx( await rtcEngine.setupRemoteVideoEx(
{ {
renderMode: agora.getRrenderMode(item.uid), renderMode: item.renderMode || agora.getRrenderMode(item.uid),
sourceType: VideoSourceType.VideoSourceRemote, sourceType: VideoSourceType.VideoSourceRemote,
uid: item.uid, uid: item.uid,
view: item.view, view: item.view,
@ -261,7 +265,7 @@ export const agora = {
setupRemoteVideo: async (item: any) => { setupRemoteVideo: async (item: any) => {
await rtcEngine.setupRemoteVideo( await rtcEngine.setupRemoteVideo(
{ {
renderMode: agora.getRrenderMode(item.uid), renderMode: item.renderMode || agora.getRrenderMode(item.uid),
sourceType: VideoSourceType.VideoSourceRemote, sourceType: VideoSourceType.VideoSourceRemote,
uid: item.uid, uid: item.uid,
view: item.view, view: item.view,
@ -320,10 +324,10 @@ export const agora = {
) )
}, },
// 共享屏幕单独用户 // 共享屏幕单独用户
joinChannelEx: async (uid: any) => { joinChannelEx: async (uid: any, token: string) => {
await agora.leaveChannelEx(uid) await agora.leaveChannelEx(uid)
await rtcEngine.joinChannelEx( await rtcEngine.joinChannelEx(
option.token, token,
{ channelId: option.channelId, localUid: Number(uid) }, { channelId: option.channelId, localUid: Number(uid) },
{ {
autoSubscribeAudio: false,//设置是否自动订阅所有音频流 autoSubscribeAudio: false,//设置是否自动订阅所有音频流
@ -336,11 +340,11 @@ export const agora = {
); );
}, },
// 所有用户加入的第二个房间 // 所有用户加入的第二个房间
allJoinChannelEx: async (bool: boolean = false) => { allJoinChannelEx: async (bool: boolean = false, token: string) => {
const user = await JSON.parse(storage.getItem('user') as string) const user = await JSON.parse(storage.getItem('user') as string)
await agora.startCameraCapture(true) await agora.startCameraCapture(true)
await rtcEngine.joinChannelEx( await rtcEngine.joinChannelEx(
option.tokenA, token,
{ channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) }, { channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) },
{ {
clientRoleType: bool ? ClientRoleType.ClientRoleAudience : ClientRoleType.ClientRoleBroadcaster, //用户角色 ClientRoleBroadcaster 主播 ClientRoleAudience 观众 clientRoleType: bool ? ClientRoleType.ClientRoleAudience : ClientRoleType.ClientRoleBroadcaster, //用户角色 ClientRoleBroadcaster 主播 ClientRoleAudience 观众
@ -382,10 +386,14 @@ export const agora = {
}, },
// 销毁视频渲染dom // 销毁视频渲染dom
destroyRendererByConfig: async (uid: number, channelId?: string) => { destroyRendererByConfig: async (uid: number, channelId?: string) => {
await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, channelId, uid); await rtcEngine.destroyRendererByConfig(option.uid === uid ? VideoSourceType.VideoSourceCameraPrimary : VideoSourceType.VideoSourceRemote, channelId, uid);
}, },
destroyRendererByView: async () => { destroyRendererByConfigPreview: async (uid: number, channelId: number) => {
let dom = document.getElementById(`meetingAbsoluteVideo`); await agora.destroyRendererByView('videoPreview')
await rtcEngine.leaveChannelEx({ channelId: `${channelId + uid}`, localUid: Number(uid) })
},
destroyRendererByView: async (key: string) => {
let dom = document.getElementById(key);
if (dom) { if (dom) {
await rtcEngine.destroyRendererByView(dom); await rtcEngine.destroyRendererByView(dom);
} }
@ -446,7 +454,6 @@ export const agora = {
// 加入频道 // 加入频道
setJoinChannel: async (data: any) => { setJoinChannel: async (data: any) => {
option.token = data.token; option.token = data.token;
option.tokenA = data.tokenA;
option.channelId = data.channelId; option.channelId = data.channelId;
option.uid = Number(data.uid); option.uid = Number(data.uid);
option.screenShareId = data.screenShareId; option.screenShareId = data.screenShareId;
@ -457,7 +464,7 @@ export const agora = {
return await rtcEngine.getScreenCaptureSources(thumbSize, iconSize, includeScreen) return await rtcEngine.getScreenCaptureSources(thumbSize, iconSize, includeScreen)
}, },
// 共享屏幕采集 // 共享屏幕采集
setDesktopCapturerVideo: async (targetSource: any, isComputerAudio: boolean, isFluencyPriority: boolean) => { setDesktopCapturerVideo: async (targetSource: any, isComputerAudio: boolean, isFluencyPriority: boolean, token: string) => {
const user = JSON.parse(storage.getItem('user') as string) const user = JSON.parse(storage.getItem('user') as string)
agora.stopScreenCapture(); agora.stopScreenCapture();
if (isComputerAudio) { if (isComputerAudio) {
@ -493,7 +500,7 @@ export const agora = {
} }
); );
} }
await agora.joinChannelEx(user.screenShareId) await agora.joinChannelEx(user.screenShareId, token)
}, },
// 获取系统中所有的视频设备列表。 // 获取系统中所有的视频设备列表。
getVideoDeviceManager: async (): Promise<any> => { getVideoDeviceManager: async (): Promise<any> => {
@ -507,12 +514,12 @@ export const agora = {
await rtcEngine.getVideoDeviceManager().setDevice(deviceIdUTF8) await rtcEngine.getVideoDeviceManager().setDevice(deviceIdUTF8)
}, },
// 开启本地视频预览 // 开启本地视频预览
startPreview: async (id: string, uid: number): Promise<void> => { startPreview: async (id: string, uid: number, channelId: number): Promise<void> => {
rtcEngine.enableVideo(); rtcEngine.enableVideo();
rtcEngine.startPreview(); rtcEngine.startPreview();
await GetRoomRtcToken(`${+new Date()}`).then(async (res) => { await GetRoomRtcToken(`${channelId + uid}`).then(async (res) => {
await rtcEngine.joinChannelEx(res.data, { await rtcEngine.joinChannelEx(res.data, {
channelId: `${+new Date() + uid}`, channelId: `${channelId + uid}`,
localUid: uid, localUid: uid,
}, { }, {
channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting, channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,

View File

@ -84,6 +84,8 @@ import icon52Select from '@/assets/icon52-select.png'
import icon53 from '@/assets/icon53.png' import icon53 from '@/assets/icon53.png'
import icon54 from '@/assets/icon54.png' import icon54 from '@/assets/icon54.png'
import icon55 from '@/assets/icon55.png' import icon55 from '@/assets/icon55.png'
import icon56 from '@/assets/icon56.png'
import icon56Active from '@/assets/icon56-active.png'
export default { export default {
loading, loading,
icon, icon,
@ -170,5 +172,7 @@ export default {
icon52Select, icon52Select,
icon53, icon53,
icon54, icon54,
icon55 icon55,
icon56,
icon56Active
} }

View File

@ -405,3 +405,16 @@ $pagination-hover-background-color: #5575F2;
.ant-tabs { .ant-tabs {
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
} }
.ant-rate .ant-rate-star-first,
.ant-rate .ant-rate-star-second {
color: gray;
}
.hideCancelText {
.ant-modal-confirm-btns {
>button:nth-child(1){
display: none;
}
}
}

View File

@ -64,7 +64,6 @@ export default defineConfig({
AudioAinsMode, AudioAinsMode,
SimulcastStreamMode, SimulcastStreamMode,
VideoStreamType, VideoStreamType,
QualityType,
RtcConnection, RtcConnection,
RtcStats, RtcStats,
AudioVolumeInfo, AudioVolumeInfo,
@ -90,7 +89,6 @@ export default defineConfig({
AudioAinsMode, AudioAinsMode,
SimulcastStreamMode, SimulcastStreamMode,
VideoStreamType, VideoStreamType,
QualityType,
RtcConnection, RtcConnection,
RtcStats, RtcStats,
AudioVolumeInfo, AudioVolumeInfo,