Merge pull request 'yangjie' (#45) from yangjie into master

Reviewed-on: #45
This commit is contained in:
yangqiang 2024-12-18 13:52:10 +08:00
commit f47681d2f1
11 changed files with 334 additions and 105 deletions

View File

@ -181,6 +181,7 @@ app.on('ready', () => {
connection.off('DriverList'); connection.off('DriverList');
connection.off('SetDriver'); connection.off('SetDriver');
connection.off('ShowDriverList'); connection.off('ShowDriverList');
connection.off('ModifyNickName');
} }
}); });
ipcMain.handle('onStop', (event) => { ipcMain.handle('onStop', (event) => {
@ -370,6 +371,14 @@ app.on('ready', () => {
driversJsonString driversJsonString
}) })
}); });
// 修改用户名称
connection.on("ModifyNickName", (uid, nickName) => {
mainWindow.webContents.send('onSignalr', {
key: 'ModifyNickName',
uid,
nickName
})
});
} }
}); });
// 放大缩小退出窗口 // 放大缩小退出窗口

View File

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

View File

@ -147,4 +147,11 @@ export const PostRoomSingnIn = (data: any) =>
url: `/room/sign-in`, url: `/room/sign-in`,
method: 'post', method: 'post',
data data
})
export const PutAlterUname = (data: any) =>
request({
url: `/room/alter-uname`,
method: 'put',
data
}) })

View File

@ -20,18 +20,17 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
if (location.hash.indexOf('/meeting') === -1) { if (location.hash.indexOf('/meeting') === -1) {
await agora.init() await agora.init()
} }
setJoinRoomSettingForm((res: any) => { const list = [...joinRoomSettingForm]
res.forEach(async (item: any, index: number) => { list.forEach(async (item: any, index: number) => {
if (index === 0 && role.ID.includes(userInfo.roleId)) { if (index === 0 && role.ID.includes(userInfo.roleId)) {
await agora.getAudioMediaList().then(res => { await agora.getAudioMediaList().then(res => {
item.active = res.ecordingList.length ? true : false item.active = res.ecordingList.length ? true : false
}) })
} else { } else {
item.active = false item.active = false
} }
}); });
return res setJoinRoomSettingForm(list)
})
setRoomNumber(roomNum) setRoomNumber(roomNum)
getDeviceList() getDeviceList()
} }

View File

