WGShare.Client.Electron/src/page/Meeting/index.tsx

962 lines
37 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import styles from '@/page/Meeting/index.module.scss'
import { useEffect, useRef, useState } from "react";
import Operation from '@/components/Operation';
import { Button, Input, Popover, Modal, Checkbox, message, Table, Pagination } from "antd";
import { DeleteOutlined, ProfileOutlined, ReloadOutlined, SearchOutlined, VerticalAlignBottomOutlined } from '@ant-design/icons';
import { useLocation, useNavigate } from 'react-router-dom';
import { thumbImageBufferToBase64 } from '@/utils/package/base64'
import { storage } from '@/utils';
import { GetRoomFile, PostRoomFile, DeleteRoomFile, GetRoomUpFileurl, GetRoomFileDwUrl, GetRoomUser, PostOpenMicr, PostOpenCamera, PostRoomManager, DeleteRoomManager, GetRoomKickout } from '@/api/Meeting';
import axios from 'axios';
import ImageUrl from '@/utils/package/imageUrl'
import agora from '@/utils/package/agora'
import SpeakerModeModal from '@/components/SpeakerModeModal';
import { onInvoke, onSignalr, offSignalr } from '@/utils/package/signalr';
import dayjs from 'dayjs';
import durationPlugin from 'dayjs/plugin/duration';
import { VideoSourceType } from 'agora-electron-sdk';
import InvitingPersonnelModal from '@/components/InvitingPersonnelModal';
dayjs.extend(durationPlugin);
const { Column } = Table
const Meeting: React.FC = () => {
const navigate = useNavigate();
const { state } = useLocation();
const speakerModeModalRef = useRef<any>();
const invitingPersonnelRef = useRef<any>();
const [statusList, setStatusList] = useState({
userList: false,
userChatList: false,
})
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [isSharedScreenModal, setIsSharedScreenModal] = useState(false);
const [isInit, setIsInit] = useState(true);
const [user, setUser] = useState<any>({});
const [showRowSelection, setShowRowSelection] = useState(false);
const [isSharedFilesModel, setIsSharedFilesModel] = useState(false);
const [sharedScreenList, setSharedScreenList] = useState<any>([]);
const [sharedScreenItem, setSharedScreenItem] = useState<any>('');
const [textMsg, setTextMsg] = useState('');
const [footerList, setFooterList] = useState([
[
{
title: '关闭声音',
icon: ImageUrl.icon22,
iconActive: ImageUrl.icon22Active,
active: false,
},
{
title: '关闭视频',
icon: ImageUrl.icon23,
iconActive: ImageUrl.icon23Active,
active: false,
},
],
[
{
title: '共享屏幕',
icon: ImageUrl.icon24,
active: false,
},
{
title: '共享文件',
icon: ImageUrl.icon25,
active: false,
},
{
title: '邀请人员',
icon: ImageUrl.icon26,
active: false,
},
{
title: '录制',
icon: ImageUrl.icon27,
iconActive: ImageUrl.icon27Active,
active: false,
},
{
title: '设置向导',
icon: ImageUrl.icon28,
active: false,
},
{
title: '结束',
icon: ImageUrl.icon29,
active: false,
},
],
[
{
title: '成员列表',
icon: ImageUrl.icon30,
active: false,
},
{
title: '聊天',
icon: ImageUrl.icon31,
active: false,
},
],
])
const [footerListIndex, setFooterListIndex] = useState<any>({
itemIndex: 0,
rowIndex: 0,
});
const [fileList, setFileList] = useState({
data: [],
keyword: '',
total: 0,
pageIndex: 1,
pageSize: 10,
})
const [roomUserList, setRoomUserList] = useState<any>([])
const [chatList, setChatList] = useState<any>([])
const [currentVideoId, setCurrentVideoId] = useState('')
let [currentSeconds, setCurrentSeconds] = useState(0)
const [currentEffective, setCurrentEffective] = useState(0)
const [open, setOpen] = useState(false)
const [meetingMode, setMeetingMode] = useState('')
const [userSearchValue, setUserSearchValue] = useState('')
useEffect(() => {
let time = null as any;
if (isInit) {
let userInfo = JSON.parse(storage.getItem('user') as string)
setMeetingMode(storage.getItem('meetingMode') as string || 'FreedomMode');
agora.init()
agora.registerEventHandler({
onJoinChannelSuccess: async (info: any, _elapsed: any) => {
await onInvoke('joinChannel', {
roomNum: info.channelId,
enableMicr: true,
enableCamera: true
})
await getRoomUser()
setTimeout(() => {
agora.setupLocalVideo({
account: Number(info.localUid),
view: document.getElementById(`video-${info.localUid}`) as HTMLElement,
channelId: info.channelId,
})
}, 1000);
},
onUserJoined: async (info: any, remoteUid: any, _elapsed: any) => {
await getRoomUser()
setTimeout(() => {
agora.setupRemoteVideoJoin({
account: Number(remoteUid),
view: document.getElementById(`video-${remoteUid}`) as HTMLElement,
channelId: info.channelId,
})
}, 1000);
},
onUserOffline: async (info: any, remoteUid: any, reason: any) => {
await onInvoke('levelChannel', {
roomNum: info.channelId
})
agora.setupRemoteVideo({
account: Number(remoteUid),
view: document.getElementById(`video-${remoteUid}`) as HTMLElement,
channelId: info.channelId,
})
}
})
agora.setCameraCapture(VideoSourceType.VideoSourceCameraPrimary)
agora.setJoinChannel({
channelId: state.channelId,
userid: userInfo.account,
token: state.token,
})
setCurrentVideoId(userInfo.account)
setUser(userInfo)
setIsInit(false)
window.addEventListener('customStorageChange', handleCustomStorageChange);
time = setInterval(() => {
let effectiveTypeLength = ['slow-2g', '2g', '3g', '4g'].indexOf((navigator as any).connection.effectiveType)
if (effectiveTypeLength >= 0) {
setCurrentEffective(effectiveTypeLength + 1)
}
setCurrentSeconds(currentSeconds++)
}, 1000)
} else {
getRoomFile()
}
return () => {
window.removeEventListener('customStorageChange', handleCustomStorageChange);
clearInterval(time)
};
}, [fileList.pageIndex]);
useEffect(() => {
roomUserList.forEach((item: any) => {
if (item.account === user.account) {
const footerListTemplate = [...footerList]
footerListTemplate[0][0].title = item.enableMicr ? '关闭声音' : '开启声音'
footerListTemplate[0][0].active = !item.enableMicr
setFooterList(footerListTemplate)
}
agora.muteLocalAudioStream(!item.enableMicr)
agora.muteLocalVideoStream(!item.enableCamera)
});
}, [roomUserList]);
useEffect(() => {
onSignalr((item: any) => {
switch (item.key) {
case 'ReceiveMessage':
setChatList((newChatList: any) => [...newChatList, item])
break;
case 'RefreshUserList':
getRoomUser()
break;
case 'Operation':
// 1:全员退出会议
// 2:设置取消管理员
// 3:踢出房间
switch (item.type) {
case 1:
leaveChannel()
break;
case 2:
case 3:
getRoomUser()
break;
}
break;
case 'ForceExitRoom':
leaveChannel()
break;
case 'RefreshView':
setMeetingMode(item.type)
break;
}
})
return () => {
offSignalr()
}
}, [])
useEffect(() => {
}, [currentVideoId])
// 加入房间时间
const changeCurrentSeconds = () => {
const duration = dayjs.duration(currentSeconds, 'seconds');
const hours = duration.hours(); // 整数小时
const minutes = duration.minutes(); // 整数分钟
const secondsRemaining = duration.seconds(); // 剩余的秒数
return `${hours > 9 ? hours : '0' + hours}:${minutes > 9 ? minutes : '0' + minutes}:${secondsRemaining > 9 ? secondsRemaining : '0' + secondsRemaining}`
}
// 操作按钮
const changeStatusList = async (row: any, itemIndex: number, rowIndex: number): Promise<void> => {
const footerListTemplate = [...footerList]
setFooterListIndex({
itemIndex,
rowIndex,
})
switch (row.title) {
case '成员列表':
setStatusList({
userList: true,
userChatList: false,
})
break;
case '聊天':
setStatusList({
userList: false,
userChatList: true,
})
break;
case '共享屏幕':
getDesktopCapturerVideo()
setIsSharedScreenModal(true)
break;
case '停止共享':
agora.setCameraCapture(VideoSourceType.VideoSourceCameraPrimary)
footerListTemplate[itemIndex][rowIndex].title = '共享屏幕'
break;
case '关闭声音':
footerListTemplate[itemIndex][rowIndex].title = '开启声音'
footerListTemplate[itemIndex][rowIndex].active = true
setFooterList(footerListTemplate)
postOpenMicr(false)
break;
case '开启声音':
footerListTemplate[itemIndex][rowIndex].title = '关闭声音'
footerListTemplate[itemIndex][rowIndex].active = false
setFooterList(footerListTemplate)
postOpenMicr(true)
break;
case '关闭视频':
footerListTemplate[itemIndex][rowIndex].title = '开启视频'
footerListTemplate[itemIndex][rowIndex].active = true
setFooterList(footerListTemplate)
postOpenCamera(false)
break;
case '开启视频':
footerListTemplate[itemIndex][rowIndex].title = '关闭视频'
footerListTemplate[itemIndex][rowIndex].active = false
setFooterList(footerListTemplate)
postOpenCamera(true)
break;
case '设置向导':
break;
case '邀请人员':
invitingPersonnelRef.current.changeInvitingPersonnelModal()
break;
case '录制':
if (currentVideoId === user.account) {
message.error('请勿自己录制自己!')
} else {
footerListTemplate[itemIndex][rowIndex].title = '录制中'
footerListTemplate[itemIndex][rowIndex].active = true
setFooterList(footerListTemplate)
agora.startRecording(Number(currentVideoId))
}
break;
case '录制中':
footerListTemplate[itemIndex][rowIndex].title = '录制'
footerListTemplate[itemIndex][rowIndex].active = false
setFooterList(footerListTemplate)
agora.stopRecording()
break;
case '共享文件':
await getRoomFile()
setIsSharedFilesModel(true)
break;
}
}
// 退出房间
const leaveChannel = (): void => {
agora.leaveChannel()
agora.stopScreenCapture()
navigate(-1)
}
// 分享屏幕
const clickSharedScreen = async (): Promise<void> => {
let data = sharedScreenList.find((item: any) => item.sourceId === sharedScreenItem.sourceId)
if (data) {
const footerListTemplate = [...footerList]
footerListTemplate[footerListIndex.itemIndex][footerListIndex.rowIndex].title = '停止共享'
setIsSharedScreenModal(false)
agora.setDesktopCapturerVideo(sharedScreenItem, VideoSourceType.VideoSourceScreen)
} else {
message.error('请选择应用!')
}
}
// 获取桌面可共享屏幕的引用
const getDesktopCapturerVideo = (): void => {
agora.getDesktopCapturerVideo().then((res: any) => {
if (sharedScreenList.length !== res.length) {
res.forEach((item: any) => {
if (item.thumbImage.buffer) {
item.thumbnailUrl = thumbImageBufferToBase64(item.thumbImage)
}
if (item.iconImage.buffer) {
item.iconDataUrl = thumbImageBufferToBase64(item.iconImage)
}
})
setSharedScreenList(res)
}
})
};
// 获取共享文件列表
const getRoomFile = async (): Promise<void> => {
await GetRoomFile({
pageIndex: fileList.pageIndex,
pageSize: fileList.pageSize,
keyword: fileList.keyword,
roomId: state.roomId
}).then(res => {
if (res.code === 200) {
setFileList({
...fileList,
data: res.data.items.map((item: any) => {
return {
...item,
key: item.id,
}
}),
total: res.data.total
})
}
})
}
// 获取房间用户
const getRoomUser = async (): Promise<void> => {
await GetRoomUser(state.channelId).then(res => {
if (res.code === 200) {
setRoomUserList(res.data.map((item: any) => {
return {
isShow: true,
...item
}
}))
}
})
}
const handleCustomStorageChange = async (e: any): Promise<void> => {
switch (e.key) {
case 'meetingMode':
setMeetingMode(e.value)
break;
}
};
// 聊天发送
const sendMsg = (): void => {
if (textMsg) {
onInvoke('sendChannelMsg', {
roomNum: state.channelId,
msg: textMsg,
})
setChatList((newChatList: any) => [...newChatList, {
uid: state.uid,
userName: user.userName,
message: textMsg,
}])
setTextMsg('')
} else {
message.success('请输入内容!')
}
}
// 开关麦克风
const postOpenMicr = async (enableMicr: boolean, isAll?: boolean): Promise<void> => {
await PostOpenMicr({
roomNum: state.channelId,
uid: user.uid,
enableMicr,
isAll,
})
}
// 开关视频
const postOpenCamera = async (enableCamera: boolean): Promise<void> => {
await PostOpenCamera({
roomNum: state.channelId,
uid: user.uid,
enableCamera
})
}
// 演讲者模式
const changeSpeakerMode = (): void => {
speakerModeModalRef.current.changeSpeakerMode()
}
// 获取当前模式样式
const getMeetingContentBodyLeftModeClass = (): string => {
switch (meetingMode) {
case 'FreedomMode':
return styles.meetingContentBodyLeftFreedomMode
case 'StandardMode':
return styles.meetingContentBodyLeftStandardMode
case 'SpeakerMode':
return styles.meetingContentBodyLeftSpeakerMode
case 'SingleScreenMode':
return styles.meetingContentBodyLeftSingleScreenMode
case 'DualScreenMode':
return styles.meetingContentBodyLeftDualScreenMode
case 'FourScreenMode':
return styles.meetingContentBodyLeftFourScreenMode
}
return ''
}
// 获取当前模式文字
const getMeetingContentBodyLeftModeText = (): string => {
switch (meetingMode) {
case 'FreedomMode':
return '自由者模式'
case 'StandardMode':
return '标准模式'
case 'SpeakerMode':
return '演讲者模式'
case 'SingleScreenMode':
return '单画面模式'
case 'DualScreenMode':
return '二分屏模式'
case 'FourScreenMode':
return '四分屏模式'
}
return ''
}
// 设置单个视频样式
const setMeetingContentSwiperCardClass = (account: string): string => {
if (currentVideoId === account && (meetingMode === 'StandardMode' || meetingMode === 'SpeakerMode')) {
switch (meetingMode) {
case 'StandardMode':
return styles.meetingContentSwiperCardStandardMode
case 'SpeakerMode':
return styles.meetingContentSwiperCardSpeakerMode
}
}
return ''
}
return (
<>
<div className={styles.meeting}>
<div className={styles.meetingHeader}>
<div>
<div>
{currentEffective >= 1 ? <span></span> : null}
{currentEffective >= 2 ? <span></span> : null}
{currentEffective >= 3 ? <span></span> : null}
{currentEffective >= 4 ? <span></span> : null}
</div>
<div>{changeCurrentSeconds()}</div>
</div>
<div>{state.channelId}</div>
<div className='drag'>
<div className={styles.meetingGrayButton} onClick={changeSpeakerMode}>{getMeetingContentBodyLeftModeText()}</div>
<Operation></Operation>
</div>
</div>
<div className={styles.meetingContent}>
<div className={styles.meetingContentBody}>
<div className={`${styles.meetingContentBodyLeft} drag`}>
<div className={getMeetingContentBodyLeftModeClass()} >
{roomUserList.map((item: any, index: number) =>
<div
className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass(item.account)}`}
key={index}
onClick={() => {
if (footerList[1][3].active) {
return message.error('视频录制中请勿切换,或结束录制再切换!')
}
setCurrentVideoId(item.account)
}}
>
<div className={`${styles.meetingContentSwiperCardVdeio} ${currentVideoId === item.account ? styles.active : ''}`} id={`video-${item.account}`}>
<div className={styles.meetingContentSwiperCardVdeioLoading}>
</div>
</div>
{meetingContentUser(item)}
{item.enableCamera ? null : meetingContentError(currentVideoId, item)}
</div>
)}
</div>
</div>
{
(statusList.userList || statusList.userChatList) ? (
<div className={styles.meetingContentBodyRight}>
{statusList.userList ?
<div className={styles.meetingUserList}>
<div className={styles.meetingUserListTitle}>
<span></span>
<img src={ImageUrl.icon18} alt="" className='drag' onClick={() => {
setStatusList({
userList: false,
userChatList: false,
})
}} />
</div>
<div style={{ padding: '0 14px', boxSizing: 'border-box' }}>
<Input
placeholder="请输入用户名"
className='drag'
prefix={<SearchOutlined style={{ color: 'white' }} />}
value={userSearchValue}
onChange={(e) => {
setUserSearchValue(e.target.value)
const newRoomUserList = [...roomUserList]
newRoomUserList.forEach(row => {
if (e.target.value) {
if (row.userName.indexOf(e.target.value) !== -1) {
row.isShow = true;
} else {
row.isShow = false;
}
} else {
row.isShow = true;
}
});
setRoomUserList(newRoomUserList)
}}
/>
</div>
<div className={styles.meetingUserListContent}>
{roomUserList.map((item: any, index: number) => {
return (
<>
{item.isShow ? <div key={index + item.id} className='drag'>
<div>
<div><img src={ImageUrl.avatar} alt="" /></div>
<span>
{item.userName}
{item.roleId === '1' || item.isManager ?
<span style={{ color: '#02B188', marginLeft: '4px' }}>
{item.roleId === '1' ? '主持人' : '临时主持人'}
</span>
: null}
</span>
</div>
<div>
<img src={item.enableMicr ? ImageUrl.icon22 : ImageUrl.icon22Active} alt="" />
<img src={item.enableCamera ? ImageUrl.icon23 : ImageUrl.icon23Active} alt="" />
</div>
{item.account !== user.account && user.roleId === '1' ? <div className='drag'>
{!item.isManager ? <Button
type="primary"
className='m-ant-btn'
style={{ marginBottom: '10px', width: '80%' }}
size={'small'}
onClick={() => {
PostRoomManager(state.roomId, [item.id]).then(res => {
if (res.code === 200) {
onInvoke('sendOper', {
roomNum: state.channelId,
type: 2,
})
}
})
}}
></Button> : <Button
type="primary"
className='m-ant-btn'
style={{ marginBottom: '10px', width: '80%' }}
size={'small'}
onClick={() => {
DeleteRoomManager(state.roomId, [item.id]).then(res => {
if (res.code === 200) {
onInvoke('sendOper', {
roomNum: state.channelId,
type: 2,
})
}
})
}}
></Button>}
<Button
type="primary"
className='m-ant-btn'
style={{ width: '80%' }}
size={'small'}
onClick={() => {
GetRoomKickout(state.channelId, item.id).then(res => {
if (res.code === 200) {
onInvoke('sendOper', {
roomNum: state.channelId,
type: 3,
})
}
})
}}
></Button>
</div> : null}
</div> : null}
</>
)
}
)}
</div>
<div className={`${styles.meetingUserListFooter} drag`} onClick={() => invitingPersonnelRef.current.changeInvitingPersonnelModal()}>
<div></div>
<div onClick={() => postOpenMicr(false, true)}></div>
</div>
</div>
:
<div className={styles.meetingUserChat}>
<div className={styles.meetingUserChatTitle}>
<span></span>
<img src={ImageUrl.icon18} alt="" className='drag' onClick={() => {
setStatusList({
userList: false,
userChatList: false,
})
}} />
</div>
<div className={styles.meetingUserChatContent}>
{chatList.map((item: any, index: number) =>
<div
key={index}
className={`${item.uid !== state.uid ? styles.meetingUserChatContentLeft : styles.meetingUserChatContentRight} drag`}>
<div>
<div><img src={ImageUrl.avatar} alt="" /></div>
<span>{item.userName}</span>
</div>
<div>{item.message}</div>
</div>
)}
</div>
<div className={`${styles.meetingUserChatInput} drag`}>
<Input.TextArea placeholder="请输入消息" value={textMsg} style={{ flexGrow: 1 }} onChange={(e) => {
setTextMsg(e.target.value)
}} onPressEnter={sendMsg}></Input.TextArea>
<Button type="primary" className='m-ant-btn' style={{ flexShrink: 0, marginTop: '4px' }} onClick={sendMsg}></Button>
</div>
</div>
}
</div>
) : null
}
</div>
<div className={styles.meetingContentFooter}>
{footerList.map((item, itemIndex) => {
return (
<div key={itemIndex}>
{item.map((row, rowIndex) => {
return (
row.title === '结束' ?
<Popover key={rowIndex}
content={
<div className='meetingContentFooterPopover'>
<div onClick={async () => {
await onInvoke('sendOper', {
roomNum: state.channelId,
type: 1,
})
leaveChannel()
}}></div>
<div onClick={() => leaveChannel()}></div>
<div onClick={() => { setOpen(false) }}></div>
</div>
}
title=""
trigger="click"
open={open}
onOpenChange={() => setOpen(true)}
>
<div className='drag'>
<img src={row.active ? row.iconActive : row.icon} alt="" />
<span>{row.title}</span>
</div>
</Popover> :
<div className='drag' onClick={() => changeStatusList(row, itemIndex, rowIndex)} key={rowIndex}>
<img src={row.active ? row.iconActive : row.icon} alt="" />
<span>{row.title}</span>
</div>
)
})}
</div>
)
})}
</div>
</div>
</div>
<Modal title="共享屏幕" open={isSharedScreenModal} footer={null} closable={false} centered width={'800px'}>
<div className={styles.sharedScreenModal}>
<div>
{sharedScreenList.map((item: any, index: number) => {
return (
<div
className={sharedScreenItem.sourceId === item.sourceId ? styles.active : ''}
key={index}
onClick={() => {
setSharedScreenItem(item)
}}>
{item.iconDataUrl ? <img src={item.iconDataUrl} alt="" /> : ''}
<div><img src={item.thumbnailUrl} alt="" /></div>
<span>{item.sourceTitle}</span>
</div>
)
})}
</div>
<div>
<Checkbox onChange={() => {
}}></Checkbox>
<div>
<Button type="primary" onClick={() => { setIsSharedScreenModal(false) }} style={{ backgroundColor: '#31353A', marginRight: '14px' }}></Button>
<Button type="primary" className='m-ant-btn' onClick={() => clickSharedScreen()}></Button>
</div>
</div>
</div>
</Modal>
<Modal
title="共享文件"
open={isSharedFilesModel}
footer={null}
centered
width={'800px'}
onCancel={() => setIsSharedFilesModel(false)}
maskClosable
>
<div>
<div className={styles.sharedFilesModel}>
<div>
<span>{fileList.total}</span>
<div style={{ color: 'white' }}>
<Input
placeholder="搜索"
style={{ width: '200px' }}
prefix={<SearchOutlined style={{ color: 'white' }} />}
onChange={(e) => {
setFileList({
...fileList,
keyword: e.target.value
})
}}
onBlur={() => {
if (fileList.pageIndex === 1) {
getRoomFile()
} else {
setFileList({
...fileList,
pageIndex: 1
})
}
}}
/>
<ReloadOutlined title='刷新' onClick={() => {
if (fileList.pageIndex === 1) {
getRoomFile()
} else {
setFileList({
...fileList,
pageIndex: 1
})
}
}} />
<ProfileOutlined title={showRowSelection ? '取消框选' : '显示框选'} onClick={() => {
setShowRowSelection(!showRowSelection)
}} style={{ color: showRowSelection ? '#5575F2' : 'white' }} />
{showRowSelection ? <DeleteOutlined title='删除' onClick={() => {
if (selectedRowKeys.length) {
DeleteRoomFile(selectedRowKeys).then(res => {
if (res.code === 200) {
message.success('删除成功!')
getRoomFile()
}
})
} else {
message.error('请选择文件!')
}
}} /> : null}
<Button type="primary" style={{ backgroundColor: '#31353A' }}
onClick={() => {
const file = document.createElement("input") as any;
file.accept = "image/*,.doc,.docx,.ppt,.pptx,.xls,.xlsx,application/pdf";
file.type = "file";
file.onchange = async () => {
const fileInfo = file.files[0];
const maxSize = 100 * 1024 * 1024; // 100MB in bytes
if (fileInfo.size > maxSize) {
message.error('文件太大请上传小于100MB的文件。')
// 清除文件输入框的值,以便用户可以选择其他文件
return
}
const fileType = fileInfo.name.split('.');
const fileTypeName = fileType[fileType.length - 1];
await GetRoomUpFileurl(state.channelId, fileTypeName).then(async res => {
const formData = new FormData();
formData.append("name", fileInfo.name);
formData.append("OSSAccessKeyId", res.data.ossAccessKeyId);
formData.append("key", res.data.key);
formData.append("policy", res.data.policy);
formData.append("signature", res.data.signature);
formData.append("success_action_status", res.data.success_action_status);
formData.append("file", fileInfo);
await axios.post(res.data.host, formData, {
headers: {
"Content-Type": "multipart/form-data",
"Authorization": `Bearer ${user.token}`
},
withCredentials: false
})
await PostRoomFile({
fileUrl: res.data.key,
size: fileInfo.size,
fileName: fileInfo.name,
roomId: state.roomId
})
getRoomFile()
})
};
file.click();
}}
></Button>
</div>
</div>
<div>
<Table
size={'small'}
rowSelection={showRowSelection ? {
selectedRowKeys,
onChange: (newSelectedRowKeys: React.Key[]) => {
setSelectedRowKeys(newSelectedRowKeys);
}
} : undefined}
dataSource={fileList.data}
pagination={false}
scroll={{ y: '40vh' }}
style={{ width: '100%' }}
>
<Column title="文件" dataIndex="fileName" key="fileName" width={140} />
<Column title="更新时间" dataIndex="modifyTime" key="modifyTime" width={200} />
<Column title="大小" render={(item) => (
<>
<span>{item.size / 1024 > 1000 ? (item.size / (1024 * 1024)).toFixed(2) + 'MB' : (item.size / 1024).toFixed(2) + 'KB'}</span>
</>
)} />
<Column title="上传者" dataIndex="userName" key="userName" />
<Column title="下载次数"
render={(item) => (
<>
<span>{item.downloadCount}</span>
</>
)}
/>
<Column title="操作" render={(item) => (
<>
<VerticalAlignBottomOutlined title='下载' style={{ color: '#5575F2', cursor: 'pointer' }} onClick={() => {
GetRoomFileDwUrl(item.fileUrl, item.id).then(res => {
if (res.code === 200) {
const downloadLink = document.createElement("a");
downloadLink.href = res.data;
downloadLink.download = item.fileName;
downloadLink.click();
getRoomFile()
}
})
}} />
{/* <FolderOutlined title='文件' style={{ color: '#FFA000', cursor: 'pointer' }} /> */}
</>
)} />
</Table>
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '10px' }}>
<Pagination size="small" total={fileList.total} onChange={(e) => {
setFileList({
...fileList,
pageIndex: e
})
}} pageSize={fileList.pageSize} current={fileList.pageIndex} hideOnSinglePage={true} />
</div>
</div>
</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}>
</div>
</div>
</Modal>
<SpeakerModeModal ref={speakerModeModalRef} />
<InvitingPersonnelModal ref={invitingPersonnelRef} />
</>
)
}
const meetingContentUser = (item: any) => {
return (
<>
<div className={styles.meetingContentUser}>
{item.roleId === '1' || item.isManager ? <div className={styles.meetingContentUserRole}>
<img src={ImageUrl.icon32} alt="" />
</div> : null}
<div className={styles.meetingContentUserName}>
<img src={item.enableMicr ? ImageUrl.icon22 : ImageUrl.icon22Active} alt="" />
<span>{item.userName}</span>
</div>
</div>
</>
)
}
const meetingContentError = (currentVideoId: any, item: any) => {
return (
<>
<div className={`${styles.meetingContentError} ${currentVideoId === item.account ? styles.active : ''}`}>
</div>
</>
)
}
export default Meeting