@ -1,12 +1,13 @@
import styles from '@/components/SharedFilesModel/index.module.scss' import styles from '@/components/SharedFilesModel/index.module.scss'
import { import {
DeleteOutlined, DeleteOutlined,
LoadingOutlined,
ProfileOutlined, ProfileOutlined,
ReloadOutlined, ReloadOutlined,
SearchOutlined, SearchOutlined,
VerticalAlignBottomOutlined VerticalAlignBottomOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import { Button, Input, message, Modal, Pagination, Progress, Table } from 'antd'; import { Button, Input, message, Modal, Pagination, Popconfirm, Progress, Table } from 'antd';
import { forwardRef, useEffect, useImperativeHandle, useState, useRef } from "react"; import { forwardRef, useEffect, useImperativeHandle, useState, useRef } from "react";
import { DeleteRoomFile, GetRoomFile, GetRoomFileDwUrl, GetRoomUpFileurl, GetRoomUserItem, PostRoomFile } from '@/api/Meeting'; import { DeleteRoomFile, GetRoomFile, GetRoomFileDwUrl, GetRoomUpFileurl, GetRoomUserItem, PostRoomFile } from '@/api/Meeting';
import axios from 'axios'; import axios from 'axios';
@ -100,10 +101,10 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
</div> </div>
<div style={{ color: 'white' }}> <div style={{ color: 'white' }}>
<Input <Input
placeholder="搜索" placeholder="请输入文件名"
style={{ width: '200px' }} style={{ width: '200px' }}
prefix={<SearchOutlined style={{ color: 'white' }} />} prefix={<SearchOutlined style={{ color: 'white' }} />}
onChange={(e) => { onPressEnter={(e: any) => {
if (fileList.pageIndex === 1) { if (fileList.pageIndex === 1) {
setFileList({ setFileList({
...fileList, ...fileList,
@ -116,8 +117,30 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
pageIndex: 1 pageIndex: 1
}) })
} }
}} }}
onBlur={(e) => {
if (fileList.pageIndex === 1) {
setFileList({
...fileList,
keyword: e.target.value,
})
} else {
setFileList({
...fileList,
keyword: e.target.value,
pageIndex: 1
})
}
}}
suffix={
<span
style={{ color: '#47D3D0', cursor: 'pointer' }}
onClick={() => {
getRoomFile()
}}
>
</span>
}
/> />
<ReloadOutlined title='刷新' onClick={() => { <ReloadOutlined title='刷新' onClick={() => {
if (fileList.pageIndex === 1) { if (fileList.pageIndex === 1) {
@ -129,29 +152,41 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
}) })
} }
}} /> }} />
{roomUserItem && role.ID.includes(roomUserItem.roleId) || roomUserItem.isRoomManager ? <ProfileOutlined title={showRowSelection ? '取消框选' : '显示框选'} onClick={() => { {roomUserItem && (role.ID.includes(roomUserItem.roleId) || roomUserItem.isRoomManager) && fileList.data.length ? <ProfileOutlined title={showRowSelection ? '取消框选' : '显示框选'} onClick={() => {
setShowRowSelection(!showRowSelection) setShowRowSelection(!showRowSelection)
}} style={{ color: showRowSelection ? '#5575F2' : 'white' }} /> : null} }} style={{ color: showRowSelection ? '#5575F2' : 'white' }} /> : null}
{showRowSelection ? <DeleteOutlined title='删除' onClick={() => { {showRowSelection && fileList.data.length && selectedRowKeys.length ?
if (selectedRowKeys.length) { <Popconfirm
DeleteRoomFile(selectedRowKeys).then(res => { title="提示"
if (res.code === 200) { description="确认删除吗?"
message.success('删除成功!') onConfirm={() => {
if (fileList.pageIndex === 1) { if (selectedRowKeys.length) {
getRoomFile() DeleteRoomFile(selectedRowKeys).then(res => {
} else { if (res.code === 200) {
setFileList({ message.success('删除成功!')
...fileList, if (fileList.pageIndex === 1) {
pageIndex: 1 getRoomFile()
}) } else {
} setFileList({
...fileList,
pageIndex: 1
})
}
}
})
} else {
message.error('请选择文件!')
} }
}) }}
} else { onCancel={() => {
message.error('请选择文件!')
} }}
}} /> : null} okText="确认"
<Button type="primary" style={{ backgroundColor: '#31353A' }} cancelText="取消"
>
<DeleteOutlined title='删除' />
</Popconfirm> : null}
{roomUserItem && role.ID.includes(roomUserItem.roleId) || roomUserItem.isRoomManager ? <Button type="primary" style={{ backgroundColor: '#31353A' }}
onClick={() => { onClick={() => {
if (isUpFile) { if (isUpFile) {
message.error('文件上传中,请稍后上传。') message.error('文件上传中,请稍后上传。')
@ -211,7 +246,7 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
}; };
file.click(); file.click();
}} }}
></Button> ></Button> : null}
</div> </div>
</div> </div>
<div> <div>
@ -258,9 +293,10 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
/> />
<Column title="操作" render={(item) => ( <Column title="操作" render={(item) => (
<> <>
<VerticalAlignBottomOutlined title='下载' {!item.showPercentComplete ? <VerticalAlignBottomOutlined title='下载'
style={{ color: '#5575F2', cursor: 'pointer' }} style={{ color: '#5575F2', cursor: 'pointer' }}
onClick={async () => { onClick={async () => {
storage.setItem('loading', true)
GetRoomFileDwUrl(item.fileUrl, item.id).then(res => { GetRoomFileDwUrl(item.fileUrl, item.id).then(res => {
if (res.code === 200) { if (res.code === 200) {
axios({ axios({
@ -329,8 +365,10 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
}, 2000) }, 2000)
}) })
} }
}).finally(() => {
storage.setItem('loading', false)
}) })
}} /> }} /> : <LoadingOutlined />}
{/* <FolderOutlined title='文件' style={{ color: '#FFA000', cursor: 'pointer', marginLeft: '10px' }} /> */} {/* <FolderOutlined title='文件' style={{ color: '#FFA000', cursor: 'pointer', marginLeft: '10px' }} /> */}
</> </>
)} /> )} />
@ -341,7 +379,7 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
...fileList, ...fileList,
pageIndex: e pageIndex: e
}) })
}} pageSize={fileList.pageSize} current={fileList.pageIndex} hideOnSinglePage={true} showSizeChanger={false}/> }} pageSize={fileList.pageSize} current={fileList.pageIndex} hideOnSinglePage={true} showSizeChanger={false} />
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,8 @@
.userNameModal {
.userNameModalFooter {
margin-top: 10px;
display: flex;
justify-content: center;
}
}

View File

@ -0,0 +1,74 @@
import { PutAlterUname } from '@/api/Meeting';
import styles from '@/components/UserName/index.module.scss'
import { storage } from '@/utils';
import { Button, Input, message, Modal } from 'antd';
import { useState, useImperativeHandle, forwardRef } from "react";
const UserName = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({
changeModal: (data: any) => {
setInfo(data)
setUserNameModal(true)
}
}))
const [userNameModal, setUserNameModal] = useState(false);
const [info, setInfo] = useState({
userName: '',
uid: ''
});
return (
<>
<Modal
title="修改用户名"
open={userNameModal}
footer={null}
destroyOnClose={true}
onCancel={() => setUserNameModal(false)}
centered
width={'300px'}
>
<div className={styles.userNameModal}>
<div className={styles.userNameModalContent}>
<Input
placeholder="请输入用户名称"
style={{ width: '100%' }}
value={info.userName}
onChange={(e) => {
setInfo({
...info,
userName: e.target.value
})
}}
/>
</div>
<div className={styles.userNameModalFooter}>
<Button type="primary" style={{ backgroundColor: 'rgb(16,20,24)', marginRight: '14px' }}
onClick={() => setUserNameModal(false)}></Button>
<Button type="primary"
onClick={async () => {
const stateInfo = await JSON.parse(storage.getItem('stateInfo') as string);
if (info.userName) {
PutAlterUname({
nickName: info.userName,
roomNum: stateInfo.channelId,
uid: info.uid,
}).then((res) => {
if (res.code === 200) {
message.success('修改成功')
setUserNameModal(false)
}
})
} else {
message.error('请输入用户名')
}
}}
className='m-ant-btn'>
</Button>
</div>
</div>
</Modal>
</>
)
})
export default UserName

View File

@ -1,7 +1,7 @@
import styles from '@/page/Home/Index/index.module.scss' import styles from '@/page/Home/Index/index.module.scss'
import { useEffect, useState, useRef } from "react"; import { useEffect, useState, useRef } from "react";
import Operation from '@/components/Operation'; import Operation from '@/components/Operation';
import { Button, Input, Modal, Pagination, Empty, message, Popover, Popconfirm, DatePicker, Select } from "antd"; import { Button, Input, Modal, Pagination, Empty, message, Popover, Popconfirm, DatePicker, Select, Radio } from "antd";
import { GetRoom, PostRoom, GetCheckoutRoomNum, GetRoomRtcToken, DeleteRoom, GetRecord, PostRoomInfo, GetQrcode } from '@/api/Home/Index'; import { GetRoom, PostRoom, GetCheckoutRoomNum, GetRoomRtcToken, DeleteRoom, GetRecord, PostRoomInfo, GetQrcode } from '@/api/Home/Index';
import ImageUrl from '@/utils/package/imageUrl' import ImageUrl from '@/utils/package/imageUrl'
import { ExclamationCircleFilled, ReloadOutlined } from '@ant-design/icons'; import { ExclamationCircleFilled, ReloadOutlined } from '@ant-design/icons';
@ -42,6 +42,7 @@ const Index: React.FC = () => {
const [subjectList, setSubjectList] = useState<any>([]); const [subjectList, setSubjectList] = useState<any>([]);
const [timeData, setTimeData] = useState<any>([]); const [timeData, setTimeData] = useState<any>([]);
const [isCreateRoom, setIsCreateRoom] = useState<boolean>(false); const [isCreateRoom, setIsCreateRoom] = useState<boolean>(false);
const [allowAnonymous, setAllowAnonymous] = useState(true);
const [baseImage, setBaseImage] = useState(''); const [baseImage, setBaseImage] = useState('');
const userInfo = JSON.parse(storage.getItem('user') as string) const userInfo = JSON.parse(storage.getItem('user') as string)
useEffect(() => { useEffect(() => {
@ -305,6 +306,7 @@ const Index: React.FC = () => {
year: item.year, year: item.year,
id: item.id, id: item.id,
}) })
setAllowAnonymous(item.allowAnonymous)
getSubDpList() getSubDpList()
setIsCreateRoom(false) setIsCreateRoom(false)
setCreateRoomModal(true) setCreateRoomModal(true)
@ -385,7 +387,6 @@ const Index: React.FC = () => {
placeholder="请输入房间号" placeholder="请输入房间号"
style={{ flexGrow: 1 }} style={{ flexGrow: 1 }}
className={styles.letterSpacing} className={styles.letterSpacing}
showCount
maxLength={8} maxLength={8}
value={createRoomFrom.roomNum} value={createRoomFrom.roomNum}
onChange={(e) => { onChange={(e) => {
@ -422,7 +423,6 @@ const Index: React.FC = () => {
<Input.TextArea <Input.TextArea
placeholder="请输入房间名字" placeholder="请输入房间名字"
style={{ flexGrow: 1 }} style={{ flexGrow: 1 }}
showCount
maxLength={30} maxLength={30}
value={createRoomFrom.roomName} value={createRoomFrom.roomName}
onChange={(e) => { onChange={(e) => {
@ -437,6 +437,7 @@ const Index: React.FC = () => {
<span></span> <span></span>
<Input <Input
placeholder="请输入届" placeholder="请输入届"
maxLength={4}
style={{ flexGrow: 1 }} style={{ flexGrow: 1 }}
value={createRoomFrom.year} value={createRoomFrom.year}
onChange={(e) => { onChange={(e) => {
@ -463,6 +464,15 @@ const Index: React.FC = () => {
}) })
}} /> }} />
</div> </div>
<div>
<span></span>
<Radio.Group onChange={(e) => {
setAllowAnonymous(e.target.value);
}} value={allowAnonymous}>
<Radio value={true}></Radio>
<Radio value={false}></Radio>
</Radio.Group>
</div>
</div> </div>
<div style={{ <div style={{
display: 'flex', justifyContent: 'center' display: 'flex', justifyContent: 'center'
@ -483,20 +493,22 @@ const Index: React.FC = () => {
if (bool) { if (bool) {
message.error('房间号已存在!') message.error('房间号已存在!')
} else { } else {
PostRoom(createRoomFrom).then(res => { PostRoom({ ...createRoomFrom, allowAnonymous }).then(res => {
if (res.code === 200) { if (res.code === 200) {
message.success('创建成功!') message.success('创建成功!')
setCreateRoomModal(false) setCreateRoomModal(false)
setAllowAnonymous(true)
getRoomList() getRoomList()
} }
}) })
} }
}) })
} else { } else {
PostRoomInfo(createRoomFrom).then(res => { PostRoomInfo({ ...createRoomFrom, allowAnonymous }).then(res => {
if (res.code === 200) { if (res.code === 200) {
message.success('更新成功!') message.success('更新成功!')
setCreateRoomModal(false) setCreateRoomModal(false)
setAllowAnonymous(true)
getRoomList() getRoomList()
} }
}) })

View File

@ -221,7 +221,7 @@ const User: React.FC = () => {
> >
</Button> </Button>
</div> </div>
<div className={`${styles.userBtnsRight} drag`}> <div className={`${styles.userBtnsRight} drag`}>
<Input <Input
@ -415,6 +415,7 @@ const User: React.FC = () => {
<Input <Input
placeholder="请输入届" placeholder="请输入届"
value={addUserFrom.year} value={addUserFrom.year}
maxLength={4}
onChange={(e) => { onChange={(e) => {
const regex = /^[0-9]*$/; const regex = /^[0-9]*$/;
if (regex.test(e.target.value)) { if (regex.test(e.target.value)) {
@ -602,7 +603,7 @@ const User: React.FC = () => {
onClick={() => { onClick={() => {
const file = document.createElement("input") as any; const file = document.createElement("input") as any;
file.type = "file"; file.type = "file";
// file.accept = ".xls,.xlsx"; file.accept = ".xls,.xlsx";
file.onchange = async () => { file.onchange = async () => {
const setting = await JSON.parse(storage.getItem('setting') as string) const setting = await JSON.parse(storage.getItem('setting') as string)
const fileInfo = file.files[0]; const fileInfo = file.files[0];
@ -669,7 +670,7 @@ const User: React.FC = () => {
onClick={() => { onClick={() => {
const file = document.createElement("input") as any; const file = document.createElement("input") as any;
file.type = "file"; file.type = "file";
// file.accept = ".xls,.xlsx"; file.accept = ".xls,.xlsx";
file.onchange = async () => { file.onchange = async () => {
const setting = await JSON.parse(storage.getItem('setting') as string) const setting = await JSON.parse(storage.getItem('setting') as string)
const fileInfo = file.files[0]; const fileInfo = file.files[0];
@ -762,12 +763,17 @@ const User: React.FC = () => {
<Button type="primary" style={{ backgroundColor: '#31353A', marginRight: '14px' }} <Button type="primary" style={{ backgroundColor: '#31353A', marginRight: '14px' }}
onClick={() => setSignInUserModal(false)}></Button> onClick={() => setSignInUserModal(false)}></Button>
<Button type="primary" className='m-ant-btn' onClick={() => { <Button type="primary" className='m-ant-btn' onClick={() => {
PutSigns(signInUserForm.uid, signInUserForm.currentUserList).then(res => { let signInNameNull = signInUserForm.currentUserList.find((item: any) => !item.signInName)
if (res.code === 200) { if (signInNameNull) {
message.success('签到绑定成功') message.error('请输入用户名称');
setSignInUserModal(false) } else {
} PutSigns(signInUserForm.uid, signInUserForm.currentUserList).then(res => {
}) if (res.code === 200) {
message.success('签到绑定成功')
setSignInUserModal(false)
}
})
}
}}></Button> }}></Button>
</div> </div>
</div> </div>

View File

@ -7,6 +7,7 @@ import Avatar from '@/components/Avatar';
import { useEffect, useState, useRef } from "react"; import { useEffect, useState, useRef } from "react";
import { storage } from '@/utils'; import { storage } from '@/utils';
import EquipmentManagement from '@/components/EquipmentManagement'; import EquipmentManagement from '@/components/EquipmentManagement';
import UserName from '@/components/UserName';
const { confirm } = Modal; const { confirm } = Modal;
const UserListWindow: React.FC = () => { const UserListWindow: React.FC = () => {
@ -14,6 +15,7 @@ const UserListWindow: React.FC = () => {
const [user, setUser] = useState<any>({}); const [user, setUser] = useState<any>({});
const [roomUserList, setRoomUserList] = useState<any>([]) const [roomUserList, setRoomUserList] = useState<any>([])
const equipmentManagementRef = useRef<any>(); const equipmentManagementRef = useRef<any>();
const userNameRef = useRef<any>();
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)
useEffect(() => { useEffect(() => {
@ -149,6 +151,18 @@ const UserListWindow: React.FC = () => {
}); });
}} }}
></Button> ></Button>
<Button
type="primary"
className='m-ant-btn'
style={{ width: '100%', marginTop: '10px' }}
size={'small'}
onClick={() => {
userNameRef.current.changeModal({
userName: item.userName,
uid: item.uid
})
}}
></Button>
</div> </div>
}> }>
<EllipsisOutlined style={{ <EllipsisOutlined style={{
@ -192,6 +206,7 @@ const UserListWindow: React.FC = () => {
}}></div> }}></div>
</div> </div>
</div> </div>
<UserName ref={userNameRef} />
<EquipmentManagement ref={equipmentManagementRef} getDriver={(uid: string) => { <EquipmentManagement ref={equipmentManagementRef} getDriver={(uid: string) => {
channel.postMessage({ channel.postMessage({
type: 'userListWindowEquipmentManagement', type: 'userListWindowEquipmentManagement',

View File

@ -24,6 +24,7 @@ import { fixWebmDuration } from "webm-duration-fix-buffer";
import { getKeyOpenChildWindow, setKeyOpenChildWindow } from '@/utils/package/public'; import { getKeyOpenChildWindow, setKeyOpenChildWindow } from '@/utils/package/public';
import MeetingDisconnected from '@/components/MeetingDisconnected'; import MeetingDisconnected from '@/components/MeetingDisconnected';
import SingIn from '@/components/SingIn'; import SingIn from '@/components/SingIn';
import UserName from '@/components/UserName';
const { setTimeout, setInterval, clearTimeout, clearInterval } = require('timers'); const { setTimeout, setInterval, clearTimeout, clearInterval } = require('timers');
const { confirm } = Modal; const { confirm } = Modal;
const { exec } = require('child_process'); const { exec } = require('child_process');
@ -39,7 +40,9 @@ const Meeting: React.FC = () => {
const equipmentManagementRef = useRef<any>(); const equipmentManagementRef = useRef<any>();
const meetingDisconnectedRef = useRef<any>(); const meetingDisconnectedRef = useRef<any>();
const singInRef = useRef<any>(); const singInRef = useRef<any>();
const userNameRef = useRef<any>();
const [isClicked, setIsClicked] = useState(false); const [isClicked, setIsClicked] = useState(false);
const [isClickedMediaSteam, setIsClickedMediaSteam] = useState(false);
const [statusList, setStatusList] = useState({ const [statusList, setStatusList] = useState({
userList: false, userList: false,
userChatList: false, userChatList: false,
@ -431,7 +434,7 @@ const Meeting: React.FC = () => {
setTimeout(async () => { setTimeout(async () => {
const setting = await JSON.parse(storage.getItem('setting') as string); const setting = await JSON.parse(storage.getItem('setting') as string);
const stateInfo = await JSON.parse(storage.getItem('stateInfo') as string); const stateInfo = await JSON.parse(storage.getItem('stateInfo') as string);
if (stateInfo && setting.isRecordingTips) { if (stateInfo && setting.isRecordingTips && location.href.indexOf('/meeting') !== -1) {
setRecorder((data: any) => { setRecorder((data: any) => {
if (!data) { if (!data) {
setIsScreenCapture(bool => { setIsScreenCapture(bool => {
@ -855,6 +858,10 @@ const Meeting: React.FC = () => {
} }
} }
break; break;
// 修改用户名称
case 'ModifyNickName':
setAllUserListData('ModifyNickName', item)
break;
} }
}) })
return () => { return () => {
@ -912,6 +919,18 @@ const Meeting: React.FC = () => {
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [isClicked]); }, [isClicked]);
useEffect(() => {
let timer: NodeJS.Timeout;
if (isClickedMediaSteam) {
timer = setTimeout(() => {
setIsClickedMediaSteam(false)
}, 3000);
}
return () => clearTimeout(timer);
}, [isClickedMediaSteam]);
useEffect(() => { useEffect(() => {
const elements = document.querySelectorAll('.intersectionObserver-view'); const elements = document.querySelectorAll('.intersectionObserver-view');
if (elements.length && currentVideoId) { if (elements.length && currentVideoId) {
@ -1276,6 +1295,15 @@ const Meeting: React.FC = () => {
return res return res
}) })
break; break;
case 'ModifyNickName':
setRoomUserList((res: any) => {
let userItem = res.find((row: any) => row.uid == item.uid)
if (userItem) {
userItem.userName = item.nickName
}
return res
})
break;
} }
changeAgoraDevice() changeAgoraDevice()
} }
@ -1506,52 +1534,57 @@ const Meeting: React.FC = () => {
case '录制': case '录制':
const setting = await JSON.parse(storage.getItem('setting') as string); const setting = await JSON.parse(storage.getItem('setting') as string);
try { try {
await fs.access(setting.recordingFilesPath, fs.constants.F_OK); if (!isClickedMediaSteam) {
footerListTemplate[itemIndex][rowIndex].title = '录制中'; setIsClickedMediaSteam(true)
footerListTemplate[itemIndex][rowIndex].active = true; await fs.access(setting.recordingFilesPath, fs.constants.F_OK);
setFooterList(footerListTemplate); footerListTemplate[itemIndex][rowIndex].title = '录制中';
window.electron.getSources().then(async (sources: any) => { footerListTemplate[itemIndex][rowIndex].active = true;
const screenId = sources[0].id; setFooterList(footerListTemplate);
const stream = await navigator.mediaDevices.getUserMedia({ window.electron.getSources().then(async (sources: any) => {
audio: { const screenId = sources[0].id;
mandatory: { const stream = await navigator.mediaDevices.getUserMedia({
chromeMediaSource: 'desktop', audio: {
chromeMediaSourceId: screenId, mandatory: {
} chromeMediaSource: 'desktop',
} as any, chromeMediaSourceId: screenId,
video: { }
mandatory: { } as any,
chromeMediaSource: 'desktop', video: {
chromeMediaSourceId: screenId, mandatory: {
} chromeMediaSource: 'desktop',
} as any chromeMediaSourceId: screenId,
}
} as any
});
// 获取所有音频输入设备
const devices = await navigator.mediaDevices.enumerateDevices();
const audioInputDevices = devices.filter(device => device.kind === 'audioinput' &&
device.deviceId !== 'default' &&
device.deviceId !== 'communications');
// 使用Web Audio API来捕获系统声音和麦克风声音将它们合并到同一个MediaStream中。
const audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)();
const systemSoundSource = audioCtx.createMediaStreamSource(stream);
const systemSoundDestination = audioCtx.createMediaStreamDestination();
systemSoundSource.connect(systemSoundDestination);
// 录制所有音频输入设备
audioInputDevices.forEach(async device => {
const micStream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId: { exact: device.deviceId } } });
setMediaStream((res: any) => [...res, micStream]);
const micSoundSource = audioCtx.createMediaStreamSource(micStream);
micSoundSource.connect(systemSoundDestination);
})
// 合并音频流与视频流
const combinedSource = new MediaStream([...stream.getVideoTracks(), ...systemSoundDestination.stream.getAudioTracks()]);
// 开始录制
const mediaRecorder = new MediaRecorder(combinedSource, {
mimeType: 'video/webm;codecs=vp9,opus',
videoBitsPerSecond: 1.5e6,
});
setRecorder(mediaRecorder);
}); });
// 获取所有音频输入设备 } else {
const devices = await navigator.mediaDevices.enumerateDevices(); message.error('录制太频繁了,请稍后重试!');
const audioInputDevices = devices.filter(device => device.kind === 'audioinput' && }
device.deviceId !== 'default' &&
device.deviceId !== 'communications');
// 使用Web Audio API来捕获系统声音和麦克风声音将它们合并到同一个MediaStream中。
const audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)();
const systemSoundSource = audioCtx.createMediaStreamSource(stream);
const systemSoundDestination = audioCtx.createMediaStreamDestination();
systemSoundSource.connect(systemSoundDestination);
// 录制所有音频输入设备
audioInputDevices.forEach(async device => {
const micStream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId: { exact: device.deviceId } } });
setMediaStream((res: any) => [...res, micStream]);
const micSoundSource = audioCtx.createMediaStreamSource(micStream);
micSoundSource.connect(systemSoundDestination);
})
// 合并音频流与视频流
const combinedSource = new MediaStream([...stream.getVideoTracks(), ...systemSoundDestination.stream.getAudioTracks()]);
// 开始录制
const mediaRecorder = new MediaRecorder(combinedSource, {
mimeType: 'video/webm;codecs=vp9,opus',
videoBitsPerSecond: 1.5e6,
});
setRecorder(mediaRecorder);
});
} catch (error: any) { } catch (error: any) {
if (error.code === 'ENOENT') { if (error.code === 'ENOENT') {
message.error({ message.error({
@ -1567,10 +1600,14 @@ const Meeting: React.FC = () => {
break; break;
case '录制中': case '录制中':
footerListTemplate[itemIndex][rowIndex].title = '录制' if (isClickedMediaSteam) {
footerListTemplate[itemIndex][rowIndex].active = false message.error('录制时长不足3秒请稍后重试');
setFooterList(footerListTemplate) } else {
stopRecorderMedia() footerListTemplate[itemIndex][rowIndex].title = '录制'
footerListTemplate[itemIndex][rowIndex].active = false
setFooterList(footerListTemplate)
stopRecorderMedia()
}
break; break;
case '共享文件': case '共享文件':
sharedFilesModelRef.current.getData() sharedFilesModelRef.current.getData()
@ -2329,6 +2366,17 @@ const Meeting: React.FC = () => {
getRoomKickout(state.channelId, item.uid, item.userName) getRoomKickout(state.channelId, item.uid, item.userName)
}} }}
></Button> : null} ></Button> : null}
{item.uid !== user.uid ? <Button
type="primary"
className='m-ant-btn'
size={'small'}
onClick={() => {
userNameRef.current.changeModal({
userName: item.userName,
uid: item.uid
})
}}
></Button> : null}
</div> </div>
}> }>
<div className={styles.meetingContentOperation}> <div className={styles.meetingContentOperation}>
@ -2487,6 +2535,18 @@ const Meeting: React.FC = () => {
getRoomKickout(state.channelId, item.uid, item.userName) getRoomKickout(state.channelId, item.uid, item.userName)
}} }}
></Button> ></Button>
<Button
type="primary"
className='m-ant-btn'
style={{ width: '100%', marginTop: '10px' }}
size={'small'}
onClick={() => {
userNameRef.current.changeModal({
userName: item.userName,
uid: item.uid
})
}}
></Button>
</div> </div>
}> }>
<EllipsisOutlined style={{ color: '#fff', fontSize: '20px' }} /> <EllipsisOutlined style={{ color: '#fff', fontSize: '20px' }} />
@ -2995,6 +3055,7 @@ const Meeting: React.FC = () => {
<EquipmentManagement ref={equipmentManagementRef} /> <EquipmentManagement ref={equipmentManagementRef} />
<MeetingDisconnected ref={meetingDisconnectedRef} /> <MeetingDisconnected ref={meetingDisconnectedRef} />
<SingIn ref={singInRef} /> <SingIn ref={singInRef} />
<UserName ref={userNameRef} />
</> </>
) )
} }